mewa fixes
[clinton/lisp-on-lines.git] / src / mewa / mewa.lisp
CommitLineData
579597e3 1(in-package :mewa)
2
3(defparameter *default-type* :ucw)
4
5;;; maps meta-model slot-types to slot-presentation
9d6c69fb 6(defparameter *slot-type-map*
38a016c7
DC
7 '(boolean mewa-boolean
8 string mewa-string
9 number mewa-currency
10 integer mewa-integer
d2dbe50f
DC
11 currency mewa-currency
12 generalized-boolean mewa-boolean))
579597e3 13
14;;; an alist of model-class-name . attributes
15;;; should really be a hash-table.
16(defvar *attribute-map* (list))
17
18;;; some utilities for merging plists
19
20(defun plist-nunion (new-props plist)
d0c40011 21 (loop for cons on new-props by #'cddr
579597e3 22 do (setf (getf plist (first cons)) (second cons))
23 finally (return plist)))
24
25(defun plist-union (new-props plist)
26 "Non-destructive version of plist-nunion"
27 (plist-nunion new-props (copy-list plist)))
28
29(defun gen-ptype (type)
30 (or (getf *slot-type-map* type) type))
31
32(defun gen-presentation-slots (instance)
33 (mapcar #'(lambda (x) (gen-pslot (cadr x)
34 (string (car x))
35 (car x)))
9d6c69fb 36 (meta-model:list-slot-types instance)))
579597e3 37
38
39(defun gen-pslot (type label slot-name)
40 (copy-list `(,(gen-ptype type)
41 :label ,label
42 :slot-name ,slot-name)))
43
44(defun gen-presentation-args (instance args)
45 (declare (ignore instance))
46 (if args args nil))
47
48
49(defun find-or-create-attributes (class-name)
50 "return an exisiting class attribute map or create one.
51
52A map is a cons of class-name . attributes.
8e6e6b56 53attributes is an alist keyed on the attribute name."
579597e3 54 (or (assoc class-name *attribute-map*)
55 (progn
56 (setf *attribute-map* (acons class-name (list (list)) *attribute-map*))
57 (assoc class-name *attribute-map*))))
58
59(defgeneric find-class-attributes (class))
60
61(defmethod find-class-attributes ((model t))
62 (find-or-create-attributes (class-name (class-of model))))
63
64(defmethod find-class-attributes ((model symbol))
65 (find-or-create-attributes model))
66
46cea8c8
DC
67(defmethod clear-class-attributes ((model t))
68 (setf (cdr (find-class-attributes model)) nil))
69
579597e3 70(defmethod add-attribute ((model t) name def)
71 (let ((map (find-class-attributes model)))
72 (setf (cdr map) (acons name def (cdr map)))))
73
74(defmethod find-attribute ((model t) name)
75 (assoc name (cdr (find-class-attributes model))))
76
77(defmethod (setf find-attribute) ((def list) (model t) name)
78 (let ((attr (find-attribute model name)))
79 (if attr
80 (prog2
81 (setf (cdr attr) def)
82 attr)
83 (prog2
84 (add-attribute model name def)
85 (find-attribute model name)))))
86
87(defmethod set-attribute ((model t) name definition &key (inherit t))
88 (setf (find-attribute model name)
89 (if inherit
90 (cons (car definition)
91 (plist-union (cdr definition)
92 (cddr (find-attribute model name))))
93 definition)))
94
e8e743d7 95(defmethod perform-set-attributes ((model t) definitions)
96 (dolist (def definitions)
97 (funcall #'set-attribute model (first def) (rest def))))
579597e3 98
34e8e2d6
DC
99(defmethod set-attribute-properties ((model t) attribute properties)
100 (let ((a (find-attribute model attribute)))
101 (if a
102 (setf (cddr a) (plist-nunion properties (cddr a)))
103 (error "Attribute ~A does not exist" attribute) )))
104
105(defmethod perform-set-attribute-properties ((model t) definitions)
106 (dolist (def definitions)
107 (funcall #'set-attribute-properties model (car def) (cdr def))))
108
109
19531fbd 110(defmethod default-attributes ((model t))
a6644385 111 "return the default attributes for a given model using the meta-model's meta-data"
7129498f 112 (append (mapcar #'(lambda (s)
113 (cons (car s)
114 (gen-pslot
1679abef 115 (if (meta-model:foreign-key-p model (car s))
38a016c7 116 'foreign-key
1679abef 117 (cadr s))
118 (string (car s)) (car s))))
119 (meta-model:list-slot-types model))
120 (mapcar #'(lambda (s)
38a016c7 121 (cons s (append (gen-pslot 'has-many (string s) s)
1679abef 122 `(:presentation
123 (make-presentation
124 ,model
125 :type :one-line)))))
19531fbd 126 (meta-model:list-has-many model))))
127
128(defmethod set-default-attributes ((model t))
8e6e6b56 129 "Set the default attributes for MODEL"
46cea8c8 130 (clear-class-attributes model)
19531fbd 131 (mapcar #'(lambda (x)
132 (setf (find-attribute model (car x)) (cdr x)))
133 (default-attributes model)))
134
135
579597e3 136(defgeneric attributes-getter (model))
137
19531fbd 138;;;presentations
139
140
141
579597e3 142(defcomponent mewa ()
38a016c7 143 ((instance :accessor instance :initarg :instance)
2cb4247d 144 (attributes
579597e3 145 :initarg :attributes
146 :accessor attributes
147 :initform nil)
148 (attributes-getter
149 :accessor attributes-getter
150 :initform #'get-attributes
151 :initarg :attributes-getter)
569ad9e6
DC
152 (attribute-slot-map
153 :accessor attribute-slot-map
154 :initform nil)
579597e3 155 (global-properties
156 :initarg :global-properties
157 :accessor global-properties
158 :initform nil)
159 (classes
160 :initarg :classes
161 :accessor classes
162 :initform nil)
163 (use-instance-class-p
164 :initarg :use-instance-class-p
165 :accessor use-instance-class-p
166 :initform t)
167 (initializedp :initform nil)
8e6e6b56 168 (modifiedp :accessor modifiedp :initform nil :initarg :modifiedp)
d1bb68e0 169 (modifications :accessor modifications :initform nil)))
579597e3 170
579597e3 171
172(defmethod attributes :around ((self mewa))
173 (let ((a (call-next-method)))
174 (or a (funcall (attributes-getter self) self))))
175
19531fbd 176(defgeneric get-attributes (mewa))
177
579597e3 178(defmethod get-attributes ((self mewa))
179 (if (instance self)
180 (append (meta-model:list-slots (instance self))
181 (meta-model:list-has-many (instance self)))
182 nil))
183
579597e3 184(defmethod find-instance-classes ((self mewa))
185 (mapcar #'class-name
186 (it.bese.arnesi.mopp:compute-class-precedence-list (class-of (instance self)))))
187
188(defmethod find-all-attributes ((self mewa))
189 (reduce #'append
190 (mapcar #'(lambda (x)
191 (cdr (find-class-attributes x)))
192 (classes self))))
193
194(defun make-attribute (&rest props &key type &allow-other-keys)
195 (remf props :type)
196 (cons (gensym) (cons type props)))
197
198
199(defmethod find-applicable-attributes ((self mewa))
200 (let ((all-attributes (find-all-attributes self)))
201 (flet ((gen-att (x) (let ((att (assoc x all-attributes)))
202 (when att
203 (setf (cddr att) (plist-union (global-properties self) (cddr att)))
204 att))))
205 (if (attributes self)
206 (remove 'nil
207 (mapcar #'(lambda (x)
208 (cond
209 ;;simple casee
210 ((symbolp x)
211 (gen-att x))
212 ;;if the car is a keyword then this is an inline def
213 ((and (listp x) (keywordp (car x)))
214 (let ((att (apply #'make-attribute x)))
215 (setf (cddr att)
216 (plist-union (cddr att) (global-properties self)))
217 att))
218 ;; if the plist has a :type
219 ((and (listp x) (getf (cdr x) :type))
220 (let ((new (cdr (apply #'make-attribute (cdr x))))
221 (def (gen-att (car x))))
222 (setf (cdr new) (plist-union (cdr new) (cddr def)))
223 (cons (car def) new)))
224 ;;finally if we are just overiding the props
225 ((and (listp x) (symbolp (car x)))
226 (let ((new (cdr (apply #'make-attribute (cdr x))))
227 (def (gen-att (car x))))
228 (setf (cdr new) (plist-union (cdr new) (cddr def)))
229 (cons (car def) (cons (second def) (cdr new)))))
230
231 )
232 )
233
234 (attributes self)))
235 all-attributes))))
236
569ad9e6
DC
237(defmethod find-slot-presentation-for-attribute ((self mewa) attribute)
238 (let ((class-name
5a4eea11
DC
239 (or (gethash (if (consp (second attribute))
240 (car (second attribute))
38a016c7
DC
241 (second attribute))
242 *presentation-slot-type-mapping*)
21d89532 243 (error "Can't find slot type for ~A in ~A" attribute *presentation-slot-type-mapping* ))))
569ad9e6 244
38a016c7
DC
245 (cons (first attribute) (apply #'make-instance
246 class-name
247 (append (cddr attribute) (list :parent self :size 30))))))
569ad9e6 248
579597e3 249(defmethod find-slot-presentations ((self mewa))
569ad9e6 250 (mapcar #'(lambda (a) (find-slot-presentation-for-attribute self a))
579597e3 251 (find-applicable-attributes self)))
252
569ad9e6
DC
253(defmethod find-attribute-slot ((self mewa) (attribute symbol))
254 (cdr (assoc attribute (attribute-slot-map self))))
579597e3 255
256(defmethod initialize-slots ((self mewa))
65792e79
DC
257 (when (instance self)
258 (when (use-instance-class-p self)
259 (setf (classes self)
260 (append (find-instance-classes self)
261 (classes self))))
262 (setf (attribute-slot-map self) (find-slot-presentations self))
263 (setf (slots self) (mapcar #'(lambda (x)(cdr x)) (attribute-slot-map self )))))
579597e3 264
19531fbd 265(defmethod make-presentation ((object t) &key (type :viewer) (initargs nil))
266 (let* ((p (make-instance 'mewa-object-presentation))
68a53dce 267 (a (progn (setf (slot-value p 'ucw::instance) object)
19531fbd 268 (initialize-slots p)
269 (assoc type (find-all-attributes p))))
5a4eea11 270 (i (apply #'make-instance (or (second a)
68a53dce
DC
271 ;; if we didnt find the type,
272 ;; use the symbol as a class.
273 (if (eql (symbol-package type)
274 (find-package 'keyword))
275 (symbol-name type)
276 type))
277 (plist-union initargs (cddr a)))))
19531fbd 278 (setf (slot-value i 'instance) object)
2acd3ba2 279 (initialize-slots i)
280 (setf (slot-value i 'initializedp) t)
19531fbd 281 i))
282
283
8e6e6b56
DC
284(defmethod initialize-slots-place ((place ucw::place) (mewa mewa))
285 (setf (slots mewa) (mapcar #'(lambda (x)
286 (prog1 x
287 (setf (component.place x) place)))
288 (slots mewa))))
289
68a53dce 290(arnesi:defmethod/cc call-component :before ((from standard-component) (to mewa))
579597e3 291 (unless (slot-value to 'initializedp)
292 (initialize-slots to))
293 (setf (slot-value to 'initializedp) t)
68a53dce
DC
294 (initialize-slots-place (component.place from) to)
295 to)
296
297
579597e3 298
299(defmacro call-presentation (object &rest args)
ae09804a 300 `(present-object ,object :presentation (make-presentation ,object ,@args)))
301
4e2ecf69
DC
302
303(defcomponent about-dialog (option-dialog)
304 ((body :initarg :body)))
305
306(defmethod render-on ((res response) (self about-dialog))
d75822e6
DC
307 (call-next-method)
308 (render-on res (slot-value self 'body)))
4e2ecf69 309
8e6e6b56
DC
310
311(defmethod instance-is-stored-p ((instance clsql:standard-db-object))
569ad9e6
DC
312 (slot-value instance 'clsql-sys::view-database))
313
314(defmethod instance-is-stored-p ((mewa mewa))
315 (instance-is-stored-p (instance mewa)))
8e6e6b56 316
ae09804a 317(defaction cancel-save-instance ((self mewa))
1679abef 318 (cond
8e6e6b56 319 ((instance-is-stored-p (instance self))
1679abef 320 (meta-model::update-instance-from-records (instance self))
321 (answer self))
322 (t (answer nil))))
ae09804a 323
324(defaction save-instance ((self mewa))
7129498f 325 (meta-model:sync-instance (instance self))
8e6e6b56
DC
326 (setf (modifiedp self) nil)
327 (answer self))
ae09804a 328
329
12dcf3d4 330(defaction ensure-instance-sync ((self mewa))
ae09804a 331 (when (modifiedp self)
8e6e6b56 332 (let ((message (format nil "Record has been modified, Do you wish to save the changes?")))
4e2ecf69
DC
333 (case (call 'about-dialog
334 :body (make-presentation (instance self)
335 :type :viewer)
ae09804a 336 :message message
337 :options '((:save . "Save changes to Database")
338 (:cancel . "Cancel all changes")))
339 (:cancel
340 (cancel-save-instance self))
341 (:save
12dcf3d4 342 (save-instance self))))))
ae09804a 343
e8e743d7 344(defaction ok ((self mewa) &optional arg)
345 "Returns the component if it has not been modified. if it has been, prompt user to save or cancel"
68a53dce
DC
346 ;(declare (ignore arg))
347 (meta-model::sync-instance (instance self))
348 (answer (instance self)))
e8e743d7 349
ae09804a 350(defmethod (setf presentation-slot-value) :around (value (slot slot-presentation) instance)
351 (let* ((old (prog1
352 (presentation-slot-value slot instance)
353 (call-next-method)))
354 (new (presentation-slot-value slot instance)))
355
356 (unless (equal new old )
357 (let ((self (ucw::parent slot)))
358 (setf (modifiedp self) instance
4e2ecf69 359 (modifications self) (append (list new old value slot instance) (modifications self)))))))
233380f7 360
ab7ef8e9
DC
361;;;; * Finally set up some defaults
362
363(setf (find-attribute t :viewer)
364 '(mewa-object-presentation :global-properties (:editablep nil))
365 (find-attribute t :editor)
366 '(mewa-object-presentation :global-properties (:editablep t))
65792e79
DC
367 (find-attribute t :creator)
368 '(mewa-object-presentation :global-properties (:editablep t))
ab7ef8e9 369 (find-attribute t :one-line)
65792e79 370 '(mewa-one-line-presentation)
ab7ef8e9 371 (find-attribute t :listing)
65792e79
DC
372 '(mewa-list-presentation :global-properties (:editablep nil) :editablep t)
373 (find-attribute t :search-model)
ab7ef8e9
DC
374 '(mewa-object-presentation))
375
376
377
378
233380f7 379
380;; This software is Copyright (c) Drew Crampsie, 2004-2005.
381;; You are granted the rights to distribute
382;; and use this software as governed by the terms
383;; of the Lisp Lesser GNU Public License
384;; (http://opensource.franz.com/preamble.html),
385;; known as the LLGPL.