Annotation of src/usr.bin/tmux/layout-set.c, Revision 1.6
1.6 ! nicm 1: /* $OpenBSD: layout-set.c,v 1.5 2010/02/03 22:24:34 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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 one-off
27: * and generate a layout tree.
28: */
29:
30: void layout_set_even_h(struct window *);
31: void layout_set_even_v(struct window *);
32: void layout_set_main_h(struct window *);
33: void layout_set_main_v(struct window *);
1.6 ! nicm 34: void layout_set_tiled(struct window *);
1.1 nicm 35:
36: 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 },
1.6 ! nicm 44: { "tiled", layout_set_tiled },
1.1 nicm 45: };
46:
47: const char *
48: layout_set_name(u_int layout)
49: {
50: return (layout_sets[layout].name);
51: }
52:
53: int
54: layout_set_lookup(const char *name)
55: {
56: u_int i;
57: int matched = -1;
58:
59: for (i = 0; i < nitems(layout_sets); i++) {
60: if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
61: if (matched != -1) /* ambiguous */
62: return (-1);
63: matched = i;
64: }
65: }
66:
67: return (matched);
68: }
69:
70: u_int
71: layout_set_select(struct window *w, u_int layout)
72: {
73: if (layout > nitems(layout_sets) - 1)
74: layout = nitems(layout_sets) - 1;
75:
76: if (layout_sets[layout].arrange != NULL)
77: layout_sets[layout].arrange(w);
78:
1.3 nicm 79: w->lastlayout = layout;
1.1 nicm 80: return (layout);
81: }
82:
83: u_int
84: layout_set_next(struct window *w)
85: {
1.3 nicm 86: u_int layout;
87:
88: if (w->lastlayout == -1)
89: layout = 0;
90: else {
91: layout = w->lastlayout + 1;
92: if (layout > nitems(layout_sets) - 1)
93: layout = 0;
94: }
1.1 nicm 95:
96: if (layout_sets[layout].arrange != NULL)
97: layout_sets[layout].arrange(w);
1.3 nicm 98: w->lastlayout = layout;
1.1 nicm 99: return (layout);
100: }
101:
102: u_int
103: layout_set_previous(struct window *w)
104: {
1.3 nicm 105: u_int layout;
106:
107: if (w->lastlayout == -1)
108: layout = nitems(layout_sets) - 1;
109: else {
110: layout = w->lastlayout;
111: if (layout == 0)
112: layout = nitems(layout_sets) - 1;
113: else
114: layout--;
115: }
1.1 nicm 116:
117: if (layout_sets[layout].arrange != NULL)
118: layout_sets[layout].arrange(w);
1.3 nicm 119: w->lastlayout = layout;
1.1 nicm 120: return (layout);
121: }
122:
123: void
124: layout_set_even_h(struct window *w)
125: {
126: struct window_pane *wp;
127: struct layout_cell *lc, *lcnew;
128: u_int i, n, width, xoff;
129:
130: layout_print_cell(w->layout_root, __func__, 1);
131:
132: /* Get number of panes. */
133: n = window_count_panes(w);
134: if (n <= 1)
135: return;
136:
137: /* How many can we fit? */
1.2 nicm 138: if (w->sx / n < PANE_MINIMUM + 1)
1.1 nicm 139: width = PANE_MINIMUM + 1;
1.2 nicm 140: else
1.1 nicm 141: width = w->sx / n;
142:
143: /* Free the old root and construct a new. */
144: layout_free(w);
145: lc = w->layout_root = layout_create_cell(NULL);
146: layout_set_size(lc, w->sx, w->sy, 0, 0);
147: layout_make_node(lc, LAYOUT_LEFTRIGHT);
148:
149: /* Build new leaf cells. */
150: i = xoff = 0;
151: TAILQ_FOREACH(wp, &w->panes, entry) {
152: /* Create child cell. */
153: lcnew = layout_create_cell(lc);
154: layout_set_size(lcnew, width - 1, w->sy, xoff, 0);
155: layout_make_leaf(lcnew, wp);
156: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
157:
158: i++;
159: xoff += width;
160: }
161:
162: /* Allocate any remaining space. */
163: if (w->sx > xoff - 1) {
164: lc = TAILQ_LAST(&lc->cells, layout_cells);
165: layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1));
166: }
167:
168: /* Fix cell offsets. */
169: layout_fix_offsets(lc);
170: layout_fix_panes(w, w->sx, w->sy);
171:
172: layout_print_cell(w->layout_root, __func__, 1);
173:
174: server_redraw_window(w);
175: }
176:
177: void
178: layout_set_even_v(struct window *w)
179: {
180: struct window_pane *wp;
181: struct layout_cell *lc, *lcnew;
182: u_int i, n, height, yoff;
183:
184: layout_print_cell(w->layout_root, __func__, 1);
185:
186: /* Get number of panes. */
187: n = window_count_panes(w);
188: if (n <= 1)
189: return;
190:
191: /* How many can we fit? */
1.2 nicm 192: if (w->sy / n < PANE_MINIMUM + 1)
1.1 nicm 193: height = PANE_MINIMUM + 1;
1.2 nicm 194: else
1.1 nicm 195: height = w->sy / n;
196:
197: /* Free the old root and construct a new. */
198: layout_free(w);
199: lc = w->layout_root = layout_create_cell(NULL);
200: layout_set_size(lc, w->sx, w->sy, 0, 0);
201: layout_make_node(lc, LAYOUT_TOPBOTTOM);
202:
203: /* Build new leaf cells. */
204: i = yoff = 0;
205: TAILQ_FOREACH(wp, &w->panes, entry) {
206: /* Create child cell. */
207: lcnew = layout_create_cell(lc);
208: layout_set_size(lcnew, w->sx, height - 1, 0, yoff);
209: layout_make_leaf(lcnew, wp);
210: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
211:
212: i++;
213: yoff += height;
214: }
215:
216: /* Allocate any remaining space. */
217: if (w->sy > yoff - 1) {
218: lc = TAILQ_LAST(&lc->cells, layout_cells);
219: layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1));
220: }
221:
222: /* Fix cell offsets. */
223: layout_fix_offsets(lc);
224: layout_fix_panes(w, w->sx, w->sy);
225:
226: layout_print_cell(w->layout_root, __func__, 1);
227:
228: server_redraw_window(w);
229: }
230:
231: void
232: layout_set_main_h(struct window *w)
233: {
234: struct window_pane *wp;
235: struct layout_cell *lc, *lcmain, *lcrow, *lcchild;
236: u_int n, mainheight, width, height, used;
237: u_int i, j, columns, rows, totalrows;
238:
239: layout_print_cell(w->layout_root, __func__, 1);
240:
241: /* Get number of panes. */
242: n = window_count_panes(w);
243: if (n <= 1)
244: return;
245: n--; /* take off main pane */
246:
247: /* How many rows and columns will be needed? */
248: columns = w->sx / (PANE_MINIMUM + 1); /* maximum columns */
1.5 nicm 249: if (columns == 0)
250: columns = 1;
1.1 nicm 251: rows = 1 + (n - 1) / columns;
252: columns = 1 + (n - 1) / rows;
253: width = w->sx / columns;
254:
255: /* Get the main pane height and add one for separator line. */
256: mainheight = options_get_number(&w->options, "main-pane-height") + 1;
257: if (mainheight < PANE_MINIMUM + 1)
258: mainheight = PANE_MINIMUM + 1;
259:
260: /* Try and make everything fit. */
261: totalrows = rows * (PANE_MINIMUM + 1) - 1;
262: if (mainheight + totalrows > w->sy) {
263: if (totalrows + PANE_MINIMUM + 1 > w->sy)
264: mainheight = PANE_MINIMUM + 2;
265: else
266: mainheight = w->sy - totalrows;
267: height = PANE_MINIMUM + 1;
268: } else
269: height = (w->sy - mainheight) / rows;
270:
271: /* Free old tree and create a new root. */
272: layout_free(w);
273: lc = w->layout_root = layout_create_cell(NULL);
274: layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0);
275: layout_make_node(lc, LAYOUT_TOPBOTTOM);
276:
277: /* Create the main pane. */
278: lcmain = layout_create_cell(lc);
279: layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
280: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
281: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
282:
283: /* Create a grid of the remaining cells. */
284: wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
285: for (j = 0; j < rows; j++) {
286: /* If this is the last cell, all done. */
287: if (wp == NULL)
288: break;
289:
290: /* Create the new row. */
291: lcrow = layout_create_cell(lc);
292: layout_set_size(lcrow, w->sx, height - 1, 0, 0);
293: TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
294:
295: /* If only one column, just use the row directly. */
296: if (columns == 1) {
297: layout_make_leaf(lcrow, wp);
298: wp = TAILQ_NEXT(wp, entry);
299: continue;
300: }
301:
1.4 nicm 302: /* Add in the columns. */
1.1 nicm 303: layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
304: for (i = 0; i < columns; i++) {
305: /* Create and add a pane cell. */
306: lcchild = layout_create_cell(lcrow);
307: layout_set_size(lcchild, width - 1, height - 1, 0, 0);
308: layout_make_leaf(lcchild, wp);
309: TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
310:
311: /* Move to the next cell. */
312: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
313: break;
314: }
315:
316: /* Adjust the row to fit the full width if necessary. */
317: if (i == columns)
318: i--;
1.4 nicm 319: used = ((i + 1) * width) - 1;
320: if (w->sx <= used)
321: continue;
322: lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
323: layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
1.1 nicm 324: }
325:
326: /* Adjust the last row height to fit if necessary. */
327: used = mainheight + (rows * height) - 1;
328: if (w->sy > used) {
329: lcrow = TAILQ_LAST(&lc->cells, layout_cells);
330: layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
331: }
332:
333: /* Fix cell offsets. */
334: layout_fix_offsets(lc);
335: layout_fix_panes(w, w->sx, w->sy);
336:
337: layout_print_cell(w->layout_root, __func__, 1);
338:
339: server_redraw_window(w);
340: }
341:
342: void
343: layout_set_main_v(struct window *w)
344: {
345: struct window_pane *wp;
346: struct layout_cell *lc, *lcmain, *lccolumn, *lcchild;
347: u_int n, mainwidth, width, height, used;
348: u_int i, j, columns, rows, totalcolumns;
349:
350: layout_print_cell(w->layout_root, __func__, 1);
351:
352: /* Get number of panes. */
353: n = window_count_panes(w);
354: if (n <= 1)
355: return;
356: n--; /* take off main pane */
357:
358: /* How many rows and columns will be needed? */
359: rows = w->sy / (PANE_MINIMUM + 1); /* maximum rows */
1.5 nicm 360: if (rows == 0)
361: rows = 1;
1.1 nicm 362: columns = 1 + (n - 1) / rows;
363: rows = 1 + (n - 1) / columns;
364: height = w->sy / rows;
365:
366: /* Get the main pane width and add one for separator line. */
367: mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
368: if (mainwidth < PANE_MINIMUM + 1)
369: mainwidth = PANE_MINIMUM + 1;
370:
371: /* Try and make everything fit. */
372: totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
373: if (mainwidth + totalcolumns > w->sx) {
374: if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
375: mainwidth = PANE_MINIMUM + 2;
376: else
377: mainwidth = w->sx - totalcolumns;
378: width = PANE_MINIMUM + 1;
379: } else
380: width = (w->sx - mainwidth) / columns;
381:
382: /* Free old tree and create a new root. */
383: layout_free(w);
384: lc = w->layout_root = layout_create_cell(NULL);
385: layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0);
386: layout_make_node(lc, LAYOUT_LEFTRIGHT);
387:
388: /* Create the main pane. */
389: lcmain = layout_create_cell(lc);
390: layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
391: layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
392: TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
393:
394: /* Create a grid of the remaining cells. */
395: wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
396: for (j = 0; j < columns; j++) {
397: /* If this is the last cell, all done. */
398: if (wp == NULL)
399: break;
400:
401: /* Create the new column. */
402: lccolumn = layout_create_cell(lc);
403: layout_set_size(lccolumn, width - 1, w->sy, 0, 0);
404: TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
405:
406: /* If only one row, just use the row directly. */
407: if (rows == 1) {
408: layout_make_leaf(lccolumn, wp);
409: wp = TAILQ_NEXT(wp, entry);
410: continue;
411: }
412:
413: /* Add in the rows. */
414: layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
415: for (i = 0; i < rows; i++) {
416: /* Create and add a pane cell. */
417: lcchild = layout_create_cell(lccolumn);
418: layout_set_size(lcchild, width - 1, height - 1, 0, 0);
419: layout_make_leaf(lcchild, wp);
420: TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
421:
422: /* Move to the next cell. */
423: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
424: break;
425: }
426:
427: /* Adjust the column to fit the full height if necessary. */
428: if (i == rows)
429: i--;
430: used = ((i + 1) * height) - 1;
1.4 nicm 431: if (w->sy <= used)
432: continue;
433: lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
434: layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used);
1.1 nicm 435: }
436:
437: /* Adjust the last column width to fit if necessary. */
438: used = mainwidth + (columns * width) - 1;
439: if (w->sx > used) {
440: lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
441: layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used);
1.6 ! nicm 442: }
! 443:
! 444: /* Fix cell offsets. */
! 445: layout_fix_offsets(lc);
! 446: layout_fix_panes(w, w->sx, w->sy);
! 447:
! 448: layout_print_cell(w->layout_root, __func__, 1);
! 449:
! 450: server_redraw_window(w);
! 451: }
! 452:
! 453: void
! 454: layout_set_tiled(struct window *w)
! 455: {
! 456: struct window_pane *wp;
! 457: struct layout_cell *lc, *lcrow, *lcchild;
! 458: u_int n, width, height, used;
! 459: u_int i, j, columns, rows;
! 460:
! 461: layout_print_cell(w->layout_root, __func__, 1);
! 462:
! 463: /* Get number of panes. */
! 464: n = window_count_panes(w);
! 465: if (n <= 1)
! 466: return;
! 467:
! 468: /* How many rows and columns are wanted? */
! 469: rows = columns = 1;
! 470: while (rows * columns < n) {
! 471: rows++;
! 472: if (rows * columns < n)
! 473: columns++;
! 474: }
! 475:
! 476: /* What width and height should they be? */
! 477: width = w->sx / columns;
! 478: if (width < PANE_MINIMUM + 1)
! 479: width = PANE_MINIMUM + 1;
! 480: height = w->sy / rows;
! 481: if (width < PANE_MINIMUM + 1)
! 482: width = PANE_MINIMUM + 1;
! 483:
! 484: /* Free old tree and create a new root. */
! 485: layout_free(w);
! 486: lc = w->layout_root = layout_create_cell(NULL);
! 487: layout_set_size(lc, width * columns, height * rows, 0, 0);
! 488: layout_make_node(lc, LAYOUT_TOPBOTTOM);
! 489:
! 490: /* Create a grid of the cells. */
! 491: wp = TAILQ_FIRST(&w->panes);
! 492: for (j = 0; j < rows; j++) {
! 493: /* If this is the last cell, all done. */
! 494: if (wp == NULL)
! 495: break;
! 496:
! 497: /* Create the new row. */
! 498: lcrow = layout_create_cell(lc);
! 499: layout_set_size(lcrow, w->sx, height - 1, 0, 0);
! 500: TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
! 501:
! 502: /* If only one column, just use the row directly. */
! 503: if (n - (j * columns) == 1) {
! 504: layout_make_leaf(lcrow, wp);
! 505: wp = TAILQ_NEXT(wp, entry);
! 506: continue;
! 507: }
! 508:
! 509: /* Add in the columns. */
! 510: layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
! 511: for (i = 0; i < columns; i++) {
! 512: /* Create and add a pane cell. */
! 513: lcchild = layout_create_cell(lcrow);
! 514: layout_set_size(lcchild, width - 1, height - 1, 0, 0);
! 515: layout_make_leaf(lcchild, wp);
! 516: TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
! 517:
! 518: /* Move to the next cell. */
! 519: if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
! 520: break;
! 521: }
! 522:
! 523: /*
! 524: * Adjust the row and columns to fit the full width if
! 525: * necessary.
! 526: */
! 527: if (i == columns)
! 528: i--;
! 529: used = ((i + 1) * width) - 1;
! 530: if (w->sx <= used)
! 531: continue;
! 532: lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
! 533: layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
! 534: }
! 535:
! 536: /* Adjust the last row height to fit if necessary. */
! 537: used = (rows * height) - 1;
! 538: if (w->sy > used) {
! 539: lcrow = TAILQ_LAST(&lc->cells, layout_cells);
! 540: layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
1.1 nicm 541: }
542:
543: /* Fix cell offsets. */
544: layout_fix_offsets(lc);
545: layout_fix_panes(w, w->sx, w->sy);
546:
547: layout_print_cell(w->layout_root, __func__, 1);
548:
549: server_redraw_window(w);
550: }