4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #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 int layout_resize_pane_grow(struct layout_cell
*, enum layout_type
, int);
37 int layout_resize_pane_shrink(struct layout_cell
*, enum layout_type
, int);
40 layout_create_cell(struct layout_cell
*lcparent
)
42 struct layout_cell
*lc
;
44 lc
= xmalloc(sizeof *lc
);
45 lc
->type
= LAYOUT_WINDOWPANE
;
46 lc
->parent
= lcparent
;
48 TAILQ_INIT(&lc
->cells
);
62 layout_free_cell(struct layout_cell
*lc
)
64 struct layout_cell
*lcchild
;
67 case LAYOUT_LEFTRIGHT
:
68 case LAYOUT_TOPBOTTOM
:
69 while (!TAILQ_EMPTY(&lc
->cells
)) {
70 lcchild
= TAILQ_FIRST(&lc
->cells
);
71 TAILQ_REMOVE(&lc
->cells
, lcchild
, entry
);
72 layout_free_cell(lcchild
);
75 case LAYOUT_WINDOWPANE
:
77 lc
->wp
->layout_cell
= NULL
;
85 layout_print_cell(struct layout_cell
*lc
, const char *hdr
, u_int n
)
87 struct layout_cell
*lcchild
;
90 "%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr
, n
, " ", lc
,
91 lc
->type
, lc
->parent
, lc
->wp
, lc
->xoff
, lc
->yoff
, lc
->sx
, lc
->sy
);
93 case LAYOUT_LEFTRIGHT
:
94 case LAYOUT_TOPBOTTOM
:
95 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
96 layout_print_cell(lcchild
, hdr
, n
+ 1);
98 case LAYOUT_WINDOWPANE
:
105 struct layout_cell
*lc
, u_int sx
, u_int sy
, u_int xoff
, u_int yoff
)
115 layout_make_leaf(struct layout_cell
*lc
, struct window_pane
*wp
)
117 lc
->type
= LAYOUT_WINDOWPANE
;
119 TAILQ_INIT(&lc
->cells
);
121 wp
->layout_cell
= lc
;
126 layout_make_node(struct layout_cell
*lc
, enum layout_type type
)
128 if (type
== LAYOUT_WINDOWPANE
)
129 fatalx("bad layout type");
132 TAILQ_INIT(&lc
->cells
);
135 lc
->wp
->layout_cell
= NULL
;
139 /* Fix cell offsets based on their sizes. */
141 layout_fix_offsets(struct layout_cell
*lc
)
143 struct layout_cell
*lcchild
;
146 if (lc
->type
== LAYOUT_LEFTRIGHT
) {
148 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
149 lcchild
->xoff
= xoff
;
150 lcchild
->yoff
= lc
->yoff
;
151 if (lcchild
->type
!= LAYOUT_WINDOWPANE
)
152 layout_fix_offsets(lcchild
);
153 xoff
+= lcchild
->sx
+ 1;
157 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
158 lcchild
->xoff
= lc
->xoff
;
159 lcchild
->yoff
= yoff
;
160 if (lcchild
->type
!= LAYOUT_WINDOWPANE
)
161 layout_fix_offsets(lcchild
);
162 yoff
+= lcchild
->sy
+ 1;
167 /* Update pane offsets and sizes based on their cells. */
169 layout_fix_panes(struct window
*w
, u_int wsx
, u_int wsy
)
171 struct window_pane
*wp
;
172 struct layout_cell
*lc
;
175 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
176 if ((lc
= wp
->layout_cell
) == NULL
)
182 * Layout cells are limited by the smallest size of other cells
183 * within the same row or column; if this isn't the case
184 * resizing becomes difficult.
186 * However, panes do not have to take up their entire cell, so
187 * they can be cropped to the window edge if the layout
188 * overflows and they are partly visible.
190 * This stops cells being hidden unnecessarily.
194 * Work out the horizontal size. If the pane is actually
195 * outside the window or the entire pane is already visible,
198 if (lc
->xoff
>= wsx
|| lc
->xoff
+ lc
->sx
< wsx
)
207 * Similarly for the vertical size; the minimum vertical size
208 * is two because scroll regions cannot be one line.
210 if (lc
->yoff
>= wsy
|| lc
->yoff
+ lc
->sy
< wsy
)
218 window_pane_resize(wp
, sx
, sy
);
222 /* Count the number of available cells in a layout. */
224 layout_count_cells(struct layout_cell
*lc
)
226 struct layout_cell
*lcchild
;
230 case LAYOUT_WINDOWPANE
:
232 case LAYOUT_LEFTRIGHT
:
233 case LAYOUT_TOPBOTTOM
:
235 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
236 n
+= layout_count_cells(lcchild
);
239 fatalx("bad layout type");
243 /* Calculate how much size is available to be removed from a cell. */
245 layout_resize_check(struct layout_cell
*lc
, enum layout_type type
)
247 struct layout_cell
*lcchild
;
248 u_int available
, minimum
;
250 if (lc
->type
== LAYOUT_WINDOWPANE
) {
251 /* Space available in this cell only. */
252 if (type
== LAYOUT_LEFTRIGHT
)
257 if (available
> PANE_MINIMUM
)
258 available
-= PANE_MINIMUM
;
261 } else if (lc
->type
== type
) {
262 /* Same type: total of available space in all child cells. */
264 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
265 available
+= layout_resize_check(lcchild
, type
);
267 /* Different type: minimum of available space in child cells. */
269 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
270 available
= layout_resize_check(lcchild
, type
);
271 if (available
< minimum
)
281 * Adjust cell size evenly, including altering its children. This function
282 * expects the change to have already been bounded to the space available.
285 layout_resize_adjust(struct layout_cell
*lc
, enum layout_type type
, int change
)
287 struct layout_cell
*lcchild
;
289 /* Adjust the cell size. */
290 if (type
== LAYOUT_LEFTRIGHT
)
295 /* If this is a leaf cell, that is all that is necessary. */
296 if (type
== LAYOUT_WINDOWPANE
)
299 /* Child cell runs in a different direction. */
300 if (lc
->type
!= type
) {
301 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
)
302 layout_resize_adjust(lcchild
, type
, change
);
307 * Child cell runs in the same direction. Adjust each child equally
308 * until no further change is possible.
310 while (change
!= 0) {
311 TAILQ_FOREACH(lcchild
, &lc
->cells
, entry
) {
315 layout_resize_adjust(lcchild
, type
, 1);
319 if (layout_resize_check(lcchild
, type
) > 0) {
320 layout_resize_adjust(lcchild
, type
, -1);
327 /* Destroy a cell and redistribute the space. */
329 layout_destroy_cell(struct layout_cell
*lc
, struct layout_cell
**lcroot
)
331 struct layout_cell
*lcother
, *lcparent
;
334 * If no parent, this is the last pane so window close is imminent and
335 * there is no need to resize anything.
337 lcparent
= lc
->parent
;
338 if (lcparent
== NULL
) {
339 layout_free_cell(lc
);
344 /* Merge the space into the previous or next cell. */
345 if (lc
== TAILQ_FIRST(&lcparent
->cells
))
346 lcother
= TAILQ_NEXT(lc
, entry
);
348 lcother
= TAILQ_PREV(lc
, layout_cells
, entry
);
349 if (lcparent
->type
== LAYOUT_LEFTRIGHT
)
350 layout_resize_adjust(lcother
, lcparent
->type
, lc
->sx
+ 1);
352 layout_resize_adjust(lcother
, lcparent
->type
, lc
->sy
+ 1);
354 /* Remove this from the parent's list. */
355 TAILQ_REMOVE(&lcparent
->cells
, lc
, entry
);
356 layout_free_cell(lc
);
359 * If the parent now has one cell, remove the parent from the tree and
360 * replace it by that cell.
362 lc
= TAILQ_FIRST(&lcparent
->cells
);
363 if (TAILQ_NEXT(lc
, entry
) == NULL
) {
364 TAILQ_REMOVE(&lcparent
->cells
, lc
, entry
);
366 lc
->parent
= lcparent
->parent
;
367 if (lc
->parent
== NULL
) {
368 lc
->xoff
= 0; lc
->yoff
= 0;
371 TAILQ_REPLACE(&lc
->parent
->cells
, lcparent
, lc
, entry
);
373 layout_free_cell(lcparent
);
378 layout_init(struct window
*w
)
380 struct layout_cell
*lc
;
382 lc
= w
->layout_root
= layout_create_cell(NULL
);
383 layout_set_size(lc
, w
->sx
, w
->sy
, 0, 0);
384 layout_make_leaf(lc
, TAILQ_FIRST(&w
->panes
));
386 layout_fix_panes(w
, w
->sx
, w
->sy
);
390 layout_free(struct window
*w
)
392 layout_free_cell(w
->layout_root
);
395 /* Resize the entire layout after window resize. */
397 layout_resize(struct window
*w
, u_int sx
, u_int sy
)
399 struct layout_cell
*lc
= w
->layout_root
;
400 int xlimit
, ylimit
, xchange
, ychange
;
403 * Adjust horizontally. Do not attempt to reduce the layout lower than
404 * the minimum (more than the amount returned by layout_resize_check).
406 * This can mean that the window size is smaller than the total layout
407 * size: redrawing this is handled at a higher level, but it does leave
408 * a problem with growing the window size here: if the current size is
409 * < the minimum, growing proportionately by adding to each pane is
410 * wrong as it would keep the layout size larger than the window size.
411 * Instead, spread the difference between the minimum and the new size
412 * out proportionately - this should leave the layout fitting the new
415 xchange
= sx
- w
->sx
;
416 xlimit
= layout_resize_check(lc
, LAYOUT_LEFTRIGHT
);
417 if (xchange
< 0 && xchange
< -xlimit
)
420 if (sx
<= lc
->sx
) /* lc->sx is minimum possible */
423 xchange
= sx
- lc
->sx
;
426 layout_resize_adjust(lc
, LAYOUT_LEFTRIGHT
, xchange
);
428 /* Adjust vertically in a similar fashion. */
429 ychange
= sy
- w
->sy
;
430 ylimit
= layout_resize_check(lc
, LAYOUT_TOPBOTTOM
);
431 if (ychange
< 0 && ychange
< -ylimit
)
434 if (sy
<= lc
->sy
) /* lc->sy is minimum possible */
437 ychange
= sy
- lc
->sy
;
440 layout_resize_adjust(lc
, LAYOUT_TOPBOTTOM
, ychange
);
442 /* Fix cell offsets. */
443 layout_fix_offsets(lc
);
444 layout_fix_panes(w
, sx
, sy
);
447 /* Resize a single pane within the layout. */
449 layout_resize_pane(struct window_pane
*wp
, enum layout_type type
, int change
)
451 struct layout_cell
*lc
, *lcparent
;
454 lc
= wp
->layout_cell
;
456 /* Find next parent of the same type. */
457 lcparent
= lc
->parent
;
458 while (lcparent
!= NULL
&& lcparent
->type
!= type
) {
460 lcparent
= lc
->parent
;
462 if (lcparent
== NULL
)
465 /* If this is the last cell, move back one. */
466 if (lc
== TAILQ_LAST(&lcparent
->cells
, layout_cells
))
467 lc
= TAILQ_PREV(lc
, layout_cells
, entry
);
469 /* Grow or shrink the cell. */
471 while (needed
!= 0) {
473 size
= layout_resize_pane_grow(lc
, type
, needed
);
476 size
= layout_resize_pane_shrink(lc
, type
, needed
);
480 if (size
== 0) /* no more change possible */
484 /* Fix cell offsets. */
485 layout_fix_offsets(wp
->window
->layout_root
);
486 layout_fix_panes(wp
->window
, wp
->window
->sx
, wp
->window
->sy
);
487 notify_window_layout_changed(wp
->window
);
491 layout_resize_pane_mouse(struct client
*c
, struct mouse_event
*mouse
)
494 struct window_pane
*wp
;
497 w
= c
->session
->curw
->window
;
500 if ((c
->last_mouse
.b
& MOUSE_BUTTON
) != MOUSE_UP
&&
501 (c
->last_mouse
.b
& MOUSE_RESIZE_PANE
)) {
502 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
503 if (wp
->xoff
+ wp
->sx
== c
->last_mouse
.x
&&
504 wp
->yoff
<= 1 + c
->last_mouse
.y
&&
505 wp
->yoff
+ wp
->sy
>= c
->last_mouse
.y
) {
506 layout_resize_pane(wp
, LAYOUT_LEFTRIGHT
,
507 mouse
->x
- c
->last_mouse
.x
);
510 if (wp
->yoff
+ wp
->sy
== c
->last_mouse
.y
&&
511 wp
->xoff
<= 1 + c
->last_mouse
.x
&&
512 wp
->xoff
+ wp
->sx
>= c
->last_mouse
.x
) {
513 layout_resize_pane(wp
, LAYOUT_TOPBOTTOM
,
514 mouse
->y
- c
->last_mouse
.y
);
519 server_redraw_window(w
);
520 } else if (mouse
->b
!= MOUSE_UP
&&
521 mouse
->b
== (mouse
->b
& MOUSE_BUTTON
)) {
522 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
523 if ((wp
->xoff
+ wp
->sx
== mouse
->x
&&
524 wp
->yoff
<= 1 + mouse
->y
&&
525 wp
->yoff
+ wp
->sy
>= mouse
->y
) ||
526 (wp
->yoff
+ wp
->sy
== mouse
->y
&&
527 wp
->xoff
<= 1 + mouse
->x
&&
528 wp
->xoff
+ wp
->sx
>= mouse
->x
)) {
534 mouse
->b
|= MOUSE_RESIZE_PANE
;
538 layout_resize_pane_grow(
539 struct layout_cell
*lc
, enum layout_type type
, int needed
)
541 struct layout_cell
*lcadd
, *lcremove
;
544 /* Growing. Always add to the current cell. */
547 /* Look towards the tail for a suitable cell for reduction. */
548 lcremove
= TAILQ_NEXT(lc
, entry
);
549 while (lcremove
!= NULL
) {
550 size
= layout_resize_check(lcremove
, type
);
553 lcremove
= TAILQ_NEXT(lcremove
, entry
);
556 /* If none found, look towards the head. */
557 if (lcremove
== NULL
) {
558 lcremove
= TAILQ_PREV(lc
, layout_cells
, entry
);
559 while (lcremove
!= NULL
) {
560 size
= layout_resize_check(lcremove
, type
);
563 lcremove
= TAILQ_PREV(lcremove
, layout_cells
, entry
);
565 if (lcremove
== NULL
)
569 /* Change the cells. */
570 if (size
> (u_int
) needed
)
572 layout_resize_adjust(lcadd
, type
, size
);
573 layout_resize_adjust(lcremove
, type
, -size
);
578 layout_resize_pane_shrink(
579 struct layout_cell
*lc
, enum layout_type type
, int needed
)
581 struct layout_cell
*lcadd
, *lcremove
;
584 /* Shrinking. Find cell to remove from by walking towards head. */
587 size
= layout_resize_check(lcremove
, type
);
590 lcremove
= TAILQ_PREV(lcremove
, layout_cells
, entry
);
591 } while (lcremove
!= NULL
);
592 if (lcremove
== NULL
)
595 /* And add onto the next cell (from the original cell). */
596 lcadd
= TAILQ_NEXT(lc
, entry
);
600 /* Change the cells. */
601 if (size
> (u_int
) -needed
)
603 layout_resize_adjust(lcadd
, type
, size
);
604 layout_resize_adjust(lcremove
, type
, -size
);
608 /* Assign window pane to newly split cell. */
610 layout_assign_pane(struct layout_cell
*lc
, struct window_pane
*wp
)
612 layout_make_leaf(lc
, wp
);
613 layout_fix_panes(wp
->window
, wp
->window
->sx
, wp
->window
->sy
);
617 * Split a pane into two. size is a hint, or -1 for default half/half
618 * split. This must be followed by layout_assign_pane before much else happens!
622 struct window_pane
*wp
, enum layout_type type
, int size
, int insert_before
)
624 struct layout_cell
*lc
, *lcparent
, *lcnew
, *lc1
, *lc2
;
625 u_int sx
, sy
, xoff
, yoff
, size1
, size2
;
627 lc
= wp
->layout_cell
;
629 /* Copy the old cell size. */
635 /* Check there is enough space for the two new panes. */
637 case LAYOUT_LEFTRIGHT
:
638 if (sx
< PANE_MINIMUM
* 2 + 1)
641 case LAYOUT_TOPBOTTOM
:
642 if (sy
< PANE_MINIMUM
* 2 + 1)
646 fatalx("bad layout type");
649 if (lc
->parent
!= NULL
&& lc
->parent
->type
== type
) {
651 * If the parent exists and is of the same type as the split,
652 * create a new cell and insert it after this one.
655 /* Create the new child cell. */
656 lcparent
= lc
->parent
;
657 lcnew
= layout_create_cell(lcparent
);
659 TAILQ_INSERT_BEFORE(lc
, lcnew
, entry
);
661 TAILQ_INSERT_AFTER(&lcparent
->cells
, lc
, lcnew
, entry
);
664 * Otherwise create a new parent and insert it.
667 /* Create and insert the replacement parent. */
668 lcparent
= layout_create_cell(lc
->parent
);
669 layout_make_node(lcparent
, type
);
670 layout_set_size(lcparent
, sx
, sy
, xoff
, yoff
);
671 if (lc
->parent
== NULL
)
672 wp
->window
->layout_root
= lcparent
;
674 TAILQ_REPLACE(&lc
->parent
->cells
, lc
, lcparent
, entry
);
676 /* Insert the old cell. */
677 lc
->parent
= lcparent
;
678 TAILQ_INSERT_HEAD(&lcparent
->cells
, lc
, entry
);
680 /* Create the new child cell. */
681 lcnew
= layout_create_cell(lcparent
);
683 TAILQ_INSERT_HEAD(&lcparent
->cells
, lcnew
, entry
);
685 TAILQ_INSERT_TAIL(&lcparent
->cells
, lcnew
, entry
);
695 /* Set new cell sizes. size is the target size or -1 for middle split,
696 * size1 is the size of the top/left and size2 the bottom/right.
699 case LAYOUT_LEFTRIGHT
:
701 size2
= ((sx
+ 1) / 2) - 1;
704 if (size2
< PANE_MINIMUM
)
705 size2
= PANE_MINIMUM
;
706 else if (size2
> sx
- 2)
708 size1
= sx
- 1 - size2
;
709 layout_set_size(lc1
, size1
, sy
, xoff
, yoff
);
710 layout_set_size(lc2
, size2
, sy
, xoff
+ lc1
->sx
+ 1, yoff
);
712 case LAYOUT_TOPBOTTOM
:
714 size2
= ((sy
+ 1) / 2) - 1;
717 if (size2
< PANE_MINIMUM
)
718 size2
= PANE_MINIMUM
;
719 else if (size2
> sy
- 2)
721 size1
= sy
- 1 - size2
;
722 layout_set_size(lc1
, sx
, size1
, xoff
, yoff
);
723 layout_set_size(lc2
, sx
, size2
, xoff
, yoff
+ lc1
->sy
+ 1);
726 fatalx("bad layout type");
729 /* Assign the panes. */
730 layout_make_leaf(lc
, wp
);
735 /* Destroy the cell associated with a pane. */
737 layout_close_pane(struct window_pane
*wp
)
739 /* Remove the cell. */
740 layout_destroy_cell(wp
->layout_cell
, &wp
->window
->layout_root
);
742 /* Fix pane offsets and sizes. */
743 if (wp
->window
->layout_root
!= NULL
) {
744 layout_fix_offsets(wp
->window
->layout_root
);
745 layout_fix_panes(wp
->window
, wp
->window
->sx
, wp
->window
->sy
);
747 notify_window_layout_changed(wp
->window
);
750 /* Add layout to list. */
752 layout_list_add(struct window
*w
)
754 struct last_layout
*ll
, *ll_last
;
758 layout
= layout_dump(w
);
760 ll_last
= w
->layout_list_last
;
761 if (ll_last
!= NULL
&& strcmp(ll_last
->layout
, layout
) == 0) {
766 ll
= xmalloc(sizeof *ll
);
769 TAILQ_INSERT_TAIL(&w
->layout_list
, ll
, entry
);
771 TAILQ_INSERT_AFTER(&w
->layout_list
, ll_last
, ll
, entry
);
772 w
->layout_list_size
++;
773 w
->layout_list_last
= ll
;
775 limit
= options_get_number(&w
->options
, "layout-history-limit");
776 while (w
->layout_list_size
> limit
) {
777 ll
= TAILQ_LAST(&w
->layout_list
, last_layouts
);
778 if (ll
== w
->layout_list_last
)
779 ll
= TAILQ_FIRST(&w
->layout_list
);
781 TAILQ_REMOVE(&w
->layout_list
, ll
, entry
);
782 w
->layout_list_size
--;
789 /* Apply next layout from list. */
791 layout_list_redo(struct window
*w
)
793 struct last_layout
*ll
, *ll_last
;
795 ll_last
= w
->layout_list_last
;
798 ll
= TAILQ_NEXT(ll_last
, entry
);
801 w
->layout_list_last
= ll
;
805 /* Apply previous layout from list. */
807 layout_list_undo(struct window
*w
)
809 struct last_layout
*ll
, *ll_last
;
811 ll_last
= w
->layout_list_last
;
814 ll
= TAILQ_PREV(ll_last
, last_layouts
, entry
);
817 w
->layout_list_last
= ll
;