4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
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 * Set window layouts - predefined methods to arrange windows. These are
28 * one-off and generate a layout tree.
31 static void layout_set_even_h(struct window
*);
32 static void layout_set_even_v(struct window
*);
33 static void layout_set_main_h(struct window
*);
34 static void layout_set_main_h_mirrored(struct window
*);
35 static void layout_set_main_v(struct window
*);
36 static void layout_set_main_v_mirrored(struct window
*);
37 static void layout_set_tiled(struct window
*);
41 void (*arrange
)(struct window
*);
43 { "even-horizontal", layout_set_even_h
},
44 { "even-vertical", layout_set_even_v
},
45 { "main-horizontal", layout_set_main_h
},
46 { "main-horizontal-mirrored", layout_set_main_h_mirrored
},
47 { "main-vertical", layout_set_main_v
},
48 { "main-vertical-mirrored", layout_set_main_v_mirrored
},
49 { "tiled", layout_set_tiled
},
53 layout_set_lookup(const char *name
)
58 for (i
= 0; i
< nitems(layout_sets
); i
++) {
59 if (strcmp(layout_sets
[i
].name
, name
) == 0)
62 for (i
= 0; i
< nitems(layout_sets
); i
++) {
63 if (strncmp(layout_sets
[i
].name
, name
, strlen(name
)) == 0) {
64 if (matched
!= -1) /* ambiguous */
74 layout_set_select(struct window
*w
, u_int layout
)
76 if (layout
> nitems(layout_sets
) - 1)
77 layout
= nitems(layout_sets
) - 1;
79 if (layout_sets
[layout
].arrange
!= NULL
)
80 layout_sets
[layout
].arrange(w
);
82 w
->lastlayout
= layout
;
87 layout_set_next(struct window
*w
)
91 if (w
->lastlayout
== -1)
94 layout
= w
->lastlayout
+ 1;
95 if (layout
> nitems(layout_sets
) - 1)
99 if (layout_sets
[layout
].arrange
!= NULL
)
100 layout_sets
[layout
].arrange(w
);
101 w
->lastlayout
= layout
;
106 layout_set_previous(struct window
*w
)
110 if (w
->lastlayout
== -1)
111 layout
= nitems(layout_sets
) - 1;
113 layout
= w
->lastlayout
;
115 layout
= nitems(layout_sets
) - 1;
120 if (layout_sets
[layout
].arrange
!= NULL
)
121 layout_sets
[layout
].arrange(w
);
122 w
->lastlayout
= layout
;
127 layout_set_even(struct window
*w
, enum layout_type type
)
129 struct window_pane
*wp
;
130 struct layout_cell
*lc
, *lcnew
;
133 layout_print_cell(w
->layout_root
, __func__
, 1);
135 /* Get number of panes. */
136 n
= window_count_panes(w
);
140 /* Free the old root and construct a new. */
142 lc
= w
->layout_root
= layout_create_cell(NULL
);
143 if (type
== LAYOUT_LEFTRIGHT
) {
144 sx
= (n
* (PANE_MINIMUM
+ 1)) - 1;
149 sy
= (n
* (PANE_MINIMUM
+ 1)) - 1;
154 layout_set_size(lc
, sx
, sy
, 0, 0);
155 layout_make_node(lc
, type
);
157 /* Build new leaf cells. */
158 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
159 lcnew
= layout_create_cell(lc
);
160 layout_make_leaf(lcnew
, wp
);
163 TAILQ_INSERT_TAIL(&lc
->cells
, lcnew
, entry
);
166 /* Spread out cells. */
167 layout_spread_cell(w
, lc
);
169 /* Fix cell offsets. */
170 layout_fix_offsets(w
);
171 layout_fix_panes(w
, NULL
);
173 layout_print_cell(w
->layout_root
, __func__
, 1);
175 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
176 notify_window("window-layout-changed", w
);
177 server_redraw_window(w
);
181 layout_set_even_h(struct window
*w
)
183 layout_set_even(w
, LAYOUT_LEFTRIGHT
);
187 layout_set_even_v(struct window
*w
)
189 layout_set_even(w
, LAYOUT_TOPBOTTOM
);
193 layout_set_main_h(struct window
*w
)
195 struct window_pane
*wp
;
196 struct layout_cell
*lc
, *lcmain
, *lcother
, *lcchild
;
197 u_int n
, mainh
, otherh
, sx
, sy
;
201 layout_print_cell(w
->layout_root
, __func__
, 1);
203 /* Get number of panes. */
204 n
= window_count_panes(w
);
207 n
--; /* take off main pane */
209 /* Find available height - take off one line for the border. */
212 /* Get the main pane height. */
213 s
= options_get_string(w
->options
, "main-pane-height");
214 mainh
= args_string_percentage(s
, 0, sy
, sy
, &cause
);
220 /* Work out the other pane height. */
221 if (mainh
+ PANE_MINIMUM
>= sy
) {
222 if (sy
<= PANE_MINIMUM
+ PANE_MINIMUM
)
223 mainh
= PANE_MINIMUM
;
225 mainh
= sy
- PANE_MINIMUM
;
226 otherh
= PANE_MINIMUM
;
228 s
= options_get_string(w
->options
, "other-pane-height");
229 otherh
= args_string_percentage(s
, 0, sy
, sy
, &cause
);
230 if (cause
!= NULL
|| otherh
== 0) {
233 } else if (otherh
> sy
|| sy
- otherh
< mainh
)
239 /* Work out what width is needed. */
240 sx
= (n
* (PANE_MINIMUM
+ 1)) - 1;
244 /* Free old tree and create a new root. */
246 lc
= w
->layout_root
= layout_create_cell(NULL
);
247 layout_set_size(lc
, sx
, mainh
+ otherh
+ 1, 0, 0);
248 layout_make_node(lc
, LAYOUT_TOPBOTTOM
);
250 /* Create the main pane. */
251 lcmain
= layout_create_cell(lc
);
252 layout_set_size(lcmain
, sx
, mainh
, 0, 0);
253 layout_make_leaf(lcmain
, TAILQ_FIRST(&w
->panes
));
254 TAILQ_INSERT_TAIL(&lc
->cells
, lcmain
, entry
);
256 /* Create the other pane. */
257 lcother
= layout_create_cell(lc
);
258 layout_set_size(lcother
, sx
, otherh
, 0, 0);
260 wp
= TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
);
261 layout_make_leaf(lcother
, wp
);
262 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
264 layout_make_node(lcother
, LAYOUT_LEFTRIGHT
);
265 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
267 /* Add the remaining panes as children. */
268 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
269 if (wp
== TAILQ_FIRST(&w
->panes
))
271 lcchild
= layout_create_cell(lcother
);
272 layout_set_size(lcchild
, PANE_MINIMUM
, otherh
, 0, 0);
273 layout_make_leaf(lcchild
, wp
);
274 TAILQ_INSERT_TAIL(&lcother
->cells
, lcchild
, entry
);
276 layout_spread_cell(w
, lcother
);
279 /* Fix cell offsets. */
280 layout_fix_offsets(w
);
281 layout_fix_panes(w
, NULL
);
283 layout_print_cell(w
->layout_root
, __func__
, 1);
285 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
286 notify_window("window-layout-changed", w
);
287 server_redraw_window(w
);
291 layout_set_main_h_mirrored(struct window
*w
)
293 struct window_pane
*wp
;
294 struct layout_cell
*lc
, *lcmain
, *lcother
, *lcchild
;
295 u_int n
, mainh
, otherh
, sx
, sy
;
299 layout_print_cell(w
->layout_root
, __func__
, 1);
301 /* Get number of panes. */
302 n
= window_count_panes(w
);
305 n
--; /* take off main pane */
307 /* Find available height - take off one line for the border. */
310 /* Get the main pane height. */
311 s
= options_get_string(w
->options
, "main-pane-height");
312 mainh
= args_string_percentage(s
, 0, sy
, sy
, &cause
);
318 /* Work out the other pane height. */
319 if (mainh
+ PANE_MINIMUM
>= sy
) {
320 if (sy
<= PANE_MINIMUM
+ PANE_MINIMUM
)
321 mainh
= PANE_MINIMUM
;
323 mainh
= sy
- PANE_MINIMUM
;
324 otherh
= PANE_MINIMUM
;
326 s
= options_get_string(w
->options
, "other-pane-height");
327 otherh
= args_string_percentage(s
, 0, sy
, sy
, &cause
);
328 if (cause
!= NULL
|| otherh
== 0) {
331 } else if (otherh
> sy
|| sy
- otherh
< mainh
)
337 /* Work out what width is needed. */
338 sx
= (n
* (PANE_MINIMUM
+ 1)) - 1;
342 /* Free old tree and create a new root. */
344 lc
= w
->layout_root
= layout_create_cell(NULL
);
345 layout_set_size(lc
, sx
, mainh
+ otherh
+ 1, 0, 0);
346 layout_make_node(lc
, LAYOUT_TOPBOTTOM
);
348 /* Create the other pane. */
349 lcother
= layout_create_cell(lc
);
350 layout_set_size(lcother
, sx
, otherh
, 0, 0);
352 wp
= TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
);
353 layout_make_leaf(lcother
, wp
);
354 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
356 layout_make_node(lcother
, LAYOUT_LEFTRIGHT
);
357 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
359 /* Add the remaining panes as children. */
360 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
361 if (wp
== TAILQ_FIRST(&w
->panes
))
363 lcchild
= layout_create_cell(lcother
);
364 layout_set_size(lcchild
, PANE_MINIMUM
, otherh
, 0, 0);
365 layout_make_leaf(lcchild
, wp
);
366 TAILQ_INSERT_TAIL(&lcother
->cells
, lcchild
, entry
);
368 layout_spread_cell(w
, lcother
);
371 /* Create the main pane. */
372 lcmain
= layout_create_cell(lc
);
373 layout_set_size(lcmain
, sx
, mainh
, 0, 0);
374 layout_make_leaf(lcmain
, TAILQ_FIRST(&w
->panes
));
375 TAILQ_INSERT_TAIL(&lc
->cells
, lcmain
, entry
);
377 /* Fix cell offsets. */
378 layout_fix_offsets(w
);
379 layout_fix_panes(w
, NULL
);
381 layout_print_cell(w
->layout_root
, __func__
, 1);
383 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
384 notify_window("window-layout-changed", w
);
385 server_redraw_window(w
);
389 layout_set_main_v(struct window
*w
)
391 struct window_pane
*wp
;
392 struct layout_cell
*lc
, *lcmain
, *lcother
, *lcchild
;
393 u_int n
, mainw
, otherw
, sx
, sy
;
397 layout_print_cell(w
->layout_root
, __func__
, 1);
399 /* Get number of panes. */
400 n
= window_count_panes(w
);
403 n
--; /* take off main pane */
405 /* Find available width - take off one line for the border. */
408 /* Get the main pane width. */
409 s
= options_get_string(w
->options
, "main-pane-width");
410 mainw
= args_string_percentage(s
, 0, sx
, sx
, &cause
);
416 /* Work out the other pane width. */
417 if (mainw
+ PANE_MINIMUM
>= sx
) {
418 if (sx
<= PANE_MINIMUM
+ PANE_MINIMUM
)
419 mainw
= PANE_MINIMUM
;
421 mainw
= sx
- PANE_MINIMUM
;
422 otherw
= PANE_MINIMUM
;
424 s
= options_get_string(w
->options
, "other-pane-width");
425 otherw
= args_string_percentage(s
, 0, sx
, sx
, &cause
);
426 if (cause
!= NULL
|| otherw
== 0) {
429 } else if (otherw
> sx
|| sx
- otherw
< mainw
)
435 /* Work out what height is needed. */
436 sy
= (n
* (PANE_MINIMUM
+ 1)) - 1;
440 /* Free old tree and create a new root. */
442 lc
= w
->layout_root
= layout_create_cell(NULL
);
443 layout_set_size(lc
, mainw
+ otherw
+ 1, sy
, 0, 0);
444 layout_make_node(lc
, LAYOUT_LEFTRIGHT
);
446 /* Create the main pane. */
447 lcmain
= layout_create_cell(lc
);
448 layout_set_size(lcmain
, mainw
, sy
, 0, 0);
449 layout_make_leaf(lcmain
, TAILQ_FIRST(&w
->panes
));
450 TAILQ_INSERT_TAIL(&lc
->cells
, lcmain
, entry
);
452 /* Create the other pane. */
453 lcother
= layout_create_cell(lc
);
454 layout_set_size(lcother
, otherw
, sy
, 0, 0);
456 wp
= TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
);
457 layout_make_leaf(lcother
, wp
);
458 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
460 layout_make_node(lcother
, LAYOUT_TOPBOTTOM
);
461 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
463 /* Add the remaining panes as children. */
464 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
465 if (wp
== TAILQ_FIRST(&w
->panes
))
467 lcchild
= layout_create_cell(lcother
);
468 layout_set_size(lcchild
, otherw
, PANE_MINIMUM
, 0, 0);
469 layout_make_leaf(lcchild
, wp
);
470 TAILQ_INSERT_TAIL(&lcother
->cells
, lcchild
, entry
);
472 layout_spread_cell(w
, lcother
);
475 /* Fix cell offsets. */
476 layout_fix_offsets(w
);
477 layout_fix_panes(w
, NULL
);
479 layout_print_cell(w
->layout_root
, __func__
, 1);
481 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
482 notify_window("window-layout-changed", w
);
483 server_redraw_window(w
);
487 layout_set_main_v_mirrored(struct window
*w
)
489 struct window_pane
*wp
;
490 struct layout_cell
*lc
, *lcmain
, *lcother
, *lcchild
;
491 u_int n
, mainw
, otherw
, sx
, sy
;
495 layout_print_cell(w
->layout_root
, __func__
, 1);
497 /* Get number of panes. */
498 n
= window_count_panes(w
);
501 n
--; /* take off main pane */
503 /* Find available width - take off one line for the border. */
506 /* Get the main pane width. */
507 s
= options_get_string(w
->options
, "main-pane-width");
508 mainw
= args_string_percentage(s
, 0, sx
, sx
, &cause
);
514 /* Work out the other pane width. */
515 if (mainw
+ PANE_MINIMUM
>= sx
) {
516 if (sx
<= PANE_MINIMUM
+ PANE_MINIMUM
)
517 mainw
= PANE_MINIMUM
;
519 mainw
= sx
- PANE_MINIMUM
;
520 otherw
= PANE_MINIMUM
;
522 s
= options_get_string(w
->options
, "other-pane-width");
523 otherw
= args_string_percentage(s
, 0, sx
, sx
, &cause
);
524 if (cause
!= NULL
|| otherw
== 0) {
527 } else if (otherw
> sx
|| sx
- otherw
< mainw
)
533 /* Work out what height is needed. */
534 sy
= (n
* (PANE_MINIMUM
+ 1)) - 1;
538 /* Free old tree and create a new root. */
540 lc
= w
->layout_root
= layout_create_cell(NULL
);
541 layout_set_size(lc
, mainw
+ otherw
+ 1, sy
, 0, 0);
542 layout_make_node(lc
, LAYOUT_LEFTRIGHT
);
544 /* Create the other pane. */
545 lcother
= layout_create_cell(lc
);
546 layout_set_size(lcother
, otherw
, sy
, 0, 0);
548 wp
= TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
);
549 layout_make_leaf(lcother
, wp
);
550 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
552 layout_make_node(lcother
, LAYOUT_TOPBOTTOM
);
553 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
555 /* Add the remaining panes as children. */
556 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
557 if (wp
== TAILQ_FIRST(&w
->panes
))
559 lcchild
= layout_create_cell(lcother
);
560 layout_set_size(lcchild
, otherw
, PANE_MINIMUM
, 0, 0);
561 layout_make_leaf(lcchild
, wp
);
562 TAILQ_INSERT_TAIL(&lcother
->cells
, lcchild
, entry
);
564 layout_spread_cell(w
, lcother
);
567 /* Create the main pane. */
568 lcmain
= layout_create_cell(lc
);
569 layout_set_size(lcmain
, mainw
, sy
, 0, 0);
570 layout_make_leaf(lcmain
, TAILQ_FIRST(&w
->panes
));
571 TAILQ_INSERT_TAIL(&lc
->cells
, lcmain
, entry
);
573 /* Fix cell offsets. */
574 layout_fix_offsets(w
);
575 layout_fix_panes(w
, NULL
);
577 layout_print_cell(w
->layout_root
, __func__
, 1);
579 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
580 notify_window("window-layout-changed", w
);
581 server_redraw_window(w
);
585 layout_set_tiled(struct window
*w
)
587 struct window_pane
*wp
;
588 struct layout_cell
*lc
, *lcrow
, *lcchild
;
589 u_int n
, width
, height
, used
, sx
, sy
;
590 u_int i
, j
, columns
, rows
;
592 layout_print_cell(w
->layout_root
, __func__
, 1);
594 /* Get number of panes. */
595 n
= window_count_panes(w
);
599 /* How many rows and columns are wanted? */
601 while (rows
* columns
< n
) {
603 if (rows
* columns
< n
)
607 /* What width and height should they be? */
608 width
= (w
->sx
- (columns
- 1)) / columns
;
609 if (width
< PANE_MINIMUM
)
610 width
= PANE_MINIMUM
;
611 height
= (w
->sy
- (rows
- 1)) / rows
;
612 if (height
< PANE_MINIMUM
)
613 height
= PANE_MINIMUM
;
615 /* Free old tree and create a new root. */
617 lc
= w
->layout_root
= layout_create_cell(NULL
);
618 sx
= ((width
+ 1) * columns
) - 1;
621 sy
= ((height
+ 1) * rows
) - 1;
624 layout_set_size(lc
, sx
, sy
, 0, 0);
625 layout_make_node(lc
, LAYOUT_TOPBOTTOM
);
627 /* Create a grid of the cells. */
628 wp
= TAILQ_FIRST(&w
->panes
);
629 for (j
= 0; j
< rows
; j
++) {
630 /* If this is the last cell, all done. */
634 /* Create the new row. */
635 lcrow
= layout_create_cell(lc
);
636 layout_set_size(lcrow
, w
->sx
, height
, 0, 0);
637 TAILQ_INSERT_TAIL(&lc
->cells
, lcrow
, entry
);
639 /* If only one column, just use the row directly. */
640 if (n
- (j
* columns
) == 1 || columns
== 1) {
641 layout_make_leaf(lcrow
, wp
);
642 wp
= TAILQ_NEXT(wp
, entry
);
646 /* Add in the columns. */
647 layout_make_node(lcrow
, LAYOUT_LEFTRIGHT
);
648 for (i
= 0; i
< columns
; i
++) {
649 /* Create and add a pane cell. */
650 lcchild
= layout_create_cell(lcrow
);
651 layout_set_size(lcchild
, width
, height
, 0, 0);
652 layout_make_leaf(lcchild
, wp
);
653 TAILQ_INSERT_TAIL(&lcrow
->cells
, lcchild
, entry
);
655 /* Move to the next cell. */
656 if ((wp
= TAILQ_NEXT(wp
, entry
)) == NULL
)
661 * Adjust the row and columns to fit the full width if
666 used
= ((i
+ 1) * (width
+ 1)) - 1;
669 lcchild
= TAILQ_LAST(&lcrow
->cells
, layout_cells
);
670 layout_resize_adjust(w
, lcchild
, LAYOUT_LEFTRIGHT
,
674 /* Adjust the last row height to fit if necessary. */
675 used
= (rows
* height
) + rows
- 1;
677 lcrow
= TAILQ_LAST(&lc
->cells
, layout_cells
);
678 layout_resize_adjust(w
, lcrow
, LAYOUT_TOPBOTTOM
,
682 /* Fix cell offsets. */
683 layout_fix_offsets(w
);
684 layout_fix_panes(w
, NULL
);
686 layout_print_cell(w
->layout_root
, __func__
, 1);
688 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
689 notify_window("window-layout-changed", w
);
690 server_redraw_window(w
);