Renamed package to ion1, and made it conflict with package 'ion'.
[ion1.git] / src / split.c
blobecfe9fb9aaa35c5a8011c18c30499d97630b2398
1 /*
2 * ion/split.c
4 * Copyright (c) Tuomo Valkonen 1999-2001.
5 * See the included file LICENSE for details.
6 */
8 #include <X11/Xmd.h>
10 #include "common.h"
11 #include "screen.h"
12 #include "workspace.h"
13 #include "frame.h"
14 #include "focus.h"
15 #include "global.h"
16 #include "split.h"
17 #include "window.h"
18 #include "objp.h"
19 #include "splitwin.h"
22 IMPLOBJ(WWsSplit, WObj, NULL)
25 /*{{{ Misc. */
28 int wwin_size(WWindow *wwin, int dir)
30 if(dir==HORIZONTAL)
31 return wwin->geom.w;
32 return wwin->geom.h;
36 int wwin_other_size(WWindow *wwin, int dir)
38 if(dir==HORIZONTAL)
39 return wwin->geom.w;
40 return wwin->geom.h;
44 int wwin_pos(WWindow *wwin, int dir)
46 if(dir==HORIZONTAL)
47 return wwin->geom.x;
48 return wwin->geom.y;
52 int tree_size(WObj *obj, int dir)
54 if(WOBJ_IS(obj, WWindow))
55 return wwin_size((WWindow*)obj, dir);
57 if(dir==HORIZONTAL)
58 return ((WWsSplit*)obj)->geom.w;
59 return ((WWsSplit*)obj)->geom.h;
63 int tree_pos(WObj *obj, int dir)
65 if(WOBJ_IS(obj, WWindow))
66 return wwin_pos((WWindow*)obj, dir);
68 if(dir==HORIZONTAL)
69 return ((WWsSplit*)obj)->geom.x;
70 return ((WWsSplit*)obj)->geom.y;
74 static WRectangle tree_geom(WObj *obj)
76 if(WOBJ_IS(obj, WWindow))
77 return ((WWindow*)obj)->geom;
79 return ((WWsSplit*)obj)->geom;
83 int tree_other_size(WObj *obj, int dir)
85 if(WOBJ_IS(obj, WWindow))
86 return wwin_other_size((WWindow*)obj, dir);
88 if(dir==HORIZONTAL)
89 return ((WWsSplit*)obj)->geom.h;
90 return ((WWsSplit*)obj)->geom.w;
94 static int wwin_calcresize(WWindow *wwin, int dir, int nsize)
96 if(dir==HORIZONTAL){
97 if(nsize<wwin->min_w)
98 return wwin->min_w;
99 }else{
100 if(nsize<wwin->min_h)
101 return wwin->min_h;
104 return nsize;
108 static int wwin_resize(WWindow *wwin, int dir, int npos, int nsize)
110 WRectangle geom;
112 if(dir==VERTICAL){
113 geom.x=wwin->geom.x;
114 geom.w=wwin->geom.w;
115 geom.y=npos;
116 geom.h=nsize;
117 wwin->flags&=~WWINDOW_HFORCED;
118 }else{
119 geom.x=npos;
120 geom.w=nsize;
121 geom.y=wwin->geom.y;
122 geom.h=wwin->geom.h;
123 wwin->flags&=~WWINDOW_WFORCED;
126 if(WTHING_IS(wwin, WFrame))
127 set_frame_geom((WFrame*)wwin, geom);
129 return nsize;
133 static WWsSplit *split_of(WObj *obj)
135 if(WOBJ_IS(obj, WWindow))
136 return ((WWindow*)obj)->split;
138 assert(WOBJ_IS(obj, WWsSplit));
140 return ((WWsSplit*)obj)->parent;
144 static void set_split_of(WObj *obj, WWsSplit *split)
146 if(WOBJ_IS(obj, WWindow)){
147 ((WWindow*)obj)->split=split;
148 return;
151 assert(WOBJ_IS(obj, WWsSplit));
153 ((WWsSplit*)obj)->parent=split;
157 /*}}}*/
160 /*{{{ Low-level resize code */
163 static int tree_calcresize(WObj *node_, int dir, int primn,
164 int nsize)
166 WWsSplit *node;
167 WObj *o1, *o2;
168 int s1, s2, ns1, ns2;
170 assert(node_!=NULL);
172 /* Reached a window? */
173 if(!WTHING_IS(node_, WWsSplit)){
174 assert(WTHING_IS(node_, WWindow));
175 return wwin_calcresize((WWindow*)node_, dir, nsize);
178 node=(WWsSplit*)node_;
180 if(node->dir!=dir){
181 /* Found a split in the other direction than the resize */
182 s1=tree_calcresize(node->tl, dir, primn, nsize);
183 s2=tree_calcresize(node->br, dir, primn, nsize);
185 if(s1>s2){
186 /*if(nsize>tree_size(node->tl, dir)){
187 tree_calcresize(node->tl, dir, primn, s2);
188 s1=s2;
189 }else*/{
190 tree_calcresize(node->br, dir, primn, s1);
192 }else if(s2>s1){
193 /*if(nsize>tree_size(node->br, dir)){
194 tree_calcresize(node->br, dir, primn, s1);
195 }else*/{
196 tree_calcresize(node->tl, dir, primn, s2);
197 s1=s2;
200 node->res=ANY;
201 node->knowsize=ANY;
202 return (node->tmpsize=s1);
203 }else{
204 if(primn==TOP_OR_LEFT){
205 /* Resize top or left node first */
206 o1=node->tl;
207 o2=node->br;
208 }else{
209 /* Resize bottom or right node first */
210 o1=node->br;
211 o2=node->tl;
212 primn=BOTTOM_OR_RIGHT;
215 s2=tree_size(o2, dir);
216 ns1=nsize-s2;
217 s1=tree_calcresize(o1, dir, primn, ns1);
219 /*if(s1!=ns1){*/
220 ns2=nsize-s1;
221 s2=tree_calcresize(o2, dir, primn, ns2);
222 node->res=ANY;
223 /*}else{
224 node->res=primn;
227 node->knowsize=primn;
228 node->tmpsize=s1;
229 return s1+s2;
234 /* Resize according to parameters calculated by wcalcres
236 int tree_do_resize(WObj *node_, int dir, int npos, int nsize)
238 WWsSplit *node;
239 int tls, brs;
240 int s=0;
241 int pos=npos;
243 if(node_==NULL){
244 return 0;
246 assert(node_!=NULL);
248 /* Reached a window? */
249 if(!WTHING_IS(node_, WWsSplit)){
250 assert(WTHING_IS(node_, WWindow));
251 return wwin_resize((WWindow*)node_, dir, npos, nsize);
254 node=(WWsSplit*)node_;
256 if(node->dir!=dir){
257 tree_do_resize(node->tl, dir, npos, nsize);
258 s=tree_do_resize(node->br, dir, npos, nsize);
259 }else{
260 if(node->knowsize==TOP_OR_LEFT){
261 tls=node->tmpsize;
262 brs=nsize-tls;
263 }else if(node->knowsize==BOTTOM_OR_RIGHT){
264 brs=node->tmpsize;
265 tls=nsize-brs;
266 }else{
267 fprintf(stderr, "Resize is broken!");
268 brs=nsize/2;
269 tls=nsize-brs;
273 /*if(node->res!=BOTTOM_OR_RIGHT)*/
274 s+=tree_do_resize(node->tl, dir, npos, tls);
275 /*else
276 s+=tree_size(node->tl, dir);*/
278 npos+=s;
280 /*if(node->res!=TOP_OR_LEFT)*/
281 s+=tree_do_resize(node->br, dir, npos, brs);
282 /*else
283 s+=tree_size(node->br, dir);*/
286 if(dir==VERTICAL){
287 node->geom.y=pos;
288 node->geom.h=s;
289 }else{
290 node->geom.x=pos;
291 node->geom.w=s;
294 return s;
298 /* Calculate parameters for resizing <split> and possibly anything
299 * above it so that the <dir>-dimension of <from> becomes <nsize>
300 * (if possible).
302 static void wcalcres(WWsSplit *split, int dir, int primn,
303 int nsize, int from, WResizeTmp *ret)
305 int s, s2, ds, rs, rp;
306 WObj *other=(from==TOP_OR_LEFT ? split->br : split->tl);
307 WWsSplit *p;
309 s=tree_size((WObj*)split, dir);
311 if(dir!=split->dir){
312 /* It might not be possible to shrink the other as much */
313 ds=tree_calcresize(other, dir, primn, nsize);
314 nsize=ds;
315 s2=0;
316 }else{
317 if(primn!=from)
318 s2=tree_calcresize(other, dir, from, s-nsize);
319 else
320 s2=tree_calcresize(other, dir, from, tree_size(other, dir));
322 ds=nsize+s2;
324 p=split->parent;
326 if(p==NULL || ds==s){
327 rs=s;
328 rp=tree_pos((WObj*)split, dir);
329 ret->postmp=rp;
330 ret->sizetmp=rs;
331 /* Don't have to resize the other branch if the split is not
332 * in the wanted direction
334 if(split->dir!=dir)
335 ret->startnode=(from==TOP_OR_LEFT ? split->tl : split->br);
336 else
337 ret->startnode=(WObj*)split;
338 }else{
339 wcalcres(p, dir, primn, ds,
340 (p->tl==(WObj*)split ? TOP_OR_LEFT : BOTTOM_OR_RIGHT),
341 ret);
342 rp=ret->winpostmp;
343 rs=ret->winsizetmp;
345 if(rs!=ds && dir!=split->dir)
346 tree_calcresize(other, dir, primn, rs);
349 nsize=rs-s2;
351 split->knowsize=from;
352 split->tmpsize=nsize;
353 split->res=ANY;
355 if(from==TOP_OR_LEFT)
356 ret->winpostmp=rp;
357 else
358 ret->winpostmp=rp+s2;
359 ret->winsizetmp=nsize;
363 static int calcresize_obj(WObj *obj, int dir, int primn, int nsize,
364 WResizeTmp *ret)
366 WWsSplit *split=split_of(obj);
368 nsize=tree_calcresize(obj, dir, primn, nsize);
370 ret->dir=dir;
372 if(split==NULL){
373 ret->winsizetmp=ret->sizetmp=tree_size(obj, dir);
374 ret->winpostmp=ret->postmp=tree_pos(obj, dir);
375 ret->startnode=NULL;
376 ret->dir=dir;
377 }else{
378 wcalcres(split, dir, primn, nsize,
379 (split->tl==obj ? TOP_OR_LEFT : BOTTOM_OR_RIGHT),
380 ret);
382 return ret->winsizetmp;
386 /*}}}*/
390 /*{{{ Resize interface */
393 int calcresize_window(WWindow *wwin, int dir, int primn, int nsize,
394 WResizeTmp *ret)
396 return calcresize_obj((WObj*)wwin, dir, primn, nsize, ret);
400 void resize_tmp(const WResizeTmp *tmp)
402 tree_do_resize(tmp->startnode, tmp->dir, tmp->postmp, tmp->sizetmp);
406 /*}}}*/
409 /*{{{ Split */
412 WWsSplit *create_split(int dir, WObj *tl, WObj *br, WRectangle geom)
414 WWsSplit *split=ALLOC(WWsSplit);
416 if(split==NULL){
417 warn_err();
418 return NULL;
421 WOBJ_INIT(split, WWsSplit);
423 split->dir=dir;
424 split->tl=tl;
425 split->br=br;
426 split->geom=geom;
427 split->parent=NULL;
428 split->current=0;
430 return split;
434 static WWindow *do_split_at(WWorkspace *ws, WObj *obj, int dir, int primn,
435 int minsize, WSplitCreate *fn)
437 int s, sn, gs, pos;
438 WWsSplit *split, *nsplit;
439 WRectangle geom;
440 WWindow *nwin;
441 WResizeTmp rtmp;
443 assert(obj!=NULL);
445 if(primn!=TOP_OR_LEFT && primn!=BOTTOM_OR_RIGHT)
446 primn=BOTTOM_OR_RIGHT;
447 if(dir!=HORIZONTAL && dir!=VERTICAL)
448 dir=VERTICAL;
450 /* First possibly resize <obj> so that the area allocated by it
451 * is big enough to fit the new object and <obj> itself without
452 * them becoming too small.
455 s=tree_size(obj, dir);
456 sn=s/2;
458 if(sn<minsize)
459 sn=minsize;
461 gs=tree_calcresize(obj, dir, primn, s-sn);
463 if(gs+sn>s){
464 s=calcresize_obj(obj, dir, ANY, gs+sn, &rtmp);
465 if(gs+sn>s){
466 warn("Cannot resize: minimum size reached");
467 return NULL;
469 resize_tmp(&rtmp);
472 /* Create split and new window
474 geom=tree_geom(obj);
476 nsplit=create_split(dir, NULL, NULL, geom);
478 if(nsplit==NULL)
479 return NULL;
481 if(dir==VERTICAL){
482 if(primn==BOTTOM_OR_RIGHT)
483 geom.y+=gs;
484 geom.h=sn;
485 }else{
486 if(primn==BOTTOM_OR_RIGHT)
487 geom.x+=gs;
488 geom.w=sn;
491 nwin=fn(SCREEN_OF(ws), geom);
493 if(nwin==NULL){
494 free(nsplit);
495 return NULL;
498 add_workspace_window(ws, nwin);
500 /* Now that everything's ok, resize (and move) the original
501 * obj.
503 pos=tree_pos(obj, dir);
504 if(primn!=BOTTOM_OR_RIGHT)
505 pos+=sn;
506 s=tree_calcresize(obj, dir, primn, gs);
507 tree_do_resize(obj, dir, pos, s);
509 /* Set up split structure
511 split=split_of(obj);
513 set_split_of(obj, nsplit);
514 nwin->split=nsplit;
516 if(primn==BOTTOM_OR_RIGHT){
517 nsplit->tl=obj;
518 nsplit->br=(WObj*)nwin;
519 }else{
520 nsplit->tl=(WObj*)nwin;
521 nsplit->br=obj;
524 if(split!=NULL){
525 if(obj==split->tl)
526 split->tl=(WObj*)nsplit;
527 else
528 split->br=(WObj*)nsplit;
529 nsplit->parent=split;
530 }else{
531 ws->splitree=(WObj*)nsplit;
534 return nwin;
538 WWindow *split_window(WWindow *wwin, int dir, int minsize, WSplitCreate *fn)
540 WWorkspace *ws=FIND_PARENT(wwin, WWorkspace);
542 assert(ws!=NULL);
544 return do_split_at(ws, (WObj*)wwin, dir, BOTTOM_OR_RIGHT, minsize, fn);
548 WWindow *split_toplevel(WWorkspace *ws, int dir, int primn, int minsize,
549 WSplitCreate *fn)
551 if(ws->splitree==NULL)
552 return NULL;
554 return do_split_at(ws, ws->splitree, dir, primn, minsize, fn);
558 /*}}}*/
561 /*{{{ Navigation */
564 static WWindow *left_or_topmost_current(WObj *obj, int dir)
566 WWsSplit *split;
568 while(1){
569 if(WOBJ_IS(obj, WWindow))
570 return (WWindow*)obj;
572 assert(WOBJ_IS(obj, WWsSplit));
574 split=(WWsSplit*)obj;
576 if(split->dir==dir){
577 obj=split->tl;
578 continue;
581 obj=(split->current==0 ? split->tl : split->br);
584 assert(0);
588 static WWindow *right_or_bottomost_current(WObj *obj, int dir)
590 WWsSplit *split;
592 while(1){
593 if(WOBJ_IS(obj, WWindow))
594 return (WWindow*)obj;
596 assert(WOBJ_IS(obj, WWsSplit));
598 split=(WWsSplit*)obj;
600 if(split->dir==dir){
601 obj=split->br;
602 continue;
605 obj=(split->current==0 ? split->tl : split->br);
608 assert(0);
612 WWindow *find_current(WWorkspace *ws)
614 return left_or_topmost_current(ws->splitree, -1);
618 static WWsSplit *find_split(WObj *obj, int dir, int *from)
620 WWsSplit *split;
622 if(WOBJ_IS(obj, WWindow))
623 split=((WWindow*)obj)->split;
624 else
625 split=((WWsSplit*)obj)->parent;
627 while(split!=NULL){
628 if(split->dir==dir){
629 if(obj==split->tl)
630 *from=TOP_OR_LEFT;
631 else
632 *from=BOTTOM_OR_RIGHT;
633 break;
636 obj=(WObj*)split;
637 split=split->parent;
640 return split;
644 static WWindow *right_or_down(WWindow *wwin, int dir)
646 WObj *prev=(WObj*)wwin;
647 WWorkspace *ws;
648 WWsSplit *split;
649 int from;
651 while(1){
652 split=find_split(prev, dir, &from);
654 if(split==NULL)
655 break;
657 if(from==TOP_OR_LEFT)
658 return left_or_topmost_current(split->br, dir);
660 prev=(WObj*)split;
663 return left_or_topmost_current(prev, dir);
667 static WWindow *up_or_left(WWindow *wwin, int dir)
669 WObj *prev=(WObj*)wwin;
670 WWorkspace *ws;
671 WWsSplit *split;
672 int from;
674 while(1){
675 split=find_split(prev, dir, &from);
677 if(split==NULL)
678 break;
680 if(from==BOTTOM_OR_RIGHT)
681 return right_or_bottomost_current(split->tl, dir);
683 prev=(WObj*)split;
686 return right_or_bottomost_current(prev, dir);
690 static void goto_wwin(WWindow *wwin)
692 if(wwin==NULL || wglobal.current_wswindow==wwin)
693 return;
694 set_previous((WThing*)wwin);
695 warp(wwin);
698 void goto_above(WWindow *wwin)
700 if(wwin!=NULL)
701 goto_wwin(up_or_left(wwin, VERTICAL));
704 void goto_below(WWindow *wwin)
706 if(wwin!=NULL)
707 goto_wwin(right_or_down(wwin, VERTICAL));
710 void goto_left(WWindow *wwin)
712 if(wwin!=NULL)
713 goto_wwin(up_or_left(wwin, HORIZONTAL));
717 void goto_right(WWindow *wwin)
719 if(wwin!=NULL)
720 goto_wwin(right_or_down(wwin, HORIZONTAL));
724 /*}}}*/
727 /*{{{ Remove */
730 void workspace_remove_window(WWorkspace *ws, WWindow *wwin)
732 WWsSplit *split;
734 split=wwin->split;
736 if(split==NULL){
737 ws->splitree=NULL;
738 return;
741 if(split->tl==(WObj*)wwin)
742 split->tl=NULL;
743 else
744 split->br=NULL;
746 remove_split(ws, split);
750 bool remove_split(WWorkspace *ws, WWsSplit *split)
752 WWsSplit *split2;
753 WObj *other;
754 int osize, nsize, npos;
755 int primn;
757 if(split->tl==NULL){
758 other=split->br;
759 primn=TOP_OR_LEFT;
760 }else{
761 other=split->tl;
762 primn=BOTTOM_OR_RIGHT;
765 split2=split->parent;
767 if(split2!=NULL){
768 if((WObj*)split==split2->tl)
769 split2->tl=other;
770 else
771 split2->br=other;
772 }else{
773 ws->splitree=other;
776 if(other==NULL)
777 return FALSE;
779 if(WOBJ_IS(other, WWindow))
780 ((WWindow*)other)->split=split2;
781 else
782 ((WWsSplit*)other)->parent=split2;
784 if(wglobal.opmode!=OPMODE_DEINIT){
785 nsize=tree_size((WObj*)split, split->dir);
786 npos=tree_pos((WObj*)split, split->dir);
787 nsize=tree_calcresize(other, split->dir, primn, nsize);
788 tree_do_resize(other, split->dir, npos, nsize);
791 free(split);
793 return TRUE;
797 /*}}}*/
800 /*{{{ Misc */
803 void set_current_wswindow(WWindow *wwin)
805 WWsSplit *split=wwin->split;
806 WObj *prev=(WObj*)wwin;
808 while(split!=NULL){
809 split->current=(split->tl==prev ? 0 : 1);
810 prev=(WObj*)split;
811 split=split->parent;
814 wglobal.current_wswindow=wwin;
818 /*}}}*/