]> git.decadent.org.uk Git - ion3.git/blob - ioncore/group.c
Update cfg_kludge_flash for Flash 10
[ion3.git] / ioncore / group.c
1 /*
2  * ion/ioncore/group.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2009. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10
11 #include <libtu/minmax.h>
12 #include <libtu/objp.h>
13 #include <libmainloop/defer.h>
14
15 #include "common.h"
16 #include "rootwin.h"
17 #include "focus.h"
18 #include "global.h"
19 #include "region.h"
20 #include "manage.h"
21 #include "screen.h"
22 #include "names.h"
23 #include "saveload.h"
24 #include "attach.h"
25 #include "regbind.h"
26 #include "extlconv.h"
27 #include "xwindow.h"
28 #include "resize.h"
29 #include "stacking.h"
30 #include "sizepolicy.h"
31 #include "bindmaps.h"
32 #include "navi.h"
33 #include "sizehint.h"
34 #include "llist.h"
35 #include "mplex.h"
36 #include "group.h"
37 #include "grouppholder.h"
38 #include "frame.h"
39 #include "float-placement.h"
40 #include "return.h"
41
42
43 static void group_place_stdisp(WGroup *ws, WWindow *parent,
44                                  int pos, WRegion *stdisp);
45
46 static void group_remanage_stdisp(WGroup *ws);
47
48 static void group_do_set_bottom(WGroup *grp, WStacking *st);
49
50
51 /*{{{ Stacking list stuff */
52
53
54 WStacking *group_get_stacking(WGroup *ws)
55 {
56     WWindow *par=REGION_PARENT(ws);
57     
58     return (par==NULL 
59             ? NULL
60             : window_get_stacking(par));
61 }
62
63
64 WStacking **group_get_stackingp(WGroup *ws)
65 {
66     WWindow *par=REGION_PARENT(ws);
67     
68     return (par==NULL 
69             ? NULL
70             : window_get_stackingp(par));
71 }
72
73
74 static bool wsfilt(WStacking *st, void *ws)
75 {
76     return (st->reg!=NULL && REGION_MANAGER(st->reg)==(WRegion*)ws);
77 }
78
79
80 static bool wsfilt_nostdisp(WStacking *st, void *ws)
81 {
82     return (wsfilt(st, ws) && ((WGroup*)ws)->managed_stdisp!=st);
83 }
84
85
86 void group_iter_init(WGroupIterTmp *tmp, WGroup *ws)
87 {
88     stacking_iter_mgr_init(tmp, ws->managed_list, NULL, ws);
89 }
90
91
92 void group_iter_init_nostdisp(WGroupIterTmp *tmp, WGroup *ws)
93 {
94     stacking_iter_mgr_init(tmp, ws->managed_list, wsfilt_nostdisp, ws);
95 }
96
97
98 WRegion *group_iter(WGroupIterTmp *tmp)
99 {
100     return stacking_iter_mgr(tmp);
101 }
102
103
104 WStacking *group_iter_nodes(WGroupIterTmp *tmp)
105 {
106     return stacking_iter_mgr_nodes(tmp);
107 }
108
109
110 WGroupIterTmp group_iter_default_tmp;
111
112
113 /*}}}*/
114
115
116 /*{{{ region dynfun implementations */
117
118
119 static void group_fit(WGroup *ws, const WRectangle *geom)
120 {
121     REGION_GEOM(ws)=*geom;
122 }
123
124
125 bool group_fitrep(WGroup *ws, WWindow *par, const WFitParams *fp)
126 {
127     WGroupIterTmp tmp;
128     WStacking *unweaved=NULL;
129     int xdiff=0, ydiff=0;
130     WStacking *st;
131     WWindow *oldpar;
132     WRectangle g;
133     
134     oldpar=REGION_PARENT(ws);
135     
136     if(par==NULL){
137         if(fp->mode&REGION_FIT_WHATEVER)
138             return TRUE;
139         REGION_GEOM(ws)=fp->g;
140     }else{
141         if(!region_same_rootwin((WRegion*)ws, (WRegion*)par))
142             return FALSE;
143         
144         if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL)
145             region_detach_manager(ws->managed_stdisp->reg);
146         
147         assert(ws->managed_stdisp==NULL);
148         
149         xdiff=fp->g.x-REGION_GEOM(ws).x;
150         ydiff=fp->g.y-REGION_GEOM(ws).y;
151     
152         region_unset_parent((WRegion*)ws);
153         XReparentWindow(ioncore_g.dpy, ws->dummywin, par->win, -1, -1);
154         region_set_parent((WRegion*)ws, par);
155
156         REGION_GEOM(ws).x=fp->g.x;
157         REGION_GEOM(ws).y=fp->g.y;
158         if(!(fp->mode&REGION_FIT_WHATEVER)){
159             REGION_GEOM(ws).w=fp->g.w;
160             REGION_GEOM(ws).h=fp->g.h;
161         }
162         
163         if(oldpar!=NULL)
164             unweaved=stacking_unweave(&oldpar->stacking, wsfilt, (void*)ws);
165     }
166
167     FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
168         WFitParams fp2=*fp;
169         
170         if(st->reg==NULL)
171             continue;
172         
173         g=REGION_GEOM(st->reg);
174         g.x+=xdiff;
175         g.y+=ydiff;
176
177         if(fp->mode&REGION_FIT_WHATEVER){
178             fp2.g=g;
179         }else{
180             fp2.g=REGION_GEOM(ws);
181             sizepolicy(&st->szplcy, st->reg, &g, REGION_RQGEOM_WEAK_ALL, &fp2);
182         }
183         
184         if(!region_fitrep(st->reg, par, &fp2)){
185             warn(TR("Error reparenting %s."), region_name(st->reg));
186             region_detach_manager(st->reg);
187         }
188     }
189     
190     if(unweaved!=NULL)
191         stacking_weave(&par->stacking, &unweaved, FALSE);
192
193     return TRUE;
194 }
195
196
197 static void group_map(WGroup *ws)
198 {
199     WRegion *reg;
200     WGroupIterTmp tmp;
201
202     REGION_MARK_MAPPED(ws);
203     XMapWindow(ioncore_g.dpy, ws->dummywin);
204     
205     FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
206         region_map(reg);
207     }
208 }
209
210
211 static void group_unmap(WGroup *ws)
212 {
213     WRegion *reg;
214     WGroupIterTmp tmp;
215
216     REGION_MARK_UNMAPPED(ws);
217     XUnmapWindow(ioncore_g.dpy, ws->dummywin);
218
219     FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
220         region_unmap(reg);
221     }
222 }
223
224
225 static WStacking *find_to_focus(WGroup *ws, WStacking *st, bool group_only)
226 {
227     WStacking *stacking=group_get_stacking(ws);
228     
229     if(stacking==NULL)
230         return st;
231                                 
232     return stacking_find_to_focus_mapped(stacking, st, 
233                                          (group_only ? (WRegion*)ws : NULL));
234 }
235
236
237 static void group_do_set_focus(WGroup *ws, bool warp)
238 {
239     WStacking *st=find_to_focus(ws, ws->current_managed, FALSE);
240     
241     if(st!=NULL && st->reg!=NULL)
242         region_do_set_focus(st->reg, warp);
243     else
244         region_finalise_focusing((WRegion*)ws, ws->dummywin, warp);
245 }
246
247
248 static bool group_managed_prepare_focus(WGroup *ws, WRegion *reg, 
249                                         int flags, WPrepareFocusResult *res)
250 {
251     WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
252     WStacking *st=group_find_stacking(ws, reg);    
253     
254     if(st==NULL)
255         return FALSE;
256
257     if(mplex!=NULL){
258         WStacking *node=mplex_find_stacking(mplex, (WRegion*)ws);
259         
260         if(node==NULL)
261             return FALSE;
262         
263         return mplex_do_prepare_focus(mplex, node, st,
264                                       flags, res);
265     }else{
266         if(!region_prepare_focus((WRegion*)ws, flags, res))
267             return FALSE;
268
269         st=find_to_focus(ws, st, FALSE);
270         
271         if(st==NULL)
272             return FALSE;
273         
274         if(ioncore_g.autoraise && 
275            !(flags&REGION_GOTO_ENTERWINDOW) &&
276            st->level>STACKING_LEVEL_BOTTOM){
277             WStacking **stackingp=group_get_stackingp(ws);
278             stacking_restack(stackingp, st, None, NULL, NULL, FALSE);
279         }
280         
281         res->reg=st->reg;
282         res->flags=flags;
283         
284         return (res->reg==reg);
285     }
286 }
287
288
289 void group_managed_remove(WGroup *ws, WRegion *reg)
290 {
291     bool mcf=region_may_control_focus((WRegion*)ws);
292     WStacking *st, *next_st=NULL;
293     bool was_stdisp=FALSE, was_bottom=FALSE;
294     bool was_current=FALSE;
295     
296     st=group_find_stacking(ws, reg);
297
298     if(st!=NULL){
299         if(st==ws->bottom){
300             was_bottom=TRUE;
301             group_do_set_bottom(ws, NULL);
302         }
303         
304         if(st==ws->managed_stdisp){
305             ws->managed_stdisp=NULL;
306             was_stdisp=TRUE;
307         }
308             
309         if(st==ws->current_managed){
310             ws->current_managed=NULL;
311             was_current=TRUE;
312         }
313         
314         next_st=stacking_unstack(REGION_PARENT(ws), st);
315         UNLINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
316         stacking_unassoc(st);
317         stacking_free(st);
318     }
319     
320     region_unset_manager(reg, (WRegion*)ws);
321     
322     if(!OBJ_IS_BEING_DESTROYED(ws) && was_current){
323         /* This may still potentially cause problems when focus
324          * change is pending. Perhaps we should use region_await_focus,
325          * if it is pointing to our child (and region_may_control_focus 
326          * fail if it is pointing somewhere else).
327          */
328         WStacking *stf=find_to_focus(ws, next_st, TRUE);
329         if(stf!=NULL && mcf){
330             region_maybewarp_now(stf->reg, FALSE);
331         }else{
332             ws->current_managed=stf;
333         }
334     }
335 }
336
337
338 void group_managed_notify(WGroup *ws, WRegion *reg, WRegionNotify how)
339 {
340     if(how==ioncore_g.notifies.activated || 
341        how==ioncore_g.notifies.pseudoactivated){
342         ws->current_managed=group_find_stacking(ws, reg);
343     }
344 }
345
346
347 /*}}}*/
348
349
350 /*{{{ Create/destroy */
351
352
353 bool group_init(WGroup *ws, WWindow *par, const WFitParams *fp)
354 {
355     ws->current_managed=NULL;
356     ws->managed_stdisp=NULL;
357     ws->bottom=NULL;
358     ws->managed_list=NULL;
359     ws->phs=NULL;
360     
361     ws->dummywin=XCreateWindow(ioncore_g.dpy, par->win,
362                                 fp->g.x, fp->g.y, 1, 1, 0,
363                                 CopyFromParent, InputOnly,
364                                 CopyFromParent, 0, NULL);
365     if(ws->dummywin==None)
366         return FALSE;
367
368     region_init(&ws->reg, par, fp);
369     region_register(&ws->reg);
370
371     XSelectInput(ioncore_g.dpy, ws->dummywin,
372                  FocusChangeMask|KeyPressMask|KeyReleaseMask|
373                  ButtonPressMask|ButtonReleaseMask);
374     XSaveContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context,
375                  (XPointer)ws);
376     
377     ((WRegion*)ws)->flags|=REGION_GRAB_ON_PARENT;
378     
379     region_add_bindmap((WRegion*)ws, ioncore_group_bindmap);
380     
381     return TRUE;
382 }
383
384
385 WGroup *create_group(WWindow *par, const WFitParams *fp)
386 {
387     CREATEOBJ_IMPL(WGroup, group, (p, par, fp));
388 }
389
390
391 void group_deinit(WGroup *ws)
392 {
393     WGroupIterTmp tmp;
394     WRegion *reg;
395
396     if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg!=NULL){
397         group_managed_remove(ws, ws->managed_stdisp->reg);
398         assert(ws->managed_stdisp==NULL);
399     }
400
401     FOR_ALL_MANAGED_BY_GROUP(ws, reg, tmp){
402         destroy_obj((Obj*)reg);
403     }
404
405     assert(ws->managed_list==NULL);
406
407     XDeleteContext(ioncore_g.dpy, ws->dummywin, ioncore_g.win_context);
408     XDestroyWindow(ioncore_g.dpy, ws->dummywin);
409     ws->dummywin=None;
410     
411     while(ws->phs!=NULL)
412         grouppholder_do_unlink(ws->phs);
413     
414     region_deinit(&ws->reg);
415 }
416
417
418 bool group_rescue_clientwins(WGroup *ws, WRescueInfo *info)
419 {
420     WGroupIterTmp tmp;
421     
422     group_iter_init_nostdisp(&tmp, ws);
423     
424     return region_rescue_some_clientwins((WRegion*)ws, info,
425                                          (WRegionIterator*)group_iter,
426                                          &tmp);
427 }
428
429
430 WPHolder *group_get_rescue_pholder_for(WGroup *ws, 
431                                        WRegion *forwhat)
432 {
433     WGroupAttachParams ap=GROUPATTACHPARAMS_INIT;
434     WFramedParam fp=FRAMEDPARAM_INIT;
435     WPHolder *ph;
436     
437     ap.geom_set=TRUE;
438     ap.geom=REGION_GEOM(forwhat);
439
440     ap.geom_weak_set=1;
441     
442     if(REGION_PARENT(forwhat)==REGION_PARENT(ws)){
443         ap.geom.x-=REGION_GEOM(ws).x;
444         ap.geom.y-=REGION_GEOM(ws).y;
445     }else{
446         ap.geom_weak=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
447     }
448     
449     /* frame mode */
450     /*{
451         WFrame *frame=OBJ_CAST(forwhat, WFrame);
452         if(frame!=NULL)
453             fp.mode=frame->mode;
454     }*/
455     
456     ph=(WPHolder*)create_grouppholder(ws, NULL, &ap);
457     
458     return pholder_either((WPHolder*)create_framedpholder(ph, &fp), ph);
459 }
460
461
462
463 /*}}}*/
464
465
466 /*{{{ Bottom */
467
468
469 void group_bottom_set(WGroup *grp)
470 {
471     CALL_DYN(group_bottom_set, grp, (grp));
472 }
473
474
475 static void group_do_set_bottom(WGroup *grp, WStacking *st)
476 {
477     WStacking *was=grp->bottom;
478     WStacking *std=grp->managed_stdisp;
479     
480     grp->bottom=st;
481     
482     if(!OBJ_IS_BEING_DESTROYED(grp)){
483         bool noremanage=((was==st) ||
484                          (was==NULL && std==NULL) || 
485                          (st!=NULL && st==std) || 
486                          (st==NULL && was==std));
487         
488         if(!noremanage &&
489            (st==NULL || HAS_DYN(st->reg, region_manage_stdisp))){
490             group_remanage_stdisp(grp);
491         }
492         
493         group_bottom_set(grp);
494     }
495 }
496
497
498 /*EXTL_DOC
499  * Sets the `bottom' of \var{ws}. The region \var{reg} must already
500  * be managed by \var{ws}, unless \code{nil}.
501  */
502 EXTL_EXPORT_MEMBER
503 bool group_set_bottom(WGroup *ws, WRegion *reg)
504 {
505     WStacking *st=NULL;
506     
507     if(reg!=NULL){
508         st=group_find_stacking(ws, reg);
509         
510         if(st==NULL)
511             return FALSE;
512     }
513         
514     group_do_set_bottom(ws, st);
515     
516     return TRUE;
517 }
518
519
520 /*EXTL_DOC
521  * Returns the `bottom' of \var{ws}.
522  */
523 EXTL_SAFE
524 EXTL_EXPORT_MEMBER
525 WRegion *group_bottom(WGroup *ws)
526 {
527     return (ws->bottom!=NULL ? ws->bottom->reg : NULL);
528 }
529
530
531 /*}}}*/
532
533
534 /*{{{ Attach */
535
536
537 WStacking *group_do_add_managed(WGroup *ws, WRegion *reg, int level,
538                                 WSizePolicy szplcy)
539 {
540     WStacking *st=NULL;
541     CALL_DYN_RET(st, WStacking*, group_do_add_managed, ws, 
542                  (ws, reg, level, szplcy));
543     return st;
544 }
545     
546
547 WStacking *group_do_add_managed_default(WGroup *ws, WRegion *reg, int level,
548                                         WSizePolicy szplcy)
549 {
550     WStacking *st=NULL, *tmp=NULL;
551     Window bottom=None, top=None;
552     WStacking **stackingp=group_get_stackingp(ws);
553     WFrame *frame;
554     
555     if(stackingp==NULL)
556         return NULL;
557     
558     st=create_stacking();
559     
560     if(st==NULL)
561         return NULL;
562     
563     if(!stacking_assoc(st, reg)){
564         stacking_free(st);
565         return  NULL;
566     }
567     
568     frame=OBJ_CAST(reg, WFrame);
569     if(frame!=NULL){
570         if(framemode_unalt(frame_mode(frame))==FRAME_MODE_TILED)
571             frame_set_mode(frame, FRAME_MODE_FLOATING);
572     }
573
574     st->level=level;
575     st->szplcy=szplcy;
576
577     LINK_ITEM_FIRST(tmp, st, next, prev);
578     stacking_weave(stackingp, &tmp, FALSE);
579     assert(tmp==NULL);
580
581     LINK_ITEM(ws->managed_list, st, mgr_next, mgr_prev);
582     region_set_manager(reg, (WRegion*)ws);
583
584     if(region_is_fully_mapped((WRegion*)ws))
585         region_map(reg);
586
587     return st;
588 }
589
590
591 static void geom_group_to_parent(WGroup *ws, const WRectangle *g, 
592                                  WRectangle *wg)
593 {
594     wg->x=g->x+REGION_GEOM(ws).x;
595     wg->y=g->y+REGION_GEOM(ws).y;
596     wg->w=maxof(1, g->w);
597     wg->h=maxof(1, g->h);
598 }
599
600
601 static int group_must_focus(WGroup *ws, WStacking *st)
602 {
603     WStacking *stacking=group_get_stacking(ws);
604     
605     return (stacking!=NULL && stacking_must_focus(stacking, st));
606 }
607
608
609 bool group_do_attach_final(WGroup *ws, 
610                            WRegion *reg,
611                            const WGroupAttachParams *param)
612 {
613     WStacking *st, *stabove=NULL;
614     WSizePolicy szplcy;
615     WFitParams fp;
616     WRectangle g;
617     uint level;
618     int weak;
619     bool sw;
620     
621     /* Stacking  */
622     if(param->stack_above!=NULL)
623         stabove=group_find_stacking(ws, param->stack_above);
624
625     level=(stabove!=NULL
626            ? stabove->level
627            : (param->level_set 
628               ? param->level 
629               : STACKING_LEVEL_NORMAL));
630     
631     /* Fit */
632     szplcy=(param->szplcy_set
633             ? param->szplcy
634             : (param->bottom
635                ? SIZEPOLICY_FULL_EXACT
636                : SIZEPOLICY_VISIBILITY_CONSTRAINED));
637     
638     if(!param->whatever){
639         weak=(param->geom_weak_set
640               ? param->geom_weak
641               : (param->geom_set
642                  ? 0
643                  : REGION_RQGEOM_WEAK_ALL));
644         
645         if(param->geom_set)
646             geom_group_to_parent(ws, &param->geom, &g);
647         else
648             g=REGION_GEOM(reg);
649         
650         /* If the requested geometry does not overlap the workspaces's geometry, 
651          * position request is never honoured.
652          */
653         if((g.x+g.w<=REGION_GEOM(ws).x) ||
654            (g.x>=REGION_GEOM(ws).x+REGION_GEOM(ws).w)){
655             weak|=REGION_RQGEOM_WEAK_X;
656         }
657            
658         if((g.y+g.h<=REGION_GEOM(ws).y) ||
659            (g.y>=REGION_GEOM(ws).y+REGION_GEOM(ws).h)){
660             weak|=REGION_RQGEOM_WEAK_Y;
661         }
662
663         if(weak&(REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y) &&
664             (szplcy==SIZEPOLICY_UNCONSTRAINED ||
665              szplcy==SIZEPOLICY_VISIBILITY_CONSTRAINED ||
666              szplcy==SIZEPOLICY_FREE ||
667              szplcy==SIZEPOLICY_FREE_GLUE /* without flags */)){
668             /* TODO: use 'weak'? */
669             group_calc_placement(ws, level, &g);
670         }
671
672         fp.g=REGION_GEOM(ws);
673         fp.mode=REGION_FIT_EXACT;
674
675         sizepolicy(&szplcy, reg, &g, weak, &fp);
676
677         if(rectangle_compare(&fp.g, &REGION_GEOM(reg))!=RECTANGLE_SAME)
678             region_fitrep(reg, NULL, &fp);
679     }
680     
681     /* Add */
682     st=group_do_add_managed(ws, reg, level, szplcy);
683     
684     if(st==NULL)
685         return FALSE;
686     
687     if(stabove!=NULL)
688         st->above=stabove;
689
690     if(param->bottom)
691         group_do_set_bottom(ws, st);
692     
693     /* Focus */
694     sw=((param->switchto_set ? param->switchto : ioncore_g.switchto_new)
695         ? st==find_to_focus(ws, st, FALSE)
696         : group_must_focus(ws, st));
697     
698     if(sw){
699         if(region_may_control_focus((WRegion*)ws))
700             region_set_focus(st->reg);
701         else
702             ws->current_managed=st;
703     }else if(region_is_fully_mapped(reg)){
704         region_pointer_focus_hack(reg);
705     }
706
707     return TRUE;
708 }
709
710
711 static void group_attach_fp(WGroup *ws, const WGroupAttachParams *param,
712                             WFitParams *fp)
713 {
714     if(param->geom_set){
715         geom_group_to_parent(ws, &param->geom, &fp->g);
716         fp->mode=REGION_FIT_EXACT;
717     }else{
718         fp->g=REGION_GEOM(ws);
719         fp->mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
720     }
721 }
722
723
724 WRegion *group_do_attach(WGroup *ws, 
725                          /*const*/ WGroupAttachParams *param,
726                          WRegionAttachData *data)
727 {
728     WFitParams fp;
729     WRegion *reg;
730     
731     if(ws->bottom!=NULL && param->bottom){
732         warn(TR("'bottom' already set."));
733         return NULL;
734     }
735     
736     group_attach_fp(ws, param, &fp);
737     
738     return region_attach_helper((WRegion*) ws, REGION_PARENT(ws), &fp, 
739                                 (WRegionDoAttachFn*)group_do_attach_final,
740                                 /*(const WRegionAttachParams*)*/param, data);
741     /*                            ^^^^ doesn't seem to work. */
742 }
743
744
745 void group_get_attach_params(WGroup *ws, ExtlTab tab, 
746                              WGroupAttachParams *par)
747 {
748     int tmp;
749     bool tmpb;
750     char *tmps;
751     ExtlTab g;
752     
753     par->switchto_set=0;
754     par->level_set=0;
755     par->szplcy_set=0;
756     par->geom_set=0;
757     par->bottom=0;
758     
759     if(extl_table_is_bool_set(tab, "bottom")){
760         par->level=STACKING_LEVEL_BOTTOM;
761         par->level_set=1;
762         par->bottom=1;
763     }
764     
765     if(extl_table_gets_i(tab, "level", &tmp)){
766         if(tmp>=0){
767             par->level_set=1;
768             par->level=tmp;
769         }
770     }
771     
772     if(!par->level_set && extl_table_is_bool_set(tab, "modal")){
773         par->level=STACKING_LEVEL_MODAL1;
774         par->level_set=1;
775     }
776     
777     if(extl_table_gets_b(tab, "switchto", &tmpb)){
778         par->switchto=(tmpb!=0);
779         par->switchto_set=1;
780     }
781     
782     if(extl_table_gets_i(tab, "sizepolicy", &tmp)){
783         par->szplcy_set=1;
784         par->szplcy=tmp;
785     }else if(extl_table_gets_s(tab, "sizepolicy", &tmps)){
786         if(string2sizepolicy(tmps, &par->szplcy))
787             par->szplcy_set=1;
788         free(tmps);
789     }
790     
791     if(extl_table_gets_t(tab, "geom", &g)){
792         int n=0;
793         
794         if(extl_table_gets_i(g, "x", &(par->geom.x)))
795             n++;
796         if(extl_table_gets_i(g, "y", &(par->geom.y)))
797             n++;
798         if(extl_table_gets_i(g, "w", &(par->geom.w)))
799             n++;
800         if(extl_table_gets_i(g, "h", &(par->geom.h)))
801             n++;
802         
803         if(n==4)
804             par->geom_set=1;
805         
806         extl_unref_table(g);
807     }
808 }
809
810
811
812 /*EXTL_DOC
813  * Attach and reparent existing region \var{reg} to \var{ws}.
814  * The table \var{param} may contain the fields \var{index} and
815  * \var{switchto} that are interpreted as for \fnref{WMPlex.attach_new}.
816  */
817 EXTL_EXPORT_MEMBER
818 WRegion *group_attach(WGroup *ws, WRegion *reg, ExtlTab param)
819 {
820     WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
821     WRegionAttachData data;
822
823     if(reg==NULL)
824         return NULL;
825     
826     group_get_attach_params(ws, param, &par);
827     
828     data.type=REGION_ATTACH_REPARENT;
829     data.u.reg=reg;
830     
831     return group_do_attach(ws, &par, &data);
832 }
833
834
835 /*EXTL_DOC
836  * Create a new region to be managed by \var{ws}. At least the following
837  * fields in \var{param} are understood:
838  * 
839  * \begin{tabularx}{\linewidth}{lX}
840  *  \tabhead{Field & Description}
841  *  \var{type} & (string) Class of the object to be created. Mandatory. \\
842  *  \var{name} & (string) Name of the object to be created. \\
843  *  \var{switchto} & (boolean) Should the region be switched to? \\
844  *  \var{level} & (integer) Stacking level; default is 1. \\
845  *  \var{modal} & (boolean) Make object modal; ignored if level is set. \\
846  *  \var{sizepolicy} & (string) Size policy; see Section \ref{sec:sizepolicies}. \\
847  *  \var{bottom} & (boolean) Mark the attached region as the
848  *                 ``bottom'' of \var{ws}. \\
849  * \end{tabularx}
850  * 
851  * In addition parameters to the region to be created are passed in this 
852  * same table.
853  */
854 EXTL_EXPORT_MEMBER
855 WRegion *group_attach_new(WGroup *ws, ExtlTab param)
856 {
857     WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
858     WRegionAttachData data;
859
860     group_get_attach_params(ws, param, &par);
861     
862     data.type=REGION_ATTACH_LOAD;
863     data.u.tab=param;
864     
865     return group_do_attach(ws, &par, &data);
866 }
867
868
869 /*}}}*/
870
871
872 /*{{{ Status display support */
873
874
875 static int stdisp_szplcy(const WMPlexSTDispInfo *di, WRegion *stdisp)
876 {
877     int pos=di->pos;
878     int policy=0, gravity=0;
879     
880     if(di->fullsize){
881         if(region_orientation(stdisp)==REGION_ORIENTATION_VERTICAL){
882             if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_BL)
883                 policy=SIZEPOLICY_STRETCH_LEFT;
884             else
885                 policy=SIZEPOLICY_STRETCH_RIGHT;
886         }else{
887             if(pos==MPLEX_STDISP_TL || pos==MPLEX_STDISP_TR)
888                 policy=SIZEPOLICY_STRETCH_TOP;
889             else
890                 policy=SIZEPOLICY_STRETCH_BOTTOM;
891         }
892     }else{
893         policy=SIZEPOLICY_GRAVITY;
894     }
895     
896     if(pos==MPLEX_STDISP_TL)
897         gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_LEFT;
898     else if(pos==MPLEX_STDISP_BL)
899         gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_LEFT;
900     else if(pos==MPLEX_STDISP_TR)
901         gravity=SIZEPOLICY_VERT_TOP|SIZEPOLICY_HORIZ_RIGHT;
902     else /*if(pos=MPLEX_STDISP_BR)*/
903         gravity=SIZEPOLICY_VERT_BOTTOM|SIZEPOLICY_HORIZ_RIGHT;
904     
905     return (policy|gravity);
906 }
907
908
909 void group_manage_stdisp(WGroup *ws, WRegion *stdisp, 
910                          const WMPlexSTDispInfo *di)
911 {
912     WFitParams fp;
913     uint szplcy;
914     WRegion *b=(ws->bottom==NULL ? NULL : ws->bottom->reg);
915     
916     /* Check if 'bottom' wants to manage the stdisp. */
917     if(b!=NULL 
918        && !OBJ_IS_BEING_DESTROYED(b)
919        && HAS_DYN(b, region_manage_stdisp)){
920         region_manage_stdisp(b, stdisp, di);
921         if(REGION_MANAGER(stdisp)==b)
922             return;
923     }
924     
925     /* No. */
926     
927     szplcy=stdisp_szplcy(di, stdisp)|SIZEPOLICY_SHRUNK;
928     
929     if(ws->managed_stdisp!=NULL && ws->managed_stdisp->reg==stdisp){
930         if(ws->managed_stdisp->szplcy==szplcy)
931             return;
932         ws->managed_stdisp->szplcy=szplcy;
933     }else{
934         region_detach_manager(stdisp);
935         ws->managed_stdisp=group_do_add_managed(ws, stdisp, 
936                                                 STACKING_LEVEL_ON_TOP, 
937                                                 szplcy);
938     }
939     
940     stdisp->flags|=REGION_SKIP_FOCUS;
941     
942     fp.g=REGION_GEOM(ws);
943     fp.mode=0;
944     
945     sizepolicy(&ws->managed_stdisp->szplcy, stdisp, NULL, 0, &fp);
946
947     region_fitrep(stdisp, NULL, &fp);
948 }
949
950
951 static void group_remanage_stdisp(WGroup *ws)
952 {
953     WMPlex *mplex=OBJ_CAST(REGION_MANAGER(ws), WMPlex);
954     
955     if(mplex!=NULL && 
956        mplex->mx_current!=NULL && 
957        mplex->mx_current->st->reg==(WRegion*)ws){
958         mplex_remanage_stdisp(mplex);
959     }
960 }
961
962
963 /*}}}*/
964
965
966 /*{{{ Geometry requests */
967
968
969 void group_managed_rqgeom(WGroup *ws, WRegion *reg,
970                           const WRQGeomParams *rq,
971                           WRectangle *geomret)
972 {
973     WFitParams fp;
974     WStacking *st;
975         
976     st=group_find_stacking(ws, reg);
977
978     if(st==NULL){
979         fp.g=rq->geom;
980         fp.mode=REGION_FIT_EXACT;
981     }else{
982         fp.g=REGION_GEOM(ws);
983         fp.mode=0;
984         sizepolicy(&st->szplcy, reg, &rq->geom, rq->flags, &fp);
985     }
986     
987     if(geomret!=NULL)
988         *geomret=fp.g;
989     
990     if(!(rq->flags&REGION_RQGEOM_TRYONLY))
991         region_fitrep(reg, NULL, &fp);
992 }
993
994
995 void group_managed_rqgeom_absolute(WGroup *grp, WRegion *sub, 
996                                    const WRQGeomParams *rq,
997                                    WRectangle *geomret)
998 {
999     if(grp->bottom!=NULL && grp->bottom->reg==sub){
1000         region_rqgeom((WRegion*)grp, rq, geomret);
1001         if(!(rq->flags&REGION_RQGEOM_TRYONLY) && geomret!=NULL)
1002             *geomret=REGION_GEOM(sub);
1003     }else{
1004         WRQGeomParams rq2=*rq;
1005         rq2.flags&=~REGION_RQGEOM_ABSOLUTE;
1006
1007         region_managed_rqgeom((WRegion*)grp, sub, &rq2, geomret);
1008     }
1009 }
1010
1011
1012 /*}}}*/
1013
1014
1015 /*{{{ Navigation */
1016
1017
1018 static WStacking *nxt(WGroup *ws, WStacking *st, bool wrap)
1019 {
1020     return (st->mgr_next!=NULL 
1021             ? st->mgr_next 
1022             : (wrap ? ws->managed_list : NULL));
1023 }
1024
1025
1026 static WStacking *prv(WGroup *ws, WStacking *st, bool wrap)
1027 {
1028     return (st!=ws->managed_list 
1029             ? st->mgr_prev
1030             : (wrap ? st->mgr_prev : NULL));
1031 }
1032
1033
1034 typedef WStacking *NxtFn(WGroup *ws, WStacking *st, bool wrap);
1035
1036
1037 static bool focusable(WGroup *ws, WStacking *st, uint min_level)
1038 {
1039     return (st->reg!=NULL
1040             && REGION_IS_MAPPED(st->reg)
1041             && !(st->reg->flags&REGION_SKIP_FOCUS)
1042             && st->level>=min_level);
1043 }
1044
1045        
1046 static WStacking *do_get_next(WGroup *ws, WStacking *sti,
1047                               NxtFn *fn, bool wrap, bool sti_ok)
1048 {
1049     WStacking *st, *stacking;
1050     uint min_level=0;
1051     
1052     stacking=group_get_stacking(ws);
1053     
1054     if(stacking!=NULL)
1055         min_level=stacking_min_level_mapped(stacking);
1056
1057     st=sti;
1058     while(1){
1059         st=fn(ws, st, wrap); 
1060         
1061         if(st==NULL || st==sti)
1062             break;
1063         
1064         if(focusable(ws, st, min_level))
1065             return st;
1066     }
1067
1068     if(sti_ok && focusable(ws, sti, min_level))
1069         return sti;
1070     
1071     return NULL;
1072 }
1073
1074
1075 static WStacking *group_do_navi_first(WGroup *ws, WRegionNavi nh)
1076 {
1077     WStacking *lst=ws->managed_list;
1078     
1079     if(lst==NULL)
1080         return NULL;
1081
1082     if(nh==REGION_NAVI_ANY && 
1083        ws->current_managed!=NULL &&
1084        ws->current_managed->reg!=NULL){
1085         return ws->current_managed;
1086     }
1087     
1088     if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
1089        nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1090         return do_get_next(ws, lst, prv, TRUE, TRUE);
1091     }else{
1092         return do_get_next(ws, lst->mgr_prev, nxt, TRUE, TRUE);
1093     }
1094 }
1095
1096
1097 static WRegion *group_navi_first(WGroup *ws, WRegionNavi nh,
1098                                  WRegionNaviData *data)
1099 {
1100     WStacking *st=group_do_navi_first(ws, nh);
1101     
1102     return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1103 }
1104
1105
1106 static WStacking *group_do_navi_next(WGroup *ws, WStacking *st, 
1107                                      WRegionNavi nh, bool wrap)
1108 {
1109     if(st==NULL)
1110         return group_do_navi_first(ws, nh);
1111     
1112     if(nh==REGION_NAVI_ANY || nh==REGION_NAVI_END || 
1113        nh==REGION_NAVI_BOTTOM || nh==REGION_NAVI_RIGHT){
1114         return do_get_next(ws, st, nxt, wrap, FALSE);
1115     }else{
1116         return do_get_next(ws, st, prv, wrap, FALSE);
1117     }
1118 }
1119
1120 static WRegion *group_navi_next(WGroup *ws, WRegion *reg, 
1121                                 WRegionNavi nh, WRegionNaviData *data)
1122 {
1123     WStacking *st=group_find_stacking(ws, reg);
1124     
1125     st=group_do_navi_next(ws, st, nh, FALSE);
1126     
1127     return region_navi_cont(&ws->reg, (st!=NULL ? st->reg : NULL), data);
1128 }
1129
1130
1131 /*}}}*/
1132
1133
1134 /*{{{ Stacking */
1135
1136
1137 /* 
1138  * Note: Managed objects are considered to be stacked separately from the
1139  * group, slightly violating expectations.
1140  */
1141
1142 void group_stacking(WGroup *ws, Window *bottomret, Window *topret)
1143 {
1144     Window win=region_xwindow((WRegion*)ws);
1145     
1146     *bottomret=win;
1147     *topret=win;
1148 }
1149
1150
1151 void group_restack(WGroup *ws, Window other, int mode)
1152 {
1153     Window win;
1154     
1155     win=region_xwindow((WRegion*)ws);
1156     if(win!=None){
1157         xwindow_restack(win, other, mode);
1158         other=win;
1159         mode=Above;
1160     }
1161 }
1162
1163
1164 WStacking *group_find_stacking(WGroup *ws, WRegion *r)
1165 {
1166     if(r==NULL || REGION_MANAGER(r)!=(WRegion*)ws)
1167         return NULL;
1168     
1169     return ioncore_find_stacking(r);
1170 }
1171
1172
1173 static WStacking *find_stacking_if_not_on_ws(WGroup *ws, Window w)
1174 {
1175     WRegion *r=xwindow_region_of(w);
1176     WStacking *st=NULL;
1177     
1178     while(r!=NULL){
1179         if(REGION_MANAGER(r)==(WRegion*)ws)
1180             break;
1181         st=group_find_stacking(ws, r);
1182         if(st!=NULL)
1183             break;
1184         r=REGION_MANAGER(r);
1185     }
1186     
1187     return st;
1188 }
1189
1190
1191 bool group_managed_rqorder(WGroup *grp, WRegion *reg, WRegionOrder order)
1192 {
1193     WStacking **stackingp=group_get_stackingp(grp);
1194     WStacking *st;
1195
1196     if(stackingp==NULL || *stackingp==NULL)
1197         return FALSE;
1198
1199     st=group_find_stacking(grp, reg);
1200     
1201     if(st==NULL)
1202         return FALSE;
1203     
1204     stacking_restack(stackingp, st, None, NULL, NULL,
1205                      (order!=REGION_ORDER_FRONT));
1206     
1207     return TRUE;
1208 }
1209
1210
1211 /*}}}*/
1212
1213
1214 /*{{{ Misc. */
1215
1216
1217 /*EXTL_DOC
1218  * Iterate over managed regions of \var{ws} until \var{iterfn} returns
1219  * \code{false}.
1220  * The function is called in protected mode.
1221  * This routine returns \code{true} if it reaches the end of list
1222  * without this happening.
1223  */
1224 EXTL_SAFE
1225 EXTL_EXPORT_MEMBER
1226 bool group_managed_i(WGroup *ws, ExtlFn iterfn)
1227 {
1228     WGroupIterTmp tmp;
1229     group_iter_init(&tmp, ws);
1230     
1231     return extl_iter_objlist_(iterfn, (ObjIterator*)group_iter, &tmp);
1232 }
1233
1234
1235 WRegion* group_current(WGroup *ws)
1236 {
1237     return (ws->current_managed!=NULL ? ws->current_managed->reg : NULL);
1238 }
1239
1240
1241 void group_size_hints(WGroup *ws, WSizeHints *hints_ret)
1242 {
1243     if(ws->bottom==NULL || ws->bottom->reg==NULL){
1244         sizehints_clear(hints_ret);
1245     }else{
1246         region_size_hints(ws->bottom->reg, hints_ret);
1247         hints_ret->no_constrain=TRUE;
1248     }
1249 }
1250
1251
1252 Window group_xwindow(const WGroup *ws)
1253 {
1254     return ws->dummywin;
1255 }
1256
1257
1258 /*EXTL_DOC
1259  * Returns the group of \var{reg}, if it is managed by one,
1260  * and \var{reg} itself otherwise.
1261  */
1262 /*EXTL_EXPORT_MEMBER
1263 WRegion *region_group_of(WRegion *reg)
1264 {
1265     WRegion *mgr=REGION_MANAGER(reg);
1266     
1267     return (OBJ_IS(mgr, WGroup) ? mgr : reg);
1268 }*/
1269
1270
1271 /*EXTL_DOC
1272  * Returns the group of \var{reg}, if \var{reg} is its bottom,
1273  * and \var{reg} itself otherwise.
1274  */
1275 EXTL_EXPORT_MEMBER
1276 WRegion *region_groupleader_of(WRegion *reg)
1277 {
1278     WGroup *grp=REGION_MANAGER_CHK(reg, WGroup);
1279     
1280     return ((grp!=NULL && group_bottom(grp)==reg)
1281             ? (WRegion*)grp
1282             : reg);
1283 }
1284
1285
1286 /*}}}*/
1287
1288
1289 /*{{{ Save/load */
1290
1291
1292 static ExtlTab group_get_configuration(WGroup *ws)
1293 {
1294     ExtlTab tab, mgds, subtab, g;
1295     WStacking *st;
1296     WGroupIterTmp tmp;
1297     WMPlex *par;
1298     int n=0;
1299     WRectangle tmpg;
1300     
1301     tab=region_get_base_configuration((WRegion*)ws);
1302     
1303     mgds=extl_create_table();
1304     
1305     extl_table_sets_t(tab, "managed", mgds);
1306     
1307     /* TODO: stacking order messed up */
1308     
1309     FOR_ALL_NODES_IN_GROUP(ws, st, tmp){
1310         if(st->reg==NULL)
1311             continue;
1312         
1313         subtab=region_get_configuration(st->reg);
1314
1315         if(subtab!=extl_table_none()){
1316             extl_table_sets_s(subtab, "sizepolicy", 
1317                               sizepolicy2string(st->szplcy));
1318             extl_table_sets_i(subtab, "level", st->level);
1319         
1320             tmpg=REGION_GEOM(st->reg);
1321             tmpg.x-=REGION_GEOM(ws).x;
1322             tmpg.y-=REGION_GEOM(ws).y;
1323             
1324             g=extl_table_from_rectangle(&tmpg);
1325             extl_table_sets_t(subtab, "geom", g);
1326             extl_unref_table(g);
1327             
1328             if(ws->bottom==st)
1329                 extl_table_sets_b(subtab, "bottom", TRUE);
1330         
1331             extl_table_seti_t(mgds, ++n, subtab);
1332             extl_unref_table(subtab);
1333         }
1334     }
1335     
1336     extl_unref_table(mgds);
1337     
1338     return tab;
1339 }
1340
1341     
1342 void group_do_load(WGroup *ws, ExtlTab tab)
1343 {
1344     ExtlTab substab, subtab;
1345     int i, n;
1346     
1347     if(extl_table_gets_t(tab, "managed", &substab)){
1348         n=extl_table_get_n(substab);
1349         for(i=1; i<=n; i++){
1350             if(extl_table_geti_t(substab, i, &subtab)){
1351                 WGroupAttachParams par=GROUPATTACHPARAMS_INIT;
1352                 WRegionAttachData data;
1353                 WFitParams fp;
1354                 WPHolder *ph;
1355                 
1356                 group_get_attach_params(ws, subtab, &par);
1357                 group_attach_fp(ws, &par, &fp);
1358                 
1359                 ph=(WPHolder*)create_grouppholder(ws, NULL, &par);
1360                 
1361                 region_attach_load_helper((WRegion*)ws, REGION_PARENT(ws), &fp,
1362                                           (WRegionDoAttachFn*)group_do_attach_final,
1363                                           (void*)&par, subtab, &ph);
1364                                               
1365                 if(ph!=NULL)
1366                     destroy_obj((Obj*)ph);
1367                 
1368                 extl_unref_table(subtab);
1369             }
1370         }
1371         
1372         extl_unref_table(substab);
1373     }
1374 }
1375
1376
1377 WRegion *group_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1378 {
1379     WGroup *ws;
1380     
1381     ws=create_group(par, fp);
1382     
1383     if(ws==NULL)
1384         return NULL;
1385         
1386     group_do_load(ws, tab);
1387     
1388     return (WRegion*)ws;
1389 }
1390
1391
1392 /*}}}*/
1393
1394
1395 /*{{{ Dynamic function table and class implementation */
1396
1397
1398 static DynFunTab group_dynfuntab[]={
1399     {(DynFun*)region_fitrep,
1400      (DynFun*)group_fitrep},
1401
1402     {region_map, 
1403      group_map},
1404     
1405     {region_unmap, 
1406      group_unmap},
1407     
1408     {(DynFun*)region_managed_prepare_focus, 
1409      (DynFun*)group_managed_prepare_focus},
1410
1411     {region_do_set_focus, 
1412      group_do_set_focus},
1413     
1414     {region_managed_notify, 
1415      group_managed_notify},
1416     
1417     {region_managed_remove,
1418      group_managed_remove},
1419     
1420     {(DynFun*)region_get_configuration, 
1421      (DynFun*)group_get_configuration},
1422
1423     {(DynFun*)region_current,
1424      (DynFun*)group_current},
1425     
1426     {(DynFun*)region_rescue_clientwins,
1427      (DynFun*)group_rescue_clientwins},
1428     
1429     {region_restack,
1430      group_restack},
1431
1432     {region_stacking,
1433      group_stacking},
1434
1435     {(DynFun*)region_managed_get_pholder,
1436      (DynFun*)group_managed_get_pholder},
1437
1438     {region_managed_rqgeom,
1439      group_managed_rqgeom},
1440      
1441     {region_managed_rqgeom_absolute,
1442      group_managed_rqgeom_absolute},
1443     
1444     {(DynFun*)group_do_add_managed,
1445      (DynFun*)group_do_add_managed_default},
1446     
1447     {region_size_hints,
1448      group_size_hints},
1449     
1450     {(DynFun*)region_xwindow,
1451      (DynFun*)group_xwindow},
1452     
1453     {(DynFun*)region_navi_first,
1454      (DynFun*)group_navi_first},
1455     
1456     {(DynFun*)region_navi_next,
1457      (DynFun*)group_navi_next},
1458     
1459     {(DynFun*)region_managed_rqorder,
1460      (DynFun*)group_managed_rqorder},
1461      
1462     {(DynFun*)region_get_rescue_pholder_for,
1463      (DynFun*)group_get_rescue_pholder_for},
1464     
1465     END_DYNFUNTAB
1466 };
1467
1468
1469 EXTL_EXPORT
1470 IMPLCLASS(WGroup, WRegion, group_deinit, group_dynfuntab);
1471
1472
1473 /*}}}*/
1474