Subject: Re: bug#1806: dired-pop-to-buffer in wrong place

From: martin rudalics <[email-masked]>
Date: Tue, 20 Jan 2009 16:59:17 +0100

Please have a look at the attached (only lightly tested) patch.  I
didn't provide a similar service for `special-display-buffer-names' and
`special-display-regexps' yet.

`display-buffer' and `pop-to-buffer' behave "as usual" as long as the
default values for `pop-up-windows', NOT-THIS-WINDOW, and OTHER-WINDOW
are used.  An application calling `display-buffer' or `pop-to-buffer'
can specify the preferable window to split by setting NOT-THIS-WINDOW or
OTHER-WINDOW appropriately.  Users can specify their preferences (and
override the application) by setting `pop-up-windows' appropriately.

martin

*** window.el.~1.179.~	2009-01-16 17:41:41.046875000 +0100
--- window.el	2009-01-20 16:58:01.062500000 +0100
***************
*** 789,797 ****
    :version "21.1"
    :group 'windows)
  
  (defcustom pop-up-windows t
!   "Non-nil means `display-buffer' should make a new window."
!   :type 'boolean
    :group 'windows)
  
  (defcustom split-height-threshold 80
--- 789,843 ----
    :version "21.1"
    :group 'windows)
  
+ ;; Make sure the following two variables always use the same values
+ ;; (sans `t').
+ (defconst pop-up-windows-values
+   '(largest lru selected below right bottom-left top-left)
+   "List of preferences supported by `pop-up-windows'.")
+ 
  (defcustom pop-up-windows t
!   "Non-nil means `display-buffer' is allowed to make a new window.
! A non-empty list specifies the windows `display-buffer' will
! consider for splitting on the respective frame.  The following
! entries are supported:
! 
!  largest ...... largest window
!  lru .......... least recently used window
!  selected ..... the frame's selected window
!  below ........ window below frame's selected window
!  right ........ window right of frame's selected window
!  bottom-left .. window in lower left corner of frame
!  top-left ..... window in upper left corner of frame
!  t ............ window specified by application
! 
! Note that when `display-buffer' finds the value t, it will use
! the value specified by the function that called `display-buffer'
! provided there is one, and ignore this entry otherwise.  If the
! entire list equals `(t)', `display-buffer' will pop up a new
! window if and only if the application specified value produces
! one.  If this list does not contain the value t, `display-buffer'
! will ignore any value specified by an application.
! 
! The default value t stands for the list `(t largest lru)'.  This
! means that Emacs will first try a value specified by the
! application that called `display-buffer'.  If there's no such
! value, or the window specified by that value doesn't exist, or
! that window can't be split, `display-buffer' will try to split
! the largest window and, if that fails too, the least recently
! used window."
!   :type '(choice
! 	  (const :tag "Disallow" nil)
! 	  (const :tag "Allow" t)
! 	  (repeat :tag "Preferences"
! 		  (choice
! 		   (const :tag "Largest" largest)
! 		   (const :tag "Least Recently Used" lru)
! 		   (const :tag "Selected" selected)
! 		   (const :tag "Below Selected" below)
! 		   (const :tag "Right of Selected" right)
! 		   (const :tag "Bottom Left" bottom-left)
! 		   (const :tag "Top Left" top-left)
! 		   (const :tag "Application Specified" t))))
    :group 'windows)
  
  (defcustom split-height-threshold 80
***************
*** 957,962 ****
--- 1003,1099 ----
  	  (enlarge-window (/ (- (window-height window) (window-height)) 2))
  	(error nil)))))
  
