1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2009 Nicholas Marriott <[email protected]>
5 *
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.
9 *
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.
17 */
18
19#include <sys/types.h>
20
21#include <string.h>
22
23#include "tmux.h"
24
25/*
26 * Set window layouts - predefined methods to arrange windows. These are
27 * one-off and generate a layout tree.
28 */
29
30static void layout_set_even_h(struct window *);
31static void layout_set_even_v(struct window *);
32static void layout_set_main_h(struct window *);
33static void layout_set_main_v(struct window *);
34static void layout_set_tiled(struct window *);
35
36static const struct {
37 const char *name;
38 void (*arrange)(struct window *);
39} layout_sets[] = {
40 { "even-horizontal", layout_set_even_h },
41 { "even-vertical", layout_set_even_v },
42 { "main-horizontal", layout_set_main_h },
43 { "main-vertical", layout_set_main_v },
44 { "tiled", layout_set_tiled },
45};
46
47int
48layout_set_lookup(const char *name)
49{
50 u_int i;
51 int matched = -1;
52
53 for (i = 0; i < nitems(layout_sets); i++) {
54 if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
55 if (matched != -1) /* ambiguous */
56 return (-1);
57 matched = i;
58 }
59 }
60
61 return (matched);
62}
63
64u_int
65layout_set_select(struct window *w, u_int layout)
66{
67 if (layout > nitems(layout_sets) - 1)
68 layout = nitems(layout_sets) - 1;
69
70 if (layout_sets[layout].arrange != NULL)
71 layout_sets[layout].arrange(w);
72
73 w->lastlayout = layout;
74 return (layout);
75}
76
77u_int
78layout_set_next(struct window *w)
79{
80 u_int layout;
81
82 if (w->lastlayout == -1)
83 layout = 0;
84 else {
85 layout = w->lastlayout + 1;
86 if (layout > nitems(layout_sets) - 1)
87 layout = 0;
88 }
89
90 if (layout_sets[layout].arrange != NULL)
91 layout_sets[layout].arrange(w);
92 w->lastlayout = layout;
93 return (layout);
94}
95
96u_int
97layout_set_previous(struct window *w)
98{
99 u_int layout;
100
101 if (w->lastlayout == -1)
102 layout = nitems(layout_sets) - 1;
103 else {
104 layout = w->lastlayout;
105 if (layout == 0)
106 layout = nitems(layout_sets) - 1;
107 else
108 layout--;
109 }
110
111 if (layout_sets[layout].arrange != NULL)
112 layout_sets[layout].arrange(w);
113 w->lastlayout = layout;
114 return (layout);
115}
116
117static void
118layout_set_even(struct window *w, enum layout_type type)
119{
120 struct window_pane *wp;
121 struct layout_cell *lc, *lcnew;
122 u_int n, sx, sy;
123
124 layout_print_cell(w->layout_root, __func__, 1);
125
126 /* Get number of panes. */
127 n = window_count_panes(w);
128 if (n <= 1)
129 return;
130
131 /* Free the old root and construct a new. */
132 layout_free(w);
133 lc = w->layout_root = layout_create_cell(NULL);
134 if (type == LAYOUT_LEFTRIGHT) {
135 sx = (n * (PANE_MINIMUM + 1)) - 1;
136 if (sx < w->sx)
137 sx = w->sx;
138 sy = w->sy;
139 } else {
140 sy = (n * (PANE_MINIMUM + 1)) - 1;
141 if (sy < w->sy)
142 sy = w->sy;
143 sx = w->sx;
144 }
145 layout_set_size(lc, sx, sy, 0, 0);
146 layout_make_node(lc, type);
147
148 /* Build new leaf cells. */
149 TAILQ_FOREACH(wp, &w->panes, entry) {
150 lcnew = layout_create_cell(lc);
151 layout_make_leaf(lcnew, wp);
152 lcnew->sx = w->sx;
153 lcnew->sy = w->sy;
154 TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
155 }
156
157 /* Spread out cells. */
158 layout_spread_cell(w, lc);
159
160 /* Fix cell offsets. */
161 layout_fix_offsets(w);
162 layout_fix_panes(w);
163
164 layout_print_cell(w->layout_root, __func__, 1);
165
166 window_resize(w, lc->sx, lc->sy, -1, -1);
167 notify_window("window-layout-changed", w);
168 server_redraw_window(w);
169}
170
171static void
172layout_set_even_h(struct window *w)
173{
174 layout_set_even(w, LAYOUT_LEFTRIGHT);
175}
176
177static void
178layout_set_even_v(struct window *w)
179{
180 layout_set_even(w, LAYOUT_TOPBOTTOM);
181}
182
183static void
184layout_set_main_h(struct window *w)
185{
186 struct window_pane *wp;
187 struct layout_cell *lc, *lcmain, *lcother, *lcchild;
188 u_int n, mainh, otherh, sx, sy;
189
190 layout_print_cell(w->layout_root, __func__, 1);
191
192 /* Get number of panes. */
193 n = window_count_panes(w);
194 if (n <= 1)
195 return;
196 n--; /* take off main pane */
197
198 /* Find available height - take off one line for the border. */
199 sy = w->sy - 1;
200
201 /* Get the main pane height and work out the other pane height. */
202 mainh = options_get_number(w->options, "main-pane-height");
203 if (mainh + PANE_MINIMUM >= sy) {
204 if (sy <= PANE_MINIMUM + PANE_MINIMUM)
205 mainh = PANE_MINIMUM;
206 else
207 mainh = sy - PANE_MINIMUM;
208 otherh = PANE_MINIMUM;
209 } else {
210 otherh = options_get_number(w->options, "other-pane-height");
211 if (otherh == 0)
212 otherh = sy - mainh;
213 else if (otherh > sy || sy - otherh < mainh)
214 otherh = sy - mainh;
215 else
216 mainh = sy - otherh;
217 }
218
219 /* Work out what width is needed. */
220 sx = (n * (PANE_MINIMUM + 1)) - 1;
221 if (sx < w->sx)
222 sx = w->sx;
223
224 /* Free old tree and create a new root. */
225 layout_free(w);
226 lc = w->layout_root = layout_create_cell(NULL);
227 layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
228 layout_make_node(lc, LAYOUT_TOPBOTTOM);
229
230 /* Create the main pane. */
231 lcmain = layout_create_cell(lc);
232 layout_set_size(lcmain, sx, mainh, 0, 0);
233 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
234 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
235
236 /* Create the other pane. */
237 lcother = layout_create_cell(lc);
238 layout_set_size(lcother, sx, otherh, 0, 0);
239 if (n == 1) {
240 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
241 layout_make_leaf(lcother, wp);
242 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
243 } else {
244 layout_make_node(lcother, LAYOUT_LEFTRIGHT);
245 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
246
247 /* Add the remaining panes as children. */
248 TAILQ_FOREACH(wp, &w->panes, entry) {
249 if (wp == TAILQ_FIRST(&w->panes))
250 continue;
251 lcchild = layout_create_cell(lcother);
252 layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
253 layout_make_leaf(lcchild, wp);
254 TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
255 }
256 layout_spread_cell(w, lcother);
257 }
258
259 /* Fix cell offsets. */
260 layout_fix_offsets(w);
261 layout_fix_panes(w);
262
263 layout_print_cell(w->layout_root, __func__, 1);
264
265 window_resize(w, lc->sx, lc->sy, -1, -1);
266 notify_window("window-layout-changed", w);
267 server_redraw_window(w);
268}
269
270static void
271layout_set_main_v(struct window *w)
272{
273 struct window_pane *wp;
274 struct layout_cell *lc, *lcmain, *lcother, *lcchild;
275 u_int n, mainw, otherw, sx, sy;
276
277 layout_print_cell(w->layout_root, __func__, 1);
278
279 /* Get number of panes. */
280 n = window_count_panes(w);
281 if (n <= 1)
282 return;
283 n--; /* take off main pane */
284
285 /* Find available width - take off one line for the border. */
286 sx = w->sx - 1;
287
288 /* Get the main pane width and work out the other pane width. */
289 mainw = options_get_number(w->options, "main-pane-width");
290 if (mainw + PANE_MINIMUM >= sx) {
291 if (sx <= PANE_MINIMUM + PANE_MINIMUM)
292 mainw = PANE_MINIMUM;
293 else
294 mainw = sx - PANE_MINIMUM;
295 otherw = PANE_MINIMUM;
296 } else {
297 otherw = options_get_number(w->options, "other-pane-width");
298 if (otherw == 0)
299 otherw = sx - mainw;
300 else if (otherw > sx || sx - otherw < mainw)
301 otherw = sx - mainw;
302 else
303 mainw = sx - otherw;
304 }
305
306 /* Work out what height is needed. */
307 sy = (n * (PANE_MINIMUM + 1)) - 1;
308 if (sy < w->sy)
309 sy = w->sy;
310
311 /* Free old tree and create a new root. */
312 layout_free(w);
313 lc = w->layout_root = layout_create_cell(NULL);
314 layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
315 layout_make_node(lc, LAYOUT_LEFTRIGHT);
316
317 /* Create the main pane. */
318 lcmain = layout_create_cell(lc);
319 layout_set_size(lcmain, mainw, sy, 0, 0);
320 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
321 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
322
323 /* Create the other pane. */
324 lcother = layout_create_cell(lc);
325 layout_set_size(lcother, otherw, sy, 0, 0);
326 if (n == 1) {
327 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
328 layout_make_leaf(lcother, wp);
329 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
330 } else {
331 layout_make_node(lcother, LAYOUT_TOPBOTTOM);
332 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
333
334 /* Add the remaining panes as children. */
335 TAILQ_FOREACH(wp, &w->panes, entry) {
336 if (wp == TAILQ_FIRST(&w->panes))
337 continue;
338 lcchild = layout_create_cell(lcother);
339 layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
340 layout_make_leaf(lcchild, wp);
341 TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
342 }
343 layout_spread_cell(w, lcother);
344 }
345
346 /* Fix cell offsets. */
347 layout_fix_offsets(w);
348 layout_fix_panes(w);
349
350 layout_print_cell(w->layout_root, __func__, 1);
351
352 window_resize(w, lc->sx, lc->sy, -1, -1);
353 notify_window("window-layout-changed", w);
354 server_redraw_window(w);
355}
356
357void
358layout_set_tiled(struct window *w)
359{
360 struct window_pane *wp;
361 struct layout_cell *lc, *lcrow, *lcchild;
362 u_int n, width, height, used, sx, sy;
363 u_int i, j, columns, rows;
364
365 layout_print_cell(w->layout_root, __func__, 1);
366
367 /* Get number of panes. */
368 n = window_count_panes(w);
369 if (n <= 1)
370 return;
371
372 /* How many rows and columns are wanted? */
373 rows = columns = 1;
374 while (rows * columns < n) {
375 rows++;
376 if (rows * columns < n)
377 columns++;
378 }
379
380 /* What width and height should they be? */
381 width = (w->sx - (columns - 1)) / columns;
382 if (width < PANE_MINIMUM)
383 width = PANE_MINIMUM;
384 height = (w->sy - (rows - 1)) / rows;
385 if (height < PANE_MINIMUM)
386 height = PANE_MINIMUM;
387
388 /* Free old tree and create a new root. */
389 layout_free(w);
390 lc = w->layout_root = layout_create_cell(NULL);
391 sx = ((width + 1) * columns) - 1;
392 if (sx < w->sx)
393 sx = w->sx;
394 sy = ((height + 1) * rows) - 1;
395 if (sy < w->sy)
396 sy = w->sy;
397 layout_set_size(lc, sx, sy, 0, 0);
398 layout_make_node(lc, LAYOUT_TOPBOTTOM);
399
400 /* Create a grid of the cells. */
401 wp = TAILQ_FIRST(&w->panes);
402 for (j = 0; j < rows; j++) {
403 /* If this is the last cell, all done. */
404 if (wp == NULL)
405 break;
406
407 /* Create the new row. */
408 lcrow = layout_create_cell(lc);
409 layout_set_size(lcrow, w->sx, height, 0, 0);
410 TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
411
412 /* If only one column, just use the row directly. */
413 if (n - (j * columns) == 1 || columns == 1) {
414 layout_make_leaf(lcrow, wp);
415 wp = TAILQ_NEXT(wp, entry);
416 continue;
417 }
418
419 /* Add in the columns. */
420 layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
421 for (i = 0; i < columns; i++) {
422 /* Create and add a pane cell. */
423 lcchild = layout_create_cell(lcrow);
424 layout_set_size(lcchild, width, height, 0, 0);
425 layout_make_leaf(lcchild, wp);
426 TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
427
428 /* Move to the next cell. */
429 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
430 break;
431 }
432
433 /*
434 * Adjust the row and columns to fit the full width if
435 * necessary.
436 */
437 if (i == columns)
438 i--;
439 used = ((i + 1) * (width + 1)) - 1;
440 if (w->sx <= used)
441 continue;
442 lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
443 layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
444 w->sx - used);
445 }
446
447 /* Adjust the last row height to fit if necessary. */
448 used = (rows * height) + rows - 1;
449 if (w->sy > used) {
450 lcrow = TAILQ_LAST(&lc->cells, layout_cells);
451 layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
452 w->sy - used);
453 }
454
455 /* Fix cell offsets. */
456 layout_fix_offsets(w);
457 layout_fix_panes(w);
458
459 layout_print_cell(w->layout_root, __func__, 1);
460
461 window_resize(w, lc->sx, lc->sy, -1, -1);
462 notify_window("window-layout-changed", w);
463 server_redraw_window(w);
464}
465