This commit was manufactured by cvs2svn to create tag 'TMUX_1_2'.
[tmux.git] / layout-set.c
blobe3275142e335bc20d2201195351a764a7fd16c85
1 /* $Id: layout-set.c,v 1.5 2010-02-05 01:29:04 tcunha Exp $ */
3 /*
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>
21 #include <string.h>
23 #include "tmux.h"
26 * Set window layouts - predefined methods to arrange windows. These are one-off
27 * and generate a layout tree.
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 *);
35 const struct {
36 const char *name;
37 void (*arrange)(struct window *);
38 } layout_sets[] = {
39 { "even-horizontal", layout_set_even_h },
40 { "even-vertical", layout_set_even_v },
41 { "main-horizontal", layout_set_main_h },
42 { "main-vertical", layout_set_main_v },
45 const char *
46 layout_set_name(u_int layout)
48 return (layout_sets[layout].name);
51 int
52 layout_set_lookup(const char *name)
54 u_int i;
55 int matched = -1;
57 for (i = 0; i < nitems(layout_sets); i++) {
58 if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
59 if (matched != -1) /* ambiguous */
60 return (-1);
61 matched = i;
65 return (matched);
68 u_int
69 layout_set_select(struct window *w, u_int layout)
71 if (layout > nitems(layout_sets) - 1)
72 layout = nitems(layout_sets) - 1;
74 if (layout_sets[layout].arrange != NULL)
75 layout_sets[layout].arrange(w);
77 w->lastlayout = layout;
78 return (layout);
81 u_int
82 layout_set_next(struct window *w)
84 u_int layout;
86 if (w->lastlayout == -1)
87 layout = 0;
88 else {
89 layout = w->lastlayout + 1;
90 if (layout > nitems(layout_sets) - 1)
91 layout = 0;
94 if (layout_sets[layout].arrange != NULL)
95 layout_sets[layout].arrange(w);
96 w->lastlayout = layout;
97 return (layout);
100 u_int
101 layout_set_previous(struct window *w)
103 u_int layout;
105 if (w->lastlayout == -1)
106 layout = nitems(layout_sets) - 1;
107 else {
108 layout = w->lastlayout;
109 if (layout == 0)
110 layout = nitems(layout_sets) - 1;
111 else
112 layout--;
115 if (layout_sets[layout].arrange != NULL)
116 layout_sets[layout].arrange(w);
117 w->lastlayout = layout;
118 return (layout);
121 void
122 layout_set_even_h(struct window *w)
124 struct window_pane *wp;
125 struct layout_cell *lc, *lcnew;
126 u_int i, n, width, xoff;
128 layout_print_cell(w->layout_root, __func__, 1);
130 /* Get number of panes. */
131 n = window_count_panes(w);
132 if (n <= 1)
133 return;
135 /* How many can we fit? */
136 if (w->sx / n < PANE_MINIMUM + 1)
137 width = PANE_MINIMUM + 1;
138 else
139 width = w->sx / n;
141 /* Free the old root and construct a new. */
142 layout_free(w);
143 lc = w->layout_root = layout_create_cell(NULL);
144 layout_set_size(lc, w->sx, w->sy, 0, 0);
145 layout_make_node(lc, LAYOUT_LEFTRIGHT);
147 /* Build new leaf cells. */
148 i = xoff = 0;
149 TAILQ_FOREACH(wp, &w->panes, entry) {
150 /* Create child cell. */
151 lcnew = layout_create_cell(lc);
152 layout_set_size(lcnew, width - 1, w->sy, xoff, 0);
153 layout_make_leaf(lcnew, wp);
154 TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
156 i++;
157 xoff += width;
160 /* Allocate any remaining space. */
161 if (w->sx > xoff - 1) {
162 lc = TAILQ_LAST(&lc->cells, layout_cells);
163 layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, w->sx - (xoff - 1));
166 /* Fix cell offsets. */
167 layout_fix_offsets(lc);
168 layout_fix_panes(w, w->sx, w->sy);
170 layout_print_cell(w->layout_root, __func__, 1);
172 server_redraw_window(w);
175 void
176 layout_set_even_v(struct window *w)
178 struct window_pane *wp;
179 struct layout_cell *lc, *lcnew;
180 u_int i, n, height, yoff;
182 layout_print_cell(w->layout_root, __func__, 1);
184 /* Get number of panes. */
185 n = window_count_panes(w);
186 if (n <= 1)
187 return;
189 /* How many can we fit? */
190 if (w->sy / n < PANE_MINIMUM + 1)
191 height = PANE_MINIMUM + 1;
192 else
193 height = w->sy / n;
195 /* Free the old root and construct a new. */
196 layout_free(w);
197 lc = w->layout_root = layout_create_cell(NULL);
198 layout_set_size(lc, w->sx, w->sy, 0, 0);
199 layout_make_node(lc, LAYOUT_TOPBOTTOM);
201 /* Build new leaf cells. */
202 i = yoff = 0;
203 TAILQ_FOREACH(wp, &w->panes, entry) {
204 /* Create child cell. */
205 lcnew = layout_create_cell(lc);
206 layout_set_size(lcnew, w->sx, height - 1, 0, yoff);
207 layout_make_leaf(lcnew, wp);
208 TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
210 i++;
211 yoff += height;
214 /* Allocate any remaining space. */
215 if (w->sy > yoff - 1) {
216 lc = TAILQ_LAST(&lc->cells, layout_cells);
217 layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, w->sy - (yoff - 1));
220 /* Fix cell offsets. */
221 layout_fix_offsets(lc);
222 layout_fix_panes(w, w->sx, w->sy);
224 layout_print_cell(w->layout_root, __func__, 1);
226 server_redraw_window(w);
229 void
230 layout_set_main_h(struct window *w)
232 struct window_pane *wp;
233 struct layout_cell *lc, *lcmain, *lcrow, *lcchild;
234 u_int n, mainheight, width, height, used;
235 u_int i, j, columns, rows, totalrows;
237 layout_print_cell(w->layout_root, __func__, 1);
239 /* Get number of panes. */
240 n = window_count_panes(w);
241 if (n <= 1)
242 return;
243 n--; /* take off main pane */
245 /* How many rows and columns will be needed? */
246 columns = w->sx / (PANE_MINIMUM + 1); /* maximum columns */
247 if (columns == 0)
248 columns = 1;
249 rows = 1 + (n - 1) / columns;
250 columns = 1 + (n - 1) / rows;
251 width = w->sx / columns;
253 /* Get the main pane height and add one for separator line. */
254 mainheight = options_get_number(&w->options, "main-pane-height") + 1;
255 if (mainheight < PANE_MINIMUM + 1)
256 mainheight = PANE_MINIMUM + 1;
258 /* Try and make everything fit. */
259 totalrows = rows * (PANE_MINIMUM + 1) - 1;
260 if (mainheight + totalrows > w->sy) {
261 if (totalrows + PANE_MINIMUM + 1 > w->sy)
262 mainheight = PANE_MINIMUM + 2;
263 else
264 mainheight = w->sy - totalrows;
265 height = PANE_MINIMUM + 1;
266 } else
267 height = (w->sy - mainheight) / rows;
269 /* Free old tree and create a new root. */
270 layout_free(w);
271 lc = w->layout_root = layout_create_cell(NULL);
272 layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0);
273 layout_make_node(lc, LAYOUT_TOPBOTTOM);
275 /* Create the main pane. */
276 lcmain = layout_create_cell(lc);
277 layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0);
278 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
279 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
281 /* Create a grid of the remaining cells. */
282 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
283 for (j = 0; j < rows; j++) {
284 /* If this is the last cell, all done. */
285 if (wp == NULL)
286 break;
288 /* Create the new row. */
289 lcrow = layout_create_cell(lc);
290 layout_set_size(lcrow, w->sx, height - 1, 0, 0);
291 TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
293 /* If only one column, just use the row directly. */
294 if (columns == 1) {
295 layout_make_leaf(lcrow, wp);
296 wp = TAILQ_NEXT(wp, entry);
297 continue;
300 /* Add in the columns. */
301 layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
302 for (i = 0; i < columns; i++) {
303 /* Create and add a pane cell. */
304 lcchild = layout_create_cell(lcrow);
305 layout_set_size(lcchild, width - 1, height - 1, 0, 0);
306 layout_make_leaf(lcchild, wp);
307 TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
309 /* Move to the next cell. */
310 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
311 break;
314 /* Adjust the row to fit the full width if necessary. */
315 if (i == columns)
316 i--;
317 used = ((i + 1) * width) - 1;
318 if (w->sx <= used)
319 continue;
320 lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
321 layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used);
324 /* Adjust the last row height to fit if necessary. */
325 used = mainheight + (rows * height) - 1;
326 if (w->sy > used) {
327 lcrow = TAILQ_LAST(&lc->cells, layout_cells);
328 layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used);
331 /* Fix cell offsets. */
332 layout_fix_offsets(lc);
333 layout_fix_panes(w, w->sx, w->sy);
335 layout_print_cell(w->layout_root, __func__, 1);
337 server_redraw_window(w);
340 void
341 layout_set_main_v(struct window *w)
343 struct window_pane *wp;
344 struct layout_cell *lc, *lcmain, *lccolumn, *lcchild;
345 u_int n, mainwidth, width, height, used;
346 u_int i, j, columns, rows, totalcolumns;
348 layout_print_cell(w->layout_root, __func__, 1);
350 /* Get number of panes. */
351 n = window_count_panes(w);
352 if (n <= 1)
353 return;
354 n--; /* take off main pane */
356 /* How many rows and columns will be needed? */
357 rows = w->sy / (PANE_MINIMUM + 1); /* maximum rows */
358 if (rows == 0)
359 rows = 1;
360 columns = 1 + (n - 1) / rows;
361 rows = 1 + (n - 1) / columns;
362 height = w->sy / rows;
364 /* Get the main pane width and add one for separator line. */
365 mainwidth = options_get_number(&w->options, "main-pane-width") + 1;
366 if (mainwidth < PANE_MINIMUM + 1)
367 mainwidth = PANE_MINIMUM + 1;
369 /* Try and make everything fit. */
370 totalcolumns = columns * (PANE_MINIMUM + 1) - 1;
371 if (mainwidth + totalcolumns > w->sx) {
372 if (totalcolumns + PANE_MINIMUM + 1 > w->sx)
373 mainwidth = PANE_MINIMUM + 2;
374 else
375 mainwidth = w->sx - totalcolumns;
376 width = PANE_MINIMUM + 1;
377 } else
378 width = (w->sx - mainwidth) / columns;
380 /* Free old tree and create a new root. */
381 layout_free(w);
382 lc = w->layout_root = layout_create_cell(NULL);
383 layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0);
384 layout_make_node(lc, LAYOUT_LEFTRIGHT);
386 /* Create the main pane. */
387 lcmain = layout_create_cell(lc);
388 layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0);
389 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
390 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
392 /* Create a grid of the remaining cells. */
393 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
394 for (j = 0; j < columns; j++) {
395 /* If this is the last cell, all done. */
396 if (wp == NULL)
397 break;
399 /* Create the new column. */
400 lccolumn = layout_create_cell(lc);
401 layout_set_size(lccolumn, width - 1, w->sy, 0, 0);
402 TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry);
404 /* If only one row, just use the row directly. */
405 if (rows == 1) {
406 layout_make_leaf(lccolumn, wp);
407 wp = TAILQ_NEXT(wp, entry);
408 continue;
411 /* Add in the rows. */
412 layout_make_node(lccolumn, LAYOUT_TOPBOTTOM);
413 for (i = 0; i < rows; i++) {
414 /* Create and add a pane cell. */
415 lcchild = layout_create_cell(lccolumn);
416 layout_set_size(lcchild, width - 1, height - 1, 0, 0);
417 layout_make_leaf(lcchild, wp);
418 TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry);
420 /* Move to the next cell. */
421 if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
422 break;
425 /* Adjust the column to fit the full height if necessary. */
426 if (i == rows)
427 i--;
428 used = ((i + 1) * height) - 1;
429 if (w->sy <= used)
430 continue;
431 lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells);
432 layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used);
435 /* Adjust the last column width to fit if necessary. */
436 used = mainwidth + (columns * width) - 1;
437 if (w->sx > used) {
438 lccolumn = TAILQ_LAST(&lc->cells, layout_cells);
439 layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used);
442 /* Fix cell offsets. */
443 layout_fix_offsets(lc);
444 layout_fix_panes(w, w->sx, w->sy);
446 layout_print_cell(w->layout_root, __func__, 1);
448 server_redraw_window(w);