3 * Copyright (C) 2003 Tom Payne
4 * Copyright (C) 2003 Per Olofsson
5 * Copyright (C) 2004-2009 Tuomo Valkonen
7 * by Tom Payne <ion@tompayne.org>
8 * based on code by Per Olofsson <pelle@dsv.su.se>
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.
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.
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
24 * $Header: /home/twp/cvsroot/twp/ion/ion-devel-dock/dock.c,v 1.17 2003/12/21 11:59:48 twp Exp $
34 #include <X11/Xatom.h>
35 #include <X11/Xutil.h>
36 #include <X11/extensions/shape.h>
38 #include <libtu/objp.h>
39 #include <libtu/map.h>
40 #include <libtu/minmax.h>
41 #include <libextl/extl.h>
42 #include <libextl/readconfig.h>
43 #include <libmainloop/defer.h>
45 #include <ioncore/common.h>
46 #include <ioncore/clientwin.h>
47 #include <ioncore/eventh.h>
48 #include <ioncore/global.h>
49 #include <ioncore/manage.h>
50 #include <ioncore/names.h>
51 #include <ioncore/property.h>
52 #include <ioncore/resize.h>
53 #include <ioncore/window.h>
54 #include <ioncore/mplex.h>
55 #include <ioncore/saveload.h>
56 #include <ioncore/bindmaps.h>
57 #include <ioncore/regbind.h>
58 #include <ioncore/extlconv.h>
59 #include <ioncore/event.h>
60 #include <ioncore/resize.h>
61 #include <ioncore/sizehint.h>
62 #include <ioncore/basicpholder.h>
71 #include "../version.h"
73 static const char *modname
="dock";
74 const char mod_dock_ion_api_version
[]=NOTION_API_VERSION
;
76 static WBindmap
*dock_bindmap
=NULL
;
83 INTRSTRUCT(WDockParam
);
87 DECLSTRUCT(WDockParam
){
90 const StringIntMap
*map
;
95 WDockApp
*next
, *prev
;
101 WRectangle tile_geom
;
102 WRectangle border_geom
;
107 WDock
*dock_next
, *dock_prev
;
120 static WDock
*docks
=NULL
;
125 /*{{{ Parameter conversion */
127 static void dock_param_extl_table_get(const WDockParam
*param
,
128 ExtlTab conftab
, int value
)
132 s
=stringintmap_key(param
->map
, value
, NULL
);
134 extl_table_sets_s(conftab
, param
->key
, s
);
140 static bool dock_param_do_set(const WDockParam
*param
, char *s
,
144 int i
=stringintmap_value(param
->map
, s
, -1);
146 warn_obj(modname
, "Invalid %s \"%s\"", param
->desc
, s
);
160 static bool dock_param_extl_table_set(const WDockParam
*param
, ExtlTab conftab
,
165 if(extl_table_gets_s(conftab
, param
->key
, &s
))
166 return dock_param_do_set(param
, s
, ret
);
173 static bool dock_param_brush_set(const WDockParam
*param
, GrBrush
*brush
,
178 if(grbrush_get_extra(brush
, param
->key
, 's', &s
))
179 return dock_param_do_set(param
, s
, ret
);
188 /*{{{ Parameter descriptions */
190 static const WDockParam dock_param_name
={
198 #define DOCK_HPOS_MASK 0x000f
199 #define DOCK_HPOS_LEFT 0x0000
200 #define DOCK_HPOS_CENTER 0x0001
201 #define DOCK_HPOS_RIGHT 0x0002
202 #define DOCK_VPOS_MASK 0x00f0
203 #define DOCK_VPOS_TOP 0x0000
204 #define DOCK_VPOS_MIDDLE 0x0010
205 #define DOCK_VPOS_BOTTOM 0x0020
208 static StringIntMap dock_pos_map
[]={
209 {"tl", DOCK_VPOS_TOP
|DOCK_HPOS_LEFT
},
210 {"tc", DOCK_VPOS_TOP
|DOCK_HPOS_CENTER
},
211 {"tr", DOCK_VPOS_TOP
|DOCK_HPOS_RIGHT
},
212 {"ml", DOCK_VPOS_MIDDLE
|DOCK_HPOS_LEFT
},
213 {"mc", DOCK_VPOS_MIDDLE
|DOCK_HPOS_CENTER
},
214 {"mr", DOCK_VPOS_MIDDLE
|DOCK_HPOS_RIGHT
},
215 {"bl", DOCK_VPOS_BOTTOM
|DOCK_HPOS_LEFT
},
216 {"bc", DOCK_VPOS_BOTTOM
|DOCK_HPOS_CENTER
},
217 {"br", DOCK_VPOS_BOTTOM
|DOCK_HPOS_RIGHT
},
221 static WDockParam dock_param_pos
={
225 DOCK_HPOS_LEFT
|DOCK_VPOS_BOTTOM
236 static StringIntMap dock_grow_map
[]={
237 {"up", DOCK_GROW_UP
},
238 {"down", DOCK_GROW_DOWN
},
239 {"left", DOCK_GROW_LEFT
},
240 {"right", DOCK_GROW_RIGHT
},
244 WDockParam dock_param_grow
={
252 static const WDockParam dock_param_is_auto
={
260 enum WDockOutlineStyle
{
261 DOCK_OUTLINE_STYLE_NONE
,
262 DOCK_OUTLINE_STYLE_ALL
,
263 DOCK_OUTLINE_STYLE_EACH
266 static StringIntMap dock_outline_style_map
[]={
267 {"none", DOCK_OUTLINE_STYLE_NONE
},
268 {"all", DOCK_OUTLINE_STYLE_ALL
},
269 {"each", DOCK_OUTLINE_STYLE_EACH
},
273 WDockParam dock_param_outline_style
={
276 dock_outline_style_map
,
277 DOCK_OUTLINE_STYLE_ALL
281 static const WDockParam dock_param_tile_width
={
288 static const WDockParam dock_param_tile_height
={
301 #define CLIENTWIN_WINPROP_POSITION "dockposition"
302 #define CLIENTWIN_WINPROP_BORDER "dockborder"
304 static WDockApp
*dock_find_dockapp(WDock
*dock
, WRegion
*reg
)
308 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
309 if(dockapp
->reg
==reg
){
319 static void dock_get_outline_style(WDock
*dock
, int *ret
)
322 *ret
=dock_param_outline_style
.dflt
;
323 if(dock
->brush
!=NULL
)
324 dock_param_brush_set(&dock_param_outline_style
, dock
->brush
, ret
);
331 /*{{{ Size calculation */
334 static void dock_get_tile_size(WDock
*dock
, WRectangle
*ret
)
336 ExtlTab tile_size_table
;
340 ret
->w
=dock_param_tile_width
.dflt
;
341 ret
->h
=dock_param_tile_height
.dflt
;
342 if(dock
->brush
==NULL
)
344 if(grbrush_get_extra(dock
->brush
, "tile_size", 't', &tile_size_table
)){
345 extl_table_gets_i(tile_size_table
, dock_param_tile_width
.key
, &ret
->w
);
346 extl_table_gets_i(tile_size_table
, dock_param_tile_height
.key
, &ret
->h
);
347 extl_unref_table(tile_size_table
);
353 static void dock_get_pos_grow(WDock
*dock
, int *pos
, int *grow
)
355 WMPlex
*mplex
=OBJ_CAST(REGION_PARENT(dock
), WMPlex
);
356 WRegion
*mplex_stdisp
;
357 WMPlexSTDispInfo din
;
360 mplex_get_stdisp(mplex
, &mplex_stdisp
, &din
);
361 if(mplex_stdisp
==(WRegion
*)dock
){
362 /* Ok, we're assigned as a status display for mplex, so
363 * get parameters from there.
365 *pos
=((din
.pos
==MPLEX_STDISP_TL
|| din
.pos
==MPLEX_STDISP_BL
)
368 | ((din
.pos
==MPLEX_STDISP_TL
|| din
.pos
==MPLEX_STDISP_TR
)
382 static void dock_reshape(WDock
*dock
)
386 if(!ioncore_g
.shape_extension
)
389 dock_get_outline_style(dock
, &outline_style
);
391 switch(outline_style
){
392 case DOCK_OUTLINE_STYLE_NONE
:
393 case DOCK_OUTLINE_STYLE_EACH
:
397 /* Start with an empty set */
398 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
399 ShapeBounding
, 0, 0, NULL
, 0, ShapeSet
, 0);
401 /* Union with dockapp shapes */
402 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
403 WClientWin
*cwin
=OBJ_CAST(dockapp
->reg
, WClientWin
);
404 if(outline_style
==DOCK_OUTLINE_STYLE_EACH
405 && dockapp
->draw_border
){
406 /* Union with border shape */
407 XRectangle tile_rect
;
409 tile_rect
.x
=dockapp
->border_geom
.x
;
410 tile_rect
.y
=dockapp
->border_geom
.y
;
411 tile_rect
.width
=dockapp
->border_geom
.w
;
412 tile_rect
.height
=dockapp
->border_geom
.h
;
413 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
414 ShapeBounding
, 0, 0, &tile_rect
, 1,
416 }else if(cwin
!=NULL
){
417 /* Union with dockapp shape */
421 XRectangle
*rects
=XShapeGetRectangles(ioncore_g
.dpy
, cwin
->win
,
422 ShapeBounding
, &count
,
425 WRectangle dockapp_geom
=REGION_GEOM(cwin
);
426 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
428 dockapp_geom
.x
, dockapp_geom
.y
,
429 rects
, count
, ShapeUnion
, ordering
);
437 case DOCK_OUTLINE_STYLE_ALL
:
442 geom
=REGION_GEOM(dock
);
447 XShapeCombineRectangles(ioncore_g
.dpy
, ((WWindow
*)dock
)->win
,
448 ShapeBounding
, 0, 0, &rect
, 1, ShapeSet
, 0);
456 static void dock_arrange_dockapps(WDock
*dock
, const WRectangle
*bd_dockg
,
457 const WDockApp
*replace_this
,
460 GrBorderWidths dock_bdw
, dockapp_bdw
;
461 WDockApp dummy_copy
, *dockapp
;
462 int pos
, grow
, cur_coord
=0;
463 WRectangle dock_geom
;
465 dock
->arrange_called
=TRUE
;
467 dock_get_pos_grow(dock
, &pos
, &grow
);
469 /* Determine dock and dockapp border widths */
470 memset(&dock_bdw
, 0, sizeof(GrBorderWidths
));
471 memset(&dockapp_bdw
, 0, sizeof(GrBorderWidths
));
476 dock_get_outline_style(dock
, &outline_style
);
477 switch(outline_style
){
478 case DOCK_OUTLINE_STYLE_NONE
:
480 case DOCK_OUTLINE_STYLE_ALL
:
481 grbrush_get_border_widths(dock
->brush
, &dock_bdw
);
482 dockapp_bdw
.spacing
=dock_bdw
.spacing
;
484 case DOCK_OUTLINE_STYLE_EACH
:
485 grbrush_get_border_widths(dock
->brush
, &dockapp_bdw
);
490 dock_geom
.w
=bd_dockg
->w
-dock_bdw
.left
-dock_bdw
.right
;
491 dock_geom
.h
=bd_dockg
->h
-dock_bdw
.top
-dock_bdw
.bottom
;
493 /* Calculate initial co-ordinate for layout algorithm */
496 cur_coord
=dock_bdw
.top
+dock_geom
.h
;
499 cur_coord
=dock_bdw
.top
;
502 cur_coord
=dock_bdw
.left
+dock_geom
.w
;
504 case DOCK_GROW_RIGHT
:
505 cur_coord
=dock_bdw
.left
;
509 /* Arrange dockapps */
510 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
511 WDockApp
*da
=dockapp
;
513 if(replace_this
!=NULL
){
514 if(replace_this
==dockapp
){
522 /* Calculate first co-ordinate */
526 switch(pos
&DOCK_HPOS_MASK
){
530 case DOCK_HPOS_CENTER
:
531 da
->border_geom
.x
=(dock_geom
.w
-da
->border_geom
.w
)/2;
533 case DOCK_HPOS_RIGHT
:
534 da
->border_geom
.x
=dock_geom
.w
-da
->border_geom
.w
;
537 da
->border_geom
.x
+=dock_bdw
.left
;
540 case DOCK_GROW_RIGHT
:
541 switch(pos
&DOCK_VPOS_MASK
){
545 case DOCK_VPOS_MIDDLE
:
546 da
->border_geom
.y
=(dock_geom
.h
-da
->border_geom
.h
)/2;
548 case DOCK_VPOS_BOTTOM
:
549 da
->border_geom
.y
=dock_geom
.h
-da
->border_geom
.h
;
552 da
->border_geom
.y
+=dock_bdw
.top
;
556 /* Calculate second co-ordinate */
559 cur_coord
-=da
->border_geom
.h
;
560 da
->border_geom
.y
=cur_coord
;
561 cur_coord
-=dockapp_bdw
.spacing
;
564 da
->border_geom
.y
=cur_coord
;
565 cur_coord
+=da
->border_geom
.h
+dockapp_bdw
.spacing
;
568 cur_coord
-=da
->border_geom
.w
;
569 da
->border_geom
.x
=cur_coord
;
570 cur_coord
-=dockapp_bdw
.spacing
;
572 case DOCK_GROW_RIGHT
:
573 da
->border_geom
.x
=cur_coord
;
574 cur_coord
+=da
->border_geom
.w
+dockapp_bdw
.spacing
;
578 /* Calculate tile geom */
579 da
->tile_geom
.x
=da
->border_geom
.x
+dockapp_bdw
.left
;
580 da
->tile_geom
.y
=da
->border_geom
.y
+dockapp_bdw
.top
;
582 /* Calculate dockapp geom */
584 da
->geom
.x
=da
->tile_geom
.x
+(da
->tile_geom
.w
-da
->geom
.w
)/2;
585 da
->geom
.y
=da
->tile_geom
.y
+(da
->tile_geom
.h
-da
->geom
.h
)/2;
587 da
->geom
.x
=da
->tile_geom
.x
;
588 da
->geom
.y
=da
->tile_geom
.y
;
591 if(replace_this
==NULL
)
592 region_fit(da
->reg
, &(da
->geom
), REGION_FIT_BOUNDS
);
597 static void dock_set_minmax(WDock
*dock
, int grow
, const WRectangle
*g
)
601 if(grow
==DOCK_GROW_UP
|| grow
==DOCK_GROW_DOWN
){
611 static void dockapp_calc_preferred_size(WDock
*dock
, int grow
,
612 const WRectangle
*tile_size
,
615 int w
=da
->geom
.w
, h
=da
->geom
.h
;
617 if(grow
==DOCK_GROW_UP
|| grow
==DOCK_GROW_DOWN
){
618 da
->geom
.w
=MINOF(w
, tile_size
->w
);
622 da
->geom
.h
=MINOF(h
, tile_size
->h
);
625 region_size_hints_correct(da
->reg
, &(da
->geom
.w
), &(da
->geom
.h
), TRUE
);
630 static void dock_managed_rqgeom_(WDock
*dock
, WRegion
*reg
, int flags
,
631 const WRectangle
*geom
, WRectangle
*geomret
,
632 bool just_update_minmax
)
634 WDockApp
*dockapp
=NULL
, *thisdockapp
=NULL
, thisdockapp_copy
;
635 WRectangle dock_geom
, border_dock_geom
;
636 GrBorderWidths dock_bdw
, dockapp_bdw
;
637 int n_dockapps
=0, max_w
=1, max_h
=1, total_w
=0, total_h
=0;
639 WRectangle tile_size
;
641 /* dock_resize calls with NULL parameters. */
642 assert(reg
!=NULL
|| (geomret
==NULL
&& !(flags
®ION_RQGEOM_TRYONLY
)));
644 dock_get_pos_grow(dock
, &pos
, &grow
);
645 dock_get_tile_size(dock
, &tile_size
);
647 /* Determine dock and dockapp border widths */
648 memset(&dock_bdw
, 0, sizeof(GrBorderWidths
));
649 memset(&dockapp_bdw
, 0, sizeof(GrBorderWidths
));
654 dock_get_outline_style(dock
, &outline_style
);
655 switch(outline_style
){
656 case DOCK_OUTLINE_STYLE_NONE
:
658 case DOCK_OUTLINE_STYLE_ALL
:
659 grbrush_get_border_widths(dock
->brush
, &dock_bdw
);
660 dockapp_bdw
.spacing
=dock_bdw
.spacing
;
662 case DOCK_OUTLINE_STYLE_EACH
:
663 grbrush_get_border_widths(dock
->brush
, &dockapp_bdw
);
668 /* Calculate widths and heights */
669 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
; dockapp
=dockapp
->next
){
670 WDockApp
*da
=dockapp
;
671 bool update
=!(flags
®ION_RQGEOM_TRYONLY
);
672 if(dockapp
->reg
==reg
){
674 if(flags
®ION_RQGEOM_TRYONLY
){
675 thisdockapp_copy
=*dockapp
;
676 thisdockapp_copy
.geom
=*geom
;
677 da
=&thisdockapp_copy
;
684 /* Calculcate preferred size */
685 dockapp_calc_preferred_size(dock
, grow
, &tile_size
, da
);
687 /* Determine whether dockapp should be placed on a tile */
688 da
->tile
=da
->geom
.w
<=tile_size
.w
&& da
->geom
.h
<=tile_size
.h
;
690 /* Calculate width and height */
692 da
->tile_geom
.w
=tile_size
.w
;
693 da
->tile_geom
.h
=tile_size
.h
;
695 da
->tile_geom
.w
=da
->geom
.w
;
696 da
->tile_geom
.h
=da
->geom
.h
;
699 /* Calculate border width and height */
700 da
->border_geom
.w
=dockapp_bdw
.left
+da
->tile_geom
.w
+dockapp_bdw
.right
;
701 da
->border_geom
.h
=dockapp_bdw
.top
+da
->tile_geom
.h
+dockapp_bdw
.right
;
704 /* Calculate maximum and accumulated widths and heights */
705 if(da
->border_geom
.w
>max_w
)
706 max_w
=da
->border_geom
.w
;
707 total_w
+=da
->border_geom
.w
+(n_dockapps
? dockapp_bdw
.spacing
: 0);
709 if(da
->border_geom
.h
>max_h
)
710 max_h
=da
->border_geom
.h
;
711 total_h
+=da
->border_geom
.h
+(n_dockapps
? dockapp_bdw
.spacing
: 0);
717 if(thisdockapp
==NULL
&& reg
!=NULL
){
718 warn("Requesting dockapp not found.");
720 *geomret
=REGION_GEOM(reg
);
724 /* Calculate width and height of dock */
728 case DOCK_GROW_RIGHT
:
740 dock_geom
.w
=tile_size
.w
;
741 dock_geom
.h
=tile_size
.h
;
744 border_dock_geom
.x
=REGION_GEOM(dock
).x
;
745 border_dock_geom
.y
=REGION_GEOM(dock
).y
;
746 border_dock_geom
.w
=dock_bdw
.left
+dock_geom
.w
+dock_bdw
.right
;
747 border_dock_geom
.h
=dock_bdw
.top
+dock_geom
.h
+dock_bdw
.bottom
;
749 /* Fit dock to new geom if required */
750 if(!(flags
®ION_RQGEOM_TRYONLY
)){
751 WRQGeomParams rq
=RQGEOMPARAMS_INIT
;
753 dock_set_minmax(dock
, grow
, &border_dock_geom
);
755 if(just_update_minmax
)
758 rq
.flags
=REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
;
759 rq
.geom
=border_dock_geom
;
761 dock
->arrange_called
=FALSE
;
763 region_rqgeom((WRegion
*)dock
, &rq
, NULL
);
765 if(!dock
->arrange_called
)
766 dock_arrange_dockapps(dock
, ®ION_GEOM(dock
), NULL
, NULL
);
768 if(thisdockapp
!=NULL
&& geomret
!=NULL
)
769 *geomret
=thisdockapp
->geom
;
771 if(thisdockapp
!=NULL
&& geomret
!=NULL
){
772 dock_arrange_dockapps(dock
, ®ION_GEOM(dock
),
773 thisdockapp
, &thisdockapp_copy
);
774 *geomret
=thisdockapp_copy
.geom
;
779 static void dock_managed_rqgeom(WDock
*dock
, WRegion
*reg
,
780 const WRQGeomParams
*rq
,
783 dock_managed_rqgeom_(dock
, reg
, rq
->flags
, &rq
->geom
, geomret
, FALSE
);
787 void dock_size_hints(WDock
*dock
, WSizeHints
*hints
)
790 hints
->min_width
=dock
->min_w
;
791 hints
->min_height
=dock
->min_h
;
794 hints
->max_width
=dock
->max_w
;
795 hints
->max_height
=dock
->max_h
;
799 static bool dock_fitrep(WDock
*dock
, WWindow
*parent
, const WFitParams
*fp
)
801 if(!window_fitrep(&(dock
->win
), parent
, fp
))
804 dock_arrange_dockapps(dock
, &(fp
->g
), NULL
, NULL
);
806 if(ioncore_g
.shape_extension
)
813 static int dock_orientation(WDock
*dock
)
815 return ((dock
->grow
==DOCK_GROW_LEFT
|| dock
->grow
==DOCK_GROW_RIGHT
)
816 ? REGION_ORIENTATION_HORIZONTAL
817 : REGION_ORIENTATION_VERTICAL
);
827 static void dock_draw(WDock
*dock
, bool complete
)
832 if(dock
->brush
==NULL
)
837 g
.w
=REGION_GEOM(dock
).w
;
838 g
.h
=REGION_GEOM(dock
).h
;
840 grbrush_begin(dock
->brush
, &g
, (complete
? 0 : GRBRUSH_NO_CLEAR_OK
));
842 dock_get_outline_style(dock
, &outline_style
);
843 switch(outline_style
){
844 case DOCK_OUTLINE_STYLE_NONE
:
846 case DOCK_OUTLINE_STYLE_ALL
:
848 WRectangle geom
=REGION_GEOM(dock
);
850 grbrush_draw_border(dock
->brush
, &geom
);
853 case DOCK_OUTLINE_STYLE_EACH
:
856 for(dockapp
=dock
->dockapps
; dockapp
!=NULL
;
857 dockapp
=dockapp
->next
){
858 grbrush_draw_border(dock
->brush
, &dockapp
->tile_geom
);
864 grbrush_end(dock
->brush
);
869 * Resizes and refreshes \var{dock}.
872 void dock_resize(WDock
*dock
)
874 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, FALSE
);
875 dock_draw(dock
, TRUE
);
879 static void dock_brush_release(WDock
*dock
)
883 grbrush_release(dock
->brush
);
890 static void dock_brush_get(WDock
*dock
)
893 dock_brush_release(dock
);
894 dock
->brush
=gr_get_brush(((WWindow
*)dock
)->win
,
895 region_rootwin_of((WRegion
*)dock
),
900 static void dock_updategr(WDock
*dock
)
902 dock_brush_get(dock
);
912 static void mplexpos(int pos
, int *mpos
)
914 int hp
=pos
&DOCK_HPOS_MASK
, vp
=pos
&DOCK_VPOS_MASK
;
917 p
=(vp
!=DOCK_VPOS_MIDDLE
919 ? (hp
!=DOCK_HPOS_CENTER
920 ? (hp
==DOCK_HPOS_RIGHT
924 : (hp
!=DOCK_HPOS_CENTER
925 ? (hp
==DOCK_HPOS_RIGHT
932 warn("Invalid dock position while as stdisp.");
938 static void mplexszplcy(int pos
, WSizePolicy
*szplcy
)
940 int hp
=pos
&DOCK_HPOS_MASK
, vp
=pos
&DOCK_VPOS_MASK
;
943 p
=(vp
!=DOCK_VPOS_MIDDLE
945 ? (hp
!=DOCK_HPOS_CENTER
946 ? (hp
==DOCK_HPOS_RIGHT
947 ? SIZEPOLICY_GRAVITY_NORTHEAST
948 : SIZEPOLICY_GRAVITY_NORTHWEST
)
949 : SIZEPOLICY_GRAVITY_NORTH
)
950 : (hp
!=DOCK_HPOS_CENTER
951 ? (hp
==DOCK_HPOS_RIGHT
952 ? SIZEPOLICY_GRAVITY_SOUTHEAST
953 : SIZEPOLICY_GRAVITY_SOUTHWEST
)
954 : SIZEPOLICY_GRAVITY_SOUTH
))
955 : (hp
!=DOCK_HPOS_CENTER
956 ? (hp
==DOCK_HPOS_RIGHT
957 ? SIZEPOLICY_GRAVITY_EAST
958 : SIZEPOLICY_GRAVITY_WEST
)
959 : SIZEPOLICY_GRAVITY_CENTER
));
965 static void dock_do_set(WDock
*dock
, ExtlTab conftab
, bool resize
)
973 if(extl_table_gets_s(conftab
, dock_param_name
.key
, &s
)){
974 if(!region_set_name((WRegion
*)dock
, s
)){
975 warn_obj(modname
, "Can't set name to \"%s\"", s
);
980 if(extl_table_gets_b(conftab
, "save", &save
))
983 if(dock_param_extl_table_set(&dock_param_pos
, conftab
, &dock
->pos
))
986 if(dock_param_extl_table_set(&dock_param_grow
, conftab
, &dock
->grow
))
989 if(extl_table_gets_b(conftab
, dock_param_is_auto
.key
, &b
))
992 if(resize
&& (growset
|| posset
)){
993 WMPlex
*par
=OBJ_CAST(REGION_PARENT(dock
), WMPlex
);
994 WRegion
*stdisp
=NULL
;
995 WMPlexSTDispInfo din
;
998 mplex_get_stdisp(par
, &stdisp
, &din
);
999 din
.fullsize
=FALSE
; /* not supported. */
1000 if(stdisp
==(WRegion
*)dock
){
1002 mplexpos(dock
->pos
, &din
.pos
);
1004 /* Update min/max first */
1005 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, TRUE
);
1007 mplex_set_stdisp(par
, (WRegion
*)dock
, &din
);
1008 }else if((WRegion
*)par
==REGION_MANAGER(dock
)){
1010 mplexszplcy(dock
->pos
, &szplcy
);
1011 mplex_set_szplcy(par
, (WRegion
*)dock
, szplcy
);
1021 * Configure \var{dock}. \var{conftab} is a table of key/value pairs:
1023 * \begin{tabularx}{\linewidth}{llX}
1024 * \tabhead{Key & Values & Description}
1025 * \var{name} & string & Name of dock \\
1026 * \var{pos} & string in $\{t,m,b\}\times\{t,c,b\}$ & Dock position.
1027 * Can only be used in floating mode. \\
1028 * \var{grow} & up/down/left/right &
1029 * Growth direction where new dockapps are added. Also
1030 * sets orientation for dock when working as WMPlex status
1031 * display (see \fnref{WMPlex.set_stdisp}). \\
1032 * \var{is_auto} & bool &
1033 * Should \var{dock} automatically manage new dockapps? \\
1036 * Any parameters not explicitly set in \var{conftab} will be left unchanged.
1039 void dock_set(WDock
*dock
, ExtlTab conftab
)
1041 dock_do_set(dock
, conftab
, TRUE
);
1045 static void dock_do_get(WDock
*dock
, ExtlTab conftab
)
1047 extl_table_sets_s(conftab
, dock_param_name
.key
,
1048 region_name((WRegion
*)dock
));
1049 dock_param_extl_table_get(&dock_param_pos
, conftab
, dock
->pos
);
1050 dock_param_extl_table_get(&dock_param_grow
, conftab
, dock
->grow
);
1051 extl_table_sets_b(conftab
, dock_param_is_auto
.key
, dock
->is_auto
);
1052 extl_table_sets_b(conftab
, "save", dock
->save
);
1057 * Get \var{dock}'s configuration table. See \fnref{WDock.set} for a
1058 * description of the table.
1062 ExtlTab
dock_get(WDock
*dock
)
1066 conftab
=extl_create_table();
1067 dock_do_get(dock
, conftab
);
1075 /*{{{ Init/deinit */
1078 static bool dock_init(WDock
*dock
, WWindow
*parent
, const WFitParams
*fp
)
1082 dock
->pos
=dock_param_pos
.dflt
;
1083 dock
->grow
=dock_param_grow
.dflt
;
1084 dock
->is_auto
=dock_param_is_auto
.dflt
;
1086 dock
->dockapps
=NULL
;
1091 dock
->arrange_called
=FALSE
;
1095 if(!window_init((WWindow
*)dock
, parent
, &fp2
, "WDock"))
1098 region_add_bindmap((WRegion
*)dock
, dock_bindmap
);
1100 window_select_input(&(dock
->win
), IONCORE_EVENTMASK_CWINMGR
);
1102 dock_brush_get(dock
);
1104 LINK_ITEM(docks
, dock
, dock_next
, dock_prev
);
1110 static WDock
*create_dock(WWindow
*parent
, const WFitParams
*fp
)
1112 CREATEOBJ_IMPL(WDock
, dock
, (p
, parent
, fp
));
1116 static void dock_deinit(WDock
*dock
)
1118 while(dock
->dockapps
!=NULL
)
1119 destroy_obj((Obj
*)dock
->dockapps
->reg
);
1121 UNLINK_ITEM(docks
, dock
, dock_next
, dock_prev
);
1123 dock_brush_release(dock
);
1125 window_deinit((WWindow
*) dock
);
1130 WDock
*mod_dock_create(ExtlTab tab
)
1133 bool floating
=FALSE
;
1135 WScreen
*screen
=NULL
;
1137 WRegion
*stdisp
=NULL
;
1138 WMPlexSTDispInfo din
;
1141 if(extl_table_gets_s(tab
, "mode", &mode
)){
1142 if(strcmp(mode
, "floating")==0){
1144 }else if(strcmp(mode
, "embedded")!=0){
1145 warn("Invalid dock mode.");
1152 extl_table_gets_i(tab
, "screen", &screenid
);
1153 screen
=ioncore_find_screen_id(screenid
);
1155 warn("Screen %d does not exist.", screenid
);
1159 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1160 if(region_screen_of((WRegion
*)dock
)==screen
){
1161 warn("Screen %d already has a dock. Refusing to create another.",
1168 mplex_get_stdisp((WMPlex
*)screen
, &stdisp
, &din
);
1169 if(stdisp
!=NULL
&& !extl_table_is_bool_set(tab
, "force")){
1170 warn("Screen %d already has an stdisp. Refusing to add embedded "
1176 /* Create the dock */
1177 fp
.mode
=REGION_FIT_BOUNDS
|REGION_FIT_WHATEVER
;
1183 dock
=create_dock((WWindow
*)screen
, &fp
);
1186 warn("Failed to create dock.");
1191 /* Get parameters */
1193 dock_do_set(dock
, tab
, FALSE
);
1195 /* Calculate min/max size */
1196 dock_managed_rqgeom_(dock
, NULL
, 0, NULL
, NULL
, TRUE
);
1200 WMPlexAttachParams par
=MPLEXATTACHPARAMS_INIT
;
1201 WRegionAttachData data
;
1203 par
.flags
=(MPLEX_ATTACH_UNNUMBERED
1204 |MPLEX_ATTACH_SIZEPOLICY
1206 |MPLEX_ATTACH_PASSIVE
);
1208 par
.geom
.w
=dock
->min_w
;
1209 par
.geom
.h
=dock
->min_h
;
1213 mplexszplcy(dock
->pos
, &par
.szplcy
);
1215 if(extl_table_is_bool_set(tab
, "floating_hidden"))
1216 par
.flags
|=MPLEX_ATTACH_HIDDEN
;
1218 data
.type
=REGION_ATTACH_REPARENT
;
1219 data
.u
.reg
=(WRegion
*)dock
;
1221 if(mplex_do_attach((WMPlex
*)screen
, &par
, &data
))
1224 mplexpos(dock
->pos
, &din
.pos
);
1225 din
.fullsize
=FALSE
; /* not supported */
1226 if(mplex_set_stdisp((WMPlex
*)screen
, (WRegion
*)dock
, &din
))
1230 /* Failed to attach. */
1231 warn("Failed to attach dock to screen.");
1232 destroy_obj((Obj
*)dock
);
1244 * Toggle floating docks on \var{mplex}.
1247 void mod_dock_set_floating_shown_on(WMPlex
*mplex
, const char *how
)
1249 int setpar
=libtu_setparam_invert(libtu_string_to_setparam(how
));
1252 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1253 if(REGION_MANAGER(dock
)==(WRegion
*)mplex
)
1254 mplex_set_hidden(mplex
, (WRegion
*)dock
, setpar
);
1265 ExtlTab
dock_get_configuration(WDock
*dock
)
1269 if(dock
->save
==FALSE
)
1270 return extl_table_none();
1272 tab
=region_get_base_configuration((WRegion
*)dock
);
1273 dock_do_get(dock
, tab
);
1279 WRegion
*dock_load(WWindow
*par
, const WFitParams
*fp
, ExtlTab tab
)
1281 WDock
*dock
=create_dock(par
, fp
);
1283 dock_set(dock
, tab
);
1284 dock_fitrep(dock
, NULL
, fp
);
1287 return (WRegion
*)dock
;
1294 /*{{{ Client window management setup */
1297 static bool dock_do_attach_final(WDock
*dock
, WRegion
*reg
, void *UNUSED(unused
))
1299 WDockApp
*dockapp
, *before_dockapp
;
1301 bool draw_border
=TRUE
;
1304 /* Create and initialise a new WDockApp struct */
1305 dockapp
=ALLOC(WDockApp
);
1310 if(OBJ_IS(reg
, WClientWin
)){
1311 ExtlTab proptab
=((WClientWin
*)reg
)->proptab
;
1312 extl_table_gets_b(proptab
, CLIENTWIN_WINPROP_BORDER
, &draw_border
);
1313 extl_table_gets_i(proptab
, CLIENTWIN_WINPROP_POSITION
, &pos
);
1317 dockapp
->draw_border
=draw_border
;
1319 dockapp
->tile
=FALSE
;
1321 /* Insert the dockapp at the correct relative position */
1322 before_dockapp
=dock
->dockapps
;
1323 for(before_dockapp
=dock
->dockapps
;
1324 before_dockapp
!=NULL
&& dockapp
->pos
>=before_dockapp
->pos
;
1325 before_dockapp
=before_dockapp
->next
){
1328 if(before_dockapp
!=NULL
){
1329 LINK_ITEM_BEFORE(dock
->dockapps
, before_dockapp
, dockapp
, next
, prev
);
1331 LINK_ITEM(dock
->dockapps
, dockapp
, next
, prev
);
1334 region_set_manager(reg
, (WRegion
*)dock
);
1336 geom
=REGION_GEOM(reg
);
1337 dock_managed_rqgeom_(dock
, reg
,
1338 REGION_RQGEOM_WEAK_X
|REGION_RQGEOM_WEAK_Y
,
1339 &geom
, NULL
, FALSE
);
1348 static WRegion
*dock_do_attach(WDock
*dock
, WRegionAttachData
*data
)
1351 dock_get_tile_size(dock
, &(fp
.g
));
1354 fp
.mode
=REGION_FIT_WHATEVER
|REGION_FIT_BOUNDS
;
1356 return region_attach_helper((WRegion
*)dock
, (WWindow
*)dock
, &fp
,
1357 (WRegionDoAttachFn
*)dock_do_attach_final
,
1363 * Attach \var{reg} to \var{dock}.
1366 bool dock_attach(WDock
*dock
, WRegion
*reg
)
1368 WRegionAttachData data
;
1370 data
.type
=REGION_ATTACH_REPARENT
;
1373 return (dock_do_attach(dock
, &data
)!=NULL
);
1377 static bool dock_handle_drop(WDock
*dock
, int UNUSED(x
), int UNUSED(y
),
1380 return dock_attach(dock
, dropped
);
1384 static WRegion
*dock_ph_handler(WDock
*dock
, int UNUSED(flags
), WRegionAttachData
*data
)
1386 return dock_do_attach(dock
, data
);
1390 static WPHolder
*dock_managed_get_pholder(WDock
*dock
, WRegion
*UNUSED(mgd
))
1392 return (WPHolder
*)create_basicpholder((WRegion
*)dock
,
1393 ((WBasicPHolderHandler
*)
1398 static WPHolder
*dock_prepare_manage(WDock
*dock
, const WClientWin
*UNUSED(cwin
),
1399 const WManageParams
*UNUSED(param
),
1402 if(!MANAGE_PRIORITY_OK(priority
, MANAGE_PRIORITY_LOW
))
1405 return (WPHolder
*)create_basicpholder((WRegion
*)dock
,
1406 ((WBasicPHolderHandler
*)
1411 static void dock_managed_remove(WDock
*dock
, WRegion
*reg
)
1414 WDockApp
*dockapp
=dock_find_dockapp(dock
, reg
);
1419 UNLINK_ITEM(dock
->dockapps
, dockapp
, next
, prev
);
1422 region_unset_manager(reg
, (WRegion
*)dock
);
1428 static bool dock_clientwin_is_dockapp(WClientWin
*cwin
,
1429 const WManageParams
*param
)
1431 bool is_dockapp
=FALSE
;
1433 /* First, inspect the WManageParams.dockapp parameter */
1438 /* Second, inspect the _NET_WM_WINDOW_TYPE property */
1440 static Atom atom__net_wm_window_type
=None
;
1441 static Atom atom__net_wm_window_type_dock
=None
;
1442 Atom actual_type
=None
;
1444 unsigned long nitems
;
1445 unsigned long bytes_after
;
1446 unsigned char *prop
;
1448 if(atom__net_wm_window_type
==None
){
1449 atom__net_wm_window_type
=XInternAtom(ioncore_g
.dpy
,
1450 "_NET_WM_WINDOW_TYPE",
1453 if(atom__net_wm_window_type_dock
==None
){
1454 atom__net_wm_window_type_dock
=XInternAtom(ioncore_g
.dpy
,
1455 "_NET_WM_WINDOW_TYPE_DOCK",
1458 if(XGetWindowProperty(ioncore_g
.dpy
, cwin
->win
, atom__net_wm_window_type
,
1459 0, sizeof(Atom
), False
, XA_ATOM
, &actual_type
,
1460 &actual_format
, &nitems
, &bytes_after
, &prop
)
1462 if(actual_type
==XA_ATOM
&& nitems
>=1
1463 && *(Atom
*)prop
==atom__net_wm_window_type_dock
){
1470 /* Third, inspect the WM_CLASS property */
1475 p
=xwindow_get_text_property(cwin
->win
, XA_WM_CLASS
, &n
);
1477 if(n
>=2 && strcmp(p
[1], "DockApp")==0){
1484 /* Fourth, inspect the _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR property */
1486 static Atom atom__kde_net_wm_system_tray_window_for
=None
;
1487 Atom actual_type
=None
;
1489 unsigned long nitems
;
1490 unsigned long bytes_after
;
1491 unsigned char *prop
;
1493 if(atom__kde_net_wm_system_tray_window_for
==None
){
1494 atom__kde_net_wm_system_tray_window_for
=XInternAtom(ioncore_g
.dpy
,
1495 "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR",
1498 if(XGetWindowProperty(ioncore_g
.dpy
, cwin
->win
,
1499 atom__kde_net_wm_system_tray_window_for
, 0,
1500 sizeof(Atom
), False
, AnyPropertyType
,
1501 &actual_type
, &actual_format
, &nitems
,
1502 &bytes_after
, &prop
)==Success
){
1503 if(actual_type
!=None
){
1515 static WDock
*dock_find_suitable_dock(WClientWin
*cwin
,
1516 const WManageParams
*param
)
1520 for(dock
=docks
; dock
; dock
=dock
->dock_next
){
1523 if(!region_same_rootwin((WRegion
*)dock
, (WRegion
*)cwin
))
1532 static bool clientwin_do_manage_hook(WClientWin
*cwin
, const WManageParams
*param
)
1536 if(!dock_clientwin_is_dockapp(cwin
, param
)){
1540 dock
=dock_find_suitable_dock(cwin
, param
);
1545 return region_manage_clientwin((WRegion
*)dock
, cwin
, param
,
1546 MANAGE_PRIORITY_NONE
);
1553 /*{{{ Module init/deinit */
1556 bool mod_dock_init()
1559 if(!ioncore_register_regclass(&CLASSDESCR(WDock
),
1560 (WRegionLoadCreateFn
*)dock_load
)){
1564 if(!mod_dock_register_exports()){
1565 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1569 dock_bindmap
=ioncore_alloc_bindmap("WDock", NULL
);
1570 if(dock_bindmap
==NULL
){
1571 warn("Unable to allocate dock bindmap.");
1572 mod_dock_unregister_exports();
1573 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1576 extl_read_config("cfg_dock", NULL
, TRUE
);
1578 hook_add(clientwin_do_manage_alt
,
1579 (WHookDummy
*)clientwin_do_manage_hook
);
1586 void mod_dock_deinit()
1590 ioncore_unregister_regclass(&CLASSDESCR(WDock
));
1592 hook_remove(clientwin_do_manage_alt
,
1593 (WHookDummy
*)clientwin_do_manage_hook
);
1597 WDock
*next
=dock
->dock_next
;
1598 destroy_obj((Obj
*)dock
);
1602 mod_dock_unregister_exports();
1604 if(dock_bindmap
!=NULL
){
1605 ioncore_free_bindmap("WDock", dock_bindmap
);
1614 /*{{{ WDock class description and dynfun list */
1617 static DynFunTab dock_dynfuntab
[]={
1618 {window_draw
, dock_draw
},
1619 {region_updategr
, dock_updategr
},
1620 {region_managed_rqgeom
, dock_managed_rqgeom
},
1621 {(DynFun
*)region_prepare_manage
, (DynFun
*)dock_prepare_manage
},
1622 {region_managed_remove
, dock_managed_remove
},
1623 {(DynFun
*)region_get_configuration
, (DynFun
*)dock_get_configuration
},
1624 {region_size_hints
, dock_size_hints
},
1625 {(DynFun
*)region_fitrep
, (DynFun
*)dock_fitrep
},
1626 {(DynFun
*)region_orientation
, (DynFun
*)dock_orientation
},
1627 {(DynFun
*)region_handle_drop
, (DynFun
*)dock_handle_drop
},
1629 {(DynFun
*)region_managed_get_pholder
,
1630 (DynFun
*)dock_managed_get_pholder
},
1636 IMPLCLASS(WDock
, WWindow
, dock_deinit
, dock_dynfuntab
);