emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master b8fd71d: Document and fix some bugs with side windo


From: Martin Rudalics
Subject: [Emacs-diffs] master b8fd71d: Document and fix some bugs with side windows
Date: Wed, 5 Oct 2016 08:30:40 +0000 (UTC)

branch: master
commit b8fd71d5709650c1aced92c772f70595c51881d2
Author: Martin Rudalics <address@hidden>
Commit: Martin Rudalics <address@hidden>

    Document and fix some bugs with side windows
    
    Add a documentation for side windows and fix some bugs
    found when testing their behavior.  Also add a new window
    parameter `no-delete-other-window', a new `display-buffer'
    alist member called `window-parameters', and functions to
    toggle and reverse side windows on a frame.  Add new function
    `window-swap-states' to exchange states of two live windows.
    
    * lisp/window.el (display-buffer-in-atom-window): Use
    `split-window-no-error'.
    (window-sides-vertical): Maybe change layouts when setting this
    variable.
    (window-sides-reversed): New option.
    (window-sides-slots): Rewrite doc-string and help echoes.
    (window-sides-shown): New buffer-local variable set when showing
    a buffer in a side window.
    (window--sides-inhibit-check): New variable.
    (window--sides-reverse-on-frame-p, window-toggle-side-windows)
    (window--sides-reverse-all, window--sides-reverse-frame)
    (window--sides-reverse-side, window--sides-reverse)
    (window--sides-verticalize-frame, window--sides-verticalize)
    (window--sides-check-failed): New functions.
    (window--side-window-p): Remove function.
    (window--major-non-side-window): Rename to `window-main-window',
    adjust callers, rewrite doc-string.
    (window--major-side-window): Rename to
    `window--make-major-side-window-next-to', adjust caller, fix
    doc-string.
    (display-buffer-in-major-side-window): Rename to
    `window--make-major-side-window', adjust
    caller, rewrite doc-string.  Make `window-side' and
    `window-slot' parameters persistent (Bug#23858).  Don't set
    `delete-window' parameter.  Add `preserve-size' entry to ALIST.
    (delete-side-window): Remove function.
    (display-buffer-in-side-window): Fix doc-string.  Don't set
    `delete-window' parameter.  Add `preserve-size' entry to ALIST.
    (window--side-check): Rename to window--sides-check.  Rewrite
    completely.  Adjust caller.
    (window-resize-no-error): Don't describe PIXELWISE argument.
    (adjust-window-trailing-edge): Fix bug that disallowed
    re-enlarging windows that were too small.
    (window-deletable-p): Don't tell that a minibuffer window on a
    non-minibuffer-only frame can be deleted.  Fix doc-string.
    (delete-window): Handle deleting a side window here (the
    `delete-window' parameter is no more set for side windows).
    (delete-other-windows): Handle ‘no-delete-other-window'
    parameter. Don't treat side windows separately (see discussion
    of Bug#24368) but keep optimization that makes the main window
    the root window of its frame.
    (switch-to-prev-buffer, switch-to-next-buffer): Handle side
    windows and buffers shown in side windows separately.
    (split-window-no-error): New function.
    (window--state-get-1): Use right buffer when storing window
    point and start positions and WRITABLE is nil (Bug#24368).
    (window--state-put-1): Fix handling of `window-combination-limit'.
    Use `split-window-no-error'.
    (window--state-put-2): Try to restore windows with preserved
    size to their original size.  Fix bug where a fixed window's width
    was not preserved.
    (window-state-put): When reducing an internal window to a live
    one, don't choose a side window.
    (window-swap-states): New function.
    (window-splittable-p): Don't call `window--side-window-p'.
    (window--display-buffer): Handle `window-parameters' ALIST entry.
    Minor rewrite.
    (display-buffer): Mention `window-parameters' entry in
    doc-string.
    (display-buffer-at-bottom): Call `split-window-no-error'.
    
    * doc/lispref/elisp.texi (Top): New section "Side Windows".
    * doc/lispref/windows.texi (Deleting Windows): Fix descriptions
    of `delete-window' and `delete-other-windows' wrt window
    parameters and side windows.
    (Display Action Functions): Mention `window-parameters' ALIST
    entry.
    (Side Windows): New section (Bug#18170).
    (Window Configurations): Describe new function `window-swap-states'.
    (Window Parameters): Say that functions may behave specially when
    their homonymous window parameter has been set.  Mention new
    parameter `no-delete-other-window'.  Add cross reference for
    `window-side' and `window-slot' parameters.
---
 doc/lispref/elisp.texi   |    9 +
 doc/lispref/windows.texi |  394 ++++++++++++++++++--
 etc/NEWS                 |   21 ++
 lisp/window.el           |  906 +++++++++++++++++++++++++++++++---------------
 4 files changed, 1012 insertions(+), 318 deletions(-)

diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index 3297e53..1c6e059 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -1038,6 +1038,7 @@ Windows
                               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.
@@ -1051,6 +1052,14 @@ Windows
                               redisplay going past a certain point,
                               or window configuration changes.
 
+Side Windows
+
+* 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.
+
 Frames
 
 * Creating Frames::         Creating additional frames.
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 3c9df0b..3ab8c95 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -33,6 +33,7 @@ is displayed in windows.
                               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.
@@ -1275,9 +1276,12 @@ Configurations}).
 @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
