a specific window.
* Quitting Windows:: How to restore the state prior to displaying a
buffer.
+* Side Windows:: Special windows on a frame's sides.
* Window Point:: Each window has its own location of point.
* Window Start and End:: Buffer positions indicating which text is
on-screen in a window.
@deffn Command delete-window &optional window
This function removes @var{window} from display and returns
@code{nil}. If @var{window} is omitted or @code{nil}, it defaults to
-the selected window. If deleting the window would leave no more
-windows in the window tree (e.g., if it is the only live window in the
-frame), an error is signaled.
+the selected window.
+
+If deleting the window would leave no more windows in the window tree
+(e.g., if it is the only live window in the frame) or all remaining
+windows on @var{window}'s frame are side windows (@pxref{Side Windows}),
+an error is signaled.
By default, the space taken up by @var{window} is given to one of its
adjacent sibling windows, if any. However, if the variable
proportionally distributed among any remaining windows in the same
window combination. @xref{Recombining Windows}.
-The behavior of this function may be altered by the window parameters
-of @var{window}, so long as the variable
-@code{ignore-window-parameters} is @code{nil}. If the value of
-the @code{delete-window} window parameter is @code{t}, this function
-ignores all other window parameters. Otherwise, if the value of the
-@code{delete-window} window parameter is a function, that function is
-called with the argument @var{window}, in lieu of the usual action of
-@code{delete-window}. Otherwise, this function obeys the
-@code{window-atom} or @code{window-side} window parameter, if any.
-@xref{Window Parameters}.
+The behavior of this function may be altered by the window parameters of
+@var{window}, so long as the variable @code{ignore-window-parameters} is
+@code{nil}. If the value of the @code{delete-window} window parameter
+is @code{t}, this function ignores all other window parameters.
+Otherwise, if the value of the @code{delete-window} window parameter is
+a function, that function is called with the argument @var{window}, in
+lieu of the usual action of @code{delete-window}. @xref{Window
+Parameters}.
@end deffn
@deffn Command delete-other-windows &optional window
-This function makes @var{window} fill its frame, by deleting other
-windows as necessary. If @var{window} is omitted or @code{nil}, it
-defaults to the selected window. The return value is @code{nil}.
-
-The behavior of this function may be altered by the window parameters
-of @var{window}, so long as the variable
-@code{ignore-window-parameters} is @code{nil}. If the value of
-the @code{delete-other-windows} window parameter is @code{t}, this
-function ignores all other window parameters. Otherwise, if the value
-of the @code{delete-other-windows} window parameter is a function,
-that function is called with the argument @var{window}, in lieu of the
-usual action of @code{delete-other-windows}. Otherwise, this function
-obeys the @code{window-atom} or @code{window-side} window parameter,
-if any. @xref{Window Parameters}.
+This function makes @var{window} fill its frame, deleting other windows
+as necessary. If @var{window} is omitted or @code{nil}, it defaults to
+the selected window. An error is signaled if @var{window} is a side
+window (@pxref{Side Windows}). The return value is @code{nil}.
+
+The behavior of this function may be altered by the window parameters of
+@var{window}, so long as the variable @code{ignore-window-parameters} is
+@code{nil}. If the value of the @code{delete-other-windows} window
+parameter is @code{t}, this function ignores all other window
+parameters. Otherwise, if the value of the @code{delete-other-windows}
+window parameter is a function, that function is called with the
+argument @var{window}, in lieu of the usual action of
+@code{delete-other-windows}. @xref{Window Parameters}.
+
+Also, if @code{ignore-window-parameters} is @code{nil}, this function
+does not delete any window whose @code{no-delete-other-window} parameter
+is non-@code{nil}.
@end deffn
@deffn Command delete-windows-on &optional buffer-or-name frame
from @code{display-buffer} in this case.
@end defun
-To illustrate the use of action functions, consider the following
+If the @var{alist} argument of any of these functions contains a
+@code{window-parameters} entry, @code{display-buffer} assigns the
+elements of the associated value as window parameters of the chosen
+window.
+
+ To illustrate the use of action functions, consider the following
example.
@example
@end defopt
+@node Side Windows
+@section Side Windows
+@cindex side windows
+@cindex main window
+@cindex main window of a frame
+
+Side windows are special windows positioned at any of the four sides of
+a frame's root window (@pxref{Windows and Frames}). In practice, this
+means that the area of the frame's root window is subdivided into a main
+window and a number of side windows surrounding that main window. The
+main window is either a ``normal'' live window or specifies the area
+containing all the normal windows.
+
+ In their most simple form of use, side windows allow to display
+specific buffers always in the same area of a frame. Hence they can be
+regarded as a generalization of the concept provided by
+@code{display-buffer-at-bottom} (@pxref{Display Action Functions}) to
+the remaining sides of a frame. With suitable customizations, however,
+side windows can be also used to provide frame layouts similar to those
+found in so-called integrated development environments (IDEs).
+
+@menu
+* Displaying Buffers in Side Windows:: An action function for displaying
+ buffers in side windows.
+* Side Window Options and Functions:: Further tuning of side windows.
+* Frame Layouts with Side Windows:: Setting up frame layouts with side
+ windows.
+@end menu
+
+
+@node Displaying Buffers in Side Windows
+@subsection Displaying Buffers in Side Windows
+
+The following action function for @code{display-buffer} (@pxref{Display
+Action Functions}) creates or reuses a side window for displaying the
+specified buffer.
+
+@defun display-buffer-in-side-window buffer alist
+This function displays @var{buffer} in a side window of the selected
+frame. @var{alist} is an association list of symbols and values as for
+@code{display-buffer}. The following symbols in @var{alist} are special
+for this function:
+
+@table @code
+@item side
+Denotes the side of the frame where the window shall be located. Valid
+values are @code{left}, @code{top}, @code{right} and @code{bottom}. If
+unspecified, the window is located at the bottom of the frame.
+
+@item slot
+Denotes a slot at the specified side where to locate the window. A
+value of zero means to preferably position the window in the middle of
+the specified side. A negative value means to use a slot preceding
+(that is, above or on the left of) the middle slot. A positive value
+means to use a slot following (that is, below or on the right of) the
+middle slot. Hence, all windows on a specific side are ordered by their
+@code{slot} value. If unspecified, the window is located in the middle
+of the specified side.
+@end table
+
+If you specify the same slot on the same side for two or more different
+buffers, the buffer displayed last is shown in the corresponding window.
+Hence, slots can be used for sharing the same side window between
+buffers.
+
+This function installs the @code{window-side} and @code{window-slot}
+parameters (@pxref{Window Parameters}) and makes them persistent. It
+does not install any other window parameters unless they have been
+explicitly provided via a @code{window-parameters} entry in @var{alist}.
+@end defun
+
+By default, side windows cannot be split via @code{split-window}
+(@pxref{Splitting Windows}). Also, a side window is not reused or split
+by any buffer display action (@pxref{Display Action Functions}) unless
+it is explicitly specified as target of that action. Note also that
+@code{delete-other-windows} cannot make a side window the only window on
+its frame (@pxref{Deleting Windows}).
+
+ Once set up, side windows also change the behavior of the commands
+@code{switch-to-prev-buffer} and @code{switch-to-next-buffer}
+(@pxref{Window History}). In particular, these commands will refrain
+from showing, in a side window, buffers that have not been displayed in
+that window before. And, they will refrain from having a normal,
+non-side window show a buffer that has been already displayed in a side
+window. A notable exception to the latter rule occurs when an
+application, after displaying a buffer, resets that buffer's local
+variables.
+
+
+@node Side Window Options and Functions
+@subsection Side Window Options and Functions
+
+The following options provide additional control over the placement of
+side windows.
+
+@defopt window-sides-vertical
+If non-@code{nil}, this means that the side windows on the left and
+right of a frame occupy the frame's full height. Otherwise, the side
+windows on the top and bottom of the frame occupy the frame's full
+width.
+@end defopt
+
+@defopt window-sides-slots
+This option specifies the maximum number of side windows on each side of
+a frame. The value is a list of four elements specifying the number of
+side window slots on (in this order) the left, top, right and bottom of
+each frame. If an element is a number, this means to display at most
+that many windows on the corresponding side. If an element is
+@code{nil}, this means there's no bound on the number of slots on that
+side.
+
+If any of the specified values is zero, no window can be created on the
+corresponding side. @code{display-buffer-in-side-window} will not
+signal an error in that case but return @code{nil}. If a specified
+value just forbids the creation of an additional side window, the most
+suitable window on that side is reused and may have its
+@code{window-slot} parameter changed accordingly.
+@end defopt
+
+@defopt window-sides-reversed
+This option specifies whether top/bottom side windows should appear in
+reverse order. When this is @code{nil}, side windows on the top and
+bottom of a frame are always drawn from left to right with increasing
+slot values. When this is @code{t}, the drawing order is reversed and
+side windows on the top and bottom of a frame are drawn from right to
+left with increasing slot values.
+
+When this is @code{bidi}, the drawing order is reversed if and only if
+the value of @code{bidi-paragraph-direction} is @code{right-to-left} in
+the buffer displayed in the window most recently selected within the
+main window area of this frame. Sometimes that window may be hard to
+find, so heuristics are used to avoid that the drawing order changes
+inadvertently when another window gets selected.
+
+The layout of side windows on the left or right of a frame is not
+affected by the value of this variable.
+@end defopt
+
+When a frame has side windows, the following function returns the main
+window of that frame.
+
+@defun window-main-window &optional frame
+This function returns the main window of the specified @var{frame}. The
+optional argument @var{frame} must be a live frame and defaults to the
+selected one.
+
+If @var{frame} has no side windows, it returns @var{frame}'s root
+window. Otherwise, it returns either an internal non-side window such
+that all other non-side windows on @var{frame} descend from it, or the
+single live non-side window of @var{frame}. Note that the main window
+of a frame cannot be deleted via @code{delete-window}.
+@end defun
+
+The following command is handy to toggle the appearance of all side
+windows on a specified frame.
+
+@deffn Command window-toggle-side-windows &optional frame
+This command toggles side windows on the specified @var{frame}. The
+optional argument @var{frame} must be a live frame and defaults to the
+selected one.
+
+If @var{frame} has at least one side window, this command saves the
+state of @var{frame}'s root window in the @var{frame}'s
+@code{window-state} frame parameter and deletes all side windows on
+@var{frame} afterwards.
+
+If @var{frame} has no side window but a @code{window-state} parameter,
+it uses that parameter's value to restore the side windows on
+@var{frame} leaving @var{frame}'s main window alone.
+
+An error is signaled if @var{frame} has no side window and no saved
+state is found.
+@end deffn
+
+
+@node Frame Layouts with Side Windows
+@subsection Frame Layouts with Side Windows
+
+Side windows can be used to create more complex frame layouts like those
+provided by integrated development environments (IDEs). In such
+layouts, the area of the main window is where the normal editing
+activities take place. Side windows are not conceived for editing in
+the usual sense. Rather, they are supposed to display information
+complementary to the current editing activity, like lists of files, tags
+or buffers, help information, search or grep results or shell output.
+
+ The layout of such a frame might appear as follows:
+
+@smallexample
+@group
+ ___________________________________
+ | *Buffer List* |
+ |___________________________________|
+ | | | |
+ | * | | * |
+ | d | | T |
+ | i | | a |
+ | r | Main Window Area | g |
+ | e | | s |
+ | d | | * |
+ | * | | |
+ |_____|_______________________|_____|
+ | *help*/*grep*/ | *shell*/ |
+ | *Completions* | *compilation* |
+ |_________________|_________________|
+ | Echo Area |
+ |___________________________________|
+
+
+@end group
+@end smallexample
+
+The following example illustrates how window parameters (@pxref{Window
+Parameters}) can be used with @code{display-buffer-in-side-window}
+(@pxref{Displaying Buffers in Side Windows}) to set up code for
+producing the frame sketched above.
+
+@example
+@group
+(defvar parameters
+ '(window-parameters . ((no-other-window . t) (no-delete-other-window . t))))
+
+(setq fit-window-to-buffer-horizontally t)
+(setq window-resize-pixelwise t)
+
+(setq
+ display-buffer-alist
+ `(("\\*Buffer List\\*" display-buffer-in-side-window
+ (side . top) (slot . 0) (window-height . fit-window-to-buffer)
+ (preserve-size . (nil . t)) ,parameters)
+ ("\\*Tags List\\*" display-buffer-in-side-window
+ (side . right) (slot . 0) (window-width . fit-window-to-buffer)
+ (preserve-size . (t . nil)) ,parameters)
+ ("\\*\\(?:help\\|grep\\|Completions\\)\\*" display-buffer-in-side-window
+ (side . bottom) (slot . -1) (preserve-size . (nil . t)) ,parameters)
+ ("\\*\\(?:shell\\|compilation\\)\\*" display-buffer-in-side-window
+ (side . bottom) (slot . 1) (preserve-size . (nil . t)) ,parameters)))
+@end group
+@end example
+
+This specifies @code{display-buffer-alist} entries (@pxref{Choosing
+Window}) for buffers with fixed names. In particular, it asks for
+showing @file{*Buffer List*} with adjustable height at the top of the
+frame and @file{*Tags List*} with adjustable width on the frame's right.
+It also asks for having the @file{*help*}, @file{*grep*} and
+@file{*Completions*} buffers share a window on the bottom left side of
+the frame and the @file{*shell*} and @file{*compilation*} buffers appear
+in a window on the bottom right side of the frame.
+
+ Note that the option @code{fit-window-to-buffer-horizontally} must
+have a non-@code{nil} value in order to allow horizontal adjustment of
+windows. We also added entries that ask for preserving the height of
+side windows at the top and bottom of the frame and the width of side
+windows at the left or right of the frame. To assure that side windows
+retain their respective sizes when maximizing the associated frame, we
+have also set the variable @code{window-resize-pixelwise}.
+@xref{Resizing Windows}.
+
+ The last form also makes sure that none of the side windows it
+creates are accessible via @kbd{C-x o} by installing a
+@code{no-other-window} parameter for each of these windows. In addition
+it also makes sure that side windows are not deleted via @kbd{C-x 1} by
+installing a @code{no-delete-other-window} parameter on each of these
+windows.
+
+ Since @code{dired} buffers have no fixed names, we use a special
+function @code{dired-default-directory-on-left} in order to display a
+lean directory buffer on the left side of the frame.
+
+@example
+@group
+(defun dired-default-directory-on-left ()
+ "Display `default-directory' in side window on left, hiding details."
+ (interactive)
+ (let ((buffer (dired-noselect default-directory)))
+ (with-current-buffer buffer (dired-hide-details-mode t))
+ (display-buffer-in-side-window
+ buffer `((side . left) (slot . 0)
+ (window-width . fit-window-to-buffer)
+ (preserve-size . (t . nil)) ,parameters))))
+@end group
+@end example
+
+Evaluating the preceding forms and typing, in any order, @kbd{M-x
+list-buffers}, @kbd{C-h f}, @kbd{M-x shell}, @kbd{M-x list-tags} and
+@kbd{M-x dired-default-directory-on-left} should now reproduce the frame
+layout sketched above.
+
+
@node Window Point
@section Windows and Point
@cindex window position
and/or two columns.
@end defun
+The functions @code{window-state-get} and @code{window-state-put} also
+allow to exchange the contents of two live windows. The following
+function does precisely that:
+
+@deffn Command window-swap-states &optional window-1 window-2 size
+This command swaps the states of the two live windows @var{window-1} and
+@var{window-2}. @var{window-1} must specify a live window and defaults
+to the selected one. @var{window-2} must specify a live window and
+defaults to the window following @var{window-1} in the cyclic ordering
+of windows, excluding minibuffer windows and including live windows on
+all visible frames.
+
+Optional argument @var{size} non-@code{nil} means to try swapping the
+sizes of @var{window-1} and @var{window-2} as well. A value of
+@code{height} means to swap heights only, a value of @code{width}
+means to swap widths only, while @code{t} means to swap both widths
+and heights, if possible. Frames are not resized by this function.
+@end deffn
+
@node Window Parameters
@section Window Parameters
parameters reset to @code{nil}. The following variable allows you to
override the standard behavior:
+@cindex persistent window parameters
@defvar window-persistent-parameters
This variable is an alist specifying which parameters get saved by
@code{current-window-configuration} and @code{window-state-get}, and
@end defvar
Some functions (notably @code{delete-window},
-@code{delete-other-windows} and @code{split-window}), may behave specially
-when their @var{window} argument has a parameter set. You can override
-such special behavior by binding the following variable to a
-non-@code{nil} value:
+@code{delete-other-windows} and @code{split-window}), may behave
+specially when the window specified by their @var{window} argument has
+the homonymous parameter set. You can override such special behavior by
+binding the following variable to a non-@code{nil} value:
@defvar ignore-window-parameters
If this variable is non-@code{nil}, some standard functions do not
This parameter affects the execution of @code{delete-other-windows}
(@pxref{Deleting Windows}).
+@item @code{no-delete-other-window}
+This parameter marks the window as not deletable by
+@code{delete-other-windows} (@pxref{Deleting Windows}).
+
@item @code{split-window}
This parameter affects the execution of @code{split-window}
(@pxref{Splitting Windows}).
this parameter. @code{quit-restore-window} deletes the specified window
only if it still shows that buffer.
+@item @code{window-side} @code{window-slot}
+These parameters are used for implementing side windows (@pxref{Side
+Windows}).
+
@item @code{min-margins}
The value of this parameter is a cons cell whose @sc{car} and @sc{cdr},
if non-@code{nil}, specify the minimum values (in columns) for the left
versions of Emacs.
@end table
-There are additional parameters @code{window-atom} and @code{window-side};
-these are reserved and should not be used by applications.
+The @code{window-atom} parameter is used for implemeting atomic windows.
@node Window Hooks
(setq window (window-normalize-window window))
(setq root (window-atom-root window))
;; Split off new window.
- (when (setq new (split-window window nil side))
+ (when (setq new (split-window-no-error window nil side))
(window-make-atom
(if (and root (not (eq root window)))
;; When WINDOW was part of an atomic window and we did not
(window--atom-check-1 (frame-root-window frame)))
;; Side windows.
-(defvar window-sides '(left top right bottom)
- "Window sides.")
-
(defcustom window-sides-vertical nil
- "If non-nil, left and right side windows are full height.
-Otherwise, top and bottom side windows are full width."
+ "If non-nil, left and right side windows occupy full frame height.
+If nil, top and bottom side windows occupy full frame width."
:type 'boolean
+ :initialize 'custom-initialize-default
+ :set 'window--sides-verticalize
:group 'windows
- :version "24.1")
+ :version "26.1")
+
+(defcustom window-sides-reversed nil
+ "Whether top/bottom side windows appear in reverse order.
+When this is nil, side windows on the top and bottom of a frame
+are always drawn from left to right with increasing slot values.
+When this is t, side windows on the top and bottom of a frame are
+always drawn from right to left with increasing slot values.
+
+When this is `bidi', the drawing order is like that for the value
+t if the value of `bidi-paragraph-direction' is `right-to-left'
+in the buffer most recently shown in the window selected within
+the main window area of this frame.
+
+The layout of side windows on the left or right of a frame is not
+affected by the value of this variable."
+ :type
+ '(choice (const :tag "Never" nil)
+ (const :tag "Bidi" bidi)
+ (const :tag "Always" t))
+ :initialize 'custom-initialize-default
+ :set 'window--sides-reverse
+ :group 'windows
+ :version "26.1")
(defcustom window-sides-slots '(nil nil nil nil)
- "Maximum number of side window slots.
-The value is a list of four elements specifying the number of
-side window slots on (in this order) the left, top, right and
-bottom side of each frame. If an element is a number, this means
-to display at most that many side windows on the corresponding
-side. If an element is nil, this means there's no bound on the
-number of slots on that side."
+ "Number of available side window slots on each side of a frame.
+The value is a list of four elements specifying the maximum
+number of side windows that may be created on the left, top,
+right and bottom side of any frame.
+
+If an element is a number, `display-buffer-in-side-window' will
+refrain from making a new side window if the number of windows on
+that side is equal to or exceeds that number. Rather, it will
+reuse the window whose `window-slot' value is nearest to the slot
+specified via its ALIST argument. If an element is nil, this
+means there's no bound on the number of windows on that side."
:version "24.1"
:risky t
:type
:value (nil nil nil nil)
(choice
:tag "Left"
- :help-echo "Maximum slots of left side window."
+ :help-echo "Maximum number of left side windows."
:value nil
:format "%[Left%] %v\n"
(const :tag "Unlimited" :format "%t" nil)
(integer :tag "Number" :value 2 :size 5))
(choice
:tag "Top"
- :help-echo "Maximum slots of top side window."
+ :help-echo "Maximum number of top side windows."
:value nil
:format "%[Top%] %v\n"
(const :tag "Unlimited" :format "%t" nil)
(integer :tag "Number" :value 3 :size 5))
(choice
:tag "Right"
- :help-echo "Maximum slots of right side window."
+ :help-echo "Maximum number of right side windows."
:value nil
:format "%[Right%] %v\n"
(const :tag "Unlimited" :format "%t" nil)
(integer :tag "Number" :value 2 :size 5))
(choice
:tag "Bottom"
- :help-echo "Maximum slots of bottom side window."
+ :help-echo "Maximum number of bottom side windows."
:value nil
:format "%[Bottom%] %v\n"
(const :tag "Unlimited" :format "%t" nil)
(integer :tag "Number" :value 3 :size 5)))
:group 'windows)
-(defun window--side-window-p (window)
- "Return non-nil if WINDOW is a side window or the parent of one."
- (or (window-parameter window 'window-side)
- (and (window-child window)
- (or (window-parameter
- (window-child window) 'window-side)
- (window-parameter
- (window-last-child window) 'window-side)))))
-
-(defun window--major-non-side-window (&optional frame)
- "Return the major non-side window of frame FRAME.
+(defvar-local window--sides-shown nil
+ "Non-nil if this buffer was shown in a side window once.
+If this variable is non-nil in a buffer, `switch-to-prev-buffer'
+and `switch-to-next-buffer' will refrain from showing this buffer
+within the main window area. `display-buffer-in-side-window'
+sets this variable automatically.
+
+Killing buffer local variables after showing the buffer in a side
+window annihilates any effect provided by this variable.")
+
+(defvar window--sides-inhibit-check nil
+ "Non-nil means inhibit any checks on side windows.")
+
+(defun window--sides-reverse-on-frame-p (frame)
+ "Return non-nil when side windows should appear reversed on FRAME.
+This uses some heuristics to guess the user's intentions when the
+selected window of FRAME is a side window ."
+ (cond
+ ;; Reverse when `window-sides-reversed' is t. Do not reverse when
+ ;; `window-sides-reversed' is nil.
+ ((memq window-sides-reversed '(nil t))
+ window-sides-reversed)
+ ;; Reverse when FRAME's selected window shows a right-to-left buffer.
+ ((let ((window (frame-selected-window frame)))
+ (when (and (not (window-parameter window 'window-side))
+ (or (not (window-minibuffer-p window))
+ (setq window (minibuffer-selected-window))))
+ (with-current-buffer (window-buffer window)
+ (eq bidi-paragraph-direction 'right-to-left)))))
+ ;; Reverse when FRAME's `window-sides-main-selected-window' parameter
+ ;; specifies a live window showing a right-to-left buffer.
+ ((let ((window (frame-parameter
+ frame 'window-sides-main-selected-window)))
+ (when (window-live-p window)
+ (with-current-buffer (window-buffer window)
+ (eq bidi-paragraph-direction 'right-to-left)))))
+ ;; Reverse when all windows in FRAME's main window show right-to-left
+ ;; buffers.
+ (t
+ (catch 'found
+ (walk-window-subtree
+ (lambda (window)
+ (with-current-buffer (window-buffer window)
+ (when (eq bidi-paragraph-direction 'left-to-right)
+ (throw 'found nil))))
+ (window-main-window frame))
+ t))))
+
+(defun window-main-window (&optional frame)
+ "Return the main window of specified FRAME.
The optional argument FRAME must be a live frame and defaults to
the selected one.
-If FRAME has at least one side window, the major non-side window
-is either an internal non-side window such that all other
-non-side windows on FRAME descend from it, or the single live
-non-side window of FRAME. If FRAME has no side windows, return
-its root window."
+If FRAME has no side windows, return FRAME's root window.
+Otherwise, return either an internal non-side window such that
+all other non-side windows on FRAME descend from it, or the
+single live non-side window of FRAME."
(let ((frame (window-normalize-frame frame))
- major sibling)
- ;; Set major to the _last_ window found by `walk-window-tree' that
+ main sibling)
+ ;; Set main to the _last_ window found by `walk-window-tree' that
;; is not a side window but has a side window as its sibling.
(walk-window-tree
(lambda (window)
(window-parameter sibling 'window-side))
(and (setq sibling (window-next-sibling window))
(window-parameter sibling 'window-side)))
- (setq major window)))
+ (setq main window)))
frame t 'nomini)
- (or major (frame-root-window frame))))
+ (or main (frame-root-window frame))))
-(defun window--major-side-window (side)
- "Return major side window on SIDE.
+(defun window--make-major-side-window-next-to (side)
+ "Return window to split for making a major side window.
SIDE must be one of the symbols `left', `top', `right' or
-`bottom'. Return nil if no such window exists."
+`bottom'.
+
+This is an auxiliary function of `window--make-major-side-window'
+and must not be called when a window on SIDE exists already."
(let ((root (frame-root-window))
- window)
+ (window--sides-inhibit-check t)
+ window)
;; (1) If a window on the opposite side exists, return that window's
;; sibling.
;; (2) If the new window shall span the entire side, return the
(window-prev-sibling window))
(t root))))))
-(defun display-buffer-in-major-side-window (buffer side slot &optional alist)
- "Display BUFFER in a new window on SIDE of the selected frame.
+(defun window--make-major-side-window (buffer side slot &optional alist)
+ "Display BUFFER in a new major side window on the selected frame.
SIDE must be one of `left', `top', `right' or `bottom'. SLOT
specifies the slot to use. ALIST is an association list of
symbols and values as passed to `display-buffer-in-side-window'.
-This function may be called only if no window on SIDE exists yet.
-The new window automatically becomes the \"major\" side window on
-SIDE. Return the new window, nil if its creation window failed."
+Return the new window, nil if its creation failed.
+
+This is an auxiliary function of `display-buffer-in-side-window'
+and may be called only if no window on SIDE exists yet."
(let* ((left-or-right (memq side '(left right)))
- (major (window--major-side-window side))
+ (next-to (window--make-major-side-window-next-to side))
(on-side (cond
((eq side 'top) 'above)
((eq side 'bottom) 'below)
(t side)))
+ (window--sides-inhibit-check t)
;; The following two bindings will tell `split-window' to take
- ;; the space for the new window from `major' and not make a new
- ;; parent window unless needed.
+ ;; the space for the new window from the selected frame's main
+ ;; window and not make a new parent window unless needed.
(window-combination-resize 'side)
(window-combination-limit nil)
- (new (split-window major nil on-side)))
- (when new
- ;; Initialize `window-side' parameter of new window to SIDE.
- (set-window-parameter new 'window-side side)
- ;; Install `window-slot' parameter of new window.
- (set-window-parameter new 'window-slot slot)
- ;; Install `delete-window' parameter thus making sure that when
- ;; the new window is deleted, a side window on the opposite side
- ;; does not get resized.
- (set-window-parameter new 'delete-window 'delete-side-window)
+ (window (split-window-no-error next-to nil on-side)))
+ (when window
+ ;; Initialize `window-side' parameter of new window to SIDE and
+ ;; make that parameter persistent.
+ (set-window-parameter window 'window-side side)
+ (add-to-list 'window-persistent-parameters '(window-side . writable))
+ ;; Install `window-slot' parameter of new window and make that
+ ;; parameter persistent.
+ (set-window-parameter window 'window-slot slot)
+ (add-to-list 'window-persistent-parameters '(window-slot . writable))
;; Auto-adjust height/width of new window unless a size has been
;; explicitly requested.
(unless (if left-or-right
;; root window.
4))
alist)))
- ;; Install BUFFER in new window and return NEW.
- (window--display-buffer buffer new 'window alist 'side))))
-
-(defun delete-side-window (window)
- "Delete side window WINDOW."
- (let ((window-combination-resize
- (window-parameter (window-parent window) 'window-side))
- (ignore-window-parameters t))
- (delete-window window)))
+ (with-current-buffer buffer
+ (setq window--sides-shown t))
+ ;; Install BUFFER in new window and return WINDOW.
+ (window--display-buffer buffer window 'window alist 'side))))
(defun display-buffer-in-side-window (buffer alist)
"Display BUFFER in a side window of the selected frame.
the specified side. A negative value means use a slot
preceding (that is, above or on the left of) the middle slot.
A positive value means use a slot following (that is, below or
- on the right of) the middle slot. The default is zero."
- (let ((side (or (cdr (assq 'side alist)) 'bottom))
- (slot (or (cdr (assq 'slot alist)) 0)))
+ on the right of) the middle slot. The default is zero.
+
+If the current frame size or the settings of `window-sides-slots'
+do not permit making a new window, a suitable existing window may
+be reused and have its `window-slot' parameter value accordingly
+modified.
+
+Unless `display-buffer-mark-dedicated' is non-nil, softly
+dedicate the side window used to BUFFER. Return nil if no
+suitable window is found.
+
+This function installs the `window-side' and `window-slot'
+parameters and makes them persistent. It neither modifies ALIST
+nor installs any other window parameters unless they have been
+explicitly provided via a `window-parameter' entry in ALIST."
+ (let* ((side (or (cdr (assq 'side alist)) 'bottom))
+ (slot (or (cdr (assq 'slot alist)) 0))
+ (left-or-right (memq side '(left right)))
+ ;; Softly dedicate window to BUFFER unless
+ ;; `display-buffer-mark-dedicated' already asks for it.
+ (dedicated (or display-buffer-mark-dedicated 'side)))
(cond
((not (memq side '(top bottom left right)))
(error "Invalid side %s specified" side))
(let* ((major (window-with-parameter 'window-side side nil t))
;; `major' is the major window on SIDE, `windows' the list of
;; life windows on SIDE.
- (windows
- (when major
- (let (windows)
- (walk-window-tree
- (lambda (window)
- (when (eq (window-parameter window 'window-side) side)
- (setq windows (cons window windows))))
- nil nil 'nomini)
- (nreverse windows))))
+ (reversed (window--sides-reverse-on-frame-p (selected-frame)))
+ (windows
+ (cond
+ ((window-live-p major)
+ (list major))
+ ((window-valid-p major)
+ (let* ((first (window-child major))
+ (next (window-next-sibling first))
+ (windows (list next first)))
+ (setq reversed (> (window-parameter first 'window-slot)
+ (window-parameter next 'window-slot)))
+ (while (setq next (window-next-sibling next))
+ (setq windows (cons next windows)))
+ (if reversed windows (nreverse windows))))))
(slots (when major (max 1 (window-child-count major))))
(max-slots
(nth (cond
((eq side 'right) 2)
((eq side 'bottom) 3))
window-sides-slots))
+ (window--sides-inhibit-check t)
window this-window this-slot prev-window next-window
best-window best-slot abs-slot)
(cond
((and (numberp max-slots) (<= max-slots 0))
- ;; No side-slots available on this side. Don't create an error,
+ ;; No side-slots available on this side. Don't raise an error,
;; just return nil.
nil)
((not windows)
- ;; No major window exists on this side, make one.
- (display-buffer-in-major-side-window buffer side slot alist))
+ ;; No major side window exists on this side, make one.
+ (window--make-major-side-window buffer side slot alist))
(t
;; Scan windows on SIDE.
(catch 'found
(setq this-slot (window-parameter window 'window-slot))
(cond
;; The following should not happen and probably be checked
- ;; by window--side-check.
+ ;; by window--sides-check.
((not (numberp this-slot)))
((= this-slot slot)
;; A window with a matching slot has been found.
(unless (and best-slot (<= best-slot abs-slot))
(setq best-window window)
(setq best-slot abs-slot))
- (cond
- ((<= this-slot slot)
- (setq prev-window window))
- ((not next-window)
- (setq next-window window)))))))
-
- ;; `this-window' is the first window with the same SLOT.
+ (if reversed
+ (cond
+ ((<= this-slot slot)
+ (setq next-window window))
+ ((not prev-window)
+ (setq prev-window window)))
+ (cond
+ ((<= this-slot slot)
+ (setq prev-window window))
+ ((not next-window)
+ (setq next-window window))))))))
+
+ ;; `this-window' is the first window with the same SLOT.
;; `prev-window' is the window with the largest slot < SLOT. A new
;; window will be created after it.
;; `next-window' is the window with the smallest slot > SLOT. A new
;; window will be created before it.
;; `best-window' is the window with the smallest absolute difference
;; of its slot and SLOT.
-
- ;; Note: We dedicate the window used softly to its buffer to
- ;; avoid that "other" (non-side) buffer display functions steal
- ;; it from us. This must eventually become customizable via
- ;; ALIST (or, better, avoided in the "other" functions).
(or (and this-window
;; Reuse `this-window'.
- (window--display-buffer buffer this-window 'reuse alist 'side))
+ (with-current-buffer buffer
+ (setq window--sides-shown t))
+ (window--display-buffer
+ buffer this-window 'reuse alist dedicated))
(and (or (not max-slots) (< slots max-slots))
(or (and next-window
;; Make new window before `next-window'.
- (let ((next-side
- (if (memq side '(left right)) 'above 'left))
+ (let ((next-side (if left-or-right 'above 'left))
(window-combination-resize 'side))
- (setq window (split-window next-window nil next-side))
- ;; When the new window is deleted, its space
- ;; is returned to other side windows.
- (set-window-parameter
- window 'delete-window 'delete-side-window)
- window))
+ (setq window (split-window-no-error
+ next-window nil next-side))))
(and prev-window
;; Make new window after `prev-window'.
- (let ((prev-side
- (if (memq side '(left right)) 'below 'right))
+ (let ((prev-side (if left-or-right 'below 'right))
(window-combination-resize 'side))
- (setq window (split-window prev-window nil prev-side))
- ;; When the new window is deleted, its space
- ;; is returned to other side windows.
- (set-window-parameter
- window 'delete-window 'delete-side-window)
- window)))
+ (setq window (split-window-no-error
+ prev-window nil prev-side)))))
(set-window-parameter window 'window-slot slot)
- (window--display-buffer buffer window 'window alist 'side))
+ (with-current-buffer buffer
+ (setq window--sides-shown t))
+ (window--display-buffer
+ buffer window 'window alist dedicated))
(and best-window
;; Reuse `best-window'.
(progn
;; Give best-window the new slot value.
(set-window-parameter best-window 'window-slot slot)
- (window--display-buffer
- buffer best-window 'reuse alist 'side)))))))))
-
-(defun window--side-check (&optional frame)
- "Check the side window configuration of FRAME.
-FRAME defaults to the selected frame.
-
-A valid side window configuration preserves the following two
-invariants:
-
-- If there exists a window whose window-side parameter is
- non-nil, there must exist at least one live window whose
- window-side parameter is nil.
-
-- If a window W has a non-nil window-side parameter (i) it must
- have a parent window and that parent's window-side parameter
- must be either nil or the same as for W, and (ii) any child
- window of W must have the same window-side parameter as W.
-
-If the configuration is invalid, reset the window-side parameters
-of all windows on FRAME to nil."
- (let (left top right bottom none side parent parent-side)
- (when (or (catch 'reset
- (walk-window-tree
- (lambda (window)
- (setq side (window-parameter window 'window-side))
- (setq parent (window-parent window))
- (setq parent-side
- (and parent (window-parameter parent 'window-side)))
- ;; The following `cond' seems a bit tedious, but I'd
- ;; rather stick to using just the stack.
- (cond
- (parent-side
- (when (not (eq parent-side side))
- ;; A parent whose window-side is non-nil must
- ;; have a child with the same window-side.
- (throw 'reset t)))
- ((not side)
- (when (window-buffer window)
- ;; Record that we have at least one non-side,
- ;; live window.
- (setq none t)))
- ((if (memq side '(left top))
- (window-prev-sibling window)
- (window-next-sibling window))
- ;; Left and top major side windows must not have a
- ;; previous sibling, right and bottom major side
- ;; windows must not have a next sibling.
- (throw 'reset t))
- ;; Now check that there's no more than one major
- ;; window for any of left, top, right and bottom.
- ((eq side 'left)
- (if left (throw 'reset t) (setq left t)))
- ((eq side 'top)
- (if top (throw 'reset t) (setq top t)))
- ((eq side 'right)
- (if right (throw 'reset t) (setq right t)))
- ((eq side 'bottom)
- (if bottom (throw 'reset t) (setq bottom t)))
- (t
- (throw 'reset t))))
- frame t 'nomini))
- ;; If there's a side window, there must be at least one
- ;; non-side window.
- (and (or left top right bottom) (not none)))
- (walk-window-tree
- (lambda (window)
- (set-window-parameter window 'window-side nil))
- frame t 'nomini))))
+ (with-current-buffer buffer
+ (setq window--sides-shown t))
+ (window--display-buffer
+ buffer best-window 'reuse alist dedicated)))))))))
+
+(defun window-toggle-side-windows (&optional frame)
+ "Toggle side windows on specified FRAME.
+FRAME must be a live frame and defaults to the selected one.
+
+If FRAME has at least one side window, save FRAME's state in the
+FRAME's `window-state' frame parameter and delete all side
+windows on FRAME afterwards. Otherwise, if FRAME has a
+`window-state' parameter, use that to restore any side windows on
+FRAME leaving FRAME's main window alone. Signal an error if
+FRAME has no side window and no saved state is found."
+ (interactive)
+ (let* ((frame (window-normalize-frame frame))
+ (window--sides-inhibit-check t)
+ state)
+ (cond
+ ((window-with-parameter 'window-side nil frame)
+ ;; At least one side window exists. Remove all side windows after
+ ;; saving FRAME's state in its `window-state' parameter.
+ (set-frame-parameter
+ frame 'window-state (window-state-get (frame-root-window frame)))
+ (let ((ignore-window-parameters t))
+ (delete-other-windows (window-main-window frame))))
+ ((setq state (frame-parameter frame 'window-state))
+ ;; A window state was saved for FRAME. Restore it and put the
+ ;; current root window into its main window.
+ (let ((main-state (window-state-get (frame-root-window frame))))
+ (window-state-put state (frame-root-window frame) t)
+ (window-state-put main-state (window-main-window frame)))
+ (window--sides-reverse-frame frame))
+ (t
+ (error "No side windows state found")))))
+
+(defun window--sides-reverse-all ()
+ "Maybe reverse side windows on all frames."
+ (unless window--sides-inhibit-check
+ (dolist (frame (frame-list))
+ (window--sides-reverse-frame frame))))
+
+(defun window--sides-reverse-frame (frame)
+ "Maybe reverse side windows on FRAME."
+ (when (eq window-sides-reversed 'bidi)
+ (let ((window (frame-selected-window frame)))
+ (unless (or (window-parameter window 'window-side)
+ (window-minibuffer-p window))
+ (set-frame-parameter
+ frame 'window-sides-main-selected-window window))))
+ (window--sides-reverse-side frame 'top)
+ (window--sides-reverse-side frame 'bottom))
+
+(defun window--sides-reverse-side (frame side)
+ "Maybe reverse windows on SIDE of FRAME."
+ (let ((major (window-with-parameter 'window-side side frame t))
+ (window--sides-inhibit-check t))
+ (when (and major (not (window-live-p major)))
+ (let* ((first (window-child major))
+ (reversed (> (window-parameter first 'window-slot)
+ (window-parameter
+ (window-next-sibling first) 'window-slot)))
+ (reverse (window--sides-reverse-on-frame-p frame)))
+ (unless (eq reversed reverse)
+ ;; We have to reverse.
+ (let ((last (window-last-child major)))
+ (while (and (not (eq first last))
+ (not (eq first (window-next-sibling last))))
+ (window-swap-states first last t)
+ (setq first (window-next-sibling first))
+ (setq last (window-prev-sibling last)))))))))
+
+(defun window--sides-reverse (symbol value)
+ "Helper function for customizing `window-sides-reversed'."
+ (set-default symbol value)
+ (remove-hook 'buffer-list-update-hook 'window--sides-reverse-all)
+ (remove-hook 'window-configuration-change-hook 'window--sides-reverse-all)
+ (dolist (frame (frame-list))
+ (set-frame-parameter frame 'window-sides-main-selected-window nil))
+ (when (eq value 'bidi)
+ (add-hook 'buffer-list-update-hook 'window--sides-reverse-all)
+ (add-hook 'window-configuration-change-hook 'window--sides-reverse-all))
+ (window--sides-reverse-all))
+
+(defun window--sides-verticalize-frame (&optional frame)
+ "Maybe change side windows layout on specified FRAME."
+ (setq frame (window-normalize-frame frame))
+ (let ((window--sides-inhibit-check t)
+ (root (frame-root-window frame))
+ (main (window-main-window frame)))
+ (when (and (not (eq main root))
+ (not (eq (window-parent main) root))
+ (window-combined-p main window-sides-vertical))
+ (let* ((window--sides-inhibit-check t)
+ (ignore-window-parameters t)
+ (first (window-child root))
+ (first-state
+ (and first (window-parameter first 'window-side)
+ (window-state-get first)))
+ (last (window-last-child root))
+ (last-state
+ (and last (window-parameter last 'window-side)
+ (window-state-get last)))
+ (dummy (get-buffer-create " *dummy*"))
+ major)
+ (unwind-protect
+ (progn
+ (when first-state (delete-window first))
+ (when last-state (delete-window last))
+ (when first-state
+ (setq major (window--make-major-side-window
+ dummy (if window-sides-vertical 'top 'left) 0))
+ (window-state-put first-state major t))
+ (when last-state
+ (setq major (window--make-major-side-window
+ dummy (if window-sides-vertical 'bottom 'right) 0))
+ (window-state-put last-state major t)))
+ (kill-buffer " *dummy*"))))))
+
+(defun window--sides-verticalize (symbol value)
+ "Helper function for customizing `window-sides-vertical'."
+ (set-default symbol value)
+ (dolist (frame (frame-list))
+ (window--sides-verticalize-frame frame)))
+
+(defun window--sides-check-failed (frame)
+ "Helper function for `window--sides-check'."
+ (catch 'failed
+ ;; FRAME must have a main window.
+ (unless (window-main-window frame)
+ (error "Frame %s has no main window" frame)
+ (throw 'failed t))
+ ;; Now check the side windows.
+ (dolist (side '(left top right bottom))
+ (let ((window (window-with-parameter 'window-side side frame t)))
+ (when window
+ ;; If WINDOW is live there must be no other window on this frame
+ ;; with the same `window-side' parameter.
+ (if (window-live-p window)
+ (walk-window-tree
+ (lambda (this)
+ (when (and (eq (window-parameter this 'window-side) side)
+ (not (eq this window)))
+ (error "Window %s has same side %s as window %s but no common parent"
+ this side window)
+ (throw 'failed t)))
+ frame t 'nomini)
+ (walk-window-tree
+ (lambda (this)
+ (if (eq (window-parent this) window)
+ (unless (eq (window-parameter this 'window-side) side)
+ (error "Window %s has not same side %s as its parent %s"
+ this side window)
+ (throw 'failed t))
+ (when (and (eq (window-parameter this 'window-side) side)
+ (not (eq this window)))
+ (error "Window %s has same side %s as major side window %s but its parent is %s"
+ this side window (window-parent this))
+ (throw 'failed t))))
+ frame t 'nomini)))))))
+
+(defun window--sides-check (frame)
+ "Check side windows configuration of FRAME.
+In a valid side windows configuration there can be at most one
+internal side window on each side and all its children must be
+live and have the same `window-side' parameter and no other
+window with the same `window-side' parameter exists on FRAME. If
+there is no such internal window, there may be at most one window
+with this side's `window-side' parameter on FRAME.
+
+If the configuration is invalid, reset the `window-side'
+parameters of all windows on FRAME."
+ (when (and (not window--sides-inhibit-check)
+ (window-with-parameter 'window-side nil frame t)
+ (window--sides-check-failed frame))
+ ;; Reset all `window-side' parameters.
+ (walk-window-tree
+ (lambda (window)
+ (set-window-parameter window 'window-side nil))
+ frame t 'nomini)
+ (message "Side windows configuration reset for frame %s" frame)))
(defun window--check (&optional frame)
"Check atomic and side windows on FRAME.
FRAME defaults to the selected frame."
- (window--side-check frame)
+ (window--sides-check frame)
(window--atom-check frame))
;; Dumping frame/window contents.
"Resize WINDOW vertically if it is resizable by DELTA lines.
This function is like `window-resize' but does not signal an
error when WINDOW cannot be resized. For the meaning of the
-optional arguments see the documentation of `window-resize'.
-
-Optional argument PIXELWISE non-nil means interpret DELTA as
-pixels."
+optional arguments see the documentation of `window-resize'."
(when (window--resizable-p
window delta horizontal ignore nil nil nil pixelwise)
(window-resize window delta horizontal ignore pixelwise)))
(setq left first-left)
(while (and left
(or (window-size-fixed-p left horizontal 'preserved)
- (<= (window-size left horizontal t)
- (window-min-size left horizontal 'preserved t))))
+ (and (< delta 0)
+ (<= (window-size left horizontal t)
+ (window-min-size
+ left horizontal 'preserved t)))))
(setq left
(or (window-left left)
(progn
(or (window-size-fixed-p right horizontal)
(and (> delta 0)
(<= (window-size right horizontal t)
- (window-min-size right horizontal 'preserved t)))))
+ (window-min-size
+ right horizontal 'preserved t)))))
(setq right
(or (window-right right)
(progn
(setq right first-right)
(while (and right
(or (window-size-fixed-p right horizontal 'preserved)
- (<= (window-size right horizontal t)
- (window-min-size right horizontal 'preserved t))))
+ (and (> delta 0)
+ (<= (window-size right horizontal t)
+ (window-min-size
+ right horizontal 'preserved t)))))
(setq right
(or (window-right right)
(progn
;; Start resizing.
(window--resize-reset frame horizontal)
;; Try to enlarge LEFT first.
- (setq this-delta (window--resizable
- left delta horizontal ignore 'after nil nil pixelwise))
+ (setq this-delta
+ (window--resizable
+ left delta horizontal ignore 'after nil nil pixelwise))
(unless (zerop this-delta)
(window--resize-this-window
left this-delta horizontal ignore t 'before
(defun window-deletable-p (&optional window)
"Return t if WINDOW can be safely deleted from its frame.
WINDOW must be a valid window and defaults to the selected one.
-Return `frame' if deleting WINDOW should also delete its frame."
+
+Return `frame' if WINDOW is the root window of its frame and that
+frame can be safely deleted."
(setq window (window-normalize-window window))
(unless (or ignore-window-parameters
(let ((minibuf (active-minibuffer-window)))
(and minibuf (eq frame (window-frame minibuf)))))
'frame))
+ ((window-minibuffer-p window)
+ ;; If WINDOW is the minibuffer window of a non-minibuffer-only
+ ;; frame, it cannot be deleted separately.
+ nil)
((or ignore-window-parameters
- (not (eq window (window--major-non-side-window frame))))
- ;; WINDOW can be deleted unless it is the major non-side window of
- ;; its frame.
+ (not (eq window (window-main-window frame))))
+ ;; Otherwise, WINDOW can be deleted unless it is the main window
+ ;; of its frame.
t))))
(defun window--in-subtree-p (window root)
(throw 'done (delete-window atom-root))))
((not parent)
(error "Attempt to delete minibuffer or sole ordinary window"))
- ((eq window (window--major-non-side-window frame))
- (error "Attempt to delete last non-side window")))
+ ((eq window (window-main-window frame))
+ (error "Attempt to delete main window of frame %s" frame)))
(let* ((horizontal (window-left-child parent))
(size (window-size window horizontal t))
+ (window-combination-resize
+ (or window-combination-resize
+ (window-parameter parent 'window-side)))
(frame-selected
(window--in-subtree-p (frame-selected-window frame) window))
;; Emacs 23 preferably gives WINDOW's space to its left
(setq window (window-normalize-window window))
(let* ((frame (window-frame window))
(function (window-parameter window 'delete-other-windows))
- (window-side (window-parameter window 'window-side))
- atom-root side-main)
+ atom-root main)
(window--check frame)
(catch 'done
(cond
(if (eq atom-root (frame-root-window frame))
(error "Root of atomic window is root window of its frame")
(throw 'done (delete-other-windows atom-root))))
- ((memq window-side window-sides)
+ ((window-parameter window 'window-side)
(error "Cannot make side window the only window"))
((and (window-minibuffer-p window)
(not (eq window (frame-root-window window))))
(error "Can't expand minibuffer to full frame")))
- ;; If WINDOW is the major non-side window, do nothing.
- (if (window-with-parameter 'window-side)
- (setq side-main (window--major-non-side-window frame))
- (setq side-main (frame-root-window frame)))
- (unless (eq window side-main)
- (delete-other-windows-internal window side-main)
+ (cond
+ ((or ignore-window-parameters
+ (not (window-with-parameter 'no-delete-other-window nil frame)))
+ (setq main (frame-root-window frame)))
+ ((catch 'tag
+ (walk-window-tree
+ (lambda (other)
+ (when (or (and (window-parameter other 'window-side)
+ (not (window-parameter
+ other 'no-delete-other-window)))
+ (and (not (window-parameter other 'window-side))
+ (window-parameter
+ other 'no-delete-other-window)))
+ (throw 'tag nil))))
+ t)
+ (setq main (window-main-window frame)))
+ (t
+ ;; Delete other windows via `delete-window' because either a
+ ;; side window is or a non-side-window is not deletable.
+ (dolist (other (window-list frame))
+ (when (and (window-live-p other)
+ (not (eq other window))
+ (not (window-parameter
+ other 'no-delete-other-window))
+ ;; When WINDOW and the other window are part of the
+ ;; same atomic window, don't delete the other.
+ (or (not atom-root)
+ (not (eq (window-atom-root other) atom-root))))
+ (condition-case nil
+ (delete-window other)
+ (error nil))))
+ (throw 'done nil)))
+
+ ;; If WINDOW is the main window of its frame do nothing.
+ (unless (eq window main)
+ (delete-other-windows-internal window main)
(run-window-configuration-change-hook frame)
(window--check frame))
;; Always return nil.
(interactive)
(let* ((window (window-normalize-window window t))
(frame (window-frame window))
+ (window-side (window-parameter window 'window-side))
(old-buffer (window-buffer window))
;; Save this since it's destroyed by `set-window-buffer'.
(next-buffers (window-next-buffers window))
(unless (setq window (minibuffer-selected-window))
(error "Window %s is a minibuffer window" window)))
- (when (window-dedicated-p window)
+ (unless (memq (window-dedicated-p window) '(nil side))
;; Don't switch in dedicated window.
(error "Window %s is dedicated to buffer %s" window old-buffer))
;; buffer we don't reverse the global buffer list to avoid showing
;; a buried buffer instead. Otherwise, we must reverse the global
;; buffer list in order to make sure that switching to the
- ;; previous/next buffer traverse it in opposite directions.
- (dolist (buffer (if bury-or-kill
- (buffer-list frame)
- (nreverse (buffer-list frame))))
- (when (and (buffer-live-p buffer)
- (not (eq buffer old-buffer))
- (or (null pred) (funcall pred buffer))
- (not (eq (aref (buffer-name buffer) 0) ?\s))
- (or bury-or-kill (not (memq buffer next-buffers))))
- (if (and (not switch-to-visible-buffer)
- (get-buffer-window buffer frame))
- ;; Try to avoid showing a buffer visible in some other window.
- (unless visible
- (setq visible buffer))
- (setq new-buffer buffer)
- (set-window-buffer-start-and-point window new-buffer)
- (throw 'found t))))
+ ;; previous/next buffer traverse it in opposite directions. Skip
+ ;; this step for side windows.
+ (unless window-side
+ (dolist (buffer (if bury-or-kill
+ (buffer-list frame)
+ (nreverse (buffer-list frame))))
+ (when (and (buffer-live-p buffer)
+ (not (eq buffer old-buffer))
+ (or (null pred) (funcall pred buffer))
+ (not (eq (aref (buffer-name buffer) 0) ?\s))
+ ;; Don't show a buffer shown in a side window before.
+ (not (buffer-local-value 'window--sides-shown buffer))
+ (or bury-or-kill (not (memq buffer next-buffers))))
+ (if (and (not switch-to-visible-buffer)
+ (get-buffer-window buffer frame))
+ ;; Try to avoid showing a buffer visible in some other window.
+ (unless visible
+ (setq visible buffer))
+ (setq new-buffer buffer)
+ (set-window-buffer-start-and-point window new-buffer)
+ (throw 'found t)))))
(unless bury-or-kill
;; Scan reverted next buffers last (must not use nreverse
;; here!).
(interactive)
(let* ((window (window-normalize-window window t))
(frame (window-frame window))
+ (window-side (window-parameter window 'window-side))
(old-buffer (window-buffer window))
(next-buffers (window-next-buffers window))
(pred (frame-parameter frame 'buffer-predicate))
(unless (setq window (minibuffer-selected-window))
(error "Window %s is a minibuffer window" window)))
- (when (window-dedicated-p window)
+ (unless (memq (window-dedicated-p window) '(nil side))
;; Don't switch in dedicated window.
(error "Window %s is dedicated to buffer %s" window old-buffer))
window new-buffer (nth 1 entry) (nth 2 entry))
(throw 'found t)))
;; Scan the buffer list of WINDOW's frame next, skipping previous
- ;; buffers entries.
- (dolist (buffer (buffer-list frame))
- (when (and (buffer-live-p buffer)
- (not (eq buffer old-buffer))
- (or (null pred) (funcall pred buffer))
- (not (eq (aref (buffer-name buffer) 0) ?\s))
- (not (assq buffer (window-prev-buffers window))))
- (if (and (not switch-to-visible-buffer)
- (get-buffer-window buffer frame))
- ;; Try to avoid showing a buffer visible in some other window.
- (setq visible buffer)
- (setq new-buffer buffer)
- (set-window-buffer-start-and-point window new-buffer)
- (throw 'found t))))
+ ;; buffers entries. Skip this step for side windows.
+ (unless window-side
+ (dolist (buffer (buffer-list frame))
+ (when (and (buffer-live-p buffer)
+ (not (eq buffer old-buffer))
+ (or (null pred) (funcall pred buffer))
+ (not (eq (aref (buffer-name buffer) 0) ?\s))
+ ;; Don't show a buffer shown in a side window before.
+ (not (buffer-local-value 'window--sides-shown buffer))
+ (not (assq buffer (window-prev-buffers window))))
+ (if (and (not switch-to-visible-buffer)
+ (get-buffer-window buffer frame))
+ ;; Try to avoid showing a buffer visible in some other window.
+ (setq visible buffer)
+ (setq new-buffer buffer)
+ (set-window-buffer-start-and-point window new-buffer)
+ (throw 'found t)))))
;; Scan WINDOW's reverted previous buffers last (must not use
;; nreverse here!)
(dolist (entry (reverse (window-prev-buffers window)))
;; side window, throw an error unless `window-combination-resize'
;; equals 'side.
((and (not (eq window-combination-resize 'side))
- (window--side-window-p window))
+ (window-parameter window 'window-side))
(error "Cannot split side window or parent of side window"))
;; If `window-combination-resize' is 'side and window has a side
;; window sibling, bind `window-combination-limit' to t.
;; Always return the new window.
new)))))
+(defun split-window-no-error (&optional window size side pixelwise)
+ "Make a new window adjacent to WINDOW.
+This function is like `split-window' but does not signal an error
+when WINDOW cannot be split.
+
+For the meaning of all arguments see the documentation of
+`split-window'."
+ (condition-case nil
+ (split-window window size side pixelwise)
+ (error nil)))
+
;; I think this should be the default; I think people will prefer it--rms.
(defcustom split-window-keep-point t
"If non-nil, \\[split-window-below] preserves point in the new window.
(scroll-bars . ,(window-scroll-bars window))
(vscroll . ,(window-vscroll window))
(dedicated . ,(window-dedicated-p window))
- (point . ,(if writable point
- (copy-marker point
- (buffer-local-value
- 'window-point-insertion-type
- buffer))))
- (start . ,(if writable start (copy-marker start)))))))))
+ (point . ,(if writable
+ point
+ (with-current-buffer buffer
+ (copy-marker point
+ (buffer-local-value
+ 'window-point-insertion-type
+ buffer)))))
+ (start . ,(if writable
+ start
+ (with-current-buffer buffer
+ (copy-marker start))))))))))
(tail
(when (memq type '(vc hc))
(let (list)
((memq type '(vc hc))
(let* ((horizontal (eq type 'hc))
(total (window-size window horizontal pixelwise))
- (first t)
+ (first t)
+ (window-combination-limit (cdr (assq 'combination-limit state)))
size new)
(dolist (item state)
;; Find the next child window. WINDOW always points to the
(frame-char-height (window-frame window))
1)))))
(if (window-sizable-p window (- size) horizontal 'safe pixelwise)
- (let* ((window-combination-limit
- (assq 'combination-limit item)))
- ;; We must inherit the combination limit, otherwise
- ;; we might mess up handling of atomic and side
- ;; window.
- (setq new (split-window window size horizontal pixelwise)))
+ (progn
+ (setq new (split-window-no-error
+ window size horizontal pixelwise))
+ (setq window-combination-limit nil))
;; Give up if we can't resize window down to safe sizes.
(error "Cannot resize window %s" window))
(nth 3 scroll-bars) (nth 5 scroll-bars)))
(set-window-vscroll window (cdr (assq 'vscroll state)))
;; Adjust vertically.
- (if (memq window-size-fixed '(t height))
+ (if (or (memq window-size-fixed '(t height))
+ (window-preserved-size window))
;; A fixed height window, try to restore the
;; original size.
(let ((delta
window delta nil ignore nil nil nil pixelwise))
(window-resize window delta nil ignore pixelwise))))
;; Adjust horizontally.
- (if (memq window-size-fixed '(t width))
+ (if (or (memq window-size-fixed '(t width))
+ (window-preserved-size window t))
;; A fixed width window, try to restore the original
;; size.
(let ((delta
(window-size window t pixelwise)))
window-size-fixed)
(when (window--resizable-p
- window delta nil nil nil nil nil pixelwise)
- (window-resize window delta nil nil pixelwise)))
+ window delta t nil nil nil nil pixelwise)
+ (window-resize window delta t nil pixelwise)))
;; Else check whether the window is not wide enough.
(let* ((min-size (window-min-size window t ignore pixelwise))
(delta (- min-size (window-size window t pixelwise))))
;; When WINDOW is internal, reduce it to a live one to put STATE into,
;; see Bug#16793.
(unless (window-live-p window)
- (let ((root (frame-root-window window)))
- (if (eq window root)
- (setq window (frame-first-window root))
- (setq root window)
- (setq window (catch 'live
- (walk-window-subtree
- (lambda (window)
- (when (window-live-p window)
- (throw 'live window)))
- root))))
+ (let ((root window))
+ (setq window (catch 'live
+ (walk-window-subtree
+ (lambda (window)
+ (when (and (window-live-p window)
+ (not (window-parameter window 'window-side)))
+ (throw 'live window)))
+ root)))
(delete-other-windows-internal window root)))
(set-window-dedicated-p window nil)
(when (eq (window-deletable-p window) t)
(delete-window window))))
(window--check frame))))
+
+(defun window-swap-states (&optional window-1 window-2 size)
+ "Swap the states of live windows WINDOW-1 and WINDOW-2.
+WINDOW-1 must specify a live window and defaults to the selected
+one. WINDOW-2 must specify a live window and defaults to the
+window following WINDOW-1 in the cyclic ordering of windows,
+excluding minibuffer windows and including live windows on all
+visible frames.
+
+Optional argument SIZE non-nil means to try swapping the sizes of
+WINDOW-1 and WINDOW-2 as well. A value of `height' means to swap
+heights only, a value of `width' means to swap widths only, while
+t means to swap both widths and heights, if possible. Frames are
+not resized by this function."
+ (interactive)
+ (setq window-1 (window-normalize-window window-1 t))
+ (if window-2
+ (unless (window-live-p window-2)
+ (error "%s is not a live window" window-2))
+ (setq window-2 (next-window window-1 'nomini 'visible)))
+ (unless (eq window-1 window-2)
+ (let* ((height (memq size '(t height)))
+ (width (memq size '(t width)))
+ (state-1 (window-state-get window-1))
+ (width-1 (and width (window-text-width window-1 t)))
+ (height-1 (and height (window-text-height window-1 t)))
+ (state-2 (window-state-get window-2))
+ (width-2 (and width (window-text-width window-2 t)))
+ (height-2 (and height (window-text-height window-2 t)))
+ old preserved)
+ ;; Swap basic states.
+ (window-state-put state-1 window-2 t)
+ (window-state-put state-2 window-1 t)
+ ;; Swap overlays with `window' property.
+ (with-current-buffer (window-buffer window-1)
+ (dolist (overlay (overlays-in (point-min) (point-max)))
+ (let ((window (overlay-get overlay 'window)))
+ (cond
+ ((not window))
+ ((eq window window-1)
+ (overlay-put overlay 'window window-2))
+ ((eq window window-2)
+ (overlay-put overlay 'window window-1))))))
+ (unless (eq (window-buffer window-1) (window-buffer window-2))
+ (with-current-buffer (window-buffer window-2)
+ (dolist (overlay (overlays-in (point-min) (point-max)))
+ (let ((window (overlay-get overlay 'window)))
+ (cond
+ ((not window))
+ ((eq window window-1)
+ (overlay-put overlay 'window window-2))
+ ((eq window window-2)
+ (overlay-put overlay 'window window-1)))))))
+ ;; Try to swap window sizes.
+ (when size
+ (unless (= (setq old (window-text-width window-1 t)) width-2)
+ (window-resize-no-error window-1 (- width-2 old) t t t))
+ (unless (= (setq old (window-text-width window-2 t)) width-1)
+ (setq preserved (window-preserved-size window-1 t))
+ (window-preserve-size window-1 t t)
+ (window-resize-no-error window-2 (- width-1 old) t t t)
+ (window-preserve-size window-1 t preserved))
+ (unless (= (setq old (window-text-height window-1 t)) height-2)
+ (window-resize-no-error window-1 (- height-2 old) nil t t))
+ (unless (= (setq old (window-text-height window-2 t)) height-1)
+ (setq preserved (window-preserved-size window-1))
+ (window-preserve-size window-1 nil t)
+ (window-resize-no-error window-2 (- height-1 old) nil t t)
+ (window-preserve-size window-1 nil preserved))))))
\f
(defun display-buffer-record-window (type window buffer)
"Record information for window used by `display-buffer'.
wide as `split-width-threshold'.
- When WINDOW is split evenly, the emanating windows are at least
`window-min-width' or two (whichever is larger) columns wide."
- (when (and (window-live-p window) (not (window--side-window-p window)))
+ (when (and (window-live-p window)
+ (not (window-parameter window 'window-side)))
(with-current-buffer (window-buffer window)
(if horizontal
;; A window can be split horizontally when its width is not
(set-window-dedicated-p window dedicated))
(when (memq type '(window frame))
(set-window-prev-buffers window nil)))
- (let ((parameter (window-parameter window 'quit-restore))
+ (let ((quit-restore (window-parameter window 'quit-restore))
(height (cdr (assq 'window-height alist)))
(width (cdr (assq 'window-width alist)))
(size (cdr (assq 'window-size alist)))
(preserve-size (cdr (assq 'preserve-size alist))))
(cond
((or (eq type 'frame)
- (and (eq (car parameter) 'same)
- (eq (nth 1 parameter) 'frame)))
+ (and (eq (car quit-restore) 'same)
+ (eq (nth 1 quit-restore) 'frame)))
;; Adjust size of frame if asked for.
(cond
((not size))
((functionp size)
(ignore-errors (funcall size window)))))
((or (eq type 'window)
- (and (eq (car parameter) 'same)
- (eq (nth 1 parameter) 'window)))
+ (and (eq (car quit-restore) 'same)
+ (eq (nth 1 quit-restore) 'window)))
;; Adjust height of window if asked for.
(cond
((not height))
;; Preserve window size if asked for.
(when (consp preserve-size)
(window-preserve-size window t (car preserve-size))
- (window-preserve-size window nil (cdr preserve-size))))))
-
+ (window-preserve-size window nil (cdr preserve-size)))))
+ ;; Assign any window parameters specified.
+ (let ((parameters (cdr (assq 'window-parameters alist))))
+ (dolist (parameter parameters)
+ (set-window-parameter
+ window (car parameter) (cdr parameter)))))
window))
(defun window--maybe-raise-frame (frame)
preserve the width of the window, (nil . t) to preserve its
height or (t . t) to preserve both.
+ `window-parameters' -- Value specifies an alist of window
+ parameters to give the chosen window.
+
The ACTION argument to `display-buffer' can also have a non-nil
and non-list value. This means to display the buffer in a window
other than the selected one, even if it is already displayed in
(window--display-buffer
buffer window 'window alist display-buffer-mark-dedicated))
(and (not (frame-parameter nil 'unsplittable))
- (setq window
- (condition-case nil
- (split-window (window--major-non-side-window))
- (error nil)))
+ (setq window (split-window-no-error (window-main-window)))
(window--display-buffer
buffer window 'window alist display-buffer-mark-dedicated))
(and (setq window bottom-window)