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