Fix :gobject-fn slot accessor by implementing slot-boundp-using-class
[cl-gtk2.git] / glib / gobject.meta.lisp
1 (in-package :gobject)
2
3 (defclass gobject-class (standard-class)
4   ((g-type-name :initform (error "G-TYPE-NAME must be specified")
5                 :initarg :g-type-name
6                 :reader gobject-class-g-type-name)
7    (g-type-initializer :initform nil
8                        :initarg :g-type-initializer
9                        :reader gobject-class-g-type-initializer)
10    (interface-p :initform nil
11                 :initarg :g-interface-p
12                 :reader gobject-class-interface-p))
13   (:documentation "Metaclass for GObject-based classes."))
14
15 (defun initialize-gobject-class-g-type (class)
16   (if (gobject-class-g-type-initializer class)
17       (let* ((initializer-fn-ptr (foreign-symbol-pointer (gobject-class-g-type-initializer class)))
18              (type (when initializer-fn-ptr
19                      (foreign-funcall-pointer initializer-fn-ptr nil
20                                               g-type))))
21         (if (null initializer-fn-ptr)
22             (warn "Type initializer for class '~A' (GType '~A') is invalid: foreign symbol '~A'"
23                   (gobject-class-g-type-name class) (class-name class) (gobject-class-g-type-initializer class))
24             (progn
25               (when (= +g-type-invalid+ type)
26                 (warn "Declared GType name '~A' for class '~A' is invalid ('~A' returned 0)"
27                       (gobject-class-g-type-name class) (class-name class)
28                       (gobject-class-g-type-initializer class)))
29               (unless (string= (gobject-class-g-type-name class)
30                                (g-type-name type))
31                 (warn "Declared GType name '~A' for class '~A' does not match actual GType name '~A'"
32                       (gobject-class-g-type-name class)
33                       (class-name class)
34                       (g-type-name type))))))
35       (unless (g-type-from-name (gobject-class-g-type-name class))
36         (warn "Declared GType name '~A' for class '~A' is invalid (g_type_name returned 0)"
37               (gobject-class-g-type-name class) (class-name class)))))
38
39 (defmethod initialize-instance :after ((object gobject-class) &key &allow-other-keys)
40   (register-object-type (gobject-class-g-type-name object) (class-name object))
41   (at-init (object) (initialize-gobject-class-g-type object)))
42
43 (defclass gobject-direct-slot-definition (standard-direct-slot-definition)
44   ((g-property-type :initform nil
45                     :initarg :g-property-type
46                     :reader gobject-direct-slot-definition-g-property-type)))
47
48 (defclass gobject-effective-slot-definition (standard-effective-slot-definition)
49   ((g-property-type :initform nil
50                     :initarg :g-property-type
51                     :accessor gobject-effective-slot-definition-g-property-type)))
52
53 (defclass gobject-property-direct-slot-definition (gobject-direct-slot-definition)
54   ((g-property-name :initform nil
55                     :initarg :g-property-name
56                     :reader gobject-property-direct-slot-definition-g-property-name)))
57
58 (defclass gobject-property-effective-slot-definition (gobject-effective-slot-definition)
59   ((g-property-name :initform nil
60                     :initarg :g-property-name
61                     :accessor gobject-property-effective-slot-definition-g-property-name)))
62
63 (defclass gobject-fn-direct-slot-definition (gobject-direct-slot-definition)
64   ((g-getter-name :initform nil
65                   :initarg :g-getter
66                   :reader gobject-fn-direct-slot-definition-g-getter-name)
67    (g-setter-name :initform nil
68                   :initarg :g-setter
69                   :reader gobject-fn-direct-slot-definition-g-setter-name)))
70
71 (defclass gobject-fn-effective-slot-definition (gobject-effective-slot-definition)
72   ((g-getter-name :initform nil
73                   :initarg :g-getter
74                   :accessor gobject-fn-effective-slot-definition-g-getter-name)
75    (g-setter-name :initform nil
76                   :initarg :g-setter
77                   :accessor gobject-fn-effective-slot-definition-g-setter-name)
78    (g-getter-fn :initform nil
79                 :accessor gobject-fn-effective-slot-definition-g-getter-fn)
80    (g-setter-fn :initform nil
81                 :accessor gobject-fn-effective-slot-definition-g-setter-fn)))
82
83 (defmethod validate-superclass ((class gobject-class) (superclass standard-class))
84   t)
85
86 (defmethod validate-superclass ((class standard-class) (superclass gobject-class))
87   t)
88
89 (defmethod compute-class-precedence-list ((class gobject-class))
90   (let ((classes (call-next-method)))
91     (if (member (find-class 'g-object) classes)
92         classes
93         `(,class ,(find-class 'g-object) ,@(rest classes)))))
94
95 (defmethod direct-slot-definition-class ((class gobject-class) &rest initargs &key allocation)
96   (declare (ignore initargs))
97   (case allocation
98     (:gobject-property 'gobject-property-direct-slot-definition)
99     (:gobject-fn 'gobject-fn-direct-slot-definition)
100     (otherwise (call-next-method))))
101
102 (defmethod effective-slot-definition-class ((class gobject-class) &rest initargs &key allocation)
103   (declare (ignore initargs))
104   (case allocation
105     (:gobject-property 'gobject-property-effective-slot-definition)
106     (:gobject-fn 'gobject-fn-effective-slot-definition)
107     (otherwise (call-next-method))))
108
109 (defmethod compute-effective-slot-definition ((class gobject-class) name direct-slots)
110   (let ((effective-slot (call-next-method)))
111     (when (typep effective-slot 'gobject-effective-slot-definition)
112       (let ((allocation (loop
113                               for direct-slot in direct-slots
114                               when (slot-definition-allocation direct-slot)
115                               return (slot-definition-allocation direct-slot)))
116             (property-name (loop
117                               for direct-slot in direct-slots
118                               when (and (typep direct-slot 'gobject-property-direct-slot-definition) (gobject-property-direct-slot-definition-g-property-name direct-slot))
119                               return (gobject-property-direct-slot-definition-g-property-name direct-slot)))
120             (property-type (loop
121                               for direct-slot in direct-slots
122                               when (gobject-direct-slot-definition-g-property-type direct-slot)
123                               return (gobject-direct-slot-definition-g-property-type direct-slot)))
124             (property-getter (loop
125                               for direct-slot in direct-slots
126                               when (and (typep direct-slot 'gobject-fn-direct-slot-definition) (gobject-fn-direct-slot-definition-g-getter-name direct-slot))
127                               return (gobject-fn-direct-slot-definition-g-getter-name direct-slot)))
128             (property-setter (loop
129                               for direct-slot in direct-slots
130                               when (and (typep direct-slot 'gobject-fn-direct-slot-definition) (gobject-fn-direct-slot-definition-g-setter-name direct-slot))
131                               return (gobject-fn-direct-slot-definition-g-setter-name direct-slot))))
132         (setf (gobject-effective-slot-definition-g-property-type effective-slot)
133               (gobject-effective-slot-definition-g-property-type effective-slot))
134         (ecase allocation
135           (:gobject-property (assert property-name nil "G-PROPERTY-NAME for slot ~A on class ~A must be specified" name (class-name class))
136                              (setf (gobject-property-effective-slot-definition-g-property-name effective-slot)
137                                    property-name))
138           (:gobject-fn (assert (or property-getter property-setter) nil "At least one of G-PROPERTY-GETTER or G-PROPERTY-SETTER for slot ~A on class ~A must be specified"
139                                name (class-name class))
140                        (when (or (and property-getter (stringp property-getter))
141                                  (and property-setter (stringp property-setter)))
142                         (assert property-type nil "G-PROPERTY-TYPE for slot ~A on class ~A must be specified because at least one of accessor is specified as a foreign function" name (class-name class)))
143                        
144                        (setf (gobject-fn-effective-slot-definition-g-getter-name effective-slot) property-getter
145                              (gobject-fn-effective-slot-definition-g-setter-name effective-slot) property-setter
146                              (gobject-fn-effective-slot-definition-g-getter-fn effective-slot)
147                              (and property-getter
148                                   (if (stringp property-getter)
149                                       (compile nil `(lambda (object)
150                                                       (foreign-funcall ,property-getter
151                                                                        g-object object
152                                                                        ,property-type)))
153                                       property-getter))
154                              (gobject-fn-effective-slot-definition-g-setter-fn effective-slot)
155                              (and property-setter
156                                   (if (stringp property-setter)
157                                       (compile nil `(lambda (object new-value)
158                                                       (foreign-funcall ,property-setter
159                                                                        g-object object
160                                                                        ,property-type new-value
161                                                                        :void)))
162                                       property-setter)))))))
163     effective-slot))
164
165 (defun create-gobject-from-class-and-initargs (class initargs)
166   (when (gobject-class-interface-p class)
167     (error "Trying to create instance of GInterface '~A' (class '~A')" (gobject-class-g-type-name class) (class-name class)))
168   (let (arg-names arg-values arg-types nc-setters nc-arg-values)
169     (declare (dynamic-extent arg-names arg-values arg-types nc-setters nc-arg-values))
170     (loop
171        for (arg-name arg-value) on initargs by #'cddr
172        for slot = (find arg-name (class-slots class) :key 'slot-definition-initargs :test 'member)
173        when (and slot (typep slot 'gobject-effective-slot-definition))
174        do (typecase slot
175             (gobject-property-effective-slot-definition
176              (push (gobject-property-effective-slot-definition-g-property-name slot) arg-names)
177              (push arg-value arg-values)
178              (push (gobject-effective-slot-definition-g-property-type slot) arg-types))
179             (gobject-fn-effective-slot-definition
180              (push (gobject-fn-effective-slot-definition-g-setter-fn slot) nc-setters)
181              (push arg-value nc-arg-values))))
182     (let ((object (g-object-call-constructor (gobject-class-g-type-name class) arg-names arg-values arg-types)))
183       (loop
184          for fn in nc-setters
185          for value in nc-arg-values
186          do (funcall fn object value))
187       object)))
188
189 (defun filter-initargs-by-class (class initargs)
190   (iter (with slots = (class-slots class))
191         (for (arg-name arg-value) on initargs by #'cddr)
192         (for slot = (find arg-name slots :key #'slot-definition-initargs :test 'member))
193         (unless (and slot (typep slot 'gobject-effective-slot-definition))
194           (nconcing (list arg-name arg-value)))))
195
196 (defmethod initialize-instance ((instance g-object) &rest initargs &key &allow-other-keys)
197   (let ((filtered-initargs (filter-initargs-by-class (class-of instance) initargs)))
198     (apply #'call-next-method instance filtered-initargs)))
199
200 (defmethod make-instance ((class gobject-class) &rest initargs &key pointer)
201   (if pointer
202       (progn
203         (assert (= (length initargs) 2) nil "POINTER can not be combined with other initargs (~A)" initargs)
204         (call-next-method))
205       (let ((pointer (create-gobject-from-class-and-initargs class initargs)))
206         (apply #'call-next-method class :pointer pointer initargs))))
207
208 (defmethod slot-boundp-using-class ((class gobject-class) object (slot gobject-property-effective-slot-definition))
209   (handler-case
210       (progn (g-object-property-type object (gobject-property-effective-slot-definition-g-property-name slot) :assert-readable t) t)
211     (property-unreadable-error () nil)))
212
213 (defmethod slot-value-using-class ((class gobject-class) object (slot gobject-property-effective-slot-definition))
214   (g-object-call-get-property (pointer object)
215                               (gobject-property-effective-slot-definition-g-property-name slot)
216                               (gobject-effective-slot-definition-g-property-type slot)))
217
218 (defmethod (setf slot-value-using-class) (new-value (class gobject-class) object (slot gobject-property-effective-slot-definition))
219   (g-object-call-set-property (pointer object)
220                               (gobject-property-effective-slot-definition-g-property-name slot)
221                               new-value
222                               (gobject-effective-slot-definition-g-property-type slot)))
223
224 (defmethod slot-boundp-using-class ((class gobject-class) object (slot gobject-fn-effective-slot-definition))
225   (not (null (gobject-fn-effective-slot-definition-g-getter-fn slot))))
226
227 (defmethod slot-value-using-class ((class gobject-class) object (slot gobject-fn-effective-slot-definition))
228   (let ((fn (gobject-fn-effective-slot-definition-g-getter-fn slot)))
229     (funcall fn object)))
230
231 (defmethod (setf slot-value-using-class) (new-value (class gobject-class) object (slot gobject-fn-effective-slot-definition))
232   (funcall (gobject-fn-effective-slot-definition-g-setter-fn slot) object new-value))
233
234 (defmethod slot-boundp-using-class ((class gobject-class) object (slot gobject-effective-slot-definition))
235   t)
236
237 (defmethod slot-makunbound-using-class ((class gobject-class) object (slot gobject-effective-slot-definition))
238   nil)