diff lisp/packages/balloon-help.el @ 138:6608ceec7cf8 r20-2b3

Import from CVS: tag r20-2b3
author cvs
date Mon, 13 Aug 2007 09:31:46 +0200
parents 8619ce7e4c50
children 5a88923fcbfe
line wrap: on
line diff
--- a/lisp/packages/balloon-help.el	Mon Aug 13 09:31:13 2007 +0200
+++ b/lisp/packages/balloon-help.el	Mon Aug 13 09:31:46 2007 +0200
@@ -1,4 +1,4 @@
-;;; Balloon help for XEmacs (requires 19.12 or later)
+;;; Balloon help for XEmacs (requires 19.15 or later)
 ;;; Copyright (C) 1995, 1997 Kyle E. Jones
 ;;;
 ;;; This program is free software; you can redistribute it and/or modify
@@ -28,63 +28,77 @@
 ;; following line to your .emacs:
 ;;
 ;; (require 'balloon-help)
+;; (balloon-help-mode 1)
 ;;
-;; For 19.12 users:
-;;    If you are using fvwm, [tv]twm or ol[v]wm, you can also add
-;;    the following lines to various configuration file to use
-;;    minimal decorations on the balloon help frames.
-;;
-;;    In .emacs:
-;;       (setq balloon-help-frame-name "balloon-help")
+;; The balloon-help frame is a transient window that is not
+;; normally decorated by window managers, so the following
+;; window manager directives may not be needed.  But if they
+;; are:
 ;;
-;;    For ol[v]wm use this in .Xdefaults:
-;;       olvwm.NoDecor: balloon-help
-;;         or
-;;       olwm.MinimalDecor: balloon-help
+;; For ol[v]wm use this in .Xdefaults:
+;;    olvwm.NoDecor: balloon-help
+;;      or
+;;    olwm.MinimalDecor: balloon-help
 ;;
-;;    For fvvm use this in your .fvwmrc:
-;;       NoTitle balloon-help
-;;    or
-;;       Style "balloon-help" NoTitle, NoHandles, BorderWidth 0
+;; For fvvm version 1 use this in your .fvwmrc:
+;;    NoTitle balloon-help
+;; or
+;;    Style "balloon-help" NoTitle, NoHandles, BorderWidth 0
 ;;
-;;    For twm use this in your .twmrc:
-;;       NoTitle { "balloon-help" }
-;; 
-;; Under 19.13 and later versions the balloon-help frame uses a
-;; transient window that is not normally decorated by window
-;; managers.  So the window manager directives should not be
-;; needed for XEmacs 19.13 and beyond.
+;; For twm use this in your .twmrc:
+;;    NoTitle { "balloon-help" }
+;;
 
 (provide 'balloon-help)
 
-(defvar balloon-help-version "1.05"
+(require 'custom)
+
+(defgroup balloon-help nil
+  "Balloon-help support in XEmacs"
+  :group 'frames)
+
+(defvar balloon-help-version "1.06"
   "Version string for Balloon Help.")
 
-(defvar balloon-help-mode t
+(defvar balloon-help-mode nil
   "*Non-nil means Balloon help mode is enabled.")
 
-(defvar balloon-help-timeout 1500
-  "*Display help after this many milliseconds of mouse inactivity.")
+(defcustom balloon-help-timeout 1500
+  "*Display help after this many milliseconds of mouse inactivity."
+  :type 'integer
+  :group 'balloon-help)
 
-(defvar balloon-help-foreground "black"
-  "*The foreground color for displaying balloon help text.")
+(defcustom balloon-help-foreground "black"
+  "*The foreground color for displaying balloon help text."
+  :type 'string
+  :group 'balloon-help)
 
-(defvar balloon-help-background "rgb:c0/c0/c0"
-  "*The background color for the balloon help frame.")
+(defcustom balloon-help-background "gray80"
+  "*The background color for the balloon help frame."
+  :type 'string
+  :group 'balloon-help)
 
-(defvar balloon-help-background-pixmap ""
-  "*The background pixmap for the balloon help frame.")
+(defcustom balloon-help-background-pixmap ""
+  "*The background pixmap for the balloon help frame."
+  :type 'string
+  :group 'balloon-help)
 
-(defvar balloon-help-font "fixed"
-  "*The font for displaying balloon help text.")
+(defcustom balloon-help-font "variable"
+  "*The font for displaying balloon help text."
+  :type 'string
+  :group 'balloon-help)
 
-(defvar balloon-help-border-color "black"
-  "*The color for displaying balloon help frame's border.")
+(defcustom balloon-help-border-color "black"
+  "*The color for displaying balloon help frame's border."
+  :type 'string
+  :group 'balloon-help)
 
-(defvar balloon-help-border-width 2
-  "*The width of the balloon help frame's border.")
+(defcustom balloon-help-border-width 1
+  "*The width of the balloon help frame's border."
+  :type 'integer
+  :group 'balloon-help)
 
-(defvar balloon-help-use-sound nil
+(defcustom balloon-help-use-sound nil
   "*Non-nil value means play a sound to herald the appearance
 and disappearance of the help frame.
 
@@ -92,14 +106,20 @@
 `balloon-help-disappears' will be played when the frame disappears.
 
 See the documentation for the function load-sound-file to see how
-define sounds.")
+define sounds."
+  :type 'boolean
+  :group 'balloon-help)
 
-(defvar balloon-help-frame-name nil
-  "*The frame name to use for the frame to display the balloon help.")
+(defcustom balloon-help-frame-name "balloon-help"
+  "*The frame name to use for the frame to display the balloon help."
+  :type 'string
+  :group 'balloon-help)
 
-(defvar balloon-help-aggressively-follow-mouse nil
+(defcustom balloon-help-aggressively-follow-mouse nil
   "*Non-nil means the balloon should move with the mouse even if the mouse
-is over the same object as the last mouse motion event.")
+is over the same object as the last mouse motion event."
+  :type 'boolean
+  :group 'balloon-help)
 
 ;;;
 ;;; End of user variables.
@@ -107,7 +127,9 @@
 
 (defvar mouse-motion-hook mouse-motion-handler
   "Hooks to be run whenever the user moves the mouse.
-Each hook is called with one argument, the mouse motion event.")
+Each hook is called with one argument, the mouse motion event.
+This hooks variable does not exist unless the \"balloon-help\" library
+has been loaded.")
 
 (defun mouse-motion-hook (event)
   "Run the hooks attached to mouse-motion-hook."
@@ -118,6 +140,9 @@
 (defvar balloon-help-frame nil
   "Balloon help is displayed in this frame.")
 
+(defvar balloon-help-junk-frame nil
+  "Junk parent frame of balloon-help-frame.")
+
 (defvar balloon-help-help-object nil
   "Object that the mouse is over that has a help property, nil otherwise.")
 
@@ -127,7 +152,7 @@
 (defvar balloon-help-help-object-y nil
   "Last vertical mouse position over balloon-help-help-object.")
 
-(defvar balloon-help-buffer nil
+(defvar balloon-help-buffer (get-buffer-create " *balloon-help*")
   "Buffer used to display balloon help.")
 
 (defvar balloon-help-timeout-id nil
@@ -166,22 +191,32 @@
 
 (defun balloon-help-displayed ()
   (and (frame-live-p balloon-help-frame)
-       (frame-visible-p balloon-help-frame)))
+       (frame-visible-p balloon-help-frame)
+       (eq (frame-device balloon-help-frame) (selected-device))))
+
+(defun balloon-help (&optional event)
+  "Display Balloon Help for the object under EVENT.
+If EVENT is nil, then point in the selected window is used instead.
+See the documentation for balloon-help-mode to find out what this means.
+This command must be bound to a mouse event."
+  (interactive "e")
+  (unless (device-on-window-system-p)
+    (error "Cannot display balloon help on %s device" (device-type)))
+  (let ((balloon-help-mode t))
+    (balloon-help-motion-hook event))
+  (when balloon-help-timeout-id
+    (disable-timeout balloon-help-timeout-id)
+    (setq balloon-help-timeout-id nil))
+  (balloon-help-display-help))
 
 (defun balloon-help-motion-hook (event)
   (cond
    ((null balloon-help-mode) t)
-   ((button-press-event-p event)
-    (setq balloon-help-help-object nil)
-    (if balloon-help-timeout-id
-	(disable-timeout balloon-help-timeout-id))
-    (if (balloon-help-displayed)
-	(balloon-help-undisplay-help)))
    (t
-    (let* ((buffer (event-buffer event))
-	   (frame (event-frame event))
-	   (point (and buffer (event-point event)))
-	   (modeline-point (and buffer (event-modeline-position event)))
+    (let* ((buffer (if event (event-buffer event) (current-buffer)))
+	   (frame (if event (event-frame event) (selected-frame)))
+	   (point (if event (event-point event) (point)))
+	   (modeline-point (if event (event-modeline-position event)))
 	   (modeline-extent (and modeline-point
 				 (map-extents
 				  (function (lambda (e ignored) e))
@@ -191,14 +226,14 @@
 				  modeline-point modeline-point
 				  nil nil
 				  'balloon-help)))
-	   (glyph-extent (event-glyph-extent event))
+	   (glyph-extent (and event (event-glyph-extent event)))
 	   (glyph-extent (if (and glyph-extent
 				  (extent-property glyph-extent
 						   'balloon-help))
 			     glyph-extent))
 	   (extent (and point
 			(extent-at point buffer 'balloon-help)))
-	   (button (event-toolbar-button event))
+	   (button (and event (event-toolbar-button event)))
 	   (button (if (and button (toolbar-button-help-string button))
 		       button
 		     nil))
@@ -209,8 +244,9 @@
 		   (not (eq frame balloon-help-frame)))
 	      (progn
 		(setq balloon-help-help-object nil)
-		(if id
-		    (disable-timeout id))
+		(when id
+		  (disable-timeout id)
+		  (setq balloon-help-timeout-id nil))
 		(if (balloon-help-displayed)
 		    (balloon-help-undisplay-help))))
 	(let* ((params (frame-parameters frame))
@@ -232,10 +268,15 @@
 			 (save-excursion (set-buffer buffer) current-menubar))
 		    22 0)))
 	  (setq balloon-help-help-object-x
-		(+ left xleft-toolbar-width (event-x-pixel event))
+		(if event
+		    (+ left xleft-toolbar-width
+		       (event-x-pixel event))
+		  (/ (* (device-pixel-width) 2) 5))
 		balloon-help-help-object-y
-		(+ top xtop-toolbar-height menubar-height
-		   (event-y-pixel event))))
+		(if event
+		    (+ top xtop-toolbar-height menubar-height
+		       (event-y-pixel event))
+		  (/ (* (device-pixel-height) 2) 5))))
 	(cond ((eq frame balloon-help-frame) t)
 	      ((eq object balloon-help-help-object)
 	       (if (and (balloon-help-displayed)
@@ -253,18 +294,9 @@
 				  (function balloon-help-display-help)
 				  nil)))))))))
 
-(defun balloon-help-pre-command-hook (&rest ignored)
-  (setq balloon-help-help-object nil)
-  (if (balloon-help-displayed)
-      (balloon-help-undisplay-help)))
-
-(fset 'balloon-help-post-command-hook 'balloon-help-pre-command-hook)
-(fset 'balloon-help-mouse-leave-frame-hook 'balloon-help-pre-command-hook)
-(fset 'balloon-help-deselect-frame-hook 'balloon-help-pre-command-hook)
-
 (defun balloon-help-display-help (&rest ignored)
   (setq balloon-help-timeout-id nil)
-  (if balloon-help-help-object
+  (if (and balloon-help-help-object (device-on-window-system-p))
       (let* ((object balloon-help-help-object)
 	     (help (or (and (extent-live-p object)
 			    (extent-property object 'balloon-help))
@@ -281,38 +313,33 @@
 	     (setq help (format "help function signaled: %S" data)))))
 	(if (stringp help)
 	    (save-excursion
-	      (if (not (bufferp balloon-help-buffer))
-		  (setq balloon-help-buffer
-			(get-buffer-create " *balloon-help*")))
-	      (if (not (frame-live-p balloon-help-frame))
+	      (if (or (not (frame-live-p balloon-help-frame))
+		      (not (eq (selected-device)
+			       (frame-device balloon-help-frame))))
 		  (setq balloon-help-frame (balloon-help-make-help-frame)))
 	      (set-buffer balloon-help-buffer)
 	      (erase-buffer)
 	      (insert help)
 	      (if (not (bolp))
 		  (insert ?\n))
-	      ;; help strings longer than 2 lines have the last
-	      ;; line stolen by the minibuffer, so make sure the
-	      ;; last line is blank.  Make the top line blank for
-	      ;; some symmetry.
-	      (if (< 2 (count-lines (point-min) (point-max)))
-		  (progn
-		    (insert ?\n)
-		    ;; add a second blank line at the end to
-		    ;; prevent the modeline bar from clipping the
-		    ;; descenders of the last line of text.
-		    (insert ?\n)
-		    (goto-char (point-min))
-		    (insert ?\n)))
-	      ;; cursor will be at point-min because we're just
-	      ;; moving point which doesn't affect window-point
-	      ;; when the window isn't selected.  Indent
-	      ;; everything so that the cursor will be over a
-	      ;; space.  The 1-pixel bar cursor will be
-	      ;; completely invisible this way.
+;;;	      ;; help strings longer than 2 lines have the last
+;;;	      ;; line stolen by the minibuffer, so make sure the
+;;;	      ;; last line is blank.  Make the top line blank for
+;;;	      ;; some symmetry.
+;;;	      (if (< 2 (count-lines (point-min) (point-max)))
+;;;		  (progn
+;;;		    (insert ?\n)
+;;;		    ;; add a second blank line at the end to
+;;;		    ;; prevent the modeline bar from clipping the
+;;;		    ;; descenders of the last line of text.
+;;;		    (insert ?\n)
+;;;		    (goto-char (point-min))
+;;;		    (insert ?\n)))
+	      ;; indent everything by a space for readability
  	      (indent-rigidly (point-min) (point-max) 1)
+	      (balloon-help-set-frame-properties)
+	      (balloon-help-resize-help-frame)
 	      (balloon-help-move-help-frame)
-	      (balloon-help-resize-help-frame)
 	      (balloon-help-expose-help-frame))))))
 
 (defun balloon-help-undisplay-help ()
@@ -334,49 +361,123 @@
 	    (play-sound 'balloon-help-appears))
 	(setq balloon-help-display-pending t))))
 
+(defun balloon-help-set-frame-properties ()
+  (let ((frame balloon-help-frame))
+    ;; don't set the font unconditionally because it makes the
+    ;; frame size flap visibly while XEmacs figures out the new
+    ;; frame size.
+    (if (not (equal (face-font 'default frame) balloon-help-font))
+	(set-face-font 'default balloon-help-font frame))
+    (set-face-foreground 'default balloon-help-foreground frame)
+    (set-face-background 'default balloon-help-background frame)
+    (set-face-background 'modeline balloon-help-background frame)
+    (set-face-background-pixmap 'default balloon-help-background-pixmap frame)
+    (set-frame-property frame 'border-color balloon-help-border-color)
+    (set-frame-property frame 'border-width balloon-help-border-width)))
+
+;;;(defun balloon-help-resize-help-frame ()
+;;;  (save-excursion
+;;;    (set-buffer balloon-help-buffer)
+;;;    (let ((longest 0)
+;;;	  (lines 0)
+;;;	  (done nil)
+;;;	  (window-min-height 1)
+;;;	  (window-min-width 1))
+;;;      (goto-char (point-min))
+;;;      (while (not done)
+;;;	(end-of-line)
+;;;	(setq longest (max longest (current-column))
+;;;	      done (not (= 0 (forward-line))))
+;;;	(and (not done) (setq lines (1+ lines))))
+;;;      (set-frame-size balloon-help-frame (+ 1 longest) lines))))
+
 (defun balloon-help-resize-help-frame ()
   (save-excursion
     (set-buffer balloon-help-buffer)
-    (let ((longest 0)
-	  (lines 0)
-	  (done nil)
-	  (window-min-height 1)
-	  (window-min-width 1))
+    (let* ((longest 0)
+	   (lines 0)
+	   (done nil)
+	   (inst (vector 'string ':data nil))
+	   (window (frame-selected-window balloon-help-frame))
+	   (font-width (font-width (face-font 'default) balloon-help-frame))
+	   start width
+	   (window-min-height 1)
+	   (window-min-width 1))
       (goto-char (point-min))
       (while (not done)
+	(setq start (point))
 	(end-of-line)
-	(setq longest (max longest (current-column))
+	(aset inst 2 (buffer-substring start (point)))
+	(setq longest (max longest (glyph-width (make-glyph inst) window))
 	      done (not (= 0 (forward-line))))
 	(and (not done) (setq lines (1+ lines))))
-      (set-frame-size balloon-help-frame (+ 1 longest) lines))))
+      (setq width (/ longest font-width)
+	    width (if (> longest (* width font-width)) (1+ width) width))
+      (set-frame-size balloon-help-frame (+ 0 width) lines))))
+
+(defun balloon-help-compute-help-frame-y-location ()
+  (let* ((device-bottom (device-pixel-height
+			 (frame-device balloon-help-frame)))
+	 (y-pos (max 0 (+ 48 balloon-help-help-object-y)))
+	 (height (frame-pixel-height balloon-help-frame))
+	 (bottom (+ y-pos height)))
+    (if (>= bottom device-bottom)
+	(setq y-pos (max 0 (- y-pos (- bottom device-bottom)))))
+    y-pos ))
+
+(defun balloon-help-compute-help-frame-x-location ()
+  (let* ((device-right (device-pixel-width (frame-device balloon-help-frame)))
+	 (x-pos (max 0 (+ 32 balloon-help-help-object-x)))
+	 (width (frame-pixel-width balloon-help-frame))
+	 (right (+ x-pos width)))
+    (if (>= right device-right)
+	(setq x-pos (max 0 (- x-pos (- right device-right)))))
+    x-pos ))
+
+(defun balloon-help-move-help-frame ()
+  (let ((x (balloon-help-compute-help-frame-x-location))
+	(y (balloon-help-compute-help-frame-y-location)))
+    (set-frame-position balloon-help-frame x y)))
 
 (defun balloon-help-make-junk-frame ()
   (let ((window-min-height 1)
 	(window-min-width 1))
-    (save-excursion
-      (set-buffer (generate-new-buffer "*junk-frame-buffer*"))
-      (prog1
-	  (make-frame '(minibuffer t initially-unmapped t width 1 height 1))
-	(rename-buffer " *junk-frame-buffer*" t)))))
+    (when (framep balloon-help-junk-frame)
+      (delete-frame balloon-help-junk-frame)
+      (setq balloon-help-junk-frame nil))
+    (prog1
+	(setq balloon-help-junk-frame
+	      (make-frame '(minibuffer t
+			    initially-unmapped t
+			    width 1
+			    height 1)))
+      (set-window-buffer (frame-selected-window balloon-help-junk-frame)
+			 balloon-help-buffer))))
 
 (defun balloon-help-make-help-frame ()
+  (when (framep balloon-help-frame)
+    (delete-frame balloon-help-frame)
+    (setq balloon-help-frame nil))
   (save-excursion
     (set-buffer balloon-help-buffer)
+    (setq truncate-lines t)
     (set-buffer-menubar nil)
     (let* ((x (balloon-help-compute-help-frame-x-location))
 	   (y (balloon-help-compute-help-frame-y-location))
 	   (window-min-height 1)
 	   (window-min-width 1)
+	   (junk-frame (balloon-help-make-junk-frame))
 	   (frame (make-frame (list
 			       '(initially-unmapped . t)
 			       ;; try to evade frame decorations
-			       (cons 'name (or balloon-help-frame-name
-					       "xclock"))
+			       (cons 'name balloon-help-frame-name)
 			       (cons 'border-width balloon-help-border-width)
 			       (cons 'border-color balloon-help-border-color)
 			       (cons 'top y)
 			       (cons 'left x)
-			       (cons 'popup (balloon-help-make-junk-frame))
+			       (cons 'popup junk-frame)
+			       (cons 'minibuffer
+				     (minibuffer-window junk-frame))
 			       '(width . 3)
 			       '(height . 1)))))
       (set-face-font 'default balloon-help-font frame)
@@ -390,33 +491,43 @@
       (set-specifier left-toolbar-width (cons frame 0))
       (set-specifier right-toolbar-width (cons frame 0))
       (set-specifier bottom-toolbar-height (cons frame 0))
+      (set-specifier top-toolbar-visible-p (cons frame nil))
+      (set-specifier left-toolbar-visible-p (cons frame nil))
+      (set-specifier right-toolbar-visible-p (cons frame nil))
+      (set-specifier bottom-toolbar-visible-p (cons frame nil))
       (set-specifier top-toolbar (cons frame nil))
       (set-specifier left-toolbar (cons frame nil))
       (set-specifier right-toolbar (cons frame nil))
       (set-specifier bottom-toolbar (cons frame nil))
       (set-specifier scrollbar-width (cons frame 0))
       (set-specifier scrollbar-height (cons frame 0))
-      (and (boundp 'text-cursor-visible-p)
-	   (specifierp text-cursor-visible-p)
-	   (set-specifier text-cursor-visible-p (cons frame nil)))
+      (set-specifier text-cursor-visible-p (cons frame nil))
+      (set-specifier has-modeline-p (cons frame nil))
       (set-specifier modeline-shadow-thickness (cons frame 0))
+      (set-specifier (glyph-image truncation-glyph) [nothing] frame '(x))
       (set-face-background 'modeline balloon-help-background frame)
       frame )))
 
-(defun balloon-help-compute-help-frame-x-location ()
-  (max 0 (+ 32 balloon-help-help-object-x)))
+(defun balloon-help-pre-command-hook ()
+  (unless (eq this-command 'balloon-help)
+    (balloon-help-go-away)))
+
+(defun balloon-help-go-away (&rest ignored)
+  (setq balloon-help-help-object nil)
+  (if (balloon-help-displayed)
+      (balloon-help-undisplay-help)))
 
-(defun balloon-help-compute-help-frame-y-location ()
-  (max 0 (+ 48 balloon-help-help-object-y)))
+(defun balloon-help-mouse-leave-frame-hook (&rest ignored)
+  (let* ((mouse (mouse-position))
+	 (window (car mouse)))
+    (if (or (null window) (not (eq (window-frame window) balloon-help-frame)))
+	(balloon-help-go-away))))
 
-(defun balloon-help-move-help-frame ()
-  (let ((x (balloon-help-compute-help-frame-x-location))
-	(y (balloon-help-compute-help-frame-y-location)))
-    (set-frame-position balloon-help-frame x y)))
+;; loses with ClickToFocus under fvwm
+;;(fset 'balloon-help-deselect-frame-hook 'balloon-help-go-away)
+;;(add-hook 'deselect-frame-hook 'balloon-help-deselect-frame-hook)
 
 (add-hook 'mouse-motion-hook 'balloon-help-motion-hook)
+
 (add-hook 'pre-command-hook 'balloon-help-pre-command-hook)
-(add-hook 'post-command-hook 'balloon-help-post-command-hook)
 (add-hook 'mouse-leave-frame-hook 'balloon-help-mouse-leave-frame-hook)
-;; loses with ClickToFocus under fvwm
-;;(add-hook 'deselect-frame-hook 'balloon-help-deselect-frame-hook)