modular smoke & test system.
Annotate for file /src/commonqt.lisp
2010-01-10 tobias 1 ;;; Copyright (C) 2009, 2010 Tobias Rautenkranz <tobias@rautenkranz.ch>
2009-07-24 tobias 2 ;;;
13:39:55 ' 3 ;;; This program is free software: you can redistribute it and/or modify
' 4 ;;; it under the terms of the GNU General Public License as published by
' 5 ;;; the Free Software Foundation, either version 3 of the License, or
' 6 ;;; (at your option) any later version.
' 7 ;;;
' 8 ;;; This program is distributed in the hope that it will be useful,
' 9 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
' 10 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
' 11 ;;; GNU General Public License for more details.
' 12 ;;;
' 13 ;;; You should have received a copy of the GNU General Public License
' 14 ;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
' 15 ;;;
' 16 ;;; As a special exception, the copyright holders of this library give you
' 17 ;;; permission to link this library with independent modules to produce an
' 18 ;;; executable, regardless of the license terms of these independent
' 19 ;;; modules, and to copy and distribute the resulting executable under
' 20 ;;; terms of your choice, provided that you also meet, for each linked
' 21 ;;; independent module, the terms and conditions of the license of that
' 22 ;;; module. An independent module is a module which is not derived from or
' 23 ;;; based on this library. If you modify this library, you may extend this
' 24 ;;; exception to your version of the library, but you are not obligated to
' 25 ;;; do so. If you do not wish to do so, delete this exception statement
' 26 ;;; from your version.
' 27
2009-07-02 tobias 28 (in-package :cl-smoke.commonqt)
17:34:07 ' 29
2010-01-10 tobias 30 ;; FIXME
08:53:29 ' 31 ;; There is a GLOBAL-SPACE class per module, for now we just use the :qt.core one.
' 32 ;; But it should be selected in the method call. e.g.: (#_qVersion "GlobalSpace")
' 33 (eval-when (:compile-toplevel :load-toplevel :execute)
' 34 (import 'cl-smoke.qt.core::global-space :cl-smoke.qt))
' 35
2009-07-24 tobias 36 ;;; initialization
13:39:55 ' 37 (defun ensure-smoke ())
' 38 (defun enable-syntax ())
' 39
2009-07-02 tobias 40 ;;; calling methods
2009-07-24 tobias 41 ;;;
13:39:55 ' 42 ;;; #_ reader macro
' 43 ;;;
' 44 ;;; FIXME: Return reference arguments using multiple return values.
2009-07-02 tobias 45 ;;;
17:34:07 ' 46 (eval-when (:load-toplevel :compile-toplevel :execute)
2009-07-24 tobias 47 (defun read-perserving-case (stream)
13:39:55 ' 48 (let ((*readtable* (copy-readtable nil)))
' 49 (setf (readtable-case *readtable*) :preserve)
' 50 (read stream)))
2009-07-02 tobias 51
2009-07-24 tobias 52 (defun cl-smoke-funcall-form (stream subchar arg)
13:39:55 ' 53 (declare (ignore subchar arg))
' 54 (let ((method (read-perserving-case stream)))
' 55 (if (string= "new" (symbol-name method))
' 56 `(lambda (&rest args)
' 57 (make-instance
' 58 ',(smoke::lispify (symbol-name (read-perserving-case stream)) :cl-smoke.qt)
' 59 :args args))
' 60 (let ((object (read stream)))
' 61 (if (stringp object)
' 62 (if (upper-case-p (char (symbol-name method) 0))
' 63 `(lambda ()
' 64 ,(smoke::lispify
' 65 (concatenate 'string
' 66 (if (string= "Qt" object)
' 67 "+"
' 68 (concatenate 'string
' 69 object
' 70 ".+"))
' 71 (symbol-name method) "+")
' 72 :cl-smoke.qt))
' 73 `(lambda (&rest args)
' 74 (apply (symbol-function ',(smoke::lispify (symbol-name method) :cxx))
' 75 (find-class ',(smoke::lispify object :cl-smoke.qt))
' 76 args)))
' 77 `(lambda (&rest args)
' 78 (apply (symbol-function ',(smoke::lispify (symbol-name method) :cxx))
' 79 ,object
' 80 args))))))))
2009-07-02 tobias 81
17:34:07 ' 82 (eval-when (:load-toplevel :compile-toplevel)
' 83 (set-dispatch-macro-character #\# #\_ #'cl-smoke-funcall-form))
' 84
' 85 ;;; Instantiating
' 86 ;;;
' 87 (defun new (instance &rest arguments)
' 88 "Calls the C++ construor for INSTANCE with ARGUMENTS."
2009-07-24 tobias 89 (assert (null-pointer-p (smoke:pointer instance))
13:39:55 ' 90 (instance)
' 91 "The instance ~A is already constructed." instance)
2009-07-02 tobias 92 (setf (slot-value instance 'smoke:pointer)
2010-01-10 tobias 93 (smoke::call-constructor (class-of instance) arguments))
2009-07-02 tobias 94 (smoke::set-binding instance)
17:34:07 ' 95 (smoke::take-ownership instance)
' 96 (smoke::add-object instance)
' 97 instance)
' 98
' 99 ;;; Connecting signals and slots
' 100 ;;;
' 101 (defun qsignal (name)
' 102 (cl-smoke.qt:qsignal name))
' 103 (defun qslot (name)
' 104 (cl-smoke.qt:qslot name))
' 105
' 106 ;;; QApplication
' 107 ;;;
' 108 (defun make-qapplication (&rest arguments)
' 109 "Returns a new QApplication instance with ARGUMENTS as command line
' 110 arguments argv. argv[0] is set to (LISP-IMPLEMNTATION-TYPE)."
2009-07-24 tobias 111 (let* ((argc (smoke:make-auto-pointer
2009-07-02 tobias 112 (cffi:foreign-alloc :int
2009-07-02 tobias 113 :initial-element (1+ (length arguments)))))
2009-07-24 tobias 114 (argv (smoke:make-auto-pointer
2009-07-02 tobias 115 (cffi:foreign-alloc :string
17:34:07 ' 116 :initial-contents
' 117 (cons (lisp-implementation-type) ;; arg0
2009-07-02 tobias 118 arguments))))
2009-07-02 tobias 119 (application (make-instance 'cl-smoke.qt:application :args
17:34:07 ' 120 (list argc argv))))
' 121 ;; argc & argv must remain valid during the lifetime of application.
' 122 (setf (cl-smoke.qt:property application 'arguments)
' 123 (cl-smoke.qt:make-lisp-variant (cons argc argv)))
' 124 application))
' 125
' 126 ;;; Overriding C++ methods
' 127 (defclass override-gf (standard-generic-function)
' 128 ((cxx-gf :initarg :cxx-gf
' 129 :reader cxx-gf))
' 130 (:metaclass closer-mop:funcallable-standard-class)
' 131 (:documentation "Redirects its methods to CXX-GF."))
' 132
' 133 (defmethod add-method ((gf override-gf) method)
' 134 (add-method (cxx-gf gf) method))
' 135
' 136 (defmethod remove-method ((gf override-gf) method)
' 137 (remove-method (cxx-gf gf) method))
' 138
' 139 ;;; Subclassing C++ & defining signals and slots
' 140 ;;;
' 141 (defclass qt-class (cxx:class)
2009-07-24 tobias 142 ((method-functions :accessor method-functions
13:39:55 ' 143 :documentation "The signals and slots.")
' 144 (meta-object :reader class-meta-object))
' 145 (:documentation "Metaclass for a class derived from QObject, that allows
' 146 adding signals and slots to the class."))
2009-07-02 tobias 147
2009-07-02 tobias 148 (defclass signal-slot-mixin ()
2009-07-24 tobias 149 ()
13:39:55 ' 150 (:documentation "Mixin for a class with metaclass QT-CLASS, that overwrites
' 151 the metaObject and qtMetacall methods."))
2009-07-02 tobias 152
2009-07-24 tobias 153 (defun setup-meta-object (class qt-superclass signals slots)
13:39:55 ' 154 (let ((methods (make-array (+ (length signals)
' 155 (length slots))
2009-07-02 tobias 156 :element-type 'function
17:34:07 ' 157 :initial-element #'identity))
' 158 (index 0))
2009-07-24 tobias 159 (dolist (signal-name signals)
2009-07-02 tobias 160 (setf (aref methods index)
2009-07-02 tobias 161 (let ((signal-name (first signal-name)))
21:49:43 ' 162 #'(lambda (this &rest args)
' 163 (apply #'emit-signal this signal-name args))))
2009-07-02 tobias 164 (incf index))
2009-07-24 tobias 165 (dolist (slot slots)
2009-07-02 tobias 166 (setf (aref methods index)
17:34:07 ' 167 (if (symbolp (second slot))
' 168 (second slot)
' 169 (eval `(function ,(second slot)))))
' 170 (incf index))
2009-07-24 tobias 171 (setf (method-functions class) methods))
2009-07-02 tobias 172 (let ((signals (mapcar #'(lambda (s) (make-slot-or-signal (first s)))
2009-07-24 tobias 173 signals))
2009-07-02 tobias 174 (slots (mapcar #'(lambda (s) (make-slot-or-signal (first s)))
2009-07-24 tobias 175 slots))
2009-07-02 tobias 176 (class-name (class-name class)))
17:34:07 ' 177 (setf (slot-value class 'meta-object)
' 178 (make-metaobject qt-superclass
' 179 (format nil "~A::~A"
' 180 (package-name (symbol-package class-name))
' 181 (symbol-name class-name))
' 182 nil
' 183 signals
' 184 slots))))
' 185
' 186 (defun setup-qt-class (qt-class next-method &rest args &key qt-superclass
' 187 direct-superclasses direct-default-initargs
2009-07-24 tobias 188 slots signals
2009-07-02 tobias 189 override &allow-other-keys)
17:34:07 ' 190 (dolist (method override)
' 191 (ensure-generic-function
' 192 (second method)
' 193 :generic-function-class (find-class 'override-gf)
' 194 :lambda-list '(this &rest args)
' 195 :cxx-gf (fdefinition (smoke::lispify (first method) :cxx))))
' 196 (apply next-method qt-class
' 197 :direct-default-initargs ;; the C++ instance is constructed with #'NEW
' 198 (cons `(:pointer (null-pointer) ,#'null-pointer)
' 199 direct-default-initargs)
' 200 :direct-superclasses
2009-07-24 tobias 201 (append (list (find-qclass (first qt-superclass))
13:39:55 ' 202 (find-class 'signal-slot-mixin))
' 203 direct-superclasses)
2009-07-02 tobias 204 args)
2009-07-24 tobias 205 (setup-meta-object qt-class (find-qclass (first qt-superclass))
13:39:55 ' 206 signals slots))
2009-07-02 tobias 207
2009-07-02 tobias 208 (defmethod cxx:meta-object ((this signal-slot-mixin))
2009-07-24 tobias 209 (class-meta-object (class-of this)))
2009-07-02 tobias 210
20:48:38 ' 211 (defmethod cxx:qt-metacall ((this signal-slot-mixin) call id arguments)
' 212 (let ((m-id (call-next-method)))
' 213 (if (< m-id 0)
' 214 m-id
' 215 (if (enum= call cl-smoke.qt:meta-object.+invoke-meta-method+)
' 216 (progn
2009-07-24 tobias 217 (when (< m-id (length (method-functions (class-of this))))
2009-07-02 tobias 218 (with-simple-restart
20:48:38 ' 219 (continue "Skip the method ~A of ~A."
' 220 (cxx:signature
' 221 (cxx:method (cxx:meta-object this) id))
' 222 this)
2009-07-24 tobias 223 (apply (aref (method-functions (class-of this)) m-id)
2009-07-02 tobias 224 this
2010-01-10 tobias 225 (cl-smoke.qt.core::arguments-to-lisp
2009-07-02 tobias 226 arguments
2010-01-10 tobias 227 (cl-smoke.qt.core::method-arguments-type this id)))))
2009-07-02 tobias 228 (- m-id
2009-07-24 tobias 229 (length (method-functions (class-of this)))))
2009-07-02 tobias 230 m-id))))
20:48:38 ' 231
2009-07-02 tobias 232 (defmethod initialize-instance :around ((class qt-class)
2009-08-27 tobias 233 &rest args &key &allow-other-keys)
2009-07-02 tobias 234 (apply #'setup-qt-class class #'call-next-method args))
2009-07-02 tobias 235
2009-08-27 tobias 236 (defmethod reinitialize-instance :around ((qt-class qt-class)
11:17:58 ' 237 &rest args &key &allow-other-keys)
2009-07-02 tobias 238 (apply #'setup-qt-class qt-class #'call-next-method args))
17:34:07 ' 239
' 240 (defun emit-signal (object signal-name &rest arguments)
' 241 "Emits the signal SIGNAL-NAME of OBJECT using ARGUMENTS."
2009-07-02 tobias 242 (declare (string signal-name))
2009-07-24 tobias 243 (let* ((meta-object (class-meta-object (class-of object)))
13:39:55 ' 244 (id (#_indexOfSignal meta-object
' 245 (#_normalizedSignature "QMetaObject"
' 246 signal-name))))
2009-07-02 tobias 247 (assert (>= id 0)
17:34:07 ' 248 ()
' 249 "No signal ~A of ~A." signal-name object)
2010-01-10 tobias 250 (cl-smoke.qt.core::activate
08:53:29 ' 251 object id (cl-smoke.qt.core::method-arguments-type object id)
2009-07-02 tobias 252 arguments)))
17:34:07 ' 253
' 254 (defmacro call-next-qmethod ()
' 255 "Calls the next method."
' 256 '(call-next-method))
' 257
' 258 ;;; Enum values
' 259 ;;;
' 260 (defun primitive-value (enum)
' 261 "Returns the integer value of ENUM."
' 262 (cxx-support:value enum))
' 263
' 264 ;;; Type disambiguation
' 265 ;;;
2009-07-02 tobias 266 (defun bool (value)
2009-07-24 tobias 267 "Returns true when VALUE is not 0 and false when it is 0."
13:39:55 ' 268 (not (zerop value)))
2009-07-02 tobias 269
2009-07-02 tobias 270 ;; No-op since we have overload resolution.
17:34:07 ' 271 (setf (fdefinition 'uint) #'identity
2009-07-02 tobias 272 (fdefinition 'int) #'identity
20:48:38 ' 273 (fdefinition 'qstring) #'identity)
2009-07-02 tobias 274
17:34:07 ' 275 (defun find-qclass (class-name)
' 276 "Returns the CLOS class for the C++ CLASS-NAME string."
' 277 (find-class (smoke::lispify class-name :cl-smoke.qt)))
2009-07-24 tobias 278
13:39:55 ' 279 ;;; Documentation
' 280 ;;;
' 281
' 282 (defun qapropos (name)
2010-01-10 tobias 283 (smoke::fgrep-classes cl-smoke.qt.core::*smoke-module* name)
08:53:29 ' 284 (smoke::fgrep-classes cl-smoke.qt.gui::*smoke-module* name)
' 285 (smoke::fgrep-methods cl-smoke.qt.core::*smoke-module* name)
' 286 (smoke::fgrep-methods cl-smoke.qt.gui::*smoke-module* name))
2009-07-24 tobias 287
13:39:55 ' 288 ;; FIXME: implement QDESCRIBE
' 289 (defun qdescribe (name)
' 290 (declare (ignore name))
' 291 (warn "QDESCRIBE not implemented."))