@@ -1285,33 +1289,34 @@ 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
address@hidden 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
address@hidden window parameter is a function, that function is
-called with the argument @var{window}, in lieu of the usual action of
address@hidden  Otherwise, this function obeys the
address@hidden or @code{window-side} window parameter, if any.
address@hidden Parameters}.
+The behavior of this function may be altered by the window parameters of
address@hidden, so long as the variable @code{ignore-window-parameters} is
address@hidden  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
address@hidden 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
address@hidden, so long as the variable @code{ignore-window-parameters} is
address@hidden  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
address@hidden  @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 address@hidden
 @end deffn
 
 @deffn Command delete-windows-on &optional buffer-or-name frame
@@ -2574,7 +2579,12 @@ assumed that when the caller specifies a address@hidden
 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
address@hidden 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
@@ -3068,6 +3078,295 @@ other frame on the same terminal.
 @end defopt
 
 
address@hidden Side Windows
address@hidden Side Windows
address@hidden side windows
address@hidden main window
address@hidden 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
address@hidden (@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).
+
address@hidden
+* 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.
address@hidden menu
+
+
address@hidden Displaying Buffers in Side Windows
address@hidden 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.
+
address@hidden 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
address@hidden  The following symbols in @var{alist} are special
+for this function:
+
address@hidden @code
address@hidden 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.
+
address@hidden 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
address@hidden value.  If unspecified, the window is located in the middle
+of the specified side.
address@hidden 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}.
address@hidden 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
address@hidden 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
address@hidden 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.
+
+
address@hidden Side Window Options and Functions
address@hidden Side Window Options and Functions
+
+The following options provide additional control over the placement of
+side windows.
+
address@hidden window-sides-vertical
+If address@hidden, 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.
address@hidden defopt
+
address@hidden 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
address@hidden, 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
address@hidden parameter changed accordingly.
address@hidden defopt
+
address@hidden 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.
address@hidden defopt
+
+When a frame has side windows, the following function returns the main
+window of that frame.
+
address@hidden 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}.
address@hidden defun
+
+The following command is handy to toggle the appearance of all side
+windows on a specified frame.
+
address@hidden 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
address@hidden frame parameter and deletes all side windows on
address@hidden 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
address@hidden leaving @var{frame}'s main window alone.
+
+An error is signaled if @var{frame} has no side window and no saved
+state is found.
address@hidden deffn
+
+
address@hidden Frame Layouts with Side Windows
address@hidden 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:
+
address@hidden
address@hidden
+     ___________________________________
+    |          *Buffer List*            |
+    |___________________________________|
+    |     |                       |     |
+    |  *  |                       |  *  |
+    |  d  |                       |  T  |
+    |  i  |                       |  a  |
+    |  r  |   Main Window Area    |  g  |
+    |  e  |                       |  s  |
+    |  d  |                       |  *  |
+    |  *  |                       |     |
+    |_____|_______________________|_____|
+    | *help*/*grep*/  |  *shell*/       |
+    | *Completions*   |  *compilation*  |
+    |_________________|_________________|
+    |             Echo Area             |
+    |___________________________________|
+
+
address@hidden group
address@hidden 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.
+
address@hidden
address@hidden
+(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)))
address@hidden group
address@hidden 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
address@hidden 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 address@hidden 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}.
address@hidden 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
address@hidden 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.
+
address@hidden
address@hidden
+(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))))
address@hidden group
address@hidden 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
address@hidden dired-default-directory-on-left} should now reproduce the frame
+layout sketched above.
+
+
 @node Window Point
 @section Windows and Point
 @cindex window position
