diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 0b8d7d3b76d..e67fc256b3d 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -22,6 +22,7 @@ Windows * Deleting Windows:: Removing a window from its frame. * Recombining Windows:: Preserving the frame layout when splitting and deleting windows. +* Resurrecting deleted windows:: Restoring indivdual windows. * Cyclic Window Ordering:: Moving around the existing windows. * Buffers and Windows:: Each window displays the contents of a buffer. * Switching Buffers:: Higher-level functions for switching to a buffer. @@ -1355,7 +1356,7 @@ Splitting Windows Examples of such windows are side windows (@pxref{Side Windows}) and atomic windows (@pxref{Atomic Windows}). -@defun split-window &optional window size side pixelwise +@defun split-window &optional window size side pixelwise refer This function creates a new live window next to the window @var{window}. If @var{window} is omitted or @code{nil}, it defaults to the selected window. That window is split, and reduced in @@ -1364,7 +1365,7 @@ Splitting Windows The optional second argument @var{size} determines the sizes of @var{window} and/or the new window. If it is omitted or @code{nil}, both windows are given equal sizes; if there is an odd line, it is -allocated to the new window. If @var{size} is a positive number, +allotted to the new window. If @var{size} is a positive number, @var{window} is given @var{size} lines (or columns, depending on the value of @var{side}). If @var{size} is a negative number, the new window is given @minus{}@var{size} lines (or columns). @@ -1374,13 +1375,13 @@ Splitting Windows Sizes}). Thus, it signals an error if splitting would result in making a window smaller than those variables specify. However, a non-@code{nil} value for @var{size} causes those variables to be -ignored; in that case, the smallest allowable window is considered to be -one that has space for a text that is one line tall and/or two columns -wide. +ignored; in that case, the smallest allowable sizes are determined by +the values of @code{window-safe-min-height} and +@code{window-safe-min-width}. Hence, if @var{size} is specified, it's the caller's responsibility to -check whether the emanating windows are large enough to encompass all of -their decorations like a mode line or a scroll bar. The function +check whether the emanating windows are large enough to encompass all +areas like a mode line or a scroll bar. The function @code{window-min-size} (@pxref{Window Sizes}) can be used to determine the minimum requirements of @var{window} in this regard. Since the new window usually inherits areas like the mode line or the scroll bar from @@ -1399,14 +1400,62 @@ Splitting Windows window is placed on the left of @var{window}. In both these cases, @var{size} specifies a total window width, in columns. +As a rule, if @var{window} already forms a combination (@pxref{Windows +and Frames}) that matches @var{side} (a horizontal combination matches +@var{side} if it is @code{left} or @code{right}, a vertical combination +matches @var{side} if it is @code{above} or @code{below}) and +@code{window-combination-limit} (@pxref{Recombining Windows}) is +@code{nil}, this function reuses @var{window}'s parent in the window +tree as parent of the new window. + +However, if @var{window} is in a combination that does not match +@var{side} or if @code{window-combination-limit} is non-@code{nil}, this +function makes a new parent window that replaces @var{window} in the +window tree and makes @var{window} and the new window its sole child +windows. This standard behavior can be overridden via the @var{refer} +argument. + The optional fourth argument @var{pixelwise}, if non-@code{nil}, means to interpret @var{size} in units of pixels, instead of lines and columns. -If @var{window} is a live window, the new window inherits various -properties from it, including margins and scroll bars. If -@var{window} is an internal window, the new window inherits the -properties of the window selected within @var{window}'s frame. +If the optional fifth argument @var{refer} is non-@code{nil}, it +specifies a reference window used for setting up properties of the new +window. If non-@code{nil}, @var{refer} can be either a window or a cons +cell of two windows. + +If @var{refer} is a cons cell, its @sc{car} has to specify a deleted, +former live window - a window that has shown a buffer before - on the +same frame as @var{window}. That buffer must be still live. The +@sc{cdr} has to specify a deleted window that was, before its deletion, +a parent window on the same frame as @var{window}. In this case, rather +then making new windows, this function replaces @var{window} with the +@sc{cdr} of @var{refer} in the window tree and makes @var{window} and +@var{refer}'s @sc{car} its new child windows. Buffer, start and point +positions of @var{refer}'s @sc{car} are set to the values they had +immediately before @var{refer}'s @sc{car} was deleted the last time. +Decorations and parameters remain unaltered from their values before +@var{refer}'s @sc{car} and @sc{cdr} were deleted. + +Alternatively, @var{refer} may specify a deleted, former live window - a +window that has shown a buffer before - on the same frame as +@var{window}. That buffer must be still live. In this case, this +function do not make a new window but rather makes @var{refer} live +again and inserts it into the window tree at the position and with the +sizes the new window would have been given. Buffer, start and point +positions of @var{refer} are set to the values they had immediately +before @var{refer} was deleted the last time. Decorations and +parameters remain unaltered from their values before @var{refer} was +deleted. The parent of @var{refer} is then determined as if it were a +window created anew. + +Otherwise, @var{refer} must specify a live window. In this case, the +new window will inherit properties like buffer, start and point +positions and some decorations from @var{refer}. If @var{refer} is +@code{nil} or omitted, then if @var{window} is live, any such properties +are inherited from @var{window}. If, however, @var{window} is an +internal window, the new window will inherit these properties from the +window selected on @var{window}'s frame. The behavior of this function may be altered by the window parameters of @var{window}, so long as the variable @@ -2048,6 +2097,157 @@ Recombining Windows windows. +@node Resurrecting deleted windows +@section Resurrecting deleted windows +@cindex resurrecting deleted windows + +After a window has been deleted (@pxref{Deleting Windows}) it cannot be +used any more by functions that require a valid window as their argument +even if some Lisp variable still references that window. When the last +reference to a window has ceased to exist, the window's Lisp object will +be eventually recycled by the garbage collector. + +There are two ways to resurrect a deleted window whose object has not +been yet recycled by the collector: The first is to keep a reference to +that window in a saved window configuration (@pxref{Window +Configurations}) and then call @code{set-window-configuration} with that +configuration as argument. The second one is to keep a reference to +that window in a variable or let-bind it and then use that reference as +@var{refer} argument in @code{split-window} (@pxref{Splitting Windows}). + +The major difference between these two is that +@code{set-window-configuration} restores the frame layout that existed +before deleting the window. The @code{split-window} approach, on the +other hand, allows for arbitrary variations of the layout. + +Consider the following example starting with a frame containing a single +window showing the buffer @file{*scratch*}: + +@example +@group +(let* ((old (selected-window)) + (new (split-window old nil 'right)) + overlay) + (with-current-buffer (get-buffer-create "*Messages*") + (set-window-buffer new (current-buffer)) + (setq overlay (make-overlay (point-min) (point-max))) + (overlay-put overlay 'face 'highlight) + (overlay-put overlay 'window new) + (message "new %s parent %s" new (window-parent new)) + (sit-for 3)) + (delete-window new) + (setq new (split-window old nil 'left)) + (set-window-buffer new (get-buffer-create "*Messages*")) + (format "new %s parent %s" new (window-parent new))) +@end group +@end example + +When you run that code in @file{*scratch*} it will first split the +window showing @file{*scratch*} to display @file{*Messages*} in a new +window on the right. It also sets up an overlay with a window property +to highlight the text of @file{*Messages*} in the new window and +displays a message showing the new window and its parent in the window +tree. It then deletes the new window and resurrects it on the left of +the @file{*scratch*} window again displaying a message showing the new +window and its parent in the window tree. + +Note that both, new window and its parent have changed after moving the +@file{*Messages*} window to the left. Also, the highlighting disappears +because any properties set up for the new window on the right are lost +when the new window is re-created on the left. + +The following code uses the @var{refer} argument of @code{split-window} +instead. + +@example +@group +(let* ((old (selected-window)) + (new (split-window old nil 'right)) + overlay) + (with-current-buffer (get-buffer-create "*Messages*") + (set-window-buffer new (current-buffer)) + (setq overlay (make-overlay (point-min) (point-max))) + (overlay-put overlay 'face 'highlight) + (overlay-put overlay 'window new) + (message "new %s parent %s" new (window-parent new)) + (sit-for 3)) + (delete-window new) + (split-window old nil 'left nil new) + (format "new %s parent %s" new (window-parent new))) +@end group +@end example + +Note that all properties of the resurrected window like its decorations, +parameters as well as any overlays with a window property are preserved +as if that window had never been deleted. The only things that changed +are its position in the window tree and consequently the values returned +by @code{window-left-child} of its parent window as well as the values +returned by @code{window-prev-sibling} and @code{window-next-sibling} of +the window and its sibling. + +The following code passes both, the new window on the right and its +parent, via the @var{refer} argument to @code{split-window}: instead. + +@example +@group +(let* ((old (selected-window)) + (new (split-window old nil 'right)) + (parent (window-parent new)) + overlay) + (with-current-buffer (get-buffer-create "*Messages*") + (set-window-buffer new (current-buffer)) + (setq overlay (make-overlay (point-min) (point-max))) + (overlay-put overlay 'face 'highlight) + (overlay-put overlay 'window new) + (message "new %s parent %s" new (window-parent new)) + (sit-for 3)) + (delete-window new) + (split-window old nil 'left nil (cons new parent)) + (format "new %s parent %s" new (window-parent new))) +@end group +@end example + +Note that the parent window has been resurrected along with the new +window. + +Resurrecting dead windows is useful to preserve the identity of windows +in actions that are supposed to do that like moving windows around on a +frame or hiding them temporarily. Any properties of such a window like +its decorations, the buffer it has shown previously, that buffer's start +and point position in the window, the window's dedicated status, its +cursor type are left untouched and there's no need to recreate them from +scratch. For internal windows, the value of that window's combination +limit is preerved which means that the window can be recombined +(@pxref{Recombining Windows}) as before. + +Due to certain limitations in the way windows can be split, making a +sequence of changes to the window structure can be more tricky. It's +still fairly simple to rotate three windows as follows: + +@example +@group +(let* ((old (selected-window)) + (new-1 (split-window old nil 'right)) + (parent-1 (window-parent old)) + (new-2 (split-window new-1 nil 'below)) + (parent-2 (window-parent new-2)) + new) + (message "old %s (%s) .. new-1 %s (%s) .. new-2 %s (%s)" + old (window-parent old) + new-1 (window-parent new-1) + new-2 (window-parent new-2)) + (sit-for 3) + (delete-other-windows old) + (setq new (split-window old nil 'below nil (cons new-1 parent-1))) + (split-window new nil 'right nil (cons new-2 parent-2)) + (format "old %s (%s) .. new-1 %s (%s) .. new-2 %s (%s)" + old (window-parent old) + new-1 (window-parent new-1) + new-2 (window-parent new-2))) +@end group +@end example + + @node Cyclic Window Ordering @section Cyclic Ordering of Windows @cindex cyclic ordering of windows @@ -7126,13 +7326,16 @@ Window Hooks window. @defun window-old-buffer &optional window -This function returns the buffer shown in @var{window} at the last -time window change functions were run for @var{window}'s frame. If it -returns @code{nil}, @var{window} has been created after that. If it -returns @code{t}, @var{window} was not shown at that time but has been -restored from a previously saved window configuration afterwards. -Otherwise, the return value is the buffer shown by @var{window} at -that time. +This function returns the buffer shown in @var{window} at the last time +window change functions were run for @var{window}'s frame. If it +returns @code{nil}, @var{window} is either an internal window or has +been created after that. If it returns @code{t}, @var{window} was not +shown at that time but has been restored from a previously saved window +configuration afterwards. Otherwise, the return value is the buffer +shown by @var{window} at that time. As a special case, if @var{window} +has been deleted, this function returns the last buffer @var{window} had +shown at that time. @var{window} can be any window and defaults to the +selected one. @end defun @defun window-old-pixel-width &optional window diff --git a/lisp/window.el b/lisp/window.el index b50770cbd7e..060716df936 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -5511,54 +5511,95 @@ window--combination-resizable (setq sibling (window-next-sibling sibling))) (/ size (1+ number)))) -(defun split-window (&optional window size side pixelwise) +(defun split-window (&optional window size side pixelwise refer) "Make a new window adjacent to WINDOW. WINDOW must be a valid window and defaults to the selected one. Return the new window which is always a live window. -Optional argument SIZE a positive number means make WINDOW SIZE -lines or columns tall. If SIZE is negative, make the new window --SIZE lines or columns tall. If and only if SIZE is non-nil, its -absolute value can be less than `window-min-height' or -`window-min-width'; so this command can make a new window as -small as one line or two columns. SIZE defaults to half of -WINDOW's size. +If the optional argument SIZE is a positive number, shrink WINDOW +to SIZE lines or columns in order to accommodate the new window. +If SIZE is a negative number, make the new window -SIZE lines or +columns tall. In both cases, the absolute value of SIZE can be +less than `window-min-height' or `window-min-width'; so this +function can make a new window as small as one line or two +columns. If SIZE is not a number, make the new window occupy +half of WINDOW's size. Optional third argument SIDE nil (or `below') specifies that the -new window shall be located below WINDOW. SIDE `above' means the -new window shall be located above WINDOW. In both cases SIZE +new window shall be made below WINDOW. SIDE `above' means the +new window shall be made above WINDOW. In both cases SIZE specifies the new number of lines for WINDOW (or the new window if SIZE is negative) including space reserved for the mode and/or -header line. +header line, scroll bars and window dividers. -SIDE t (or `right') specifies that the new window shall be -located on the right side of WINDOW. SIDE `left' means the new -window shall be located on the left of WINDOW. In both cases -SIZE specifies the new number of columns for WINDOW (or the new -window provided SIZE is negative) including space reserved for -fringes and the scrollbar or a divider column. +SIDE t (or `right') specifies that the new window shall be made +on the right side of WINDOW. SIDE `left' means the new window +shall be made on the left of WINDOW. In both cases, SIZE +specifies the new number of columns for WINDOW (or the new window +provided SIZE is negative) including any space reserved for +fringes, scroll bar and window dividers. For compatibility reasons, SIDE `up' and `down' are interpreted as `above' and `below'. Any other non-nil value for SIDE is currently handled like t (or `right'). +As a rule, if WINDOW already forms a combination that matches the SIDE +parameter and `window-combination-limit' is nil, reuse WINDOW's parent +in the window tree as parent of the new window. If WINDOW is in a +combination that is orthogonal to the SIDE parameter or if +`window-combination-limit' is non-nil, make a new parent window that +replaces WINDOW in the window tree and make WINDOW and the new window +its sole child windows. This standard behavior can be overridden via +the REFER argument. + PIXELWISE, if non-nil, means to interpret SIZE pixelwise. +If the optional fifth argument REFER is non-nil, it specifies a +reference window used for setting up properties of the new window. +REFER can be either a window or a cons cell of two windows. + +If REFER is a cons cell, its car has to specify a deleted, former live +window - a window that has shown a buffer before - on the same frame as +WINDOW. That buffer must be still live. The cdr has to specify a +deleted window that was a parent window on the same frame as WINDOW +before it was deleted. In this case, rather then making new windows, +replace WINDOW with the cdr of REFER in the window tree and make WINDOW +and REFER's car its new child windows. Buffer, start and point +positions of REFER's car are set to the values they had immediately +before REFER's car was deleted the last time. Decorations and +parameters remain unaltered from their values before REFER's car and cdr +were deleted. + +Alternatively REFER may specify a deleted, former live window - a window +that has shown a buffer before - on the same frame as WINDOW. In this +case do not make a new window but rather make REFER live again and +insert it into the window tree at the position and with the sizes the +new window would have been given. Buffer, start and point positions of +REFER are set to the values they had immediately before REFER was +deleted the last time. Decorations and parameters remain unaltered from +their values before REFER was deleted. Throw an error if REFER's buffer +has been deleted after REFER itself was deleted. + +Otherwise REFER must specify a live window. In this case, the new +window will inherit properties like buffer, start and point position and +some decorations from REFER. If REFER is nil or omitted, then if WINDOW +is live, any such properties are inherited from WINDOW. If, however, +WINDOW is an internal window, the new window will inherit these +properties from the window selected on WINDOW's frame. + If the variable `ignore-window-parameters' is non-nil or the `split-window' parameter of WINDOW equals t, do not process any -parameters of WINDOW. Otherwise, if the `split-window' parameter -of WINDOW specifies a function, call that function with all three -arguments and return the value returned by that function. - -Otherwise, if WINDOW is part of an atomic window, \"split\" the -root of that atomic window. The new window does not become a -member of that atomic window. - -If WINDOW is live, properties of the new window like margins and -scrollbars are inherited from WINDOW. If WINDOW is an internal -window, these properties as well as the buffer displayed in the -new window are inherited from the window selected on WINDOW's -frame. The selected window is not changed by this function." +parameters of WINDOW. Otherwise, if the `split-window' parameter of +WINDOW specifies a function, call that function with the three first +arguments WINDOW, SIZE and SIDE and return the value returned by that +function. + +Otherwise, if WINDOW is part of an atomic window, \"split\" the root of +that atomic window. The new window does not become a member of that +atomic window. + +The selected window and the selected window on WINDOW's frame are not +changed by this function." (setq window (window-normalize-window window)) (let* ((side (cond ((not side) 'below) @@ -5598,14 +5639,14 @@ split-window ((and (window-parameter window 'window-atom) (setq atom-root (window-atom-root window)) (not (eq atom-root window))) - (throw 'done (split-window atom-root size side pixelwise))) + (throw 'done (split-window atom-root size side pixelwise refer))) ;; If WINDOW's frame has a side window and WINDOW specifies the ;; frame's root window, split the frame's main window instead ;; (Bug#73627). ((and (eq window (frame-root-window frame)) (window-with-parameter 'window-side nil frame)) (throw 'done (split-window (window-main-window frame) - size side pixelwise))) + size side pixelwise refer))) ;; If WINDOW is a side window or its first or last child is a ;; side window, throw an error unless `window-combination-resize' ;; equals 'side. @@ -5644,8 +5685,8 @@ split-window (window-combined-p window horizontal))) ;; 'old-pixel-size' is the current pixel size of WINDOW. (old-pixel-size (window-size window horizontal t)) - ;; 'new-size' is the specified or calculated size of the - ;; new window. + ;; 'new-pixel-size' is the specified or calculated size + ;; of the new window. new-pixel-size new-parent new-normal) (cond ((not pixel-size) @@ -5766,8 +5807,9 @@ split-window window (- (if new-parent 1.0 (window-normal-size window horizontal)) new-normal))) - (let* ((new (split-window-internal window new-pixel-size side new-normal))) - (window--pixel-to-total frame horizontal) + (let ((new (split-window-internal + window new-pixel-size side new-normal refer))) + (window--pixel-to-total frame horizontal) ;; Assign window-side parameters, if any. (cond ((eq window-combination-resize 'side) diff --git a/src/window.c b/src/window.c index 34968ac824f..fbba51d4b37 100644 --- a/src/window.c +++ b/src/window.c @@ -337,11 +337,11 @@ DEFUN ("window-live-p", Fwindow_live_p, Swindow_live_p, 1, 1, 0, /* Frames and windows. */ DEFUN ("window-frame", Fwindow_frame, Swindow_frame, 0, 1, 0, - doc: /* Return the frame that window WINDOW is on. -WINDOW must be a valid window and defaults to the selected one. */) + doc: /* Return the frame that specified WINDOW is on. +WINDOW can be any window and defaults to the selected one. */) (Lisp_Object window) { - return decode_valid_window (window)->frame; + return decode_any_window (window)->frame; } DEFUN ("frame-root-window", Fframe_root_window, Sframe_root_window, 0, 1, 0, @@ -640,9 +640,9 @@ DEFUN ("select-window", Fselect_window, Sselect_window, 1, 2, 0, } DEFUN ("window-buffer", Fwindow_buffer, Swindow_buffer, 0, 1, 0, - doc: /* Return the buffer displayed in window WINDOW. -If WINDOW is omitted or nil, it defaults to the selected window. -Return nil for an internal window or a deleted window. */) + doc: /* Return the buffer displayed in specified WINDOW. +WINDOW can be any window and defaults to the selected one. Return nil +for an internal or deleted window. */) (Lisp_Object window) { struct window *w = decode_any_window (window); @@ -652,15 +652,16 @@ DEFUN ("window-buffer", Fwindow_buffer, Swindow_buffer, 0, 1, 0, DEFUN ("window-old-buffer", Fwindow_old_buffer, Swindow_old_buffer, 0, 1, 0, doc: /* Return the old buffer displayed by WINDOW. -WINDOW must be a live window and defaults to the selected one. +WINDOW can be any window and defaults to the selected one. The return value is the buffer shown in WINDOW at the last time window -change functions were run. It is nil if WINDOW was created after -that. It is t if WINDOW has been restored from a window configuration -after that. */) +change functions were run or WINDOW is a former live window that was +deleted. It is nil if WINDOW was created after that. It is t if WINDOW +has been restored from a window configuration after that. It is always +nil if WINDOW is an internal window. */) (Lisp_Object window) { - struct window *w = decode_live_window (window); + struct window *w = decode_any_window (window); return (NILP (w->old_buffer) /* A new window. */ @@ -668,81 +669,100 @@ DEFUN ("window-old-buffer", Fwindow_old_buffer, Swindow_old_buffer, 0, 1, 0, : (w->change_stamp != WINDOW_XFRAME (w)->change_stamp) /* A window restored from a configuration. */ ? Qt - /* A window that was live the last time seen by window - change functions. */ + /* A window that was live the last time seen by window change + functions or was deleted. */ : w->old_buffer); } DEFUN ("window-parent", Fwindow_parent, Swindow_parent, 0, 1, 0, - doc: /* Return the parent window of window WINDOW. -WINDOW must be a valid window and defaults to the selected one. -Return nil for a window with no parent (e.g. a root window). */) + doc: /* Return the parent window of specified WINDOW. +WINDOW can be any window and defaults to the selected one. Return nil +for a window with no parent (e.g. a root window). */) + (Lisp_Object window) +{ + Lisp_Object parent = decode_any_window (window)->parent; + + return WINDOWP (parent) ? parent : Qnil; +} + +DEFUN ("window-child", Fwindow_child, Swindow_child, 0, 1, 0, + doc: /* Return the first child window of specified WINDOW. +WINDOW can be any window and defaults to the selected one. Return nil +if WINDOW has no children. */) (Lisp_Object window) { - return decode_valid_window (window)->parent; + struct window *w = decode_any_window (window); + + return WINDOWP (w->contents) ? w->contents : Qnil; } DEFUN ("window-top-child", Fwindow_top_child, Swindow_top_child, 0, 1, 0, - doc: /* Return the topmost child window of window WINDOW. -WINDOW must be a valid window and defaults to the selected one. -Return nil if WINDOW is a live window (live windows have no children). -Return nil if WINDOW is an internal window whose children form a -horizontal combination. */) + doc: /* Return the topmost child window of specified WINDOW. +WINDOW can be any window and defaults to the selected one. Return nil +if WINDOW is a live window (live windows have no children). Return nil +if WINDOW is an internal window whose children form a horizontal +combination. */) (Lisp_Object window) { - struct window *w = decode_valid_window (window); + struct window *w = decode_any_window (window); + return WINDOW_VERTICAL_COMBINATION_P (w) ? w->contents : Qnil; } DEFUN ("window-left-child", Fwindow_left_child, Swindow_left_child, 0, 1, 0, - doc: /* Return the leftmost child window of window WINDOW. -WINDOW must be a valid window and defaults to the selected one. -Return nil if WINDOW is a live window (live windows have no children). -Return nil if WINDOW is an internal window whose children form a -vertical combination. */) + doc: /* Return the leftmost child window of specified WINDOW. +WINDOW can be any window and defaults to the selected one. Return nil +if WINDOW is a live window (live windows have no children). Return nil +if WINDOW is an internal window whose children form a vertical +combination. */) (Lisp_Object window) { - struct window *w = decode_valid_window (window); + struct window *w = decode_any_window (window); + return WINDOW_HORIZONTAL_COMBINATION_P (w) ? w->contents : Qnil; } DEFUN ("window-next-sibling", Fwindow_next_sibling, Swindow_next_sibling, 0, 1, 0, - doc: /* Return the next sibling window of window WINDOW. -WINDOW must be a valid window and defaults to the selected one. + doc: /* Return the next sibling window of specified WINDOW. +WINDOW can be amy window and defaults to the selected one. Return nil if WINDOW has no next sibling. */) (Lisp_Object window) { - return decode_valid_window (window)->next; + Lisp_Object next = decode_any_window (window)->next; + + return WINDOWP (next) ? next : Qnil; } DEFUN ("window-prev-sibling", Fwindow_prev_sibling, Swindow_prev_sibling, 0, 1, 0, - doc: /* Return the previous sibling window of window WINDOW. -WINDOW must be a valid window and defaults to the selected one. + doc: /* Return the previous sibling window of specified WINDOW. +WINDOW can be amy window and defaults to the selected one. Return nil if WINDOW has no previous sibling. */) (Lisp_Object window) { - return decode_valid_window (window)->prev; + Lisp_Object prev = decode_any_window (window)->prev; + + return WINDOWP (prev) ? prev : Qnil; } DEFUN ("window-combination-limit", Fwindow_combination_limit, Swindow_combination_limit, 1, 1, 0, - doc: /* Return combination limit of window WINDOW. -WINDOW must be a valid window used in horizontal or vertical combination. -If the return value is nil, child windows of WINDOW can be recombined with -WINDOW's siblings. A return value of t means that child windows of -WINDOW are never (re-)combined with WINDOW's siblings. */) + doc: /* Return combination limit of specified WINDOW. +WINDOW can be any internal window - a window that does not show a buffer +and has not shown a buffer before. If the return value is nil, child +windows of WINDOW can be recombined with WINDOW's siblings. A return +value of t means that child windows of WINDOW are never (re-)combined +with WINDOW's siblings. */) (Lisp_Object window) { - struct window *w; + struct window *w = decode_any_window (window); - CHECK_VALID_WINDOW (window); - w = XWINDOW (window); - if (WINDOW_LEAF_P (w)) + if (WINDOW_LEAF_P (w) || !NILP (w->old_buffer)) error ("Combination limit is meaningful for internal windows only"); + return w->combination_limit; } DEFUN ("set-window-combination-limit", Fset_window_combination_limit, Sset_window_combination_limit, 2, 2, 0, - doc: /* Set combination limit of window WINDOW to LIMIT; return LIMIT. + doc: /* Set combination limit of specified WINDOW to LIMIT; return LIMIT. WINDOW must be a valid window used in horizontal or vertical combination. If LIMIT is nil, child windows of WINDOW can be recombined with WINDOW's siblings. LIMIT t means that child windows of WINDOW are never @@ -761,7 +781,7 @@ DEFUN ("set-window-combination-limit", Fset_window_combination_limit, Sset_windo } DEFUN ("window-use-time", Fwindow_use_time, Swindow_use_time, 0, 1, 0, - doc: /* Return the use time of window WINDOW. + doc: /* Return the use time of specified WINDOW. WINDOW must specify a live window and defaults to the selected one. The window with the highest use time is usually the one most recently @@ -802,7 +822,7 @@ DEFUN ("window-bump-use-time", Fwindow_bump_use_time, } DEFUN ("window-pixel-width", Fwindow_pixel_width, Swindow_pixel_width, 0, 1, 0, - doc: /* Return the width of window WINDOW in pixels. + doc: /* Return the width of specified WINDOW in pixels. WINDOW must be a valid window and defaults to the selected one. The return value includes the fringes and margins of WINDOW as well as @@ -815,10 +835,10 @@ DEFUN ("window-pixel-width", Fwindow_pixel_width, Swindow_pixel_width, 0, 1, 0, } DEFUN ("window-pixel-height", Fwindow_pixel_height, Swindow_pixel_height, 0, 1, 0, - doc: /* Return the height of window WINDOW in pixels. + doc: /* Return the height of specified WINDOW in pixels. WINDOW must be a valid window and defaults to the selected one. -The return value includes the mode line and header line and the bottom +The return value includes the mode, tab and header line and the bottom divider, if any. If WINDOW is an internal window, its pixel height is the height of the screen areas spanned by its children. */) (Lisp_Object window) @@ -855,12 +875,13 @@ DEFUN ("window-old-pixel-height", Fwindow_old_pixel_height, } DEFUN ("window-total-height", Fwindow_total_height, Swindow_total_height, 0, 2, 0, - doc: /* Return the height of window WINDOW in lines. + doc: /* Return the height of specified WINDOW in lines. WINDOW must be a valid window and defaults to the selected one. -The return value includes the heights of WINDOW's mode and header line -and its bottom divider, if any. If WINDOW is an internal window, the -total height is the height of the screen areas spanned by its children. +The return value includes the heights of WINDOW's mode, tab and header +line, horizontal scroll bar and its bottom divider, if any. If WINDOW +is an internal window, the total height is the height of the screen +areas spanned by its children. If WINDOW's pixel height is not an integral multiple of its frame's character height, the number of lines occupied by WINDOW is rounded @@ -891,11 +912,11 @@ DEFUN ("window-total-height", Fwindow_total_height, Swindow_total_height, 0, 2, } DEFUN ("window-total-width", Fwindow_total_width, Swindow_total_width, 0, 2, 0, - doc: /* Return the total width of window WINDOW in columns. + doc: /* Return the total width of specified WINDOW in columns. WINDOW must be a valid window and defaults to the selected one. -The return value includes the widths of WINDOW's fringes, margins, -scroll bars and its right divider, if any. If WINDOW is an internal +The return value includes the widths of WINDOW's fringes, margins, its +vertical scroll bar and right divider, if any. If WINDOW is an internal window, the total width is the width of the screen areas spanned by its children. @@ -928,7 +949,7 @@ DEFUN ("window-total-width", Fwindow_total_width, Swindow_total_width, 0, 2, 0, } DEFUN ("window-new-total", Fwindow_new_total, Swindow_new_total, 0, 1, 0, - doc: /* Return the new total size of window WINDOW. + doc: /* Return the new total size of specified WINDOW. WINDOW must be a valid window and defaults to the selected one. The new total size of WINDOW is the value set by the last call of @@ -941,7 +962,7 @@ width (see `window-total-width'). */) } DEFUN ("window-normal-size", Fwindow_normal_size, Swindow_normal_size, 0, 2, 0, - doc: /* Return the normal height of window WINDOW. + doc: /* Return the normal height of specified WINDOW. WINDOW must be a valid window and defaults to the selected one. If HORIZONTAL is non-nil, return the normal width of WINDOW. @@ -970,7 +991,7 @@ DEFUN ("window-normal-size", Fwindow_normal_size, Swindow_normal_size, 0, 2, 0, } DEFUN ("window-new-normal", Fwindow_new_normal, Swindow_new_normal, 0, 1, 0, - doc: /* Return new normal size of window WINDOW. + doc: /* Return new normal size of specified WINDOW. WINDOW must be a valid window and defaults to the selected one. The new normal size of WINDOW is the value set by the last call of @@ -982,7 +1003,7 @@ DEFUN ("window-new-normal", Fwindow_new_normal, Swindow_new_normal, 0, 1, 0, } DEFUN ("window-new-pixel", Fwindow_new_pixel, Swindow_new_pixel, 0, 1, 0, - doc: /* Return new pixel size of window WINDOW. + doc: /* Return new pixel size of specified WINDOW. WINDOW must be a valid window and defaults to the selected one. The new pixel size of WINDOW is the value set by the last call of @@ -995,7 +1016,7 @@ width (see `window-pixel-width'). */) } DEFUN ("window-pixel-left", Fwindow_pixel_left, Swindow_pixel_left, 0, 1, 0, - doc: /* Return left pixel edge of window WINDOW. + doc: /* Return left pixel edge of specified WINDOW. WINDOW must be a valid window and defaults to the selected one. */) (Lisp_Object window) { @@ -1003,7 +1024,7 @@ DEFUN ("window-pixel-left", Fwindow_pixel_left, Swindow_pixel_left, 0, 1, 0, } DEFUN ("window-pixel-top", Fwindow_pixel_top, Swindow_pixel_top, 0, 1, 0, - doc: /* Return top pixel edge of window WINDOW. + doc: /* Return top pixel edge of specified WINDOW. WINDOW must be a valid window and defaults to the selected one. */) (Lisp_Object window) { @@ -1011,7 +1032,7 @@ DEFUN ("window-pixel-top", Fwindow_pixel_top, Swindow_pixel_top, 0, 1, 0, } DEFUN ("window-left-column", Fwindow_left_column, Swindow_left_column, 0, 1, 0, - doc: /* Return left column of window WINDOW. + doc: /* Return left column of specified WINDOW. This is the distance, in columns, between the left edge of WINDOW and the left edge of the frame's window area. For instance, the return value is 0 if there is no window to the left of WINDOW. @@ -1023,7 +1044,7 @@ DEFUN ("window-left-column", Fwindow_left_column, Swindow_left_column, 0, 1, 0, } DEFUN ("window-top-line", Fwindow_top_line, Swindow_top_line, 0, 1, 0, - doc: /* Return top line of window WINDOW. + doc: /* Return top line of specified WINDOW. This is the distance, in lines, between the top of WINDOW and the top of the frame's window area. For instance, the return value is 0 if there is no window above WINDOW. @@ -2036,7 +2057,7 @@ DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p, DEFUN ("window-line-height", Fwindow_line_height, Swindow_line_height, 0, 2, 0, - doc: /* Return height in pixels of text line LINE in window WINDOW. + doc: /* Return height in pixels of text line LINE in specified WINDOW. WINDOW must be a live window and defaults to the selected one. Return height of current line if LINE is omitted or nil. Return height of @@ -2358,11 +2379,11 @@ DEFUN ("set-window-next-buffers", Fset_window_next_buffers, DEFUN ("window-parameters", Fwindow_parameters, Swindow_parameters, 0, 1, 0, doc: /* Return the parameters of WINDOW and their values. -WINDOW must be a valid window and defaults to the selected one. The -return value is a list of elements of the form (PARAMETER . VALUE). */) +WINDOW can be any window and defaults to the selected one. The return +value is a list of elements of the form (PARAMETER . VALUE). */) (Lisp_Object window) { - return Fcopy_alist (decode_valid_window (window)->window_parameters); + return Fcopy_alist (decode_any_window (window)->window_parameters); } Lisp_Object @@ -2556,7 +2577,7 @@ replace_window (Lisp_Object old, Lisp_Object new, bool setflag) wset_combination (XWINDOW (tem), XWINDOW (tem)->horizontal, new); } -/* If window WINDOW and its parent window are iso-combined, merge +/* If specified WINDOW and its parent window are iso-combined, merge WINDOW's children into those of its parent window and mark WINDOW as deleted. */ @@ -4491,45 +4512,6 @@ allocate_window (void) PVEC_WINDOW); } -/* Make new window, have it replace WINDOW in window-tree, and make - WINDOW its only vertical child (HORFLAG means make WINDOW its only - horizontal child). */ -static void -make_parent_window (Lisp_Object window, bool horflag) -{ - Lisp_Object parent; - register struct window *o, *p; - - o = XWINDOW (window); - p = allocate_window (); - memcpy ((char *) p + sizeof (union vectorlike_header), - (char *) o + sizeof (union vectorlike_header), - word_size * VECSIZE (struct window)); - /* P's buffer slot may change from nil to a buffer... */ - adjust_window_count (p, 1); - XSETWINDOW (parent, p); - - p->sequence_number = ++sequence_number; - - replace_window (window, parent, true); - - wset_next (o, Qnil); - wset_prev (o, Qnil); - wset_parent (o, parent); - /* ...but now P becomes an internal window. */ - wset_start (p, Qnil); - wset_pointm (p, Qnil); - wset_old_pointm (p, Qnil); - wset_buffer (p, Qnil); - wset_combination (p, horflag, window); - wset_combination_limit (p, Qnil); - /* Reset any previous and next buffers of p which have been installed - by the memcpy above. */ - wset_prev_buffers (p, Qnil); - wset_next_buffers (p, Qnil); - wset_window_parameters (p, Qnil); -} - /* Make new window from scratch. */ Lisp_Object make_window (void) @@ -5073,7 +5055,7 @@ resize_frame_windows (struct frame *f, int size, bool horflag) } -DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 4, 0, +DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, 4, 5, 0, doc: /* Split window OLD. Second argument PIXEL-SIZE specifies the number of pixels of the new window. It must be a positive integer. @@ -5088,32 +5070,33 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, the right side of WINDOW. SIDE `left' means the new window shall be located on the left of WINDOW. In both cases PIXEL-SIZE specifies the width of the new window including space reserved for fringes and the -scrollbar or a divider column. +scroll bar or a divider column. Fourth argument NORMAL-SIZE specifies the normal size of the new window -according to the SIDE argument. +according to the SIDE argument. Optional fifth argument REFER is as for +'split-window'. The new pixel and normal sizes of all involved windows must have been set correctly. See the code of `split-window' for how this is done. */) - (Lisp_Object old, Lisp_Object pixel_size, Lisp_Object side, Lisp_Object normal_size) -{ - /* OLD (*o) is the window we have to split. (*p) is either OLD's - parent window or an internal window we have to install as OLD's new - parent. REFERENCE (*r) must denote a live window, or is set to OLD - provided OLD is a leaf window, or to the frame's selected window. - NEW (*n) is the new window created with some parameters taken from - REFERENCE (*r). */ - Lisp_Object new, frame, reference; - struct window *o, *p, *n, *r, *c; - struct frame *f; + (Lisp_Object old, Lisp_Object pixel_size, Lisp_Object side, + Lisp_Object normal_size, Lisp_Object refer) +{ + /* OLD (*o) is the window to split. REFER (*r) is a reference window, + either an arbitrary live window or a former live, now deleted + window on the same frame as OLD. NEW (*n) is the new window + created anew or resurrected from REFER (*r), if specified. *p + refers either to OLD's parent window that will become NEW's parent + window too or to a new internal window that becomes OLD's and NEW's + new parent. */ + struct window *o = decode_valid_window (old); + Lisp_Object frame = WINDOW_FRAME (o); + struct frame *f = XFRAME (frame); + struct window *p, *n, *r, *c; bool horflag /* HORFLAG is true when we split side-by-side, false otherwise. */ = EQ (side, Qt) || EQ (side, Qleft) || EQ (side, Qright); - - CHECK_WINDOW (old); - o = XWINDOW (old); - frame = WINDOW_FRAME (o); - f = XFRAME (frame); + Lisp_Object new, parent = Qnil; + bool dead = false; CHECK_FIXNUM (pixel_size); EMACS_INT total_size @@ -5131,14 +5114,72 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, ? WINDOW_VERTICAL_COMBINATION_P (XWINDOW (o->parent)) : WINDOW_HORIZONTAL_COMBINATION_P (XWINDOW (o->parent)))); - /* We need a live reference window to initialize some parameters. */ - if (WINDOW_LIVE_P (old)) - /* OLD is live, use it as reference window. */ - reference = old; + /* Set up reference window. */ + if (NILP (refer)) + { + if (WINDOW_LIVE_P (old)) + /* OLD is live, use it as reference window. */ + refer = old; + else + /* Use the frame's selected window as reference window. */ + refer = FRAME_SELECTED_WINDOW (f); + + r = XWINDOW (refer); + } + else if (CONSP (refer)) + { + /* If REFER is a cons, then its car must be a deleted, former live + window and its cdr must be a deleted former parent window. Set + PARENT to the cdr of REFER and REFER to its car. WINDOW and + REFER end up as the sole children of PARENT which replaces + WINDOW in the window tree. As a special case, if REFER's cdr + is t, reuse REFER's car's old parent as new parent provided it + is a deleted fromer parent window. */ + parent = Fcdr (refer); + refer = Fcar (refer); + r = decode_any_window (refer); + + if (!NILP (r->contents) || !BUFFERP (r->old_buffer)) + error ("REFER's car must specify a deleted, former live window"); + else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer))) + error ("The buffer formerly shown by REFER's car has been killed"); + else if (!EQ (r->frame, frame)) + error ("REFER's car must specify a window on same frame as WINDOW"); + + if (EQ (parent, Qt)) + /* If REFER's cdr is t, use the old parent of REFER's car as new + parent. */ + parent = r->parent; + + p = decode_any_window (parent); + + if (!NILP (p->contents) || BUFFERP (p->old_buffer)) + error ("REFER's cdr must specify a deleted, former parent window"); + else if (!EQ (p->frame, frame)) + error ("REFER's cdr must specify window on same frame as WINDOW"); + + dead = true; + } else - /* Use the frame's selected window as reference window. */ - reference = FRAME_SELECTED_WINDOW (f); - r = XWINDOW (reference); + { + r = decode_any_window (refer); + + if (NILP (r->contents)) + /* Presumably a deleted, former live window. Check whether its + contents can be used. */ + { + if (!BUFFERP (r->old_buffer)) + error ("REFER must specify a former live window (must have shown a buffer)"); + else if (!BUFFER_LIVE_P (XBUFFER (r->old_buffer))) + error ("The buffer formerly shown by REFER has been killed"); + else if (!EQ (r->frame, frame)) + error ("REFER must specify a window on same frame as WINDOW"); + + dead = true; + } + else if (!WINDOW_LIVE_P (refer)) + error ("REFER is not a live window (does not show a buffer)"); + } /* The following bugs are caught by `split-window'. */ if (MINI_WINDOW_P (o)) @@ -5149,16 +5190,18 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, /* `window-combination-resize' non-nil means try to resize OLD's siblings proportionally. */ { - p = XWINDOW (o->parent); + struct window *op = XWINDOW (o->parent); + /* Temporarily pretend we split the parent window. */ wset_new_pixel - (p, make_fixnum ((horflag ? p->pixel_width : p->pixel_height) + (op, make_fixnum ((horflag ? op->pixel_width : op->pixel_height) - XFIXNUM (pixel_size))); - if (!window_resize_check (p, horflag)) + if (!window_resize_check (op, horflag)) error ("Window sizes don't fit"); else /* Undo the temporary pretension. */ - wset_new_pixel (p, make_fixnum (horflag ? p->pixel_width : p->pixel_height)); + wset_new_pixel + (op, make_fixnum (horflag ? op->pixel_width : op->pixel_height)); } else { @@ -5178,8 +5221,24 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, Lisp_Object new_normal = horflag ? o->normal_cols : o->normal_lines; - make_parent_window (old, horflag); - p = XWINDOW (o->parent); + if (NILP (parent)) + /* This is the crux of the old make_parent_window. */ + { + p = allocate_window (); + XSETWINDOW (parent, p); + p->sequence_number = ++sequence_number; + wset_frame (p, frame); + } + else + /* Pacify GCC. */ + p = XWINDOW (parent); + + replace_window (old, parent, true); + wset_next (o, Qnil); + wset_prev (o, Qnil); + wset_parent (o, parent); + wset_combination (p, horflag, old); + if (EQ (Vwindow_combination_limit, Qt)) /* Store t in the new parent's combination_limit slot to avoid that its children get merged into another window. */ @@ -5195,7 +5254,12 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, p = XWINDOW (o->parent); fset_redisplay (f); - new = make_window (); + + if (dead) + new = refer; + else + new = make_window (); + n = XWINDOW (new); wset_frame (n, frame); wset_parent (n, o->parent); @@ -5222,16 +5286,19 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, n->window_end_valid = false; n->last_cursor_vpos = 0; - /* Get special geometry settings from reference window. */ - n->left_margin_cols = r->left_margin_cols; - n->right_margin_cols = r->right_margin_cols; - n->left_fringe_width = r->left_fringe_width; - n->right_fringe_width = r->right_fringe_width; - n->fringes_outside_margins = r->fringes_outside_margins; - n->scroll_bar_width = r->scroll_bar_width; - n->scroll_bar_height = r->scroll_bar_height; - wset_vertical_scroll_bar_type (n, r->vertical_scroll_bar_type); - wset_horizontal_scroll_bar_type (n, r->horizontal_scroll_bar_type); + if (!dead) + { + /* Get special geometry settings from reference window. */ + n->left_margin_cols = r->left_margin_cols; + n->right_margin_cols = r->right_margin_cols; + n->left_fringe_width = r->left_fringe_width; + n->right_fringe_width = r->right_fringe_width; + n->fringes_outside_margins = r->fringes_outside_margins; + n->scroll_bar_width = r->scroll_bar_width; + n->scroll_bar_height = r->scroll_bar_height; + wset_vertical_scroll_bar_type (n, r->vertical_scroll_bar_type); + wset_horizontal_scroll_bar_type (n, r->horizontal_scroll_bar_type); + } /* Directly assign orthogonal coordinates and sizes. */ if (horflag) @@ -5260,6 +5327,7 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, sum = sum + XFIXNUM (c->new_total); c = NILP (c->next) ? 0 : XWINDOW (c->next); } + wset_new_total (n, make_fixnum ((horflag ? p->total_cols : p->total_lines) @@ -5267,10 +5335,30 @@ DEFUN ("split-window-internal", Fsplit_window_internal, Ssplit_window_internal, wset_new_normal (n, normal_size); block_input (); + + if (dead) + { + /* Get dead window back its old buffer and markers. */ + wset_buffer (n, n->old_buffer); + set_marker_restricted + (n->start, make_fixnum (XMARKER (n->start)->charpos), n->contents); + set_marker_restricted + (n->pointm, make_fixnum (XMARKER (n->pointm)->charpos), n->contents); + set_marker_restricted + (n->old_pointm, make_fixnum (XMARKER (n->old_pointm)->charpos), + n->contents); + + Vwindow_list = Qnil; + /* Remove window from the table of dead windows. */ + Fremhash (make_fixnum (n->sequence_number), + window_dead_windows_table); + } + window_resize_apply (p, horflag); adjust_frame_glyphs (f); - /* Set buffer of NEW to buffer of reference window. */ + set_window_buffer (new, r->contents, true, true); + FRAME_WINDOW_CHANGE (f) = true; unblock_input (); @@ -5368,6 +5456,8 @@ DEFUN ("delete-window-internal", Fdelete_window_internal, Sdelete_window_interna } else { + /* Store WINDOW's buffer in old_buffer. */ + wset_old_buffer (w, w->contents); unshow_buffer (w); unchain_marker (XMARKER (w->pointm)); unchain_marker (XMARKER (w->old_pointm)); @@ -7712,6 +7802,8 @@ delete_all_child_windows (Lisp_Object window) } else if (BUFFERP (w->contents)) { + /* Store WINDOW's buffer in old_buffer. */ + wset_old_buffer (w, w->contents); unshow_buffer (w); unchain_marker (XMARKER (w->pointm)); unchain_marker (XMARKER (w->old_pointm)); @@ -9064,12 +9156,9 @@ syms_of_window (void) doc: /* Hash table of dead windows. Each entry in this table maps a window number to a window object. Entries are added by `delete-window-internal' and are removed by the -garbage collector. - -This table is maintained by code in window.c and is made visible in -Elisp for testing purposes only. */); +garbage collector. */); window_dead_windows_table - = CALLN (Fmake_hash_table, QCweakness, Qt); + = CALLN (Fmake_hash_table, QCweakness, Qvalue); defsubr (&Sselected_window); defsubr (&Sold_selected_window); @@ -9089,6 +9178,7 @@ syms_of_window (void) defsubr (&Swindow_buffer); defsubr (&Swindow_old_buffer); defsubr (&Swindow_parent); + defsubr (&Swindow_child); defsubr (&Swindow_top_child); defsubr (&Swindow_left_child); defsubr (&Swindow_next_sibling);