Annotation of src/usr.bin/tmux/layout.c, Revision 1.27
1.27 ! nicm 1: /* $OpenBSD: layout.c,v 1.26 2016/01/19 15:59:12 nicm Exp $ */
1.1 nicm 2:
3: /*
1.26 nicm 4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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:
1.3 nicm 21: #include <stdlib.h>
1.1 nicm 22:
23: #include "tmux.h"
24:
25: /*
1.3 nicm 26: * The window layout is a tree of cells each of which can be one of: a
27: * left-right container for a list of cells, a top-bottom container for a list
28: * of cells, or a container for a window pane.
1.1 nicm 29: *
1.3 nicm 30: * Each window has a pointer to the root of its layout tree (containing its
31: * panes), every pane has a pointer back to the cell containing it, and each
32: * cell a pointer to its parent cell.
1.1 nicm 33: */
34:
1.27 ! nicm 35: static int layout_resize_pane_grow(struct layout_cell *, enum layout_type,
! 36: int);
! 37: static int layout_resize_pane_shrink(struct layout_cell *,
! 38: enum layout_type, int);
! 39: static int layout_need_status(struct layout_cell *, int);
1.1 nicm 40:
1.3 nicm 41: struct layout_cell *
42: layout_create_cell(struct layout_cell *lcparent)
1.1 nicm 43: {
1.3 nicm 44: struct layout_cell *lc;
45:
46: lc = xmalloc(sizeof *lc);
47: lc->type = LAYOUT_WINDOWPANE;
48: lc->parent = lcparent;
49:
50: TAILQ_INIT(&lc->cells);
1.4 nicm 51:
1.3 nicm 52: lc->sx = UINT_MAX;
53: lc->sy = UINT_MAX;
1.4 nicm 54:
1.3 nicm 55: lc->xoff = UINT_MAX;
56: lc->yoff = UINT_MAX;
1.4 nicm 57:
1.3 nicm 58: lc->wp = NULL;
59:
60: return (lc);
1.1 nicm 61: }
62:
1.3 nicm 63: void
64: layout_free_cell(struct layout_cell *lc)
1.1 nicm 65: {
1.3 nicm 66: struct layout_cell *lcchild;
1.1 nicm 67:
1.3 nicm 68: switch (lc->type) {
69: case LAYOUT_LEFTRIGHT:
70: case LAYOUT_TOPBOTTOM:
71: while (!TAILQ_EMPTY(&lc->cells)) {
72: lcchild = TAILQ_FIRST(&lc->cells);
73: TAILQ_REMOVE(&lc->cells, lcchild, entry);
74: layout_free_cell(lcchild);
1.1 nicm 75: }
1.3 nicm 76: break;
77: case LAYOUT_WINDOWPANE:
78: if (lc->wp != NULL)
79: lc->wp->layout_cell = NULL;
80: break;
1.1 nicm 81: }
82:
1.14 nicm 83: free(lc);
1.1 nicm 84: }
85:
1.3 nicm 86: void
87: layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
1.1 nicm 88: {
1.3 nicm 89: struct layout_cell *lcchild;
1.1 nicm 90:
1.25 nicm 91: log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
92: " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
93: lc->sy);
1.3 nicm 94: switch (lc->type) {
95: case LAYOUT_LEFTRIGHT:
96: case LAYOUT_TOPBOTTOM:
97: TAILQ_FOREACH(lcchild, &lc->cells, entry)
98: layout_print_cell(lcchild, hdr, n + 1);
99: break;
100: case LAYOUT_WINDOWPANE:
101: break;
102: }
1.1 nicm 103: }
104:
105: void
1.25 nicm 106: layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff,
107: u_int yoff)
1.1 nicm 108: {
1.3 nicm 109: lc->sx = sx;
110: lc->sy = sy;
111:
112: lc->xoff = xoff;
113: lc->yoff = yoff;
1.1 nicm 114: }
115:
116: void
1.3 nicm 117: layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
1.1 nicm 118: {
1.3 nicm 119: lc->type = LAYOUT_WINDOWPANE;
120:
121: TAILQ_INIT(&lc->cells);
122:
123: wp->layout_cell = lc;
124: lc->wp = wp;
1.1 nicm 125: }
126:
127: void
1.3 nicm 128: layout_make_node(struct layout_cell *lc, enum layout_type type)
1.1 nicm 129: {
1.3 nicm 130: if (type == LAYOUT_WINDOWPANE)
131: fatalx("bad layout type");
132: lc->type = type;
133:
134: TAILQ_INIT(&lc->cells);
135:
136: if (lc->wp != NULL)
137: lc->wp->layout_cell = NULL;
138: lc->wp = NULL;
1.1 nicm 139: }
140:
1.3 nicm 141: /* Fix cell offsets based on their sizes. */
142: void
143: layout_fix_offsets(struct layout_cell *lc)
1.1 nicm 144: {
1.3 nicm 145: struct layout_cell *lcchild;
146: u_int xoff, yoff;
1.1 nicm 147:
1.3 nicm 148: if (lc->type == LAYOUT_LEFTRIGHT) {
149: xoff = lc->xoff;
150: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
151: lcchild->xoff = xoff;
152: lcchild->yoff = lc->yoff;
153: if (lcchild->type != LAYOUT_WINDOWPANE)
154: layout_fix_offsets(lcchild);
155: xoff += lcchild->sx + 1;
156: }
157: } else {
158: yoff = lc->yoff;
159: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
160: lcchild->xoff = lc->xoff;
161: lcchild->yoff = yoff;
162: if (lcchild->type != LAYOUT_WINDOWPANE)
163: layout_fix_offsets(lcchild);
164: yoff += lcchild->sy + 1;
165: }
166: }
1.1 nicm 167: }
168:
1.27 ! nicm 169: /*
! 170: * Returns 1 if we need to reserve space for the pane status line. This is the
! 171: * case for the most upper panes only.
! 172: */
! 173: static int
! 174: layout_need_status(struct layout_cell *lc, int at_top)
! 175: {
! 176: struct layout_cell *first_lc;
! 177:
! 178: if (lc->parent) {
! 179: if (lc->parent->type == LAYOUT_LEFTRIGHT)
! 180: return (layout_need_status(lc->parent, at_top));
! 181:
! 182: if (at_top)
! 183: first_lc = TAILQ_FIRST(&lc->parent->cells);
! 184: else
! 185: first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells);
! 186: if (lc == first_lc)
! 187: return (layout_need_status(lc->parent, at_top));
! 188: return (0);
! 189: }
! 190: return (1);
! 191: }
! 192:
1.3 nicm 193: /* Update pane offsets and sizes based on their cells. */
1.1 nicm 194: void
1.3 nicm 195: layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
1.1 nicm 196: {
197: struct window_pane *wp;
1.3 nicm 198: struct layout_cell *lc;
199: u_int sx, sy;
1.27 ! nicm 200: int shift, status, at_top;
1.1 nicm 201:
1.27 ! nicm 202: status = options_get_number(w->options, "pane-border-status");
! 203: at_top = (status == 1);
1.1 nicm 204: TAILQ_FOREACH(wp, &w->panes, entry) {
1.3 nicm 205: if ((lc = wp->layout_cell) == NULL)
206: continue;
1.27 ! nicm 207:
! 208: if (status != 0)
! 209: shift = layout_need_status(lc, at_top);
! 210: else
! 211: shift = 0;
! 212:
1.3 nicm 213: wp->xoff = lc->xoff;
214: wp->yoff = lc->yoff;
215:
1.27 ! nicm 216: if (shift && at_top)
! 217: wp->yoff += 1;
! 218:
1.3 nicm 219: /*
220: * Layout cells are limited by the smallest size of other cells
221: * within the same row or column; if this isn't the case
222: * resizing becomes difficult.
223: *
224: * However, panes do not have to take up their entire cell, so
225: * they can be cropped to the window edge if the layout
226: * overflows and they are partly visible.
227: *
228: * This stops cells being hidden unnecessarily.
229: */
230:
231: /*
232: * Work out the horizontal size. If the pane is actually
233: * outside the window or the entire pane is already visible,
234: * don't crop.
235: */
236: if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx)
237: sx = lc->sx;
238: else {
239: sx = wsx - lc->xoff;
240: if (sx < 1)
241: sx = lc->sx;
242: }
1.4 nicm 243:
244: /*
1.3 nicm 245: * Similarly for the vertical size; the minimum vertical size
246: * is two because scroll regions cannot be one line.
247: */
248: if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
249: sy = lc->sy;
1.2 nicm 250: else {
1.3 nicm 251: sy = wsy - lc->yoff;
252: if (sy < 2)
253: sy = lc->sy;
1.2 nicm 254: }
1.3 nicm 255:
1.27 ! nicm 256: if (shift)
! 257: sy -= 1;
! 258:
1.3 nicm 259: window_pane_resize(wp, sx, sy);
1.1 nicm 260: }
261: }
262:
1.6 nicm 263: /* Count the number of available cells in a layout. */
264: u_int
265: layout_count_cells(struct layout_cell *lc)
266: {
267: struct layout_cell *lcchild;
268: u_int n;
269:
270: switch (lc->type) {
271: case LAYOUT_WINDOWPANE:
272: return (1);
273: case LAYOUT_LEFTRIGHT:
274: case LAYOUT_TOPBOTTOM:
275: n = 0;
276: TAILQ_FOREACH(lcchild, &lc->cells, entry)
277: n += layout_count_cells(lcchild);
278: return (n);
279: default:
280: fatalx("bad layout type");
281: }
282: }
283:
1.3 nicm 284: /* Calculate how much size is available to be removed from a cell. */
285: u_int
286: layout_resize_check(struct layout_cell *lc, enum layout_type type)
1.1 nicm 287: {
1.3 nicm 288: struct layout_cell *lcchild;
289: u_int available, minimum;
1.1 nicm 290:
1.3 nicm 291: if (lc->type == LAYOUT_WINDOWPANE) {
292: /* Space available in this cell only. */
293: if (type == LAYOUT_LEFTRIGHT)
294: available = lc->sx;
295: else
296: available = lc->sy;
1.4 nicm 297:
1.3 nicm 298: if (available > PANE_MINIMUM)
299: available -= PANE_MINIMUM;
300: else
301: available = 0;
302: } else if (lc->type == type) {
303: /* Same type: total of available space in all child cells. */
304: available = 0;
305: TAILQ_FOREACH(lcchild, &lc->cells, entry)
306: available += layout_resize_check(lcchild, type);
307: } else {
308: /* Different type: minimum of available space in child cells. */
309: minimum = UINT_MAX;
310: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
311: available = layout_resize_check(lcchild, type);
312: if (available < minimum)
313: minimum = available;
314: }
315: available = minimum;
1.2 nicm 316: }
317:
1.3 nicm 318: return (available);
319: }
1.1 nicm 320:
1.3 nicm 321: /*
322: * Adjust cell size evenly, including altering its children. This function
323: * expects the change to have already been bounded to the space available.
324: */
325: void
326: layout_resize_adjust(struct layout_cell *lc, enum layout_type type, int change)
327: {
328: struct layout_cell *lcchild;
1.1 nicm 329:
1.3 nicm 330: /* Adjust the cell size. */
331: if (type == LAYOUT_LEFTRIGHT)
332: lc->sx += change;
333: else
334: lc->sy += change;
1.4 nicm 335:
1.3 nicm 336: /* If this is a leaf cell, that is all that is necessary. */
337: if (type == LAYOUT_WINDOWPANE)
338: return;
339:
340: /* Child cell runs in a different direction. */
341: if (lc->type != type) {
342: TAILQ_FOREACH(lcchild, &lc->cells, entry)
343: layout_resize_adjust(lcchild, type, change);
344: return;
1.1 nicm 345: }
346:
1.4 nicm 347: /*
348: * Child cell runs in the same direction. Adjust each child equally
1.3 nicm 349: * until no further change is possible.
350: */
351: while (change != 0) {
352: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
353: if (change == 0)
354: break;
355: if (change > 0) {
356: layout_resize_adjust(lcchild, type, 1);
357: change--;
358: continue;
359: }
360: if (layout_resize_check(lcchild, type) > 0) {
361: layout_resize_adjust(lcchild, type, -1);
362: change++;
363: }
364: }
1.1 nicm 365: }
366: }
367:
1.6 nicm 368: /* Destroy a cell and redistribute the space. */
369: void
370: layout_destroy_cell(struct layout_cell *lc, struct layout_cell **lcroot)
371: {
372: struct layout_cell *lcother, *lcparent;
373:
374: /*
375: * If no parent, this is the last pane so window close is imminent and
376: * there is no need to resize anything.
377: */
378: lcparent = lc->parent;
379: if (lcparent == NULL) {
380: layout_free_cell(lc);
381: *lcroot = NULL;
382: return;
383: }
384:
385: /* Merge the space into the previous or next cell. */
386: if (lc == TAILQ_FIRST(&lcparent->cells))
387: lcother = TAILQ_NEXT(lc, entry);
388: else
389: lcother = TAILQ_PREV(lc, layout_cells, entry);
390: if (lcparent->type == LAYOUT_LEFTRIGHT)
391: layout_resize_adjust(lcother, lcparent->type, lc->sx + 1);
392: else
393: layout_resize_adjust(lcother, lcparent->type, lc->sy + 1);
394:
395: /* Remove this from the parent's list. */
396: TAILQ_REMOVE(&lcparent->cells, lc, entry);
397: layout_free_cell(lc);
398:
399: /*
400: * If the parent now has one cell, remove the parent from the tree and
401: * replace it by that cell.
402: */
403: lc = TAILQ_FIRST(&lcparent->cells);
404: if (TAILQ_NEXT(lc, entry) == NULL) {
405: TAILQ_REMOVE(&lcparent->cells, lc, entry);
406:
407: lc->parent = lcparent->parent;
408: if (lc->parent == NULL) {
409: lc->xoff = 0; lc->yoff = 0;
410: *lcroot = lc;
411: } else
412: TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
413:
414: layout_free_cell(lcparent);
415: }
416: }
417:
1.1 nicm 418: void
1.18 nicm 419: layout_init(struct window *w, struct window_pane *wp)
1.1 nicm 420: {
1.3 nicm 421: struct layout_cell *lc;
1.1 nicm 422:
1.3 nicm 423: lc = w->layout_root = layout_create_cell(NULL);
424: layout_set_size(lc, w->sx, w->sy, 0, 0);
1.18 nicm 425: layout_make_leaf(lc, wp);
1.1 nicm 426:
1.3 nicm 427: layout_fix_panes(w, w->sx, w->sy);
428: }
1.2 nicm 429:
1.3 nicm 430: void
431: layout_free(struct window *w)
432: {
433: layout_free_cell(w->layout_root);
434: }
1.1 nicm 435:
1.3 nicm 436: /* Resize the entire layout after window resize. */
437: void
438: layout_resize(struct window *w, u_int sx, u_int sy)
439: {
440: struct layout_cell *lc = w->layout_root;
441: int xlimit, ylimit, xchange, ychange;
1.1 nicm 442:
1.4 nicm 443: /*
1.3 nicm 444: * Adjust horizontally. Do not attempt to reduce the layout lower than
445: * the minimum (more than the amount returned by layout_resize_check).
1.4 nicm 446: *
1.3 nicm 447: * This can mean that the window size is smaller than the total layout
448: * size: redrawing this is handled at a higher level, but it does leave
449: * a problem with growing the window size here: if the current size is
450: * < the minimum, growing proportionately by adding to each pane is
451: * wrong as it would keep the layout size larger than the window size.
452: * Instead, spread the difference between the minimum and the new size
453: * out proportionately - this should leave the layout fitting the new
454: * window size.
455: */
456: xchange = sx - w->sx;
457: xlimit = layout_resize_check(lc, LAYOUT_LEFTRIGHT);
458: if (xchange < 0 && xchange < -xlimit)
459: xchange = -xlimit;
460: if (xlimit == 0) {
461: if (sx <= lc->sx) /* lc->sx is minimum possible */
462: xchange = 0;
1.1 nicm 463: else
1.3 nicm 464: xchange = sx - lc->sx;
1.1 nicm 465: }
1.3 nicm 466: if (xchange != 0)
467: layout_resize_adjust(lc, LAYOUT_LEFTRIGHT, xchange);
1.1 nicm 468:
1.3 nicm 469: /* Adjust vertically in a similar fashion. */
470: ychange = sy - w->sy;
471: ylimit = layout_resize_check(lc, LAYOUT_TOPBOTTOM);
472: if (ychange < 0 && ychange < -ylimit)
473: ychange = -ylimit;
474: if (ylimit == 0) {
475: if (sy <= lc->sy) /* lc->sy is minimum possible */
476: ychange = 0;
477: else
478: ychange = sy - lc->sy;
1.1 nicm 479: }
1.3 nicm 480: if (ychange != 0)
481: layout_resize_adjust(lc, LAYOUT_TOPBOTTOM, ychange);
1.4 nicm 482:
1.3 nicm 483: /* Fix cell offsets. */
484: layout_fix_offsets(lc);
485: layout_fix_panes(w, sx, sy);
1.1 nicm 486: }
487:
1.17 nicm 488: /* Resize a pane to an absolute size. */
489: void
490: layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
491: u_int new_size)
492: {
493: struct layout_cell *lc, *lcparent;
494: int change, size;
495:
496: lc = wp->layout_cell;
497:
498: /* Find next parent of the same type. */
499: lcparent = lc->parent;
500: while (lcparent != NULL && lcparent->type != type) {
501: lc = lcparent;
502: lcparent = lc->parent;
503: }
504: if (lcparent == NULL)
505: return;
506:
507: /* Work out the size adjustment. */
508: if (type == LAYOUT_LEFTRIGHT)
509: size = lc->sx;
510: else
511: size = lc->sy;
512: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
513: change = size - new_size;
514: else
515: change = new_size - size;
516:
517: /* Resize the pane. */
518: layout_resize_pane(wp, type, change);
519: }
520:
1.3 nicm 521: /* Resize a single pane within the layout. */
1.1 nicm 522: void
1.3 nicm 523: layout_resize_pane(struct window_pane *wp, enum layout_type type, int change)
1.1 nicm 524: {
1.3 nicm 525: struct layout_cell *lc, *lcparent;
526: int needed, size;
1.1 nicm 527:
1.3 nicm 528: lc = wp->layout_cell;
1.1 nicm 529:
1.3 nicm 530: /* Find next parent of the same type. */
531: lcparent = lc->parent;
532: while (lcparent != NULL && lcparent->type != type) {
533: lc = lcparent;
534: lcparent = lc->parent;
535: }
536: if (lcparent == NULL)
537: return;
538:
539: /* If this is the last cell, move back one. */
540: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
541: lc = TAILQ_PREV(lc, layout_cells, entry);
542:
543: /* Grow or shrink the cell. */
544: needed = change;
545: while (needed != 0) {
546: if (change > 0) {
547: size = layout_resize_pane_grow(lc, type, needed);
548: needed -= size;
549: } else {
550: size = layout_resize_pane_shrink(lc, type, needed);
551: needed += size;
552: }
1.1 nicm 553:
1.3 nicm 554: if (size == 0) /* no more change possible */
555: break;
556: }
1.4 nicm 557:
1.3 nicm 558: /* Fix cell offsets. */
559: layout_fix_offsets(wp->window->layout_root);
560: layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
1.10 nicm 561: notify_window_layout_changed(wp->window);
1.3 nicm 562: }
1.1 nicm 563:
1.17 nicm 564: /* Helper function to grow pane. */
1.27 ! nicm 565: static int
1.25 nicm 566: layout_resize_pane_grow(struct layout_cell *lc, enum layout_type type,
567: int needed)
1.3 nicm 568: {
569: struct layout_cell *lcadd, *lcremove;
570: u_int size;
571:
572: /* Growing. Always add to the current cell. */
573: lcadd = lc;
1.4 nicm 574:
1.3 nicm 575: /* Look towards the tail for a suitable cell for reduction. */
576: lcremove = TAILQ_NEXT(lc, entry);
577: while (lcremove != NULL) {
578: size = layout_resize_check(lcremove, type);
579: if (size > 0)
580: break;
1.4 nicm 581: lcremove = TAILQ_NEXT(lcremove, entry);
1.1 nicm 582: }
583:
1.3 nicm 584: /* If none found, look towards the head. */
585: if (lcremove == NULL) {
586: lcremove = TAILQ_PREV(lc, layout_cells, entry);
587: while (lcremove != NULL) {
588: size = layout_resize_check(lcremove, type);
589: if (size > 0)
590: break;
591: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
1.1 nicm 592: }
1.3 nicm 593: if (lcremove == NULL)
594: return (0);
595: }
1.1 nicm 596:
1.3 nicm 597: /* Change the cells. */
598: if (size > (u_int) needed)
599: size = needed;
600: layout_resize_adjust(lcadd, type, size);
601: layout_resize_adjust(lcremove, type, -size);
602: return (size);
603: }
1.1 nicm 604:
1.17 nicm 605: /* Helper function to shrink pane. */
1.27 ! nicm 606: static int
1.25 nicm 607: layout_resize_pane_shrink(struct layout_cell *lc, enum layout_type type,
608: int needed)
1.3 nicm 609: {
610: struct layout_cell *lcadd, *lcremove;
611: u_int size;
1.1 nicm 612:
1.3 nicm 613: /* Shrinking. Find cell to remove from by walking towards head. */
614: lcremove = lc;
615: do {
616: size = layout_resize_check(lcremove, type);
617: if (size != 0)
1.1 nicm 618: break;
1.3 nicm 619: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
620: } while (lcremove != NULL);
621: if (lcremove == NULL)
622: return (0);
623:
624: /* And add onto the next cell (from the original cell). */
625: lcadd = TAILQ_NEXT(lc, entry);
626: if (lcadd == NULL)
627: return (0);
628:
629: /* Change the cells. */
630: if (size > (u_int) -needed)
631: size = -needed;
632: layout_resize_adjust(lcadd, type, size);
633: layout_resize_adjust(lcremove, type, -size);
634: return (size);
1.1 nicm 635: }
636:
1.5 nicm 637: /* Assign window pane to newly split cell. */
638: void
639: layout_assign_pane(struct layout_cell *lc, struct window_pane *wp)
640: {
641: layout_make_leaf(lc, wp);
642: layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
643: }
644:
645: /*
646: * Split a pane into two. size is a hint, or -1 for default half/half
647: * split. This must be followed by layout_assign_pane before much else happens!
648: **/
649: struct layout_cell *
1.25 nicm 650: layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
651: int insert_before)
1.1 nicm 652: {
1.9 nicm 653: struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
1.3 nicm 654: u_int sx, sy, xoff, yoff, size1, size2;
1.1 nicm 655:
1.3 nicm 656: lc = wp->layout_cell;
1.1 nicm 657:
1.3 nicm 658: /* Copy the old cell size. */
659: sx = lc->sx;
660: sy = lc->sy;
661: xoff = lc->xoff;
662: yoff = lc->yoff;
663:
664: /* Check there is enough space for the two new panes. */
665: switch (type) {
666: case LAYOUT_LEFTRIGHT:
667: if (sx < PANE_MINIMUM * 2 + 1)
1.5 nicm 668: return (NULL);
1.3 nicm 669: break;
670: case LAYOUT_TOPBOTTOM:
671: if (sy < PANE_MINIMUM * 2 + 1)
1.5 nicm 672: return (NULL);
1.3 nicm 673: break;
674: default:
675: fatalx("bad layout type");
676: }
1.4 nicm 677:
1.3 nicm 678: if (lc->parent != NULL && lc->parent->type == type) {
679: /*
680: * If the parent exists and is of the same type as the split,
681: * create a new cell and insert it after this one.
682: */
683:
684: /* Create the new child cell. */
1.9 nicm 685: lcparent = lc->parent;
686: lcnew = layout_create_cell(lcparent);
687: if (insert_before)
688: TAILQ_INSERT_BEFORE(lc, lcnew, entry);
689: else
690: TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
1.3 nicm 691: } else {
692: /*
693: * Otherwise create a new parent and insert it.
694: */
1.4 nicm 695:
1.3 nicm 696: /* Create and insert the replacement parent. */
697: lcparent = layout_create_cell(lc->parent);
698: layout_make_node(lcparent, type);
699: layout_set_size(lcparent, sx, sy, xoff, yoff);
700: if (lc->parent == NULL)
701: wp->window->layout_root = lcparent;
702: else
703: TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
1.4 nicm 704:
1.3 nicm 705: /* Insert the old cell. */
706: lc->parent = lcparent;
707: TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
1.4 nicm 708:
1.3 nicm 709: /* Create the new child cell. */
710: lcnew = layout_create_cell(lcparent);
1.9 nicm 711: if (insert_before)
712: TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
713: else
714: TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
715: }
716: if (insert_before) {
717: lc1 = lcnew;
718: lc2 = lc;
719: } else {
720: lc1 = lc;
721: lc2 = lcnew;
1.3 nicm 722: }
723:
724: /* Set new cell sizes. size is the target size or -1 for middle split,
725: * size1 is the size of the top/left and size2 the bottom/right.
726: */
727: switch (type) {
728: case LAYOUT_LEFTRIGHT:
729: if (size < 0)
730: size2 = ((sx + 1) / 2) - 1;
1.24 nicm 731: else if (insert_before)
732: size2 = sx - size - 1;
1.4 nicm 733: else
1.3 nicm 734: size2 = size;
735: if (size2 < PANE_MINIMUM)
736: size2 = PANE_MINIMUM;
737: else if (size2 > sx - 2)
738: size2 = sx - 2;
739: size1 = sx - 1 - size2;
1.9 nicm 740: layout_set_size(lc1, size1, sy, xoff, yoff);
741: layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
1.3 nicm 742: break;
743: case LAYOUT_TOPBOTTOM:
744: if (size < 0)
745: size2 = ((sy + 1) / 2) - 1;
1.24 nicm 746: else if (insert_before)
747: size2 = sy - size - 1;
1.3 nicm 748: else
749: size2 = size;
750: if (size2 < PANE_MINIMUM)
751: size2 = PANE_MINIMUM;
752: else if (size2 > sy - 2)
753: size2 = sy - 2;
754: size1 = sy - 1 - size2;
1.9 nicm 755: layout_set_size(lc1, sx, size1, xoff, yoff);
756: layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
1.3 nicm 757: break;
758: default:
759: fatalx("bad layout type");
760: }
761:
762: /* Assign the panes. */
763: layout_make_leaf(lc, wp);
1.1 nicm 764:
1.5 nicm 765: return (lcnew);
1.3 nicm 766: }
1.1 nicm 767:
1.6 nicm 768: /* Destroy the cell associated with a pane. */
1.3 nicm 769: void
770: layout_close_pane(struct window_pane *wp)
771: {
1.6 nicm 772: /* Remove the cell. */
773: layout_destroy_cell(wp->layout_cell, &wp->window->layout_root);
1.1 nicm 774:
1.6 nicm 775: /* Fix pane offsets and sizes. */
776: if (wp->window->layout_root != NULL) {
777: layout_fix_offsets(wp->window->layout_root);
778: layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
1.3 nicm 779: }
1.10 nicm 780: notify_window_layout_changed(wp->window);
1.1 nicm 781: }