1 /* $OpenBSD: layout-set.c,v 1.15 2016/08/03 09:07:02 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> 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 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 *); 34 void layout_set_tiled(struct window *); 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 }, 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 void 118 layout_set_even_h(struct window *w) 119 { 120 struct window_pane *wp; 121 struct layout_cell *lc, *lcnew; 122 u_int i, n, width, xoff; 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 /* How many can we fit? */ 132 width = (w->sx - (n - 1)) / n; 133 if (width < PANE_MINIMUM) 134 width = PANE_MINIMUM; 135 136 /* Free the old root and construct a new. */ 137 layout_free(w); 138 lc = w->layout_root = layout_create_cell(NULL); 139 layout_set_size(lc, w->sx, w->sy, 0, 0); 140 layout_make_node(lc, LAYOUT_LEFTRIGHT); 141 142 /* Build new leaf cells. */ 143 i = xoff = 0; 144 TAILQ_FOREACH(wp, &w->panes, entry) { 145 /* Create child cell. */ 146 lcnew = layout_create_cell(lc); 147 layout_set_size(lcnew, width, w->sy, xoff, 0); 148 layout_make_leaf(lcnew, wp); 149 TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); 150 151 i++; 152 xoff += width + 1; 153 } 154 155 /* Allocate any remaining space. */ 156 if (w->sx > xoff - 1) { 157 lc = TAILQ_LAST(&lc->cells, layout_cells); 158 layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, 159 w->sx - (xoff - 1)); 160 } 161 162 /* Fix cell offsets. */ 163 layout_fix_offsets(lc); 164 layout_fix_panes(w, w->sx, w->sy); 165 166 layout_print_cell(w->layout_root, __func__, 1); 167 168 server_redraw_window(w); 169 } 170 171 void 172 layout_set_even_v(struct window *w) 173 { 174 struct window_pane *wp; 175 struct layout_cell *lc, *lcnew; 176 u_int i, n, height, yoff; 177 178 layout_print_cell(w->layout_root, __func__, 1); 179 180 /* Get number of panes. */ 181 n = window_count_panes(w); 182 if (n <= 1) 183 return; 184 185 /* How many can we fit? */ 186 height = (w->sy - (n - 1)) / n; 187 if (height < PANE_MINIMUM) 188 height = PANE_MINIMUM; 189 190 /* Free the old root and construct a new. */ 191 layout_free(w); 192 lc = w->layout_root = layout_create_cell(NULL); 193 layout_set_size(lc, w->sx, w->sy, 0, 0); 194 layout_make_node(lc, LAYOUT_TOPBOTTOM); 195 196 /* Build new leaf cells. */ 197 i = yoff = 0; 198 TAILQ_FOREACH(wp, &w->panes, entry) { 199 /* Create child cell. */ 200 lcnew = layout_create_cell(lc); 201 layout_set_size(lcnew, w->sx, height, 0, yoff); 202 layout_make_leaf(lcnew, wp); 203 TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry); 204 205 i++; 206 yoff += height + 1; 207 } 208 209 /* Allocate any remaining space. */ 210 if (w->sy > yoff - 1) { 211 lc = TAILQ_LAST(&lc->cells, layout_cells); 212 layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, 213 w->sy - (yoff - 1)); 214 } 215 216 /* Fix cell offsets. */ 217 layout_fix_offsets(lc); 218 layout_fix_panes(w, w->sx, w->sy); 219 220 layout_print_cell(w->layout_root, __func__, 1); 221 222 server_redraw_window(w); 223 } 224 225 void 226 layout_set_main_h(struct window *w) 227 { 228 struct window_pane *wp; 229 struct layout_cell *lc, *lcmain, *lcrow, *lcchild; 230 u_int n, mainheight, otherheight, width, height; 231 u_int used, i, j, columns, rows, totalrows; 232 233 layout_print_cell(w->layout_root, __func__, 1); 234 235 /* Get number of panes. */ 236 n = window_count_panes(w); 237 if (n <= 1) 238 return; 239 n--; /* take off main pane */ 240 241 /* How many rows and columns will be needed, not counting main? */ 242 columns = (w->sx + 1) / (PANE_MINIMUM + 1); /* maximum columns */ 243 if (columns == 0) 244 columns = 1; 245 rows = 1 + (n - 1) / columns; 246 columns = 1 + (n - 1) / rows; 247 width = (w->sx - (n - 1)) / columns; 248 249 /* Get the main pane height and add one for separator line. */ 250 mainheight = options_get_number(w->options, "main-pane-height") + 1; 251 252 /* Get the optional other pane height and add one for separator line. */ 253 otherheight = options_get_number(w->options, "other-pane-height") + 1; 254 255 /* 256 * If an other pane height was specified, honour it so long as it 257 * doesn't shrink the main height to less than the main-pane-height 258 */ 259 if (otherheight > 1 && w->sy - otherheight > mainheight) 260 mainheight = w->sy - otherheight; 261 if (mainheight < PANE_MINIMUM + 1) 262 mainheight = PANE_MINIMUM + 1; 263 264 /* Try and make everything fit. */ 265 totalrows = rows * (PANE_MINIMUM + 1) - 1; 266 if (mainheight + totalrows > w->sy) { 267 if (totalrows + PANE_MINIMUM + 1 > w->sy) 268 mainheight = PANE_MINIMUM + 2; 269 else 270 mainheight = w->sy - totalrows; 271 height = PANE_MINIMUM; 272 } else 273 height = (w->sy - mainheight - (rows - 1)) / rows; 274 275 /* Free old tree and create a new root. */ 276 layout_free(w); 277 lc = w->layout_root = layout_create_cell(NULL); 278 layout_set_size(lc, w->sx, mainheight + rows * (height + 1) - 1, 0, 0); 279 layout_make_node(lc, LAYOUT_TOPBOTTOM); 280 281 /* Create the main pane. */ 282 lcmain = layout_create_cell(lc); 283 layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0); 284 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); 285 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); 286 287 /* Create a grid of the remaining cells. */ 288 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); 289 for (j = 0; j < rows; j++) { 290 /* If this is the last cell, all done. */ 291 if (wp == NULL) 292 break; 293 294 /* Create the new row. */ 295 lcrow = layout_create_cell(lc); 296 layout_set_size(lcrow, w->sx, height, 0, 0); 297 TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); 298 299 /* If only one column, just use the row directly. */ 300 if (columns == 1) { 301 layout_make_leaf(lcrow, wp); 302 wp = TAILQ_NEXT(wp, entry); 303 continue; 304 } 305 306 /* Add in the columns. */ 307 layout_make_node(lcrow, LAYOUT_LEFTRIGHT); 308 for (i = 0; i < columns; i++) { 309 /* Create and add a pane cell. */ 310 lcchild = layout_create_cell(lcrow); 311 layout_set_size(lcchild, width, height, 0, 0); 312 layout_make_leaf(lcchild, wp); 313 TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); 314 315 /* Move to the next cell. */ 316 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 317 break; 318 } 319 320 /* Adjust the row to fit the full width if necessary. */ 321 if (i == columns) 322 i--; 323 used = ((i + 1) * (width + 1)) - 1; 324 if (w->sx <= used) 325 continue; 326 lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); 327 layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT, 328 w->sx - used); 329 } 330 331 /* Adjust the last row height to fit if necessary. */ 332 used = mainheight + (rows * height) + rows - 1; 333 if (w->sy > used) { 334 lcrow = TAILQ_LAST(&lc->cells, layout_cells); 335 layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM, 336 w->sy - used); 337 } 338 339 /* Fix cell offsets. */ 340 layout_fix_offsets(lc); 341 layout_fix_panes(w, w->sx, w->sy); 342 343 layout_print_cell(w->layout_root, __func__, 1); 344 345 server_redraw_window(w); 346 } 347 348 void 349 layout_set_main_v(struct window *w) 350 { 351 struct window_pane *wp; 352 struct layout_cell *lc, *lcmain, *lccolumn, *lcchild; 353 u_int n, mainwidth, otherwidth, width, height; 354 u_int used, i, j, columns, rows, totalcolumns; 355 356 layout_print_cell(w->layout_root, __func__, 1); 357 358 /* Get number of panes. */ 359 n = window_count_panes(w); 360 if (n <= 1) 361 return; 362 n--; /* take off main pane */ 363 364 /* How many rows and columns will be needed, not counting main? */ 365 rows = (w->sy + 1) / (PANE_MINIMUM + 1); /* maximum rows */ 366 if (rows == 0) 367 rows = 1; 368 columns = 1 + (n - 1) / rows; 369 rows = 1 + (n - 1) / columns; 370 height = (w->sy - (n - 1)) / rows; 371 372 /* Get the main pane width and add one for separator line. */ 373 mainwidth = options_get_number(w->options, "main-pane-width") + 1; 374 375 /* Get the optional other pane width and add one for separator line. */ 376 otherwidth = options_get_number(w->options, "other-pane-width") + 1; 377 378 /* 379 * If an other pane width was specified, honour it so long as it 380 * doesn't shrink the main width to less than the main-pane-width 381 */ 382 if (otherwidth > 1 && w->sx - otherwidth > mainwidth) 383 mainwidth = w->sx - otherwidth; 384 if (mainwidth < PANE_MINIMUM + 1) 385 mainwidth = PANE_MINIMUM + 1; 386 387 /* Try and make everything fit. */ 388 totalcolumns = columns * (PANE_MINIMUM + 1) - 1; 389 if (mainwidth + totalcolumns > w->sx) { 390 if (totalcolumns + PANE_MINIMUM + 1 > w->sx) 391 mainwidth = PANE_MINIMUM + 2; 392 else 393 mainwidth = w->sx - totalcolumns; 394 width = PANE_MINIMUM; 395 } else 396 width = (w->sx - mainwidth - (columns - 1)) / columns; 397 398 /* Free old tree and create a new root. */ 399 layout_free(w); 400 lc = w->layout_root = layout_create_cell(NULL); 401 layout_set_size(lc, mainwidth + columns * (width + 1) - 1, w->sy, 0, 0); 402 layout_make_node(lc, LAYOUT_LEFTRIGHT); 403 404 /* Create the main pane. */ 405 lcmain = layout_create_cell(lc); 406 layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0); 407 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); 408 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); 409 410 /* Create a grid of the remaining cells. */ 411 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); 412 for (j = 0; j < columns; j++) { 413 /* If this is the last cell, all done. */ 414 if (wp == NULL) 415 break; 416 417 /* Create the new column. */ 418 lccolumn = layout_create_cell(lc); 419 layout_set_size(lccolumn, width, w->sy, 0, 0); 420 TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry); 421 422 /* If only one row, just use the row directly. */ 423 if (rows == 1) { 424 layout_make_leaf(lccolumn, wp); 425 wp = TAILQ_NEXT(wp, entry); 426 continue; 427 } 428 429 /* Add in the rows. */ 430 layout_make_node(lccolumn, LAYOUT_TOPBOTTOM); 431 for (i = 0; i < rows; i++) { 432 /* Create and add a pane cell. */ 433 lcchild = layout_create_cell(lccolumn); 434 layout_set_size(lcchild, width, height, 0, 0); 435 layout_make_leaf(lcchild, wp); 436 TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry); 437 438 /* Move to the next cell. */ 439 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 440 break; 441 } 442 443 /* Adjust the column to fit the full height if necessary. */ 444 if (i == rows) 445 i--; 446 used = ((i + 1) * (height + 1)) - 1; 447 if (w->sy <= used) 448 continue; 449 lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells); 450 layout_resize_adjust(w, lcchild, LAYOUT_TOPBOTTOM, 451 w->sy - used); 452 } 453 454 /* Adjust the last column width to fit if necessary. */ 455 used = mainwidth + (columns * width) + columns - 1; 456 if (w->sx > used) { 457 lccolumn = TAILQ_LAST(&lc->cells, layout_cells); 458 layout_resize_adjust(w, lccolumn, LAYOUT_LEFTRIGHT, 459 w->sx - used); 460 } 461 462 /* Fix cell offsets. */ 463 layout_fix_offsets(lc); 464 layout_fix_panes(w, w->sx, w->sy); 465 466 layout_print_cell(w->layout_root, __func__, 1); 467 468 server_redraw_window(w); 469 } 470 471 void 472 layout_set_tiled(struct window *w) 473 { 474 struct window_pane *wp; 475 struct layout_cell *lc, *lcrow, *lcchild; 476 u_int n, width, height, used; 477 u_int i, j, columns, rows; 478 479 layout_print_cell(w->layout_root, __func__, 1); 480 481 /* Get number of panes. */ 482 n = window_count_panes(w); 483 if (n <= 1) 484 return; 485 486 /* How many rows and columns are wanted? */ 487 rows = columns = 1; 488 while (rows * columns < n) { 489 rows++; 490 if (rows * columns < n) 491 columns++; 492 } 493 494 /* What width and height should they be? */ 495 width = (w->sx - (columns - 1)) / columns; 496 if (width < PANE_MINIMUM) 497 width = PANE_MINIMUM; 498 height = (w->sy - (rows - 1)) / rows; 499 if (height < PANE_MINIMUM) 500 height = PANE_MINIMUM; 501 502 /* Free old tree and create a new root. */ 503 layout_free(w); 504 lc = w->layout_root = layout_create_cell(NULL); 505 layout_set_size(lc, (width + 1) * columns - 1, 506 (height + 1) * rows - 1, 0, 0); 507 layout_make_node(lc, LAYOUT_TOPBOTTOM); 508 509 /* Create a grid of the cells. */ 510 wp = TAILQ_FIRST(&w->panes); 511 for (j = 0; j < rows; j++) { 512 /* If this is the last cell, all done. */ 513 if (wp == NULL) 514 break; 515 516 /* Create the new row. */ 517 lcrow = layout_create_cell(lc); 518 layout_set_size(lcrow, w->sx, height, 0, 0); 519 TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); 520 521 /* If only one column, just use the row directly. */ 522 if (n - (j * columns) == 1 || columns == 1) { 523 layout_make_leaf(lcrow, wp); 524 wp = TAILQ_NEXT(wp, entry); 525 continue; 526 } 527 528 /* Add in the columns. */ 529 layout_make_node(lcrow, LAYOUT_LEFTRIGHT); 530 for (i = 0; i < columns; i++) { 531 /* Create and add a pane cell. */ 532 lcchild = layout_create_cell(lcrow); 533 layout_set_size(lcchild, width, height, 0, 0); 534 layout_make_leaf(lcchild, wp); 535 TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); 536 537 /* Move to the next cell. */ 538 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 539 break; 540 } 541 542 /* 543 * Adjust the row and columns to fit the full width if 544 * necessary. 545 */ 546 if (i == columns) 547 i--; 548 used = ((i + 1) * (width + 1)) - 1; 549 if (w->sx <= used) 550 continue; 551 lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); 552 layout_resize_adjust(w, lcchild, LAYOUT_LEFTRIGHT, 553 w->sx - used); 554 } 555 556 /* Adjust the last row height to fit if necessary. */ 557 used = (rows * height) + rows - 1; 558 if (w->sy > used) { 559 lcrow = TAILQ_LAST(&lc->cells, layout_cells); 560 layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM, 561 w->sy - used); 562 } 563 564 /* Fix cell offsets. */ 565 layout_fix_offsets(lc); 566 layout_fix_panes(w, w->sx, w->sy); 567 568 layout_print_cell(w->layout_root, __func__, 1); 569 570 server_redraw_window(w); 571 } 572