;;;; -*- lisp -*- ;; Copyright (c) 2003-2005 Edward Marco Baringer ;; All rights reserved. ;; ;; Redistribution and use in source and binary forms, with or without ;; modification, are permitted provided that the following conditions are ;; met: ;; ;; - Redistributions of source code must retain the above copyright ;; notice, this list of conditions and the following disclaimer. ;; ;; - Redistributions in binary form must reproduce the above copyright ;; notice, this list of conditions and the following disclaimer in the ;; documentation and/or other materials provided with the distribution. ;; ;; - Neither the name of Edward Marco Baringer, nor BESE, nor the names ;; of its contributors may be used to endorse or promote products ;; derived from this software without specific prior written permission. ;; ;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ;; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ;; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ;; A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ;; OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ;; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ;; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ;; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (in-package :org.unknownlamer.ucw-im) ;; we need this pair to delay execution of functions which contain yacml macros ;; before the right place (defun force (value) "Pseudo lazy-evaluation FORCE. Complementary to DELAY." (if (consp value) (case (car value) (:%delayed% (let ((vs (multiple-value-list (funcall (cdr value))))) (setf (car value) :%force-done% (cdr value) vs) (apply #'values vs))) (:%force-done% (values-list (cdr value))) (otherwise value)) value)) (defmacro delay (&rest forms) "Pseudo lazy-evaluation DELAY. Complementary to FORCE." `(cons :%delayed% #'(lambda () (progn ,@forms)))) (defmacro with-simple-restore (places &body forms) "Preserve values of setfable PLACES before execution of the FORMS and restore them after. Returns the result of the FORMS." (let ((keepers (mapcar #'(lambda (p) (declare (ignore p)) (gensym)) places))) `(let ,keepers (setf (values ,@keepers) (values ,@places)) (prog1 (progn ,@forms) (setf (values ,@places) (values ,@keepers)))))) ;; (eval-when (:compile-toplevel :load-toplevel :execute) ;; (defclass interface-element-class (standard-component-class ;; indirect-value-mixin-class) ;; () ;; (:documentation "Metaclass for interface elements."))) (define-ie-type (t t-element) (widget-component standard-component) ((view :accessor view :initarg :view :initform (make-instance 'ieview) :documentation "Element presentation control.") (label :accessor label :initarg :label :initform nil) (label-plural :accessor label-plural :initarg :label-plural :initform nil) (editablep :accessor editablep :initarg :editablep :initform t :documentation "T - render element with ACCEPT method NIL - render element with PRESENT method") (enabledp :accessor enabledp :initarg :enabledp :initform t :documentation "Has element be shown to a client. T - present element, NIL - don't present element.")) (:metaclass standard-component-class) (:documentation "Just a basic interface element")) (defmethod present :around ((element t-element) (view ieview)) (render-box element view (delay (render-value-box element view (delay (call-next-method)))))) (defmethod accept :around ((element t-element) (view ieview)) (render-box element view (delay (render-value-box element view (delay (call-next-method)))))) (defmethod render ((e t-element)) (when (enabledp e) (render-element (view e) e))) (defmethod render-box ((e t-element) (view ieview) value) (declare (ignore view e)) (force value)) (defmethod render-value-box ((e t-element) (view ieview) value) (declare (ignore view e)) (force value)) (defgeneric render-element (view element) (:method-combination wrapping-standard)) (defmethod render-element ((view ieview) (e t-element)) (when (labelp view) (render-label view e)) (funcall (if (editablep e) #'accept #'present) e view)) (defmethod render-label ((view ieview) (e t-element)) (awhen (ecase (labelp view) (:plural (or (label-plural e) "")) ((:singular t) (or (label e) "")) ((nil) nil)) (<:as-html (if (functionp it) (funcall it e) it)))) (defgeneric slot-elements (element &key ie-type test) (:documentation "Returns all the slots names of ELEMENT which are interface elements and optionally satisfied the TEST. TEST -- function of one argument (slot value).") (:method-combination nconc)) (defmethod slot-elements nconc ((e t-element) &key (ie-type 't) (test (constantly t))) (iterate (with interface-element-class = (ie-type-class ie-type)) (for slot in (mopp:class-slots (class-of e))) (for slot-name = (mopp:slot-definition-name slot)) (for element = (and (slot-boundp e slot-name) (slot-value e slot-name))) (when (and element (not (eq element (parent e))) (not (and (slot-boundp e 'it.bese.ucw::calling-component) (eq element (slot-value e 'it.bese.ucw::calling-component)))) (subtypep (class-of element) interface-element-class) (funcall test element)) (collect slot-name)))) (defgeneric clone-element (obj &rest initargs) (:documentation "Clone interface element.")) (defmethod clone-element ((obj t-element) &rest initargs) (let ((class (class-of obj)) copy-initargs) (setf copy-initargs (iterate (with ses = (slot-elements obj)) (for slot in (mopp:class-slots class)) (for slot-name = (mopp:slot-definition-name slot)) (for slot-initarg = (car (mopp:slot-definition-initargs slot))) (when (and slot-initarg ; skip slots without initarg (slot-indirect-boundp obj slot-name)) (let ((sv (slot-indirect-value obj slot-name))) (appending (list slot-initarg (cond ((member slot-name ses) (clone-element sv)) ((indirect-value-p sv) (copy-indirect-value sv)) (t sv) ; haven't other ideas ))))))) (apply #'make-instance class (append initargs copy-initargs)))) (define-ie-type (interface-element interface-element) ((ie-type t)) ((client-value :accessor client-value :initform "" :initarg :client-value :documentation "Whetever the client's browse sent for this interface element." :backtrack t) (direct-value :accessor direct-value :initform nil :initarg :direct-value :documentation "The current lisp object in this interface element." :backtrack t) (default-value :accessor default-value :initarg :default-value :initform nil :documentation "Default LISP-VALUE. If it is a function it is called with ELEMENT as argument.") (input-filter :accessor input-filter :initarg :input-filter :initform (lambda (value &optional element) (declare (ignore element)) value) :documentation "Function/symbol of two arguments. (input-filter client-value interface-element) => LISP-VALUE. Function can signal an uninterrupted error with SIGNAL-IE-BAD-INPUT CLIENT-VALUE REQUIRED-TYPE &optional CONDITION-TYPE CONDITION-TYPE -- :format | :type | nil.") (output-format :accessor output-format :initarg :output-format :initform #'(lambda (v &optional e) (declare (ignore e)) v) :documentation "Function/symbol of two arguments. OUTPUT-FORMAT LISP-VALUE INTERFACE-ELEMENT => CLIENT-VALUE.") (constraints :accessor constraints :initarg :constraints :initform nil :documentation "List of constraints => CONSTRAINT*. CONSTRAINT -- CONSTRAINT-FUNCTION | CONSTRAINT-KEYWORD. CONSTRAINT-FUNCTION -- function LISP-VALUE INTERFACE-ELEMENT => T | NIL CONSTRAINT-KEYWORD -- key for predefined constraint CONSTRAINT-FUNCTION can signal an uninterrupted error with SIGNAL-IE-CONSTRAINT-VIOLATION FORMAT-CONTROL &rest FORMAT-ARGS.") (initial-focus :accessor initial-focus :initarg :initial-focus :initform nil) (element-id :accessor element-id :initarg :element-id :initform (random-string 32))) (:metaclass interface-element-class) (:documentation "A single value in a form. A interface-element is, simply put, a wrapper for a value in an html form.")) (defgeneric coerce-client-value (element value) (:method ((e interface-element) (value string)) (if (= (length value) 0) nil value)) (:documentation "Coerce CLIENT-VALUE to LISP-VALUE type.")) (defgeneric validate-value (element value &key &allow-other-keys) (:method ((e interface-element) value &key &allow-other-keys) (every #'(lambda (c) (etypecase c (function (funcall c value e)) (keyword (funcall (ie-constraint c) value e)))) (constraints e))) (:documentation "Validate value with ELEMENT constraints. VALIDATE-VALUE ELEMENT VALUE => VALID VALID -- boolean.")) (defmethod present ((e interface-element) (view ieview)) "Render interface element fore viewing." (declare (ignore view)) (when (lisp-value e) (<:as-html (format-lisp-value e (lisp-value e))))) (defmethod accept ((e interface-element) (view ieview)) "Render interface element fore editing." (declare (ignore view)) (-results - Used for the div which wraps all the available completions. div#-results ul - Used for the UL which wraps the list of completions. div#-results ul li - Every completions is placed in the body of an LI tag, use this style to change the formatting of the single completions. li#-highlight - Use this style to change the formatting of a selected completions. In the above css selectors is the value of component's prefix slot. Customizing the UCWInputCompleter object (setting auto completion, minimum number of chars, etc.) is, unfortunetly, done by creating a new javascript class (in a new javascript file) and using that instead of UCWInputCompleter.js. In the near future this situation will improve.")) (defmethod accept ((e completing-text-element) (view ieview)) (declare (ignore view)) ( as opposed to