1 /*-
2  * Copyright (c) 2002 Jordan DeLong
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of contributors may be
14  *    used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 #include "editor.h"
30 
31 /*
32  * minimum view sizes; these are used when initially splitting views; when
33  * views are being resized through grow/shrink keys or a window size change,
34  * the minimums are treated as 1 in either dimension.
35  */
36 #define MIN_VIEW_WIDTH		5
37 #define MIN_VIEW_HEIGHT		3
38 
39 /* list of all frames in existence */
40 framelist_t	frame_list	= TAILQ_HEAD_INITIALIZER(frame_list);
41 
42 /* destroy all frames */
frame_shutdown()43 void frame_shutdown() {
44 	while (!TAILQ_EMPTY(&frame_list))
45 		frame_destroy(TAILQ_FIRST(&frame_list));
46 }
47 
48 /*
49  * allocate a new frame, with a single view in it.
50  */
frame_create(viewhdr_t * v,buffer_t * buffer)51 frame_t *frame_create(viewhdr_t *v, buffer_t *buffer) {
52 	frame_t *frame;
53 
54 	frame = ckmalloc(sizeof(frame_t));
55 	frame->node = ckmalloc(sizeof(framenode_t));
56 	frame->node->type = FRAME_UNSPLIT;
57 	frame->node->d.v = v;
58 	frame->node->above = NULL;
59 	v->node = frame->node;
60 	TAILQ_INIT(&frame->views);
61 	TAILQ_INSERT_HEAD(&frame_list, frame, f_list);
62 	TAILQ_INSERT_TAIL(&frame->views, v, v_list);
63 	frame->width = screen_width;
64 	frame->height = screen_height;
65 	view_mapstack(v, buffer, 0, 1, screen_width, screen_height - 2);
66 	frame->redraw_hsplits = 0;
67 
68 	return frame;
69 }
70 
71 /*
72  * recursive routine to free a tree of frame nodes, the views (and
73  * their node pointers) are untouched.  frames aren't removed from
74  * their parent nodes.
75  */
ndfreetree(framenode_t * node)76 static void ndfreetree(framenode_t *node) {
77 	int i;
78 
79 	if (node->type != FRAME_UNSPLIT) {
80 		for (i = 0; i < node->d.s.count; i++) {
81 			ndfreetree(node->d.s.f[i]);
82 			free(node->d.s.f[i]);
83 		}
84 		free(node->d.s.f);
85 	}
86 }
87 
88 /* destroy all the views in frame, free allocated memory */
frame_destroy(frame_t * frame)89 void frame_destroy(frame_t *frame) {
90 	viewhdr_t *v;
91 
92 	ndfreetree(frame->node);
93 	free(frame->node);
94 	while (!TAILQ_EMPTY(&frame->views)) {
95 		v = TAILQ_FIRST(&frame->views);
96 		TAILQ_REMOVE(&frame->views, v, v_list);
97 		while ((v = view_destroy(v)) != NULL)
98 			;
99 	}
100 	TAILQ_REMOVE(&frame_list, frame, f_list);
101 	free(frame);
102 }
103 
104 /* redraw all the views and their headers */
frame_draw(frame_t * frame)105 void frame_draw(frame_t *frame) {
106 	viewhdr_t *v;
107 	int i;
108 
109 	/* currently shouldn't ever draw anything other than active */
110 	assert(frame == frame_active);
111 
112 	/* draw splitters if neccesary */
113 	if (!frame->redraw_hsplits)
114 		goto drawviews;
115 	frame->redraw_hsplits = 0;
116 	screen_setcolor(COLOR_HEADING);
117 	TAILQ_FOREACH(v, &frame->views, v_list) {
118 		if (v->geom.x == 0)
119 			continue;
120 		for (i = v->geom.y - 1; i < v->geom.y +
121 				v->geom.height; i++) {
122 			screen_gotoxy(v->geom.x - 1, i);
123 			screen_putc(' ');
124 		}
125 	}
126 
127 drawviews:
128 	/* call draw and drawhdr for every view */
129 	TAILQ_FOREACH(v, &frame->views, v_list) {
130 		VIEWCALL(v, drawhdr, v->geom.x, v->geom.y - 1, v->geom.width);
131 		VIEWCALL(v, draw);
132 	}
133 }
134 
135 /*
136  * tell all the views in this frame that they need to be fully
137  * redrawn next time.
138  */
frame_forcedraw(frame_t * frame)139 void frame_forcedraw(frame_t *frame) {
140 	viewhdr_t *v;
141 
142 	TAILQ_FOREACH(v, &frame_active->views, v_list)
143 		VIEWCALL(v, activate);
144 
145 	/* flag as needing a splitter redraw */
146 	frame_active->redraw_hsplits = 1;
147 }
148 
149 /*
150  * make a new framenode by splitting an existing one (that is already
151  * a split node -- the first split must be done seperately); the
152  * returned node's view should be set up by the caller.
153  */
splitnode(framenode_t * node,int idx)154 static __inline framenode_t *splitnode(framenode_t *node, int idx) {
155 	int i;
156 
157 	assert(node->type != FRAME_UNSPLIT && node->d.s.count >= 2);
158 	node->d.s.count++;
159 	node->d.s.f = ckrealloc(node->d.s.f, sizeof(framenode_t *) *
160 		node->d.s.count);
161 	idx++;
162 	memmove(&node->d.s.f[idx + 1], &node->d.s.f[idx], (node->d.s.count -
163 		(idx + 1)) * sizeof(framenode_t *));
164 	for (i = idx + 1; i < node->d.s.count; i++)
165 		node->d.s.f[i]->aboveidx++;
166 	node->d.s.f[idx] = ckmalloc(sizeof(framenode_t));
167 	node->d.s.f[idx]->type = FRAME_UNSPLIT;
168 	node->d.s.f[idx]->above = node;
169 	node->d.s.f[idx]->aboveidx = idx;
170 
171 	return node->d.s.f[idx];
172 }
173 
typedsplit(framenode_t * node,int type)174 static framenode_t *typedsplit(framenode_t *node, int type) {
175 	framenode_t *new;
176 
177 	assert(node->type == FRAME_UNSPLIT);
178 	if (node->above && node->above->type == type)
179 		return splitnode(node->above, node->aboveidx);
180 
181 	/*
182 	 * change the node into a node of type, move the view for
183 	 * this node into a child, and then do the split.
184 	 */
185 	new = ckmalloc(sizeof(framenode_t));
186 	new->type = FRAME_UNSPLIT;
187 	new->above = node;
188 	new->aboveidx = 0;
189 	new->d.v = node->d.v;
190 	new->d.v->node = new;
191 
192 	/* set up the parent */
193 	node->type = type;
194 	node->d.s.count = 2;
195 	node->d.s.f = ckmalloc(2 * sizeof(framenode_t *));
196 	node->d.s.f[0] = new;
197 
198 	/*
199 	 * the caller is responsible for setting up the new child.
200 	 */
201 	new = ckmalloc(sizeof(framenode_t));
202 	new->type = FRAME_UNSPLIT;
203 	new->above = node;
204 	new->aboveidx = 1;
205 	node->d.s.f[1] = new;
206 
207 	return new;
208 }
209 
frame_vertsplit(frame_t * frame,viewhdr_t * view)210 void frame_vertsplit(frame_t *frame, viewhdr_t *view) {
211 	framenode_t *newnode;
212 	viewhdr_t *newview;
213 	int r, h;
214 
215 	/* don't make unreasonably sized frames */
216 	r = view->geom.height / 2 - 1;
217 	h = view->geom.height - r - 1;
218 	if (r < MIN_VIEW_HEIGHT || h < MIN_VIEW_HEIGHT) {
219 		minibuff_printf("Not enough room to split vertical.");
220 		return;
221 	}
222 
223 	/*
224 	 * split and add a new view in the new branch of the
225 	 * tree of framenodes.
226 	 */
227 	newnode = typedsplit(view->node, FRAME_VSPLIT);
228 	newview = view_duplicate(view);
229 	newview->node = newnode;
230 	newnode->d.v = newview;
231 	view_mapstack(newview, view->buffer, view->geom.x,
232 		view->geom.y + r + 1, view->geom.width, h);
233 	view_resize(view, view->geom.x, view->geom.y, view->geom.width, r);
234 
235 	/* add the new one to the frame list */
236 	TAILQ_INSERT_TAIL(&frame->views, newview, v_list);
237 }
238 
frame_horizsplit(frame_t * frame,viewhdr_t * view)239 void frame_horizsplit(frame_t *frame, viewhdr_t *view) {
240 	framenode_t *newnode;
241 	viewhdr_t *newview;
242 	int c, w;
243 
244 	/* don't make unreasonably sized frames */
245 	c = view->geom.width / 2 - 1;
246 	w = view->geom.width - c - 1;
247 	if (c < MIN_VIEW_WIDTH || w < MIN_VIEW_WIDTH) {
248 		minibuff_printf("Not enough room to split horizontal.");
249 		return;
250 	}
251 
252 	/* split and add the new view */
253 	newnode = typedsplit(view->node, FRAME_HSPLIT);
254 	newview = view_duplicate(view);
255 	newview->node = newnode;
256 	newnode->d.v = newview;
257 	view_mapstack(newview, view->buffer, view->geom.x + c + 1,
258 		view->geom.y, w, view->geom.height);
259 	view_resize(view, view->geom.x, view->geom.y, c, view->geom.height);
260 
261 	/* add the new one to the frame list */
262 	TAILQ_INSERT_TAIL(&frame->views, newview, v_list);
263 
264 	/* we're going to need to draw splitters */
265 	frame->redraw_hsplits = 1;
266 }
267 
268 /*
269  * compute the screen dimensions of a framenode by recursing
270  * down till we find views, and adding for splitters.
271  */
nodedims(framenode_t * node,int * width,int * height)272 static void nodedims(framenode_t *node, int *width, int *height) {
273 	int i;
274 
275 	switch (node->type) {
276 	case FRAME_UNSPLIT:
277 		*width += node->d.v->geom.width;
278 		*height += node->d.v->geom.width;
279 		return;
280 	case FRAME_VSPLIT:
281 		for (i = 1; i < node->d.s.count; i++)
282 			(*height)++;
283 		break;
284 	case FRAME_HSPLIT:
285 		for (i = 1; i < node->d.s.count; i++)
286 			(*width)++;
287 		break;
288 	}
289 
290 	for (i = 0; i < node->d.s.count; i++)
291 		nodedims(node->d.s.f[i], width, height);
292 }
293 
294 /* merge a frame node with less than 2 entries upwards */
mergeup(frame_t * frame,framenode_t * node)295 static int mergeup(frame_t *frame, framenode_t *node) {
296 	int ret, i;
297 
298 	assert(node->type != FRAME_UNSPLIT && node->d.s.count < 2);
299 
300 	/*
301 	 * if the node is empty, instead of merging it up, we just need
302 	 * to delete it's place in the parent node.  (if node->above isn't
303 	 * set here, there's something wrong..)
304 	 */
305 	if (node->d.s.count == 0) {
306 		assert(node->above);
307 		for (i = node->aboveidx + 1; i < node->above->d.s.count; i++)
308 			node->above->d.s.f[i]->aboveidx--;
309 		memmove(&node->above->d.s.f[node->aboveidx],
310 			&node->above->d.s.f[node->aboveidx + 1],
311 			(node->above->d.s.count - (node->aboveidx + 1)) *
312 			sizeof(framenode_t *));
313 		node->above->d.s.count--;
314 		ret = 1;
315 		goto free;
316 	}
317 
318 	/*
319 	 * if there is no node above this, we can't merge up; instead
320 	 * just set the frame's top framenode to the only child of this
321 	 * one.
322 	 */
323 	ret = 0;
324 	if (!node->above) {
325 		assert(frame->node == node);
326 		frame->node = node->d.s.f[0];
327 		frame->node->above = NULL;
328 	} else {
329 		node->above->d.s.f[node->aboveidx] = node->d.s.f[0];
330 		node->d.s.f[0]->above = node->above;
331 		node->d.s.f[0]->aboveidx = node->aboveidx;
332 	}
333 
334 free:
335 	free(node->d.s.f);
336 	free(node);
337 
338 	return ret;
339 }
340 
341 /* forward declaration */
342 static int recsz(frame_t *frame, framenode_t *node, int type,
343 		int idx, int doadjust, int move, int cnt);
344 
345 /*
346  * handle adjustment for the size of a removed frame (we need to correct
347  * the amount of space that was being used for the splitter bar).  the
348  * idx passed in is the idx of the just-removed node.
349  */
adjrmed(frame_t * frame,framenode_t * node,int type,int idx,int amt)350 static __inline void adjrmed(frame_t *frame, framenode_t *node, int type,
351 		int idx, int amt) {
352 	assert(node->type != FRAME_UNSPLIT);
353 
354 	/* empty nodes is a valid call for convenience */
355 	if (node->d.s.count == 0)
356 		abort();
357 
358 	/*
359 	 * it shouldn't be possible for either of these calls to
360 	 * recsz to result in node removal.
361 	 */
362 	if (idx > 0) {
363 		if (recsz(frame, node->d.s.f[idx - 1], type, 0, 0, 0, amt))
364 			assert(0);
365 	} else if (idx < node->d.s.count) {
366 		if (recsz(frame, node->d.s.f[idx], type, 0, 0, -amt, amt))
367 			assert(0);
368 	}
369 }
370 
371 /*
372  * recurse down a framenode tree and size in the specified manner; no
373  * checking is done to make sure there is room for the root node to
374  * size; that must be done from outside this routine.  if it is
375  * impossible to handle making something under this tree size in the
376  * specified manner, the view will be removed and it's frame unsplit.
377  * this should only happen when making something smaller.
378  *
379  * the return value is used to communicate to the caller whether the
380  * frame node needed to be destroyed because it couldn't be sized in
381  * the requested manner.  nonzero indicates it was destroyed.  the
382  * calling routine (should be this routine) should then handle sizing
383  * adjacent nodes to occupy the space that was in use for the splitter
384  * bar (using the adjrmed routine).
385  *
386  * rather than try to obfuscate the recsz logic to support magnitudes of
387  * cnt > 1, it's easier (although perhaps less efficent) to just require
388  * that it be called multiple times.  this restriction isn't placed on
389  * situations that can be certain that there is room (such as adjrmed).
390  *
391  * doadjust indicates whether another child node on idx should be sized
392  * opposite cnt to accommodate the change at idx
393  *
394  * XXX: when removing impossible views, if the buffer is modified, data
395  * will currently be lost.  the buffer needs to be detached; this isn't
396  * yet possible because buffer detaching isn't implemented yet.
397  */
recsz(frame_t * frame,framenode_t * node,int type,int idx,int doadjust,int move,int cnt)398 static int recsz(frame_t *frame, framenode_t *node, int type,
399 		int idx, int doadjust, int move, int cnt) {
400 	viewhdr_t *v;
401 	int x, y, width, height;
402 	int szidx, low, high, i;
403 
404 	/*
405 	 * for unsplit nodes just size/move the view according to the
406 	 * parameters that were passed in.
407 	 */
408 	if (node->type == FRAME_UNSPLIT) {
409 		x = node->d.v->geom.x;
410 		y = node->d.v->geom.y;
411 		width = node->d.v->geom.width;
412 		height = node->d.v->geom.height;
413 
414 		switch (type) {
415 		case FRAME_VSPLIT:
416 			y += move;
417 			height += cnt;
418 			if (height < 1)
419 				goto destroy;
420 			break;
421 		case FRAME_HSPLIT:
422 			x += move;
423 			width += cnt;
424 			if (width < 1)
425 				goto destroy;
426 			break;
427 		}
428 
429 		view_resize(node->d.v, x, y, width, height);
430 		return 0;
431 
432 destroy:
433 		/*
434 		 * XXX: need to make the buffer 'backgrounded' if this
435 		 * will rm the last reference to it.
436 		 */
437 		if (!node->above)
438 			abort();
439 		v = node->d.v;
440 		TAILQ_REMOVE(&frame->views, v, v_list);
441 		while ((v = view_destroy(v)) != NULL)
442 			;
443 		for (i = node->aboveidx + 1; i < node->above->d.s.count; i++)
444 			node->above->d.s.f[i]->aboveidx--;
445 		memmove(&node->above->d.s.f[node->aboveidx],
446 			&node->above->d.s.f[node->aboveidx + 1],
447 			(node->above->d.s.count - (node->aboveidx + 1)) *
448 			sizeof(framenode_t *));
449 		node->above->d.s.count--;
450 		free(node);
451 		return 1;
452 	}
453 
454 	/*
455 	 * if this node is of a different split type, just propagate
456 	 * to all child nodes and handle merging.
457 	 */
458 	if (node->type != type) {
459 		for (i = 0; i < node->d.s.count;) {
460 			if (!recsz(frame, node->d.s.f[i], type, 0, 0,
461 					move, cnt))
462 				i++;
463 		}
464 		if (node->d.s.count < 2) {
465 			assert(node->d.s.count == 0);
466 			return mergeup(frame, node);
467 		}
468 		return 0;
469 	}
470 
471 	/*
472 	 * if we don't need to adjust a sibling to make room, just
473 	 * size the idx we were handed.
474 	 */
475 	if (!doadjust) {
476 		/*
477 		 * this probably only works with idx == 0, and there is
478 		 * currently no reason to make it work for other idx yet.
479 		 */
480 		assert(idx == 0);
481 
482 		/*
483 		 * the logic here is slightly different if the node isn't
484 		 * supposed to be moved, but only sized, or isn't supposed
485 		 * to be sized, but only moved.  the simpler case is when
486 		 * it is both moved and sized, because all of the changes
487 		 * may be made to the first child node (plus any removal
488 		 * correction).
489 		 */
490 		if (!move || !cnt) {
491 			while (recsz(frame, node->d.s.f[idx], type, 0, 0,
492 					move, cnt))
493 				adjrmed(frame, node, type, idx, 2);
494 
495 			/*
496 			 * move the ones past the one that was sized to
497 			 * accomodate the same space.  it should be impossible
498 			 * for these recszes to destroy a node.
499 			 */
500 			for (i = idx + 1; i < node->d.s.count; i++) {
501 				if (recsz(frame, node->d.s.f[i], type, 0, 0,
502 						move + cnt, 0))
503 					assert(0);
504 			}
505 		} else {
506 			if (recsz(frame, node->d.s.f[idx], type, 0, 0,
507 					move, cnt))
508 				adjrmed(frame, node, type, idx, 1);
509 		}
510 
511 		if (node->d.s.count < 2)
512 			return mergeup(frame, node);
513 		return 0;
514 	}
515 
516 	/*
517 	 * determine the idx of the node that should be corrected for
518 	 * the change of cnt in the node at idx.
519 	 */
520 	szidx = idx + 1;
521 	if (szidx == node->d.s.count)
522 		szidx = 0;
523 
524 	/*
525 	 * adjust node at szidx to accommodate the change in the node
526 	 * at idx; the nodes in between to must be moved, but their
527 	 * sizes wont change.
528 	 */
529 	low = min(szidx, idx);
530 	high = max(szidx, idx);
531 	if (recsz(frame, node->d.s.f[low], type, 0, 0, move,
532 			low == idx ? cnt : -cnt)) {
533 		adjrmed(frame, node, type, low, 1);
534 		low--, high--, idx--;
535 	}
536 	if (recsz(frame, node->d.s.f[high], type, 0, 0, move +
537 			(high == idx ? -cnt : cnt),
538 			high == idx ? cnt : -cnt))
539 		adjrmed(frame, node, type, high, 1);
540 	for (i = low + 1; i < high; i++)
541 		if (recsz(frame, node->d.s.f[i], type, 0, 0, move + -cnt, 0))
542 			/*
543 			 * this condition should never happen.  unless I
544 			 * misthought this...
545 			 */
546 			assert(0);
547 	if (node->d.s.count < 2)
548 		return mergeup(frame, node);
549 
550 	return 0;
551 }
552 
553 /*
554  * return the first node encountered of the specified type while
555  * traversing upwards.  the index of the branch used to travel
556  * up to this node is passed out as well.
557  */
topnode(framenode_t * node,int type,int * idx)558 static framenode_t *topnode(framenode_t *node, int type, int *idx) {
559 	while (node->above) {
560 		if (node->above->type == type) {
561 			*idx = node->aboveidx;
562 			return node->above;
563 		}
564 
565 		node = node->above;
566 	}
567 
568 	return NULL;
569 }
570 
frame_size(frame_t * frame,viewhdr_t * v,int type,int cnt)571 int frame_size(frame_t *frame, viewhdr_t *v, int type, int cnt) {
572 	framenode_t *node;
573 	int idx;
574 
575 	node = topnode(v->node, type, &idx);
576 	if (!node)
577 		return -1;
578 	recsz(frame, node, type, idx, 1, 0, cnt);
579 	frame->redraw_hsplits = 1;
580 
581 	return 0;
582 }
583 
584 /*
585  * resize all the frames to the specified size (should probably be
586  * the new screen size).
587  */
frame_resize(int width,int height)588 void frame_resize(int width, int height) {
589 	frame_t *frame;
590 	int dir;
591 
592 	/*
593 	 * size every frame according to the change in size.  see
594 	 * the explaination at recsz() for why the recsz routine
595 	 * is called multiple times.
596 	 */
597 	TAILQ_FOREACH(frame, &frame_list, f_list) {
598 		dir = width - frame->width > 0 ? 1 : -1;
599 		while (frame->width != width) {
600 			recsz(frame, frame->node, FRAME_HSPLIT, 0, 0, 0, dir);
601 			frame->width += dir;
602 		}
603 		dir = height - frame->height > 0 ? 1 : -1;
604 		while (frame->height != height) {
605 			recsz(frame, frame->node, FRAME_VSPLIT, 0, 0, 0, dir);
606 			frame->height += dir;
607 		}
608 		frame->redraw_hsplits = 1;
609 	}
610 }
611 
frame_rmview(frame_t * frame,viewhdr_t * v)612 int frame_rmview(frame_t *frame, viewhdr_t *v) {
613 	framenode_t *above;
614 	int useidx, amt, dir;
615 
616 	/* don't remove the last view */
617 	if (TAILQ_FIRST(&frame->views) == TAILQ_LAST(&frame->views, viewlist))
618 		return -1;
619 
620 	/*
621 	 * the simplest way to do this is to just call the sizing routine to
622 	 * shrink the node till it needs to be destroyed.  which idx we use
623 	 * it on is obviously closely tied to the choice of szidx in recsz:
624 	 * if recsz changes, changes will be required here.
625 	 */
626 	assert(v->node->above);
627 	above = v->node->above;
628 	useidx = v->node->aboveidx - 1;
629 	if (useidx < 0) {
630 		useidx = 0;
631 		dir = -1;
632 	} else
633 		dir = 1;
634 	amt = above->type == FRAME_VSPLIT ? v->geom.height : v->geom.width;
635 	while (amt--)
636 		if (recsz(frame, above, above->type,
637 				useidx, 1, 0, dir))
638 			assert(amt == -1);
639 	frame->redraw_hsplits = 1;
640 
641 	return 0;
642 }
643 
frame_oneview(frame_t * frame,viewhdr_t * oneview)644 int frame_oneview(frame_t *frame, viewhdr_t *oneview) {
645 	viewhdr_t *v;
646 
647 	/* don't do it if there's already only one view */
648 	if (!oneview->node->above)
649 		return -1;
650 
651 	/* free old views and the tree of nodes */
652 	assert(frame->node->type != FRAME_UNSPLIT);
653 	ndfreetree(frame->node);
654 	while (!TAILQ_EMPTY(&frame->views)) {
655 		v = TAILQ_FIRST(&frame->views);
656 		TAILQ_REMOVE(&frame->views, v, v_list);
657 		if (v != oneview)
658 			while ((v = view_destroy(v)) != NULL)
659 				;
660 	}
661 
662 	/* revert the root node to an unsplit node */
663 	frame->node->type = FRAME_UNSPLIT;
664 	frame->node->d.v = oneview;
665 	oneview->node = frame->node;
666 
667 	TAILQ_INSERT_TAIL(&frame->views, oneview, v_list);
668 	view_resize(oneview, 0, 1, screen_width, screen_height - 2);
669 
670 	return 0;
671 }
672