Released version 3-2015061300
[notion.git] / mod_tiling / split-stdisp.c
blobeb1bd4f97f3e93344ee3ebea704cfc151bcbe363
1 /*
2 * ion/mod_tiling/split-stdisp.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <libtu/minmax.h>
10 #include <ioncore/common.h>
11 #include <ioncore/mplex.h>
12 #include <ioncore/resize.h>
13 #include "split.h"
14 #include "split-stdisp.h"
15 #include "tiling.h"
18 /*{{{ Helper routines */
21 #define STDISP_IS_HORIZONTAL(STDISP) \
22 ((STDISP)->orientation==REGION_ORIENTATION_HORIZONTAL)
24 #define STDISP_IS_VERTICAL(STDISP) \
25 ((STDISP)->orientation==REGION_ORIENTATION_VERTICAL)
27 #define STDISP_GROWS_L_TO_R(STDISP) (STDISP_IS_HORIZONTAL(STDISP) && \
28 ((STDISP)->corner==MPLEX_STDISP_TL || \
29 (STDISP)->corner==MPLEX_STDISP_BL))
31 #define STDISP_GROWS_R_TO_L(STDISP) (STDISP_IS_HORIZONTAL(STDISP) && \
32 ((STDISP)->corner==MPLEX_STDISP_TR || \
33 (STDISP)->corner==MPLEX_STDISP_BR))
35 #define STDISP_GROWS_T_TO_B(STDISP) (STDISP_IS_VERTICAL(STDISP) && \
36 ((STDISP)->corner==MPLEX_STDISP_TL || \
37 (STDISP)->corner==MPLEX_STDISP_TR))
39 #define STDISP_GROWS_B_TO_T(STDISP) (STDISP_IS_VERTICAL(STDISP) && \
40 ((STDISP)->corner==MPLEX_STDISP_BL || \
41 (STDISP)->corner==MPLEX_STDISP_BR))
43 #define GEOM(S) (((WSplit*)S)->geom)
45 #define IMPLIES(X, Y) (!(X) || (Y))
48 static int other_dir(int dir)
50 return (dir==SPLIT_VERTICAL ? SPLIT_HORIZONTAL : SPLIT_VERTICAL);
54 int stdisp_recommended_w(WSplitST *stdisp)
56 if(stdisp->regnode.reg==NULL)
57 return CF_STDISP_MIN_SZ;
59 if(stdisp->fullsize && stdisp->orientation==REGION_ORIENTATION_HORIZONTAL){
60 WTiling *ws=REGION_MANAGER_CHK(stdisp->regnode.reg, WTiling);
61 assert(ws!=NULL);
62 return REGION_GEOM(ws).w;
65 return MAXOF(CF_STDISP_MIN_SZ, region_min_w(stdisp->regnode.reg));
69 int stdisp_recommended_h(WSplitST *stdisp)
71 if(stdisp->regnode.reg==NULL)
72 return CF_STDISP_MIN_SZ;
74 if(stdisp->fullsize && stdisp->orientation==REGION_ORIENTATION_VERTICAL){
75 WTiling *ws=REGION_MANAGER_CHK(stdisp->regnode.reg, WTiling);
76 assert(ws!=NULL);
77 return REGION_GEOM(ws).h;
80 return MAXOF(CF_STDISP_MIN_SZ, region_min_h(stdisp->regnode.reg));
84 static bool stdisp_dir_ok(WSplitSplit *p, WSplitST *stdisp)
86 assert(p->tl==(WSplit*)stdisp || p->br==(WSplit*)stdisp);
88 return (IMPLIES(STDISP_IS_HORIZONTAL(stdisp), p->dir==SPLIT_VERTICAL) &&
89 IMPLIES(STDISP_IS_VERTICAL(stdisp), p->dir==SPLIT_HORIZONTAL));
93 /*}}}*/
96 /*{{{ New rotation and flipping primitives */
99 static void replace(WSplitSplit *a, WSplitSplit *p)
101 if(((WSplit*)a)->parent!=NULL)
102 splitinner_replace(((WSplit*)a)->parent, (WSplit*)a, (WSplit*)p);
103 else
104 splittree_changeroot((WSplit*)a, (WSplit*)p);
108 /* Yes, it is overparametrised */
109 static void rotate_right(WSplitSplit *a, WSplitSplit *p, WSplit *y)
111 assert(a->tl==(WSplit*)p && p->tl==y);
113 /* Right rotation:
114 * a p
115 * / \ / \
116 * p x => y a
117 * / \ / \
118 * y ? ? x
121 a->tl=p->br;
122 a->tl->parent=(WSplitInner*)a;
123 replace(a, p);
124 p->br=(WSplit*)a;
125 ((WSplit*)a)->parent=(WSplitInner*)p;
129 static void rot_rs_rotate_right(WSplitSplit *a, WSplitSplit *p, WSplit *y)
131 WRectangle xg, yg, pg, ag, qg;
132 WSplit *x=a->br, *q=p->br;
134 assert(a->dir==other_dir(p->dir));
136 qg=GEOM(q);
137 xg=GEOM(x);
138 yg=GEOM(y);
139 pg=GEOM(p);
140 ag=GEOM(a);
142 if(a->dir==SPLIT_HORIZONTAL){
143 /* yyxx yyyy
144 * ??xx => ??xx
145 * ??xx ??xx
147 yg.w=ag.w;
148 pg.w=ag.w;
149 xg.h=qg.h;
150 ag.h=qg.h;
151 xg.y=qg.y;
152 ag.y=qg.y;
153 }else{
154 /* y?? y??
155 * y?? y??
156 * xxx => yxx
157 * xxx yxx
159 yg.h=ag.h;
160 pg.h=ag.h;
161 xg.w=qg.w;
162 ag.w=qg.w;
163 xg.x=qg.x;
164 ag.x=qg.x;
167 rotate_right(a, p, y);
169 GEOM(p)=pg;
170 GEOM(a)=ag;
172 split_do_resize(x, &xg, PRIMN_TL, PRIMN_TL, FALSE);
173 split_do_resize(y, &yg, PRIMN_BR, PRIMN_BR, FALSE);
178 static void rotate_left(WSplitSplit *a, WSplitSplit *p, WSplit *y)
180 assert(a->br==(WSplit*)p && p->br==y);
182 /* Left rotation:
183 * a p
184 * / \ / \
185 * x p => a y
186 * / \ / \
187 * ? y x ?
190 a->br=p->tl;
191 a->br->parent=(WSplitInner*)a;
192 replace(a, p);
193 p->tl=(WSplit*)a;
194 ((WSplit*)a)->parent=(WSplitInner*)p;
198 static void rot_rs_rotate_left(WSplitSplit *a, WSplitSplit *p, WSplit *y)
200 WRectangle xg, yg, pg, ag, qg;
201 WSplit *x=a->tl, *q=p->tl;
203 assert(a->dir==other_dir(p->dir));
205 qg=GEOM(q);
206 xg=GEOM(x);
207 yg=GEOM(y);
208 pg=GEOM(p);
209 ag=GEOM(a);
211 if(a->dir==SPLIT_HORIZONTAL){
212 /* xx?? xx??
213 * xx?? => xx??
214 * xxyy yyyy
216 yg.w=ag.w;
217 yg.x=ag.x;
218 pg.w=ag.w;
219 pg.x=ag.x;
220 xg.h=qg.h;
221 ag.h=qg.h;
222 }else{
223 /* xxx xxy
224 * xxx xxy
225 * ??y => ??y
226 * ??y ??y
228 yg.h=ag.h;
229 yg.y=ag.y;
230 pg.h=ag.h;
231 pg.y=ag.y;
232 xg.w=qg.w;
233 ag.w=qg.w;
236 rotate_left(a, p, y);
238 GEOM(p)=pg;
239 GEOM(a)=ag;
241 split_do_resize(x, &xg, PRIMN_BR, PRIMN_BR, FALSE);
242 split_do_resize(y, &yg, PRIMN_TL, PRIMN_TL, FALSE);
247 static void flip_right(WSplitSplit *a, WSplitSplit *p)
249 assert(a->tl==(WSplit*)p);
251 /* Right flip:
252 * a p
253 * / \ / \
254 * p x => a y
255 * / \ / \
256 * ? y ? x
259 a->tl=p->tl;
260 a->tl->parent=(WSplitInner*)a;
261 replace(a, p);
262 p->tl=(WSplit*)a;
263 ((WSplit*)a)->parent=(WSplitInner*)p;
267 static void rot_rs_flip_right(WSplitSplit *a, WSplitSplit *p)
269 WRectangle xg, yg, pg, ag, qg;
270 WSplit *x=a->br, *q=p->tl, *y=p->br;
272 assert(a->dir==other_dir(p->dir));
274 qg=GEOM(q);
275 xg=GEOM(x);
276 yg=GEOM(y);
277 pg=GEOM(p);
278 ag=GEOM(a);
280 if(a->dir==SPLIT_HORIZONTAL){
281 /* ??xx ??xx
282 * ??xx => ??xx
283 * yyxx yyyy
285 yg.w=ag.w;
286 pg.w=ag.w;
287 ag.h=qg.h;
288 xg.h=qg.h;
289 }else{
290 /* ??y ??y
291 * ??y ??y
292 * xxx => xxy
293 * xxx xxy
295 yg.h=ag.h;
296 pg.h=ag.h;
297 ag.w=qg.w;
298 xg.w=qg.w;
301 flip_right(a, p);
303 GEOM(p)=pg;
304 GEOM(a)=ag;
306 split_do_resize(x, &xg, PRIMN_BR, PRIMN_BR, FALSE);
307 split_do_resize(y, &yg, PRIMN_BR, PRIMN_BR, FALSE);
311 static void flip_left(WSplitSplit *a, WSplitSplit *p)
313 assert(a->br==(WSplit*)p);
315 /* Left flip:
316 * a p
317 * / \ / \
318 * x p => y a
319 * / \ / \
320 * y ? x ?
323 a->br=p->br;
324 a->br->parent=(WSplitInner*)a;
325 replace(a, p);
326 p->br=(WSplit*)a;
327 ((WSplit*)a)->parent=(WSplitInner*)p;
331 static void rot_rs_flip_left(WSplitSplit *a, WSplitSplit *p)
333 WRectangle xg, yg, pg, ag, qg;
334 WSplit *x=a->tl, *q=p->br, *y=p->tl;
336 assert(a->dir==other_dir(p->dir));
338 qg=GEOM(q);
339 xg=GEOM(x);
340 yg=GEOM(y);
341 pg=GEOM(p);
342 ag=GEOM(a);
344 if(a->dir==SPLIT_HORIZONTAL){
345 /* xxyy yyyy
346 * xx?? => xx??
347 * xx?? xx??
349 yg.x=ag.x;
350 yg.w=ag.w;
351 pg.x=ag.x;
352 pg.w=ag.w;
353 xg.h=qg.h;
354 xg.y=qg.y;
355 ag.h=qg.h;
356 ag.y=qg.y;
357 }else{
358 /* xxx yxx
359 * xxx yxx
360 * y?? => y??
361 * y?? y??
363 yg.y=ag.y;
364 yg.h=ag.h;
365 pg.y=ag.y;
366 pg.h=ag.h;
367 xg.w=qg.w;
368 xg.x=qg.x;
369 ag.w=qg.w;
370 ag.x=qg.x;
373 flip_left(a, p);
375 GEOM(p)=pg;
376 GEOM(a)=ag;
378 split_do_resize(x, &xg, PRIMN_TL, PRIMN_TL, FALSE);
379 split_do_resize(y, &yg, PRIMN_TL, PRIMN_TL, FALSE);
383 static void rot_para_right(WSplitSplit *a, WSplitSplit *p,
384 WSplit *y)
386 rotate_right(a, p, y);
387 if(a->dir==SPLIT_VERTICAL){
388 GEOM(p).y=GEOM(a).y;
389 GEOM(p).h=GEOM(a).h;
390 GEOM(a).y=GEOM(a->tl).y;
391 GEOM(a).h=GEOM(a->br).y+GEOM(a->br).h-GEOM(a).y;
392 }else{
393 GEOM(p).x=GEOM(a).x;
394 GEOM(p).w=GEOM(a).w;
395 GEOM(a).x=GEOM(a->tl).x;
396 GEOM(a).w=GEOM(a->br).x+GEOM(a->br).w-GEOM(a).x;
401 static void rot_para_left(WSplitSplit *a, WSplitSplit *p,
402 WSplit *y)
404 rotate_left(a, p, y);
405 if(a->dir==SPLIT_VERTICAL){
406 GEOM(p).y=GEOM(a).y;
407 GEOM(p).h=GEOM(a).h;
408 GEOM(a).h=GEOM(a->br).y+GEOM(a->br).h-GEOM(a).y;
409 }else{
410 GEOM(p).x=GEOM(a).x;
411 GEOM(p).w=GEOM(a).w;
412 GEOM(a).w=GEOM(a->br).x+GEOM(a->br).w-GEOM(a).x;
417 /*}}}*/
420 /*{{{ Sink */
423 static bool do_try_sink_stdisp_orth(WSplitSplit *p, WSplitST *stdisp,
424 WSplitSplit *other, bool force)
426 bool doit=force;
428 assert(p->dir==other_dir(other->dir));
429 assert(stdisp_dir_ok(p, stdisp));
431 if(STDISP_GROWS_T_TO_B(stdisp) || STDISP_GROWS_L_TO_R(stdisp)){
432 if(STDISP_GROWS_L_TO_R(stdisp)){
433 assert(other->dir==SPLIT_HORIZONTAL);
434 if(other->tl->geom.w>=stdisp_recommended_w(stdisp))
435 doit=TRUE;
436 }else{ /* STDISP_GROWS_T_TO_B */
437 assert(other->dir==SPLIT_VERTICAL);
438 if(other->tl->geom.h>=stdisp_recommended_h(stdisp))
439 doit=TRUE;
442 if(doit){
443 if(p->br==(WSplit*)stdisp)
444 rot_rs_flip_right(p, other);
445 else /* p->tl==stdisp */
446 rot_rs_rotate_left(p, other, other->br);
448 }else{ /* STDISP_GROWS_B_TO_T or STDISP_GROW_R_TO_L */
449 if(STDISP_GROWS_R_TO_L(stdisp)){
450 assert(other->dir==SPLIT_HORIZONTAL);
451 if(other->br->geom.w>=stdisp_recommended_w(stdisp))
452 doit=TRUE;
453 }else{ /* STDISP_GROWS_B_TO_T */
454 assert(other->dir==SPLIT_VERTICAL);
455 if(other->br->geom.h>=stdisp_recommended_h(stdisp))
456 doit=TRUE;
459 if(doit){
460 if(p->tl==(WSplit*)stdisp)
461 rot_rs_flip_left(p, other);
462 else /* p->br==stdisp */
463 rot_rs_rotate_right(p, other, other->tl);
467 return doit;
471 static bool do_try_sink_stdisp_para(WSplitSplit *p, WSplitST *stdisp,
472 WSplitSplit *other, bool force)
474 if(!force){
475 if(STDISP_IS_HORIZONTAL(stdisp)){
476 if(stdisp_recommended_w(stdisp)>=GEOM(p).w)
477 return FALSE;
478 }else{
479 if(stdisp_recommended_h(stdisp)>=GEOM(p).h)
480 return FALSE;
484 if(p->tl==(WSplit*)stdisp)
485 rot_para_left(p, other, other->br);
486 else
487 rot_para_right(p, other, other->tl);
489 return TRUE;
493 bool split_try_sink_stdisp(WSplitSplit *node, bool iterate, bool force)
495 bool didsomething=FALSE;
496 bool more=TRUE;
498 /*assert(OBJ_IS_EXACTLY(node, WSplitSplit));*/
500 while(more){
501 WSplit *tl=node->tl;
502 WSplit *br=node->br;
503 WSplitSplit *other=NULL;
504 WSplitST *st;
506 if(OBJ_IS(tl, WSplitST)){
507 st=(WSplitST*)tl;
508 other=OBJ_CAST(br, WSplitSplit);
509 }else if(OBJ_IS(br, WSplitST)){
510 st=(WSplitST*)br;
511 other=OBJ_CAST(tl, WSplitSplit);
512 }else{
513 break;
516 if(other==NULL)
517 break;
519 if(!stdisp_dir_ok(node, st))
520 break;
522 if(other->dir==other_dir(node->dir)){
523 if(!do_try_sink_stdisp_orth(node, st, other, force))
524 break;
525 }else /*if(br->dir==node->dir)*/{
526 if(!do_try_sink_stdisp_para(node, st, other, force))
527 break;
529 didsomething=TRUE;
530 more=iterate;
533 return didsomething;
537 /*}}}*/
540 /*{{{ Unsink */
543 static bool do_try_unsink_stdisp_orth(WSplitSplit *a, WSplitSplit *p,
544 WSplitST *stdisp, bool force)
546 bool doit=force;
548 assert(p->dir==other_dir(a->dir));
549 assert(stdisp_dir_ok(p, stdisp));
551 if(STDISP_GROWS_T_TO_B(stdisp) || STDISP_GROWS_L_TO_R(stdisp)){
552 if(STDISP_GROWS_L_TO_R(stdisp)){
553 assert(a->dir==SPLIT_HORIZONTAL);
554 if(GEOM(stdisp).w<stdisp_recommended_w(stdisp))
555 doit=TRUE;
556 }else{ /* STDISP_GROWS_T_TO_B */
557 assert(a->dir==SPLIT_VERTICAL);
558 if(GEOM(stdisp).h<stdisp_recommended_h(stdisp))
559 doit=TRUE;
562 if(doit){
563 if((WSplit*)p==a->tl){
564 if((WSplit*)stdisp==p->br)
565 rot_rs_flip_right(a, p);
566 else /*stdisp==p->tl*/
567 rot_rs_rotate_right(a, p, (WSplit*)stdisp);
568 }else{ /*p==a->br*/
569 #if 0
570 /* abnormal cases. */
571 warn(TR("Status display in bad split configuration."));
572 return FALSE;
573 #else
574 if((WSplit*)stdisp==p->br)
575 rot_rs_rotate_left(a, p, (WSplit*)stdisp);
576 else /*stdisp==p->tl*/
577 rot_rs_flip_left(a, p);
578 #endif
581 }else{ /*STDISP_GROWS_B_TO_T || STDISP_GROWS_R_TO_L*/
582 if(STDISP_GROWS_R_TO_L(stdisp)){
583 assert(a->dir==SPLIT_HORIZONTAL);
584 if(GEOM(stdisp).w<stdisp_recommended_w(stdisp))
585 doit=TRUE;
586 }else{ /* STDISP_GROWS_B_TO_T */
587 assert(a->dir==SPLIT_VERTICAL);
588 if(GEOM(stdisp).h<stdisp_recommended_h(stdisp))
589 doit=TRUE;
592 if(doit){
593 if((WSplit*)p==a->tl){
594 #if 0
595 /* abnormal cases. */
596 warn(TR("Status display in bad split configuration."));
597 return FALSE;
598 #else
599 if((WSplit*)stdisp==p->br)
600 rot_rs_flip_right(a, p);
601 else /*stdisp==p->tl*/
602 rot_rs_rotate_right(a, p, (WSplit*)stdisp);
603 #endif
604 }else{ /*p==a->br*/
605 if((WSplit*)stdisp==p->br)
606 rot_rs_rotate_left(a, p, (WSplit*)stdisp);
607 else /*stdisp==p->tl*/
608 rot_rs_flip_left(a, p);
613 return doit;
617 static bool do_try_unsink_stdisp_para(WSplitSplit *a, WSplitSplit *p,
618 WSplitST *stdisp, bool force)
620 if(!force){
621 if(STDISP_IS_HORIZONTAL(stdisp)){
622 if(stdisp_recommended_w(stdisp)<=GEOM(p).w)
623 return FALSE;
624 }else{
625 if(stdisp_recommended_h(stdisp)<=GEOM(p).h)
626 return FALSE;
631 if(a->tl==(WSplit*)p && p->tl==(WSplit*)stdisp){
632 rot_para_right(a, p, (WSplit*)stdisp);
633 }else if(a->br==(WSplit*)p && p->br==(WSplit*)stdisp){
634 rot_para_left(a, p, (WSplit*)stdisp);
635 }else{
636 warn(TR("Status display badly located in split tree."));
637 return FALSE;
640 return TRUE;
644 bool split_try_unsink_stdisp(WSplitSplit *node, bool iterate, bool force)
646 bool didsomething=FALSE;
647 bool more=TRUE;
649 /*assert(OBJ_IS_EXACTLY(node, WSplitSplit));*/
651 while(more){
652 WSplitSplit *p=OBJ_CAST(((WSplit*)node)->parent, WSplitSplit);
653 WSplit *tl=node->tl;
654 WSplit *br=node->br;
655 WSplitST *st;
657 if(p==NULL)
658 break;
660 if(OBJ_IS(tl, WSplitST))
661 st=(WSplitST*)tl;
662 else if(OBJ_IS(br, WSplitST))
663 st=(WSplitST*)br;
664 else
665 break;
667 if(!stdisp_dir_ok(node, st))
668 break;
670 if(p->dir==other_dir(node->dir)){
671 if(!do_try_unsink_stdisp_orth(p, node, st, force))
672 break;
673 }else /*if(p->dir==node->dir)*/{
674 if(!do_try_unsink_stdisp_para(p, node, st, force))
675 break;
678 didsomething=TRUE;
679 more=iterate;
682 return didsomething;
686 /*}}}*/
689 /*{{{ Sink or unsink */
692 bool split_regularise_stdisp(WSplitST *stdisp)
694 WSplitSplit *node=OBJ_CAST(((WSplit*)stdisp)->parent, WSplitSplit);
696 if(node==NULL)
697 return FALSE;
699 if(STDISP_IS_HORIZONTAL(stdisp)){
700 if(GEOM(stdisp).w<stdisp_recommended_w(stdisp))
701 return split_try_unsink_stdisp(node, TRUE, FALSE);
702 else if(GEOM(stdisp).w>stdisp_recommended_w(stdisp))
703 return split_try_sink_stdisp(node, TRUE, FALSE);
704 }else{
705 if(GEOM(stdisp).h<stdisp_recommended_h(stdisp))
706 return split_try_unsink_stdisp(node, TRUE, FALSE);
707 else if(GEOM(stdisp).h>stdisp_recommended_h(stdisp))
708 return split_try_sink_stdisp(node, TRUE, FALSE);
711 return FALSE;
715 /*}}}*/