prefixing for the dock. use ObDirection instead of dupliacting it in another enum...
[mikachu/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 ObDock *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(ObDock, 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     ObDockApp *app;
55     XWindowAttributes attrib;
56     gchar **data;
57
58     app = g_new0(ObDockApp, 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 == OB_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_CURSOR_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(ObDockApp *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     gint spot;
152     gint gravity;
153     gint minw, minh;
154     gint strw, strh;
155     Rect *a;
156
157     RrMinsize(dock->a_frame, &minw, &minh);
158
159     dock->w = dock->h = 0;
160
161     /* get the size */
162     for (it = dock->dock_apps; it; it = it->next) {
163         ObDockApp *app = it->data;
164         if (config_dock_horz) {
165             dock->w += app->w;
166             dock->h = MAX(dock->h, app->h);
167         } else {
168             dock->w = MAX(dock->w, app->w);
169             dock->h += app->h;
170         }
171     }
172
173     spot = (config_dock_horz ? minw : minh) / 2;
174
175     /* position the apps */
176     for (it = dock->dock_apps; it; it = it->next) {
177         ObDockApp *app = it->data;
178         if (config_dock_horz) {
179             app->x = spot;
180             app->y = (dock->h - app->h) / 2;
181             spot += app->w;
182         } else {
183             app->x = (dock->w - app->w) / 2;
184             app->y = spot;
185             spot += app->h;
186         }
187
188         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
189     }
190
191     /* used for calculating offsets */
192     dock->w += ob_rr_theme->bwidth * 2;
193     dock->h += ob_rr_theme->bwidth * 2;
194
195     a = screen_physical_area();
196
197     /* calculate position */
198     if (config_dock_floating) {
199         dock->x = config_dock_x;
200         dock->y = config_dock_y;
201         gravity = NorthWestGravity;
202     } else {
203         switch (config_dock_pos) {
204         case OB_DIRECTION_NORTHWEST:
205             dock->x = 0;
206             dock->y = 0;
207             gravity = NorthWestGravity;
208             break;
209         case OB_DIRECTION_NORTH:
210             dock->x = a->width / 2;
211             dock->y = 0;
212             gravity = NorthGravity;
213             break;
214         case OB_DIRECTION_NORTHEAST:
215             dock->x = a->width;
216             dock->y = 0;
217             gravity = NorthEastGravity;
218             break;
219         case OB_DIRECTION_WEST:
220             dock->x = 0;
221             dock->y = a->height / 2;
222             gravity = WestGravity;
223             break;
224         case OB_DIRECTION_EAST:
225             dock->x = a->width;
226             dock->y = a->height / 2;
227             gravity = EastGravity;
228             break;
229         case OB_DIRECTION_SOUTHWEST:
230             dock->x = 0;
231             dock->y = a->height;
232             gravity = SouthWestGravity;
233             break;
234         case OB_DIRECTION_SOUTH:
235             dock->x = a->width / 2;
236             dock->y = a->height;
237             gravity = SouthGravity;
238             break;
239         case OB_DIRECTION_SOUTHEAST:
240             dock->x = a->width;
241             dock->y = a->height;
242             gravity = SouthEastGravity;
243             break;
244         }
245     }
246
247     switch(gravity) {
248     case NorthGravity:
249     case CenterGravity:
250     case SouthGravity:
251         dock->x -= dock->w / 2;
252         break;
253     case NorthEastGravity:
254     case EastGravity:
255     case SouthEastGravity:
256         dock->x -= dock->w;
257         break;
258     }
259     switch(gravity) {
260     case WestGravity:
261     case CenterGravity:
262     case EastGravity:
263         dock->y -= dock->h / 2;
264         break;
265     case SouthWestGravity:
266     case SouthGravity:
267     case SouthEastGravity:
268         dock->y -= dock->h;
269         break;
270     }
271
272     if (config_dock_hide && dock->hidden) {
273         if (!config_dock_floating) {
274             switch (config_dock_pos) {
275             case OB_DIRECTION_NORTHWEST:
276                 if (config_dock_horz)
277                     dock->y -= dock->h - ob_rr_theme->bwidth;
278                 else
279                     dock->x -= dock->w - ob_rr_theme->bwidth;
280                 break;
281             case OB_DIRECTION_NORTH:
282                 dock->y -= dock->h - ob_rr_theme->bwidth;
283                 break;
284             case OB_DIRECTION_NORTHEAST:
285                 if (config_dock_horz)
286                     dock->y -= dock->h - ob_rr_theme->bwidth;
287                 else
288                     dock->x += dock->w - ob_rr_theme->bwidth;
289                 break;
290             case OB_DIRECTION_WEST:
291                 dock->x -= dock->w - ob_rr_theme->bwidth;
292                 break;
293             case OB_DIRECTION_EAST:
294                 dock->x += dock->w - ob_rr_theme->bwidth;
295                 break;
296             case OB_DIRECTION_SOUTHWEST:
297                 if (config_dock_horz)
298                     dock->y += dock->h - ob_rr_theme->bwidth;
299                 else
300                     dock->x -= dock->w - ob_rr_theme->bwidth;
301                 break;
302             case OB_DIRECTION_SOUTH:
303                 dock->y += dock->h - ob_rr_theme->bwidth;
304                 break;
305             case OB_DIRECTION_SOUTHEAST:
306                 if (config_dock_horz)
307                     dock->y += dock->h - ob_rr_theme->bwidth;
308                 else
309                     dock->x += dock->w - ob_rr_theme->bwidth;
310                 break;
311             }    
312         }
313     }
314
315     if (!config_dock_floating && config_dock_hide) {
316         strw = strh = ob_rr_theme->bwidth;
317     } else {
318         strw = dock->w;
319         strh =  dock->h;
320     }
321
322     /* set the strut */
323     if (config_dock_floating) {
324         STRUT_SET(dock_strut, 0, 0, 0, 0);
325     } else {
326         switch (config_dock_pos) {
327         case OB_DIRECTION_NORTHWEST:
328             if (config_dock_horz)
329                 STRUT_SET(dock_strut, 0, strh, 0, 0);
330             else
331                 STRUT_SET(dock_strut, strw, 0, 0, 0);
332             break;
333         case OB_DIRECTION_NORTH:
334             STRUT_SET(dock_strut, 0, strh, 0, 0);
335             break;
336         case OB_DIRECTION_NORTHEAST:
337             if (config_dock_horz)
338                 STRUT_SET(dock_strut, 0, strh, 0, 0);
339             else
340                 STRUT_SET(dock_strut, 0, 0, strw, 0);
341             break;
342         case OB_DIRECTION_WEST:
343             STRUT_SET(dock_strut, strw, 0, 0, 0);
344             break;
345         case OB_DIRECTION_EAST:
346             STRUT_SET(dock_strut, 0, 0, strw, 0);
347             break;
348         case OB_DIRECTION_SOUTHWEST:
349             if (config_dock_horz)
350                 STRUT_SET(dock_strut, 0, 0, 0, strh);
351             else
352                 STRUT_SET(dock_strut, strw, 0, 0, 0);
353             break;
354         case OB_DIRECTION_SOUTH:
355             STRUT_SET(dock_strut, 0, 0, 0, strh);
356             break;
357         case OB_DIRECTION_SOUTHEAST:
358             if (config_dock_horz)
359                 STRUT_SET(dock_strut, 0, 0, 0, strh);
360             else
361                 STRUT_SET(dock_strut, 0, 0, strw, 0);
362             break;
363         }
364     }
365
366     dock->w += minw;
367     dock->h += minh;
368
369     /* not used for actually sizing shit */
370     dock->w -= ob_rr_theme->bwidth * 2;
371     dock->h -= ob_rr_theme->bwidth * 2;
372
373     if (dock->w > 0 && dock->h > 0) {
374         XMoveResizeWindow(ob_display, dock->frame,
375                           dock->x, dock->y, dock->w, dock->h);
376
377         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
378         XMapWindow(ob_display, dock->frame);
379     } else
380         XUnmapWindow(ob_display, dock->frame);
381
382     /* but they are useful outside of this function! */
383     dock->w += ob_rr_theme->bwidth * 2;
384     dock->h += ob_rr_theme->bwidth * 2;
385
386     screen_update_areas();
387 }
388
389 void dock_app_configure(ObDockApp *app, gint w, gint h)
390 {
391     app->w = w;
392     app->h = h;
393     dock_configure();
394 }
395
396 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
397 {
398     ObDockApp *over = NULL;
399     GList *it;
400     gint x, y;
401     gboolean after;
402
403     x = e->x_root;
404     y = e->y_root;
405
406     /* are we on top of the dock? */
407     if (!(x >= dock->x &&
408           y >= dock->y &&
409           x < dock->x + dock->w &&
410           y < dock->y + dock->h))
411         return;
412
413     x -= dock->x;
414     y -= dock->y;
415
416     /* which dock app are we on top of? */
417     for (it = dock->dock_apps; it; it = it->next) {
418         over = it->data;
419         if (config_dock_horz) {
420             if (x >= over->x && x < over->x + over->w)
421                 break;
422         } else {
423             if (y >= over->y && y < over->y + over->h)
424                 break;
425         }
426     }
427     if (!it || app == over) return;
428
429     x -= over->x;
430     y -= over->y;
431
432     if (config_dock_horz)
433         after = (x > over->w / 2);
434     else
435         after = (y > over->h / 2);
436
437     /* remove before doing the it->next! */
438     dock->dock_apps = g_list_remove(dock->dock_apps, app);
439
440     if (after) it = it->next;
441
442     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
443     dock_configure();
444 }
445
446 static void hide_timeout(void *n)
447 {
448     /* dont repeat */
449     timer_stop(dock->hide_timer);
450     dock->hide_timer = NULL;
451
452     /* hide */
453     dock->hidden = TRUE;
454     dock_configure();
455 }
456
457 void dock_hide(gboolean hide)
458 {
459     if (dock->hidden == hide || !config_dock_hide)
460         return;
461     if (!hide) {
462         /* show */
463         dock->hidden = FALSE;
464         dock_configure();
465
466         /* if was hiding, stop it */
467         if (dock->hide_timer) {
468             timer_stop(dock->hide_timer);
469             dock->hide_timer = NULL;
470         }
471     } else {
472         g_assert(!dock->hide_timer);
473         dock->hide_timer = timer_start(config_dock_hide_timeout * 1000,
474                                        (TimeoutHandler)hide_timeout,
475                                        NULL);
476     }
477 }