[logaling-commit] logaling/logalimacs [master] Update newest popup.el and popwin.el

Back to archive index

null+****@clear***** null+****@clear*****
Sat Jun 9 20:24:49 JST 2012


yuta yamada	2012-06-09 20:24:49 +0900 (Sat, 09 Jun 2012)

  New Revision: f47399b736c0e7eb81b7f5aaccff643577f9c216

  Log:
    Update newest popup.el and popwin.el

  Modified files:
    popup.el
    popwin.el

  Modified: popup.el (+132 -83)
===================================================================
--- popup.el    2012-06-09 17:28:51 +0900 (f61b8c8)
+++ popup.el    2012-06-09 20:24:49 +0900 (1a0db18)
@@ -1,10 +1,10 @@
 ;;; popup.el --- Visual Popup User Interface
 
-;; Copyright (C) 2009, 2010, 2011  Tomohiro Matsuyama
+;; Copyright (C) 2009, 2010, 2011, 2012  Tomohiro Matsuyama
 
 ;; Author: Tomohiro Matsuyama <tomo****@cx4a*****>
 ;; Keywords: lisp
-;; Version: 0.4
+;; Version: 0.5
 
 ;; This program is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
@@ -28,8 +28,7 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl))
+(require 'cl)
 
 
 
@@ -39,12 +38,17 @@
   "Use the optimized column computation routine.
 If there is a problem, please set it nil.")
 
-(defmacro popup-aif (test-form then-form &rest else-forms)
-  "Anaphoric if. Temporary variable `it' is the result of
-TEST-FORM."
+(defmacro popup-aif (test then &rest else)
+  "Anaphoric if."
   (declare (indent 2))
-  `(let ((it ,test-form))
-     (if it ,then-form , at else-forms)))
+  `(let ((it ,test))
+     (if it ,then , at else)))
+
+(defmacro popup-awhen (test &rest body)
+  "Anaphoric when."
+  (declare (indent 1))
+  `(let ((it ,test))
+     (when it , at body)))
 
 (defun popup-x-to-string (x)
   "Convert any object to string effeciently.
@@ -231,11 +235,11 @@ buffer."
   "Background character for scroll-bar.")
 
 (defstruct popup
-  point row column width height min-height direction overlays
+  point row column width height min-height direction overlays keymap
   parent depth
-  face selection-face
+  face mouse-face selection-face
   margin-left margin-right margin-left-cancel scroll-bar symbol
-  cursor offset scroll-top current-height list newlines
+  cursor offset scroll-top current-height list padding
   pattern original-list)
 
 (defun popup-item-propertize (item &rest properties)
@@ -257,7 +261,8 @@ ITEM is not string."
 (defun* popup-make-item (name
                          &key
                          value
-                         popup-face
+                         face
+                         mouse-face
                          selection-face
                          sublist
                          document
@@ -267,7 +272,8 @@ ITEM is not string."
 `popup-item-propertize'."
   (popup-item-propertize name
                          'value value
-                         'popup-face popup-face
+                         'popup-face face
+                         'popup-mouse-face mouse-face
                          'selection-face selection-face
                          'document document
                          'symbol symbol
@@ -276,7 +282,8 @@ ITEM is not string."
 
 (defsubst popup-item-value (item)               (popup-item-property item 'value))
 (defsubst popup-item-value-or-self (item)       (or (popup-item-value item) item))
-(defsubst popup-item-popup-face (item)          (popup-item-property item 'popup-face))
+(defsubst popup-item-face (item)                (popup-item-property item 'popup-face))
+(defsubst popup-item-mouse-face (item)          (popup-item-property item 'popup-mouse-face))
 (defsubst popup-item-selection-face (item)      (popup-item-property item 'selection-face))
 (defsubst popup-item-document (item)            (popup-item-property item 'document))
 (defsubst popup-item-summary (item)             (popup-item-property item 'summary))
@@ -360,7 +367,7 @@ usual."
     (and (eq (overlay-get overlay 'display) nil)
          (eq (overlay-get overlay 'after-string) nil))))
 
-(defun* popup-set-line-item (popup line &key item face margin-left margin-right scroll-bar-char symbol summary)
+(defun* popup-set-line-item (popup line &key item face mouse-face margin-left margin-right scroll-bar-char symbol summary keymap)
   (let* ((overlay (popup-line-overlay popup line))
          (content (popup-create-line-string popup (popup-x-to-string item)
                                             :margin-left margin-left
@@ -371,18 +378,22 @@ usual."
          (prefix (overlay-get overlay 'prefix))
          (postfix (overlay-get overlay 'postfix))
          end)
+    (put-text-property 0 (length content) 'popup-item item content)
+    (put-text-property 0 (length content) 'keymap keymap content)
     ;; Overlap face properties
-    (if (get-text-property start 'face content)
-        (setq start (next-single-property-change start 'face content)))
+    (when (get-text-property start 'face content)
+      (setq start (next-single-property-change start 'face content)))
     (while (and start (setq end (next-single-property-change start 'face content)))
       (put-text-property start end 'face face content)
       (setq start (next-single-property-change end 'face content)))
-    (if start
-        (put-text-property start (length content) 'face face content))
+    (when start
+      (put-text-property start (length content) 'face face content))
+    (when mouse-face
+      (put-text-property 0 (length content) 'mouse-face mouse-face content))
     (unless (overlay-get overlay 'dangle)
       (overlay-put overlay 'display (concat prefix (substring content 0 1)))
       (setq prefix nil
-            content (concat (substring content 1))))
+            content (substring content 1)))
     (overlay-put overlay
                  'after-string
                  (concat prefix
@@ -440,13 +451,15 @@ number at the point."
                       min-height
                       around
                       (face 'popup-face)
+                      mouse-face
                       (selection-face face)
                       scroll-bar
                       margin-left
                       margin-right
                       symbol
                       parent
-                      parent-offset)
+                      parent-offset
+                      keymap)
   "Create a popup instance at POINT with WIDTH and HEIGHT.
 
 MIN-HEIGHT is a minimal height of the popup. The default value is
@@ -474,7 +487,9 @@ SYMBOL is a single character which indicates a kind of the item.
 PARENT is a parent popup instance. If PARENT is omitted, the
 popup will be a root instance.
 
-PARENT-OFFSET is a row offset from the parent popup."
+PARENT-OFFSET is a row offset from the parent popup.
+
+KEYMAP is a keymap that will be put on the popup contents."
   (or margin-left (setq margin-left 0))
   (or margin-right (setq margin-right 0))
   (unless point
@@ -508,13 +523,15 @@ PARENT-OFFSET is a row offset from the parent popup."
                        ;; Calculate direction
                        (popup-calculate-direction height row)))
            (depth (if parent (1+ (popup-depth parent)) 0))
-           (newlines (max 0 (+ (- height (count-lines point (point-max))) (if around 1 0))))
+           padding
            current-column)
-      ;; Case: no newlines at the end of the buffer
-      (when (> newlines 0)
+      ;; Case: no room to put overlays
+      (when (eobp)
         (popup-save-buffer-state
-          (goto-char (point-max))
-          (insert (make-string newlines ?\n))))
+          (let ((begin (point)))
+            (insert " ")
+            (setq padding (make-overlay begin (point)))
+            (overlay-put padding 'evaporate t))))
       
       ;; Case: the popup overflows
       (if overflow
@@ -539,11 +556,11 @@ PARENT-OFFSET is a row offset from the parent popup."
         (setq margin-left-cancel t))
       
       (dotimes (i height)
-        (let (overlay begin w (dangle t) (prefix "") (postfix ""))
+        (let (overlay begin w bottom (dangle t) (prefix "") (postfix ""))
           (when around
-            (popup-vertical-motion column direction))
-	  (setq around t
-                current-column (popup-current-physical-column))
+            (setq bottom (zerop (popup-vertical-motion column direction))))
+	  (setq around t)
+          (setq current-column (if bottom 0 (popup-current-physical-column)))
 
           (when (> current-column column)
             (backward-char)
@@ -551,7 +568,8 @@ PARENT-OFFSET is a row offset from the parent popup."
           (when (< current-column column)
             ;; Extend short buffer lines by popup prefix (line of spaces)
             (setq prefix (make-string
-                          (+ (if (= current-column 0)
+                          (+ (if (and (not bottom)
+                                      (= current-column 0))
                                  (- window-hscroll (current-column))
                                0)
                              (- column current-column))
@@ -559,6 +577,8 @@ PARENT-OFFSET is a row offset from the parent popup."
 
           (setq begin (point))
           (setq w (+ popup-width (length prefix)))
+          (when bottom
+            (setq prefix (concat "\n" prefix)))
           (while (and (not (eolp)) (> w 0))
             (setq dangle nil)
             (decf w (char-width (char-after)))
@@ -566,6 +586,7 @@ PARENT-OFFSET is a row offset from the parent popup."
           (if (< w 0)
               (setq postfix (make-string (- w) ? )))
 
+
           (setq overlay (make-overlay begin (point)))
           (overlay-put overlay 'window window)
           (overlay-put overlay 'dangle dangle)
@@ -588,6 +609,7 @@ PARENT-OFFSET is a row offset from the parent popup."
                             :parent parent
                             :depth depth
                             :face face
+                            :mouse-face mouse-face
                             :selection-face selection-face
                             :margin-left margin-left
                             :margin-right margin-right
@@ -599,8 +621,9 @@ PARENT-OFFSET is a row offset from the parent popup."
                             :scroll-top 0
                             :current-height 0
                             :list nil
-                            :newlines newlines
-                            :overlays overlays)))
+                            :padding padding
+                            :overlays overlays
+                            :keymap keymap)))
         (push it popup-instances)
         it))))
 
@@ -611,14 +634,10 @@ PARENT-OFFSET is a row offset from the parent popup."
     (mapc 'delete-overlay (popup-overlays popup))
     (setf (popup-overlays popup) nil)
     (setq popup-instances (delq popup popup-instances))
-    ;; Restore newlines state
-    (let ((newlines (popup-newlines popup)))
-      (when (> newlines 0)
+    (let ((padding (popup-padding popup)))
+      (when (overlayp padding)
         (popup-save-buffer-state
-          (goto-char (point-max))
-          (dotimes (i newlines)
-            (if (= (char-before) ?\n)
-                (delete-char -1)))))))
+          (delete-region (overlay-start padding) (overlay-end padding))))))
   nil)
 
 (defun popup-draw (popup)
@@ -626,6 +645,7 @@ PARENT-OFFSET is a row offset from the parent popup."
   (loop with height = (popup-height popup)
         with min-height = (popup-min-height popup)
         with popup-face = (popup-face popup)
+        with mouse-face = (popup-mouse-face popup)
         with selection-face = (popup-selection-face popup)
         with list = (popup-list popup)
         with length = (length list)
@@ -638,6 +658,7 @@ PARENT-OFFSET is a row offset from the parent popup."
         with cursor = (popup-cursor popup)
         with scroll-top = (popup-scroll-top popup)
         with offset = (popup-offset popup)
+        with keymap = (popup-keymap popup)
         for o from offset
         for i from scroll-top
         while (< o height)
@@ -645,11 +666,12 @@ PARENT-OFFSET is a row offset from the parent popup."
         for page-index = (* thum-size (/ o thum-size))
         for face = (if (= i cursor)
                        (or (popup-item-selection-face item) selection-face)
-                     (or (popup-item-popup-face item) popup-face))
+                     (or (popup-item-face item) popup-face))
         for empty-char = (propertize " " 'face face)
         for scroll-bar-char = (if scroll-bar
                                   (cond
-                                   ((<= page-size 1)
+                                   ((and (not (eq scroll-bar :always))
+                                         (<= page-size 1))
                                     empty-char)
                                    ((and (> page-size 1)
                                          (>= cursor (* page-index page-size))
@@ -668,11 +690,13 @@ PARENT-OFFSET is a row offset from the parent popup."
         (popup-set-line-item popup o
                              :item item
                              :face face
+                             :mouse-face mouse-face
                              :margin-left margin-left
                              :margin-right margin-right
                              :scroll-bar-char scroll-bar-char
                              :symbol sym
-                             :summary summary)
+                             :summary summary
+                             :keymap keymap)
         
         finally
         ;; Remember current height
@@ -989,6 +1013,11 @@ PROMPT is a prompt string when reading events during event loop."
   "Face for popup menu."
   :group 'popup)
 
+(defface popup-menu-mouse-face
+  '((t (:background "blue" :foreground "white")))
+  "Face for popup menu."
+  :group 'popup)
+
 (defface popup-menu-selection-face
   '((t (:background "steelblue" :foreground "white")))
   "Face for popup menu selection."
@@ -1030,6 +1059,14 @@ PROMPT is a prompt string when reading events during event loop."
                :parent-offset parent-offset
                args)))))
 
+(defun popup-menu-item-of-mouse-event (event)
+  (when (and (consp event)
+             (memq (first event) '(mouse-1 mouse-2 mouse-3 mouse-4 mouse-5)))
+    (let* ((position (second event))
+           (object (elt position 4)))
+      (when (consp object)
+        (get-text-property (cdr object) 'popup-item (car object))))))
+
 (defun popup-menu-read-key-sequence (keymap &optional prompt timeout)
   (catch 'timeout
     (let ((timer (and timeout
@@ -1080,46 +1117,52 @@ PROMPT is a prompt string when reading events during event loop."
                           :help-delay help-delay)
            (keyboard-quit))
       (setq key (popup-menu-read-key-sequence keymap prompt help-delay))
-      (if (null key)
-          (unless (funcall popup-menu-show-quick-help-function menu nil :prompt prompt)
-            (clear-this-command-keys)
-            (push (read-event prompt) unread-command-events))
-        (if (eq (lookup-key (current-global-map) key) 'keyboard-quit)
-            (keyboard-quit))
-        (setq binding (lookup-key keymap key))
-        (cond
-         ((eq binding 'popup-close)
-          (if (popup-parent menu)
-              (return)))
-         ((memq binding '(popup-select popup-open))
-          (let* ((item (popup-selected-item menu))
-                 (sublist (popup-item-sublist item)))
-            (if sublist
-                (popup-aif (popup-cascade-menu sublist
+      (setq binding (lookup-key keymap key))
+      (cond
+       ((or (null key) (zerop (length key)))
+        (unless (funcall popup-menu-show-quick-help-function menu nil :prompt prompt)
+          (clear-this-command-keys)
+          (push (read-event prompt) unread-command-events)))
+       ((eq (lookup-key (current-global-map) key) 'keyboard-quit)
+        (keyboard-quit)
+        (return))
+       ((eq binding 'popup-close)
+        (if (popup-parent menu)
+            (return)))
+       ((memq binding '(popup-select popup-open))
+        (let* ((item (or (popup-menu-item-of-mouse-event (elt key 0))
+                         (popup-selected-item menu)))
+               (index (position item (popup-list menu)))
+               (sublist (popup-item-sublist item)))
+          (unless index (return))
+          (if sublist
+              (popup-aif (let (popup-use-optimized-column-computation)
+                           (popup-cascade-menu sublist
                                                :around nil
-                                               :parent menu
                                                :margin-left (popup-margin-left menu)
                                                :margin-right (popup-margin-right menu)
-                                               :scroll-bar (popup-scroll-bar menu))
-                    (and it (return it)))
-              (if (eq binding 'popup-select)
-                  (return (popup-item-value-or-self item))))))
-         ((eq binding 'popup-next)
-          (popup-next menu))
-         ((eq binding 'popup-previous)
-          (popup-previous menu))
-         ((eq binding 'popup-help)
-          (popup-menu-show-help menu))
-         ((eq binding 'popup-isearch)
-          (popup-isearch menu
-                         :cursor-color isearch-cursor-color
-                         :keymap isearch-keymap
-                         :callback isearch-callback
-                         :help-delay help-delay))
-         ((commandp binding)
-          (call-interactively binding))
-         (t
-          (funcall fallback key (key-binding key))))))))
+                                               :scroll-bar (popup-scroll-bar menu)
+                                               :parent menu
+                                               :parent-offset index))
+                  (and it (return it)))
+            (if (eq binding 'popup-select)
+                (return (popup-item-value-or-self item))))))
+       ((eq binding 'popup-next)
+        (popup-next menu))
+       ((eq binding 'popup-previous)
+        (popup-previous menu))
+       ((eq binding 'popup-help)
+        (popup-menu-show-help menu))
+       ((eq binding 'popup-isearch)
+        (popup-isearch menu
+                       :cursor-color isearch-cursor-color
+                       :keymap isearch-keymap
+                       :callback isearch-callback
+                       :help-delay help-delay))
+       ((commandp binding)
+        (call-interactively binding))
+       (t
+        (funcall fallback key (key-binding key)))))))
 
 (defun* popup-menu* (list
                      &key
@@ -1188,12 +1231,14 @@ isearch canceled. The arguments is whole filtered list of items."
   (setq menu (popup-create point width height
                            :around around
                            :face 'popup-menu-face
+                           :mouse-face 'popup-menu-mouse-face
                            :selection-face 'popup-menu-selection-face
                            :margin-left margin-left
                            :margin-right margin-right
                            :scroll-bar scroll-bar
                            :symbol symbol
-                           :parent parent))
+                           :parent parent
+                           :parent-offset parent-offset))
   (unwind-protect
       (progn
         (popup-set-list menu list)
@@ -1243,6 +1288,10 @@ the sub menu."
     (define-key map (kbd "\C-?") 'popup-help)
 
     (define-key map "\C-s"      'popup-isearch)
+
+    (define-key map [mouse-1]   'popup-select)
+    (define-key map [mouse-4]   'popup-previous)
+    (define-key map [mouse-5]   'popup-next)
     map))
 
 (provide 'popup)

  Modified: popwin.el (+364 -162)
===================================================================
--- popwin.el    2012-06-09 17:28:51 +0900 (a360c64)
+++ popwin.el    2012-06-09 20:24:49 +0900 (fddea8b)
@@ -1,6 +1,6 @@
 ;;; popwin.el --- Popup Window Manager.
 
-;; Copyright (C) 2011  Tomohiro Matsuyama
+;; Copyright (C) 2011, 2012  Tomohiro Matsuyama
 
 ;; Author: Tomohiro Matsuyama <tomo****@cx4a*****>
 ;; Keywords: convenience
@@ -41,17 +41,6 @@
 ;; how to display such buffers. See docstring of
 ;; `popwin:special-display-config' for more information.
 ;;
-;; Instead of a recommended way, you can also use popwin by setting
-;; `special-display-function' like:
-;;
-;;     (require 'popwin)
-;;     (setq special-display-function
-;;           'popwin:special-display-popup-window)
-;;
-;; In this case, you need to change `special-display-buffer-names' or
-;; `special-display-regexps' so that popwin takes care of such
-;; buffers.
-;; 
 ;; The default width/height/position of popup window can be changed by
 ;; setting `popwin:popup-window-width', `popwin:popup-window-height',
 ;; and `popwin:popup-window-position'.  You can also change the
@@ -62,18 +51,34 @@
 
 (eval-when-compile (require 'cl))
 
-(defgroup popwin nil
-  "Popup Window Manager."
-  :group 'convenience
-  :prefix "popwin:")
-
 
 
-;;; Common
-
-(defmacro popwin:save-selected-window (&rest body)
-  "Evaluate BODY saving the selected window."
-  `(with-selected-window (selected-window) , at body))
+;;; Utility
+
+(defun popwin:listify (object)
+  "Return a singleton list of OBJECT if OBJECT is an atom,
+otherwise OBJECT itself."
+  (if (atom object) (list object) object))
+
+(defun popwin:subsitute-in-tree (map tree)
+  (if (consp tree)
+      (cons (popwin:subsitute-in-tree map (car tree))
+            (popwin:subsitute-in-tree map (cdr tree)))
+    (or (cdr (assq tree map)) tree)))
+
+(defun popwin:get-buffer (buffer-or-name &optional if-not-found)
+  "Return a buffer named BUFFER-OR-NAME or BUFFER-OR-NAME itself
+if BUFFER-OR-NAME is a buffer. If BUFFER-OR-NAME is a string and
+such a buffer named BUFFER-OR-NAME not found, a new buffer will
+be returned when IF-NOT-FOUND is :create, or an error reported
+when IF-NOT-FOUND is :error. The default of value of IF-NOT-FOUND
+is :error."
+  (ecase (or if-not-found :error)
+    (:create
+     (get-buffer-create buffer-or-name))
+    (:error
+     (or (get-buffer buffer-or-name)
+         (error "No buffer named %s" buffer-or-name)))))
 
 (defun popwin:switch-to-buffer (buffer-or-name &optional norecord)
   "Call `switch-to-buffer' forcing BUFFER-OF-NAME be displayed in
@@ -83,6 +88,21 @@ the selected window."
         (switch-to-buffer buffer-or-name norecord t)
       (switch-to-buffer buffer-or-name norecord))))
 
+(defun popwin:buried-buffer-p (buffer)
+  "Return t if BUFFER might be thought of as a buried buffer."
+  (eq (car (last (buffer-list))) buffer))
+
+(defun popwin:window-deletable-p (window)
+  "Return t if WINDOW is deletable, meaning that WINDOW is alive
+and not a minibuffer's window, plus there is two or more windows."
+  (and (window-live-p window)
+       (not (window-minibuffer-p window))
+       (not (one-window-p))))
+
+(defmacro popwin:save-selected-window (&rest body)
+  "Evaluate BODY saving the selected window."
+  `(with-selected-window (selected-window) , at body))
+
 (defun popwin:last-selected-window ()
   "Return currently selected window or lastly selected window if
 minibuffer window is selected."
@@ -90,32 +110,40 @@ minibuffer window is selected."
       (minibuffer-selected-window)
     (selected-window)))
 
-(defun popwin:buried-buffer-p (buffer)
-  "Return t if BUFFER might be thought of as a buried buffer."
-  (eq (car (last (buffer-list))) buffer))
+
 
-(defun popwin:called-interactively-p ()
-  (with-no-warnings
-    (if (>= emacs-major-version 23)
-        (called-interactively-p 'any)
-      (called-interactively-p))))
+;;; Common
 
-(defvar popwin:empty-buffer nil
-  "Marker buffer of indicating a window of the buffer is being a
-popup window.")
+(defvar popwin:debug nil)
 
-(defun popwin:empty-buffer ()
-  (if (buffer-live-p popwin:empty-buffer)
-      popwin:empty-buffer
-    (setq popwin:empty-buffer
-          (get-buffer-create " *popwin-empty*"))))
+(defvar popwin:dummy-buffer nil)
+
+(defun popwin:dummy-buffer ()
+  (if (buffer-live-p popwin:dummy-buffer)
+      popwin:dummy-buffer
+    (setq popwin:dummy-buffer (get-buffer-create " *popwin-dummy*"))))
+
+(defun popwin:kill-dummy-buffer ()
+  (when (buffer-live-p popwin:dummy-buffer)
+    (kill-buffer popwin:dummy-buffer))
+  (setq popwin:dummy-buffer nil))
+
+(defun popwin:window-point (window)
+  (if (eq window (selected-window))
+      (with-current-buffer (window-buffer window) (point))
+    (window-point window)))
+
+(defun popwin:set-window-point (window point)
+  "Forcely set window-point."
+  (with-current-buffer (popwin:dummy-buffer)
+    (set-window-point window point)))
 
 (defun popwin:window-trailing-edge-adjustable-p (window)
   "Return t if a trailing edge of WINDOW is adjustable."
   (let ((next-window (next-window window)))
     (and (not (eq next-window (frame-first-window)))
          (not (eq (window-buffer next-window)
-                  (popwin:empty-buffer))))))
+                  (popwin:dummy-buffer))))))
 
 (defun* popwin:adjust-window-edges (window
                                     edges
@@ -138,7 +166,9 @@ HFACTOR, and vertical factor VFACTOR."
 (defun popwin:window-config-tree-1 (node)
   (if (windowp node)
       (list 'window
+            node
             (window-buffer node)
+            (popwin:window-point node)
             (window-edges node)
             (eq (selected-window) node))
     (destructuring-bind (dir edges . windows) node
@@ -154,24 +184,25 @@ with persistent representations."
 
 (defun popwin:replicate-window-config (window node hfactor vfactor)
   "Replicate NODE of window configuration on WINDOW with
-horizontal factor HFACTOR, and vertical factor VFACTOR."
+horizontal factor HFACTOR, and vertical factor VFACTOR. The
+return value is a association list of mapping from old-window to
+new-window."
   (if (eq (car node) 'window)
-      (destructuring-bind (buffer edges selected)
+      (destructuring-bind (old-win buffer point edges selected)
           (cdr node)
         (popwin:adjust-window-edges window edges hfactor vfactor)
         (with-selected-window window
           (popwin:switch-to-buffer buffer t))
+        (popwin:set-window-point window point)
         (when selected
-          (select-window window)))
+          (select-window window))
+        `((,old-win . ,window)))
     (destructuring-bind (dir edges . windows) node
       (loop while windows
-            for w1 = (pop windows) then w2
-            for w2 = (pop windows)
-            do
-            (let ((new-window (split-window window nil (not dir))))
-              (popwin:replicate-window-config window w1 hfactor vfactor)
-              (popwin:replicate-window-config new-window w2 hfactor vfactor)
-              (setq window new-window))))))
+            for sub-node = (pop windows)
+            for win = window then next-win
+            for next-win = (and windows (split-window win nil (not dir)))
+            append (popwin:replicate-window-config win sub-node hfactor vfactor)))))
 
 (defun popwin:restore-window-outline (node outline)
   "Restore window outline accoding to the structures of NODE
@@ -181,8 +212,10 @@ which is a node of `window-tree' and OUTLINE which is a node of
    ((and (windowp node)
          (eq (car outline) 'window))
     ;; same window
-    (let ((edges (nth 2 outline)))
-      (popwin:adjust-window-edges node edges)))
+    (let ((point (nth 3 outline))
+          (edges (nth 4 outline)))
+      (popwin:adjust-window-edges node edges)
+      (popwin:set-window-point node point)))
    ((or (windowp node)
         (not (eq (car node) (car outline))))
     ;; different structure
@@ -199,11 +232,11 @@ which is a node of `window-tree' and OUTLINE which is a node of
 
 (defun popwin:position-horizontal-p (position)
   "Return t if POSITION is hozirontal."
-  (memq position '(left right)))
+  (and (memq position '(left :left right :right)) t))
 
 (defun popwin:position-vertical-p (position)
   "Return t if POSITION is vertical."
-  (memq position '(top bottom)))
+  (and (memq position '(top :top bottom :bottom)) t))
 
 (defun popwin:create-popup-window-1 (window size position)
   "Create a new window with SIZE at POSITION of WINDOW. The
@@ -226,7 +259,7 @@ return value is a list of a master window and the popup window."
 
 (defun* popwin:create-popup-window (&optional (size 15) (position 'bottom) (adjust t))
   "Create a popup window with SIZE on the frame.  If SIZE
-isinteger, the size of the popup window will be SIZE. If SIZE is
+is integer, the size of the popup window will be SIZE. If SIZE is
 float, the size of popup window will be a multiplier of SIZE and
 frame-size. can be an integer and a float. If ADJUST is t, all of
 windows will be adjusted to fit the frame. POSITION must be one
@@ -257,14 +290,19 @@ window-configuration."
           (popwin:create-popup-window-1 root-win size position)
         ;; Mark popup-win being a popup window.
         (with-selected-window popup-win
-          (popwin:switch-to-buffer (popwin:empty-buffer) t))
-        (popwin:replicate-window-config master-win root hfactor vfactor)
-        (list master-win popup-win)))))
+          (popwin:switch-to-buffer (popwin:dummy-buffer) t))
+        (let ((win-map (popwin:replicate-window-config master-win root hfactor vfactor)))
+          (list master-win popup-win win-map))))))
 
 
 
 ;;; Common User Interface
 
+(defgroup popwin nil
+  "Popup Window Manager."
+  :group 'convenience
+  :prefix "popwin:")
+
 (defcustom popwin:popup-window-position 'bottom
   "Default popup window position. This must be one of (left top right
 bottom)."
@@ -293,12 +331,15 @@ frame when a popup window is shown."
   :type 'boolean
   :group 'popwin)
 
+(defvar popwin:context-stack nil)
+
 (defvar popwin:popup-window nil
   "Main popup window instance.")
 
 (defvar popwin:popup-buffer nil
   "Buffer of currently shown in the popup window.")
 
+;; Deprecated
 (defvar popwin:master-window nil
   "Master window of a popup window.")
 
@@ -309,6 +350,10 @@ the popup window.")
 (defvar popwin:selected-window nil
   "Last selected window when the popup window is shown.")
 
+(defvar popwin:popup-window-dedicated-p nil
+  "Non-nil means the popup window is dedicated to the original
+popup buffer.")
+
 (defvar popwin:popup-window-stuck-p nil
   "Non-nil means the popup window has been stuck.")
 
@@ -316,16 +361,73 @@ the popup window.")
   "Original window outline which is obtained by
 `popwin:window-config-tree'.")
 
+(defvar popwin:window-map nil
+  "Mapping from old windows to new windows.")
+
 (defvar popwin:close-popup-window-timer nil
   "Timer of closing the popup window.")
 
 (defvar popwin:close-popup-window-timer-interval 0.01
   "Interval of `popwin:close-popup-window-timer'.")
 
+(defvar popwin:before-popup-hook nil)
+
+(defvar popwin:after-popup-hook nil)
+
+(symbol-macrolet ((context-vars '(popwin:popup-window
+                                  popwin:popup-buffer
+                                  popwin:master-window
+                                  popwin:focus-window
+                                  popwin:selected-window
+                                  popwin:popup-window-dedicated-p
+                                  popwin:popup-window-stuck-p
+                                  popwin:window-outline
+                                  popwin:window-map)))
+  (defun popwin:valid-context-p (context)
+    (window-live-p (plist-get context 'popwin:popup-window)))
+
+  (defun popwin:current-context ()
+    (loop for var in context-vars
+          collect var
+          collect (symbol-value var)))
+  
+  (defun popwin:use-context (context)
+    (loop for var = (pop context)
+          for val = (pop context)
+          while var
+          do (set var val)))
+
+  (defun popwin:push-context ()
+    (push (popwin:current-context) popwin:context-stack))
+
+  (defun popwin:pop-context ()
+    (popwin:use-context (pop popwin:context-stack)))
+
+  (defun* popwin:find-context-for-buffer (buffer &key valid-only)
+    (loop with stack = popwin:context-stack
+          for context = (pop stack)
+          while context
+          if (and (eq buffer (plist-get context 'popwin:popup-buffer))
+                  (or (not valid-only)
+                      (popwin:valid-context-p context)))
+          return (list context stack))))
+
 (defun popwin:popup-window-live-p ()
   "Return t if `popwin:popup-window' is alive."
   (window-live-p popwin:popup-window))
 
+(defun* popwin:update-window-reference (symbol
+                                        &key
+                                        (map popwin:window-map)
+                                        safe
+                                        recursive)
+  (unless (and safe (not (boundp symbol)))
+    (let ((value (symbol-value symbol)))
+      (set symbol
+           (if recursive
+               (popwin:subsitute-in-tree map value)
+             (or (cdr (assq value map)) value))))))
+
 (defun popwin:start-close-popup-window-timer ()
   (or popwin:close-popup-window-timer
       (setq popwin:close-popup-window-timer
@@ -340,67 +442,94 @@ the popup window.")
 
 (defun popwin:close-popup-window-timer ()
   (condition-case var
-      (popwin:close-popup-window-if-necessary
-       (popwin:should-close-popup-window-p))
-    (error (message "popwin:close-popup-window-timer: error: %s" var))))
+      (popwin:close-popup-window-if-necessary)
+    (error
+     (message "popwin:close-popup-window-timer: error: %s" var)
+     (when popwin:debug (backtrace)))))
 
 (defun popwin:close-popup-window (&optional keep-selected)
   "Close the popup window and restore to the previous window
 configuration. If KEEP-SELECTED is non-nil, the lastly selected
 window will not be selected."
   (interactive)
-  (unwind-protect
-      (when popwin:popup-window
-        (popwin:stop-close-popup-window-timer)
-        (when (and (popwin:popup-window-live-p)
-                   (window-live-p popwin:master-window))
-          (delete-window popwin:popup-window))
-        (popwin:restore-window-outline (car (window-tree))
-                                       popwin:window-outline)
-        (when (and (not keep-selected)
-                   (window-live-p popwin:selected-window))
-          (select-window popwin:selected-window)))
-    (setq popwin:popup-buffer nil
-          popwin:popup-window nil
-          popwin:focus-window nil
-          popwin:selected-window nil
-          popwin:popup-window-stuck-p nil
-          popwin:window-outline nil)))
-
-(defun popwin:should-close-popup-window-p ()
-  "Return t if popwin should close the popup window
-immediately. It might be useful if this is customizable
-function."
-  (and popwin:popup-window
-       (or (and (eq last-command 'keyboard-quit)
-                (eq last-command-event ?\C-g))
-           (not (buffer-live-p popwin:popup-buffer))
-           (popwin:buried-buffer-p popwin:popup-buffer))))
-
-(defun popwin:close-popup-window-if-necessary (&optional force)
-  "Close the popup window if another window has been selected. If
-FORCE is non-nil, this function tries to close the popup window
-immediately if possible, and the lastly selected window will be
-selected again."
+  (when popwin:popup-window
+    (unwind-protect
+        (progn
+          (when (popwin:window-deletable-p popwin:popup-window)
+            (delete-window popwin:popup-window))
+          (popwin:restore-window-outline (car (window-tree)) popwin:window-outline)
+          (when (and (not keep-selected)
+                     (window-live-p popwin:selected-window))
+            (select-window popwin:selected-window)))
+      (popwin:pop-context)
+      ;; Cleanup if no context left.
+      (when (null popwin:context-stack)
+        (popwin:kill-dummy-buffer)
+        (popwin:stop-close-popup-window-timer)))))
+
+(defun popwin:close-popup-window-if-necessary ()
+  "Close the popup window if necessary. The all situations where
+the popup window will be closed are followings:
+
+* `C-g' has been pressed.
+* The popup buffer has been killed.
+* The popup buffer has been buried.
+* The popup buffer has been changed if the popup window is
+  dedicated to the buffer.
+* Another window has been selected."
   (when popwin:popup-window
     (let* ((window (selected-window))
+           (window-point (popwin:window-point window))
+           (window-buffer (window-buffer window))
            (minibuf-window-p (window-minibuffer-p window))
+           (reading-from-minibuf
+            (and minibuf-window-p
+                 (minibuffer-prompt)
+                 t))
+           (quit-requested
+            (and (eq last-command 'keyboard-quit)
+                 (eq last-command-event ?\C-g)))
            (other-window-selected
             (and (not (eq window popwin:focus-window))
                  (not (eq window popwin:popup-window))))
-           (popup-buffer-still-working
-            (and (buffer-live-p popwin:popup-buffer)
-                 (not (popwin:buried-buffer-p popwin:popup-buffer))))
-           (not-stuck-or-closed
-            (or (not popwin:popup-window-stuck-p)
-                (not (popwin:popup-window-live-p)))))
-      (if (or force
-              (and (not minibuf-window-p)
-                   not-stuck-or-closed
-                   other-window-selected))
+           (orig-this-command this-command)
+           (popup-buffer-alive
+            (buffer-live-p popwin:popup-buffer))
+           (popup-buffer-buried
+            (popwin:buried-buffer-p popwin:popup-buffer))
+           (popup-buffer-changed-despite-of-dedicated
+            (and popwin:popup-window-dedicated-p
+                 (or (not other-window-selected)
+                     (not reading-from-minibuf))
+                 (buffer-live-p window-buffer)
+                 (not (eq popwin:popup-buffer window-buffer))))
+           (popup-window-alive (popwin:popup-window-live-p)))
+      (when (or quit-requested
+                (not popup-buffer-alive)
+                popup-buffer-buried
+                popup-buffer-changed-despite-of-dedicated
+                (not popup-window-alive)
+                (and other-window-selected
+                     (not minibuf-window-p)
+                     (not popwin:popup-window-stuck-p)))
+        (when (and quit-requested
+                   (null orig-this-command))
+          (setq this-command 'popwin:close-popup-window)
+          (run-hooks 'pre-command-hook))
+        (if reading-from-minibuf
+            (progn
+              (popwin:close-popup-window)
+              (select-window (minibuffer-window)))
           (popwin:close-popup-window
            (and other-window-selected
-                popup-buffer-still-working))))))
+                (and popup-buffer-alive
+                     (not popup-buffer-buried))))
+          (when popup-buffer-changed-despite-of-dedicated
+            (popwin:switch-to-buffer window-buffer)
+            (goto-char window-point)))
+        (when (and quit-requested
+                   (null orig-this-command))
+          (run-hooks 'post-command-hook))))))
 
 (defun* popwin:popup-buffer (buffer
                              &key
@@ -408,6 +537,7 @@ selected again."
                              (height popwin:popup-window-height)
                              (position popwin:popup-window-position)
                              noselect
+                             dedicated
                              stick)
   "Show BUFFER in a popup window and return the popup window. If
 NOSELECT is non-nil, the popup window will not be selected. If
@@ -417,25 +547,36 @@ that case, the buffer of the popup window will be replaced with
 BUFFER."
   (interactive "BPopup buffer:\n")
   (setq buffer (get-buffer buffer))
-  (unless (popwin:popup-window-live-p)
-    (let ((win-outline (car (popwin:window-config-tree))))
-      (destructuring-bind (master-win popup-win)
-          (let ((size (if (popwin:position-horizontal-p position) width height))
-                (adjust popwin:adjust-other-windows))
-            (popwin:create-popup-window size position adjust))
-        (setq popwin:popup-window popup-win
-              popwin:master-window master-win
-              popwin:window-outline win-outline
-              popwin:selected-window (selected-window))
-        (popwin:start-close-popup-window-timer))))
-  (with-selected-window popwin:popup-window
-    (popwin:switch-to-buffer buffer))
-  (setq popwin:popup-buffer buffer
-        popwin:popup-window-stuck-p stick)
+  (popwin:push-context)
+  (run-hooks 'popwin:before-popup-hook)
+  (multiple-value-bind (context context-stack)
+      (popwin:find-context-for-buffer buffer :valid-only t)
+    (if context
+        (progn
+          (popwin:use-context context)
+          (setq popwin:context-stack context-stack))
+      (let ((win-outline (car (popwin:window-config-tree))))
+        (destructuring-bind (master-win popup-win win-map)
+            (let ((size (if (popwin:position-horizontal-p position) width height))
+                  (adjust popwin:adjust-other-windows))
+              (popwin:create-popup-window size position adjust))
+          (setq popwin:popup-window popup-win
+                popwin:master-window master-win
+                popwin:window-outline win-outline
+                popwin:window-map win-map
+                popwin:selected-window (selected-window)))
+        (popwin:update-window-reference 'popwin:context-stack :recursive t)
+        (popwin:start-close-popup-window-timer))
+      (with-selected-window popwin:popup-window
+        (popwin:switch-to-buffer buffer))
+      (setq popwin:popup-buffer buffer
+            popwin:popup-window-dedicated-p dedicated
+            popwin:popup-window-stuck-p stick)))
   (if noselect
       (setq popwin:focus-window popwin:selected-window)
     (setq popwin:focus-window popwin:popup-window)
     (select-window popwin:popup-window))
+  (run-hooks 'popwin:after-popup-hook)
   popwin:popup-window)
 
 (defun popwin:select-popup-window ()
@@ -457,9 +598,9 @@ be closed by `popwin:close-popup-window'."
 
 ;;; Special Display
 
-(defmacro popwin:without-special-display (&rest body)
+(defmacro popwin:without-special-displaying (&rest body)
   "Evaluate BODY without special displaying."
-  `(let (display-buffer-function special-display-function) , at body))
+  `(let (display-buffer-function) , at body))
 
 (defcustom popwin:special-display-config
   '(("*Help*")
@@ -493,6 +634,12 @@ empty. Available keywords are following:
   noselect: If the value is non-nil, the popup window will not be
     selected when it is shown.
 
+  dedicated: If the value is non-nil, the popup window will be
+    dedicated to the original popup buffer. In this case, when
+    another buffer is selected in the popup window, the popup
+    window will be closed immedicately and the selected buffer
+    will be shown on the previously selected window.
+
   stick: If the value is non-nil, the popup window will be stuck
     when it is shown.
 
@@ -507,46 +654,62 @@ buffers will be shown at the left of the frame with width 80."
 
 (defun popwin:original-display-buffer (buffer &optional not-this-window)
   "Call `display-buffer' for BUFFER without special displaying."
-  (popwin:without-special-display
-   ;; Close the popup window here so that the popup window won't to
-   ;; be splitted.
-   (when (and (eq (selected-window) popwin:popup-window)
-              (not (same-window-p (buffer-name buffer))))
-     (popwin:close-popup-window))
+  (popwin:without-special-displaying
+   (let ((same-window
+          (or (same-window-p (buffer-name buffer))
+              (and (>= emacs-major-version 24)
+                   (boundp 'action)
+                   (consp action)
+                   (eq (car action) 'display-buffer-same-window)))))
+     ;; Close the popup window here so that the popup window won't to
+     ;; be splitted.
+     (when (and (eq (selected-window) popwin:popup-window)
+                (not same-window))
+       (popwin:close-popup-window)))
    (if (and (>= emacs-major-version 24)
             (boundp 'action)
             (boundp 'frame))
        ;; Use variables ACTION and FRAME which are formal parameters
        ;; of DISPLAY-BUFFER.
+       ;; 
+       ;; TODO use display-buffer-alist instead of
+       ;; display-buffer-function.
        (display-buffer buffer action frame)
      (display-buffer buffer not-this-window))))
 
-(defun* popwin:display-buffer-1 (buffer-or-name &key default-config-keywords if-buffer-not-found if-config-not-found)
+(defun* popwin:display-buffer-1 (buffer-or-name
+                                 &key
+                                 default-config-keywords
+                                 (if-buffer-not-found :create)
+                                 if-config-not-found)
   "Display BUFFER-OR-NAME, if possible, in a popup
 window. Otherwise call IF-CONFIG-NOT-FOUND with BUFFER-OR-NAME if
-it is non-nil. If IF-CONFIG-NOT-FOUND is nil, `display-buffer'
-will be called with `special-display-function' nil. If
-IF-BUFFER-NOT-FOUND is :create, create a buffer named
-BUFFER-OR-NAME if there is no such a
-buffer. DEFAULT-CONFIG-KEYWORDS is a property list which
-specifies default values of the selected config."
-  (loop with buffer = (if (eq if-buffer-not-found :create)
-                          (get-buffer-create buffer-or-name)
-                        (get-buffer buffer-or-name))
+the value is a function. If IF-CONFIG-NOT-FOUND is nil,
+`popwin:popup-buffer' will be called. IF-BUFFER-NOT-FOUND
+indicates what happens when there is no such buffers. If the
+value is :create, create a new buffer named BUFFER-OR-NAME. If
+the value is :error, report an error. The default value
+is :create. DEFAULT-CONFIG-KEYWORDS is a property list which
+specifies default values of the config."
+  (loop with buffer = (popwin:get-buffer buffer-or-name if-buffer-not-found)
         with name = (buffer-name buffer)
         with mode = (buffer-local-value 'major-mode buffer)
         with win-width = popwin:popup-window-width
         with win-height = popwin:popup-window-height
         with win-position = popwin:popup-window-position
         with win-noselect
+        with win-dedicated
         with win-stick
         with found
         until found
-        for config in popwin:special-display-config
-        for (pattern . keywords) = (if (atom config) (list config) config) do
-        (destructuring-bind (&key regexp width height position noselect stick)
+        for config in (if if-config-not-found
+                          popwin:special-display-config
+                        `(, at popwin:special-display-config t))
+        for (pattern . keywords) = (popwin:listify config) do
+        (destructuring-bind (&key regexp width height position noselect dedicated stick)
             (append keywords default-config-keywords)
-          (let ((matched (cond ((and (stringp pattern) regexp)
+          (let ((matched (cond ((eq pattern t) t)
+                               ((and (stringp pattern) regexp)
                                 (string-match pattern name))
                                ((stringp pattern)
                                 (string= pattern name))
@@ -561,19 +724,19 @@ specifies default values of the selected config."
                     win-height (or height win-height)
                     win-position (or position win-position)
                     win-noselect noselect
+                    win-dedicated dedicated
                     win-stick stick))))
         finally return
-        (if (or found
-                (null if-config-not-found))
-            (progn
-              (setq popwin:last-display-buffer buffer)
-              (popwin:popup-buffer buffer
-                                   :width win-width
-                                   :height win-height
-                                   :position win-position
-                                   :noselect (or (minibufferp) win-noselect)
-                                   :stick win-stick))
-          (funcall if-config-not-found buffer))))
+        (if (not found)
+            (funcall if-config-not-found buffer)
+          (setq popwin:last-display-buffer buffer)
+          (popwin:popup-buffer buffer
+                               :width win-width
+                               :height win-height
+                               :position win-position
+                               :noselect (or (minibufferp) win-noselect)
+                               :dedicated win-dedicated
+                               :stick win-stick))))
 
 (defun popwin:display-buffer (buffer-or-name &optional not-this-window)
   "Display BUFFER-OR-NAME, if possible, in a popup window, or as
@@ -583,12 +746,12 @@ usual. This function can be used as a value of
   (popwin:display-buffer-1
    buffer-or-name
    :if-config-not-found
-   (unless (popwin:called-interactively-p)
+   (unless (called-interactively-p)
      (lambda (buffer)
        (popwin:original-display-buffer buffer not-this-window)))))
 
 (defun popwin:special-display-popup-window (buffer &rest ignore)
-  "The `special-display-function' with a popup window."
+  "Obsolete."
   (popwin:display-buffer-1 buffer))
 
 (defun popwin:display-last-buffer ()
@@ -599,6 +762,43 @@ usual. This function can be used as a value of
       (popwin:display-buffer-1 popwin:last-display-buffer)
     (error "No popup window displayed")))
 
+(defun* popwin:pop-to-buffer-1 (buffer
+                                &key
+                                default-config-keywords
+                                other-window
+                                norecord)
+  (popwin:display-buffer-1 buffer
+                           :default-config-keywords default-config-keywords
+                           :if-config-not-found
+                           (lambda (buffer)
+                             (pop-to-buffer buffer other-window norecord))))
+
+(defun popwin:pop-to-buffer (buffer &optional other-window norecord)
+  "Same as `pop-to-buffer' except that this function will use
+`popwin:display-buffer-1' instead of `display-buffer'."
+  (popwin:pop-to-buffer-1 buffer
+                          :other-window other-window
+                          :norecord norecord))
+
+
+
+;;; Universal Display
+
+(defcustom popwin:universal-display-config '(t)
+  "Same as `popwin:special-display-config' except that this will
+be used for `popwin:universal-display'."
+  :group 'popwin)
+
+(defun popwin:universal-display ()
+  "Call the following command interactively with letting
+`popwin:special-display-config' be
+`popwin:universal-display-config'. This wil be useful when
+displaying buffers in popup windows temporarily."
+  (interactive)
+  (let ((command (key-binding (read-key-sequence "" t)))
+        (popwin:special-display-config popwin:universal-display-config))
+    (call-interactively command)))
+
 
 
 ;;; Extensions
@@ -639,23 +839,25 @@ usual. This function can be used as a value of
 ;;; Keymaps
 
 (defvar popwin:keymap
-  (let ((map (make-keymap)))
-    (define-key map "b" 'popwin:popup-buffer)
+  (let ((map (make-sparse-keymap)))
+    (define-key map "b"    'popwin:popup-buffer)
     (define-key map "\C-b" 'popwin:popup-buffer)
     (define-key map "\M-b" 'popwin:popup-buffer-tail)
-    (define-key map "o" 'popwin:display-buffer)
+    (define-key map "o"    'popwin:display-buffer)
     (define-key map "\C-o" 'popwin:display-buffer)
-    (define-key map "p" 'popwin:display-last-buffer)
+    (define-key map "p"    'popwin:display-last-buffer)
     (define-key map "\C-p" 'popwin:display-last-buffer)
-    (define-key map "f" 'popwin:find-file)
+    (define-key map "f"    'popwin:find-file)
     (define-key map "\C-f" 'popwin:find-file)
     (define-key map "\M-f" 'popwin:find-file-tail)
-    (define-key map "s" 'popwin:select-popup-window)
+    (define-key map "s"    'popwin:select-popup-window)
     (define-key map "\C-s" 'popwin:select-popup-window)
     (define-key map "\M-s" 'popwin:stick-popup-window)
-    (define-key map "0" 'popwin:close-popup-window)
-    (define-key map "m" 'popwin:messages)
+    (define-key map "0"    'popwin:close-popup-window)
+    (define-key map "m"    'popwin:messages)
     (define-key map "\C-m" 'popwin:messages)
+    (define-key map "u"    'popwin:universal-display)
+    (define-key map "\C-u" 'popwin:universal-display)
     map)
   "Default keymap for popwin commands. Use like:
 \(global-set-key (kbd \"C-x C-p\") popwin:keymap\)




More information about the logaling-commit mailing list
Back to archive index