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