initial import
Annotate for file src/commonqt.lisp
2009-07-02 tobias 1 (in-package :cl-smoke.commonqt)
17:34:07 ' 2
' 3 ;;; calling methods
' 4 ;;;
' 5 (eval-when (:load-toplevel :compile-toplevel :execute)
' 6 (defun read-perserving-case (stream)
' 7 (let ((*readtable* (copy-readtable nil)))
' 8 (setf (readtable-case *readtable*) :preserve)
' 9 (read stream)))
' 10
' 11 (defun cl-smoke-funcall-form (stream subchar arg)
' 12 (declare (ignore subchar arg))
' 13 (let ((method (read-perserving-case stream)))
' 14 (if (string= "new" (symbol-name method))
' 15 `(lambda (&rest args)
' 16 (make-instance
' 17 ',(smoke::lispify (symbol-name (read-perserving-case stream)) :cl-smoke.qt)
' 18 :args args))
' 19 (let ((object (read stream)))
' 20 (if (stringp object)
' 21 `(lambda (&rest args)
' 22 (apply (symbol-function ',(smoke::lispify (symbol-name method) :cxx))
' 23 (find-class ',(smoke::lispify object :cl-smoke.qt))
' 24 args))
' 25 `(lambda (&rest args)
' 26 (apply (symbol-function ',(smoke::lispify (symbol-name method) :cxx))
' 27 ,object
' 28 args))))))))
' 29
' 30 (eval-when (:load-toplevel :compile-toplevel)
' 31 (set-dispatch-macro-character #\# #\_ #'cl-smoke-funcall-form))
' 32
' 33 ;;; Instantiating
' 34 ;;;
' 35 (defun new (instance &rest arguments)
' 36 "Calls the C++ construor for INSTANCE with ARGUMENTS."
' 37 (assert (null-pointer-p (smoke:pointer instance)))
' 38 (setf (slot-value instance 'smoke:pointer)
' 39 (smoke::call-constructor instance arguments))
' 40 (assert (not (null-pointer-p (smoke:pointer instance))))
' 41 (smoke::set-binding instance)
' 42 (smoke::take-ownership instance)
' 43 (smoke::add-object instance)
' 44 instance)
' 45
' 46 ;;; Connecting signals and slots
' 47 ;;;
' 48 (defun qsignal (name)
' 49 (cl-smoke.qt:qsignal name))
' 50 (defun qslot (name)
' 51 (cl-smoke.qt:qslot name))
' 52
' 53 ;;; QApplication
' 54 ;;;
' 55 (defun make-qapplication (&rest arguments)
' 56 "Returns a new QApplication instance with ARGUMENTS as command line
' 57 arguments argv. argv[0] is set to (LISP-IMPLEMNTATION-TYPE)."
' 58 (let* ((argc ;(smoke::make-auto-pointer
' 59 (cffi:foreign-alloc :int
' 60 :initial-element (1+ (length arguments))))
' 61 (argv ;(smoke::make-auto-pointer
' 62 (cffi:foreign-alloc :string
' 63 :initial-contents
' 64 (cons (lisp-implementation-type) ;; arg0
' 65 arguments)))
' 66 (application (make-instance 'cl-smoke.qt:application :args
' 67 (list argc argv))))
' 68 ;; argc & argv must remain valid during the lifetime of application.
' 69 (setf (cl-smoke.qt:property application 'arguments)
' 70 (cl-smoke.qt:make-lisp-variant (cons argc argv)))
' 71 application))
' 72
' 73 ;;; Overriding C++ methods
' 74 (defclass override-gf (standard-generic-function)
' 75 ((cxx-gf :initarg :cxx-gf
' 76 :reader cxx-gf))
' 77 (:metaclass closer-mop:funcallable-standard-class)
' 78 (:documentation "Redirects its methods to CXX-GF."))
' 79
' 80 (defmethod add-method ((gf override-gf) method)
' 81 (add-method (cxx-gf gf) method))
' 82
' 83 (defmethod remove-method ((gf override-gf) method)
' 84 (remove-method (cxx-gf gf) method))
' 85
' 86 ;;; Subclassing C++ & defining signals and slots
' 87 ;;;
' 88 (defclass qt-class (cxx:class)
' 89 ((qt-slots :initarg :slots :initform nil) ;; FIXME remove this
' 90 (signals :initarg :signals :initform nil) ;; FIXME remove this
' 91 (method-functions :documentation "The signals and slots.")
' 92 (meta-object)))
' 93
' 94 (defun setup-meta-object (class qt-superclass)
' 95 (let ((methods (make-array (+ (length (slot-value class 'signals))
' 96 (length (slot-value class 'qt-slots)))
' 97 :element-type 'function
' 98 :initial-element #'identity))
' 99 (index 0))
' 100 (dolist (signal-name (slot-value class 'signals))
' 101 (incf index)
' 102 (setf (aref methods index)
' 103 #'(lambda (this &rest args)
' 104 (apply #'emit-signal this signal-name args))))
' 105 (dolist (slot (slot-value class 'qt-slots))
' 106 (setf (aref methods index)
' 107 (if (symbolp (second slot))
' 108 (second slot)
' 109 (eval `(function ,(second slot)))))
' 110 (incf index))
' 111 (setf (slot-value class 'method-functions) methods))
' 112 (let ((signals (mapcar #'(lambda (s) (make-slot-or-signal (first s)))
' 113 (slot-value class 'signals)))
' 114 (slots (mapcar #'(lambda (s) (make-slot-or-signal (first s)))
' 115 (slot-value class 'qt-slots)))
' 116 (class-name (class-name class)))
' 117 (setf (slot-value class 'meta-object)
' 118 (make-metaobject qt-superclass
' 119 (format nil "~A::~A"
' 120 (package-name (symbol-package class-name))
' 121 (symbol-name class-name))
' 122 nil
' 123 signals
' 124 slots))))
' 125
' 126 (defun setup-qt-class (qt-class next-method &rest args &key qt-superclass
' 127 direct-superclasses direct-default-initargs
' 128 override &allow-other-keys)
' 129 (assert (null direct-superclasses))
' 130 (dolist (method override)
' 131 (ensure-generic-function
' 132 (second method)
' 133 :generic-function-class (find-class 'override-gf)
' 134 :lambda-list '(this &rest args)
' 135 :cxx-gf (fdefinition (smoke::lispify (first method) :cxx))))
' 136 (apply next-method qt-class
' 137 :direct-default-initargs ;; the C++ instance is constructed with #'NEW
' 138 (cons `(:pointer (null-pointer) ,#'null-pointer)
' 139 direct-default-initargs)
' 140 :direct-superclasses
' 141 (list (find-qclass (first qt-superclass)))
' 142 args)
' 143 (setup-meta-object qt-class (find-qclass (first qt-superclass))))
' 144
' 145 (defmethod initialize-instance :around ((class qt-class)
' 146 &rest args)
' 147 (apply #'setup-qt-class class #'call-next-method args)
' 148 (closer-mop:ensure-method #'cxx:meta-object
' 149 '(lambda (this)
' 150 (slot-value (class-of this) 'meta-object))
' 151 :specializers (list class))
' 152 (closer-mop:ensure-method
' 153 #'cxx:qt-metacall
' 154 '(lambda (this call id arguments)
' 155 (let ((m-id (call-next-method)))
' 156 (if (< m-id 0)
' 157 m-id
' 158 (if (enum= call cl-smoke.qt:meta-object.+invoke-meta-method+)
' 159 (progn
' 160 (when (< m-id (length (slot-value (class-of this)
' 161 'method-functions)))
' 162 (with-simple-restart
' 163 (continue "Skip the method ~A of ~A."
' 164 id
' 165 this)
' 166 (apply (aref (slot-value (class-of this) 'method-functions)
' 167 m-id)
' 168 this
' 169 (cl-smoke.qt-impl::arguments-to-lisp
' 170 arguments
' 171 (cl-smoke.qt-impl::method-arguments-type
' 172 (cxx:meta-object this) id)))))
' 173 (- m-id
' 174 (length (slot-value (class-of this) 'method-functions))))
' 175 m-id))))
' 176 :specializers (list class (find-class t) (find-class t) (find-class t))))
' 177
' 178 (defmethod reinitialize-instance :around ((qt-class qt-class) &rest args)
' 179 (apply #'setup-qt-class qt-class #'call-next-method args))
' 180
' 181 (defun emit-signal (object signal-name &rest arguments)
' 182 "Emits the signal SIGNAL-NAME of OBJECT using ARGUMENTS."
' 183 (let* ((meta-object (slot-value (class-of object) 'meta-object))
' 184 (id (#_indexOfSignal meta-object signal-name)))
' 185 (assert (>= id 0)
' 186 ()
' 187 "No signal ~A of ~A." signal-name object)
' 188 (cl-smoke.qt-impl::activate
' 189 object id (cl-smoke.qt-impl::method-arguments-type meta-object id)
' 190 arguments)))
' 191
' 192 (defmacro call-next-qmethod ()
' 193 "Calls the next method."
' 194 '(call-next-method))
' 195
' 196
' 197 ;;; Enum values
' 198 ;;;
' 199 (defun primitive-value (enum)
' 200 "Returns the integer value of ENUM."
' 201 (cxx-support:value enum))
' 202
' 203 ;;; Type disambiguation
' 204 ;;;
' 205 ;; No-op since we have overload resolution.
' 206 (setf (fdefinition 'uint) #'identity
' 207 (fdefinition 'int) #'identity)
' 208
' 209 (defun find-qclass (class-name)
' 210 "Returns the CLOS class for the C++ CLASS-NAME string."
' 211 (find-class (smoke::lispify class-name :cl-smoke.qt)))