@@ -4242,6 +4541,25 @@ is @code{safe}, this means windows can get as small as 
one line
 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:
+
address@hidden Command window-swap-states &optional window-1 window-2 size
+This command swaps the states of the two live windows @var{window-1} and
address@hidden  @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} address@hidden means to try swapping the
+sizes of @var{window-1} and @var{window-2} as well.  A value of
address@hidden 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.
address@hidden deffn
+
 
 @node Window Parameters
 @section Window Parameters
@@ -4279,6 +4597,7 @@ earlier by @code{window-state-get}, all cloned windows 
have their
 parameters reset to @code{nil}.  The following variable allows you to
 override the standard behavior:
 
address@hidden 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
@@ -4308,10 +4627,10 @@ may fail with an @code{invalid-read-syntax} error.
 @end defvar
 
 Some functions (notably @code{delete-window},
address@hidden 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
address@hidden value:
address@hidden 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 address@hidden value:
 
 @defvar ignore-window-parameters
 If this variable is address@hidden, some standard functions do not
@@ -4337,6 +4656,10 @@ This parameter affects the execution of 
@code{delete-window}
 This parameter affects the execution of @code{delete-other-windows}
 (@pxref{Deleting Windows}).
 
address@hidden @code{no-delete-other-window}
+This parameter marks the window as not deletable by
address@hidden (@pxref{Deleting Windows}).
+
 @item @code{split-window}
 This parameter affects the execution of @code{split-window}
 (@pxref{Splitting Windows}).
@@ -4387,6 +4710,10 @@ The fourth element is the buffer whose display caused 
the creation of
 this parameter.  @code{quit-restore-window} deletes the specified window
 only if it still shows that buffer.
 
address@hidden @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 address@hidden, specify the minimum values (in columns) for the left
@@ -4409,8 +4736,7 @@ applications.  It might be replaced by an improved 
solution in future
 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
diff --git a/etc/NEWS b/etc/NEWS
index bd94c94..4268a40 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -611,6 +611,27 @@ collection).
 ** The new functions 'make-nearby-temp-file' and 'temporary-file-directory'
 can be used for creation of temporary files of remote or mounted directories.
 
