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