diff lisp/utils/annotations.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 0293115a14e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/utils/annotations.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,445 @@
+;;; annotations.el --- interface to marginal annotations
+
+;; Copyright (C) 1992-1994 Free Software Foundation, Inc.
+;;
+;; Created: 10-Oct-93, Chuck Thompson <cthomp@cs.uiuc.edu>
+;; Keywords: extensions, hypermedia, outlining
+;; Enhanced by Andy Piper <ajp@eng.cam.ac.uk>: 6-may-94
+;;
+;; Last modified:  12-May-95 by Chuck Thompson.
+
+;; This file is part of XEmacs.
+
+;; XEmacs is free software; you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; XEmacs is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+;; General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with XEmacs; see the file COPYING.  If not, write to the Free
+;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+;;; Synched up with: Not in FSF.
+
+;;
+;; The annotations are implemented on top of extents.  The extent property
+;; 'annotation of an extent being used as an annotation is vector of size 6:
+;;	[<data> <action> <menu> <glyph> <down-glyph> <rightp>]
+;;
+
+;;;###autoload
+(defvar make-annotation-hook nil
+  "*Function or functions to run immediately after creating an annotation.")
+
+;;;###autoload
+(defvar before-delete-annotation-hook nil
+  "*Function or functions to run immediately before deleting an annotation.")
+
+;;;###autoload
+(defvar after-delete-annotation-hook nil
+  "*Function or functions to run immediately after deleting an annotation.")
+
+(defvar annotation-local-map-default
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-name map 'annotation-local-map)
+    (define-key map 'button1 'annotation-activate-function-default)
+    (define-key map 'button3 'annotation-popup-menu)
+    map)
+  "Keymap used to activate annotations with only annotation data passed.")
+
+(defvar annotation-local-map-with-event
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-name map 'annotation-local-map)
+    (define-key map 'button1 'annotation-activate-function-with-event)
+    (define-key map 'button3 'annotation-popup-menu)
+    map)
+  "Keymap used to activate annotations with annotation data and event passed.")
+
+;;
+;; When the mouse is pressed and released over an annotation glyph
+;; this will run the annotation action passing a single arg, the value
+;; of the annotation data field.
+;;
+(defun annotation-activate-function-default (event)
+  (interactive "e")
+  (let ((extent (event-glyph-extent event))
+	(mouse-down t)
+	(up-glyph nil))
+    ;; make the glyph look pressed
+    (cond ((annotation-down-glyph extent)
+	   (setq up-glyph (annotation-glyph extent))
+	   (set-annotation-glyph extent (annotation-down-glyph extent))))
+    (while mouse-down
+      (setq event (next-event event))
+      (if (button-release-event-p event)
+	  (setq mouse-down nil)))
+    ;; make the glyph look released
+    (cond ((annotation-down-glyph extent)
+	   (set-annotation-glyph extent up-glyph)))
+    (if (eq extent (event-glyph-extent event))
+	(if (annotation-action extent)
+	    (funcall (annotation-action extent) (annotation-data extent))))))
+
+;;
+;; When the mouse is pressed and released over an annotation glyph
+;; this will run the annotation action passing two args, the value
+;; of the annotation data field and the event which triggered the
+;; annotation.
+;;
+(defun annotation-activate-function-with-event (event)
+  (interactive "e")
+  (let ((extent (event-glyph-extent event))
+	(mouse-down t)
+	(up-glyph nil))
+    ;; make the glyph look pressed
+    (cond ((annotation-down-glyph extent)
+	   (setq up-glyph (annotation-glyph extent))
+	   (set-annotation-glyph extent (annotation-down-glyph extent))))
+    (while mouse-down
+      (setq event (next-event event))
+      (if (button-release-event-p event)
+	  (setq mouse-down nil)))
+    ;; make the glyph look released
+    (cond ((annotation-down-glyph extent)
+	   (set-annotation-glyph extent up-glyph)))
+    (if (eq extent (event-glyph-extent event))
+	(if (annotation-action extent)
+	    (funcall (annotation-action extent) (annotation-data extent)
+		     event)))))
+
+;; #### Glyphs should be glyphs should be glyphs
+;;;###autoload
+(defun make-annotation (glyph &optional pos layout buffer with-event d-glyph rightp)
+  "Create a marginal annotation, displayed using GLYPH, at position POS.
+GLYPH may be either a glyph object or a string.  Use layout policy
+LAYOUT and place the annotation in buffer BUFFER.  If POS is nil, point is
+used.  If LAYOUT is nil, `whitespace' is used.  If BUFFER is nil, the
+current buffer is used.  If WITH-EVENT is non-nil, then when an annotation
+is activated, the triggering event is passed as the second arg to the
+annotation function.  If D-GLYPH is non-nil then it is used as the glyph 
+that will be displayed when button1 is down.  If RIGHTP is non-nil then
+the glyph will be displayed on the right side of the buffer instead of the
+left."
+  (let ((new-annotation))
+    ;; get the buffer to add the annotation at
+    (if (not buffer)
+	(setq buffer (current-buffer))
+      (setq buffer (get-buffer buffer)))
+    ;; get the position to put it at
+    (if (not pos)
+	(save-excursion
+	  (set-buffer buffer)
+	  (setq pos (point))))
+    ;; make sure it gets some layout policy
+    (if (not layout)
+	(setq layout 'whitespace))
+
+    ;; make sure the glyph arguments are actually glyphs
+    (if (and glyph (not (glyphp glyph)))
+	(setq glyph (make-glyph glyph)))
+    (if (and d-glyph (not (glyphp d-glyph)))
+	(setq d-glyph (make-glyph d-glyph)))
+
+    ;; create the actual annotation
+    (setq new-annotation (make-extent pos pos buffer))
+    (detach-extent new-annotation)
+    (set-extent-endpoints new-annotation pos pos)
+    (if rightp
+	(set-extent-end-glyph new-annotation glyph layout)
+      (set-extent-begin-glyph new-annotation glyph layout))
+    (set-extent-property new-annotation 'annotation 
+			 (vector nil nil nil glyph d-glyph rightp))
+    (set-extent-property new-annotation 'end-closed t)
+    (set-extent-property new-annotation 'start-open t)
+    (set-extent-property new-annotation 'duplicable t)
+    (if with-event
+	(set-extent-property new-annotation 'keymap
+			     annotation-local-map-with-event)
+      (set-extent-property new-annotation 'keymap
+			   annotation-local-map-default))
+    (run-hook-with-args 'make-annotation-hook new-annotation)
+    new-annotation))
+
+(fset 'make-graphic-annotation 'make-annotation)
+(make-obsolete 'make-graphic-annotation 'make-annotation)
+
+;;;###autoload
+(defun delete-annotation (annotation)
+  "Remove ANNOTATION from its buffer.  This does not modify the buffer text."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (progn
+      (run-hook-with-args 'before-delete-annotation-hook annotation)
+      (delete-extent annotation)
+      (run-hooks 'after-delete-annotation-hook))))
+
+;;;###autoload
+(defun annotationp (annotation)
+  "T if OBJECT is an annotation."
+  (and (extent-live-p annotation)
+       (not (null (extent-property annotation 'annotation)))))
+
+(defun annotation-visible (annotation)
+  "T if there is enough available space to display ANNOTATION."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (not (extent-property annotation 'glyph-invisible))))
+
+;;;###autoload
+(defun annotation-at (&optional pos buffer)
+  "Return the first annotation at POS in BUFFER.
+BUFFER defaults to the current buffer.  POS defaults to point in BUFFER."
+  (car (annotations-at pos buffer)))
+(make-obsolete 'annotation-at 'annotations-at)
+
+(defun annotation-layout (annotation)
+  "Return the layout policy of annotation ANNOTATION.  The layout policy
+is set using `set-annotation-layout'."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (if (eq 'right (annotation-side annotation))
+	(extent-end-glyph-layout annotation)
+      (extent-begin-glyph-layout annotation))))
+
+
+(defun annotation-side (annotation)
+  "Return the side of the buffer the annotation is displayed on.
+Return value is either 'left or 'right."
+  (if (aref (extent-property annotation 'annotation) 5)
+      'right
+    'left))
+
+(defun set-annotation-layout (annotation layout)
+  "Set the layout policy of ANNOTATION to LAYOUT.  The function
+`annotation-layout' returns the current layout policy."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (if (eq 'right (annotation-side annotation))
+	(set-extent-end-glyph-layout annotation layout)
+      (set-extent-begin-glyph-layout annotation layout))))
+
+;; Now that annotatios use glyphs this function has little value and
+;; will actually not work as is.
+;(defun annotation-type (annotation)
+;  "Return the display type of the annotation ANNOTATION.  The type will
+;be one of the following symbols:
+;
+;	pixmap
+;	bitmap
+;	string
+;	nil	(the object is not an annotation)"
+;  (if (not (annotationp annotation))
+;      nil
+;    (let ((glyph (annotation-glyph annotation)))
+;      (if (stringp glyph)
+;	  'stringp
+;	(if (not (pixmapp glyph))
+;	    (error "%s is a corrupt annotation" annotation)
+;	  (if (> (pixmap-depth glyph) 0)
+;	      'pixmap
+;	    'bitmap))))))
+(make-obsolete 'annotation-type "This function no longer has any meaning.")
+
+(defun annotation-width (annotation)
+  "Return the width of the annotation ANNOTATION in pixels."  
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (glyph-width (annotation-glyph annotation))))
+
+(defun annotation-glyph (annotation)
+  "If ANNOTATION is of type `string' return the string.  Otherwise, return
+the glyph object used to display ANNOTATION.  The glyph is set using
+`set-annotation-glyph'."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aref (extent-property annotation 'annotation) 3)))
+
+(defun set-annotation-glyph (annotation glyph &optional layout side)
+  "Set the representation of ANNOTATION to GLYPH.
+GLYPH should be a glyph object.  If LAYOUT is non-nil, set the layout
+policy of the annotation to LAYOUT.  If SIDE is equal to 'left or 'right
+change the side of the annotation to that value.
+The function `annotation-glyph' returns the current glyph."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (progn
+      (if (not layout)
+	  (setq layout (extent-layout annotation)))
+      (if (or (eq side 'right)
+	      (and (not (eq side 'left))
+		   (eq (annotation-side annotation) 'right)))
+	  (set-extent-end-glyph annotation glyph layout)
+	(set-extent-begin-glyph annotation glyph layout))
+      (aset (extent-property annotation 'annotation) 3 glyph)
+      (if (eq side 'right)
+	  (aset (extent-property annotation 'annotation) 5 t))
+      (if (eq side 'left)
+	  (aset (extent-property annotation 'annotation) 5 nil))
+      (annotation-glyph annotation))))
+
+(defun annotation-down-glyph (annotation)
+  "If ANNOTATION is of type `string' return the down string.  Otherwise,
+return the glyph object of the down-glyph representing ANNOTATION.
+The down-glyph is set using `set-annotation-down-glyph'."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aref (extent-property annotation 'annotation) 4)))
+
+(defun set-annotation-down-glyph (annotation glyph)
+  "Set the depressed representation of ANNOTATION to GLYPH.  
+GLYPH should be a glyph object. 
+The function `annotation-down-glyph' returns the current down-glyph."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aset (extent-property annotation 'annotation) 4 glyph)))
+
+(define-obsolete-function-alias 'annotation-graphic 'annotation-glyph)
+(define-obsolete-function-alias 'set-annotation-graphic 'set-annotation-glyph)
+  
+(defun annotation-data (annotation)
+  "Return the data associated with annotation ANNOTATION.  The data is
+set using `set-annotation-data'."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aref (extent-property annotation 'annotation) 0)))
+
+(defun set-annotation-data (annotation data)
+  "Set the data field of ANNOTATION to DATA.
+The function `annotation-data' returns the current data."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aset (extent-property annotation 'annotation) 0 data)))
+
+(defun annotation-action (annotation)
+  "Return the action associated with annotation ANNOTATION.  The action
+is set using `set-annotation-action'."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aref (extent-property annotation 'annotation) 1)))
+
+(defun set-annotation-action (annotation action)
+  "Set the action field of ANNOTATION to ACTION.
+The function `annotation-action' returns the current action."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aset (extent-property annotation 'annotation) 1 action)))
+
+(defun annotation-face (annotation)
+  "Return the face associated with annotation ANNOTATION.
+The face is set using `set-annotation-face'."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (extent-face annotation)))
+
+(defun set-annotation-face (annotation face)
+  "Set the face associated with annotation ANNOTATION to FACE.
+The function `annotation-face' returns the current face."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (set-extent-face annotation face)))
+
+(defun hide-annotation (annotation)
+  "Remove ANNOTATION's glyph so that it is invisible."
+  (if (eq (annotation-side annotation) 'left)
+      (set-extent-begin-glyph annotation nil)
+    (set-extent-end-glyph annotation nil)))
+(define-obsolete-function-alias 'annotation-hide 'hide-annotation)
+
+(defun reveal-annotation (annotation)
+  "Add ANNOTATION's glyph so that it is visible."
+  (if (eq (annotation-side annotation) 'left)
+      (set-extent-begin-glyph annotation (annotation-glyph annotation))
+    (set-extent-end-glyph annotation (annotation-glyph annotation))))
+(define-obsolete-function-alias 'annotation-reveal 'reveal-annotation)
+
+;;;###autoload
+(defun annotations-in-region (start end buffer)
+  "Return all annotations in BUFFER between START and END inclusively."
+  (save-excursion
+    (set-buffer buffer)
+
+    (if (< start (point-min))
+      (error "<start> not in range of buffer"))
+    (if (> end (point-max))
+      (error "<end> not in range of buffer"))
+
+    (let (note-list)
+      (map-extents
+       (function (lambda (extent dummy)
+		   (progn
+		     (if (annotationp extent)
+			 (setq note-list (cons extent note-list)))
+		     nil)))
+       buffer start end nil t)
+      note-list)))
+
+;;;###autoload
+(defun annotations-at (&optional pos buffer)
+  "Return a list of all annotations at POS in BUFFER.
+If BUFFER is nil, the current buffer is used.  If POS is nil, point is used."
+  (if (not buffer)
+      (setq buffer (current-buffer)))
+  (if (not pos)
+      (save-excursion
+	(set-buffer buffer)
+	(setq pos (point))))
+
+  (annotations-in-region pos pos buffer)
+)
+
+;;;###autoload
+(defun annotation-list (&optional buffer)
+  "Return a list of all annotations in BUFFER.
+If BUFFER is nil, the current buffer is used."
+  (if (not buffer)
+    (setq buffer (current-buffer)))
+
+  (save-excursion
+    (set-buffer buffer)
+    (annotations-in-region (point-min) (point-max) buffer)))
+
+;;;###autoload
+(defun all-annotations ()
+  "Return a list of all annotations in existence."
+  (let ((b (buffer-list))
+	result)
+    (while b
+      (setq result (nconc result (annotation-list (car b))))
+      (setq b (cdr b)))
+    result))
+
+;;; #### really this menus junk should append to the prevailing menu
+;;;      in the same way `popup-mode-menu' does.  --jwz
+
+;; annotations menu stuff
+(defun annotation-popup-menu (event)
+  "Pop up a menu of annotations commands.
+Point is temporarily moved to the click position."
+  (interactive "e")
+  (let ((extent (event-glyph-extent event)))
+    (save-excursion
+      (goto-char (extent-end-position extent))
+      (if (annotation-menu extent)
+	  (popup-menu (annotation-menu extent))
+	(popup-mode-menu)))))
+
+(defun set-annotation-menu (annotation menu)
+  "Set the menu field of ANNOTATION to MENU.  The function
+`annotation-menu' returns the current menu."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aset (extent-property annotation 'annotation) 2 menu)))
+
+(defun annotation-menu (annotation)
+  "Return the menu associated with annotation ANNOTATION.  The menu
+is set using `set-annotation-menu'."
+  (if (not (annotationp annotation))
+      (error "%s is not an annotation" annotation)
+    (aref (extent-property annotation 'annotation) 2)))
+
+(provide 'annotations)