Use 'goto_focus' instead of 'goto' in contrib
[notion.git] / ioncore / stacking.c
blob0c6480d7b5331ecc7479ca59b16dd6a22f1a6360
1 /*
2 * ion/ioncore/stacking.c
4 * Copyright (c) Tuomo Valkonen 1999-2009.
6 * See the included file LICENSE for details.
7 */
9 #include <libtu/rb.h>
10 #include <libtu/minmax.h>
12 #include "common.h"
13 #include "region.h"
14 #include "stacking.h"
15 #include "window.h"
16 #include "sizepolicy.h"
19 /*{{{ Alloc */
22 WStacking *create_stacking()
24 WStacking *st=ALLOC(WStacking);
26 if(st!=NULL){
27 st->reg=NULL;
28 st->above=NULL;
29 st->level=0;
30 st->szplcy=SIZEPOLICY_DEFAULT;
31 st->hidden=FALSE;
32 st->lnode=NULL;
33 st->pseudomodal=FALSE;
36 return st;
40 void stacking_free(WStacking *st)
42 assert(st->mgr_next==NULL && st->mgr_prev==NULL &&
43 st->next==NULL && st->prev==NULL &&
44 /*st->above==NULL &&*/
45 st->lnode==NULL &&
46 st->reg==NULL);
48 free(st);
52 /*}}}*/
55 /*{{{ Lookup */
58 static Rb_node stacking_of_reg=NULL;
61 WStacking *ioncore_find_stacking(WRegion *reg)
63 Rb_node node=NULL;
64 int found=0;
66 if(stacking_of_reg!=NULL)
67 node=rb_find_pkey_n(stacking_of_reg, reg, &found);
69 return (found ? (WStacking*)node->v.val : NULL);
73 void stacking_unassoc(WStacking *st)
75 Rb_node node=NULL;
76 int found=0;
78 if(st->reg==NULL)
79 return;
81 if(stacking_of_reg!=NULL)
82 node=rb_find_pkey_n(stacking_of_reg, st->reg, &found);
84 if(node!=NULL)
85 rb_delete_node(node);
87 st->reg=NULL;
91 bool stacking_assoc(WStacking *st, WRegion *reg)
93 assert(st->reg==NULL);
95 if(stacking_of_reg==NULL){
96 stacking_of_reg=make_rb();
97 if(stacking_of_reg==NULL)
98 return FALSE;
101 if(rb_insertp(stacking_of_reg, reg, st)==NULL)
102 return FALSE;
104 st->reg=reg;
105 return TRUE;
109 /*}}}*/
113 /*{{{ List processing */
116 static WStacking *link_lists(WStacking *l1, WStacking *l2)
118 /* As everywhere, doubly-linked lists without the forward
119 * link in last item!
121 WStacking *tmp=l2->prev;
122 l1->prev->next=l2;
123 l2->prev=l1->prev;
124 l1->prev=tmp;
125 return l1;
129 static WStacking *link_list_before(WStacking *l1,
130 WStacking *i1,
131 WStacking *l2)
133 WStacking *tmp;
135 if(i1==l1)
136 return link_lists(l2, l1);
138 l2->prev->next=i1;
139 i1->prev->next=l2;
140 tmp=i1->prev;
141 i1->prev=l2->prev;
142 l2->prev=tmp;
144 return l1;
148 static WStacking *link_list_after(WStacking *l1,
149 WStacking *i1,
150 WStacking *l2)
152 WStacking *tmp;
154 if(i1==l1->prev)
155 return link_lists(l1, l2);
157 i1->next->prev=l2->prev;
158 l2->prev->next=i1->next;
159 i1->next=l2;
160 l2->prev=i1;
162 return l1;
166 WStacking *stacking_unstack(WWindow *par, WStacking *regst)
168 WStacking *nxt=NULL, *st;
170 /*st=regst->next;*/
172 UNLINK_ITEM(par->stacking, regst, next, prev);
174 /*while(st!=NULL){*/
175 for(st=par->stacking; st!=NULL; st=st->next){
176 if(st->above==regst){
177 st->above=NULL;
178 nxt=st;
180 /*st=st->next;*/
183 if(nxt==NULL)
184 nxt=regst->above;
186 if(regst->above==NULL)
187 regst->above=NULL;
189 return nxt;
193 static bool cf(WStackingFilter *filt, void *filt_data, WStacking *st)
195 return (filt==NULL || filt(st, filt_data));
199 static bool check_unweave(WStacking *st)
201 /* 2: unknown, 1: yes, 0: no */
203 if(st->to_unweave==2){
204 if(st->above!=NULL)
205 st->to_unweave=check_unweave(st->above);
206 else
207 st->to_unweave=0;
210 return st->to_unweave;
214 WStacking *stacking_unweave(WStacking **stacking,
215 WStackingFilter *filt, void *filt_data)
217 WStacking *np=NULL;
218 WStacking *st, *next;
220 for(st=*stacking; st!=NULL; st=st->next){
221 st->to_unweave=2;
222 if(st->above==NULL && cf(filt, filt_data, st))
223 st->to_unweave=1;
226 for(st=*stacking; st!=NULL; st=st->next)
227 check_unweave(st);
229 for(st=*stacking; st!=NULL; st=next){
230 next=st->next;
231 if(st->to_unweave==1){
232 UNLINK_ITEM(*stacking, st, next, prev);
233 LINK_ITEM(np, st, next, prev);
237 return np;
241 static int check_above_lvl(WStacking *st)
243 if(st->above==NULL)
244 return st->level;
245 st->level=check_above_lvl(st->above);
246 return st->level;
250 static void enforce_level_sanity(WStacking **np)
252 WStacking *st;
254 /* Make sure that the levels of stuff stacked 'above' match
255 * the level of the thing stacked above.
257 for(st=*np; st!=NULL; st=st->next)
258 check_above_lvl(st);
260 /* And now make sure things are ordered by levels. */
261 st=*np;
262 while(st->next!=NULL){
263 if(st->next->level < st->level){
264 WStacking *st2=st->next;
265 UNLINK_ITEM(*np, st2, next, prev);
266 LINK_ITEM_BEFORE(*np, st2, st, next, prev);
267 if(st2->prev!=NULL)
268 st=st2->prev;
269 }else{
270 st=st->next;
276 static void get_bottom(WStacking *st, Window fb_win,
277 Window *other, int *mode)
279 Window bottom=None, top=None;
281 while(st!=NULL){
282 if(st->reg!=NULL){
283 region_stacking(st->reg, &bottom, &top);
284 if(bottom!=None){
285 *other=bottom;
286 *mode=Below;
287 return;
290 st=st->next;
293 *other=fb_win;
294 *mode=Above;
298 static void stacking_do_weave(WStacking **stacking, WStacking **np,
299 bool below, Window fb_win)
301 WStacking *st, *ab;
302 uint lvl;
303 Window other;
304 int mode;
306 if(*np==NULL)
307 return;
309 /* Should do nothing.. */
310 enforce_level_sanity(np);
312 ab=*stacking;
314 while(*np!=NULL){
315 lvl=(*np)->level;
317 while(ab!=NULL){
318 if(ab->level>lvl || (below && ab->level==lvl))
319 break;
320 ab=ab->next;
322 get_bottom(ab, fb_win, &other, &mode);
324 st=*np;
326 UNLINK_ITEM(*np, st, next, prev);
328 region_restack(st->reg, other, mode);
330 if(ab!=NULL){
331 LINK_ITEM_BEFORE(*stacking, ab, st, next, prev);
332 }else{
333 LINK_ITEM_LAST(*stacking, st, next, prev);
339 void stacking_weave(WStacking **stacking, WStacking **np, bool below)
341 stacking_do_weave(stacking, np, below, None);
345 /*}}}*/
348 /*{{{ Raise/lower */
351 static bool is_above(WStacking *st, WStacking *p)
353 if(st->above==NULL)
354 return FALSE;
355 else if(st->above==p)
356 return TRUE;
357 else
358 return is_above(st->above, p);
362 static void collect_first(WStacking **dst, WStacking **src, WStacking *st)
364 UNLINK_ITEM(*src, st, next, prev);
365 LINK_ITEM_FIRST(*dst, st, next, prev);
369 static void collect_last(WStacking **dst, WStacking **src, WStacking *st)
371 UNLINK_ITEM(*src, st, next, prev);
372 LINK_ITEM_LAST(*dst, st, next, prev);
376 static void collect_above(WStacking **dst, WStacking **src, WStacking *regst)
378 WStacking *stabove, *stnext;
380 for(stabove=*src; stabove!=NULL; stabove=stnext){
381 stnext=stabove->next;
383 if(is_above(stabove, regst))
384 collect_last(dst, src, stabove);
389 static WStacking *unweave_subtree(WStacking **stacking, WStacking *regst,
390 bool parents)
392 WStacking *tmp=NULL;
394 if(parents){
395 WStacking *st=regst;
396 while(st!=NULL){
397 collect_first(&tmp, stacking, st);
398 st=st->above;
400 }else{
401 collect_first(&tmp, stacking, regst);
404 collect_above(&tmp, stacking, regst);
406 return tmp;
410 void stacking_restack(WStacking **stacking, WStacking *st, Window fb_win,
411 WStackingFilter *filt, void *filt_data, bool lower)
413 WStacking *tmp=unweave_subtree(stacking, st, lower);
415 stacking_do_weave(stacking, &tmp, lower, fb_win);
417 assert(tmp==NULL);
421 /*}}}*/
424 /*{{{ Stacking lists */
427 WStacking **window_get_stackingp(WWindow *wwin)
429 return &(wwin->stacking);
433 WStacking *window_get_stacking(WWindow *wwin)
435 return wwin->stacking;
439 /*}}}*/
442 /*{{{ Stacking list iteration */
445 void stacking_iter_init(WStackingIterTmp *tmp,
446 WStacking *st,
447 WStackingFilter *filt,
448 void *filt_data)
450 tmp->st=st;
451 tmp->filt=filt;
452 tmp->filt_data=filt_data;
456 WStacking *stacking_iter_nodes(WStackingIterTmp *tmp)
458 WStacking *next=NULL;
460 while(tmp->st!=NULL){
461 next=tmp->st;
462 tmp->st=tmp->st->next;
463 if(cf(tmp->filt, tmp->filt_data, next))
464 break;
465 next=NULL;
468 return next;
472 WRegion *stacking_iter(WStackingIterTmp *tmp)
474 WStacking *st=stacking_iter_nodes(tmp);
475 return (st!=NULL ? st->reg : NULL);
479 void stacking_iter_mgr_init(WStackingIterTmp *tmp,
480 WStacking *st,
481 WStackingFilter *filt,
482 void *filt_data)
484 tmp->st=st;
485 tmp->filt=filt;
486 tmp->filt_data=filt_data;
490 WStacking *stacking_iter_mgr_nodes(WStackingIterTmp *tmp)
492 WStacking *next=NULL;
494 while(tmp->st!=NULL){
495 next=tmp->st;
496 tmp->st=tmp->st->mgr_next;
497 if(cf(tmp->filt, tmp->filt_data, next))
498 break;
499 next=NULL;
502 return next;
506 WRegion *stacking_iter_mgr(WStackingIterTmp *tmp)
508 WStacking *st=stacking_iter_mgr_nodes(tmp);
509 return (st!=NULL ? st->reg : NULL);
513 /*}}}*/
516 /*{{{ Focus */
519 uint stacking_min_level(WStacking *stacking,
520 WStackingFilter *include_filt,
521 void *filt_data)
523 uint min_level=STACKING_LEVEL_BOTTOM;
524 WStacking *st=NULL;
526 if(stacking==NULL)
527 return STACKING_LEVEL_BOTTOM;
529 st=stacking;
531 st=st->prev;
533 if(st->reg!=NULL
534 && !(st->reg->flags&REGION_SKIP_FOCUS)
535 && cf(include_filt, filt_data, st)){
537 if(st->level>=STACKING_LEVEL_MODAL1)
538 min_level=st->level;
540 break;
542 }while(st!=stacking);
544 return min_level;
548 WStacking *stacking_find_to_focus(WStacking *stacking,
549 WStacking *to_try,
550 WStackingFilter *include_filt,
551 WStackingFilter *approve_filt,
552 void *filt_data)
554 uint min_level=STACKING_LEVEL_BOTTOM;
555 WStacking *st=NULL, *found=NULL;
557 if(stacking==NULL)
558 return NULL;
560 st=stacking;
562 st=st->prev;
564 if(st->reg==NULL)
565 continue;
567 if(st!=to_try && (st->reg->flags&REGION_SKIP_FOCUS ||
568 !cf(include_filt, filt_data, st))){
569 /* skip */
570 continue;
573 if(st->level<min_level)
574 break; /* no luck */
576 if(st==to_try)
577 return st;
579 if(found==NULL && cf(approve_filt, filt_data, st)){
580 found=st;
581 if(to_try==NULL)
582 break;
585 if(st->level>=STACKING_LEVEL_MODAL1)
586 min_level=maxof(min_level, st->level);
587 }while(st!=stacking);
589 return found;
593 static bool mapped_filt(WStacking *st, void *unused)
595 return (st->reg!=NULL && REGION_IS_MAPPED(st->reg));
599 static bool mapped_filt_neq(WStacking *st, void *st_neq)
601 return (st!=(WStacking*)st_neq && mapped_filt(st, NULL));
605 static bool mgr_filt(WStacking *st, void *mgr_)
607 return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)mgr_);
611 WStacking *stacking_find_to_focus_mapped(WStacking *stacking,
612 WStacking *to_try,
613 WRegion *mgr)
615 if(mgr==NULL){
616 return stacking_find_to_focus(stacking, to_try, mapped_filt,
617 NULL, NULL);
618 }else{
619 return stacking_find_to_focus(stacking, to_try, mapped_filt,
620 mgr_filt, mgr);
625 uint stacking_min_level_mapped(WStacking *stacking)
627 return stacking_min_level(stacking, mapped_filt, NULL);
631 bool stacking_must_focus(WStacking *stacking, WStacking *st)
633 WStacking *stf=stacking_find_to_focus(stacking, NULL,
634 mapped_filt_neq, NULL, st);
636 return (stf==NULL ||
637 (st->level>stf->level &&
638 st->level>=STACKING_LEVEL_MODAL1));
642 /*}}}*/