xref: /minix/external/bsd/tmux/dist/layout-custom.c (revision e3b78ef1)
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