diff lisp/menubar.el @ 442:abe6d1db359e r21-2-36

Import from CVS: tag r21-2-36
author cvs
date Mon, 13 Aug 2007 11:35:02 +0200
parents 3ecd8885ac67
children 576fb035e263
line wrap: on
line diff
--- a/lisp/menubar.el	Mon Aug 13 11:33:40 2007 +0200
+++ b/lisp/menubar.el	Mon Aug 13 11:35:02 2007 +0200
@@ -30,7 +30,7 @@
 
 ;; This file is dumped with XEmacs (when menubar support is compiled in).
 
-;; Some stuff in FSF menu-bar.el is in x-menubar.el
+;; Some stuff in FSF menu-bar.el is in menubar-items.el
 
 ;;; Code:
 
@@ -128,19 +128,6 @@
 			     menuitem)))
 	  (setq plistp (or (>= L 5)
 			   (and (> L 2) (keywordp (aref menuitem 2)))))
-	  (or (stringp (aref menuitem 0))
-	      (signal 'error
-		      (list
-		       "first element of a button must be a string (the label)"
-		       menuitem)))
-	  (or plistp
-	      (< L 4)
-	      (null (aref menuitem 3))
-	      (stringp (aref menuitem 3))
-	      (signal 'error
-		      (list
-		       "fourth element of a button must be a string (the label suffix)"
-		       menuitem)))
 	  (if plistp
 	      (let ((i 2)
 		    selp
@@ -474,6 +461,199 @@
   (enable-menu-item-1 path t nil))
 
 
+
+;;;;;;; popup menus
+
+(defvar global-popup-menu nil
+  "The global popup menu.  This is present in all modes.
+See the function `popup-menu' for a description of menu syntax.")
+
+(defvar mode-popup-menu nil
+  "The mode-specific popup menu.  Automatically buffer local.
+This is appended to the default items in `global-popup-menu'.
+See the function `popup-menu' for a description of menu syntax.")
+(make-variable-buffer-local 'mode-popup-menu)
+
+(defvar activate-popup-menu-hook nil
+  "Function or functions run before a mode-specific popup menu is made visible.
+These functions are called with no arguments, and should interrogate and
+modify the value of `global-popup-menu' or `mode-popup-menu' as desired.
+Note: this hook is only run if you use `popup-mode-menu' for activating the
+global and mode-specific commands; if you have your own binding for button3,
+this hook won't be run.")
+
+(defvar last-popup-menu-event nil
+  "The mouse event that invoked the last popup menu.
+NOTE: This is EXPERIMENTAL and may change at any time.")
+
+(defun popup-mode-menu (&optional event)
+  "Pop up a menu of global and mode-specific commands.
+The menu is computed by combining `global-popup-menu' and `mode-popup-menu'
+with any items derived from the `context-menu' property of the extent where the
+button was clicked."
+  (interactive "_e")
+  (setq last-popup-menu-event
+	(or (and event (button-event-p event) event)
+	    (let* ((mouse-pos (mouse-position))
+		   (win (car mouse-pos))
+		   (x (cadr mouse-pos))
+		   (y (cddr mouse-pos))
+		   (edges (window-pixel-edges win))
+		   (winx (first edges))
+		   (winy (second edges))
+		   (x (+ x winx))
+		   (y (+ y winy)))
+	      (make-event 'button-press
+			  `(button 3 x ,x y ,y channel ,(window-frame win)
+				   timestamp ,(current-event-timestamp
+					       (cdfw-console win)))))))
+  (run-hooks 'activate-popup-menu-hook)
+  (let* ((context-window (and event (event-window event)))
+	 (context-point (and event (event-point event)))
+	 (context-extents (and context-window
+			       context-point
+			       (extents-at context-point
+					   (window-buffer context-window)
+					   'context-menu)))
+	 (context-menu-items
+	  (apply 'append (mapcar #'(lambda (extent)
+				     (extent-property extent 'context-menu))
+				 context-extents))))
+    (popup-menu
+     (cond ((and global-popup-menu mode-popup-menu)
+	    ;; Merge global-popup-menu and mode-popup-menu
+	    (check-menu-syntax mode-popup-menu)
+	    (let* ((title (car mode-popup-menu))
+		   (items (cdr mode-popup-menu))
+		   mode-filters)
+	      ;; Strip keywords from local menu for attaching them at the top
+	      (while (and items
+			  (keywordp (car items)))
+		;; Push both keyword and its argument.
+		(push (pop items) mode-filters)
+		(push (pop items) mode-filters))
+	      (setq mode-filters (nreverse mode-filters))
+	      ;; If mode-filters contains a keyword already present in
+	      ;; `global-popup-menu', you will probably lose.
+	      (append (list (car global-popup-menu))
+		      mode-filters
+		      (cdr global-popup-menu)
+		      '("---" "---")
+		      (if popup-menu-titles (list title))
+		      (if popup-menu-titles '("---" "---"))
+		      items
+		      context-menu-items)))
+	   (t
+	    (append
+	     (or mode-popup-menu
+		 global-popup-menu
+		 (error "No menu defined in this buffer"))
+	     context-menu-items))))
+
+    (while (popup-up-p)
+      (dispatch-event (next-event)))
+
+    ))
+  
+(defun popup-buffer-menu (event)
+  "Pop up a copy of the Buffers menu (from the menubar) where the mouse is clicked."
+  (interactive "e")
+  (let ((window (and (event-over-text-area-p event) (event-window event)))
+	(bmenu nil))
+    (or window
+	(error "Pointer must be in a normal window"))
+    (select-window window)
+    (if current-menubar
+	(setq bmenu (assoc "%_Buffers" current-menubar)))
+    (if (null bmenu)
+	(setq bmenu (assoc "%_Buffers" default-menubar)))
+    (if (null bmenu)
+	(error "Can't find the Buffers menu"))
+    (popup-menu bmenu)))
+
+(defun popup-menubar-menu (event)
+  "Pop up a copy of menu that also appears in the menubar."
+  (interactive "e")
+  (let ((window (and (event-over-text-area-p event) (event-window event)))
+	popup-menubar)
+    (or window
+	(error "Pointer must be in a normal window"))
+    (select-window window)
+    (and current-menubar (run-hooks 'activate-menubar-hook))
+    ;; #### Instead of having to copy this just to safely get rid of
+    ;; any nil what we should really do is fix up the internal menubar
+    ;; code to just ignore nil if generating a popup menu
+    (setq popup-menubar (delete nil (copy-sequence (or current-menubar
+						       default-menubar))))
+    (popup-menu (cons "%_Menubar Menu" popup-menubar))
+    ))
+
+(defun menu-call-at-event (form &optional event default-behavior-fallback)
+  "Call FORM while temporarily setting point to the position in EVENT.
+NOTE: This is EXPERIMENTAL and may change at any time.
+
+FORM is called the way forms in menu specs are: i.e. if a symbol, it's called
+with `call-interactively', otherwise with `eval'.  EVENT defaults to
+`last-popup-menu-event', making this function especially useful in popup
+menus.  The buffer and point are set temporarily within a `save-excursion'.
+If EVENT is not a mouse event, or was not over a buffer, nothing
+happens unless DEFAULT-BEHAVIOR-FALLBACK is non-nil, in which case the
+FORM is called normally."
+  (or event (setq event last-popup-menu-event))
+  (let ((buf (event-buffer event))
+	(p (event-closest-point event)))
+    (cond ((and buf p (> p 0))
+	   (save-excursion
+	     (set-buffer buf)
+	     (goto-char p)
+	     (if (symbolp form)
+		 (call-interactively form)
+	       (eval form))))
+	  (default-behavior-fallback
+	    (if (symbolp form)
+		(call-interactively form)
+	      (eval form))))))
+
+(global-set-key 'button3 'popup-mode-menu)
+;; shift button3 and shift button2 are reserved for Hyperbole
+(global-set-key '(meta control button3) 'popup-buffer-menu)
+;; The following command is way too dangerous with Custom.
+;; (global-set-key '(meta shift button3) 'popup-menubar-menu)
+
+;; Here's a test of the cool new menu features (from Stig).
+
+;;(setq mode-popup-menu
+;;      '("Test Popup Menu"
+;;        :filter cdr
+;;        ["this item won't appear because of the menu filter" ding t]
+;;        "--:singleLine"
+;;        "singleLine"
+;;        "--:doubleLine"
+;;        "doubleLine"
+;;        "--:singleDashedLine"
+;;        "singleDashedLine"
+;;        "--:doubleDashedLine"
+;;        "doubleDashedLine"
+;;        "--:noLine"
+;;        "noLine"
+;;        "--:shadowEtchedIn"
+;;        "shadowEtchedIn"
+;;        "--:shadowEtchedOut"
+;;        "shadowEtchedOut"
+;;        "--:shadowDoubleEtchedIn"
+;;        "shadowDoubleEtchedIn"
+;;        "--:shadowDoubleEtchedOut"
+;;        "shadowDoubleEtchedOut"
+;;        "--:shadowEtchedInDash"
+;;        "shadowEtchedInDash"
+;;        "--:shadowEtchedOutDash"
+;;        "shadowEtchedOutDash"
+;;        "--:shadowDoubleEtchedInDash"
+;;        "shadowDoubleEtchedInDash"
+;;        "--:shadowDoubleEtchedOutDash"
+;;        "shadowDoubleEtchedOutDash"
+;;        ))
+
 (defun get-popup-menu-response (menu-desc &optional event)
   "Pop up the given menu and wait for a response.
 This blocks until the response is received, and returns the misc-user