;;; -*- Mode:LISP; Package:USER; Fonts:(MEDFNT MEDFNB); Readtable:CL; Base:10 -*- 1;;; DEFOBEDIT.LISP* ;;; Author: Keith Corbett, 6/23/87. Copyright GigaMOS Systems Inc., 1987 1;;; Provide an ObjectLISP interfac*e1 to TV:CHOOSE-VARIABLE-VALUES, the* 1window ;;; system popup menu for editing with a variable list. See the window ;;; system* 1manual for documentation on variable types, etc.* ;;; Following are the steps required to use an editable object, with example code at ;;; each step. ;;; 1. To make an object class editable, you must DEFKIND it (or a class from which ;;; it inherits) as an EDITABLE-THING. ;;; (defkind foothing other-things editable-thing) ;;; (definstancevars foothing (name "") foo-p) ;;; 2. Each editable class is defined with its own class variable, EDITLIST, which is ;;; its contributing portion of the choose-variable-values 'alist'. Each element of ;;; the EDITLIST is either a variable name or a sublist which has the form: ;;; ( ;;; The class or instance binding of EDITLABEL will appear as the menu label. ;;; (defclassvars foothing ;;; (editlist '((name "Name" :string) ;;; (foo-p "Is this a foo?" :choose (t nil)))) ;;; (editlabel "Edit this FOO object")) ;;; The EDITLABEL can also be specified for an instance by supplying the keyword ;;; argument :LABEL to ONEOF or EXIST. ;;; 3. Each class which has an EDITLIST must also define its own obfun which makes it ;;; EDITABLE; to do this, call DEFOBEDIT on the class name. ;;; (defobedit foothing) ;;; 4. To edit an instance of the class (it must first 'exist') call the obfun EDIT. ;;; EDIT returns T if the user proceeded through the menu; two values, NIL and ;;; :ABORT, are returned if the menu was aborted. ;;; (ask (setq foo (oneof foothing)) (edit)) ;;; 5. Each instance possesses its own EDIT-FUNC, an instance variable which is the ;;; object-specific function which EDIT calls. The advantage of this redundancy is ;;; that each instance can manipulate its own EDITLIST, which for an instance is the ;;; accumulated total EDITLIST of each editable class it has inherited from. ;;; Since the EDITLIST is inherited from each class, duplicate EQUAL elements are ;;; removed from the end, but watch out for multiple (redundant) inheritances, since ;;; the edit list might not be what you want, and surgery could be required. ;;; (ask foo (have 'stuff nil)) ;;; (ask foo (push '(stuff "More stuff" :sexp) editlist)) ;;; (ask foo (edit)) (in-package 'user) (eval-when(eval compile) (unintern 'editable-thing)) (defkind editable-thing) (defclassvars editable-thing editlist editlabel) (definstancevars editable-thing (edit-func #'identity)) (defobfun editable ()() nil) (defobfun (editable-label editable-thing) () (string-append "Edit " class-name " class object")) (defobfun (editable editable-thing) (&optional edit) (have 'editlist (remove-duplicates edit :key #'zl:car-safe :test #'(lambda(x y) (and x y (eq x y))) :from-end nil)) editlist) (defmacro defobedit(class) "Setup CLASS, an EDITABLE-THING, for editing objects via TV:CHOOSE-VARIABLE-VALUES. Defines EDITABLE which rolls up the class EDITLIST variable(s) for the instance." `(defobfun(editable ,class)(&optional edit) (funcall #'shadowed-editable (append (ask ,class editlist) edit)))) (defobfun (editfunc editable-thing) () (let((vars (remove nil (mapcar #'car-safe (ask (current-obj) editlist)))) (func (intern(gensym)))) (eval `(defobfun (,func editable-thing) () (let(,@(mapcar #'list vars vars)) (declare(special ,@vars)) (catch 'abort (tv:choose-variable-values ',editlist :label editlabel :margin-choices '("Continue" ("Abort" (throw 'abort (values nil :abort)))) ) ,@(mapcar #'(lambda (var val) (list 'ask '(current-obj) (list 'have (list 'quote var) val))) vars vars) t)))) (have 'edit-func func) func)) (defobfun (exist editable-thing) (&rest args &key label &allow-other-keys) (apply #'shadowed-exist args) (editable) (have 'editlabel (or label editlabel (editable-label))) (editfunc)) (defobfun (edit editable-thing) () (funcall edit-func)) ;;; Display-only variable type ;;; You can specify a variable name in the editlist as type :DISPLAY-ONLY to display ;;; it but prevent it from being edited (defprop :display-only (princ nil nil nil nil "Display only; value cannot be changed") tv:choose-variable-values-keyword)