+** Changes in Frame- and Window- Handling
+
++++
+*** Support for side windows is now official.  The display action
+function `display-buffer-in-side-window' will display its buffer in a
+side window.  Functions for toggling all side windows on a frame,
+changing and reversing the layout of side windows and returning the main
+(major non-side) window of a frame are provided.  For details consult
+the section "Side Windows" in the Elisp manual.
+
++++
+*** New `display-buffer' alist entry `window-parameters' allows to
+assign window parameters to the window used for displaying the buffer.
+
++++
+*** New window parameter `no-delete-other-window' prevents that
+its window gets deleted by `delete-other-windows'.
+
++++
+*** New command `window-swap-states' swaps the states of two live
+windows.
 
 * Changes in Emacs 26.1 on Non-Free Operating Systems
 
diff --git a/lisp/window.el b/lisp/window.el
index 6728ea3..4511267 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -657,7 +657,7 @@ failed."
     (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
@@ -709,24 +709,50 @@ no child windows or one of its child windows is not 
atomic."
   (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
@@ -734,56 +760,94 @@ number of slots on that side."
     :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)
@@ -792,16 +856,20 @@ its root 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
@@ -839,35 +907,37 @@ SIDE must be one of the symbols `left', `top', `right' or
        (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
@@ -882,15 +952,10 @@ SIDE.  Return the new window, nil if its creation window 
failed."
                   ;; 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.
@@ -906,9 +971,27 @@ following special symbols can be used in ALIST.
   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))
@@ -918,15 +1001,20 @@ following special symbols can be used in ALIST.
     (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
@@ -935,17 +1023,18 @@ following special symbols can be used in ALIST.
                  ((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
@@ -953,7 +1042,7 @@ following special symbols can be used in ALIST.
            (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.
@@ -970,131 +1059,241 @@ following special symbols can be used in ALIST.
              (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.
@@ -2631,10 +2830,7 @@ instead."
   "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)))
@@ -3224,8 +3420,10 @@ move it as far as possible in the desired direction."
        (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
@@ -3245,7 +3443,8 @@ move it as far as possible in the desired direction."
                  (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
@@ -3259,8 +3458,10 @@ move it as far as possible in the desired direction."
        (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
@@ -3289,8 +3490,9 @@ move it as far as possible in the desired direction."
          ;; 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
@@ -3740,7 +3942,9 @@ and no others."
 (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
@@ -3767,10 +3971,14 @@ Return `frame' if deleting WINDOW should also delete 
its frame."
                  (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)
@@ -3826,11 +4034,14 @@ that is its frame's root window."
          (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
@@ -3886,8 +4097,7 @@ window signal an error."
   (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
@@ -3905,18 +4115,48 @@ window signal an error."
        (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.
@@ -4066,6 +4306,7 @@ to it."
   (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))
@@ -4076,7 +4317,7 @@ to it."
       (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))
 
@@ -4106,23 +4347,27 @@ to it."
       ;; 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!).
@@ -4184,6 +4429,7 @@ found."
   (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))
@@ -4193,7 +4439,7 @@ found."
       (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))
 
@@ -4211,20 +4457,23 @@ found."
           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)))
@@ -4700,7 +4949,7 @@ frame.  The selected window is not changed by this 
function."
        ;; 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.
@@ -4894,6 +5143,17 @@ frame.  The selected window is not changed by this 
function."
          ;; 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.
@@ -5286,12 +5546,17 @@ specific buffers."
                     (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)
@@ -5363,7 +5628,8 @@ value can be also stored on disk and read back in a new 
session."
      ((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
@@ -5406,12 +5672,10 @@ value can be also stored on disk and read back in a new 
session."
                                       (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))
 
@@ -5462,7 +5726,8 @@ value can be also stored on disk and read back in a new 
session."
                   (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
@@ -5484,7 +5749,8 @@ value can be also stored on disk and read back in a new 
session."
                                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
@@ -5494,8 +5760,8 @@ value can be also stored on disk and read back in a new 
session."
                              (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))))
@@ -5540,16 +5806,14 @@ windows can get as small as `window-safe-min-height' and
   ;; 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)
@@ -5634,6 +5898,75 @@ windows can get as small as `window-safe-min-height' and
          (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))))))
 
 (defun display-buffer-record-window (type window buffer)
   "Record information for window used by `display-buffer'.
@@ -6139,7 +6472,8 @@ hold:
   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
@@ -6314,15 +6648,15 @@ live."
        (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))
@@ -6340,8 +6674,8 @@ live."
         ((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))
@@ -6377,8 +6711,12 @@ live."
        ;; 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)
@@ -6602,6 +6940,9 @@ Recognized alist entries include:
     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
@@ -6952,10 +7293,7 @@ selected frame."
             (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)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]