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 | |
30 | static void layout_set_even_h(struct window *); |
31 | static void layout_set_even_v(struct window *); |
32 | static void layout_set_main_h(struct window *); |
33 | static void layout_set_main_v(struct window *); |
34 | static void layout_set_tiled(struct window *); |
35 | |
36 | static 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 | |
47 | int |
48 | layout_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 | |
64 | u_int |
65 | layout_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 | |
77 | u_int |
78 | layout_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 | |
96 | u_int |
97 | layout_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 | |
117 | static void |
118 | layout_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 | |
171 | static void |
172 | layout_set_even_h(struct window *w) |
173 | { |
174 | layout_set_even(w, LAYOUT_LEFTRIGHT); |
175 | } |
176 | |
177 | static void |
178 | layout_set_even_v(struct window *w) |
179 | { |
180 | layout_set_even(w, LAYOUT_TOPBOTTOM); |
181 | } |
182 | |
183 | static void |
184 | layout_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 | |
270 | static void |
271 | layout_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 | |
357 | void |
358 | layout_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 | |