Drastic reworking of MEWA code using contextL. also declaimed for space, as this...
[clinton/lisp-on-lines.git] / src / mewa.lisp
CommitLineData
5dea194e 1(in-package :lisp-on-lines)
579597e3 2
3(defparameter *default-type* :ucw)
4
15bc66bd
DC
5;;;; I think these are unused now
6(defmethod perform-set-attributes ((occurence-name t) definitions)
7 (dolist (def definitions)
8 (funcall #'set-attribute occurence-name (first def) (rest def))))
9
10(defmethod perform-set-attribute-properties ((occurence-name t) definitions)
11 (dolist (def definitions)
12 (funcall #'set-attribute-properties occurence-name (car def) (cdr def))))
13
14;;;; PLIST Utilities.
579597e3 15
16(defun plist-nunion (new-props plist)
15bc66bd
DC
17 "Destructive Merge of plists. PLIST is modified and returned.
18NEW-PROPS is merged into PLIST such that any properties
19in both PLIST and NEW-PROPS get the value in NEW-PROPS.
20The other properties in PLIST are left untouched."
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
579597e3 29
15bc66bd
DC
30;;;; * Occurences
31
32(defvar *occurence-map* (make-hash-table)
33 "Presentations are created by associating an 'occurence'
34with an instance of a class. This is usually keyed off class-name,
35although an arbitrary occurence can be used with an arbitrary class.")
36
37(define-layered-class
38 standard-occurence ()
39 ((attribute-map :accessor attribute-map :initform (make-hash-table)))
40 (:documentation
41 "an occurence holds the attributes like a class holds slot-definitions.
42Attributes are the metadata used to display, validate, and otherwise manipulate actual values stored in lisp objects."))
43
44(defun find-or-create-occurence (name)
45 "Returns the occurence associated with this name."
46 (let ((occurence (gethash name *occurence-map*)))
47 (if occurence
48 occurence
49 (let ((new-occurence (make-instance 'standard-occurence)))
50 (setf (gethash name *occurence-map*) new-occurence)
51 new-occurence))))
52
53(defun clear-occurence (occurence)
54 "removes all attributes from the occurence"
55 (setf (attribute-map occurence) (make-hash-table)))
56
57(defgeneric find-occurence (name)
58 (:method ((name symbol))
59 (find-or-create-occurence name))
60 (:method (instance)
61 (find-or-create-occurence (class-name (class-of instance)))))
62
63
64;;;; * Attributes
65
66(define-layered-class
67 standard-attribute ()
68 ((name :layered-accessor attribute.name :initarg :name :initform "attribute")
69 (type :layered-accessor attribute.type :initarg :type :initform t :type symbol)
70 (plist :layered-accessor attribute.plist :initarg :plist :initform nil))
71 (:documentation "Attributes are used to display a part of a thing, such as a slot of an object, a text label, the car of a list, etc."))
72
73
74(defmethod print-object ((self standard-attribute) stream)
75 (print-unreadable-object (self stream :type t)
76 (with-slots (name type) self
77 (format stream "~A ~A" name type))))
78
79(define-layered-class
80 presentation-attribute (standard-attribute)
81 ()
82 (:documentation "Presentation Attributes are used to display objects
83using the attributes defined in an occurence. Presentation Attributes are always named using keywords."))
84
85(defun clear-attributes (name)
86 "removes all attributes from an occurance"
87 (clear-occurence (find-occurence name)))
88
89(defmethod find-attribute-class-for-name (name)
90 "presentation attributes are named using keywords"
91 (if (keywordp name)
92 'presentation-attribute
93 'standard-attribute))
94
95(defmethod ensure-attribute ((occurence standard-occurence) name type plist)
96 "Creates an attribute in the given occurence"
97 (setf (gethash name (attribute-map occurence))
98 (make-instance (find-attribute-class-for-name name)
99 :name name :type type :plist plist)))
100
101(defmethod find-attribute ((occurence standard-occurence) name)
102 (gethash name (attribute-map occurence)))
103
104(defmethod find-all-attributes ((occurence standard-occurence))
105 (loop for att being the hash-values of (attribute-map occurence)
106 collect att))
107
108(defmethod ensure-attribute (occurence-name name type plist)
109 (ensure-attribute
110 (find-occurence occurence-name)
111 name
112 type
113 plist))
114
115;;;; The following functions make up the public interface to the
116;;;; MEWA Attribute Occurence system.
117
118(defmethod find-all-attributes (occurence-name)
119 (find-all-attributes (find-occurence occurence-name)))
120
121(defmethod find-attribute (occurence-name attribute-name)
122 "Returns the ATTRIBUTE named by ATTRIBUTE-NAME in OCCURANCE-name"
123 (find-attribute (find-occurence occurence-name) attribute-name))
124
125(defmethod (setf find-attribute) ((def list) occurence-name attribute-name)
126 (ensure-attribute occurence-name attribute-name (first def) (rest def)))
127
128(defmethod set-attribute (occurence-name attribute-name definition &key (inherit t))
129 (let ((att (find-attribute occurence-name attribute-name)))
130 (setf (find-attribute occurence-name attribute-name)
131 (if (and att inherit)
579597e3 132 (cons (car definition)
133 (plist-union (cdr definition)
15bc66bd
DC
134 (attribute.plist att)))
135 definition))))
579597e3 136
15bc66bd
DC
137(defmethod set-attribute-properties ((occurence-name t) attribute properties)
138 (let ((a (find-attribute occurence-name attribute)))
34e8e2d6 139 (if a
15bc66bd
DC
140 (setf (attribute.plist a) (plist-nunion properties (attribute.plist a)))
141 (error "Attribute ~A does not exist" attribute))))
fc3e754f 142
15bc66bd 143(defmethod perform-define-attributes ((occurence-name t) attributes)
fc3e754f
DC
144 (loop for attribute in attributes
145 do (destructuring-bind (name type &rest args)
146 attribute
147 (cond ((eq type t)
148 ;;use the existing (default) type
15bc66bd 149 (set-attribute-properties occurence-name name args))
fc3e754f
DC
150 ((not (null type))
151 ;;set the type as well
15bc66bd 152 (set-attribute occurence-name name (cons type args)))))))
fc3e754f 153
15bc66bd 154(defmacro define-attributes (occurence-names &body attribute-definitions)
fc3e754f 155 `(progn
15bc66bd
DC
156 ,@(loop for occurence-name in occurence-names
157 collect `(perform-define-attributes (quote ,occurence-name) (quote ,attribute-definitions)))))
158
159
160(defmethod setter (attribute)
161 (let ((setter (getf (attribute.plist attribute) :setter))
162 (slot-name (getf (attribute.plist attribute) :slot-name)))
163 (cond (setter
164 setter)
165 (slot-name
166 #'(lambda (value object)
167 (setf (slot-value object slot-name) value)))
168 (t
169 #'(lambda (value object)
170 (warn "Can't find anywere to set ~A in ~A using ~A" value object attribute))))))
171
172(defmethod getter (attribute)
173 (let ((getter (getf (attribute.plist attribute) :getter))
174 (slot-name (getf (attribute.plist attribute) :slot-name)))
175 (cond (getter
176 getter)
177 (slot-name
178 #'(lambda (object)
179 (when (slot-boundp object slot-name)
180 (slot-value object slot-name)))))))
181
182(defgeneric attribute-value (instance attribute)
183 (:method (instance (attribute standard-attribute))
184 (funcall (getter attribute) instance)))
d5e996b3 185
15bc66bd
DC
186(defgeneric (setf attribute-value) (value instance attribute)
187 (:method (value instance (attribute standard-attribute))
188 (funcall (setter attribute) value instance)))
d5e996b3
DC
189
190
191;;;; ** Default Attributes
192
193
194;;;; The default mewa class contains the types use as defaults.
195;;;; maps meta-model slot-types to slot-presentation
196
197(defvar *default-attributes-class-name* 'default)
198
15bc66bd
DC
199(defmacro with-default-attributes ((occurence-name) &body body)
200 `(let ((*default-attributes-class-name* ',occurence-name))
201 ,@body))
202
d5e996b3
DC
203(define-attributes (default)
204 (boolean mewa-boolean)
205 (string mewa-string)
206 (number mewa-currency)
207 (integer mewa-integer)
208 (currency mewa-currency)
209 (clsql:generalized-boolean mewa-boolean)
210 (foreign-key foreign-key)
211 (:viewer mewa-viewer)
212 (:editor mewa-editor)
213 (:creator mewa-creator)
63c06c54 214 (:as-string mewa-one-line-presentation)
d5e996b3
DC
215 (:one-line mewa-one-line-presentation)
216 (:listing mewa-list-presentation :global-properties (:editablep nil) :editablep t)
217 (:search-model mewa-object-presentation))
218
15bc66bd
DC
219(defun find-presentation-attributes (occurence-name)
220 (loop for att in (find-all-attributes occurence-name)
221 when (typep att 'presentation-attribute)
222 collect att))
d5e996b3 223
15bc66bd
DC
224(defun attribute-to-definition (attribute)
225 (nconc (list (attribute.name attribute)
226 (attribute.type attribute))
227 (attribute.plist attribute)))
d5e996b3 228
15bc66bd
DC
229(defun find-default-presentation-attribute-definitions ()
230 (if (eql *default-attributes-class-name* 'default)
231 (mapcar #'attribute-to-definition (find-presentation-attributes 'default))
232 (remove-duplicates (mapcar #'attribute-to-definition
233 (append
234 (find-presentation-attributes 'default)
235 (find-presentation-attributes
236 *default-attributes-class-name*))))))
d5e996b3 237(defun gen-ptype (type)
15bc66bd
DC
238 (let* ((type (if (consp type) (car type) type))
239 (possible-default (find-attribute *default-attributes-class-name* type))
240 (real-default (find-attribute 'default type)))
241 (cond
242 (possible-default
243 (attribute.type possible-default))
244 (real-default
245 (attribute.type real-default))
246 (t type))))
d5e996b3
DC
247
248(defun gen-presentation-slots (instance)
249 (mapcar #'(lambda (x) (gen-pslot (cadr x)
250 (string (car x))
251 (car x)))
252 (meta-model:list-slot-types instance)))
253
34e8e2d6 254
d5e996b3
DC
255(defun gen-pslot (type label slot-name)
256 (copy-list `(,(gen-ptype type)
257 :label ,label
258 :slot-name ,slot-name)))
259
d5e996b3 260(defmethod find-default-attributes ((model t))
a6644385 261 "return the default attributes for a given model using the meta-model's meta-data"
7129498f 262 (append (mapcar #'(lambda (s)
263 (cons (car s)
264 (gen-pslot
1679abef 265 (if (meta-model:foreign-key-p model (car s))
38a016c7 266 'foreign-key
1679abef 267 (cadr s))
268 (string (car s)) (car s))))
269 (meta-model:list-slot-types model))
270 (mapcar #'(lambda (s)
38a016c7 271 (cons s (append (gen-pslot 'has-many (string s) s)
1679abef 272 `(:presentation
273 (make-presentation
274 ,model
275 :type :one-line)))))
d5e996b3 276 (meta-model:list-has-many model))
15bc66bd 277 (find-default-presentation-attribute-definitions)))
19531fbd 278
279(defmethod set-default-attributes ((model t))
8e6e6b56 280 "Set the default attributes for MODEL"
15bc66bd 281 (clear-attributes model)
19531fbd 282 (mapcar #'(lambda (x)
283 (setf (find-attribute model (car x)) (cdr x)))
d5e996b3 284 (find-default-attributes model)))
579597e3 285
19531fbd 286;;;presentations
579597e3 287(defcomponent mewa ()
38a016c7 288 ((instance :accessor instance :initarg :instance)
2cb4247d 289 (attributes
579597e3 290 :initarg :attributes
291 :accessor attributes
292 :initform nil)
293 (attributes-getter
294 :accessor attributes-getter
295 :initform #'get-attributes
296 :initarg :attributes-getter)
569ad9e6
DC
297 (attribute-slot-map
298 :accessor attribute-slot-map
299 :initform nil)
579597e3 300 (global-properties
301 :initarg :global-properties
302 :accessor global-properties
303 :initform nil)
304 (classes
305 :initarg :classes
306 :accessor classes
307 :initform nil)
308 (use-instance-class-p
309 :initarg :use-instance-class-p
310 :accessor use-instance-class-p
311 :initform t)
312 (initializedp :initform nil)
8e6e6b56 313 (modifiedp :accessor modifiedp :initform nil :initarg :modifiedp)
d1bb68e0 314 (modifications :accessor modifications :initform nil)))
579597e3 315
579597e3 316
317(defmethod attributes :around ((self mewa))
318 (let ((a (call-next-method)))
319 (or a (funcall (attributes-getter self) self))))
320
19531fbd 321(defgeneric get-attributes (mewa))
322
579597e3 323(defmethod get-attributes ((self mewa))
324 (if (instance self)
325 (append (meta-model:list-slots (instance self))
326 (meta-model:list-has-many (instance self)))
327 nil))
328
579597e3 329(defmethod find-instance-classes ((self mewa))
330 (mapcar #'class-name
331 (it.bese.arnesi.mopp:compute-class-precedence-list (class-of (instance self)))))
332
579597e3 333(defun make-attribute (&rest props &key type &allow-other-keys)
334 (remf props :type)
335 (cons (gensym) (cons type props)))
336
15bc66bd
DC
337(defun make-presentation-for-attribute-list-item
338 (occurence att-name plist parent-presentation &optional type)
339 (declare (type list plist) (type symbol att-name))
340 "This is a ucw specific function that will eventually be factored elsewhere."
341 (let* ((attribute (find-attribute occurence att-name))
342 (type (when attribute (or type (attribute.type attribute))))
343 (class-name
344 (or (gethash (if (consp type)
345 (car type)
346 type)
347 *presentation-slot-type-mapping*)
348 (error "Can't find slot type for ~A in ~A from ~A" att-name occurence parent-presentation))))
349
350 (cons (attribute.name attribute) (apply #'make-instance
351 class-name
352 (append (plist-nunion
353 plist
354 (plist-union
355 (global-properties parent-presentation)
356 (attribute.plist attribute)))
357 (list :size 30 :parent parent-presentation))))))
358
359(defmethod find-applicable-attributes-using-attribute-list (occurence attribute-list)
360 "Returns a list of functions that, when called with an object presentation,
361returns the ucw slot presentation that will be used to present this attribute
362in that object presentation."
363 (loop for att in attribute-list
364 with funs = (list)
365 do (let ((att att)) (cond
366 ;;simple casee
367 ((symbolp att)
368 (push #'(lambda (p)
369 (make-presentation-for-attribute-list-item occurence att nil p))
370 funs))
371 ;;if the car is a keyword then this is an inline def
372 ;; drewc nov 12 2005:
373 ;; i never used this, and never told anybody about it.
374 ;; removing it.
375 #+ (or) ((and (listp x) (keywordp (car x)))
376 (let ((att (apply #'make-attribute x)))
377 (setf (cddr att)
378 (plist-union (cddr att) (global-properties self)))
379 att))
380
381 ;; if the plist has a :type
382 ((and (listp att) (getf (cdr att) :type))
383 (let ((type (getf (cdr att) :type)))
384 (push #'(lambda (p)
385 (make-presentation-for-attribute-list-item
386 occurence (first att)
387 (cdr att)
388 p
389 type))
390 funs)))
391 ;;finally if we are just overiding the props
392 ((and (listp att) (symbolp (car att)))
393 (push #'(lambda (p)
394 (make-presentation-for-attribute-list-item occurence (first att) (rest att) p))
395 funs))))
396 finally (return (nreverse funs))))
397
398
399(defun find-attribute-names (mewa)
400 (mapcar #'(lambda (x)
401 (if (listp x)
402 (first x)
403 x))
404 (attributes mewa)))
579597e3 405
406(defmethod find-applicable-attributes ((self mewa))
15bc66bd
DC
407 (if (attributes self)
408 (find-applicable-attributes-using-attribute-list (instance self) (attributes self))
409 (find-applicable-attributes-using-attribute-list (instance (get-attributes self)))))
410
569ad9e6 411
579597e3 412(defmethod find-slot-presentations ((self mewa))
15bc66bd 413 (mapcar #'(lambda (a) (funcall a self))
579597e3 414 (find-applicable-attributes self)))
415
569ad9e6
DC
416(defmethod find-attribute-slot ((self mewa) (attribute symbol))
417 (cdr (assoc attribute (attribute-slot-map self))))
579597e3 418
419(defmethod initialize-slots ((self mewa))
65792e79
DC
420 (when (instance self)
421 (when (use-instance-class-p self)
422 (setf (classes self)
423 (append (find-instance-classes self)
424 (classes self))))
425 (setf (attribute-slot-map self) (find-slot-presentations self))
426 (setf (slots self) (mapcar #'(lambda (x)(cdr x)) (attribute-slot-map self )))))
cf5da3ed
DC
427
428
19531fbd 429(defmethod make-presentation ((object t) &key (type :viewer) (initargs nil))
598f1fa8 430 ;(warn "Initargs : ~A" initargs)
15bc66bd
DC
431 (let* ((a (find-attribute object type))
432 (i (apply #'make-instance
433 (if a
434 (attribute.type a)
435 type)
436 (plist-union initargs (when a
437 (attribute.plist a))))))
0fd9d744 438
19531fbd 439 (setf (slot-value i 'instance) object)
2acd3ba2 440 (initialize-slots i)
441 (setf (slot-value i 'initializedp) t)
19531fbd 442 i))
443
5dea194e 444(defmethod make-presentation ((list list) &key (type :listing) (initargs nil))
cf5da3ed
DC
445 (let ((args (append
446 `(:type ,type)
447 `(:initargs
448 (:instances ,list
449 ,@initargs)))))
450
451 (apply #'make-presentation (car list) args)))
19531fbd 452
8e6e6b56
DC
453(defmethod initialize-slots-place ((place ucw::place) (mewa mewa))
454 (setf (slots mewa) (mapcar #'(lambda (x)
455 (prog1 x
456 (setf (component.place x) place)))
457 (slots mewa))))
458
68a53dce 459(arnesi:defmethod/cc call-component :before ((from standard-component) (to mewa))
579597e3 460 (unless (slot-value to 'initializedp)
461 (initialize-slots to))
462 (setf (slot-value to 'initializedp) t)
68a53dce
DC
463 (initialize-slots-place (component.place from) to)
464 to)
465
466
579597e3 467
468(defmacro call-presentation (object &rest args)
ae09804a 469 `(present-object ,object :presentation (make-presentation ,object ,@args)))
470
4e2ecf69
DC
471
472(defcomponent about-dialog (option-dialog)
473 ((body :initarg :body)))
474
475(defmethod render-on ((res response) (self about-dialog))
d75822e6
DC
476 (call-next-method)
477 (render-on res (slot-value self 'body)))
4e2ecf69 478
569ad9e6 479
13ada38f 480
8e6e6b56 481
ae09804a 482(defaction cancel-save-instance ((self mewa))
1679abef 483 (cond
5dea194e 484 ((meta-model::persistentp (instance self))
1679abef 485 (meta-model::update-instance-from-records (instance self))
486 (answer self))
487 (t (answer nil))))
ae09804a 488
489(defaction save-instance ((self mewa))
7129498f 490 (meta-model:sync-instance (instance self))
8e6e6b56
DC
491 (setf (modifiedp self) nil)
492 (answer self))
ae09804a 493
d5e996b3
DC
494(defmethod confirm-sync-instance ((self mewa))
495 nil)
ae09804a 496
12dcf3d4 497(defaction ensure-instance-sync ((self mewa))
ae09804a 498 (when (modifiedp self)
d5e996b3
DC
499 (if nil
500 (let ((message (format nil "Record has been modified, Do you wish to save the changes?")))
501 (case (call 'about-dialog
502 :body (make-presentation (instance self)
503 :type :viewer)
504 :message message
505 :options '((:save . "Save changes to Database")
506 (:cancel . "Cancel all changes")))
507 (:cancel
508 (cancel-save-instance self))
509 (:save
510 (save-instance self))))
511 (save-instance self))))
512
513(defaction sync-and-answer ((self mewa))
514 (ensure-instance-sync self)
515 (answer (instance self)))
ae09804a 516
e8e743d7 517(defaction ok ((self mewa) &optional arg)
518 "Returns the component if it has not been modified. if it has been, prompt user to save or cancel"
68a53dce 519 ;(declare (ignore arg))
d5e996b3 520 (sync-and-answer self))
e8e743d7 521
ae09804a 522(defmethod (setf presentation-slot-value) :around (value (slot slot-presentation) instance)
523 (let* ((old (prog1
524 (presentation-slot-value slot instance)
525 (call-next-method)))
526 (new (presentation-slot-value slot instance)))
527
528 (unless (equal new old )
529 (let ((self (ucw::parent slot)))
530 (setf (modifiedp self) instance
4e2ecf69 531 (modifications self) (append (list new old value slot instance) (modifications self)))))))
233380f7 532
d5e996b3 533
ab7ef8e9
DC
534
535
536
537
233380f7 538
539;; This software is Copyright (c) Drew Crampsie, 2004-2005.
540;; You are granted the rights to distribute
541;; and use this software as governed by the terms
542;; of the Lisp Lesser GNU Public License
543;; (http://opensource.franz.com/preamble.html),
544;; known as the LLGPL.