1 ;;; outl-mouse.el --- outline mode mouse commands for Emacs
3 ;; Copyright (C) 1997 Free Software Foundation, Inc.
4 ;; Copyright 1994 (C) Andy Piper <ajp@eng.cam.ac.uk>
6 ;; Author: Andy Piper <ajp@eng.cam.ac.uk>
7 ;; Maintainer: XEmacs Development Team
8 ;; Keywords: outlines, mouse
10 ;; This file is part of XEmacs.
12 ;; XEmacs is free software; you can redistribute it and/or modify it
13 ;; under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; XEmacs is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with XEmacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
27 ;;; Synched up with: Not in FSF
31 ;; outl-mouse.el v1.3.8:
33 ;; Defines button one to hide blocks when clicked on outline-up-arrow
34 ;; and expand blocks when clicked on outline-down-arrow. Features are
35 ;; activated when outline-minor-mode or outline-mode are turned
36 ;; on. There is also a menu for each glyph on button 3.
39 ;; (require 'outl-mouse)
40 ;; in your .emacs file.
42 ;; If you use func-menu all the time and want outl-mouse on all the
43 ;; time as well then put:
44 ;; (setq outline-sync-with-func-menu t)
45 ;; outlining will then be turned on when func-menu is. Note that this
46 ;; requires a patch to func-menu 2.16 (in 19.10) to work:
48 ;RCS file: func-menu.el,v
49 ;retrieving revision 1.1
50 ;diff -r1.1 func-menu.el
52 ;> (defvar fume-found-function-hook nil
53 ;> "*Hook to call after every function match.")
56 ;< (if (listp funcname)
57 ;< (setq funclist (cons funcname funclist)))
59 ;> (cond ((listp funcname)
60 ;> (setq funclist (cons funcname funclist))
61 ;> (save-excursion (run-hooks 'fume-found-function-hook))))
63 ;; If you want mac-style outlining then set outline-mac-style to t.
64 ;; If you have xpm then arrows are much better defined.
66 ;; This package uses func-menu to define outline regexps if they are
67 ;; not already defined. You should no longer need to use out-xtra.
69 ;; You can define the package to do something other than outlining by
70 ;; setting outline-fold-in-function and outline-fold-out-function.
72 ;; You can define the color of outline arrows, but only in your .emacs.
74 ;; Only works in XEmacs 19.10 and onwards.
78 ;; User definable variables.
80 (defgroup outl-mouse nil
81 "Outline mouse mode commands for Emacs"
87 (defcustom outline-mac-style nil
88 "*If t then outline glyphs will be right and down arrows."
92 ; #### Mike Sperber says: I haven't found a way to make annotation
93 ; glyphs display on the right of the header of a hidden heading: The
94 ; invisiblity hides the glyphs.
96 (defcustom outline-glyphs-on-left t
97 "*The position of outline glyphs on a line.
98 Note: This setting is currently being ignored:
99 The outline glyphs are always on the left."
103 (defcustom outline-glyph-colour "Gray75"
104 "*The colour of outlining arrows."
108 (defcustom outline-glyph-shade-colour "Gray40"
109 "*The shadow colour of outlining arrows."
113 (defcustom outline-glyph-lit-colour "Gray90"
114 "*The lit colour of outlining arrows."
118 (defvar outline-fold-in-function 'outline-fold-in
119 "Function to call for folding in.
120 The function should take an annotation argument.")
121 (make-variable-buffer-local 'outline-fold-in-function)
123 (defvar outline-fold-out-function 'outline-fold-out
124 "Function to call for folding out.
125 The function should take an annotation argument.")
126 (make-variable-buffer-local 'outline-fold-out-function)
128 (defcustom outline-sync-with-func-menu nil
129 "*If t then outline glyphs are permanently added by func-menu scans.
130 If outline-minor-mode is turned off then turning it back on will have
131 no effect. Instead the buffer should be rescanned from the function
136 (defcustom outline-move-point-after-click t
137 "*If t then point is moved to the current heading when clicked."
141 (defcustom outline-scanning-message "Adding glyphs... (%3d%%)"
142 "*Progress message during the scanning of the buffer.
143 Set this to nil to inhibit progress messages."
148 ;; No user definable variables beyond this point.
151 ;; I'll bet there's a neat way to do this with specifiers -- a pity the
152 ;; sucks so badly on it. -sb
153 (defconst outline-up-arrow ; XEmacs
154 (make-glyph ; an up-arrow
155 (cond ((featurep 'xpm) (vector 'xpm :data (concat "/* XPM */
156 static char * arrow[] = {
159 \". c " outline-glyph-lit-colour "\",
160 \"X c " outline-glyph-shade-colour "\",
161 \"o c " outline-glyph-colour "\",
162 \"O c " outline-glyph-shade-colour "\",
177 (concat "\000\000\000\000\060\000\060\000\150\000"
178 "\150\000\324\000\324\000\376\001\376\001"))))
180 "Bitmap object for outline up glyph.")
182 (defconst outline-up-arrow-mask ; XEmacs
183 (make-glyph ; an up-arrow
184 (cond ((featurep 'xpm) (vector 'xpm :data (concat "/* XPM */
185 static char * arrow[] = {
188 \". c " outline-glyph-shade-colour "\",
189 \"X c " outline-glyph-lit-colour "\",
190 \"o c " outline-glyph-colour "\",
191 \"O c " outline-glyph-lit-colour "\",
206 (concat "\000\000\000\000\060\000\060\000\130\000"
207 "\130\000\254\000\274\000\006\001\376\001"))))
209 "Bitmap object for outline depressed up glyph.")
211 (defconst outline-down-arrow ; XEmacs
212 (make-glyph ; a down-arrow
213 (cond ((featurep 'xpm) (vector 'xpm :data (concat "/* XPM */
214 static char * down[] = {
216 \" c " outline-glyph-lit-colour "\",
217 \". c " outline-glyph-lit-colour "\",
218 \"X c " outline-glyph-shade-colour "\",
220 \"O c " outline-glyph-colour "\",
235 (concat "\000\000\000\000\376\001\202\001\364\000"
236 "\324\000\150\000\150\000\060\000\060\000"))))
238 "Bitmap object for outline down glyph.")
240 (defconst outline-down-arrow-mask ; XEmacs
241 (make-glyph ; a down-arrow
242 (cond ((featurep 'xpm) (vector 'xpm :data (concat "/* XPM */
243 static char * down[] = {
245 \" c " outline-glyph-shade-colour "\",
246 \". c " outline-glyph-shade-colour "\",
247 \"X c " outline-glyph-lit-colour "\",
249 \"O c " outline-glyph-colour "\",
264 (concat "\000\000\000\000\376\001\376\001\254\000"
265 "\254\000\130\000\130\000\060\000\060\000"))))
267 "Bitmap object for outline depressed down glyph.")
269 (defconst outline-right-arrow
270 (make-glyph ; a right-arrow
271 (cond ((featurep 'xpm) (vector 'xpm :data (concat "/* XPM */
272 static char * right[] = {
274 \" c " outline-glyph-lit-colour "\",
275 \". c " outline-glyph-lit-colour "\",
277 \"o c " outline-glyph-colour "\",
278 \"O c " outline-glyph-shade-colour "\",
293 (concat "\000\000\006\000\032\000\142\000\232\001"
294 "\352\001\172\000\036\000\006\000\000\000"))))
296 "Bitmap object for outline right glyph.")
298 (defconst outline-right-arrow-mask
299 (make-glyph ; a right-arrow
300 (cond ((featurep 'xpm) (vector 'xpm :data (concat "/* XPM */
301 static char * right[] = {
303 \" c " outline-glyph-shade-colour "\",
304 \". c " outline-glyph-shade-colour "\",
306 \"o c " outline-glyph-colour "\",
307 \"O c " outline-glyph-lit-colour "\",
322 (concat "\000\000\006\000\036\000\176\000\346\001"
323 "\236\001\146\000\036\000\006\000\000\000"))))
325 "Bitmap object for outline depressed right glyph.")
327 (defvar outline-glyph-menu
329 ["Hide all" hide-body t]
330 ["Hide all subtrees" hide-subtrees-same-level t]
331 ["Hide subtree" hide-subtree t]
332 ; ["Hide body" hide-body t]
334 ["Show all" show-all t]
335 ["Show subtree" show-subtree t]
336 ["Show body" show-entry t]
338 ["Update buffer" outline-add-glyphs t]
339 ["Rescan buffer" outline-rescan-buffer t])
340 "Menu of commands for outline glyphs.")
342 (set-glyph-contrib-p outline-down-arrow nil)
343 (set-glyph-contrib-p outline-up-arrow nil)
344 (set-glyph-contrib-p outline-down-arrow-mask nil)
345 (set-glyph-contrib-p outline-up-arrow-mask nil)
346 (set-glyph-contrib-p outline-right-arrow nil)
347 (set-glyph-contrib-p outline-right-arrow-mask nil)
349 (require 'annotations)
350 (require 'advice) ; help me doctor !
352 (require 'func-menu) ; for those most excellent regexps.
354 (add-hook 'outline-mode-hook 'outline-mouse-hooks)
355 (add-hook 'outline-minor-mode-hook 'outline-mouse-hooks)
356 ;; I thought this was done already ...
357 (make-variable-buffer-local 'outline-regexp)
358 (make-variable-buffer-local 'outline-level)
360 (cond (outline-sync-with-func-menu
361 (add-hook 'fume-found-function-hook 'outline-heading-add-glyph-1)
362 (setq-default fume-rescan-buffer-hook '(lambda ()
363 (outline-minor-mode 1)))))
365 (defadvice fume-set-defaults (after fume-set-defaults-ad activate)
366 "Advise fume-set-defaults to setup outline regexps."
367 (if (and (not (assq 'outline-regexp (buffer-local-variables)))
368 fume-function-name-regexp)
370 (setq outline-regexp (if (listp fume-function-name-regexp)
371 (car fume-function-name-regexp)
372 fume-function-name-regexp))
373 (setq outline-level '(lambda () 1)))))
375 (defadvice outline-minor-mode (after outline-mode-mouse activate)
376 "Advise outline-minor-mode to delete glyphs when switched off."
377 (if (not outline-minor-mode)
379 (outline-delete-glyphs)
382 ;; advise all outline commands so that glyphs are synced after use
383 (defadvice show-all (after show-all-ad activate)
384 "Advise show-all to sync headings."
385 (outline-sync-visible-sub-headings-in-region (point-min) (point-max)))
387 (defadvice hide-subtree (after hide-subtree-ad activate)
388 "Advise hide-subtree to sync headings."
389 (outline-sync-visible-sub-headings))
391 (defadvice hide-entry (after hide-entry-ad activate)
392 "Advise hide-entry to sync headings."
393 (outline-sync-visible-sub-headings))
395 (defadvice hide-body (after hide-body-ad activate)
396 "Advise hide-body to sync headings."
397 (outline-sync-visible-sub-headings-in-region (point-min) (point-max)))
399 (defadvice show-subtree (after show-subtree-ad activate)
400 "Advise show-subtree to sync headings."
401 (outline-sync-visible-sub-headings))
403 (defadvice show-entry (after show-entry-ad activate)
404 "Advise shown-entry to sync headings."
405 (outline-sync-visible-sub-headings))
408 (defun outl-mouse-mode ()
409 "Calls outline-mode, with outl-mouse extensions"
414 (defun outl-mouse-minor-mode (&optional arg)
415 "Toggles outline-minor-mode, with outl-mouse extensions"
417 (outline-minor-mode arg))
419 (defun hide-subtrees-same-level ()
420 "Hide all subtrees below the current level."
427 (outline-forward-same-level 1)
431 (defun outline-mouse-hooks ()
432 "Hook for installing outlining with the mouse."
433 ;; use function menu regexps if not set
435 ;; only add glyphs when we're not synced.
436 (if (not outline-sync-with-func-menu) (outline-add-glyphs))
437 ;; add C-a to local keymap
438 (let ((outline (cond ((keymapp (lookup-key (current-local-map)
439 outline-minor-mode-prefix))
440 (lookup-key (current-local-map)
441 outline-minor-mode-prefix))
443 (define-key (current-local-map)
444 outline-minor-mode-prefix (make-sparse-keymap))
445 (lookup-key (current-local-map)
446 outline-minor-mode-prefix)))))
447 (define-key outline "\C-a" 'outline-heading-add-glyph)
448 (define-key outline-mode-map "\C-c\C-a" 'outline-heading-add-glyph)))
450 (defun outline-add-glyphs ()
451 "Add annotations and glyphs to all heading lines that don't have them."
454 (and outline-scanning-message (display-message
456 (format outline-scanning-message 0)))
457 (goto-char (point-min))
458 (if (not (outline-on-heading-p)) (outline-next-visible-heading-safe))
461 (outline-heading-add-glyph-1)
462 (and outline-scanning-message
465 (format outline-scanning-message (fume-relative-position))))
466 (outline-next-visible-heading-safe)))
467 (and outline-scanning-message
470 (format "%s done" (format outline-scanning-message 100))))))
472 (defun outline-delete-glyphs ()
473 "Remove annotations and glyphs from heading lines."
475 (mapcar 'outline-heading-delete-glyph (annotation-list))))
477 (defun outline-rescan-buffer ()
478 "Remove and insert all annotations."
480 (outline-delete-glyphs)
483 (outline-sync-visible-sub-headings-in-region (point-min) (point-max))))
485 (defun outline-heading-delete-glyph (ext)
486 "Delete annotation and glyph from a heading with annotation EXT."
489 (goto-char (extent-start-position ext))
491 (outline-on-heading-p))
492 (extent-property ext 'outl-mouse))
493 (delete-annotation ext))
496 (defun outline-heading-add-glyph ()
497 "Interactive version of outline-heading-add-glyph-1."
500 (outline-heading-add-glyph-1)))
502 (defun outline-heading-add-glyph-1 ()
503 "Add glyph to the end of heading line which point is on.
504 Returns nil if point is not on a heading or glyph already exists."
505 (if (or (not (outline-on-heading-p))
506 (outline-heading-has-glyph-p)
507 (save-excursion (forward-line) (outline-on-heading-p)))
509 (outline-back-to-heading)
511 (make-annotation (if outline-mac-style
514 (save-excursion (if t ; disabled: outline-glyphs-on-left
516 (outline-end-of-heading))
519 (if outline-mac-style
520 outline-right-arrow-mask
521 outline-down-arrow-mask)))
523 (make-annotation (if outline-mac-style
526 (save-excursion (if t ; disabled: outline-glyphs-on-left
528 (outline-end-of-heading))
531 (if outline-mac-style
532 outline-down-arrow-mask
533 outline-up-arrow-mask))))
534 ;; we cunningly make the annotation data point to its twin.
535 (set-annotation-data anot1 anot2)
536 (set-extent-property anot1 'outl-mouse 'up)
537 (set-annotation-action anot1 'outline-up-click)
538 (set-annotation-menu anot1 outline-glyph-menu)
539 (set-extent-priority anot1 1)
540 (set-annotation-data anot2 anot1)
541 (set-extent-property anot2 'outl-mouse 'down)
542 (set-annotation-menu anot2 outline-glyph-menu)
543 (set-annotation-action anot2 'outline-down-click)
544 (hide-annotation anot2))
547 (defun outline-heading-has-glyph-p ()
548 "Return t if heading has an outline glyph."
552 (if (extent-property a 'outl-mouse)
554 (annotations-in-region (save-excursion (outline-back-to-heading) (point))
555 (save-excursion (outline-end-of-heading)
560 (defun outline-sync-visible-sub-headings-in-region (pmin pmax)
561 "Make sure all annotations on headings in region PMIN PMAX are
562 displayed correctly."
564 (goto-char (extent-start-position x))
566 (cond ((and (eq (extent-property x 'outl-mouse) 'down)
567 ;; skip things we can't see
568 (not (outline-invisible-p (point-at-eol))))
569 (if (outline-more-to-hide)
571 (reveal-annotation (annotation-data x))
572 (hide-annotation (annotation-data x)))
573 (if (not (outline-hidden-p))
576 (reveal-annotation x)))))
577 (annotations-in-region pmin pmax (current-buffer))))
579 (defun outline-sync-visible-sub-headings ()
580 "Make sure all annotations on sub-headings below the one point is on are
581 displayed correctly."
582 (outline-sync-visible-sub-headings-in-region
584 (progn (outline-end-of-subtree) (point))))
586 (defun outline-fold-out (annotation)
587 "Fold out the current heading."
589 ; (if (not (equal (condition-case nil
590 ; (save-excursion (outline-next-visible-heading 1)
593 ; (save-excursion (outline-next-heading)
594 ; (if (eobp) nil (point)))))
595 (if (save-excursion (outline-next-heading)
596 (outline-invisible-p (point-at-eol)))
598 (save-excursion (show-children))
599 (outline-sync-visible-sub-headings))
600 ;; mess with single entry
601 (if (outline-hidden-p)
603 (save-excursion (show-entry))
604 ;; reveal my twin and hide me
605 (hide-annotation annotation)
606 (reveal-annotation (annotation-data annotation))))))
608 (defun outline-fold-in (annotation)
609 "Fold in the current heading."
611 ;; mess with single entries
612 (if (not (outline-hidden-p))
614 (save-excursion (hide-entry))
615 (if (not (outline-more-to-hide))
616 (hide-annotation annotation))
617 (reveal-annotation (annotation-data annotation)))
618 ;; otherwise look for more leaves
620 (if (outline-more-to-hide t)
624 (outline-sync-visible-sub-headings)))
626 (defun outline-more-to-hide (&optional arg)
627 "Return t if there are more visible sub-headings or text.
628 With ARG return t only if visible sub-headings have no visible text."
629 (if (not (outline-hidden-p))
632 (and (< (funcall outline-level) (condition-case nil
634 (outline-next-visible-heading 1)
635 (funcall outline-level))
637 (if (and (not (outline-hidden-p)) arg)
640 (defun outline-hidden-p ()
641 "Return t if point is on the header of a hidden subtree."
643 (let ((end-of-entry (save-excursion (outline-next-heading))))
644 ;; Make sure that the end of the entry really exists.
645 (if (not end-of-entry)
646 (setq end-of-entry (point-max)))
647 (outline-back-to-heading)
648 ;; If anything is invisible, the entry is hidden.
649 (map-extents #'(lambda (extent _)
650 (eq 'outline (extent-property extent 'invisible)))
652 (point) end-of-entry nil nil 'invisible))))
654 (defun outline-next-visible-heading-safe ()
655 "Safely go to the next visible heading.
656 nil is returned if there is none."
659 (outline-next-visible-heading 1)
663 (defun outline-up-click (data ev)
664 "Annotation action for clicking on an up arrow.
665 DATA is the annotation data. EV is the mouse click event."
666 (outline-click data ev outline-fold-in-function))
668 (defun outline-down-click (data ev)
669 "Annotation action for clicking on an up arrow.
670 DATA is the annotation data. EV is the mouse click event."
671 (outline-click data ev outline-fold-out-function))
673 (defun outline-click (data ev fold-function)
674 "Annotation action for clicking on an arrow.
675 DATA is the annotation data. EV is the mouse click event,
676 FUNCTION is either `outline-fold-in-function' or `outline-fold-out-function'."
677 (let ((buffer (event-buffer ev)))
680 (goto-char (extent-end-position (event-glyph-extent ev)))
681 (funcall fold-function (event-glyph-extent ev)))
682 (if outline-move-point-after-click
684 (goto-char (extent-end-position (event-glyph-extent ev)) buffer)
685 (beginning-of-line nil buffer)))))
687 (provide 'outl-mouse)
688 (provide 'outln-18) ; fool auctex - outline is ok now.
691 ;; outline-regexp: ";;; \\|(def.."
694 ;;; outl-mouse.el ends here