Annotation of embedaddon/tmux/layout.c, revision 1.1.1.1
1.1 misho 1: /* $OpenBSD$ */
2:
3: /*
4: * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
5: * Copyright (c) 2016 Stephen Kent <smkent@smkent.net>
6: *
7: * Permission to use, copy, modify, and distribute this software for any
8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
10: *
11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18: */
19:
20: #include <sys/types.h>
21:
22: #include <stdlib.h>
23:
24: #include "tmux.h"
25:
26: /*
27: * The window layout is a tree of cells each of which can be one of: a
28: * left-right container for a list of cells, a top-bottom container for a list
29: * of cells, or a container for a window pane.
30: *
31: * Each window has a pointer to the root of its layout tree (containing its
32: * panes), every pane has a pointer back to the cell containing it, and each
33: * cell a pointer to its parent cell.
34: */
35:
36: static u_int layout_resize_check(struct window *, struct layout_cell *,
37: enum layout_type);
38: static int layout_resize_pane_grow(struct window *, struct layout_cell *,
39: enum layout_type, int, int);
40: static int layout_resize_pane_shrink(struct window *, struct layout_cell *,
41: enum layout_type, int);
42: static int layout_need_status(struct layout_cell *, int);
43: static u_int layout_new_pane_size(struct window *, u_int,
44: struct layout_cell *, enum layout_type, u_int, u_int,
45: u_int);
46: static int layout_set_size_check(struct window *, struct layout_cell *,
47: enum layout_type, int);
48: static void layout_resize_child_cells(struct window *,
49: struct layout_cell *);
50:
51: struct layout_cell *
52: layout_create_cell(struct layout_cell *lcparent)
53: {
54: struct layout_cell *lc;
55:
56: lc = xmalloc(sizeof *lc);
57: lc->type = LAYOUT_WINDOWPANE;
58: lc->parent = lcparent;
59:
60: TAILQ_INIT(&lc->cells);
61:
62: lc->sx = UINT_MAX;
63: lc->sy = UINT_MAX;
64:
65: lc->xoff = UINT_MAX;
66: lc->yoff = UINT_MAX;
67:
68: lc->wp = NULL;
69:
70: return (lc);
71: }
72:
73: void
74: layout_free_cell(struct layout_cell *lc)
75: {
76: struct layout_cell *lcchild;
77:
78: switch (lc->type) {
79: case LAYOUT_LEFTRIGHT:
80: case LAYOUT_TOPBOTTOM:
81: while (!TAILQ_EMPTY(&lc->cells)) {
82: lcchild = TAILQ_FIRST(&lc->cells);
83: TAILQ_REMOVE(&lc->cells, lcchild, entry);
84: layout_free_cell(lcchild);
85: }
86: break;
87: case LAYOUT_WINDOWPANE:
88: if (lc->wp != NULL)
89: lc->wp->layout_cell = NULL;
90: break;
91: }
92:
93: free(lc);
94: }
95:
96: void
97: layout_print_cell(struct layout_cell *lc, const char *hdr, u_int n)
98: {
99: struct layout_cell *lcchild;
100:
101: log_debug("%s:%*s%p type %u [parent %p] wp=%p [%u,%u %ux%u]", hdr, n,
102: " ", lc, lc->type, lc->parent, lc->wp, lc->xoff, lc->yoff, lc->sx,
103: lc->sy);
104: switch (lc->type) {
105: case LAYOUT_LEFTRIGHT:
106: case LAYOUT_TOPBOTTOM:
107: TAILQ_FOREACH(lcchild, &lc->cells, entry)
108: layout_print_cell(lcchild, hdr, n + 1);
109: break;
110: case LAYOUT_WINDOWPANE:
111: break;
112: }
113: }
114:
115: void
116: layout_set_size(struct layout_cell *lc, u_int sx, u_int sy, u_int xoff,
117: u_int yoff)
118: {
119: lc->sx = sx;
120: lc->sy = sy;
121:
122: lc->xoff = xoff;
123: lc->yoff = yoff;
124: }
125:
126: void
127: layout_make_leaf(struct layout_cell *lc, struct window_pane *wp)
128: {
129: lc->type = LAYOUT_WINDOWPANE;
130:
131: TAILQ_INIT(&lc->cells);
132:
133: wp->layout_cell = lc;
134: lc->wp = wp;
135: }
136:
137: void
138: layout_make_node(struct layout_cell *lc, enum layout_type type)
139: {
140: if (type == LAYOUT_WINDOWPANE)
141: fatalx("bad layout type");
142: lc->type = type;
143:
144: TAILQ_INIT(&lc->cells);
145:
146: if (lc->wp != NULL)
147: lc->wp->layout_cell = NULL;
148: lc->wp = NULL;
149: }
150:
151: /* Fix cell offsets based on their sizes. */
152: void
153: layout_fix_offsets(struct layout_cell *lc)
154: {
155: struct layout_cell *lcchild;
156: u_int xoff, yoff;
157:
158: if (lc->type == LAYOUT_LEFTRIGHT) {
159: xoff = lc->xoff;
160: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
161: lcchild->xoff = xoff;
162: lcchild->yoff = lc->yoff;
163: if (lcchild->type != LAYOUT_WINDOWPANE)
164: layout_fix_offsets(lcchild);
165: xoff += lcchild->sx + 1;
166: }
167: } else {
168: yoff = lc->yoff;
169: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
170: lcchild->xoff = lc->xoff;
171: lcchild->yoff = yoff;
172: if (lcchild->type != LAYOUT_WINDOWPANE)
173: layout_fix_offsets(lcchild);
174: yoff += lcchild->sy + 1;
175: }
176: }
177: }
178:
179: /*
180: * Returns 1 if we need to reserve space for the pane status line. This is the
181: * case for the most upper panes only.
182: */
183: static int
184: layout_need_status(struct layout_cell *lc, int at_top)
185: {
186: struct layout_cell *first_lc;
187:
188: if (lc->parent) {
189: if (lc->parent->type == LAYOUT_LEFTRIGHT)
190: return (layout_need_status(lc->parent, at_top));
191:
192: if (at_top)
193: first_lc = TAILQ_FIRST(&lc->parent->cells);
194: else
195: first_lc = TAILQ_LAST(&lc->parent->cells,layout_cells);
196: if (lc == first_lc)
197: return (layout_need_status(lc->parent, at_top));
198: return (0);
199: }
200: return (1);
201: }
202:
203: /* Update pane offsets and sizes based on their cells. */
204: void
205: layout_fix_panes(struct window *w, u_int wsx, u_int wsy)
206: {
207: struct window_pane *wp;
208: struct layout_cell *lc;
209: u_int sx, sy;
210: int shift, status, at_top;
211:
212: status = options_get_number(w->options, "pane-border-status");
213: at_top = (status == 1);
214: TAILQ_FOREACH(wp, &w->panes, entry) {
215: if ((lc = wp->layout_cell) == NULL)
216: continue;
217:
218: if (status != 0)
219: shift = layout_need_status(lc, at_top);
220: else
221: shift = 0;
222:
223: wp->xoff = lc->xoff;
224: wp->yoff = lc->yoff;
225:
226: if (shift && at_top)
227: wp->yoff += 1;
228:
229: /*
230: * Layout cells are limited by the smallest size of other cells
231: * within the same row or column; if this isn't the case
232: * resizing becomes difficult.
233: *
234: * However, panes do not have to take up their entire cell, so
235: * they can be cropped to the window edge if the layout
236: * overflows and they are partly visible.
237: *
238: * This stops cells being hidden unnecessarily.
239: */
240:
241: /*
242: * Work out the horizontal size. If the pane is actually
243: * outside the window or the entire pane is already visible,
244: * don't crop.
245: */
246: if (lc->xoff >= wsx || lc->xoff + lc->sx < wsx)
247: sx = lc->sx;
248: else {
249: sx = wsx - lc->xoff;
250: if (sx < 1)
251: sx = lc->sx;
252: }
253:
254: /*
255: * Similarly for the vertical size; the minimum vertical size
256: * is two because scroll regions cannot be one line.
257: */
258: if (lc->yoff >= wsy || lc->yoff + lc->sy < wsy)
259: sy = lc->sy;
260: else {
261: sy = wsy - lc->yoff;
262: if (sy < 2)
263: sy = lc->sy;
264: }
265:
266: if (shift)
267: sy -= 1;
268:
269: window_pane_resize(wp, sx, sy);
270: }
271: }
272:
273: /* Count the number of available cells in a layout. */
274: u_int
275: layout_count_cells(struct layout_cell *lc)
276: {
277: struct layout_cell *lcchild;
278: u_int count;
279:
280: switch (lc->type) {
281: case LAYOUT_WINDOWPANE:
282: return (1);
283: case LAYOUT_LEFTRIGHT:
284: case LAYOUT_TOPBOTTOM:
285: count = 0;
286: TAILQ_FOREACH(lcchild, &lc->cells, entry)
287: count += layout_count_cells(lcchild);
288: return (count);
289: default:
290: fatalx("bad layout type");
291: }
292: }
293:
294: /* Calculate how much size is available to be removed from a cell. */
295: static u_int
296: layout_resize_check(struct window *w, struct layout_cell *lc,
297: enum layout_type type)
298: {
299: struct layout_cell *lcchild;
300: u_int available, minimum;
301:
302: if (lc->type == LAYOUT_WINDOWPANE) {
303: /* Space available in this cell only. */
304: minimum = PANE_MINIMUM;
305: if (type == LAYOUT_LEFTRIGHT)
306: available = lc->sx;
307: else {
308: available = lc->sy;
309: minimum += layout_need_status(lc,
310: options_get_number(w->options,
311: "pane-border-status") == 1);
312: }
313: if (available > minimum)
314: available -= minimum;
315: else
316: available = 0;
317: } else if (lc->type == type) {
318: /* Same type: total of available space in all child cells. */
319: available = 0;
320: TAILQ_FOREACH(lcchild, &lc->cells, entry)
321: available += layout_resize_check(w, lcchild, type);
322: } else {
323: /* Different type: minimum of available space in child cells. */
324: minimum = UINT_MAX;
325: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
326: available = layout_resize_check(w, lcchild, type);
327: if (available < minimum)
328: minimum = available;
329: }
330: available = minimum;
331: }
332:
333: return (available);
334: }
335:
336: /*
337: * Adjust cell size evenly, including altering its children. This function
338: * expects the change to have already been bounded to the space available.
339: */
340: void
341: layout_resize_adjust(struct window *w, struct layout_cell *lc,
342: enum layout_type type, int change)
343: {
344: struct layout_cell *lcchild;
345:
346: /* Adjust the cell size. */
347: if (type == LAYOUT_LEFTRIGHT)
348: lc->sx += change;
349: else
350: lc->sy += change;
351:
352: /* If this is a leaf cell, that is all that is necessary. */
353: if (type == LAYOUT_WINDOWPANE)
354: return;
355:
356: /* Child cell runs in a different direction. */
357: if (lc->type != type) {
358: TAILQ_FOREACH(lcchild, &lc->cells, entry)
359: layout_resize_adjust(w, lcchild, type, change);
360: return;
361: }
362:
363: /*
364: * Child cell runs in the same direction. Adjust each child equally
365: * until no further change is possible.
366: */
367: while (change != 0) {
368: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
369: if (change == 0)
370: break;
371: if (change > 0) {
372: layout_resize_adjust(w, lcchild, type, 1);
373: change--;
374: continue;
375: }
376: if (layout_resize_check(w, lcchild, type) > 0) {
377: layout_resize_adjust(w, lcchild, type, -1);
378: change++;
379: }
380: }
381: }
382: }
383:
384: /* Destroy a cell and redistribute the space. */
385: void
386: layout_destroy_cell(struct window *w, struct layout_cell *lc,
387: struct layout_cell **lcroot)
388: {
389: struct layout_cell *lcother, *lcparent;
390:
391: /*
392: * If no parent, this is the last pane so window close is imminent and
393: * there is no need to resize anything.
394: */
395: lcparent = lc->parent;
396: if (lcparent == NULL) {
397: layout_free_cell(lc);
398: *lcroot = NULL;
399: return;
400: }
401:
402: /* Merge the space into the previous or next cell. */
403: if (lc == TAILQ_FIRST(&lcparent->cells))
404: lcother = TAILQ_NEXT(lc, entry);
405: else
406: lcother = TAILQ_PREV(lc, layout_cells, entry);
407: if (lcparent->type == LAYOUT_LEFTRIGHT)
408: layout_resize_adjust(w, lcother, lcparent->type, lc->sx + 1);
409: else
410: layout_resize_adjust(w, lcother, lcparent->type, lc->sy + 1);
411:
412: /* Remove this from the parent's list. */
413: TAILQ_REMOVE(&lcparent->cells, lc, entry);
414: layout_free_cell(lc);
415:
416: /*
417: * If the parent now has one cell, remove the parent from the tree and
418: * replace it by that cell.
419: */
420: lc = TAILQ_FIRST(&lcparent->cells);
421: if (TAILQ_NEXT(lc, entry) == NULL) {
422: TAILQ_REMOVE(&lcparent->cells, lc, entry);
423:
424: lc->parent = lcparent->parent;
425: if (lc->parent == NULL) {
426: lc->xoff = 0; lc->yoff = 0;
427: *lcroot = lc;
428: } else
429: TAILQ_REPLACE(&lc->parent->cells, lcparent, lc, entry);
430:
431: layout_free_cell(lcparent);
432: }
433: }
434:
435: void
436: layout_init(struct window *w, struct window_pane *wp)
437: {
438: struct layout_cell *lc;
439:
440: lc = w->layout_root = layout_create_cell(NULL);
441: layout_set_size(lc, w->sx, w->sy, 0, 0);
442: layout_make_leaf(lc, wp);
443:
444: layout_fix_panes(w, w->sx, w->sy);
445: }
446:
447: void
448: layout_free(struct window *w)
449: {
450: layout_free_cell(w->layout_root);
451: }
452:
453: /* Resize the entire layout after window resize. */
454: void
455: layout_resize(struct window *w, u_int sx, u_int sy)
456: {
457: struct layout_cell *lc = w->layout_root;
458: int xlimit, ylimit, xchange, ychange;
459:
460: /*
461: * Adjust horizontally. Do not attempt to reduce the layout lower than
462: * the minimum (more than the amount returned by layout_resize_check).
463: *
464: * This can mean that the window size is smaller than the total layout
465: * size: redrawing this is handled at a higher level, but it does leave
466: * a problem with growing the window size here: if the current size is
467: * < the minimum, growing proportionately by adding to each pane is
468: * wrong as it would keep the layout size larger than the window size.
469: * Instead, spread the difference between the minimum and the new size
470: * out proportionately - this should leave the layout fitting the new
471: * window size.
472: */
473: xchange = sx - w->sx;
474: xlimit = layout_resize_check(w, lc, LAYOUT_LEFTRIGHT);
475: if (xchange < 0 && xchange < -xlimit)
476: xchange = -xlimit;
477: if (xlimit == 0) {
478: if (sx <= lc->sx) /* lc->sx is minimum possible */
479: xchange = 0;
480: else
481: xchange = sx - lc->sx;
482: }
483: if (xchange != 0)
484: layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, xchange);
485:
486: /* Adjust vertically in a similar fashion. */
487: ychange = sy - w->sy;
488: ylimit = layout_resize_check(w, lc, LAYOUT_TOPBOTTOM);
489: if (ychange < 0 && ychange < -ylimit)
490: ychange = -ylimit;
491: if (ylimit == 0) {
492: if (sy <= lc->sy) /* lc->sy is minimum possible */
493: ychange = 0;
494: else
495: ychange = sy - lc->sy;
496: }
497: if (ychange != 0)
498: layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, ychange);
499:
500: /* Fix cell offsets. */
501: layout_fix_offsets(lc);
502: layout_fix_panes(w, sx, sy);
503: }
504:
505: /* Resize a pane to an absolute size. */
506: void
507: layout_resize_pane_to(struct window_pane *wp, enum layout_type type,
508: u_int new_size)
509: {
510: struct layout_cell *lc, *lcparent;
511: int change, size;
512:
513: lc = wp->layout_cell;
514:
515: /* Find next parent of the same type. */
516: lcparent = lc->parent;
517: while (lcparent != NULL && lcparent->type != type) {
518: lc = lcparent;
519: lcparent = lc->parent;
520: }
521: if (lcparent == NULL)
522: return;
523:
524: /* Work out the size adjustment. */
525: if (type == LAYOUT_LEFTRIGHT)
526: size = lc->sx;
527: else
528: size = lc->sy;
529: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
530: change = size - new_size;
531: else
532: change = new_size - size;
533:
534: /* Resize the pane. */
535: layout_resize_pane(wp, type, change, 1);
536: }
537:
538: /* Resize a single pane within the layout. */
539: void
540: layout_resize_pane(struct window_pane *wp, enum layout_type type, int change,
541: int opposite)
542: {
543: struct window *w = wp->window;
544: struct layout_cell *lc, *lcparent;
545: int needed, size;
546:
547: lc = wp->layout_cell;
548:
549: /* Find next parent of the same type. */
550: lcparent = lc->parent;
551: while (lcparent != NULL && lcparent->type != type) {
552: lc = lcparent;
553: lcparent = lc->parent;
554: }
555: if (lcparent == NULL)
556: return;
557:
558: /* If this is the last cell, move back one. */
559: if (lc == TAILQ_LAST(&lcparent->cells, layout_cells))
560: lc = TAILQ_PREV(lc, layout_cells, entry);
561:
562: /* Grow or shrink the cell. */
563: needed = change;
564: while (needed != 0) {
565: if (change > 0) {
566: size = layout_resize_pane_grow(w, lc, type, needed,
567: opposite);
568: needed -= size;
569: } else {
570: size = layout_resize_pane_shrink(w, lc, type, needed);
571: needed += size;
572: }
573:
574: if (size == 0) /* no more change possible */
575: break;
576: }
577:
578: /* Fix cell offsets. */
579: layout_fix_offsets(wp->window->layout_root);
580: layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
581: notify_window("window-layout-changed", wp->window);
582: }
583:
584: /* Helper function to grow pane. */
585: static int
586: layout_resize_pane_grow(struct window *w, struct layout_cell *lc,
587: enum layout_type type, int needed, int opposite)
588: {
589: struct layout_cell *lcadd, *lcremove;
590: u_int size = 0;
591:
592: /* Growing. Always add to the current cell. */
593: lcadd = lc;
594:
595: /* Look towards the tail for a suitable cell for reduction. */
596: lcremove = TAILQ_NEXT(lc, entry);
597: while (lcremove != NULL) {
598: size = layout_resize_check(w, lcremove, type);
599: if (size > 0)
600: break;
601: lcremove = TAILQ_NEXT(lcremove, entry);
602: }
603:
604: /* If none found, look towards the head. */
605: if (opposite && lcremove == NULL) {
606: lcremove = TAILQ_PREV(lc, layout_cells, entry);
607: while (lcremove != NULL) {
608: size = layout_resize_check(w, lcremove, type);
609: if (size > 0)
610: break;
611: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
612: }
613: }
614: if (lcremove == NULL)
615: return (0);
616:
617: /* Change the cells. */
618: if (size > (u_int) needed)
619: size = needed;
620: layout_resize_adjust(w, lcadd, type, size);
621: layout_resize_adjust(w, lcremove, type, -size);
622: return (size);
623: }
624:
625: /* Helper function to shrink pane. */
626: static int
627: layout_resize_pane_shrink(struct window *w, struct layout_cell *lc,
628: enum layout_type type, int needed)
629: {
630: struct layout_cell *lcadd, *lcremove;
631: u_int size;
632:
633: /* Shrinking. Find cell to remove from by walking towards head. */
634: lcremove = lc;
635: do {
636: size = layout_resize_check(w, lcremove, type);
637: if (size != 0)
638: break;
639: lcremove = TAILQ_PREV(lcremove, layout_cells, entry);
640: } while (lcremove != NULL);
641: if (lcremove == NULL)
642: return (0);
643:
644: /* And add onto the next cell (from the original cell). */
645: lcadd = TAILQ_NEXT(lc, entry);
646: if (lcadd == NULL)
647: return (0);
648:
649: /* Change the cells. */
650: if (size > (u_int) -needed)
651: size = -needed;
652: layout_resize_adjust(w, lcadd, type, size);
653: layout_resize_adjust(w, lcremove, type, -size);
654: return (size);
655: }
656:
657: /* Assign window pane to newly split cell. */
658: void
659: layout_assign_pane(struct layout_cell *lc, struct window_pane *wp)
660: {
661: layout_make_leaf(lc, wp);
662: layout_fix_panes(wp->window, wp->window->sx, wp->window->sy);
663: }
664:
665: /* Calculate the new pane size for resized parent. */
666: static u_int
667: layout_new_pane_size(struct window *w, u_int previous, struct layout_cell *lc,
668: enum layout_type type, u_int size, u_int count_left, u_int size_left)
669: {
670: u_int new_size, min, max, available;
671:
672: /* If this is the last cell, it can take all of the remaining size. */
673: if (count_left == 1)
674: return (size_left);
675:
676: /* How much is available in this parent? */
677: available = layout_resize_check(w, lc, type);
678:
679: /*
680: * Work out the minimum size of this cell and the new size
681: * proportionate to the previous size.
682: */
683: min = (PANE_MINIMUM + 1) * (count_left - 1);
684: if (type == LAYOUT_LEFTRIGHT) {
685: if (lc->sx - available > min)
686: min = lc->sx - available;
687: new_size = (lc->sx * size) / previous;
688: } else {
689: if (lc->sy - available > min)
690: min = lc->sy - available;
691: new_size = (lc->sy * size) / previous;
692: }
693:
694: /* Check against the maximum and minimum size. */
695: max = size_left - min;
696: if (new_size > max)
697: new_size = max;
698: if (new_size < PANE_MINIMUM)
699: new_size = PANE_MINIMUM;
700: return (new_size);
701: }
702:
703: /* Check if the cell and all its children can be resized to a specific size. */
704: static int
705: layout_set_size_check(struct window *w, struct layout_cell *lc,
706: enum layout_type type, int size)
707: {
708: struct layout_cell *lcchild;
709: u_int new_size, available, previous, count, idx;
710:
711: /* Cells with no children must just be bigger than minimum. */
712: if (lc->type == LAYOUT_WINDOWPANE)
713: return (size >= PANE_MINIMUM);
714: available = size;
715:
716: /* Count number of children. */
717: count = 0;
718: TAILQ_FOREACH(lcchild, &lc->cells, entry)
719: count++;
720:
721: /* Check new size will work for each child. */
722: if (lc->type == type) {
723: if (type == LAYOUT_LEFTRIGHT)
724: previous = lc->sx;
725: else
726: previous = lc->sy;
727:
728: idx = 0;
729: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
730: new_size = layout_new_pane_size(w, previous, lcchild,
731: type, size, count - idx, available);
732: if (new_size > available)
733: return (0);
734:
735: available -= (new_size + 1);
736: if (!layout_set_size_check(w, lcchild, type, new_size))
737: return (0);
738:
739: idx++;
740: }
741: } else {
742: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
743: if (lcchild->type == LAYOUT_WINDOWPANE)
744: continue;
745: if (!layout_set_size_check(w, lcchild, type, size))
746: return (0);
747: }
748: }
749:
750: return (1);
751: }
752:
753: /* Resize all child cells to fit within the current cell. */
754: static void
755: layout_resize_child_cells(struct window *w, struct layout_cell *lc)
756: {
757: struct layout_cell *lcchild;
758: u_int previous, available, count, idx;
759:
760: if (lc->type == LAYOUT_WINDOWPANE)
761: return;
762:
763: /* What is the current size used? */
764: count = 0;
765: previous = 0;
766: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
767: count++;
768: if (lc->type == LAYOUT_LEFTRIGHT)
769: previous += lcchild->sx;
770: else if (lc->type == LAYOUT_TOPBOTTOM)
771: previous += lcchild->sy;
772: }
773: previous += (count - 1);
774:
775: /* And how much is available? */
776: available = 0;
777: if (lc->type == LAYOUT_LEFTRIGHT)
778: available = lc->sx;
779: else if (lc->type == LAYOUT_TOPBOTTOM)
780: available = lc->sy;
781:
782: /* Resize children into the new size. */
783: idx = 0;
784: TAILQ_FOREACH(lcchild, &lc->cells, entry) {
785: if (lc->type == LAYOUT_TOPBOTTOM) {
786: lcchild->sx = lc->sx;
787: lcchild->xoff = lc->xoff;
788: } else {
789: lcchild->sx = layout_new_pane_size(w, previous, lcchild,
790: lc->type, lc->sx, count - idx, available);
791: available -= (lcchild->sx + 1);
792: }
793: if (lc->type == LAYOUT_LEFTRIGHT)
794: lcchild->sy = lc->sy;
795: else {
796: lcchild->sy = layout_new_pane_size(w, previous, lcchild,
797: lc->type, lc->sy, count - idx, available);
798: available -= (lcchild->sy + 1);
799: }
800: layout_resize_child_cells(w, lcchild);
801: idx++;
802: }
803: }
804:
805: /*
806: * Split a pane into two. size is a hint, or -1 for default half/half
807: * split. This must be followed by layout_assign_pane before much else happens!
808: */
809: struct layout_cell *
810: layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
811: int insert_before, int full_size)
812: {
813: struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
814: u_int sx, sy, xoff, yoff, size1, size2;
815: u_int new_size, saved_size, resize_first = 0;
816:
817: /*
818: * If full_size is specified, add a new cell at the top of the window
819: * layout. Otherwise, split the cell for the current pane.
820: */
821: if (full_size)
822: lc = wp->window->layout_root;
823: else
824: lc = wp->layout_cell;
825:
826: /* Copy the old cell size. */
827: sx = lc->sx;
828: sy = lc->sy;
829: xoff = lc->xoff;
830: yoff = lc->yoff;
831:
832: /* Check there is enough space for the two new panes. */
833: switch (type) {
834: case LAYOUT_LEFTRIGHT:
835: if (sx < PANE_MINIMUM * 2 + 1)
836: return (NULL);
837: break;
838: case LAYOUT_TOPBOTTOM:
839: if (sy < PANE_MINIMUM * 2 + 1)
840: return (NULL);
841: break;
842: default:
843: fatalx("bad layout type");
844: }
845:
846: /*
847: * Calculate new cell sizes. size is the target size or -1 for middle
848: * split, size1 is the size of the top/left and size2 the bottom/right.
849: */
850: if (type == LAYOUT_LEFTRIGHT)
851: saved_size = sx;
852: else
853: saved_size = sy;
854: if (size < 0)
855: size2 = ((saved_size + 1) / 2) - 1;
856: else if (insert_before)
857: size2 = saved_size - size - 1;
858: else
859: size2 = size;
860: if (size2 < PANE_MINIMUM)
861: size2 = PANE_MINIMUM;
862: else if (size2 > saved_size - 2)
863: size2 = saved_size - 2;
864: size1 = saved_size - 1 - size2;
865:
866: /* Which size are we using? */
867: if (insert_before)
868: new_size = size2;
869: else
870: new_size = size1;
871:
872: /* Confirm there is enough space for full size pane. */
873: if (full_size && !layout_set_size_check(wp->window, lc, type, new_size))
874: return (NULL);
875:
876: if (lc->parent != NULL && lc->parent->type == type) {
877: /*
878: * If the parent exists and is of the same type as the split,
879: * create a new cell and insert it after this one.
880: */
881: lcparent = lc->parent;
882: lcnew = layout_create_cell(lcparent);
883: if (insert_before)
884: TAILQ_INSERT_BEFORE(lc, lcnew, entry);
885: else
886: TAILQ_INSERT_AFTER(&lcparent->cells, lc, lcnew, entry);
887: } else if (full_size && lc->parent == NULL && lc->type == type) {
888: /*
889: * If the new full size pane is the same type as the root
890: * split, insert the new pane under the existing root cell
891: * instead of creating a new root cell. The existing layout
892: * must be resized before inserting the new cell.
893: */
894: if (lc->type == LAYOUT_LEFTRIGHT) {
895: lc->sx = new_size;
896: layout_resize_child_cells(wp->window, lc);
897: lc->sx = saved_size;
898: } else if (lc->type == LAYOUT_TOPBOTTOM) {
899: lc->sy = new_size;
900: layout_resize_child_cells(wp->window, lc);
901: lc->sy = saved_size;
902: }
903: resize_first = 1;
904:
905: /* Create the new cell. */
906: lcnew = layout_create_cell(lc);
907: size = saved_size - 1 - new_size;
908: if (lc->type == LAYOUT_LEFTRIGHT)
909: layout_set_size(lcnew, size, sy, 0, 0);
910: else if (lc->type == LAYOUT_TOPBOTTOM)
911: layout_set_size(lcnew, sx, size, 0, 0);
912: if (insert_before)
913: TAILQ_INSERT_HEAD(&lc->cells, lcnew, entry);
914: else
915: TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
916: } else {
917: /*
918: * Otherwise create a new parent and insert it.
919: */
920:
921: /* Create and insert the replacement parent. */
922: lcparent = layout_create_cell(lc->parent);
923: layout_make_node(lcparent, type);
924: layout_set_size(lcparent, sx, sy, xoff, yoff);
925: if (lc->parent == NULL)
926: wp->window->layout_root = lcparent;
927: else
928: TAILQ_REPLACE(&lc->parent->cells, lc, lcparent, entry);
929:
930: /* Insert the old cell. */
931: lc->parent = lcparent;
932: TAILQ_INSERT_HEAD(&lcparent->cells, lc, entry);
933:
934: /* Create the new child cell. */
935: lcnew = layout_create_cell(lcparent);
936: if (insert_before)
937: TAILQ_INSERT_HEAD(&lcparent->cells, lcnew, entry);
938: else
939: TAILQ_INSERT_TAIL(&lcparent->cells, lcnew, entry);
940: }
941: if (insert_before) {
942: lc1 = lcnew;
943: lc2 = lc;
944: } else {
945: lc1 = lc;
946: lc2 = lcnew;
947: }
948:
949: /*
950: * Set new cell sizes. size1 is the size of the top/left and size2 the
951: * bottom/right.
952: */
953: if (!resize_first && type == LAYOUT_LEFTRIGHT) {
954: layout_set_size(lc1, size1, sy, xoff, yoff);
955: layout_set_size(lc2, size2, sy, xoff + lc1->sx + 1, yoff);
956: } else if (!resize_first && type == LAYOUT_TOPBOTTOM) {
957: layout_set_size(lc1, sx, size1, xoff, yoff);
958: layout_set_size(lc2, sx, size2, xoff, yoff + lc1->sy + 1);
959: }
960: if (full_size) {
961: if (!resize_first)
962: layout_resize_child_cells(wp->window, lc);
963: layout_fix_offsets(wp->window->layout_root);
964: } else
965: layout_make_leaf(lc, wp);
966:
967: return (lcnew);
968: }
969:
970: /* Destroy the cell associated with a pane. */
971: void
972: layout_close_pane(struct window_pane *wp)
973: {
974: struct window *w = wp->window;
975:
976: /* Remove the cell. */
977: layout_destroy_cell(w, wp->layout_cell, &w->layout_root);
978:
979: /* Fix pane offsets and sizes. */
980: if (w->layout_root != NULL) {
981: layout_fix_offsets(w->layout_root);
982: layout_fix_panes(w, w->sx, w->sy);
983: }
984: notify_window("window-layout-changed", w);
985: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>