1 /* $OpenBSD: layout-set.c,v 1.3 2009/07/28 06:48:44 nicm Exp $ */ 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 *); 34 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 }, 43 }; 44 45 const char * 46 layout_set_name(u_int layout) 47 { 48 return (layout_sets[layout].name); 49 } 50 51 int 52 layout_set_lookup(const char *name) 53 { 54 u_int i; 55 int matched = -1; 56 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; 62 } 63 } 64 65 return (matched); 66 } 67 68 u_int 69 layout_set_select(struct window *w, u_int layout) 70 { 71 if (layout > nitems(layout_sets) - 1) 72 layout = nitems(layout_sets) - 1; 73 74 if (layout_sets[layout].arrange != NULL) 75 layout_sets[layout].arrange(w); 76 77 w->lastlayout = layout; 78 return (layout); 79 } 80 81 u_int 82 layout_set_next(struct window *w) 83 { 84 u_int layout; 85 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; 92 } 93 94 if (layout_sets[layout].arrange != NULL) 95 layout_sets[layout].arrange(w); 96 w->lastlayout = layout; 97 return (layout); 98 } 99 100 u_int 101 layout_set_previous(struct window *w) 102 { 103 u_int layout; 104 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--; 113 } 114 115 if (layout_sets[layout].arrange != NULL) 116 layout_sets[layout].arrange(w); 117 w->lastlayout = layout; 118 return (layout); 119 } 120 121 void 122 layout_set_even_h(struct window *w) 123 { 124 struct window_pane *wp; 125 struct layout_cell *lc, *lcnew; 126 u_int i, n, width, xoff; 127 128 layout_print_cell(w->layout_root, __func__, 1); 129 130 /* Get number of panes. */ 131 n = window_count_panes(w); 132 if (n <= 1) 133 return; 134 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; 140 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); 146 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); 155 156 i++; 157 xoff += width; 158 } 159 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)); 164 } 165 166 /* Fix cell offsets. */ 167 layout_fix_offsets(lc); 168 layout_fix_panes(w, w->sx, w->sy); 169 170 layout_print_cell(w->layout_root, __func__, 1); 171 172 server_redraw_window(w); 173 } 174 175 void 176 layout_set_even_v(struct window *w) 177 { 178 struct window_pane *wp; 179 struct layout_cell *lc, *lcnew; 180 u_int i, n, height, yoff; 181 182 layout_print_cell(w->layout_root, __func__, 1); 183 184 /* Get number of panes. */ 185 n = window_count_panes(w); 186 if (n <= 1) 187 return; 188 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; 194 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); 200 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); 209 210 i++; 211 yoff += height; 212 } 213 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)); 218 } 219 220 /* Fix cell offsets. */ 221 layout_fix_offsets(lc); 222 layout_fix_panes(w, w->sx, w->sy); 223 224 layout_print_cell(w->layout_root, __func__, 1); 225 226 server_redraw_window(w); 227 } 228 229 void 230 layout_set_main_h(struct window *w) 231 { 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; 236 237 layout_print_cell(w->layout_root, __func__, 1); 238 239 /* Get number of panes. */ 240 n = window_count_panes(w); 241 if (n <= 1) 242 return; 243 n--; /* take off main pane */ 244 245 /* How many rows and columns will be needed? */ 246 columns = w->sx / (PANE_MINIMUM + 1); /* maximum columns */ 247 rows = 1 + (n - 1) / columns; 248 columns = 1 + (n - 1) / rows; 249 width = w->sx / columns; 250 251 /* Get the main pane height and add one for separator line. */ 252 mainheight = options_get_number(&w->options, "main-pane-height") + 1; 253 if (mainheight < PANE_MINIMUM + 1) 254 mainheight = PANE_MINIMUM + 1; 255 256 /* Try and make everything fit. */ 257 totalrows = rows * (PANE_MINIMUM + 1) - 1; 258 if (mainheight + totalrows > w->sy) { 259 if (totalrows + PANE_MINIMUM + 1 > w->sy) 260 mainheight = PANE_MINIMUM + 2; 261 else 262 mainheight = w->sy - totalrows; 263 height = PANE_MINIMUM + 1; 264 } else 265 height = (w->sy - mainheight) / rows; 266 267 /* Free old tree and create a new root. */ 268 layout_free(w); 269 lc = w->layout_root = layout_create_cell(NULL); 270 layout_set_size(lc, w->sx, mainheight + rows * height, 0, 0); 271 layout_make_node(lc, LAYOUT_TOPBOTTOM); 272 273 /* Create the main pane. */ 274 lcmain = layout_create_cell(lc); 275 layout_set_size(lcmain, w->sx, mainheight - 1, 0, 0); 276 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); 277 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); 278 279 /* Create a grid of the remaining cells. */ 280 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); 281 for (j = 0; j < rows; j++) { 282 /* If this is the last cell, all done. */ 283 if (wp == NULL) 284 break; 285 286 /* Create the new row. */ 287 lcrow = layout_create_cell(lc); 288 layout_set_size(lcrow, w->sx, height - 1, 0, 0); 289 TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry); 290 291 /* If only one column, just use the row directly. */ 292 if (columns == 1) { 293 layout_make_leaf(lcrow, wp); 294 wp = TAILQ_NEXT(wp, entry); 295 continue; 296 } 297 298 /* Add in the columns. */ 299 layout_make_node(lcrow, LAYOUT_LEFTRIGHT); 300 for (i = 0; i < columns; i++) { 301 /* Create and add a pane cell. */ 302 lcchild = layout_create_cell(lcrow); 303 layout_set_size(lcchild, width - 1, height - 1, 0, 0); 304 layout_make_leaf(lcchild, wp); 305 TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry); 306 307 /* Move to the next cell. */ 308 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 309 break; 310 } 311 312 /* Adjust the row to fit the full width if necessary. */ 313 if (i == columns) 314 i--; 315 used = ((i + 1) * width) - 1; 316 if (w->sx <= used) 317 continue; 318 lcchild = TAILQ_LAST(&lcrow->cells, layout_cells); 319 layout_resize_adjust(lcchild, LAYOUT_LEFTRIGHT, w->sx - used); 320 } 321 322 /* Adjust the last row height to fit if necessary. */ 323 used = mainheight + (rows * height) - 1; 324 if (w->sy > used) { 325 lcrow = TAILQ_LAST(&lc->cells, layout_cells); 326 layout_resize_adjust(lcrow, LAYOUT_TOPBOTTOM, w->sy - used); 327 } 328 329 /* Fix cell offsets. */ 330 layout_fix_offsets(lc); 331 layout_fix_panes(w, w->sx, w->sy); 332 333 layout_print_cell(w->layout_root, __func__, 1); 334 335 server_redraw_window(w); 336 } 337 338 void 339 layout_set_main_v(struct window *w) 340 { 341 struct window_pane *wp; 342 struct layout_cell *lc, *lcmain, *lccolumn, *lcchild; 343 u_int n, mainwidth, width, height, used; 344 u_int i, j, columns, rows, totalcolumns; 345 346 layout_print_cell(w->layout_root, __func__, 1); 347 348 /* Get number of panes. */ 349 n = window_count_panes(w); 350 if (n <= 1) 351 return; 352 n--; /* take off main pane */ 353 354 /* How many rows and columns will be needed? */ 355 rows = w->sy / (PANE_MINIMUM + 1); /* maximum rows */ 356 columns = 1 + (n - 1) / rows; 357 rows = 1 + (n - 1) / columns; 358 height = w->sy / rows; 359 360 /* Get the main pane width and add one for separator line. */ 361 mainwidth = options_get_number(&w->options, "main-pane-width") + 1; 362 if (mainwidth < PANE_MINIMUM + 1) 363 mainwidth = PANE_MINIMUM + 1; 364 365 /* Try and make everything fit. */ 366 totalcolumns = columns * (PANE_MINIMUM + 1) - 1; 367 if (mainwidth + totalcolumns > w->sx) { 368 if (totalcolumns + PANE_MINIMUM + 1 > w->sx) 369 mainwidth = PANE_MINIMUM + 2; 370 else 371 mainwidth = w->sx - totalcolumns; 372 width = PANE_MINIMUM + 1; 373 } else 374 width = (w->sx - mainwidth) / columns; 375 376 /* Free old tree and create a new root. */ 377 layout_free(w); 378 lc = w->layout_root = layout_create_cell(NULL); 379 layout_set_size(lc, mainwidth + columns * width, w->sy, 0, 0); 380 layout_make_node(lc, LAYOUT_LEFTRIGHT); 381 382 /* Create the main pane. */ 383 lcmain = layout_create_cell(lc); 384 layout_set_size(lcmain, mainwidth - 1, w->sy, 0, 0); 385 layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes)); 386 TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry); 387 388 /* Create a grid of the remaining cells. */ 389 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry); 390 for (j = 0; j < columns; j++) { 391 /* If this is the last cell, all done. */ 392 if (wp == NULL) 393 break; 394 395 /* Create the new column. */ 396 lccolumn = layout_create_cell(lc); 397 layout_set_size(lccolumn, width - 1, w->sy, 0, 0); 398 TAILQ_INSERT_TAIL(&lc->cells, lccolumn, entry); 399 400 /* If only one row, just use the row directly. */ 401 if (rows == 1) { 402 layout_make_leaf(lccolumn, wp); 403 wp = TAILQ_NEXT(wp, entry); 404 continue; 405 } 406 407 /* Add in the rows. */ 408 layout_make_node(lccolumn, LAYOUT_TOPBOTTOM); 409 for (i = 0; i < rows; i++) { 410 /* Create and add a pane cell. */ 411 lcchild = layout_create_cell(lccolumn); 412 layout_set_size(lcchild, width - 1, height - 1, 0, 0); 413 layout_make_leaf(lcchild, wp); 414 TAILQ_INSERT_TAIL(&lccolumn->cells, lcchild, entry); 415 416 /* Move to the next cell. */ 417 if ((wp = TAILQ_NEXT(wp, entry)) == NULL) 418 break; 419 } 420 421 /* Adjust the column to fit the full height if necessary. */ 422 if (i == rows) 423 i--; 424 used = ((i + 1) * height) - 1; 425 if (w->sy <= used) 426 continue; 427 lcchild = TAILQ_LAST(&lccolumn->cells, layout_cells); 428 layout_resize_adjust(lcchild, LAYOUT_TOPBOTTOM, w->sy - used); 429 } 430 431 /* Adjust the last column width to fit if necessary. */ 432 used = mainwidth + (columns * width) - 1; 433 if (w->sx > used) { 434 lccolumn = TAILQ_LAST(&lc->cells, layout_cells); 435 layout_resize_adjust(lccolumn, LAYOUT_LEFTRIGHT, w->sx - used); 436 } 437 438 /* Fix cell offsets. */ 439 layout_fix_offsets(lc); 440 layout_fix_panes(w, w->sx, w->sy); 441 442 layout_print_cell(w->layout_root, __func__, 1); 443 444 server_redraw_window(w); 445 } 446