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