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