]> git.decadent.org.uk Git - ion3.git/blob - mod_dock/dock.c
3b18513a830288a684254c7684123cb7c61e9881
[ion3.git] / mod_dock / dock.c
1 /*
2  * Ion dock module
3  * Copyright (C) 2003 Tom Payne
4  * Copyright (C) 2003 Per Olofsson
5  * Copyright (C) 2004-2006 Tuomo Valkonen
6  *
7  * by Tom Payne <ion@tompayne.org>
8  * based on code by Per Olofsson <pelle@dsv.su.se>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  * $Header: /home/twp/cvsroot/twp/ion/ion-devel-dock/dock.c,v 1.17 2003/12/21 11:59:48 twp Exp $
25  *
26  */
27
28
29 /*{{{ Includes */
30
31 #include <limits.h>
32 #include <string.h>
33 #include <X11/Xlib.h>
34 #include <X11/Xatom.h>
35 #include <X11/Xutil.h>
36 #include <X11/extensions/shape.h>
37 #include <X11/extensions/Xext.h>
38
39 #include <libtu/objp.h>
40 #include <libtu/map.h>
41 #include <libtu/minmax.h>
42 #include <libextl/extl.h>
43 #include <libextl/readconfig.h>
44 #include <libmainloop/defer.h>
45
46 #include <ioncore/common.h>
47 #include <ioncore/clientwin.h>
48 #include <ioncore/eventh.h>
49 #include <ioncore/global.h>
50 #include <ioncore/manage.h>
51 #include <ioncore/names.h>
52 #include <ioncore/property.h>
53 #include <ioncore/resize.h>
54 #include <ioncore/window.h>
55 #include <ioncore/mplex.h>
56 #include <ioncore/saveload.h>
57 #include <ioncore/bindmaps.h>
58 #include <ioncore/regbind.h>
59 #include <ioncore/extlconv.h>
60 #include <ioncore/event.h>
61 #include <ioncore/resize.h>
62 #include <ioncore/sizehint.h>
63 #include <ioncore/basicpholder.h>
64
65 #include "exports.h"
66
67 /*}}}*/
68
69
70 /*{{{ Macros */
71
72 #ifdef __GNUC__
73 #define UNUSED __attribute__ ((unused))
74 #else
75 #define UNUSED
76 #endif
77
78 /*}}}*/
79
80
81 /*{{{ Variables */
82
83 #include "../version.h"
84
85 static const char *modname="dock";
86 const char mod_dock_ion_api_version[]=ION_API_VERSION;
87
88 static bool shape_extension=FALSE;
89 static int shape_event_basep=0;
90 static int shape_error_basep=0;
91
92 static WBindmap *dock_bindmap=NULL;
93
94 /*}}}*/
95
96
97 /*{{{ Classes */
98
99 INTRSTRUCT(WDockParam);
100 INTRSTRUCT(WDockApp);
101 INTRCLASS(WDock);
102
103 DECLSTRUCT(WDockParam){
104     const char *key;
105     const char *desc;
106     const StringIntMap *map;
107     int dflt;
108 };
109
110 DECLSTRUCT(WDockApp){
111     WDockApp *next, *prev;
112     WRegion *reg;
113     int pos;
114     bool draw_border;
115     bool tile;
116     WRectangle geom;
117     WRectangle tile_geom;
118     WRectangle border_geom;
119 };
120
121 DECLCLASS(WDock){
122     WWindow win;
123     WDock *dock_next, *dock_prev;
124     int pos, grow;
125     bool is_auto;
126     GrBrush *brush;
127     WDockApp *dockapps;
128     
129     int min_w, min_h;
130     int max_w, max_h;
131     
132     bool arrange_called;
133     bool save;
134 };
135
136 static WDock *docks=NULL;
137
138 /*}}}*/
139
140
141 /*{{{ Parameter conversion */
142
143 static void dock_param_extl_table_get(const WDockParam *param, 
144                                       ExtlTab conftab, int value)
145 {
146     const char *s;
147
148     s=stringintmap_key(param->map, value, NULL);
149     if(s){
150         extl_table_sets_s(conftab, param->key, s);
151     }
152
153 }
154
155
156 static bool dock_param_do_set(const WDockParam *param, char *s,
157                               int *ret)
158 {
159     bool changed=FALSE;
160     int i=stringintmap_value(param->map, s, -1);
161     if(i<0){
162         warn_obj(modname, "Invalid %s \"%s\"", param->desc, s);
163     }else{
164         if(*ret!=i){
165             changed=TRUE;
166         }
167         *ret=i;
168     }
169     free(s);
170
171     return changed;
172
173 }
174
175
176 static bool dock_param_extl_table_set(const WDockParam *param, ExtlTab conftab,
177                                       int *ret)
178 {
179     char *s;
180
181     if(extl_table_gets_s(conftab, param->key, &s))
182         return dock_param_do_set(param, s, ret);
183
184     return FALSE;
185
186 }
187
188
189 static bool dock_param_brush_set(const WDockParam *param, GrBrush *brush,
190                                  int *ret)
191 {
192     char *s;
193
194     if(grbrush_get_extra(brush, param->key, 's', &s))
195         return dock_param_do_set(param, s, ret);
196
197     return FALSE;
198
199 }
200
201 /*}}}*/
202
203
204 /*{{{ Parameter descriptions */
205
206 static const WDockParam dock_param_name={
207     "name",
208     "name",
209     NULL,
210     0
211 };
212
213
214 #define DOCK_HPOS_MASK   0x000f
215 #define DOCK_HPOS_LEFT   0x0000
216 #define DOCK_HPOS_CENTER 0x0001
217 #define DOCK_HPOS_RIGHT  0x0002
218 #define DOCK_VPOS_MASK   0x00f0
219 #define DOCK_VPOS_TOP    0x0000
220 #define DOCK_VPOS_MIDDLE 0x0010
221 #define DOCK_VPOS_BOTTOM 0x0020
222
223
224 static StringIntMap dock_pos_map[]={
225     {"tl", DOCK_VPOS_TOP|DOCK_HPOS_LEFT},
226     {"tc", DOCK_VPOS_TOP|DOCK_HPOS_CENTER},
227     {"tr", DOCK_VPOS_TOP|DOCK_HPOS_RIGHT},
228     {"ml", DOCK_VPOS_MIDDLE|DOCK_HPOS_LEFT},
229     {"mc", DOCK_VPOS_MIDDLE|DOCK_HPOS_CENTER},
230     {"mr", DOCK_VPOS_MIDDLE|DOCK_HPOS_RIGHT},
231     {"bl", DOCK_VPOS_BOTTOM|DOCK_HPOS_LEFT},
232     {"bc", DOCK_VPOS_BOTTOM|DOCK_HPOS_CENTER},
233     {"br", DOCK_VPOS_BOTTOM|DOCK_HPOS_RIGHT},
234     END_STRINGINTMAP
235 };
236
237 static WDockParam dock_param_pos={
238     "pos",
239     "dock position",
240     dock_pos_map,
241     DOCK_HPOS_LEFT|DOCK_VPOS_BOTTOM
242 };
243
244
245 enum WDockGrow{
246     DOCK_GROW_UP,
247     DOCK_GROW_DOWN,
248     DOCK_GROW_LEFT,
249     DOCK_GROW_RIGHT
250 };
251
252 static StringIntMap dock_grow_map[]={
253     {"up", DOCK_GROW_UP},
254     {"down", DOCK_GROW_DOWN},
255     {"left", DOCK_GROW_LEFT},
256     {"right", DOCK_GROW_RIGHT},
257     END_STRINGINTMAP
258 };
259
260 WDockParam dock_param_grow={
261     "grow",
262     "growth direction",
263     dock_grow_map,
264     DOCK_GROW_RIGHT
265 };
266
267
268 static const WDockParam dock_param_is_auto={
269     "is_auto",
270     "is automatic",
271     NULL,
272     TRUE
273 };
274
275
276 enum WDockOutlineStyle{
277     DOCK_OUTLINE_STYLE_NONE,
278     DOCK_OUTLINE_STYLE_ALL,
279     DOCK_OUTLINE_STYLE_EACH
280 };
281
282 static StringIntMap dock_outline_style_map[]={
283     {"none", DOCK_OUTLINE_STYLE_NONE},
284     {"all", DOCK_OUTLINE_STYLE_ALL},
285     {"each", DOCK_OUTLINE_STYLE_EACH},
286     END_STRINGINTMAP
287 };
288
289 WDockParam dock_param_outline_style={
290     "outline_style",
291     "outline style",
292     dock_outline_style_map,
293     DOCK_OUTLINE_STYLE_ALL
294 };
295
296
297 static const WDockParam dock_param_tile_width={
298     "width",
299     "tile width",
300     NULL,
301     64
302 };
303
304 static const WDockParam dock_param_tile_height={
305     "height",
306     "tile height",
307     NULL,
308     64
309 };
310
311
312 /*}}}*/
313
314
315 /*{{{ Misc. */
316
317 #define CLIENTWIN_WINPROP_POSITION "dockposition"
318 #define CLIENTWIN_WINPROP_BORDER "dockborder"
319
320 static WDockApp *dock_find_dockapp(WDock *dock, WRegion *reg)
321 {
322     WDockApp *dockapp;
323
324     for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
325         if(dockapp->reg==reg){
326             return dockapp;
327         }
328     }
329
330     return NULL;
331
332 }
333
334
335 static void dock_get_outline_style(WDock *dock, int *ret)
336 {
337
338     *ret=dock_param_outline_style.dflt;
339     if(dock->brush!=NULL)
340         dock_param_brush_set(&dock_param_outline_style, dock->brush, ret);
341
342 }
343
344 /*}}}*/
345
346
347 /*{{{ Size calculation */
348
349
350 static void dock_get_tile_size(WDock *dock, WRectangle *ret)
351 {
352     ExtlTab tile_size_table;
353
354     ret->x=0;
355     ret->y=0;
356     ret->w=dock_param_tile_width.dflt;
357     ret->h=dock_param_tile_height.dflt;
358     if(dock->brush==NULL)
359         return;
360     if(grbrush_get_extra(dock->brush, "tile_size", 't', &tile_size_table)){
361         extl_table_gets_i(tile_size_table, dock_param_tile_width.key, &ret->w);
362         extl_table_gets_i(tile_size_table, dock_param_tile_height.key, &ret->h);
363         extl_unref_table(tile_size_table);
364     }
365
366 }
367
368
369 static void dock_get_pos_grow(WDock *dock, int *pos, int *grow)
370 {
371     WMPlex *mplex=OBJ_CAST(REGION_PARENT(dock), WMPlex);
372     WRegion *mplex_stdisp;
373     WMPlexSTDispInfo din;
374     
375     if(mplex!=NULL){
376         mplex_get_stdisp(mplex, &mplex_stdisp, &din);
377         if(mplex_stdisp==(WRegion*)dock){
378             /* Ok, we're assigned as a status display for mplex, so
379              * get parameters from there.
380              */
381             *pos=((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_BL)
382                   ? DOCK_HPOS_LEFT
383                   : DOCK_HPOS_RIGHT) 
384                 | ((din.pos==MPLEX_STDISP_TL || din.pos==MPLEX_STDISP_TR)
385                    ? DOCK_VPOS_TOP
386                    : DOCK_VPOS_BOTTOM);
387             *grow=dock->grow;
388             return;
389         }
390     }
391     
392     *grow=dock->grow;
393     *pos=dock->pos;
394 }
395
396
397
398 static void dock_reshape(WDock *dock)
399 {
400     int outline_style;
401     
402     if(!shape_extension){
403         return;
404     }
405     
406     dock_get_outline_style(dock, &outline_style);
407     
408     switch(outline_style){
409     case DOCK_OUTLINE_STYLE_NONE:
410     case DOCK_OUTLINE_STYLE_EACH:
411         {
412             WDockApp *dockapp;
413             
414             /* Start with an empty set */
415             XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
416                                     ShapeBounding, 0, 0, NULL, 0, ShapeSet, 0);
417             
418             /* Union with dockapp shapes */
419             for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
420                 WClientWin *cwin=OBJ_CAST(dockapp->reg, WClientWin);
421                 if(outline_style==DOCK_OUTLINE_STYLE_EACH
422                    && dockapp->draw_border){
423                     /* Union with border shape */
424                     XRectangle tile_rect;
425                     
426                     tile_rect.x=dockapp->border_geom.x;
427                     tile_rect.y=dockapp->border_geom.y;
428                     tile_rect.width=dockapp->border_geom.w;
429                     tile_rect.height=dockapp->border_geom.h;
430                     XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
431                                             ShapeBounding, 0, 0, &tile_rect, 1,
432                                             ShapeUnion, 0);
433                 }else if(cwin!=NULL){
434                     /* Union with dockapp shape */
435                     int count;
436                     int ordering;
437                     
438                     XRectangle *rects=XShapeGetRectangles(ioncore_g.dpy, cwin->win,
439                                                           ShapeBounding, &count,
440                                                           &ordering);
441                     if(rects!=NULL){
442                         WRectangle dockapp_geom=REGION_GEOM(cwin);
443                         XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
444                                                 ShapeBounding,
445                                                 dockapp_geom.x, dockapp_geom.y,
446                                                 rects, count, ShapeUnion, ordering);
447                         XFree(rects);
448                     }
449                 }
450             }
451         }
452         break;
453         
454     case DOCK_OUTLINE_STYLE_ALL:
455         {
456             WRectangle geom;
457             XRectangle rect;
458             
459             geom=REGION_GEOM(dock);
460             rect.x=0;
461             rect.y=0;
462             rect.width=geom.w;
463             rect.height=geom.h;
464             XShapeCombineRectangles(ioncore_g.dpy, ((WWindow*)dock)->win,
465                                     ShapeBounding, 0, 0, &rect, 1, ShapeSet, 0);
466         }
467         break;
468     }
469     
470 }
471
472
473 static void dock_arrange_dockapps(WDock *dock, const WRectangle *bd_dockg, 
474                                   const WDockApp *replace_this, 
475                                   WDockApp *with_this)
476 {
477     GrBorderWidths dock_bdw, dockapp_bdw;
478     WDockApp dummy_copy, *dockapp;
479     int pos, grow, cur_coord=0;
480     WRectangle dock_geom;
481
482     dock->arrange_called=TRUE;
483     
484     dock_get_pos_grow(dock, &pos, &grow);
485
486     /* Determine dock and dockapp border widths */
487     memset(&dock_bdw, 0, sizeof(GrBorderWidths));
488     memset(&dockapp_bdw, 0, sizeof(GrBorderWidths));
489     
490     if(dock->brush){
491         int outline_style;
492
493         dock_get_outline_style(dock, &outline_style);
494         switch(outline_style){
495         case DOCK_OUTLINE_STYLE_NONE:
496             break;
497         case DOCK_OUTLINE_STYLE_ALL:
498             grbrush_get_border_widths(dock->brush, &dock_bdw);
499             dockapp_bdw.spacing=dock_bdw.spacing;
500             break;
501         case DOCK_OUTLINE_STYLE_EACH:
502             grbrush_get_border_widths(dock->brush, &dockapp_bdw);
503             break;
504         }
505     }
506     
507     dock_geom.w=bd_dockg->w-dock_bdw.left-dock_bdw.right;
508     dock_geom.h=bd_dockg->h-dock_bdw.top-dock_bdw.bottom;
509
510     /* Calculate initial co-ordinate for layout algorithm */
511     switch(grow){
512     case DOCK_GROW_UP:
513         cur_coord=dock_bdw.top+dock_geom.h;
514         break;
515     case DOCK_GROW_DOWN:
516         cur_coord=dock_bdw.top;
517         break;
518     case DOCK_GROW_LEFT:
519         cur_coord=dock_bdw.left+dock_geom.w;
520         break;
521     case DOCK_GROW_RIGHT:
522         cur_coord=dock_bdw.left;
523         break;
524     }
525
526     /* Arrange dockapps */
527     for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
528         WDockApp *da=dockapp;
529         
530         if(replace_this!=NULL){
531             if(replace_this==dockapp){
532                 da=with_this;
533             }else{
534                 dummy_copy=*dockapp;
535                 da=&dummy_copy;
536             }
537         }
538             
539         /* Calculate first co-ordinate */
540         switch(grow){
541         case DOCK_GROW_UP:
542         case DOCK_GROW_DOWN:
543             switch(pos&DOCK_HPOS_MASK){
544             case DOCK_HPOS_LEFT:
545                 da->border_geom.x=0;
546                 break;
547             case DOCK_HPOS_CENTER:
548                 da->border_geom.x=(dock_geom.w-da->border_geom.w)/2;
549                 break;
550             case DOCK_HPOS_RIGHT:
551                 da->border_geom.x=dock_geom.w-da->border_geom.w;
552                 break;
553             }
554             da->border_geom.x+=dock_bdw.left;
555             break;
556         case DOCK_GROW_LEFT:
557         case DOCK_GROW_RIGHT:
558             switch(pos&DOCK_VPOS_MASK){
559             case DOCK_VPOS_TOP:
560                 da->border_geom.y=0;
561                 break;
562             case DOCK_VPOS_MIDDLE:
563                 da->border_geom.y=(dock_geom.h-da->border_geom.h)/2;
564                 break;
565             case DOCK_VPOS_BOTTOM:
566                 da->border_geom.y=dock_geom.h-da->border_geom.h;
567                 break;
568             }
569             da->border_geom.y+=dock_bdw.top;
570             break;
571         }
572
573         /* Calculate second co-ordinate */
574         switch(grow){
575         case DOCK_GROW_UP:
576             cur_coord-=da->border_geom.h;
577             da->border_geom.y=cur_coord;
578             cur_coord-=dockapp_bdw.spacing;
579             break;
580         case DOCK_GROW_DOWN:
581             da->border_geom.y=cur_coord;
582             cur_coord+=da->border_geom.h+dockapp_bdw.spacing;
583             break;
584         case DOCK_GROW_LEFT:
585             cur_coord-=da->border_geom.w;
586             da->border_geom.x=cur_coord;
587             cur_coord-=dockapp_bdw.spacing;
588             break;
589         case DOCK_GROW_RIGHT:
590             da->border_geom.x=cur_coord;
591             cur_coord+=da->border_geom.w+dockapp_bdw.spacing;
592             break;
593         }
594
595         /* Calculate tile geom */
596         da->tile_geom.x=da->border_geom.x+dockapp_bdw.left;
597         da->tile_geom.y=da->border_geom.y+dockapp_bdw.top;
598
599         /* Calculate dockapp geom */
600         if(da->tile){
601             da->geom.x=da->tile_geom.x+(da->tile_geom.w-da->geom.w)/2;
602             da->geom.y=da->tile_geom.y+(da->tile_geom.h-da->geom.h)/2;
603         }else{
604             da->geom.x=da->tile_geom.x;
605             da->geom.y=da->tile_geom.y;
606         }
607         
608         if(replace_this==NULL)
609             region_fit(da->reg, &(da->geom), REGION_FIT_BOUNDS);
610     }
611 }
612
613
614 static void calc_dock_pos(WRectangle *dg, const WRectangle *pg, int pos)
615 {
616     switch(pos&DOCK_HPOS_MASK){
617     case DOCK_HPOS_LEFT:
618         dg->x=pg->x;
619         break;
620     case DOCK_HPOS_CENTER:
621         dg->x=pg->x+(pg->w-dg->w)/2;
622         break;
623     case DOCK_HPOS_RIGHT:
624         dg->x=pg->x+(pg->w-dg->w);
625         break;
626     }
627     
628     switch(pos&DOCK_VPOS_MASK){
629     case DOCK_VPOS_TOP:
630         dg->y=pg->y;
631         break;
632     case DOCK_VPOS_MIDDLE:
633         dg->y=pg->y+(pg->h-dg->h)/2;
634         break;
635     case DOCK_VPOS_BOTTOM:
636         dg->y=pg->y+(pg->h-dg->h);
637         break;
638     }
639 }
640
641     
642 static void dock_set_minmax(WDock *dock, int grow, const WRectangle *g)
643 {
644     dock->min_w=g->w;
645     dock->min_h=g->h;
646     if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){
647         dock->max_w=g->w;
648         dock->max_h=INT_MAX;
649     }else{
650         dock->max_w=INT_MAX;
651         dock->max_h=g->h;
652     }
653 }
654
655
656 static void dockapp_calc_preferred_size(WDock *dock, int grow, 
657                                         const WRectangle *tile_size,
658                                         WDockApp *da)
659 {
660     int w=da->geom.w, h=da->geom.h;
661     
662     if(grow==DOCK_GROW_UP || grow==DOCK_GROW_DOWN){
663         da->geom.w=minof(w, tile_size->w);
664         da->geom.h=h;
665     }else{
666         da->geom.w=w;
667         da->geom.h=minof(h, tile_size->h);
668     }
669     
670     region_size_hints_correct(da->reg, &(da->geom.w), &(da->geom.h), TRUE);
671 }
672
673
674
675 static void dock_managed_rqgeom_(WDock *dock, WRegion *reg, int flags,
676                                  const WRectangle *geom, WRectangle *geomret,
677                                  bool just_update_minmax)
678 {
679     WDockApp *dockapp=NULL, *thisdockapp=NULL, thisdockapp_copy;
680     WRectangle parent_geom, dock_geom, border_dock_geom;
681     GrBorderWidths dock_bdw, dockapp_bdw;
682     int n_dockapps=0, max_w=1, max_h=1, total_w=0, total_h=0;
683     int pos, grow;
684     WRectangle tile_size;
685     WWindow *par=REGION_PARENT(dock);
686     
687     /* dock_resize calls with NULL parameters. */
688     assert(reg!=NULL || (geomret==NULL && !(flags&REGION_RQGEOM_TRYONLY)));
689     
690     dock_get_pos_grow(dock, &pos, &grow);
691
692     /* Determine parent and tile geoms */
693     parent_geom.x=0;
694     parent_geom.y=0;
695     if(par!=NULL){
696         parent_geom.w=REGION_GEOM(par).w;
697         parent_geom.h=REGION_GEOM(par).h;
698     }else{
699         /* Should not happen in normal operation. */
700         parent_geom.w=1;
701         parent_geom.h=1;
702     }
703     
704     dock_get_tile_size(dock, &tile_size);
705
706     /* Determine dock and dockapp border widths */
707     memset(&dock_bdw, 0, sizeof(GrBorderWidths));
708     memset(&dockapp_bdw, 0, sizeof(GrBorderWidths));
709     
710     if(dock->brush){
711         int outline_style;
712
713         dock_get_outline_style(dock, &outline_style);
714         switch(outline_style){
715         case DOCK_OUTLINE_STYLE_NONE:
716             break;
717         case DOCK_OUTLINE_STYLE_ALL:
718             grbrush_get_border_widths(dock->brush, &dock_bdw);
719             dockapp_bdw.spacing=dock_bdw.spacing;
720             break;
721         case DOCK_OUTLINE_STYLE_EACH:
722             grbrush_get_border_widths(dock->brush, &dockapp_bdw);
723             break;
724         }
725     }
726
727     /* Calculate widths and heights */
728     for(dockapp=dock->dockapps; dockapp!=NULL; dockapp=dockapp->next){
729         WDockApp *da=dockapp;
730         bool update=!(flags&REGION_RQGEOM_TRYONLY);
731         if(dockapp->reg==reg){
732             thisdockapp=dockapp;
733             if(flags&REGION_RQGEOM_TRYONLY){
734                 thisdockapp_copy=*dockapp;
735                 thisdockapp_copy.geom=*geom;
736                 da=&thisdockapp_copy;
737                 update=TRUE;
738             }
739             da->geom=*geom;
740         }
741         
742         if(update){
743             /* Calculcate preferred size */
744             dockapp_calc_preferred_size(dock, grow, &tile_size, da);
745                         
746             /* Determine whether dockapp should be placed on a tile */
747             da->tile=da->geom.w<=tile_size.w && da->geom.h<=tile_size.h;
748             
749             /* Calculate width and height */
750             if(da->tile){
751                 da->tile_geom.w=tile_size.w;
752                 da->tile_geom.h=tile_size.h;
753             }else{
754                 da->tile_geom.w=da->geom.w;
755                 da->tile_geom.h=da->geom.h;
756             }
757
758             /* Calculate border width and height */
759             da->border_geom.w=dockapp_bdw.left+da->tile_geom.w+dockapp_bdw.right;
760             da->border_geom.h=dockapp_bdw.top+da->tile_geom.h+dockapp_bdw.right;
761         }
762         
763         /* Calculate maximum and accumulated widths and heights */
764         if(da->border_geom.w>max_w)
765             max_w=da->border_geom.w;
766         total_w+=da->border_geom.w+(n_dockapps ? dockapp_bdw.spacing : 0);
767         
768         if(da->border_geom.h>max_h)
769             max_h=da->border_geom.h;
770         total_h+=da->border_geom.h+(n_dockapps ? dockapp_bdw.spacing : 0);
771         
772         /* Count dockapps */
773         ++n_dockapps;
774     }
775     
776     if(thisdockapp==NULL && reg!=NULL){
777         warn("Requesting dockapp not found.");
778         if(geomret)
779             *geomret=REGION_GEOM(reg);
780         return;
781     }
782     
783     /* Calculate width and height of dock */
784     if(n_dockapps){
785         switch(grow){
786         case DOCK_GROW_LEFT:
787         case DOCK_GROW_RIGHT:
788             dock_geom.w=total_w;
789             dock_geom.h=max_h;
790             break;
791         case DOCK_GROW_UP:
792         case DOCK_GROW_DOWN:
793         default:
794             dock_geom.w=max_w;
795             dock_geom.h=total_h;
796             break;
797         }
798     }else{
799         dock_geom.w=tile_size.w;
800         dock_geom.h=tile_size.h;
801     }
802     border_dock_geom.w=dock_bdw.left+dock_geom.w+dock_bdw.right;
803     border_dock_geom.h=dock_bdw.top+dock_geom.h+dock_bdw.bottom;
804     
805     calc_dock_pos(&border_dock_geom, &parent_geom, pos);
806     
807     /* Fit dock to new geom if required */
808     if(!(flags&REGION_RQGEOM_TRYONLY)){
809         WRQGeomParams rq=RQGEOMPARAMS_INIT;
810         
811         dock_set_minmax(dock, grow, &border_dock_geom);
812         
813         if(just_update_minmax)
814             return;
815         
816         rq.flags=REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y;
817         rq.geom=border_dock_geom;
818
819         dock->arrange_called=FALSE;
820         
821         region_rqgeom((WRegion*)dock, &rq, NULL);
822         
823         if(!dock->arrange_called)
824             dock_arrange_dockapps(dock, &REGION_GEOM(dock), NULL, NULL);
825         
826         if(thisdockapp!=NULL && geomret!=NULL)
827             *geomret=thisdockapp->geom;
828     }else{
829         if(thisdockapp!=NULL && geomret!=NULL){
830             dock_arrange_dockapps(dock, &REGION_GEOM(dock), 
831                                   thisdockapp, &thisdockapp_copy);
832             *geomret=thisdockapp_copy.geom;
833         }
834     }
835 }
836
837 static void dock_managed_rqgeom(WDock *dock, WRegion *reg, 
838                                 const WRQGeomParams *rq, 
839                                 WRectangle *geomret)
840 {
841     dock_managed_rqgeom_(dock, reg, rq->flags, &rq->geom, geomret, FALSE);
842 }
843
844
845 void dock_size_hints(WDock *dock, WSizeHints *hints)
846 {
847     hints->min_set=TRUE;
848     hints->min_width=dock->min_w;
849     hints->min_height=dock->min_h;
850     
851     hints->max_set=TRUE;
852     hints->max_width=dock->max_w;
853     hints->max_height=dock->max_h;
854 }
855
856
857 static bool dock_fitrep(WDock *dock, WWindow *parent, const WFitParams *fp)
858 {
859     if(!window_fitrep(&(dock->win), parent, fp))
860         return FALSE;
861
862     dock_arrange_dockapps(dock, &(fp->g), NULL, NULL);
863     
864     if(shape_extension)
865         dock_reshape(dock);
866     
867     return TRUE;
868 }
869
870
871 static int dock_orientation(WDock *dock)
872 {
873     return ((dock->grow==DOCK_GROW_LEFT || dock->grow==DOCK_GROW_RIGHT)
874             ? REGION_ORIENTATION_HORIZONTAL
875             : REGION_ORIENTATION_VERTICAL);
876 }
877
878
879 /*}}}*/
880
881
882 /*{{{ Drawing */
883
884
885 static void dock_draw(WDock *dock, bool complete)
886 {
887     int outline_style;
888     WRectangle g;
889     
890     if(dock->brush==NULL)
891         return;
892     
893     g.x=0;
894     g.y=0;
895     g.w=REGION_GEOM(dock).w;
896     g.h=REGION_GEOM(dock).h;
897     
898     grbrush_begin(dock->brush, &g, (complete ? 0 : GRBRUSH_NO_CLEAR_OK));
899     
900     dock_get_outline_style(dock, &outline_style);
901     switch(outline_style){
902     case DOCK_OUTLINE_STYLE_NONE:
903         break;
904     case DOCK_OUTLINE_STYLE_ALL:
905         {
906             WRectangle geom=REGION_GEOM(dock);
907             geom.x=geom.y=0;
908             grbrush_draw_border(dock->brush, &geom, "dock");
909         }
910         break;
911     case DOCK_OUTLINE_STYLE_EACH:
912         {
913             WDockApp *dockapp;
914             for(dockapp=dock->dockapps; dockapp!=NULL;
915                 dockapp=dockapp->next){
916                 grbrush_draw_border(dock->brush, &dockapp->tile_geom, 
917                                     "dock");
918             }
919         }
920         break;
921     }
922     
923     grbrush_end(dock->brush);
924 }
925
926
927 /*EXTL_DOC
928  * Resizes and refreshes \var{dock}.
929  */
930 EXTL_EXPORT_MEMBER
931 void dock_resize(WDock *dock)
932 {
933     dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, FALSE);
934     dock_draw(dock, TRUE);
935     
936 }
937
938 static void dock_brush_release(WDock *dock)
939 {
940     
941     if(dock->brush){
942         grbrush_release(dock->brush);
943         dock->brush=NULL;
944     }
945
946 }
947
948
949 static void dock_brush_get(WDock *dock)
950 {
951
952     dock_brush_release(dock);
953     dock->brush=gr_get_brush(((WWindow*)dock)->win, 
954                              region_rootwin_of((WRegion*)dock), 
955                              "stdisp-dock");
956 }
957
958
959 static void dock_updategr(WDock *dock)
960 {
961     dock_brush_get(dock);
962     dock_resize(dock);
963 }
964
965 /*}}}*/
966
967
968 /*{{{ Set/get */
969
970
971 static void mplexpos(int pos, int *mpos)
972 {
973     int hp=pos&DOCK_HPOS_MASK, vp=pos&DOCK_VPOS_MASK;
974     int p;
975     
976     p=(vp!=DOCK_VPOS_MIDDLE
977        ? (vp==DOCK_VPOS_TOP
978           ? (hp!=DOCK_HPOS_CENTER
979              ? (hp==DOCK_HPOS_RIGHT
980                 ? MPLEX_STDISP_TR
981                 : MPLEX_STDISP_TL)
982              : -1)
983           : (hp!=DOCK_HPOS_CENTER
984              ? (hp==DOCK_HPOS_RIGHT
985                 ? MPLEX_STDISP_BR
986                 : MPLEX_STDISP_BL)
987              : -1))
988        : -1);
989     
990     if(p==-1)
991         warn("Invalid dock position while as stdisp.");
992     else
993         *mpos=p;
994 }
995     
996
997 static void dock_do_set(WDock *dock, ExtlTab conftab, bool resize)
998 {
999     char *s;
1000     bool b;
1001     bool growset=FALSE;
1002     bool posset=FALSE;
1003     bool save=FALSE;
1004     
1005     if(extl_table_gets_s(conftab, dock_param_name.key, &s)){
1006         if(!region_set_name((WRegion*)dock, s)){
1007             warn_obj(modname, "Can't set name to \"%s\"", s);
1008         }
1009         free(s);
1010     }
1011
1012     if(extl_table_gets_b(conftab, "save", &save))
1013         dock->save=save;
1014     
1015     if(dock_param_extl_table_set(&dock_param_pos, conftab, &dock->pos))
1016         posset=TRUE;
1017
1018     if(dock_param_extl_table_set(&dock_param_grow, conftab, &dock->grow))
1019         growset=TRUE;
1020     
1021     if(extl_table_gets_b(conftab, dock_param_is_auto.key, &b))
1022         dock->is_auto=b;
1023
1024     if(resize && (growset || posset)){
1025         WMPlex *par=OBJ_CAST(REGION_PARENT(dock), WMPlex);
1026         WRegion *stdisp=NULL;
1027         WMPlexSTDispInfo din;
1028         
1029         if(par!=NULL){
1030             mplex_get_stdisp(par, &stdisp, &din);
1031             din.fullsize=FALSE; /* not supported. */
1032             if(stdisp==(WRegion*)dock){
1033                 if(posset)
1034                     mplexpos(dock->pos, &din.pos);
1035                 if(growset){
1036                     /* Update min/max first */
1037                     dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE);
1038                 }
1039                 mplex_set_stdisp(par, (WRegion*)dock, &din);
1040             }
1041         }
1042         
1043         dock_resize(dock);
1044     }
1045 }
1046
1047
1048 /*EXTL_DOC
1049  * Configure \var{dock}. \var{conftab} is a table of key/value pairs:
1050  *
1051  * \begin{tabularx}{\linewidth}{llX}
1052  *  \tabhead{Key & Values & Description}
1053  *  \var{name} & string & Name of dock \\
1054  *  \var{pos} & string in $\{t,m,b\}\times\{t,c,b\}$ & Dock position. 
1055  *       Can only be used in floating mode. \\
1056  *  \var{grow} & up/down/left/right & 
1057  *       Growth direction where new dockapps are added. Also
1058  *       sets orientation for dock when working as WMPlex status
1059  *       display (see \fnref{WMPlex.set_stdisp}). \\
1060  *  \var{is_auto} & bool & 
1061  *       Should \var{dock} automatically manage new dockapps? \\
1062  * \end{tabularx}
1063  *
1064  * Any parameters not explicitly set in \var{conftab} will be left unchanged.
1065  */
1066 EXTL_EXPORT_MEMBER
1067 void dock_set(WDock *dock, ExtlTab conftab)
1068 {
1069     dock_do_set(dock, conftab, TRUE);
1070 }
1071
1072
1073 static void dock_do_get(WDock *dock, ExtlTab conftab)
1074 {
1075     extl_table_sets_s(conftab, dock_param_name.key,
1076                       region_name((WRegion*)dock));
1077     dock_param_extl_table_get(&dock_param_pos, conftab, dock->pos);
1078     dock_param_extl_table_get(&dock_param_grow, conftab, dock->grow);
1079     extl_table_sets_b(conftab, dock_param_is_auto.key, dock->is_auto);
1080     extl_table_sets_b(conftab, "save", dock->save);
1081 }
1082
1083
1084 /*EXTL_DOC
1085  * Get \var{dock}'s configuration table. See \fnref{WDock.set} for a
1086  * description of the table.
1087  */
1088 EXTL_SAFE
1089 EXTL_EXPORT_MEMBER
1090 ExtlTab dock_get(WDock *dock)
1091 {
1092     ExtlTab conftab;
1093
1094     conftab=extl_create_table();
1095     dock_do_get(dock, conftab);
1096     return conftab;
1097 }
1098
1099
1100 /*}}}*/
1101
1102
1103 /*{{{ Init/deinit */
1104
1105
1106 static bool dock_init(WDock *dock, WWindow *parent, const WFitParams *fp)
1107 {
1108     WFitParams fp2=*fp;
1109     
1110     dock->pos=dock_param_pos.dflt;
1111     dock->grow=dock_param_grow.dflt;
1112     dock->is_auto=dock_param_is_auto.dflt;
1113     dock->brush=NULL;
1114     dock->dockapps=NULL;
1115     dock->min_w=1;
1116     dock->min_h=1;
1117     dock->max_w=1;
1118     dock->max_h=1;
1119     dock->arrange_called=FALSE;
1120     dock->save=TRUE;
1121
1122     
1123     if(!window_init((WWindow*)dock, parent, &fp2))
1124         return FALSE;
1125     
1126     region_add_bindmap((WRegion*)dock, dock_bindmap);
1127
1128     ((WRegion*)dock)->flags|=REGION_SKIP_FOCUS;
1129
1130     window_select_input(&(dock->win), IONCORE_EVENTMASK_CWINMGR);
1131
1132     dock_brush_get(dock);
1133
1134     LINK_ITEM(docks, dock, dock_next, dock_prev);
1135     
1136     return TRUE;
1137 }
1138
1139
1140 static WDock *create_dock(WWindow *parent, const WFitParams *fp)
1141 {
1142     CREATEOBJ_IMPL(WDock, dock, (p, parent, fp));
1143 }
1144
1145
1146 static void dock_deinit(WDock *dock)
1147 {
1148     while(dock->dockapps!=NULL)
1149         destroy_obj((Obj*)dock->dockapps->reg);
1150
1151     UNLINK_ITEM(docks, dock, dock_next, dock_prev);
1152
1153     dock_brush_release(dock);
1154
1155     window_deinit((WWindow*) dock);
1156 }
1157
1158
1159 bool dock_may_destroy(WDock *dock)
1160 {
1161     if(dock->dockapps!=NULL){
1162         warn_obj(modname, "Dock \"%s\" is still managing other objects "
1163                 " -- refusing to close.", region_name((WRegion*)dock));
1164         return FALSE;
1165     }
1166
1167     return TRUE;
1168 }
1169
1170
1171 EXTL_EXPORT
1172 WDock *mod_dock_create(ExtlTab tab)
1173 {
1174     char *mode=NULL;
1175     bool floating=FALSE;
1176     int screenid=0;
1177     WScreen *screen=NULL;
1178     WDock *dock=NULL;
1179     WRegion *stdisp=NULL;
1180     WMPlexSTDispInfo din;
1181     
1182     if(extl_table_gets_s(tab, "mode", &mode)){
1183         if(strcmp(mode, "floating")==0){
1184             floating=TRUE;
1185         }else if(strcmp(mode, "embedded")!=0){
1186             warn("Invalid dock mode.");
1187             free(mode);
1188             return NULL;
1189         }
1190         free(mode);
1191     }
1192     
1193     extl_table_gets_i(tab, "screen", &screenid);
1194     screen=ioncore_find_screen_id(screenid);
1195     if(screen==NULL){
1196         warn("Screen %d does not exist.", screenid);
1197         return NULL;
1198     }
1199     
1200     for(dock=docks; dock; dock=dock->dock_next){
1201         if(region_screen_of((WRegion*)dock)==screen){
1202             warn("Screen %d already has a dock. Refusing to create another.",
1203                  screenid);
1204             return NULL;
1205         }
1206     }
1207
1208     if(!floating){
1209         mplex_get_stdisp((WMPlex*)screen, &stdisp, &din);
1210         if(stdisp!=NULL && !extl_table_is_bool_set(tab, "force")){
1211             warn("Screen %d already has an stdisp. Refusing to add embedded "
1212                  "dock.", screenid);
1213             return NULL;
1214         }
1215     }
1216
1217     /* Create the dock */
1218     
1219     if(floating){
1220         WMPlexAttachParams par;
1221         
1222         par.flags=(MPLEX_ATTACH_UNNUMBERED
1223                    |MPLEX_ATTACH_SIZEPOLICY
1224                    |MPLEX_ATTACH_GEOM);
1225         
1226         par.szplcy=SIZEPOLICY_FREE;
1227         par.geom.x=0;
1228         par.geom.y=0;
1229         par.geom.w=1;
1230         par.geom.h=1;
1231         
1232         if(extl_table_is_bool_set(tab, "floating_hidden"))
1233             par.flags|=MPLEX_ATTACH_HIDDEN;
1234         
1235         dock=(WDock*)mplex_do_attach_new((WMPlex*)screen, &par,
1236                                          (WRegionCreateFn*)create_dock, 
1237                                          NULL);
1238     }else{
1239         WFitParams fp;
1240         
1241         fp.mode=REGION_FIT_BOUNDS|REGION_FIT_WHATEVER;
1242         fp.g.x=0;
1243         fp.g.y=0;
1244         fp.g.w=1;
1245         fp.g.h=1;
1246         
1247         dock=create_dock((WWindow*)screen, &fp);
1248     }
1249
1250     if(dock==NULL){
1251         warn("Failed to create dock.");
1252         return NULL;
1253     }
1254     
1255     /* Get parameters */
1256     dock->save=FALSE;
1257     dock_do_set(dock, tab, FALSE);
1258     
1259     /* Final setup */    
1260     if(floating){
1261         WRQGeomParams rq=RQGEOMPARAMS_INIT;
1262         const WRectangle *pg=&REGION_GEOM(screen);
1263         
1264         /* Just calculate real min/max size */
1265         dock_managed_rqgeom_(dock, NULL, 0, NULL, NULL, TRUE);
1266         
1267         rq.geom.w=minof(dock->min_w, pg->w);
1268         rq.geom.h=minof(dock->min_h, pg->h);
1269         calc_dock_pos(&rq.geom, pg, dock->pos);
1270         
1271         region_rqgeom((WRegion*)dock, &rq, NULL);
1272         
1273         return dock;
1274     }else{
1275         mplexpos(dock->pos, &din.pos);
1276         din.fullsize=FALSE; /* not supported */
1277         if(mplex_set_stdisp((WMPlex*)screen, (WRegion*)dock, &din))
1278             return dock;
1279     }
1280
1281     /* Failed to attach. */
1282     warn("Failed to attach dock to screen.");
1283     destroy_obj((Obj*)dock);
1284     return NULL;
1285 }
1286
1287
1288 /*}}}*/
1289
1290
1291 /*{{{ Toggle */
1292
1293
1294 /*EXTL_DOC
1295  * Toggle floating docks on \var{mplex}.
1296  */
1297 EXTL_EXPORT
1298 void mod_dock_set_floating_shown_on(WMPlex *mplex, const char *how)
1299 {
1300     int setpar=libtu_setparam_invert(libtu_string_to_setparam(how));
1301     WDock *dock;
1302     
1303     for(dock=docks; dock; dock=dock->dock_next){
1304         if(REGION_MANAGER(dock)==(WRegion*)mplex)
1305             mplex_set_hidden(mplex, (WRegion*)dock, setpar);
1306     }
1307 }
1308
1309
1310 /*}}}*/
1311
1312
1313 /*{{{ Save/load */
1314
1315
1316 ExtlTab dock_get_configuration(WDock *dock)
1317 {
1318     ExtlTab tab;
1319     
1320     if(dock->save==FALSE)
1321         return extl_table_none();
1322     
1323     tab=region_get_base_configuration((WRegion*)dock);
1324     dock_do_get(dock, tab);
1325
1326     return tab;
1327 }
1328
1329
1330 WRegion *dock_load(WWindow *par, const WFitParams *fp, ExtlTab tab)
1331 {
1332     WDock *dock=create_dock(par, fp);
1333     if(dock!=NULL){
1334         dock_set(dock, tab);
1335         dock_fitrep(dock, NULL, fp);
1336     }
1337
1338     return (WRegion*)dock;
1339 }
1340
1341
1342 /*}}}*/
1343
1344
1345 /*{{{ Client window management setup */
1346
1347
1348 static bool dock_do_attach_final(WDock *dock, WRegion *reg, void *unused)
1349 {
1350     WDockApp *dockapp, *before_dockapp;
1351     WRectangle geom;
1352     WFitParams fp;
1353     bool draw_border=TRUE;
1354     int pos=INT_MAX;
1355      
1356     /* Create and initialise a new WDockApp struct */
1357     dockapp=ALLOC(WDockApp);
1358     
1359     if(dockapp==NULL)
1360         return FALSE;
1361     
1362     if(OBJ_IS(reg, WClientWin)){
1363         ExtlTab proptab=((WClientWin*)reg)->proptab;
1364         extl_table_gets_b(proptab, CLIENTWIN_WINPROP_BORDER, &draw_border);
1365         extl_table_gets_i(proptab, CLIENTWIN_WINPROP_POSITION, &pos);
1366     }
1367
1368     dockapp->reg=reg;
1369     dockapp->draw_border=draw_border;
1370     dockapp->pos=pos;
1371     dockapp->tile=FALSE;
1372
1373     /* Insert the dockapp at the correct relative position */
1374     before_dockapp=dock->dockapps;
1375     for(before_dockapp=dock->dockapps;
1376         before_dockapp!=NULL && dockapp->pos>=before_dockapp->pos;
1377         before_dockapp=before_dockapp->next){
1378     }
1379     
1380     if(before_dockapp!=NULL){
1381         LINK_ITEM_BEFORE(dock->dockapps, before_dockapp, dockapp, next, prev);
1382     }else{
1383         LINK_ITEM(dock->dockapps, dockapp, next, prev);
1384     }
1385
1386     region_set_manager(reg, (WRegion*)dock);
1387     
1388     geom=REGION_GEOM(reg);
1389     dock_managed_rqgeom_(dock, reg, 
1390                          REGION_RQGEOM_WEAK_X|REGION_RQGEOM_WEAK_Y,
1391                          &geom, NULL, FALSE);
1392     
1393     region_map(reg);
1394
1395     return TRUE;
1396 }
1397
1398
1399
1400 static WRegion *dock_do_attach(WDock *dock, WRegionAttachData *data)
1401 {
1402     WFitParams fp;
1403     dock_get_tile_size(dock, &(fp.g));
1404     fp.g.x=0;
1405     fp.g.y=0;
1406     fp.mode=REGION_FIT_WHATEVER|REGION_FIT_BOUNDS;
1407     
1408     return region_attach_helper((WRegion*)dock, (WWindow*)dock, &fp,
1409                                 (WRegionDoAttachFn*)dock_do_attach_final, 
1410                                 NULL, data);
1411 }
1412
1413
1414 /*EXTL_DOC
1415  * Attach \var{reg} to \var{dock}.
1416  */
1417 EXTL_EXPORT_MEMBER
1418 bool dock_attach(WDock *dock, WRegion *reg)
1419 {
1420     WRegionAttachData data;
1421     WFitParams fp;
1422     
1423     data.type=REGION_ATTACH_REPARENT;
1424     data.u.reg=reg;
1425
1426     return (dock_do_attach(dock, &data)!=NULL);
1427 }
1428
1429
1430 static bool dock_handle_drop(WDock *dock, int x, int y,
1431                              WRegion *dropped)
1432 {
1433     return dock_attach(dock, dropped);
1434 }
1435
1436
1437 static WRegion *dock_ph_handler(WDock *dock, int flags, WRegionAttachData *data)
1438 {
1439     return dock_do_attach(dock, data);
1440 }
1441
1442     
1443 static WPHolder *dock_managed_get_pholder(WDock *dock, WRegion *mgd)
1444 {
1445     return (WPHolder*)create_basicpholder((WRegion*)dock,
1446                                           ((WBasicPHolderHandler*)
1447                                            dock_ph_handler));
1448 }
1449
1450
1451 static WPHolder *dock_prepare_manage(WDock *dock, const WClientWin *cwin,
1452                                      const WManageParams *param UNUSED,
1453                                      int redir)
1454 {
1455     if(redir==MANAGE_REDIR_STRICT_YES)
1456         return NULL;
1457
1458     return (WPHolder*)create_basicpholder((WRegion*)dock,
1459                                           ((WBasicPHolderHandler*)
1460                                            dock_ph_handler));
1461 }
1462
1463
1464 static void dock_managed_remove(WDock *dock, WRegion *reg)
1465 {
1466
1467     WDockApp *dockapp=dock_find_dockapp(dock, reg);
1468     
1469     if(dockapp==NULL)
1470         return;
1471     
1472     UNLINK_ITEM(dock->dockapps, dockapp, next, prev);
1473     free(dockapp);
1474
1475     region_unset_manager(reg, (WRegion*)dock);
1476
1477     dock_resize(dock);
1478 }
1479
1480
1481 static bool dock_clientwin_is_dockapp(WClientWin *cwin,
1482                                       const WManageParams *param)
1483 {
1484     bool is_dockapp=FALSE;
1485
1486     /* First, inspect the WManageParams.dockapp parameter */
1487     if(param->dockapp){
1488         is_dockapp=TRUE;
1489     }
1490
1491     /* Second, inspect the _NET_WM_WINDOW_TYPE property */
1492     if(!is_dockapp){
1493         static Atom atom__net_wm_window_type=None;
1494         static Atom atom__net_wm_window_type_dock=None;
1495         Atom actual_type=None;
1496         int actual_format;
1497         unsigned long nitems;
1498         unsigned long bytes_after;
1499         unsigned char *prop;
1500
1501         if(atom__net_wm_window_type==None){
1502             atom__net_wm_window_type=XInternAtom(ioncore_g.dpy,
1503                                                  "_NET_WM_WINDOW_TYPE",
1504                                                  False);
1505         }
1506         if(atom__net_wm_window_type_dock==None){
1507             atom__net_wm_window_type_dock=XInternAtom(ioncore_g.dpy,
1508                                                      "_NET_WM_WINDOW_TYPE_DOCK",
1509                                                       False);
1510         }
1511         if(XGetWindowProperty(ioncore_g.dpy, cwin->win, atom__net_wm_window_type,
1512                               0, sizeof(Atom), False, XA_ATOM, &actual_type,
1513                               &actual_format, &nitems, &bytes_after, &prop)
1514            ==Success){
1515             if(actual_type==XA_ATOM && nitems>=1
1516                && *(Atom*)prop==atom__net_wm_window_type_dock){
1517                 is_dockapp=TRUE;
1518             }
1519             XFree(prop);
1520         }
1521     }
1522
1523     /* Third, inspect the WM_CLASS property */
1524     if(!is_dockapp){
1525         char **p;
1526         int n;
1527
1528         p=xwindow_get_text_property(cwin->win, XA_WM_CLASS, &n);
1529         if(p!=NULL){
1530             if(n>=2 && strcmp(p[1], "DockApp")==0){
1531                 is_dockapp=TRUE;
1532             }
1533             XFreeStringList(p);
1534         }
1535     }
1536
1537     /* Fourth, inspect the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR property */
1538     if(!is_dockapp){
1539         static Atom atom__kde_net_wm_system_tray_window_for=None;
1540         Atom actual_type=None;
1541         int actual_format;
1542         unsigned long nitems;
1543         unsigned long bytes_after;
1544         unsigned char *prop;
1545
1546         if(atom__kde_net_wm_system_tray_window_for==None){
1547             atom__kde_net_wm_system_tray_window_for=XInternAtom(ioncore_g.dpy,
1548                                                                 "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
1549                                                                 False);
1550         }
1551         if(XGetWindowProperty(ioncore_g.dpy, cwin->win,
1552                               atom__kde_net_wm_system_tray_window_for, 0,
1553                               sizeof(Atom), False, AnyPropertyType, 
1554                               &actual_type, &actual_format, &nitems,
1555                               &bytes_after, &prop)==Success){
1556             if(actual_type!=None){
1557                 is_dockapp=TRUE;
1558             }
1559             XFree(prop);
1560         }
1561     }
1562
1563     return is_dockapp;
1564
1565 }
1566
1567
1568 static WDock *dock_find_suitable_dock(WClientWin *cwin,
1569                                       const WManageParams *param)
1570 {
1571     WDock *dock;
1572
1573     for(dock=docks; dock; dock=dock->dock_next){
1574         if(!dock->is_auto)
1575             continue;
1576         if(!region_same_rootwin((WRegion*)dock, (WRegion*)cwin))
1577             continue;
1578         break;
1579     }
1580
1581     return dock;
1582 }
1583
1584
1585 static bool clientwin_do_manage_hook(WClientWin *cwin, const WManageParams *param)
1586 {
1587     WDock *dock;
1588
1589     if(!dock_clientwin_is_dockapp(cwin, param)){
1590         return FALSE;
1591     }
1592
1593     dock=dock_find_suitable_dock(cwin, param);
1594     if(!dock){
1595         return FALSE;
1596     }
1597
1598     return region_manage_clientwin((WRegion*)dock, cwin, param,
1599                                    MANAGE_REDIR_PREFER_NO);
1600 }
1601
1602
1603 /*}}}*/
1604
1605
1606 /*{{{ Module init/deinit */
1607
1608
1609 bool mod_dock_init()
1610 {
1611
1612     if(XShapeQueryExtension(ioncore_g.dpy, &shape_event_basep,
1613                             &shape_error_basep)){
1614         shape_extension=TRUE;
1615     }else{
1616         XMissingExtension(ioncore_g.dpy, "SHAPE");
1617     }
1618
1619     if(!ioncore_register_regclass(&CLASSDESCR(WDock), 
1620                                   (WRegionLoadCreateFn*)dock_load)){
1621         return FALSE;
1622     }
1623
1624     if(!mod_dock_register_exports()){
1625         ioncore_unregister_regclass(&CLASSDESCR(WDock));
1626         return FALSE;
1627     }
1628
1629     dock_bindmap=ioncore_alloc_bindmap("WDock", NULL);
1630     if(dock_bindmap==NULL){
1631         warn("Unable to allocate dock bindmap.");
1632         mod_dock_unregister_exports();
1633         ioncore_unregister_regclass(&CLASSDESCR(WDock));
1634     }
1635
1636     extl_read_config("cfg_dock", NULL, TRUE);
1637
1638     hook_add(clientwin_do_manage_alt, 
1639              (WHookDummy*)clientwin_do_manage_hook);
1640
1641     return TRUE;
1642
1643 }
1644
1645
1646 void mod_dock_deinit()
1647 {
1648     WDock *dock;
1649
1650     ioncore_unregister_regclass(&CLASSDESCR(WDock));
1651
1652     hook_remove(clientwin_do_manage_alt, 
1653                 (WHookDummy*)clientwin_do_manage_hook);
1654
1655     dock=docks;
1656     while(dock!=NULL){
1657         WDock *next=dock->dock_next;
1658         destroy_obj((Obj*)dock);
1659         dock=next;
1660     }
1661
1662     mod_dock_unregister_exports();
1663     
1664     if(dock_bindmap!=NULL){
1665         ioncore_free_bindmap("WDock", dock_bindmap);
1666         dock_bindmap=NULL;
1667     }
1668 }
1669
1670
1671 /*}}}*/
1672
1673
1674 /*{{{ WDock class description and dynfun list */
1675
1676
1677 static DynFunTab dock_dynfuntab[]={
1678     {window_draw, dock_draw},
1679     {region_updategr, dock_updategr},
1680     {region_managed_rqgeom, dock_managed_rqgeom},
1681     {(DynFun*)region_prepare_manage, (DynFun*)dock_prepare_manage},
1682     {region_managed_remove, dock_managed_remove},
1683     {(DynFun*)region_get_configuration, (DynFun*)dock_get_configuration},
1684     {region_size_hints, dock_size_hints},
1685     {(DynFun*)region_fitrep, (DynFun*)dock_fitrep},
1686     {(DynFun*)region_orientation, (DynFun*)dock_orientation},
1687     {(DynFun*)region_may_destroy, (DynFun*)dock_may_destroy},
1688     {(DynFun*)region_handle_drop, (DynFun*)dock_handle_drop},
1689
1690     {(DynFun*)region_managed_get_pholder,
1691      (DynFun*)dock_managed_get_pholder},
1692     END_DYNFUNTAB
1693 };
1694
1695
1696 EXTL_EXPORT
1697 IMPLCLASS(WDock, WWindow, dock_deinit, dock_dynfuntab);
1698
1699
1700 /*}}}*/
1701