]> icculus.org git repositories - dana/openbox.git/blob - openbox/dock.c
use the minsize to keep the stuff ni the dock off of the bevel edges etc
[dana/openbox.git] / openbox / dock.c
1 #include "dock.h"
2 #include "screen.h"
3 #include "prop.h"
4 #include "config.h"
5 #include "grab.h"
6 #include "openbox.h"
7 #include "render/theme.h"
8
9 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
10                          EnterWindowMask | LeaveWindowMask)
11 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
12
13 static Dock *dock;
14
15 Strut dock_strut;
16
17 void dock_startup()
18 {
19     XSetWindowAttributes attrib;
20
21     STRUT_SET(dock_strut, 0, 0, 0, 0);
22
23     dock = g_new0(struct Dock, 1);
24     dock->obwin.type = Window_Dock;
25
26     dock->hidden = TRUE;
27
28     attrib.event_mask = DOCK_EVENT_MASK;
29     attrib.override_redirect = True;
30     dock->frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
31                                 RrDepth(ob_rr_inst), InputOutput,
32                                 RrVisual(ob_rr_inst),
33                                 CWOverrideRedirect | CWEventMask,
34                                 &attrib);
35     dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
36     XSetWindowBorder(ob_display, dock->frame, ob_rr_theme->b_color->pixel);
37     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->bwidth);
38
39     g_hash_table_insert(window_map, &dock->frame, dock);
40     stacking_add(DOCK_AS_WINDOW(dock));
41     stacking_raise(DOCK_AS_WINDOW(dock));
42 }
43
44 void dock_shutdown()
45 {
46     XDestroyWindow(ob_display, dock->frame);
47     RrAppearanceFree(dock->a_frame);
48     g_hash_table_remove(window_map, &dock->frame);
49     stacking_remove(dock);
50 }
51
52 void dock_add(Window win, XWMHints *wmhints)
53 {
54     DockApp *app;
55     XWindowAttributes attrib;
56     char **data;
57
58     app = g_new0(DockApp, 1);
59     app->obwin.type = Window_DockApp;
60     app->win = win;
61     app->icon_win = (wmhints->flags & IconWindowHint) ?
62         wmhints->icon_window : win;
63
64     if (PROP_GETSS(app->win, wm_class, locale, &data)) {
65         if (data[0]) {
66             app->name = g_strdup(data[0]);
67             if (data[1])
68                 app->class = g_strdup(data[1]);
69         }
70         g_strfreev(data);     
71     }
72
73     if (app->name == NULL) app->name = g_strdup("");
74     if (app->class == NULL) app->class = g_strdup("");
75     
76     if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
77         app->w = attrib.width;
78         app->h = attrib.height;
79     } else {
80         app->w = app->h = 64;
81     }
82
83     dock->dock_apps = g_list_append(dock->dock_apps, app);
84     dock_configure();
85
86     XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
87     /*
88       This is the same case as in frame.c for client windows. When Openbox is
89       starting, the window is already mapped so we see unmap events occur for
90       it. There are 2 unmap events generated that we see, one with the 'event'
91       member set the root window, and one set to the client, but both get
92       handled and need to be ignored.
93     */
94     if (ob_state == State_Starting)
95         app->ignore_unmaps += 2;
96
97     if (app->win != app->icon_win) {
98         /* have to map it so that it can be re-managed on a restart */
99         XMoveWindow(ob_display, app->win, -1000, -1000);
100         XMapWindow(ob_display, app->win);
101     }
102     XMapWindow(ob_display, app->icon_win);
103     XSync(ob_display, False);
104
105     /* specify that if we exit, the window should not be destroyed and should
106        be reparented back to root automatically */
107     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
108     XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
109
110     grab_button_full(2, 0, app->icon_win,
111                      ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
112                      GrabModeAsync, ob_cursors.move);
113
114     g_hash_table_insert(window_map, &app->icon_win, app);
115
116     g_message("Managed Dock App: 0x%lx (%s)", app->icon_win, app->class);
117 }
118
119 void dock_remove_all()
120 {
121     while (dock->dock_apps)
122         dock_remove(dock->dock_apps->data, TRUE);
123 }
124
125 void dock_remove(DockApp *app, gboolean reparent)
126 {
127     ungrab_button(2, 0, app->icon_win);
128     XSelectInput(ob_display, app->icon_win, NoEventMask);
129     /* remove the window from our save set */
130     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
131     XSync(ob_display, False);
132
133     g_hash_table_remove(window_map, &app->icon_win);
134
135     if (reparent)
136         XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
137
138     dock->dock_apps = g_list_remove(dock->dock_apps, app);
139     dock_configure();
140
141     g_message("Unmanaged Dock App: 0x%lx (%s)", app->icon_win, app->class);
142
143     g_free(app->name);
144     g_free(app->class);
145     g_free(app);
146 }
147
148 void dock_configure()
149 {
150     GList *it;
151     int spot;
152     int gravity;
153     int minw, minh;
154
155     RrMinsize(dock->a_frame, &minw, &minh);
156
157     dock->w = dock->h = 0;
158
159     /* get the size */
160     for (it = dock->dock_apps; it; it = it->next) {
161         struct DockApp *app = it->data;
162         if (config_dock_horz) {
163             dock->w += app->w;
164             dock->h = MAX(dock->h, app->h);
165         } else {
166             dock->w = MAX(dock->w, app->w);
167             dock->h += app->h;
168         }
169     }
170
171     spot = (config_dock_horz ? minw : minh) / 2;
172
173     /* position the apps */
174     for (it = dock->dock_apps; it; it = it->next) {
175         struct DockApp *app = it->data;
176         if (config_dock_horz) {
177             app->x = spot;
178             app->y = (dock->h - app->h) / 2;
179             spot += app->w;
180         } else {
181             app->x = (dock->w - app->w) / 2;
182             app->y = spot;
183             spot += app->h;
184         }
185
186         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
187     }
188
189     /* used for calculating offsets */
190     dock->w += ob_rr_theme->bwidth * 2;
191     dock->h += ob_rr_theme->bwidth * 2;
192
193     /* calculate position */
194     switch (config_dock_pos) {
195     case DockPos_Floating:
196         dock->x = config_dock_x;
197         dock->y = config_dock_y;
198         gravity = NorthWestGravity;
199         break;
200     case DockPos_TopLeft:
201         dock->x = 0;
202         dock->y = 0;
203         gravity = NorthWestGravity;
204         break;
205     case DockPos_Top:
206         dock->x = screen_physical_size.width / 2;
207         dock->y = 0;
208         gravity = NorthGravity;
209         break;
210     case DockPos_TopRight:
211         dock->x = screen_physical_size.width;
212         dock->y = 0;
213         gravity = NorthEastGravity;
214         break;
215     case DockPos_Left:
216         dock->x = 0;
217         dock->y = screen_physical_size.height / 2;
218         gravity = WestGravity;
219         break;
220     case DockPos_Right:
221         dock->x = screen_physical_size.width;
222         dock->y = screen_physical_size.height / 2;
223         gravity = EastGravity;
224         break;
225     case DockPos_BottomLeft:
226         dock->x = 0;
227         dock->y = screen_physical_size.height;
228         gravity = SouthWestGravity;
229         break;
230     case DockPos_Bottom:
231         dock->x = screen_physical_size.width / 2;
232         dock->y = screen_physical_size.height;
233         gravity = SouthGravity;
234         break;
235     case DockPos_BottomRight:
236         dock->x = screen_physical_size.width;
237         dock->y = screen_physical_size.height;
238         gravity = SouthEastGravity;
239         break;
240     }
241
242     switch(gravity) {
243     case NorthGravity:
244     case CenterGravity:
245     case SouthGravity:
246         dock->x -= dock->w / 2;
247         break;
248     case NorthEastGravity:
249     case EastGravity:
250     case SouthEastGravity:
251         dock->x -= dock->w;
252         break;
253     }
254     switch(gravity) {
255     case WestGravity:
256     case CenterGravity:
257     case EastGravity:
258         dock->y -= dock->h / 2;
259         break;
260     case SouthWestGravity:
261     case SouthGravity:
262     case SouthEastGravity:
263         dock->y -= dock->h;
264         break;
265     }
266
267     if (config_dock_hide && dock->hidden) {
268         switch (config_dock_pos) {
269         case DockPos_Floating:
270             break;
271         case DockPos_TopLeft:
272             if (config_dock_horz)
273                 dock->y -= dock->h - ob_rr_theme->bwidth;
274             else
275                 dock->x -= dock->w - ob_rr_theme->bwidth;
276             break;
277         case DockPos_Top:
278             dock->y -= dock->h - ob_rr_theme->bwidth;
279             break;
280         case DockPos_TopRight:
281             if (config_dock_horz)
282                 dock->y -= dock->h - ob_rr_theme->bwidth;
283             else
284                 dock->x += dock->w - ob_rr_theme->bwidth;
285             break;
286         case DockPos_Left:
287             dock->x -= dock->w - ob_rr_theme->bwidth;
288             break;
289         case DockPos_Right:
290             dock->x += dock->w - ob_rr_theme->bwidth;
291             break;
292         case DockPos_BottomLeft:
293             if (config_dock_horz)
294                 dock->y += dock->h - ob_rr_theme->bwidth;
295             else
296                 dock->x -= dock->w - ob_rr_theme->bwidth;
297             break;
298         case DockPos_Bottom:
299             dock->y += dock->h - ob_rr_theme->bwidth;
300             break;
301         case DockPos_BottomRight:
302             if (config_dock_horz)
303                 dock->y += dock->h - ob_rr_theme->bwidth;
304             else
305                 dock->x += dock->w - ob_rr_theme->bwidth;
306             break;
307         }    
308     }
309
310     /* set the strut */
311     switch (config_dock_pos) {
312     case DockPos_Floating:
313         STRUT_SET(dock_strut, 0, 0, 0, 0);
314         break;
315     case DockPos_TopLeft:
316         if (config_dock_horz)
317             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
318         else
319             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
320         break;
321     case DockPos_Top:
322         STRUT_SET(dock_strut, 0, dock->h, 0, 0);
323         break;
324     case DockPos_TopRight:
325         if (config_dock_horz)
326             STRUT_SET(dock_strut, 0, dock->h, 0, 0);
327         else
328             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
329         break;
330     case DockPos_Left:
331         STRUT_SET(dock_strut, dock->w, 0, 0, 0);
332         break;
333     case DockPos_Right:
334         STRUT_SET(dock_strut, 0, 0, dock->w, 0);
335         break;
336     case DockPos_BottomLeft:
337         if (config_dock_horz)
338             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
339         else
340             STRUT_SET(dock_strut, dock->w, 0, 0, 0);
341         break;
342     case DockPos_Bottom:
343         STRUT_SET(dock_strut, 0, 0, 0, dock->h);
344         break;
345     case DockPos_BottomRight:
346         if (config_dock_horz)
347             STRUT_SET(dock_strut, 0, 0, 0, dock->h);
348         else
349             STRUT_SET(dock_strut, 0, 0, dock->w, 0);
350         break;
351     }
352
353     dock->w += minw;
354     dock->h += minh;
355
356     /* not used for actually sizing shit */
357     dock->w -= ob_rr_theme->bwidth * 2;
358     dock->h -= ob_rr_theme->bwidth * 2;
359
360     if (dock->w > 0 && dock->h > 0) {
361         XMoveResizeWindow(ob_display, dock->frame,
362                           dock->x, dock->y, dock->w, dock->h);
363
364         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
365         XMapWindow(ob_display, dock->frame);
366     } else
367         XUnmapWindow(ob_display, dock->frame);
368
369     /* but they are useful outside of this function! */
370     dock->w += ob_rr_theme->bwidth * 2;
371     dock->h += ob_rr_theme->bwidth * 2;
372
373     screen_update_struts();
374 }
375
376 void dock_app_configure(DockApp *app, int w, int h)
377 {
378     app->w = w;
379     app->h = h;
380     dock_configure();
381 }
382
383 void dock_app_drag(DockApp *app, XMotionEvent *e)
384 {
385     DockApp *over = NULL;
386     GList *it;
387     int x, y;
388     gboolean after;
389
390     x = e->x_root;
391     y = e->y_root;
392
393     /* are we on top of the dock? */
394     if (!(x >= dock->x &&
395           y >= dock->y &&
396           x < dock->x + dock->w &&
397           y < dock->y + dock->h))
398         return;
399
400     x -= dock->x;
401     y -= dock->y;
402
403     /* which dock app are we on top of? */
404     for (it = dock->dock_apps; it; it = it->next) {
405         over = it->data;
406         if (config_dock_horz) {
407             if (x >= over->x && x < over->x + over->w)
408                 break;
409         } else {
410             if (y >= over->y && y < over->y + over->h)
411                 break;
412         }
413     }
414     if (!it || app == over) return;
415
416     x -= over->x;
417     y -= over->y;
418
419     if (config_dock_horz)
420         after = (x > over->w / 2);
421     else
422         after = (y > over->h / 2);
423
424     /* remove before doing the it->next! */
425     dock->dock_apps = g_list_remove(dock->dock_apps, app);
426
427     if (after) it = it->next;
428
429     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
430     dock_configure();
431 }
432
433 static void hide_timeout(void *n)
434 {
435     /* dont repeat */
436     timer_stop(dock->hide_timer);
437     dock->hide_timer = NULL;
438
439     /* hide */
440     dock->hidden = TRUE;
441     dock_configure();
442 }
443
444 void dock_hide(gboolean hide)
445 {
446     if (dock->hidden == hide || !config_dock_hide)
447         return;
448     if (!hide) {
449         /* show */
450         dock->hidden = FALSE;
451         dock_configure();
452
453         /* if was hiding, stop it */
454         if (dock->hide_timer) {
455             timer_stop(dock->hide_timer);
456             dock->hide_timer = NULL;
457         }
458     } else {
459         g_assert(!dock->hide_timer);
460         dock->hide_timer = timer_start(config_dock_hide_timeout * 1000,
461                                        (TimeoutHandler)hide_timeout,
462                                        NULL);
463     }
464 }