Update of /project/mcclim/cvsroot/mcclim/Doc In directory clnet:/tmp/cvs-serv12406
Modified Files: manual.tex Added Files: views.lisp Log Message: Added a new part (User manual) to the manual, and a first chapter (Using views) to that part.
--- /project/mcclim/cvsroot/mcclim/Doc/manual.tex 2005/08/22 02:49:05 1.31 +++ /project/mcclim/cvsroot/mcclim/Doc/manual.tex 2006/10/30 06:26:53 1.32 @@ -607,6 +607,78 @@ What is finally displayed (in the interactor pane, which is the standard input of the frame), is ``the 15 of The third month''.
+\part{User Manual} + +\chapter{Using views} + +The CLIM specification mentions a concept called a \emph{view}, and +also lists a number of predefined views to be used in various +different contexts. + +In this chapter we show how the \emph{view} concept can be used in +some concrete programming examples. In particular, we show how to use +a single pane to show different views of the application data +structure at different times. To switch between the different views, +we supply a set of commands that alter the +\texttt{stream-default-view} feature of all CLIM extended output +streams. + +The example shown here has been stripped to a bare minimum in order to +illustrate the important concepts. A more complete version can be +found in \texttt{Examples/views.lisp} in the McCLIM source tree. + +Here is the example: + +\verbatimtabinput{views.lisp} + +The example shows a stripped-down example of a simple database of +members of some organization. + +The main trick used in this example is the \texttt{display-main-pane} +function that is declared to be the display function of the main pane +in the application frame. The \texttt{display-main-pane} function +trampolines to a generic function called +\texttt{display-pane-with-view}, and which takes an additional +argument compared to the display functions of CLIM panes. This +additional argument is of type \texttt{view} which allows us to +dispatch not only on the type of frame and the type of pane, but also +on the type of the current default view. In this example the view +argument is simply taken from the default view of the pane. + +A possibility that is not obvious from reading the CLIM specification +is to have views that contain additional slots. Our example defines +two subclasses of the CLIM \texttt{view} class, namely +\texttt{members-view} and \texttt{person-view}. + +The first one of these does not contain any additional slots, and is +used when a global view of the members of our organization is wanted. +Since no instance-specific data is required in this view, we follow +the idea of the examples of the CLIM specification to instantiate a +singleton of this class and store that singleton in the +\texttt{stream-default-view} of our main pane whenever a global view +of our organization is required. + +The \texttt{person-view} class, on the other hand, is used when we +want a closer view of a single member of the organization. This class +therefore contains an additional slot which holds the particular +person instance we are interested in. The method on +\texttt{display-pane-with-view} that specializes on +\texttt{person-view} displays the data of the particular person that +is contained in the view. + +To switch between the views, we provide two commands. The command +\texttt{com-show-all} simply changes the default view of the main pane +to be the singleton instance of the \texttt{members-view} class. The +command \texttt{com-show-person} is more complicated. It takes an +argument of type person, creates an instance of the +\texttt{person-view} class initialized with the person that was passed +as an argument, and stores the instance as the default view of the +main pane. + +\chapter{Using command tables} + +(to be filled in) + \part{Reference Manual}
\chapter{Concepts}
--- /project/mcclim/cvsroot/mcclim/Doc/views.lisp 2006/10/30 06:26:53 NONE +++ /project/mcclim/cvsroot/mcclim/Doc/views.lisp 2006/10/30 06:26:53 1.1 ;;; part of application "business logic" (defclass person () ((%last-name :initarg :last-name :accessor last-name) (%first-name :initarg :first-name :accessor first-name) (%address :initarg :address :accessor address) (%membership-number :initarg :membership-number :reader membership-number)))
;;; constructor for the PERSON class. Not strictly necessary. (defun make-person (last-name first-name address membership-number) (make-instance 'person :last-name last-name :first-name first-name :address address :membership-number membership-number))
;;; initial list of members of the organization we imagine for this example (defparameter *members* (list (make-person "Doe" "Jane" "123, Glencoe Terrace" 12345) (make-person "Dupont" "Jean" "111, Rue de la Republique" 54321) (make-person "Smith" "Eliza" "22, Trafalgar Square" 121212) (make-person "Nilsson" "Sven" "Uppsalagatan 33" 98765)))
;;; the CLIM view class that corresponds to a list of members, one member ;;; per line of text in a CLIM application pane. (defclass members-view (view) ())
;;; since this view does not take any parameters in our simple example, ;;; we need only a single instance of it. (defparameter *members-view* (make-instance 'members-view))
;;; the application frame. It contains instance-specific data ;;; such as the members of our organization. (define-application-frame views () ((%members :initform *members* :accessor members)) (:panes (main-pane :application :height 500 :width 500 :display-function 'display-main-pane ;; notice the initialization of the default view of ;; the application pane. :default-view *members-view*) (interactor :interactor :height 100 :width 500)) (:layouts (default (vertically () main-pane interactor))))
;;; the trick here is to define a generic display function ;;; that is called on the frame, the pane AND the view, ;;; whereas the standard CLIM display functions are called ;;; only on the frame and the pane. (defgeneric display-pane-with-view (frame pane view))
;;; this is the display function that is called in each iteration ;;; of the CLIM command loop. We simply call our own, more elaborate ;;; display function with the default view of the pane. (defun display-main-pane (frame pane) (display-pane-with-view frame pane (stream-default-view pane)))
;;; now we can start writing methods on our own display function ;;; for different views. This one displays the data each member ;;; on a line of its own. (defmethod display-pane-with-view (frame pane (view members-view)) (loop for member in (members frame) do (with-output-as-presentation (pane member 'person) (format pane "~a, ~a, ~a, ~a~%" (membership-number member) (last-name member) (first-name member) (address member)))))
;;; this CLIM view is used to display the information about ;;; a single person. It has a slot that indicates what person ;;; we want to view. (defclass person-view (view) ((%person :initarg :person :reader person)))
;;; this method on our own display function shows the detailed ;;; information of a single member. (defmethod display-pane-with-view (frame pane (view person-view)) (let ((person (person view))) (format pane "Last name: ~a~%First Name: ~a~%Address: ~a~%Membership Number: ~a~%" (last-name person) (first-name person) (address person) (membership-number person))))
;;; entry point to start our applciation (defun views-example () (run-frame-top-level (make-application-frame 'views)))
;;; command to quit the application (define-views-command (com-quit :name t) () (frame-exit *application-frame*))
;;; command to switch the default view of the application pane ;;; (which is the value of *standard-output*) to the one that ;;; shows a member per line. (define-views-command (com-show-all :name t) () (setf (stream-default-view *standard-output*) *members-view*))
;;; command to switch to a view that displays a single member. ;;; this command takes as an argument the person to display. ;;; In this application, the only way to satisfy the demand for ;;; the argument is to click on a line of the members view. In ;;; more elaborate application, you might be able to type a ;;; textual representation (using completion) of the person. (define-views-command (com-show-person :name t) ((person 'person)) (setf (stream-default-view *standard-output*) (make-instance 'person-view :person person)))