miscellaneous classes, containers, child properties
[cl-gtk2.git] / glib / gobject.generating.lisp
1 (in-package :gobject)
2
3 (defvar *lisp-name-package* (find-package :gobject))
4 (defvar *strip-prefix* "")
5 (defvar *lisp-name-exceptions* nil)
6 (defvar *generation-exclusions* nil)
7 (defvar *known-interfaces* (make-hash-table :test 'equal))
8 (defvar *additional-properties* nil)
9
10 (defun name->supplied-p (name)
11   (intern (format nil "~A-SUPPLIED-P" (symbol-name name))
12           *lisp-name-package*))
13
14 (defstruct property name accessor-name readable writable)
15
16 (defstruct (gobject-property (:include property)) gname type)
17
18 (defstruct (cffi-property (:include property)) type reader writer)
19
20 (defmethod make-load-form ((object gobject-property) &optional env)
21   (declare (ignore env))
22   `(make-gobject-property :name ',(property-name object)
23                           :accessor-name ',(property-accessor-name object)
24                           :readable ',(property-readable object)
25                           :writable ',(property-writable object)
26                           :gname ',(gobject-property-gname object)
27                           :type ',(gobject-property-type object)))
28
29 (defmethod make-load-form ((object cffi-property) &optional env)
30   (declare (ignore env))
31   `(make-cffi-property :name ',(property-name object)
32                        :accessor-name ',(property-accessor-name object)
33                        :readable ',(property-readable object)
34                        :writable ',(property-writable object)
35                        :type ',(cffi-property-type object)
36                        :reader ',(cffi-property-reader object)
37                        :writer ',(cffi-property-writer object)))
38
39 (defun parse-gobject-property (spec)
40   (destructuring-bind (name accessor-name gname type readable writable) spec
41       (make-gobject-property :name name
42                              :accessor-name accessor-name
43                              :gname gname
44                              :type type
45                              :readable readable
46                              :writable writable)))
47
48 (defun parse-cffi-property (spec)
49   (destructuring-bind (name accessor-name type reader writer) spec
50     (make-cffi-property :name name
51                         :accessor-name accessor-name
52                         :type type
53                         :reader reader
54                         :writer writer
55                         :readable (not (null reader))
56                         :writable (not (null writer)))))
57
58 (defun parse-property (spec)
59   (cond
60     ((eq (first spec) :cffi) (parse-cffi-property (rest spec)))
61     (t (parse-gobject-property spec))))
62
63 (defun property->method-arg (property)
64   (when (or (gobject-property-p property)
65             (and (cffi-property-p property)
66                  (property-writable property)))
67     (let ((name (property-name property)))
68       `(,name nil ,(name->supplied-p name)))))
69
70 (defun gobject-property->arg-push (property)
71   (assert (typep property 'gobject-property))
72   (with-slots (name type gname) property
73     `(when ,(name->supplied-p name)
74        (push ,gname arg-names)
75        (push ,type arg-types)
76        (push ,name arg-values))))
77
78 (defun cffi-property->initarg (property)
79   (assert (typep property 'cffi-property))
80   (when (property-writable property)
81     (with-slots (accessor-name name type writer) property
82       `(when ,(name->supplied-p name)
83          (setf (,accessor-name object) ,name)))))
84
85 (defun accessor-name (class-name property-name)
86   (intern (format nil "~A-~A" (symbol-name class-name)
87                   (lispify-name property-name))
88           *lisp-name-package*))
89
90 (defgeneric property->reader (class property))
91 (defgeneric property->writer (class property))
92
93 (defmethod property->reader (class (property gobject-property))
94   (with-slots (accessor-name type gname) property
95    `(defmethod ,accessor-name ((object ,class))
96       (g-object-call-get-property object ,gname ,type))))
97
98 (defmethod property->reader (class (property cffi-property))
99   (with-slots (accessor-name type reader) property
100     (etypecase reader
101       (string `(defmethod ,accessor-name ((object ,class))
102                  (foreign-funcall ,reader g-object object ,type)))
103       (symbol `(defmethod ,accessor-name ((object ,class))
104                  (funcall ',reader object))))))
105
106 (defmethod property->writer (class (property gobject-property))
107   (with-slots (accessor-name type gname) property
108     `(defmethod (setf ,accessor-name) (new-value (object ,class))
109        (g-object-call-set-property object ,gname new-value ,type)
110        new-value)))
111
112 (defmethod property->writer (class (property cffi-property))
113   (with-slots (accessor-name type writer) property
114     (etypecase writer
115       (string `(defmethod (setf ,accessor-name) (new-value (object ,class))
116                  (foreign-funcall ,writer g-object object ,type new-value :void)
117                  new-value))
118       (symbol `(defmethod (setf ,accessor-name) (new-value (object ,class))
119                  (funcall ',writer object new-value)
120                  new-value)))))
121
122 (defun property->accessors (class property export)
123   (append (when (property-readable property)
124             (list (property->reader class property)))
125           (when (property-writable property)
126             (list (property->writer class property)))
127           (when export
128             (list `(export ',(property-accessor-name property)
129                            (find-package ,(package-name (symbol-package (property-accessor-name property)))))))))
130
131 (defun interface->lisp-class-name (interface)
132   (etypecase interface
133     (symbol interface)
134     (string (or (gethash interface *known-interfaces*)
135                 (error "Unknown interface ~A" interface)))))
136
137 (defun type-initializer-call (type-initializer)
138   (etypecase type-initializer
139     (string `(foreign-funcall ,type-initializer g-type))
140     (symbol `(funcall ',type-initializer))))
141
142 (defmacro define-g-object-class (g-type-name name
143                                  (&key (superclass 'g-object)
144                                        (export t)
145                                        interfaces
146                                        type-initializer)
147                                  (&rest properties))
148   (setf properties (mapcar #'parse-property properties))
149   (let* ((superclass-properties (get superclass 'properties))
150          (interface-properties (map-append (lambda (iface-name)
151                                              (get (gethash iface-name *known-interfaces*) 'properties))
152                                            interfaces))
153          (combined-properties (append superclass-properties properties interface-properties)))
154     `(progn
155        (defclass ,name (,superclass ,@(mapcar #'interface->lisp-class-name interfaces)) ())
156        (register-object-type ,g-type-name ',name)
157        ,@(when type-initializer
158                (list (type-initializer-call type-initializer)))
159        ,@(when export
160                (list `(export ',name (find-package ,(package-name (symbol-package name)))))) 
161        (defmethod initialize-instance :before 
162            ((object ,name) &key pointer
163             ,@(remove nil (mapcar #'property->method-arg
164                                   combined-properties)))
165          (unless (or pointer (and (slot-boundp object 'pointer)
166                                   (not (null-pointer-p (pointer object)))))
167            (let (arg-names arg-values arg-types)
168              ,@(mapcar #'gobject-property->arg-push (remove-if-not #'gobject-property-p combined-properties))
169              (setf (pointer object)
170                    (g-object-call-constructor ,g-type-name
171                                               arg-names
172                                               arg-values
173                                               arg-types)
174                    (g-object-has-reference object) t)
175              ,@(mapcar #'cffi-property->initarg (remove-if-not #'cffi-property-p combined-properties)))))
176        ,@(loop
177             for property in properties
178             append (property->accessors name property export))
179        
180        (eval-when (:compile-toplevel :load-toplevel :execute)
181          (register-object-type ,g-type-name ',name)
182          (setf (get ',name 'superclass) ',superclass
183                (get ',name 'properties) ',combined-properties)))))
184
185 (defmacro define-g-interface (g-name name (&key (export t) type-initializer) &body properties)
186   (setf properties (mapcar #'parse-property properties))
187   `(progn
188      (defclass ,name () ())
189      ,@(when export
190              (list `(export ',name (find-package ,(package-name (symbol-package name))))))
191      ,@(when type-initializer
192              (list (type-initializer-call type-initializer)))
193      ,@(loop
194           for property in properties
195           append (property->accessors name property export))
196      (eval-when (:compile-toplevel :load-toplevel :execute)
197        (setf (get ',name 'properties) ',properties)
198        (setf (gethash ,g-name *known-interfaces*) ',name))))
199
200 (defun starts-with (name prefix)
201   (and prefix (> (length name) (length prefix)) (string= (subseq name 0 (length prefix)) prefix)))
202
203 (defun strip-start (name prefix)
204   (if (starts-with name prefix)
205       (subseq name (length prefix))
206       name))
207
208 (defun lispify-name (name)
209   (with-output-to-string (stream)
210     (loop for c across (strip-start name *strip-prefix*)
211        for firstp = t then nil
212        do (when (and (not firstp) (upper-case-p c)) (write-char #\- stream))
213        do (write-char (char-upcase c) stream))))
214
215 (defun g-name->name (name)
216   (or (second (assoc name *lisp-name-exceptions* :test 'equal))
217       (intern (string-upcase (lispify-name name)) *lisp-name-package*)))
218
219 (defun property->property-definition (class-name property)
220   (let ((name (g-name->name (g-class-property-definition-name property)))
221         (accessor-name (accessor-name class-name (g-class-property-definition-name property)))
222         (g-name (g-class-property-definition-name property))
223         (type (g-type-name (g-class-property-definition-type property)))
224         (readable (g-class-property-definition-readable property))
225         (writable (and (g-class-property-definition-writable property)
226                        (not (g-class-property-definition-constructor-only property)))))
227     `(,name ,accessor-name ,g-name ,type ,readable ,writable)))
228
229 (defun probable-type-init-name (type-name)
230   (with-output-to-string (stream)
231     (iter (for c in-string type-name)
232           (for prev-c previous c)
233           (when (and (not (first-iteration-p))
234                      (upper-case-p c)
235                      (not (upper-case-p prev-c))
236                      (not (char= prev-c #\_)))
237             (write-char #\_ stream))
238           (write-char (char-downcase c) stream))
239     (write-string "_get_type" stream)))
240
241 (defun get-g-class-definition (type)
242   (let* ((g-type (ensure-g-type type))
243          (g-name (g-type-name g-type))
244          (name (g-name->name g-name))
245          (superclass-g-type (g-type-parent g-type))
246          (superclass-name (g-name->name (g-type-name superclass-g-type)))
247          (interfaces (g-type-interfaces g-type))
248          (properties (class-properties g-type))
249          (type-init-name (probable-type-init-name g-name))
250          (own-properties
251           (remove-if-not (lambda (property)
252                            (= g-type
253                               (g-class-property-definition-owner-type property)))
254                          properties)))
255     `(define-g-object-class ,g-name ,name 
256          (:superclass ,superclass-name
257                       :export t
258                       :interfaces (,@(sort (mapcar #'g-type-name interfaces) 'string<))
259                       ,@(when (and (foreign-symbol-pointer type-init-name)
260                                    (not (null-pointer-p (foreign-symbol-pointer type-init-name))))
261                               `(:type-initializer ,type-init-name)))
262        (,@(mapcar (lambda (property)
263                     (property->property-definition name property))
264                   own-properties)
265           ,@(cdr (find g-name *additional-properties* :key 'car :test 'string=))))))
266
267 (defun get-g-interface-definition (interface)
268   (let* ((type (ensure-g-type interface))
269          (g-name (g-type-name type))
270          (name (g-name->name g-name))
271          (properties (interface-properties type))
272          (probable-type-initializer (probable-type-init-name g-name)))
273     `(define-g-interface ,g-name ,name
274          (:export t
275                   ,@(when (foreign-symbol-pointer probable-type-initializer)
276                           `(:type-initializer ,probable-type-initializer)))
277        ,@(append (mapcar (lambda (property)
278                            (property->property-definition name property))
279                          properties)
280                  (cdr (find g-name *additional-properties* :key 'car :test 'string=))))))
281
282 (defun get-g-class-definitions-for-root-1 (type)
283   (unless (member (ensure-g-type type) *generation-exclusions* :test '=)
284     (cons (get-g-class-definition type)
285           (reduce #'append
286                   (mapcar #'get-g-class-definitions-for-root-1
287                           (g-type-children type))))))
288
289 (defun get-g-class-definitions-for-root (type)
290   (setf type (ensure-g-type type))
291   (get-g-class-definitions-for-root-1 type))
292
293 (defvar *referenced-types*)
294
295 (defun class-or-interface-properties (type)
296   (setf type (ensure-g-type type))
297   (cond 
298     ((= (g-type-fundamental type) +g-type-object+) (class-properties type))
299     ((= (g-type-fundamental type) +g-type-interface+) (interface-properties type))))
300
301 (defun get-shallow-referenced-types (type)
302   (setf type (ensure-g-type type))
303   (remove-duplicates (sort (loop
304                               for property in (class-or-interface-properties type)
305                               when (= type (g-class-property-definition-owner-type property))
306                               collect (g-class-property-definition-type property))
307                            #'<)
308                      :test 'equal))
309
310 (defun get-referenced-types-1 (type)
311   (setf type (ensure-g-type type))
312   (loop
313      for property-type in (get-shallow-referenced-types type)
314      do (pushnew property-type *referenced-types* :test '=))
315   (loop
316      for type in (g-type-children type)
317      do (get-referenced-types-1 type)))
318
319 (defun get-referenced-types (root-type)
320   (let (*referenced-types*)
321     (get-referenced-types-1 (ensure-g-type root-type))
322     *referenced-types*))
323
324 (defun filter-types-by-prefix (types prefix)
325   (remove-if-not
326    (lambda (type)
327      (starts-with (g-type-name (ensure-g-type type)) prefix))
328    types))
329
330 (defun filter-types-by-fund-type (types fund-type)
331   (setf fund-type (ensure-g-type fund-type))
332   (remove-if-not
333    (lambda (type)
334      (equal (g-type-fundamental (ensure-g-type type)) fund-type))
335    types))
336
337 (defmacro define-g-enum (g-name name (&key (export t) type-initializer)  &body values)
338   `(progn
339      (defcenum ,name ,@values)
340      (register-enum-type ,g-name ',name)
341      ,@(when export
342              (list `(export ',name (find-package ,(package-name (symbol-package name))))))
343      ,@(when type-initializer
344              (list (type-initializer-call type-initializer)))))
345
346 (defun enum-value->definition (enum-value)
347   (let ((value-name (intern (lispify-name (enum-item-nick enum-value))
348                             (find-package :keyword)))
349         (numeric-value (enum-item-value enum-value)))
350     `(,value-name ,numeric-value)))
351
352 (defun get-g-enum-definition (type)
353   (let* ((g-type (ensure-g-type type))
354          (g-name (g-type-name g-type))
355          (name (g-name->name g-name))
356          (items (get-enum-items g-type))
357          (probable-type-initializer (probable-type-init-name g-name)))
358     `(define-g-enum ,g-name ,name
359          (:export t
360                   ,@(when (foreign-symbol-pointer probable-type-initializer)
361                           (list :type-initializer
362                                 probable-type-initializer)))
363        ,@(mapcar #'enum-value->definition items))))
364
365 (defmacro define-g-flags (g-name name (&key (export t) type-initializer) &body values)
366   `(progn
367      (defbitfield ,name ,@values)
368      (register-enum-type ,g-name ',name)
369      ,@(when export
370              (list `(export ',name (find-package ,(package-name (symbol-package name))))))
371      ,@(when type-initializer
372              (list (type-initializer-call type-initializer)))))
373
374 (defun flags-value->definition (flags-value)
375   (let ((value-name (intern (lispify-name (flags-item-nick flags-value))
376                             (find-package :keyword)))
377         (numeric-value (flags-item-value flags-value)))
378     `(,value-name ,numeric-value)))
379
380 (defun get-g-flags-definition (type)
381   (let* ((g-type (ensure-g-type type))
382          (g-name (g-type-name g-type))
383          (name (g-name->name g-name))
384          (items (get-flags-items g-type))
385          (probable-type-initializer (probable-type-init-name g-name)))
386     `(define-g-flags ,g-name ,name
387          (:export t
388                   ,@(when (foreign-symbol-pointer probable-type-initializer)
389                           (list :type-initializer
390                                 probable-type-initializer)))
391        ,@(mapcar #'flags-value->definition items))))
392
393 (defun generate-types-hierarchy-to-file (file root-type &key include-referenced prefix package exceptions prologue interfaces enums flags objects exclusions additional-properties)
394   (if (not (streamp file))
395       (with-open-file (stream file :direction :output :if-exists :supersede)
396         (generate-types-hierarchy-to-file stream root-type
397                                           :prefix prefix
398                                           :package package
399                                           :exceptions exceptions
400                                           :prologue prologue
401                                           :include-referenced include-referenced
402                                           :interfaces interfaces
403                                           :enums enums
404                                           :flags flags
405                                           :objects objects
406                                           :exclusions exclusions
407                                           :additional-properties additional-properties))
408       (let* ((*generation-exclusions* (mapcar #'ensure-g-type exclusions))
409              (*lisp-name-package* (or package *package*))
410              (*package* *lisp-name-package*)
411              (*strip-prefix* (or prefix ""))
412              (*lisp-name-exceptions* exceptions)
413              (*print-case* :downcase)
414              (*additional-properties* additional-properties)
415              (referenced-types (and include-referenced
416                                     (filter-types-by-prefix
417                                      (get-referenced-types root-type)
418                                      prefix))))
419         (setf exclusions (mapcar #'ensure-g-type exclusions))
420         (when prologue
421           (write-string prologue file)
422           (terpri file))
423         (when include-referenced
424           (loop
425              for interface in interfaces
426              do (loop
427                    for referenced-type in (get-shallow-referenced-types interface)
428                    do (pushnew referenced-type referenced-types :test 'equal)))
429           (loop
430              for object in objects
431              do (loop
432                    for referenced-type in (get-shallow-referenced-types object)
433                    do (pushnew referenced-type referenced-types :test 'equal)))
434           (loop
435              for enum-type in (filter-types-by-fund-type
436                                referenced-types "GEnum")
437              for def = (get-g-enum-definition enum-type)
438              unless (member (ensure-g-type enum-type) exclusions :test '=)
439              do (format file "~S~%~%" def))
440             
441           (loop
442              for flags-type in (filter-types-by-fund-type
443                                 referenced-types "GFlags")
444              for def = (get-g-flags-definition flags-type)
445              unless (member (ensure-g-type flags-type) exclusions :test '=)
446              do (format file "~S~%~%" def)))
447         (loop
448            with auto-enums = (and include-referenced
449                                   (filter-types-by-fund-type
450                                    referenced-types "GEnum"))
451            for enum in enums
452            for def = (get-g-enum-definition enum)
453            unless (find (ensure-g-type enum) auto-enums :test 'equal)
454            do (format file "~S~%~%" def))
455         (loop
456            with auto-flags = (and include-referenced
457                                   (filter-types-by-fund-type
458                                    referenced-types "GFlags"))
459            for flags-type in flags
460            for def = (get-g-flags-definition flags-type)
461            unless (find (ensure-g-type flags-type) auto-flags :test 'equal)
462            do (format file "~S~%~%" def))
463         (loop
464            for interface in interfaces
465            for def = (get-g-interface-definition interface)
466            do (format file "~S~%~%" def))
467         (loop
468            for def in (get-g-class-definitions-for-root root-type)
469            do (format file "~S~%~%" def))
470         (loop
471            for object in objects
472            for def = (get-g-class-definition object)
473            do (format file "~S~%~%" def)))))