4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5 * Copyright (c) 2016 Stephen Kent <smkent@smkent.net>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <sys/types.h>
27 * The window layout is a tree of cells each of which can be one of: a
28 * left-right container for a list of cells, a top-bottom container for a list
29 * of cells, or a container for a window pane.
31 * Each window has a pointer to the root of its layout tree (containing its
32 * panes), every pane has a pointer back to the cell containing it, and each
33 * cell a pointer to its parent cell.
36 static u_int
layout_resize_check(struct window
*, struct layout_cell
*,
38 static int layout_resize_pane_grow(struct window
*, struct layout_cell
*,
39 enum layout_type
, int, int);
40 static int layout_resize_pane_shrink(struct window
*, struct layout_cell
*,
41 enum layout_type
, int);
42 static u_int
layout_new_pane_size(struct window
*, u_int
,
43 struct layout_cell
*, enum layout_type
, u_int
, u_int
,
45 static int layout_set_size_check(struct window
*, struct layout_cell
*,
46 enum layout_type
, int);
47 static void layout_resize_child_cells(struct window
*,
48 struct layout_cell
*);
51 layout_create_cell(struct layout_cell
*lcparent
)
53 struct layout_cell
*lc
;
55 lc
= xmalloc(sizeof *lc
);
56 lc
->type
= LAYOUT_WINDOWPANE
;
57 lc
->parent
= lcparent
;
59 TAILQ_INIT(&lc
->cells
);
73 layout_free_cell(struct layout_cell
*lc
)
75 struct layout_cell
*lcchild
;
78 case LAYOUT_LEFTRIGHT
:
79 case LAYOUT_TOPBOTTOM
:
80 while (!TAILQ_EMPTY(&lc
->cells
)) {
81 lcchild
= TAILQ_FIRST(&lc
->cells
);
82 TAILQ_REMOVE(&lc
->cells
, lcchild
, entry
);
83 layout_free_cell(lcchild
);
86 case LAYOUT_WINDOWPANE
:
88 lc
->wp
->layout_cell
= NULL
;
96 layout_print_cell(struct layout_cell
*lc
, const char *hdr
, u_int n
)
98 struct layout_cell
*lcchild
;
102 case LAYOUT_LEFTRIGHT
:
105 case LAYOUT_TOPBOTTOM
:
108 case LAYOUT_WINDOWPANE
:
115 log_debug("%s:%*s%p type %s [parent %p] wp=%p [%u,%u %ux%u]", hdr
, n
,
116 " ", lc
, type
, lc
->parent
, lc
->wp
, lc
->xoff
, lc
->yoff
, lc
->sx
,
119 case LAYOUT_LEFTRIGHT
:
120 case LAYOUT_TOPBOTTOM
:
121 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
122 layout_print_cell(lcchild
, hdr
, n
+ 1);
124 case LAYOUT_WINDOWPANE
:
130 layout_search_by_border(struct layout_cell
*lc
, u_int x
, u_int y
)
132 struct layout_cell
*lcchild
, *last
= NULL
;
134 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
135 if (x
>= lcchild
->xoff
&& x
< lcchild
->xoff
+ lcchild
->sx
&&
136 y
>= lcchild
->yoff
&& y
< lcchild
->yoff
+ lcchild
->sy
) {
137 /* Inside the cell - recurse. */
138 return (layout_search_by_border(lcchild
, x
, y
));
147 case LAYOUT_LEFTRIGHT
:
148 if (x
< lcchild
->xoff
&& x
>= last
->xoff
+ last
->sx
)
151 case LAYOUT_TOPBOTTOM
:
152 if (y
< lcchild
->yoff
&& y
>= last
->yoff
+ last
->sy
)
155 case LAYOUT_WINDOWPANE
:
166 layout_set_size(struct layout_cell
*lc
, u_int sx
, u_int sy
, u_int xoff
,
177 layout_make_leaf(struct layout_cell
*lc
, struct window_pane
*wp
)
179 lc
->type
= LAYOUT_WINDOWPANE
;
181 TAILQ_INIT(&lc
->cells
);
183 wp
->layout_cell
= lc
;
188 layout_make_node(struct layout_cell
*lc
, enum layout_type type
)
190 if (type
== LAYOUT_WINDOWPANE
)
191 fatalx("bad layout type");
194 TAILQ_INIT(&lc
->cells
);
197 lc
->wp
->layout_cell
= NULL
;
201 /* Fix cell offsets for a child cell. */
203 layout_fix_offsets1(struct layout_cell
*lc
)
205 struct layout_cell
*lcchild
;
208 if (lc
->type
== LAYOUT_LEFTRIGHT
) {
210 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
211 lcchild
->xoff
= xoff
;
212 lcchild
->yoff
= lc
->yoff
;
213 if (lcchild
->type
!= LAYOUT_WINDOWPANE
)
214 layout_fix_offsets1(lcchild
);
215 xoff
+= lcchild
->sx
+ 1;
219 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
220 lcchild
->xoff
= lc
->xoff
;
221 lcchild
->yoff
= yoff
;
222 if (lcchild
->type
!= LAYOUT_WINDOWPANE
)
223 layout_fix_offsets1(lcchild
);
224 yoff
+= lcchild
->sy
+ 1;
229 /* Update cell offsets based on their sizes. */
231 layout_fix_offsets(struct window
*w
)
233 struct layout_cell
*lc
= w
->layout_root
;
238 layout_fix_offsets1(lc
);
241 /* Is this a top cell? */
243 layout_cell_is_top(struct window
*w
, struct layout_cell
*lc
)
245 struct layout_cell
*next
;
247 while (lc
!= w
->layout_root
) {
249 if (next
->type
== LAYOUT_TOPBOTTOM
&&
250 lc
!= TAILQ_FIRST(&next
->cells
))
257 /* Is this a bottom cell? */
259 layout_cell_is_bottom(struct window
*w
, struct layout_cell
*lc
)
261 struct layout_cell
*next
;
263 while (lc
!= w
->layout_root
) {
265 if (next
->type
== LAYOUT_TOPBOTTOM
&&
266 lc
!= TAILQ_LAST(&next
->cells
, layout_cells
))
274 * Returns 1 if we need to add an extra line for the pane status line. This is
275 * the case for the most upper or lower panes only.
278 layout_add_horizontal_border(struct window
*w
, struct layout_cell
*lc
,
281 if (status
== PANE_STATUS_TOP
)
282 return (layout_cell_is_top(w
, lc
));
283 if (status
== PANE_STATUS_BOTTOM
)
284 return (layout_cell_is_bottom(w
, lc
));
288 /* Update pane offsets and sizes based on their cells. */
290 layout_fix_panes(struct window
*w
, struct window_pane
*skip
)
292 struct window_pane
*wp
;
293 struct layout_cell
*lc
;
294 int status
, scrollbars
, sb_pos
, sb_w
, sb_pad
;
297 status
= options_get_number(w
->options
, "pane-border-status");
298 scrollbars
= options_get_number(w
->options
, "pane-scrollbars");
299 sb_pos
= options_get_number(w
->options
, "pane-scrollbars-position");
301 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
302 if ((lc
= wp
->layout_cell
) == NULL
|| wp
== skip
)
310 if (layout_add_horizontal_border(w
, lc
, status
)) {
311 if (status
== PANE_STATUS_TOP
)
316 if (window_pane_show_scrollbar(wp
, scrollbars
)) {
317 sb_w
= wp
->scrollbar_style
.width
;
318 sb_pad
= wp
->scrollbar_style
.pad
;
323 if (sb_pos
== PANE_SCROLLBARS_LEFT
) {
324 if ((int)sx
- sb_w
< PANE_MINIMUM
) {
325 wp
->xoff
= wp
->xoff
+
326 (int)sx
- PANE_MINIMUM
;
329 sx
= sx
- sb_w
- sb_pad
;
330 wp
->xoff
= wp
->xoff
+ sb_w
+ sb_pad
;
332 } else /* sb_pos == PANE_SCROLLBARS_RIGHT */
333 if ((int)sx
- sb_w
- sb_pad
< PANE_MINIMUM
)
336 sx
= sx
- sb_w
- sb_pad
;
337 wp
->flags
|= PANE_REDRAWSCROLLBAR
;
340 window_pane_resize(wp
, sx
, sy
);
344 /* Count the number of available cells in a layout. */
346 layout_count_cells(struct layout_cell
*lc
)
348 struct layout_cell
*lcchild
;
352 case LAYOUT_WINDOWPANE
:
354 case LAYOUT_LEFTRIGHT
:
355 case LAYOUT_TOPBOTTOM
:
357 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
358 count
+= layout_count_cells(lcchild
);
361 fatalx("bad layout type");
365 /* Calculate how much size is available to be removed from a cell. */
367 layout_resize_check(struct window
*w
, struct layout_cell
*lc
,
368 enum layout_type type
)
370 struct layout_cell
*lcchild
;
371 struct style
*sb_style
= &w
->active
->scrollbar_style
;
372 u_int available
, minimum
;
373 int status
, scrollbars
;
375 status
= options_get_number(w
->options
, "pane-border-status");
376 scrollbars
= options_get_number(w
->options
, "pane-scrollbars");
378 if (lc
->type
== LAYOUT_WINDOWPANE
) {
379 /* Space available in this cell only. */
380 if (type
== LAYOUT_LEFTRIGHT
) {
383 minimum
= PANE_MINIMUM
+ sb_style
->width
+
386 minimum
= PANE_MINIMUM
;
389 if (layout_add_horizontal_border(w
, lc
, status
))
390 minimum
= PANE_MINIMUM
+ 1;
392 minimum
= PANE_MINIMUM
;
394 if (available
> minimum
)
395 available
-= minimum
;
398 } else if (lc
->type
== type
) {
399 /* Same type: total of available space in all child cells. */
401 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
402 available
+= layout_resize_check(w
, lcchild
, type
);
404 /* Different type: minimum of available space in child cells. */
406 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
407 available
= layout_resize_check(w
, lcchild
, type
);
408 if (available
< minimum
)
418 * Adjust cell size evenly, including altering its children. This function
419 * expects the change to have already been bounded to the space available.
422 layout_resize_adjust(struct window
*w
, struct layout_cell
*lc
,
423 enum layout_type type
, int change
)
425 struct layout_cell
*lcchild
;
427 /* Adjust the cell size. */
428 if (type
== LAYOUT_LEFTRIGHT
)
433 /* If this is a leaf cell, that is all that is necessary. */
434 if (type
== LAYOUT_WINDOWPANE
)
437 /* Child cell runs in a different direction. */
438 if (lc
->type
!= type
) {
439 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
440 layout_resize_adjust(w
, lcchild
, type
, change
);
445 * Child cell runs in the same direction. Adjust each child equally
446 * until no further change is possible.
448 while (change
!= 0) {
449 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
453 layout_resize_adjust(w
, lcchild
, type
, 1);
457 if (layout_resize_check(w
, lcchild
, type
) > 0) {
458 layout_resize_adjust(w
, lcchild
, type
, -1);
465 /* Destroy a cell and redistribute the space. */
467 layout_destroy_cell(struct window
*w
, struct layout_cell
*lc
,
468 struct layout_cell
**lcroot
)
470 struct layout_cell
*lcother
, *lcparent
;
473 * If no parent, this is the last pane so window close is imminent and
474 * there is no need to resize anything.
476 lcparent
= lc
->parent
;
477 if (lcparent
== NULL
) {
478 layout_free_cell(lc
);
483 /* Merge the space into the previous or next cell. */
484 if (lc
== TAILQ_FIRST(&lcparent
->cells
))
485 lcother
= TAILQ_NEXT(lc
, entry
);
487 lcother
= TAILQ_PREV(lc
, layout_cells
, entry
);
488 if (lcother
!= NULL
&& lcparent
->type
== LAYOUT_LEFTRIGHT
)
489 layout_resize_adjust(w
, lcother
, lcparent
->type
, lc
->sx
+ 1);
490 else if (lcother
!= NULL
)
491 layout_resize_adjust(w
, lcother
, lcparent
->type
, lc
->sy
+ 1);
493 /* Remove this from the parent's list. */
494 TAILQ_REMOVE(&lcparent
->cells
, lc
, entry
);
495 layout_free_cell(lc
);
498 * If the parent now has one cell, remove the parent from the tree and
499 * replace it by that cell.
501 lc
= TAILQ_FIRST(&lcparent
->cells
);
502 if (TAILQ_NEXT(lc
, entry
) == NULL
) {
503 TAILQ_REMOVE(&lcparent
->cells
, lc
, entry
);
505 lc
->parent
= lcparent
->parent
;
506 if (lc
->parent
== NULL
) {
507 lc
->xoff
= 0; lc
->yoff
= 0;
510 TAILQ_REPLACE(&lc
->parent
->cells
, lcparent
, lc
, entry
);
512 layout_free_cell(lcparent
);
517 layout_init(struct window
*w
, struct window_pane
*wp
)
519 struct layout_cell
*lc
;
521 lc
= w
->layout_root
= layout_create_cell(NULL
);
522 layout_set_size(lc
, w
->sx
, w
->sy
, 0, 0);
523 layout_make_leaf(lc
, wp
);
524 layout_fix_panes(w
, NULL
);
528 layout_free(struct window
*w
)
530 layout_free_cell(w
->layout_root
);
533 /* Resize the entire layout after window resize. */
535 layout_resize(struct window
*w
, u_int sx
, u_int sy
)
537 struct layout_cell
*lc
= w
->layout_root
;
538 int xlimit
, ylimit
, xchange
, ychange
;
541 * Adjust horizontally. Do not attempt to reduce the layout lower than
542 * the minimum (more than the amount returned by layout_resize_check).
544 * This can mean that the window size is smaller than the total layout
545 * size: redrawing this is handled at a higher level, but it does leave
546 * a problem with growing the window size here: if the current size is
547 * < the minimum, growing proportionately by adding to each pane is
548 * wrong as it would keep the layout size larger than the window size.
549 * Instead, spread the difference between the minimum and the new size
550 * out proportionately - this should leave the layout fitting the new
553 xchange
= sx
- lc
->sx
;
554 xlimit
= layout_resize_check(w
, lc
, LAYOUT_LEFTRIGHT
);
555 if (xchange
< 0 && xchange
< -xlimit
)
558 if (sx
<= lc
->sx
) /* lc->sx is minimum possible */
561 xchange
= sx
- lc
->sx
;
564 layout_resize_adjust(w
, lc
, LAYOUT_LEFTRIGHT
, xchange
);
566 /* Adjust vertically in a similar fashion. */
567 ychange
= sy
- lc
->sy
;
568 ylimit
= layout_resize_check(w
, lc
, LAYOUT_TOPBOTTOM
);
569 if (ychange
< 0 && ychange
< -ylimit
)
572 if (sy
<= lc
->sy
) /* lc->sy is minimum possible */
575 ychange
= sy
- lc
->sy
;
578 layout_resize_adjust(w
, lc
, LAYOUT_TOPBOTTOM
, ychange
);
580 /* Fix cell offsets. */
581 layout_fix_offsets(w
);
582 layout_fix_panes(w
, NULL
);
585 /* Resize a pane to an absolute size. */
587 layout_resize_pane_to(struct window_pane
*wp
, enum layout_type type
,
590 struct layout_cell
*lc
, *lcparent
;
593 lc
= wp
->layout_cell
;
595 /* Find next parent of the same type. */
596 lcparent
= lc
->parent
;
597 while (lcparent
!= NULL
&& lcparent
->type
!= type
) {
599 lcparent
= lc
->parent
;
601 if (lcparent
== NULL
)
604 /* Work out the size adjustment. */
605 if (type
== LAYOUT_LEFTRIGHT
)
609 if (lc
== TAILQ_LAST(&lcparent
->cells
, layout_cells
))
610 change
= size
- new_size
;
612 change
= new_size
- size
;
614 /* Resize the pane. */
615 layout_resize_pane(wp
, type
, change
, 1);
619 layout_resize_layout(struct window
*w
, struct layout_cell
*lc
,
620 enum layout_type type
, int change
, int opposite
)
624 /* Grow or shrink the cell. */
626 while (needed
!= 0) {
628 size
= layout_resize_pane_grow(w
, lc
, type
, needed
,
632 size
= layout_resize_pane_shrink(w
, lc
, type
, needed
);
636 if (size
== 0) /* no more change possible */
640 /* Fix cell offsets. */
641 layout_fix_offsets(w
);
642 layout_fix_panes(w
, NULL
);
643 notify_window("window-layout-changed", w
);
646 /* Resize a single pane within the layout. */
648 layout_resize_pane(struct window_pane
*wp
, enum layout_type type
, int change
,
651 struct layout_cell
*lc
, *lcparent
;
653 lc
= wp
->layout_cell
;
655 /* Find next parent of the same type. */
656 lcparent
= lc
->parent
;
657 while (lcparent
!= NULL
&& lcparent
->type
!= type
) {
659 lcparent
= lc
->parent
;
661 if (lcparent
== NULL
)
664 /* If this is the last cell, move back one. */
665 if (lc
== TAILQ_LAST(&lcparent
->cells
, layout_cells
))
666 lc
= TAILQ_PREV(lc
, layout_cells
, entry
);
668 layout_resize_layout(wp
->window
, lc
, type
, change
, opposite
);
671 /* Helper function to grow pane. */
673 layout_resize_pane_grow(struct window
*w
, struct layout_cell
*lc
,
674 enum layout_type type
, int needed
, int opposite
)
676 struct layout_cell
*lcadd
, *lcremove
;
679 /* Growing. Always add to the current cell. */
682 /* Look towards the tail for a suitable cell for reduction. */
683 lcremove
= TAILQ_NEXT(lc
, entry
);
684 while (lcremove
!= NULL
) {
685 size
= layout_resize_check(w
, lcremove
, type
);
688 lcremove
= TAILQ_NEXT(lcremove
, entry
);
691 /* If none found, look towards the head. */
692 if (opposite
&& lcremove
== NULL
) {
693 lcremove
= TAILQ_PREV(lc
, layout_cells
, entry
);
694 while (lcremove
!= NULL
) {
695 size
= layout_resize_check(w
, lcremove
, type
);
698 lcremove
= TAILQ_PREV(lcremove
, layout_cells
, entry
);
701 if (lcremove
== NULL
)
704 /* Change the cells. */
705 if (size
> (u_int
) needed
)
707 layout_resize_adjust(w
, lcadd
, type
, size
);
708 layout_resize_adjust(w
, lcremove
, type
, -size
);
712 /* Helper function to shrink pane. */
714 layout_resize_pane_shrink(struct window
*w
, struct layout_cell
*lc
,
715 enum layout_type type
, int needed
)
717 struct layout_cell
*lcadd
, *lcremove
;
720 /* Shrinking. Find cell to remove from by walking towards head. */
723 size
= layout_resize_check(w
, lcremove
, type
);
726 lcremove
= TAILQ_PREV(lcremove
, layout_cells
, entry
);
727 } while (lcremove
!= NULL
);
728 if (lcremove
== NULL
)
731 /* And add onto the next cell (from the original cell). */
732 lcadd
= TAILQ_NEXT(lc
, entry
);
736 /* Change the cells. */
737 if (size
> (u_int
) -needed
)
739 layout_resize_adjust(w
, lcadd
, type
, size
);
740 layout_resize_adjust(w
, lcremove
, type
, -size
);
744 /* Assign window pane to newly split cell. */
746 layout_assign_pane(struct layout_cell
*lc
, struct window_pane
*wp
,
749 layout_make_leaf(lc
, wp
);
751 layout_fix_panes(wp
->window
, wp
);
753 layout_fix_panes(wp
->window
, NULL
);
756 /* Calculate the new pane size for resized parent. */
758 layout_new_pane_size(struct window
*w
, u_int previous
, struct layout_cell
*lc
,
759 enum layout_type type
, u_int size
, u_int count_left
, u_int size_left
)
761 u_int new_size
, min
, max
, available
;
763 /* If this is the last cell, it can take all of the remaining size. */
767 /* How much is available in this parent? */
768 available
= layout_resize_check(w
, lc
, type
);
771 * Work out the minimum size of this cell and the new size
772 * proportionate to the previous size.
774 min
= (PANE_MINIMUM
+ 1) * (count_left
- 1);
775 if (type
== LAYOUT_LEFTRIGHT
) {
776 if (lc
->sx
- available
> min
)
777 min
= lc
->sx
- available
;
778 new_size
= (lc
->sx
* size
) / previous
;
780 if (lc
->sy
- available
> min
)
781 min
= lc
->sy
- available
;
782 new_size
= (lc
->sy
* size
) / previous
;
785 /* Check against the maximum and minimum size. */
786 max
= size_left
- min
;
789 if (new_size
< PANE_MINIMUM
)
790 new_size
= PANE_MINIMUM
;
794 /* Check if the cell and all its children can be resized to a specific size. */
796 layout_set_size_check(struct window
*w
, struct layout_cell
*lc
,
797 enum layout_type type
, int size
)
799 struct layout_cell
*lcchild
;
800 u_int new_size
, available
, previous
, count
, idx
;
802 /* Cells with no children must just be bigger than minimum. */
803 if (lc
->type
== LAYOUT_WINDOWPANE
)
804 return (size
>= PANE_MINIMUM
);
807 /* Count number of children. */
809 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
812 /* Check new size will work for each child. */
813 if (lc
->type
== type
) {
814 if (available
< (count
* 2) - 1)
817 if (type
== LAYOUT_LEFTRIGHT
)
823 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
824 new_size
= layout_new_pane_size(w
, previous
, lcchild
,
825 type
, size
, count
- idx
, available
);
826 if (idx
== count
- 1) {
827 if (new_size
> available
)
829 available
-= new_size
;
831 if (new_size
+ 1 > available
)
833 available
-= new_size
+ 1;
835 if (!layout_set_size_check(w
, lcchild
, type
, new_size
))
840 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
841 if (lcchild
->type
== LAYOUT_WINDOWPANE
)
843 if (!layout_set_size_check(w
, lcchild
, type
, size
))
851 /* Resize all child cells to fit within the current cell. */
853 layout_resize_child_cells(struct window
*w
, struct layout_cell
*lc
)
855 struct layout_cell
*lcchild
;
856 u_int previous
, available
, count
, idx
;
858 if (lc
->type
== LAYOUT_WINDOWPANE
)
861 /* What is the current size used? */
864 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
866 if (lc
->type
== LAYOUT_LEFTRIGHT
)
867 previous
+= lcchild
->sx
;
868 else if (lc
->type
== LAYOUT_TOPBOTTOM
)
869 previous
+= lcchild
->sy
;
871 previous
+= (count
- 1);
873 /* And how much is available? */
875 if (lc
->type
== LAYOUT_LEFTRIGHT
)
877 else if (lc
->type
== LAYOUT_TOPBOTTOM
)
880 /* Resize children into the new size. */
882 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
883 if (lc
->type
== LAYOUT_TOPBOTTOM
) {
884 lcchild
->sx
= lc
->sx
;
885 lcchild
->xoff
= lc
->xoff
;
887 lcchild
->sx
= layout_new_pane_size(w
, previous
, lcchild
,
888 lc
->type
, lc
->sx
, count
- idx
, available
);
889 available
-= (lcchild
->sx
+ 1);
891 if (lc
->type
== LAYOUT_LEFTRIGHT
)
892 lcchild
->sy
= lc
->sy
;
894 lcchild
->sy
= layout_new_pane_size(w
, previous
, lcchild
,
895 lc
->type
, lc
->sy
, count
- idx
, available
);
896 available
-= (lcchild
->sy
+ 1);
898 layout_resize_child_cells(w
, lcchild
);
904 * Split a pane into two. size is a hint, or -1 for default half/half
905 * split. This must be followed by layout_assign_pane before much else happens!
908 layout_split_pane(struct window_pane
*wp
, enum layout_type type
, int size
,
911 struct layout_cell
*lc
, *lcparent
, *lcnew
, *lc1
, *lc2
;
912 struct style
*sb_style
= &wp
->scrollbar_style
;
913 u_int sx
, sy
, xoff
, yoff
, size1
, size2
, minimum
;
914 u_int new_size
, saved_size
, resize_first
= 0;
915 int full_size
= (flags
& SPAWN_FULLSIZE
), status
;
919 * If full_size is specified, add a new cell at the top of the window
920 * layout. Otherwise, split the cell for the current pane.
923 lc
= wp
->window
->layout_root
;
925 lc
= wp
->layout_cell
;
926 status
= options_get_number(wp
->window
->options
, "pane-border-status");
927 scrollbars
= options_get_number(wp
->window
->options
, "pane-scrollbars");
929 /* Copy the old cell size. */
935 /* Check there is enough space for the two new panes. */
937 case LAYOUT_LEFTRIGHT
:
939 minimum
= PANE_MINIMUM
* 2 + sb_style
->width
+
942 minimum
= PANE_MINIMUM
* 2 + 1;
946 case LAYOUT_TOPBOTTOM
:
947 if (layout_add_horizontal_border(wp
->window
, lc
, status
))
948 minimum
= PANE_MINIMUM
* 2 + 2;
950 minimum
= PANE_MINIMUM
* 2 + 1;
955 fatalx("bad layout type");
959 * Calculate new cell sizes. size is the target size or -1 for middle
960 * split, size1 is the size of the top/left and size2 the bottom/right.
962 if (type
== LAYOUT_LEFTRIGHT
)
967 size2
= ((saved_size
+ 1) / 2) - 1;
968 else if (flags
& SPAWN_BEFORE
)
969 size2
= saved_size
- size
- 1;
972 if (size2
< PANE_MINIMUM
)
973 size2
= PANE_MINIMUM
;
974 else if (size2
> saved_size
- 2)
975 size2
= saved_size
- 2;
976 size1
= saved_size
- 1 - size2
;
978 /* Which size are we using? */
979 if (flags
& SPAWN_BEFORE
)
984 /* Confirm there is enough space for full size pane. */
985 if (full_size
&& !layout_set_size_check(wp
->window
, lc
, type
, new_size
))
988 if (lc
->parent
!= NULL
&& lc
->parent
->type
== type
) {
990 * If the parent exists and is of the same type as the split,
991 * create a new cell and insert it after this one.
993 lcparent
= lc
->parent
;
994 lcnew
= layout_create_cell(lcparent
);
995 if (flags
& SPAWN_BEFORE
)
996 TAILQ_INSERT_BEFORE(lc
, lcnew
, entry
);
998 TAILQ_INSERT_AFTER(&lcparent
->cells
, lc
, lcnew
, entry
);
999 } else if (full_size
&& lc
->parent
== NULL
&& lc
->type
== type
) {
1001 * If the new full size pane is the same type as the root
1002 * split, insert the new pane under the existing root cell
1003 * instead of creating a new root cell. The existing layout
1004 * must be resized before inserting the new cell.
1006 if (lc
->type
== LAYOUT_LEFTRIGHT
) {
1008 layout_resize_child_cells(wp
->window
, lc
);
1009 lc
->sx
= saved_size
;
1010 } else if (lc
->type
== LAYOUT_TOPBOTTOM
) {
1012 layout_resize_child_cells(wp
->window
, lc
);
1013 lc
->sy
= saved_size
;
1017 /* Create the new cell. */
1018 lcnew
= layout_create_cell(lc
);
1019 size
= saved_size
- 1 - new_size
;
1020 if (lc
->type
== LAYOUT_LEFTRIGHT
)
1021 layout_set_size(lcnew
, size
, sy
, 0, 0);
1022 else if (lc
->type
== LAYOUT_TOPBOTTOM
)
1023 layout_set_size(lcnew
, sx
, size
, 0, 0);
1024 if (flags
& SPAWN_BEFORE
)
1025 TAILQ_INSERT_HEAD(&lc
->cells
, lcnew
, entry
);
1027 TAILQ_INSERT_TAIL(&lc
->cells
, lcnew
, entry
);
1030 * Otherwise create a new parent and insert it.
1033 /* Create and insert the replacement parent. */
1034 lcparent
= layout_create_cell(lc
->parent
);
1035 layout_make_node(lcparent
, type
);
1036 layout_set_size(lcparent
, sx
, sy
, xoff
, yoff
);
1037 if (lc
->parent
== NULL
)
1038 wp
->window
->layout_root
= lcparent
;
1040 TAILQ_REPLACE(&lc
->parent
->cells
, lc
, lcparent
, entry
);
1042 /* Insert the old cell. */
1043 lc
->parent
= lcparent
;
1044 TAILQ_INSERT_HEAD(&lcparent
->cells
, lc
, entry
);
1046 /* Create the new child cell. */
1047 lcnew
= layout_create_cell(lcparent
);
1048 if (flags
& SPAWN_BEFORE
)
1049 TAILQ_INSERT_HEAD(&lcparent
->cells
, lcnew
, entry
);
1051 TAILQ_INSERT_TAIL(&lcparent
->cells
, lcnew
, entry
);
1053 if (flags
& SPAWN_BEFORE
) {
1062 * Set new cell sizes. size1 is the size of the top/left and size2 the
1065 if (!resize_first
&& type
== LAYOUT_LEFTRIGHT
) {
1066 layout_set_size(lc1
, size1
, sy
, xoff
, yoff
);
1067 layout_set_size(lc2
, size2
, sy
, xoff
+ lc1
->sx
+ 1, yoff
);
1068 } else if (!resize_first
&& type
== LAYOUT_TOPBOTTOM
) {
1069 layout_set_size(lc1
, sx
, size1
, xoff
, yoff
);
1070 layout_set_size(lc2
, sx
, size2
, xoff
, yoff
+ lc1
->sy
+ 1);
1074 layout_resize_child_cells(wp
->window
, lc
);
1075 layout_fix_offsets(wp
->window
);
1077 layout_make_leaf(lc
, wp
);
1082 /* Destroy the cell associated with a pane. */
1084 layout_close_pane(struct window_pane
*wp
)
1086 struct window
*w
= wp
->window
;
1088 /* Remove the cell. */
1089 layout_destroy_cell(w
, wp
->layout_cell
, &w
->layout_root
);
1091 /* Fix pane offsets and sizes. */
1092 if (w
->layout_root
!= NULL
) {
1093 layout_fix_offsets(w
);
1094 layout_fix_panes(w
, NULL
);
1096 notify_window("window-layout-changed", w
);
1100 layout_spread_cell(struct window
*w
, struct layout_cell
*parent
)
1102 struct layout_cell
*lc
;
1103 struct style
*sb_style
= &w
->active
->scrollbar_style
;
1104 u_int number
, each
, size
, this;
1105 int change
, changed
, status
, scrollbars
;
1108 TAILQ_FOREACH (lc
, &parent
->cells
, entry
)
1112 status
= options_get_number(w
->options
, "pane-border-status");
1113 scrollbars
= options_get_number(w
->options
, "pane-scrollbars");
1115 if (parent
->type
== LAYOUT_LEFTRIGHT
) {
1117 size
= parent
->sx
- sb_style
->width
+ sb_style
->pad
;
1121 else if (parent
->type
== LAYOUT_TOPBOTTOM
) {
1122 if (layout_add_horizontal_border(w
, parent
, status
))
1123 size
= parent
->sy
- 1;
1128 if (size
< number
- 1)
1130 each
= (size
- (number
- 1)) / number
;
1135 TAILQ_FOREACH (lc
, &parent
->cells
, entry
) {
1136 if (TAILQ_NEXT(lc
, entry
) == NULL
)
1137 each
= size
- ((each
+ 1) * (number
- 1));
1139 if (parent
->type
== LAYOUT_LEFTRIGHT
) {
1140 change
= each
- (int)lc
->sx
;
1141 layout_resize_adjust(w
, lc
, LAYOUT_LEFTRIGHT
, change
);
1142 } else if (parent
->type
== LAYOUT_TOPBOTTOM
) {
1143 if (layout_add_horizontal_border(w
, lc
, status
))
1147 change
= this - (int)lc
->sy
;
1148 layout_resize_adjust(w
, lc
, LAYOUT_TOPBOTTOM
, change
);
1157 layout_spread_out(struct window_pane
*wp
)
1159 struct layout_cell
*parent
;
1160 struct window
*w
= wp
->window
;
1162 parent
= wp
->layout_cell
->parent
;
1167 if (layout_spread_cell(w
, parent
)) {
1168 layout_fix_offsets(w
);
1169 layout_fix_panes(w
, NULL
);
1172 } while ((parent
= parent
->parent
) != NULL
);