Fix cursor position after zero width space, GitHub issue 3469.
[tmux-openbsd.git] / layout-set.c
blobc702817dc7eebabdf3e3fb9b6b1a6d90f587bc0b
1 /* $OpenBSD$ */
3 /*
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>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "tmux.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 *);
37 static const struct {
38 const char *name;
39 void (*arrange)(struct window *);
40 } layout_sets[] = {
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 },
48 int
49 layout_set_lookup(const char *name)
51 u_int i;
52 int matched = -1;
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 */
57 return (-1);
58 matched = i;
62 return (matched);
65 u_int
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;
75 return (layout);
78 u_int
79 layout_set_next(struct window *w)
81 u_int layout;
83 if (w->lastlayout == -1)
84 layout = 0;
85 else {
86 layout = w->lastlayout + 1;
87 if (layout > nitems(layout_sets) - 1)
88 layout = 0;
91 if (layout_sets[layout].arrange != NULL)
92 layout_sets[layout].arrange(w);
93 w->lastlayout = layout;
94 return (layout);
97 u_int
98 layout_set_previous(struct window *w)
100 u_int layout;
102 if (w->lastlayout == -1)
103 layout = nitems(layout_sets) - 1;
104 else {
105 layout = w->lastlayout;
106 if (layout == 0)
107 layout = nitems(layout_sets) - 1;
108 else
109 layout--;
112 if (layout_sets[layout].arrange != NULL)
113 layout_sets[layout].arrange(w);
114 w->lastlayout = layout;
115 return (layout);
118 static void
119 layout_set_even(struct window *w, enum layout_type type)
121 struct window_pane *wp;
122 struct layout_cell *lc, *lcnew;
123 u_int n, sx, sy;
125 layout_print_cell(w->layout_root, __func__, 1);
127 /* Get number of panes. */
128 n = window_count_panes(w);
129 if (n <= 1)
130 return;
132 /* Free the old root and construct a new. */
133 layout_free(w);
134 lc = w->layout_root = layout_create_cell(NULL);
135 if (type == LAYOUT_LEFTRIGHT) {
136 sx = (n * (PANE_MINIMUM + 1)) - 1;
137 if (sx < w->sx)
138 sx = w->sx;
139 sy = w->sy;
140 } else {
141 sy = (n * (PANE_MINIMUM + 1)) - 1;
142 if (sy < w->sy)
143 sy = w->sy;
144 sx = w->sx;
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);
153 lcnew->sx = w->sx;
154 lcnew->sy = w->sy;
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);
172 static void
173 layout_set_even_h(struct window *w)
175 layout_set_even(w, LAYOUT_LEFTRIGHT);
178 static void
179 layout_set_even_v(struct window *w)
181 layout_set_even(w, LAYOUT_TOPBOTTOM);
184 static void
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;
190 char *cause;
191 const char *s;
193 layout_print_cell(w->layout_root, __func__, 1);
195 /* Get number of panes. */
196 n = window_count_panes(w);
197 if (n <= 1)
198 return;
199 n--; /* take off main pane */
201 /* Find available height - take off one line for the border. */
202 sy = w->sy - 1;
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);
207 if (cause != NULL) {
208 mainh = 24;
209 free(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;
216 else
217 mainh = sy - PANE_MINIMUM;
218 otherh = PANE_MINIMUM;
219 } else {
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) {
223 otherh = sy - mainh;
224 free(cause);
225 } else if (otherh > sy || sy - otherh < mainh)
226 otherh = sy - mainh;
227 else
228 mainh = sy - otherh;
231 /* Work out what width is needed. */
232 sx = (n * (PANE_MINIMUM + 1)) - 1;
233 if (sx < w->sx)
234 sx = w->sx;
236 /* Free old tree and create a new root. */
237 layout_free(w);
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);
251 if (n == 1) {
252 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
253 layout_make_leaf(lcother, wp);
254 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
255 } else {
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))
262 continue;
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);
282 static void
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;
288 char *cause;
289 const char *s;
291 layout_print_cell(w->layout_root, __func__, 1);
293 /* Get number of panes. */
294 n = window_count_panes(w);
295 if (n <= 1)
296 return;
297 n--; /* take off main pane */
299 /* Find available width - take off one line for the border. */
300 sx = w->sx - 1;
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);
305 if (cause != NULL) {
306 mainw = 80;
307 free(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;
314 else
315 mainw = sx - PANE_MINIMUM;
316 otherw = PANE_MINIMUM;
317 } else {
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) {
321 otherw = sx - mainw;
322 free(cause);
323 } else if (otherw > sx || sx - otherw < mainw)
324 otherw = sx - mainw;
325 else
326 mainw = sx - otherw;
329 /* Work out what height is needed. */
330 sy = (n * (PANE_MINIMUM + 1)) - 1;
331 if (sy < w->sy)
332 sy = w->sy;
334 /* Free old tree and create a new root. */
335 layout_free(w);
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);
349 if (n == 1) {
350 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
351 layout_make_leaf(lcother, wp);
352 TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
353 } else {
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))
360 continue;
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);
380 void
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);
392 if (n <= 1)
393 return;
395 /* How many rows and columns are wanted? */
396 rows = columns = 1;
397 while (rows * columns < n) {
398 rows++;
399 if (rows * columns < n)
400 columns++;
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. */
412 layout_free(w);
413 lc = w->layout_root = layout_create_cell(NULL);
414 sx = ((width + 1) * columns) - 1;
415 if (sx < w->sx)
416 sx = w->sx;
417 sy = ((height + 1) * rows) - 1;
418 if (sy < w->sy)
419 sy = w->sy;
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. */
427 if (wp == NULL)
428 break;
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);
439 continue;
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)
453 break;
457 * Adjust the row and columns to fit the full width if
458 * necessary.
460 if (i == columns)
461 i--;
462 used = ((i + 1) * (width + 1)) - 1;
463 if (w->sx <= used)
464 continue;
465 lcchild = TAILQ_LAST(&lcrow->cells, layout_cells);
466 layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT,
467 w->sx - used);
470 /* Adjust the last row height to fit if necessary. */
471 used = (rows * height) + rows - 1;
472 if (w->sy > used) {
473 lcrow = TAILQ_LAST(&lc->cells, layout_cells);
474 layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
475 w->sy - used);
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);