1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
| | diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 3ff78b599de..166d264d02e 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -22,6 +22,7 @@ is displayed in 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.
@@ -1357,7 +1358,7 @@ the sense that these functions may fail to split them as described here.
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
@@ -1366,7 +1367,7 @@ size. The space is taken up by the new window, which is returned.
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).
@@ -1376,13 +1377,13 @@ If @var{size} is @code{nil}, this function obeys the variables
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
@@ -1401,14 +1402,62 @@ the right of @var{window}. If @var{side} is @code{left}, the new
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
@@ -2050,6 +2099,157 @@ distribute its space proportionally among the two remaining live
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
@@ -7135,13 +7335,16 @@ live window as single, optional argument, defaulting to the selected
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/etc/NEWS b/etc/NEWS
index 85213cbaa6f..45b7d56a4cd 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -149,6 +149,17 @@ the "*Completions*" buffer is hidden.
** Windows
++++
+*** New functions to modify window layout.
+Several new functions to modify the window layout have been added:
+- rotate-window-layout-anticlockwise
+- rotate-window-layout-clockwise
+- flip-window-layout-horizontally
+- flip-window-layout-vertically
+- transpose-window-layout
+- rotate-windows-back
+- rotate-windows
+
+++
*** New hook 'window-deletable-functions'.
This abnormal hook gives its client a way to save a window from getting
diff --git a/lisp/window-x.el b/lisp/window-x.el
new file mode 100644
index 00000000000..f46d230b26b
--- /dev/null
+++ b/lisp/window-x.el
@@ -0,0 +1,336 @@
+;;; window-x.el --- extended window commands -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; Author: Pranshu Sharma <pranshu@bauherren.ovh>
+;; Martin Rudalics <rudalics@gmx.at>
+;; Maintainer: emacs-devel@gnu.org
+;; Keywords: files
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file defines additional infrequently used window commands that
+;; should not be in window.el to not make the dumped image bigger.
+
+;;; Code:
+
+(defun window-tree-normal-sizes (window &optional next)
+ "Return normal sizes of all windows rooted at WINDOW.
+A list of the form (SPLIT-TYPE PARENT-WIN PARENT-WIN-HEIGHT
+PARENT-WIN-WIDTH W1 W2 ...) is returned. SPLIT-TYPE is non-nil if
+PARENT-WIN is split horizontally. PARENT-WIN is the internal window.
+PARENT-WIN-HEIGHT and PARENT-WIN-WIDTH are the normal heights of
+PARENT-WIN. Wn is a list of the form (WINDOW HEIGHT WIDTH) where HEIGHT
+and WIDTH are the normal height and width of the window."
+ (let (list)
+ (while window
+ (setq list
+ (cons
+ (cond
+ ((window-top-child window)
+ (append
+ (list t window
+ (window-normal-size window nil)
+ (window-normal-size window t))
+ (window-tree-normal-sizes (window-top-child window) t)))
+ ((window-left-child window)
+ (append
+ (list nil window
+ (window-normal-size window nil)
+ (window-normal-size window t))
+ (window-tree-normal-sizes (window-left-child window) t)))
+ (t (list window
+ (window-normal-size window nil)
+ (window-normal-size window t))))
+ list))
+ (setq window (when next (window-next-sibling window))))
+ (nreverse list)))
+
+(defun window--window-to-transpose (frame-or-window)
+ "Return the window to be acted upon by `window--transpose'.
+If FRAME-OR-WINDOW is a window return FRAME-OR-WINDOW. If
+FRAME-OR-WINDOW is a frame, return FRAME-OR-WINDOW's main window. If
+FRAME-OR-WINDOW is nil, than the frames main window wil be returned. If
+FRAME-OR-WINDOW is non-nil, and not a frame or a window or a number,
+than the return value will be the parent window of the selected window."
+ (cond
+ ((windowp frame-or-window)
+ frame-or-window)
+ ((or (framep frame-or-window) (not frame-or-window))
+ (window-main-window frame-or-window))
+ (frame-or-window
+ (window-parent))))
+
+(defun rotate-window-layout-anticlockwise (&optional frame-or-window)
+ "Rotate windows of FRAME-OR-WINDOW anticlockwise by 90 degrees.
+Transform the layout of windows such that a window on top becomes a
+window on the right, a window on the right moves to the bottom, a window
+on the bottom moves to the left and a window on the left becomes one on
+the top.
+
+If FRAME-OR-WINDOW is nil, rotate the main window of the selected
+frame. If FRAME-OR-WINDOW specifies a live frame, rotate the main
+window of that frame. If FRAME-OR-WINDOW specifies a parent window,
+rotate that window. In any other case and interactively with a prefix
+argument rotate the parent window of the selected window."
+ (interactive "P")
+ (let ((window (window--window-to-transpose frame-or-window)))
+ (window--transpose window '(right . above) nil)))
+
+(defun rotate-window-layout-clockwise (&optional frame-or-window)
+ "Rotate windows of FRAME-OR-WINDOW clockwise by 90 degrees.
+Transform the layout of windows such that a window on top becomes a
+window on the right, a window on the right moves to the bottom, a
+window on the bottom moves to the left and a window on the left becomes
+one on the top.
+
+If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame.
+If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
+that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that
+window. In any other case and interactively with a prefix argument
+rotate the parent window of the selected window."
+ (interactive "P")
+ (let ((window (window--window-to-transpose frame-or-window)))
+ (window--transpose window '(left . below) nil)))
+
+(defun flip-window-layout-horizontally (&optional frame-or-window)
+ "Horizontally flip windows of FRAME-OR-WINDOW.
+Flip the window layout so that the window on the right becomes the
+window on the left, and vice-versa.
+
+If FRAME-OR-WINDOW is nil, flip the main window of the selected frame.
+If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
+that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that
+window. In any other case and interactively with a prefix argument
+rotate the parent window of the selected window."
+ (interactive "P")
+ (let ((window (window--window-to-transpose frame-or-window)))
+ (window--transpose window '(below . left) t)))
+
+(defun flip-window-layout-vertically (&optional frame-or-window)
+ "Verticlly flip windows of FRAME-OR-WINDOW.
+Flip the window layout so that the top window becomes the bottom window
+and vice-versa.
+
+If FRAME-OR-WINDOW is nil, flip the main window of the selected frame.
+If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
+that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that
+window. In any other case and interactively with a prefix argument
+rotate the parent window of the selected window."
+ (interactive "P")
+ (let ((window (window--window-to-transpose frame-or-window)))
+ (window--transpose window '(above . right) t)))
+
+(defun transpose-window-layout (&optional frame-or-window)
+ "Transpose windows of FRAME-OR-WINDOW.
+Make the windows on FRAME-OR-WINDOW so that every horizontal split
+becomes a vertical split, and vice versa. This is equivalent to
+diagonally flipping.
+
+If FRAME-OR-WINDOW is nil, transpose the main window of the selected frame.
+If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
+that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that
+window. In any other case and interactively with a prefix argument
+rotate the parent window of the selected window."
+ (interactive "P")
+ (let ((window (window--window-to-transpose frame-or-window)))
+ (window--transpose window '(right . below) nil)))
+
+(defun window--depmap(fun ls)
+ "Map FUN across all nodes of list LS."
+ (if (consp ls)
+ (cons
+ (if (consp (car ls))
+ (window--depmap fun (car ls))
+ (funcall fun (car ls)))
+ (window--depmap fun (cdr ls)))
+ (funcall fun ls)))
+
+(defun rotate-windows-back(&optional frame-or-window)
+ "Move windows into locations of their predecessors in cyclic ordering.
+
+If FRAME-OR-WINDOW is nil, rotate the main window of the selected frame.
+If FRAME-OR-WINDOW specifies a live frame, rotate the main window of
+that frame. If FRAME-OR-WINDOW specifies a parent window, rotate that
+window. In any other case and interactively with a prefix argument
+rotate the parent window of the selected window."
+ (interactive "P")
+ (rotate-window-layout frame-or-window t))
+
+(defun rotate-windows (&optional frame-or-window)
+ "Move windows into locations of their forerunners in cyclic ordering.
+
+Else if FRAME-OR-WINDOW is nil, rotate the main window of the
+selected frame. If FRAME-OR-WINDOW specifies a live frame, rotate the
+main window of that frame. If FRAME-OR-WINDOW specifies a parent
+window, rotate that window. In any other case and interactively with a
+prefix argument rotate the parent window of the selected window."
+ (interactive "P")
+ (let ((window (window--window-to-transpose frame-or-window)))
+ (if (or (not window)
+ (window-live-p window))
+ (message "No windows to transpose")
+ (let* ((frame (window-frame window))
+ (selected-window (frame-selected-window window))
+ (win-tree (car (window-tree-normal-sizes window)))
+ (winls (seq-filter 'window-live-p (flatten-list win-tree)))
+ (rotated-ls (if reverse
+ (append (cdr winls) (list (car winls)))
+ (append (last winls) winls)))
+ (other-window-arg (if reverse 1 -1))
+ (first-window (car rotated-ls))
+ (new-win-tree (window--depmap
+ (lambda (x)
+ (if (window-live-p x)
+ (pop rotated-ls)
+ x))
+ win-tree)))
+ (if (or (seq-some 'window-atom-root winls)
+ (seq-some 'window-fixed-size-p winls))
+ (message "This does not work with fixed size or atom windows.")
+ (progn
+ ;; All child windows need to be recursively deleted.
+ (delete-other-windows-internal first-window window)
+ ;; (delete-dups atom-windows)
+ (window--transpose-1 new-win-tree first-window '(below . right) t nil)
+ (set-frame-selected-window frame selected-window)
+ (other-window other-window-arg)
+ (while (not (memq (selected-window) winls))
+ (other-window other-window-arg))))))))
+
+(defun window--transpose (window conf no-resize)
+ "Rearrange windows of WINDOW recursively.
+CONF should be a cons cell: (HORIZONTAL-SPLIT . VERTICAL-SPLIT) where
+HORIZONTAL-SPLIT will be used as the third argument of `split-window'
+when splitting a window that was previously horizontally split, and
+VERTICAL-SPLIT as third argument of `split-window' for a window that was
+previously vertically split. If NO-RESIZE is nil, the SIDE argument of
+the window-split is converted from vertical to horizontal or vice versa,
+with the same proportion of the total split."
+ (if (or (not window)
+ (window-live-p window))
+ (message "No windows to transpose")
+ (let* ((frame (window-frame window))
+ (first-window window)
+ (selected-window (frame-selected-window window))
+ (win-tree (car (window-tree-normal-sizes window)))
+ (win-list (seq-filter 'window-live-p (flatten-list win-tree)))
+ (atom-windows
+ (remq nil (mapcar 'window-atom-root
+ win-list))))
+ (if (and (not (eq (car atom-windows) window))
+ (or no-resize
+ (and (not atom-windows)
+ (not (seq-some 'window-fixed-size-p win-list)))))
+ (progn
+ (delete-dups atom-windows)
+ (while (not (window-live-p first-window))
+ (setq first-window (window-child first-window)))
+ (delete-other-windows-internal first-window window)
+ (window--transpose-1 win-tree first-window conf no-resize atom-windows)
+ ;; Go back to previously selected window.
+ (set-frame-selected-window frame selected-window)
+ (mapc 'window-make-atom atom-windows))
+ (message "This does not work with fixed size or atom windows.")))))
+
+(defun window--transpose-1 (subtree cwin conf no-resize atom-windows)
+ "Subroutine of `window--transpose'.
+SUBTREE must be in the format of the result of
+`window-tree-normal-sizes'. CWIN is the current window through which
+the window splits are made. ATOM-WINDOWS is a list of internal atom
+windows. The CONF and NO-RESIZE arguments are the same as the
+ones in `window--transpose'."
+ ;; `flen' is max size the window could be converted to the opposite
+ ;; of the given split type.
+ (let ((parent-window-is-set t)
+ (flen (if (funcall (if no-resize 'not 'identity)
+ (car subtree))
+ (float (window-pixel-width cwin))
+ (float (window-pixel-height cwin)))))
+ (mapc
+ (pcase-lambda (`(,window . ,size))
+ (prog1
+ (let* ((split-size (- (round (* flen size))))
+ (split-type
+ (funcall (if (car subtree) 'car 'cdr) conf))
+ (return-win
+ (if (listp window)
+ ;; `window' is a window subtree.
+ ;; `first-child' is a live window that is an descended of window
+ (let* ((first-child window)
+ ;; If the window being split is atomic
+ (is-atom
+ ;; cadr will return the internal parent window
+ (memq (cadr first-child) atom-windows)))
+ ;; (caar (cddddr first-child)) is the first window in the
+ ;; list if there is a live window.
+ (while (not (windowp (caar (cddddr first-child))))
+ (setq first-child (car (cddddr first-child))))
+ (window--transpose-1
+ window
+ (let ((window-combination-limit parent-window-is-set))
+ (split-window
+ cwin
+ split-size
+ split-type
+ t
+ (if window-combination-limit
+ (cons (caar (cddddr first-child)) (cadr subtree))
+ (caar (cddddr first-child)))))
+ (if is-atom
+ '(nil . t)
+ conf)
+ no-resize
+ atom-windows))
+ ;; `window' is a window.
+ (split-window
+ cwin
+ split-size
+ split-type t
+ ;; We need to set parent window if it hasn't been set
+ ;; already.
+ (if parent-window-is-set
+ (cons window (cadr subtree))
+ window)))))
+ (when (eq window-combination-limit t)
+ (set-window-combination-limit (cadr subtree) nil))
+ return-win)
+ (setq parent-window-is-set nil)))
+ (mapcar
+ (lambda (e)
+ (pcase-let* ((`(,window . ,window-size-info)
+ (if (windowp (car e))
+ (cons (car e) e)
+ (cons e (cdr e)))))
+ (cons window
+ ;; The respective size of the window.
+ (if (car subtree)
+ (cadr window-size-info)
+ (caddr window-size-info)))))
+ ;; We need to ingore first 5 elements of window list, we ignore
+ ;; window split type, sizes and the first window (it's
+ ;; implicitly created). We just have a list of windows.
+ (nreverse (cdr (cddddr subtree)))))
+ ;; (caar (cddddr subtree)) is the first child window of subtree.
+ (unless (windowp (caar (cddddr subtree)))
+ (let ((is-atom (memq (cadr (cadr (cddddr subtree))) atom-windows)))
+ (window--transpose-1 (car (cddddr subtree)) cwin (if is-atom '(nil . t) conf)
+ no-resize atom-windows)))))
+
+;;; window-x.el ends here
diff --git a/lisp/window.el b/lisp/window.el
index cd19fd73849..5318ba299a3 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5511,54 +5511,95 @@ PARENT divided by their number plus 1."
(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 @@ frame. The selected window is not changed by this function."
((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 @@ frame. The selected window is not changed by this function."
(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 @@ frame. The selected window is not changed by this function."
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 7f157911685..d533904a550 100644
--- a/src/window.c
+++ b/src/window.c
@@ -652,15 +652,16 @@ Return nil for an internal window or a deleted window. */)
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,8 +669,8 @@ after that. */)
: (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);
}
@@ -4491,45 +4492,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 +5035,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 +5050,33 @@ 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 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 +5094,74 @@ set correctly. See the code of `split-window' for how this is done. */)
? 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 (!NILP (parent))
+ error ("If REFER is a cons, its car must not specify a live window");
+ 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 +5172,18 @@ set correctly. See the code of `split-window' for how this is done. */)
/* `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 +5203,24 @@ set correctly. See the code of `split-window' for how this is done. */)
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 +5236,12 @@ set correctly. See the code of `split-window' for how this is done. */)
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 +5268,19 @@ set correctly. See the code of `split-window' for how this is done. */)
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 +5309,7 @@ set correctly. See the code of `split-window' for how this is done. */)
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 +5317,30 @@ set correctly. See the code of `split-window' for how this is done. */)
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 +5438,8 @@ Signal an error when WINDOW is the only window on its frame. */)
}
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 +7784,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 +9138,9 @@ displayed after a scrolling operation to be somewhat inaccurate. */);
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);
|