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_v(struct window
*);
35 static void layout_set_tiled(struct window
*);
39 void (*arrange
)(struct window
*);
41 { "even-horizontal", layout_set_even_h
},
42 { "even-vertical", layout_set_even_v
},
43 { "main-horizontal", layout_set_main_h
},
44 { "main-vertical", layout_set_main_v
},
45 { "tiled", layout_set_tiled
},
49 layout_set_lookup(const char *name
)
54 for (i
= 0; i
< nitems(layout_sets
); i
++) {
55 if (strncmp(layout_sets
[i
].name
, name
, strlen(name
)) == 0) {
56 if (matched
!= -1) /* ambiguous */
66 layout_set_select(struct window
*w
, u_int layout
)
68 if (layout
> nitems(layout_sets
) - 1)
69 layout
= nitems(layout_sets
) - 1;
71 if (layout_sets
[layout
].arrange
!= NULL
)
72 layout_sets
[layout
].arrange(w
);
74 w
->lastlayout
= layout
;
79 layout_set_next(struct window
*w
)
83 if (w
->lastlayout
== -1)
86 layout
= w
->lastlayout
+ 1;
87 if (layout
> nitems(layout_sets
) - 1)
91 if (layout_sets
[layout
].arrange
!= NULL
)
92 layout_sets
[layout
].arrange(w
);
93 w
->lastlayout
= layout
;
98 layout_set_previous(struct window
*w
)
102 if (w
->lastlayout
== -1)
103 layout
= nitems(layout_sets
) - 1;
105 layout
= w
->lastlayout
;
107 layout
= nitems(layout_sets
) - 1;
112 if (layout_sets
[layout
].arrange
!= NULL
)
113 layout_sets
[layout
].arrange(w
);
114 w
->lastlayout
= layout
;
119 layout_set_even(struct window
*w
, enum layout_type type
)
121 struct window_pane
*wp
;
122 struct layout_cell
*lc
, *lcnew
;
125 layout_print_cell(w
->layout_root
, __func__
, 1);
127 /* Get number of panes. */
128 n
= window_count_panes(w
);
132 /* Free the old root and construct a new. */
134 lc
= w
->layout_root
= layout_create_cell(NULL
);
135 if (type
== LAYOUT_LEFTRIGHT
) {
136 sx
= (n
* (PANE_MINIMUM
+ 1)) - 1;
141 sy
= (n
* (PANE_MINIMUM
+ 1)) - 1;
146 layout_set_size(lc
, sx
, sy
, 0, 0);
147 layout_make_node(lc
, type
);
149 /* Build new leaf cells. */
150 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
151 lcnew
= layout_create_cell(lc
);
152 layout_make_leaf(lcnew
, wp
);
155 TAILQ_INSERT_TAIL(&lc
->cells
, lcnew
, entry
);
158 /* Spread out cells. */
159 layout_spread_cell(w
, lc
);
161 /* Fix cell offsets. */
162 layout_fix_offsets(w
);
163 layout_fix_panes(w
, NULL
);
165 layout_print_cell(w
->layout_root
, __func__
, 1);
167 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
168 notify_window("window-layout-changed", w
);
169 server_redraw_window(w
);
173 layout_set_even_h(struct window
*w
)
175 layout_set_even(w
, LAYOUT_LEFTRIGHT
);
179 layout_set_even_v(struct window
*w
)
181 layout_set_even(w
, LAYOUT_TOPBOTTOM
);
185 layout_set_main_h(struct window
*w
)
187 struct window_pane
*wp
;
188 struct layout_cell
*lc
, *lcmain
, *lcother
, *lcchild
;
189 u_int n
, mainh
, otherh
, sx
, sy
;
193 layout_print_cell(w
->layout_root
, __func__
, 1);
195 /* Get number of panes. */
196 n
= window_count_panes(w
);
199 n
--; /* take off main pane */
201 /* Find available height - take off one line for the border. */
204 /* Get the main pane height. */
205 s
= options_get_string(w
->options
, "main-pane-height");
206 mainh
= args_string_percentage(s
, 0, sy
, sy
, &cause
);
212 /* Work out the other pane height. */
213 if (mainh
+ PANE_MINIMUM
>= sy
) {
214 if (sy
<= PANE_MINIMUM
+ PANE_MINIMUM
)
215 mainh
= PANE_MINIMUM
;
217 mainh
= sy
- PANE_MINIMUM
;
218 otherh
= PANE_MINIMUM
;
220 s
= options_get_string(w
->options
, "other-pane-height");
221 otherh
= args_string_percentage(s
, 0, sy
, sy
, &cause
);
222 if (cause
!= NULL
|| otherh
== 0) {
225 } else if (otherh
> sy
|| sy
- otherh
< mainh
)
231 /* Work out what width is needed. */
232 sx
= (n
* (PANE_MINIMUM
+ 1)) - 1;
236 /* Free old tree and create a new root. */
238 lc
= w
->layout_root
= layout_create_cell(NULL
);
239 layout_set_size(lc
, sx
, mainh
+ otherh
+ 1, 0, 0);
240 layout_make_node(lc
, LAYOUT_TOPBOTTOM
);
242 /* Create the main pane. */
243 lcmain
= layout_create_cell(lc
);
244 layout_set_size(lcmain
, sx
, mainh
, 0, 0);
245 layout_make_leaf(lcmain
, TAILQ_FIRST(&w
->panes
));
246 TAILQ_INSERT_TAIL(&lc
->cells
, lcmain
, entry
);
248 /* Create the other pane. */
249 lcother
= layout_create_cell(lc
);
250 layout_set_size(lcother
, sx
, otherh
, 0, 0);
252 wp
= TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
);
253 layout_make_leaf(lcother
, wp
);
254 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
256 layout_make_node(lcother
, LAYOUT_LEFTRIGHT
);
257 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
259 /* Add the remaining panes as children. */
260 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
261 if (wp
== TAILQ_FIRST(&w
->panes
))
263 lcchild
= layout_create_cell(lcother
);
264 layout_set_size(lcchild
, PANE_MINIMUM
, otherh
, 0, 0);
265 layout_make_leaf(lcchild
, wp
);
266 TAILQ_INSERT_TAIL(&lcother
->cells
, lcchild
, entry
);
268 layout_spread_cell(w
, lcother
);
271 /* Fix cell offsets. */
272 layout_fix_offsets(w
);
273 layout_fix_panes(w
, NULL
);
275 layout_print_cell(w
->layout_root
, __func__
, 1);
277 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
278 notify_window("window-layout-changed", w
);
279 server_redraw_window(w
);
283 layout_set_main_v(struct window
*w
)
285 struct window_pane
*wp
;
286 struct layout_cell
*lc
, *lcmain
, *lcother
, *lcchild
;
287 u_int n
, mainw
, otherw
, sx
, sy
;
291 layout_print_cell(w
->layout_root
, __func__
, 1);
293 /* Get number of panes. */
294 n
= window_count_panes(w
);
297 n
--; /* take off main pane */
299 /* Find available width - take off one line for the border. */
302 /* Get the main pane width. */
303 s
= options_get_string(w
->options
, "main-pane-width");
304 mainw
= args_string_percentage(s
, 0, sx
, sx
, &cause
);
310 /* Work out the other pane width. */
311 if (mainw
+ PANE_MINIMUM
>= sx
) {
312 if (sx
<= PANE_MINIMUM
+ PANE_MINIMUM
)
313 mainw
= PANE_MINIMUM
;
315 mainw
= sx
- PANE_MINIMUM
;
316 otherw
= PANE_MINIMUM
;
318 s
= options_get_string(w
->options
, "other-pane-width");
319 otherw
= args_string_percentage(s
, 0, sx
, sx
, &cause
);
320 if (cause
!= NULL
|| otherw
== 0) {
323 } else if (otherw
> sx
|| sx
- otherw
< mainw
)
329 /* Work out what height is needed. */
330 sy
= (n
* (PANE_MINIMUM
+ 1)) - 1;
334 /* Free old tree and create a new root. */
336 lc
= w
->layout_root
= layout_create_cell(NULL
);
337 layout_set_size(lc
, mainw
+ otherw
+ 1, sy
, 0, 0);
338 layout_make_node(lc
, LAYOUT_LEFTRIGHT
);
340 /* Create the main pane. */
341 lcmain
= layout_create_cell(lc
);
342 layout_set_size(lcmain
, mainw
, sy
, 0, 0);
343 layout_make_leaf(lcmain
, TAILQ_FIRST(&w
->panes
));
344 TAILQ_INSERT_TAIL(&lc
->cells
, lcmain
, entry
);
346 /* Create the other pane. */
347 lcother
= layout_create_cell(lc
);
348 layout_set_size(lcother
, otherw
, sy
, 0, 0);
350 wp
= TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
);
351 layout_make_leaf(lcother
, wp
);
352 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
354 layout_make_node(lcother
, LAYOUT_TOPBOTTOM
);
355 TAILQ_INSERT_TAIL(&lc
->cells
, lcother
, entry
);
357 /* Add the remaining panes as children. */
358 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
359 if (wp
== TAILQ_FIRST(&w
->panes
))
361 lcchild
= layout_create_cell(lcother
);
362 layout_set_size(lcchild
, otherw
, PANE_MINIMUM
, 0, 0);
363 layout_make_leaf(lcchild
, wp
);
364 TAILQ_INSERT_TAIL(&lcother
->cells
, lcchild
, entry
);
366 layout_spread_cell(w
, lcother
);
369 /* Fix cell offsets. */
370 layout_fix_offsets(w
);
371 layout_fix_panes(w
, NULL
);
373 layout_print_cell(w
->layout_root
, __func__
, 1);
375 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
376 notify_window("window-layout-changed", w
);
377 server_redraw_window(w
);
381 layout_set_tiled(struct window
*w
)
383 struct window_pane
*wp
;
384 struct layout_cell
*lc
, *lcrow
, *lcchild
;
385 u_int n
, width
, height
, used
, sx
, sy
;
386 u_int i
, j
, columns
, rows
;
388 layout_print_cell(w
->layout_root
, __func__
, 1);
390 /* Get number of panes. */
391 n
= window_count_panes(w
);
395 /* How many rows and columns are wanted? */
397 while (rows
* columns
< n
) {
399 if (rows
* columns
< n
)
403 /* What width and height should they be? */
404 width
= (w
->sx
- (columns
- 1)) / columns
;
405 if (width
< PANE_MINIMUM
)
406 width
= PANE_MINIMUM
;
407 height
= (w
->sy
- (rows
- 1)) / rows
;
408 if (height
< PANE_MINIMUM
)
409 height
= PANE_MINIMUM
;
411 /* Free old tree and create a new root. */
413 lc
= w
->layout_root
= layout_create_cell(NULL
);
414 sx
= ((width
+ 1) * columns
) - 1;
417 sy
= ((height
+ 1) * rows
) - 1;
420 layout_set_size(lc
, sx
, sy
, 0, 0);
421 layout_make_node(lc
, LAYOUT_TOPBOTTOM
);
423 /* Create a grid of the cells. */
424 wp
= TAILQ_FIRST(&w
->panes
);
425 for (j
= 0; j
< rows
; j
++) {
426 /* If this is the last cell, all done. */
430 /* Create the new row. */
431 lcrow
= layout_create_cell(lc
);
432 layout_set_size(lcrow
, w
->sx
, height
, 0, 0);
433 TAILQ_INSERT_TAIL(&lc
->cells
, lcrow
, entry
);
435 /* If only one column, just use the row directly. */
436 if (n
- (j
* columns
) == 1 || columns
== 1) {
437 layout_make_leaf(lcrow
, wp
);
438 wp
= TAILQ_NEXT(wp
, entry
);
442 /* Add in the columns. */
443 layout_make_node(lcrow
, LAYOUT_LEFTRIGHT
);
444 for (i
= 0; i
< columns
; i
++) {
445 /* Create and add a pane cell. */
446 lcchild
= layout_create_cell(lcrow
);
447 layout_set_size(lcchild
, width
, height
, 0, 0);
448 layout_make_leaf(lcchild
, wp
);
449 TAILQ_INSERT_TAIL(&lcrow
->cells
, lcchild
, entry
);
451 /* Move to the next cell. */
452 if ((wp
= TAILQ_NEXT(wp
, entry
)) == NULL
)
457 * Adjust the row and columns to fit the full width if
462 used
= ((i
+ 1) * (width
+ 1)) - 1;
465 lcchild
= TAILQ_LAST(&lcrow
->cells
, layout_cells
);
466 layout_resize_adjust(w
, lcchild
, LAYOUT_LEFTRIGHT
,
470 /* Adjust the last row height to fit if necessary. */
471 used
= (rows
* height
) + rows
- 1;
473 lcrow
= TAILQ_LAST(&lc
->cells
, layout_cells
);
474 layout_resize_adjust(w
, lcrow
, LAYOUT_TOPBOTTOM
,
478 /* Fix cell offsets. */
479 layout_fix_offsets(w
);
480 layout_fix_panes(w
, NULL
);
482 layout_print_cell(w
->layout_root
, __func__
, 1);
484 window_resize(w
, lc
->sx
, lc
->sy
, -1, -1);
485 notify_window("window-layout-changed", w
);
486 server_redraw_window(w
);