+ (defun window--probe-windows (frame windows probe)
+   "Find window in WINDOWS satisfying PROBE on FRAME.
+ To be called by `window--pop-up-window' exclusively."
+   (cond
+    ((eq probe 'selected)
+     (let ((window (frame-selected-window frame)))
+       (car (member window windows))))
+    ((eq probe 'largest)
+     (get-largest-window frame t))
+    ((eq probe 'lru)
+     (get-lru-window frame t))
+    ((memq probe '(below right bottom-left top-left))
+     (let ((this-edges
+ 	   (window-edges
+ 	    (cond
+ 	     ((memq probe '(below right))
+ 	      (frame-selected-window frame))
+ 	     ((memq probe '(top-left bottom-left))
+ 	      (frame-root-window frame)))))
+ 	  edges)
+       (catch 'found
+ 	(dolist (window windows)
+ 	  (setq edges (window-edges window))
+ 	  (when (or (and (eq probe 'below)
+ 			 (= (nth 1 edges) (nth 3 this-edges))
+ 			 (= (nth 2 edges) (nth 2 this-edges)))
+ 		    (and (eq probe 'right)
+ 			 (= (nth 0 edges) (nth 2 this-edges))
+ 			 (= (nth 1 edges) (nth 1 this-edges)))
+ 		    (and (eq probe 'top-left)
+ 			 (= (nth 0 edges) (nth 0 this-edges))
+ 			 (= (nth 1 edges) (nth 1 this-edges)))
+ 		    (and (eq probe 'bottom-left)
+ 			 (= (nth 0 edges) (nth 0 this-edges))
+ 			 (= (nth 3 edges) (nth 3 this-edges))))
+ 	    (throw 'found window))))))))
+ 
+ (defun window--pop-up-window (buffer frame user system)
+   "Pop up a new window for BUFFER on FRAME.
+ FRAME nil means the selected frame.  If FRAME cannot be split,
+ try to split a window on the last nonminibuffer frame instead.
+ 
+ USER, if not nil and not t, is a list specifying the user's
+ preferences for finding the window to split.  For a list of
+ supported values see the variable `pop-up-windows-values'.  The
+ special value t means to use SYSTEM, if applicable, instead.  For
+ the meaning of all values, consult the documentation of the
+ variable `pop-up-windows'.  If USER equals t, this means to use
+ the list `(t largest lru)' instead.
+ 
+ SYSTEM, provided it is any of `pop-up-windows-values',
+ substitutes an occurence of t within USER."
+   ;; FRAME nil means use the selected frame.
+   (setq frame (or frame (selected-frame)))
+   ;; But make sure our frame is not a minibuffer frame.
+   (when (window-minibuffer-p (frame-selected-window frame))
+     (setq frame (last-nonminibuffer-frame)))
+   (unless (and (frame-parameter frame 'unsplittable)
+ 	       ;; If the frame cannot be split have a look at
+ 	       ;; `last-nonminibuffer-frame'.
+ 	       (or (not (eq frame (selected-frame)))
+ 		   (not (setq frame (last-nonminibuffer-frame)))
+ 		   (not (window--frame-usable-p frame))
+ 		   (frame-parameter frame 'unsplittable)))
+     (when (eq user t)
+       ;; USER t means use '(t largest lru) instead.
+       (setq user '(t largest lru)))
+     (let* ((this-window (frame-selected-window frame))
+ 	   (windows (window-list frame 'nomini this-window))
+ 	   (probe (car user))
+ 	   window window-to-use)
+       ;; While we have not yet found a usable window, scan `windows' for
+       ;; a windows satisfying `probe'.
+       (while (and (not window-to-use) windows probe)
+ 	(setq window
+ 	      (if (eq probe t)
+ 		  ;; SYSTEM overrides t in USER.
+ 		  (when (memq system pop-up-windows-values)
+ 		    ;; SYSTEM is a valid value, so use it.
+ 		    (window--probe-windows frame windows system))
+ 		(window--probe-windows frame windows probe)))
+ 	(when window
+ 	  (setq window-to-use (window--try-to-split-window window)))
+ 	(unless window-to-use
+ 	  ;; Don't consider `window' again.
+ 	  (setq windows (remove window windows))
+ 	  (setq user (cdr user))
+ 	  (setq probe (car user))))
+       (when window-to-use
+ 	(window--display-buffer-2 buffer window-to-use)))))
+ 
  (defun window--display-buffer-1 (window)
    "Raise the frame containing WINDOW.
  Do not raise the selected frame.  Return WINDOW."
***************
*** 987,993 ****
  
  Optional argument NOT-THIS-WINDOW non-nil means display the
  buffer in a window other than the selected one, even if it is
! already displayed in the selected window.
  
  Optional argument FRAME specifies which frames to investigate
  when the specified buffer is already displayed.  If the buffer is
--- 1124,1135 ----
  
  Optional argument NOT-THIS-WINDOW non-nil means display the
  buffer in a window other than the selected one, even if it is
! already displayed in the selected window.  As a special case, if
! NOT-THIS-WINDOW is any of the values in `pop-up-windows-values',
! this means to use that for finding a window to split.  The
! documentation of the variable `pop-up-windows' contains an
! explanation of what these values mean as well as when such a
! value is applied.
  
  Optional argument FRAME specifies which frames to investigate
  when the specified buffer is already displayed.  If the buffer is
***************
*** 1009,1017 ****
  consider all visible or iconified frames."
    (interactive "BDisplay buffer:\nP")
    (let* ((can-use-selected-window
! 	  ;; The selected window is usable unless either NOT-THIS-WINDOW
! 	  ;; is non-nil, it is dedicated to its buffer, or it is the
! 	  ;; `minibuffer-window'.
  	  (not (or not-this-window
  		   (window-dedicated-p (selected-window))
  		   (window-minibuffer-p))))
--- 1151,1159 ----
  consider all visible or iconified frames."
    (interactive "BDisplay buffer:\nP")
    (let* ((can-use-selected-window
! 	  ;; The selected window is usable unless either
! 	  ;; NOT-THIS-WINDOW is non-nil, it is dedicated to its
! 	  ;; buffer, or it is the `minibuffer-window'.
  	  (not (or not-this-window
  		   (window-dedicated-p (selected-window))
  		   (window-minibuffer-p))))
***************
*** 1039,1046 ****
        (funcall display-buffer-function buffer not-this-window))
       ((and (not not-this-window)
  	   (eq (window-buffer (selected-window)) buffer))
!       ;; The selected window already displays BUFFER and
!       ;; `not-this-window' is nil, so use it.
        (window--display-buffer-1 (selected-window)))
       ((and can-use-selected-window (same-window-p name-of-buffer))
        ;; If the buffer's name tells us to use the selected window do so.
--- 1181,1188 ----
        (funcall display-buffer-function buffer not-this-window))
       ((and (not not-this-window)
  	   (eq (window-buffer (selected-window)) buffer))
!       ;; The selected window already displays BUFFER and NOT-THIS-WINDOW
!       ;; is nil, so use it.
        (window--display-buffer-1 (selected-window)))
       ((and can-use-selected-window (same-window-p name-of-buffer))
        ;; If the buffer's name tells us to use the selected window do so.
***************
*** 1073,1093 ****
        (window--display-buffer-2
         buffer (frame-selected-window (funcall pop-up-frame-function))))
       ((and pop-up-windows
! 	   ;; Make a new window.
! 	   (or (not (frame-parameter frame-to-use 'unsplittable))
! 	       ;; If the selected frame cannot be split look at
! 	       ;; `last-nonminibuffer-frame'.
! 	       (and (eq frame-to-use (selected-frame))
! 		    (setq frame-to-use (last-nonminibuffer-frame))
! 		    (window--frame-usable-p frame-to-use)
! 		    (not (frame-parameter frame-to-use 'unsplittable))))
! 	   ;; Attempt to split largest or least recently used window.
! 	   (setq window-to-use
! 		 (or (window--try-to-split-window
! 		      (get-largest-window frame-to-use t))
! 		     (window--try-to-split-window
! 		      (get-lru-window frame-to-use t))))
! 	   (window--display-buffer-2 buffer window-to-use)))
       ((let ((window-to-undedicate
  	     ;; When NOT-THIS-WINDOW is non-nil, temporarily dedicate
  	     ;; the selected window to its buffer, to avoid that some of
--- 1215,1222 ----
        (window--display-buffer-2
         buffer (frame-selected-window (funcall pop-up-frame-function))))
       ((and pop-up-windows
! 	   (window--pop-up-window
! 	    buffer frame-to-use pop-up-windows not-this-window)))
       ((let ((window-to-undedicate
  	     ;; When NOT-THIS-WINDOW is non-nil, temporarily dedicate
  	     ;; the selected window to its buffer, to avoid that some of
***************
*** 1129,1134 ****
--- 1258,1269 ----
  already visible in the selected window, and ignore
  `same-window-regexps' and `same-window-buffer-names'.
  
+ As a special case, if OTHER-WINDOW is any of the values in
+ `pop-up-windows-values', this means to use that value for finding
+ a suitable window to split.  The documentation of the variable
+ `pop-up-windows' contains an explanation of what these values
+ mean as well as when such a value is applied.
+ 
  If the window to show BUFFER-OR-NAME is not on the selected
  frame, raise that window's frame and give it input focus.