1 /* $Id: layout-custom.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ 2 3 /* 4 * Copyright (c) 2010 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 <ctype.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 struct layout_cell *layout_find_bottomright(struct layout_cell *); 27 u_short layout_checksum(const char *); 28 int layout_append(struct layout_cell *, char *, size_t); 29 struct layout_cell *layout_construct(struct layout_cell *, const char **); 30 void layout_assign(struct window_pane **, struct layout_cell *); 31 32 /* Find the bottom-right cell. */ 33 struct layout_cell * 34 layout_find_bottomright(struct layout_cell *lc) 35 { 36 if (lc->type == LAYOUT_WINDOWPANE) 37 return (lc); 38 lc = TAILQ_LAST(&lc->cells, layout_cells); 39 return (layout_find_bottomright(lc)); 40 } 41 42 /* Calculate layout checksum. */ 43 u_short 44 layout_checksum(const char *layout) 45 { 46 u_short csum; 47 48 csum = 0; 49 for (; *layout != '\0'; layout++) { 50 csum = (csum >> 1) + ((csum & 1) << 15); 51 csum += *layout; 52 } 53 return (csum); 54 } 55 56 /* Dump layout as a string. */ 57 char * 58 layout_dump(struct window *w) 59 { 60 char layout[BUFSIZ], *out; 61 62 *layout = '\0'; 63 if (layout_append(w->layout_root, layout, sizeof layout) != 0) 64 return (NULL); 65 66 xasprintf(&out, "%4x,%s", layout_checksum(layout), layout); 67 return (out); 68 } 69 70 /* Append information for a single cell. */ 71 int 72 layout_append(struct layout_cell *lc, char *buf, size_t len) 73 { 74 struct layout_cell *lcchild; 75 char tmp[64]; 76 size_t tmplen; 77 const char *brackets = "]["; 78 79 if (len == 0) 80 return (-1); 81 82 tmplen = xsnprintf(tmp, sizeof tmp, 83 "%ux%u,%u,%u", lc->sx, lc->sy, lc->xoff, lc->yoff); 84 if (tmplen > (sizeof tmp) - 1) 85 return (-1); 86 if (strlcat(buf, tmp, len) >= len) 87 return (-1); 88 89 switch (lc->type) { 90 case LAYOUT_LEFTRIGHT: 91 brackets = "}{"; 92 /* FALLTHROUGH */ 93 case LAYOUT_TOPBOTTOM: 94 if (strlcat(buf, &brackets[1], len) >= len) 95 return (-1); 96 TAILQ_FOREACH(lcchild, &lc->cells, entry) { 97 if (layout_append(lcchild, buf, len) != 0) 98 return (-1); 99 if (strlcat(buf, ",", len) >= len) 100 return (-1); 101 } 102 buf[strlen(buf) - 1] = brackets[0]; 103 break; 104 case LAYOUT_WINDOWPANE: 105 break; 106 } 107 108 return (0); 109 } 110 111 /* Parse a layout string and arrange window as layout. */ 112 int 113 layout_parse(struct window *w, const char *layout) 114 { 115 struct layout_cell *lc, *lcchild; 116 struct window_pane *wp; 117 u_int npanes, ncells, sx, sy; 118 u_short csum; 119 120 /* Check validity. */ 121 if (sscanf(layout, "%hx,", &csum) != 1) 122 return (-1); 123 layout += 5; 124 if (csum != layout_checksum(layout)) 125 return (-1); 126 127 /* Build the layout. */ 128 lc = layout_construct(NULL, &layout); 129 if (lc == NULL) 130 return (-1); 131 if (*layout != '\0') 132 goto fail; 133 134 /* Check this window will fit into the layout. */ 135 for (;;) { 136 npanes = window_count_panes(w); 137 ncells = layout_count_cells(lc); 138 if (npanes > ncells) 139 goto fail; 140 if (npanes == ncells) 141 break; 142 143 /* Fewer panes than cells - close the bottom right. */ 144 lcchild = layout_find_bottomright(lc); 145 layout_destroy_cell(lcchild, &lc); 146 } 147 148 /* Save the old window size and resize to the layout size. */ 149 sx = w->sx; sy = w->sy; 150 window_resize(w, lc->sx, lc->sy); 151 152 /* Destroy the old layout and swap to the new. */ 153 layout_free_cell(w->layout_root); 154 w->layout_root = lc; 155 156 /* Assign the panes into the cells. */ 157 wp = TAILQ_FIRST(&w->panes); 158 layout_assign(&wp, lc); 159 160 /* Update pane offsets and sizes. */ 161 layout_fix_offsets(lc); 162 layout_fix_panes(w, lc->sx, lc->sy); 163 164 /* Then resize the layout back to the original window size. */ 165 layout_resize(w, sx, sy); 166 window_resize(w, sx, sy); 167 168 layout_print_cell(lc, __func__, 0); 169 170 return (0); 171 172 fail: 173 layout_free_cell(lc); 174 return (-1); 175 } 176 177 /* Assign panes into cells. */ 178 void 179 layout_assign(struct window_pane **wp, struct layout_cell *lc) 180 { 181 struct layout_cell *lcchild; 182 183 switch (lc->type) { 184 case LAYOUT_WINDOWPANE: 185 layout_make_leaf(lc, *wp); 186 *wp = TAILQ_NEXT(*wp, entry); 187 return; 188 case LAYOUT_LEFTRIGHT: 189 case LAYOUT_TOPBOTTOM: 190 TAILQ_FOREACH(lcchild, &lc->cells, entry) 191 layout_assign(wp, lcchild); 192 return; 193 } 194 } 195 196 /* Construct a cell from all or part of a layout tree. */ 197 struct layout_cell * 198 layout_construct(struct layout_cell *lcparent, const char **layout) 199 { 200 struct layout_cell *lc, *lcchild; 201 u_int sx, sy, xoff, yoff; 202 203 if (!isdigit((u_char) **layout)) 204 return (NULL); 205 if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4) 206 return (NULL); 207 208 while (isdigit((u_char) **layout)) 209 (*layout)++; 210 if (**layout != 'x') 211 return (NULL); 212 (*layout)++; 213 while (isdigit((u_char) **layout)) 214 (*layout)++; 215 if (**layout != ',') 216 return (NULL); 217 (*layout)++; 218 while (isdigit((u_char) **layout)) 219 (*layout)++; 220 if (**layout != ',') 221 return (NULL); 222 (*layout)++; 223 while (isdigit((u_char) **layout)) 224 (*layout)++; 225 226 lc = layout_create_cell(lcparent); 227 lc->sx = sx; 228 lc->sy = sy; 229 lc->xoff = xoff; 230 lc->yoff = yoff; 231 232 switch (**layout) { 233 case ',': 234 case '}': 235 case ']': 236 case '\0': 237 return (lc); 238 case '{': 239 lc->type = LAYOUT_LEFTRIGHT; 240 break; 241 case '[': 242 lc->type = LAYOUT_TOPBOTTOM; 243 break; 244 default: 245 goto fail; 246 } 247 248 do { 249 (*layout)++; 250 lcchild = layout_construct(lc, layout); 251 if (lcchild == NULL) 252 goto fail; 253 TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry); 254 } while (**layout == ','); 255 256 switch (lc->type) { 257 case LAYOUT_LEFTRIGHT: 258 if (**layout != '}') 259 goto fail; 260 break; 261 case LAYOUT_TOPBOTTOM: 262 if (**layout != ']') 263 goto fail; 264 break; 265 default: 266 goto fail; 267 } 268 (*layout)++; 269 270 return (lc); 271 272 fail: 273 layout_free_cell(lc); 274 return (NULL); 275 } 276