From: Thien-Thi Nguyen Date: Sat, 27 May 2006 17:58:26 +0000 (+0000) Subject: (Display): Add "Abstract Display" to menu. X-Git-Tag: emacs-pretest-22.0.90~2253 X-Git-Url: https://code.delx.au/gnu-emacs/commitdiff_plain/f3dffabb22006885c815cb496215b642cb8131e0 (Display): Add "Abstract Display" to menu. (Abstract Display, Abstract Display Functions) (Abstract Display Example): New nodes. --- diff --git a/lispref/display.texi b/lispref/display.texi index a77c895276..3a8b8c1b7c 100644 --- a/lispref/display.texi +++ b/lispref/display.texi @@ -29,6 +29,7 @@ that Emacs presents to the user. * Display Property:: Enabling special display features. * Images:: Displaying images in Emacs buffers. * Buttons:: Adding clickable buttons to Emacs buffers. +* Abstract Display:: Emacs' Widget for Object Collections. * Blinking:: How Emacs shows the matching open parenthesis. * Usual Display:: The usual conventions for displaying nonprinting chars. * Display Tables:: How to specify other conventions. @@ -4621,6 +4622,339 @@ buffer. If @var{count-current} is non-@code{nil}, count any button at @var{pos} in the search, instead of starting at the next button. @end defun +@node Abstract Display +@section Abstract Display +@cindex ewoc +@cindex display, abstract +@cindex display, arbitrary objects +@cindex model/view/controller +@cindex view part, model/view/controller + + The Ewoc package constructs buffer text that represents a structure +of Lisp objects, and updates the text to follow changes in that +structure. This is like the ``view'' component in the the +``model/view/controller'' design paradigm. + + An @dfn{ewoc} is a structure that organizes information required to +construct buffer text that represents certain Lisp data. The buffer +text of the ewoc has three parts, in order: first, fixed @dfn{header} +text; next, textual descriptions of a series of data elements (Lisp +objects that you specify); and last, fixed @dfn{footer} text. +Specifically, an ewoc contains information on: + +@itemize @bullet +@item +The buffer which its text is generated in. + +@item +The text's start position in the buffer. + +@item +The header and footer strings. + +@item +A doubly-linked chain of @dfn{nodes}, each of which contains: + +@itemize +@item +A @dfn{data element}, a single Lisp object. + +@item +Links to the preceding and following nodes in the chain. +@end itemize + +@item +A @dfn{pretty-printer} function which is responsible for +inserting the textual representation of a data +element value into the current buffer. +@end itemize + + Typically, you define an ewoc with @code{ewoc-create}, and then pass +the resulting ewoc structure to other functions in the Ewoc package to +build nodes within it, and display it in the buffer. Once it is +displayed in the buffer, other functions determine the correspondance +between buffer positions and nodes, move point from one node's textual +representation to another, and so forth. @xref{Abstract Display +Functions}. + + A node @dfn{encapsulates} a data element much the way a variable +holds a value. Normally, encapsulation occurs as a part of adding a +node to the ewoc. You can retrieve the data element value and place a +new value in its place, like so: + +@lisp +(ewoc-data @var{node}) +@result{} value + +(ewoc-set-data @var{node} @var{new-value}) +@result{} @var{new-value} +@end lisp + +@noindent +You can also use, as the data element value, a Lisp object (list or +vector) that is a container for the ``real'' value, or an index into +some other structure. The example (@pxref{Abstract Display Example}) +uses the latter approach. + + When the data changes, you will want to update the text in the +buffer. You can update all nodes by calling @code{ewoc-refresh}, or +just specific nodes using @code{ewoc-invalidate}, or all nodes +satisfying a predicate using @code{ewoc-map}. Alternatively, you can +delete invalid nodes using @code{ewoc-delete} or @code{ewoc-filter}, +and add new nodes in their place. Deleting a node from an ewoc deletes +its associated textual description from buffer, as well. + +@menu +* Abstract Display Functions:: +* Abstract Display Example:: +@end menu + +@node Abstract Display Functions +@subsection Abstract Display Functions + + In this subsection, @var{ewoc} and @var{node} stand for the +structures described above (@pxref{Abstract Display}), while +@var{data} stands for an arbitrary Lisp object used as a data element. + +@defun ewoc-create pretty-printer &optional header footer nosep +This constructs and returns a new ewoc, with no nodes (and thus no data +elements). @var{pretty-printer} should be a function that takes one +argument, a data element of the sort you plan to use in this ewoc, and +inserts its textual description at point using @code{insert} (and never +@code{insert-before-markers}, because that would interfere with the +Ewoc package's internal mechanisms). + +Normally, a newline is automatically inserted after the header, +the footer and every node's textual description. If @var{nosep} +is non-@code{nil}, no newline is inserted. This may be useful for +displaying an entire ewoc on a single line, for example, or for +making nodes ``invisible'' by arranging for @var{pretty-printer} +to do nothing for those nodes. + +An ewoc maintains its text in the buffer that is current when +you create it, so switch to the intended buffer before calling +@code{ewoc-create}. +@end defun + +@defun ewoc-buffer ewoc +This returns the buffer where @var{ewoc} maintains its text. +@end defun + +@defun ewoc-get-hf ewoc +This returns a cons cell @code{(@var{header} . @var{footer})} +made from @var{ewoc}'s header and footer. +@end defun + +@defun ewoc-set-hf ewoc header footer +This sets the header and footer of @var{ewoc} to the strings +@var{header} and @var{footer}, respectively. +@end defun + +@defun ewoc-enter-first ewoc data +@defunx ewoc-enter-last ewoc data +These add a new node encapsulating @var{data}, putting it, respectively, +at the beginning or end of @var{ewoc}'s chain of nodes. +@end defun + +@defun ewoc-enter-before ewoc node data +@defunx ewoc-enter-after ewoc node data +These add a new node encapsulating @var{data}, adding it to +@var{ewoc} before or after @var{node}, respectively. +@end defun + +@defun ewoc-prev ewoc node +@defunx ewoc-next ewoc node +These return, respectively, the previous node and the next node of @var{node} +in @var{ewoc}. +@end defun + +@defun ewoc-nth ewoc n +This returns the node in @var{ewoc} found at zero-based index @var{n}. +A negative @var{n} means count from the end. @code{ewoc-nth} returns +@code{nil} if @var{n} is out of range. +@end defun + +@defun ewoc-data node +This extracts the data encapsulated by @var{node} and returns it. +@end defun + +@defun ewoc-set-data node data +This sets the data encapsulated by @var{node} to @var{data}. +@end defun + +@defun ewoc-locate ewoc &optional pos guess +This determines the node in @var{ewoc} which contains point (or +@var{pos} if specified), and returns that node. If @var{ewoc} has no +nodes, it returns @code{nil}. If @var{pos} is before the first node, +it returns the first node; if @var{pos} is after the last node, it returns +the last node. The optional third arg @var{guess} +should be a node that is likely to be near @var{pos}; this doesn't +alter the result, but makes the function run faster. +@end defun + +@defun ewoc-location node +This returns the start position of @var{node}. +@end defun + +@defun ewoc-goto-prev ewoc arg +@defunx ewoc-goto-next ewoc arg +These move point to the previous or next, respectively, @var{arg}th node +in @var{ewoc}. @code{ewoc-goto-prev} does not move if it is already at +the first node or if @var{ewoc} is empty, whereas @code{ewoc-goto-next} +moves past the last node, returning @code{nil}. Excepting this special +case, these functions return the node moved to. +@end defun + +@defun ewoc-goto-node ewoc node +This moves point to the start of @var{node} in @var{ewoc}. +@end defun + +@defun ewoc-refresh ewoc +This function regenerates the text of @var{ewoc}. It works by +deleting the text between the header and the footer, i.e., all the +data elements' representations, and then calling the pretty-printer +function for each node, one by one, in order. +@end defun + +@defun ewoc-invalidate ewoc &rest nodes +This is similar to @code{ewoc-refresh}, except that only @var{nodes} in +@var{ewoc} are updated instead of the entire set. +@end defun + +@defun ewoc-delete ewoc &rest nodes +This deletes each node in @var{nodes} from @var{ewoc}. +@end defun + +@defun ewoc-filter ewoc predicate &rest args +This calls @var{predicate} for each data element in @var{ewoc} and +deletes those nodes for which @var{predicate} returns @code{nil}. +Any @var{args} are passed to @var{predicate}. +@end defun + +@defun ewoc-collect ewoc predicate &rest args +This calls @var{predicate} for each data element in @var{ewoc} +and returns a list of those elements for which @var{predicate} +returns non-@code{nil}. The elements in the list are ordered +as in the buffer. Any @var{args} are passed to @var{predicate}. +@end defun + +@defun ewoc-map map-function ewoc &rest args +This calls @var{map-function} for each data element in @var{ewoc} and +updates those nodes for which @var{map-function} returns non-@code{nil}. +Any @var{args} are passed to @var{map-function}. +@end defun + +@node Abstract Display Example +@subsection Abstract Display Example + + Here is a simple example using functions of the ewoc package to +implement a ``color components display'', an area in a buffer that +represents a vector of three integers (itself representing a 24-bit RGB +value) in various ways. + +@example +(setq colorcomp-ewoc nil + colorcomp-data nil + colorcomp-mode-map nil + colorcomp-labels ["Red" "Green" "Blue"]) + +(defun colorcomp-pp (data) + (if data + (let ((comp (aref colorcomp-data data))) + (insert (aref colorcomp-labels data) "\t: #x" + (format "%02X" comp) " " + (make-string (ash comp -2) ?#) "\n")) + (let ((cstr (format "#%02X%02X%02X" + (aref colorcomp-data 0) + (aref colorcomp-data 1) + (aref colorcomp-data 2))) + (samp " (sample text) ")) + (insert "Color\t: " + (propertize samp 'face `(foreground-color . ,cstr)) + (propertize samp 'face `(background-color . ,cstr)) + "\n")))) + +(defun colorcomp (color) + "Allow fiddling with COLOR in a new buffer. +The buffer is in Color Components mode." + (interactive "sColor (name or #RGB or #RRGGBB): ") + (when (string= "" color) + (setq color "green")) + (unless (color-values color) + (error "No such color: %S" color)) + (switch-to-buffer + (generate-new-buffer (format "originally: %s" color))) + (kill-all-local-variables) + (setq major-mode 'colorcomp-mode + mode-name "Color Components") + (use-local-map colorcomp-mode-map) + (erase-buffer) + (buffer-disable-undo) + (let ((data (apply 'vector (mapcar (lambda (n) (ash n -8)) + (color-values color)))) + (ewoc (ewoc-create 'colorcomp-pp + "\nColor Components\n\n" + (substitute-command-keys + "\n\\@{colorcomp-mode-map@}")))) + (set (make-local-variable 'colorcomp-data) data) + (set (make-local-variable 'colorcomp-ewoc) ewoc) + (ewoc-enter-last ewoc 0) + (ewoc-enter-last ewoc 1) + (ewoc-enter-last ewoc 2) + (ewoc-enter-last ewoc nil))) +@end example + +@cindex controller part, model/view/controller + This example can be extended to be a ``color selection widget'' (in +other words, the controller part of the ``model/view/controller'' +design paradigm) by defining commands to modify @code{colorcomp-data} +and to ``finish'' the selection process, and a keymap to tie it all +together conveniently. + +@example +(defun colorcomp-mod (index limit delta) + (let ((cur (aref colorcomp-data index))) + (unless (= limit cur) + (aset colorcomp-data index (+ cur delta))) + (ewoc-invalidate + colorcomp-ewoc + (ewoc-nth colorcomp-ewoc index) + (ewoc-nth colorcomp-ewoc -1)))) + +(defun colorcomp-R-more () (interactive) (colorcomp-mod 0 255 1)) +(defun colorcomp-G-more () (interactive) (colorcomp-mod 1 255 1)) +(defun colorcomp-B-more () (interactive) (colorcomp-mod 2 255 1)) +(defun colorcomp-R-less () (interactive) (colorcomp-mod 0 0 -1)) +(defun colorcomp-G-less () (interactive) (colorcomp-mod 1 0 -1)) +(defun colorcomp-B-less () (interactive) (colorcomp-mod 2 0 -1)) + +(defun colorcomp-copy-as-kill-and-exit () + "Copy the color components into the kill ring and kill the buffer. +The string is formatted #RRGGBB (hash followed by six hex digits)." + (interactive) + (kill-new (format "#%02X%02X%02X" + (aref colorcomp-data 0) + (aref colorcomp-data 1) + (aref colorcomp-data 2))) + (kill-buffer nil)) + +(setq colorcomp-mode-map + (let ((m (make-sparse-keymap))) + (suppress-keymap m) + (define-key m "i" 'colorcomp-R-less) + (define-key m "o" 'colorcomp-R-more) + (define-key m "k" 'colorcomp-G-less) + (define-key m "l" 'colorcomp-G-more) + (define-key m "," 'colorcomp-B-less) + (define-key m "." 'colorcomp-B-more) + (define-key m " " 'colorcomp-copy-as-kill-and-exit) + m)) +@end example + +Note that we never modify the data in each node, which is fixed when the +ewoc is created to be either @code{nil} or an index into the vector +@code{colorcomp-data}, the actual color components. + @node Blinking @section Blinking Parentheses @cindex parenthesis matching