hide the dock when reconfiguring otherwise the user has to move the mouse over it...
[mikachu/openbox.git] / openbox / dock.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    dock.c for the Openbox window manager
4    Copyright (c) 2003        Ben Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "debug.h"
20 #include "dock.h"
21 #include "mainloop.h"
22 #include "screen.h"
23 #include "prop.h"
24 #include "config.h"
25 #include "grab.h"
26 #include "openbox.h"
27 #include "render/theme.h"
28
29 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
30                          EnterWindowMask | LeaveWindowMask)
31 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
32
33 static ObDock *dock;
34
35 StrutPartial dock_strut;
36
37 void dock_startup(gboolean reconfig)
38 {
39     XSetWindowAttributes attrib;
40
41     if (reconfig) {
42         XSetWindowBorder(ob_display, dock->frame,
43                          RrColorPixel(ob_rr_theme->b_color));
44         XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->bwidth);
45
46         RrAppearanceFree(dock->a_frame);
47         dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
48
49         stacking_add(DOCK_AS_WINDOW(dock));
50
51         dock_configure();
52         dock_hide(TRUE);
53         return;
54     }
55
56     STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
57                       0, 0, 0, 0, 0, 0, 0, 0);
58
59     dock = g_new0(ObDock, 1);
60     dock->obwin.type = Window_Dock;
61
62     dock->hidden = TRUE;
63
64     attrib.event_mask = DOCK_EVENT_MASK;
65     attrib.override_redirect = True;
66     dock->frame = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen),
67                                 0, 0, 1, 1, 0,
68                                 RrDepth(ob_rr_inst), InputOutput,
69                                 RrVisual(ob_rr_inst),
70                                 CWOverrideRedirect | CWEventMask,
71                                 &attrib);
72     dock->a_frame = RrAppearanceCopy(ob_rr_theme->a_unfocused_title);
73     XSetWindowBorder(ob_display, dock->frame,
74                      RrColorPixel(ob_rr_theme->b_color));
75     XSetWindowBorderWidth(ob_display, dock->frame, ob_rr_theme->bwidth);
76
77     g_hash_table_insert(window_map, &dock->frame, dock);
78     stacking_add(DOCK_AS_WINDOW(dock));
79 }
80
81 void dock_shutdown(gboolean reconfig)
82 {
83     if (reconfig) {
84         stacking_remove(DOCK_AS_WINDOW(dock));
85         return;
86     }
87
88     XDestroyWindow(ob_display, dock->frame);
89     RrAppearanceFree(dock->a_frame);
90     g_hash_table_remove(window_map, &dock->frame);
91     stacking_remove(dock);
92 }
93
94 void dock_add(Window win, XWMHints *wmhints)
95 {
96     ObDockApp *app;
97     XWindowAttributes attrib;
98     gchar **data;
99
100     app = g_new0(ObDockApp, 1);
101     app->obwin.type = Window_DockApp;
102     app->win = win;
103     app->icon_win = (wmhints->flags & IconWindowHint) ?
104         wmhints->icon_window : win;
105
106     if (PROP_GETSS(app->win, wm_class, locale, &data)) {
107         if (data[0]) {
108             app->name = g_strdup(data[0]);
109             if (data[1])
110                 app->class = g_strdup(data[1]);
111         }
112         g_strfreev(data);     
113     }
114
115     if (app->name == NULL) app->name = g_strdup("");
116     if (app->class == NULL) app->class = g_strdup("");
117     
118     if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
119         app->w = attrib.width;
120         app->h = attrib.height;
121     } else {
122         app->w = app->h = 64;
123     }
124
125     dock->dock_apps = g_list_append(dock->dock_apps, app);
126     dock_configure();
127
128     XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
129     /*
130       This is the same case as in frame.c for client windows. When Openbox is
131       starting, the window is already mapped so we see unmap events occur for
132       it. There are 2 unmap events generated that we see, one with the 'event'
133       member set the root window, and one set to the client, but both get
134       handled and need to be ignored.
135     */
136     if (ob_state() == OB_STATE_STARTING)
137         app->ignore_unmaps += 2;
138
139     if (app->win != app->icon_win) {
140         /* have to map it so that it can be re-managed on a restart */
141         XMoveWindow(ob_display, app->win, -1000, -1000);
142         XMapWindow(ob_display, app->win);
143     }
144     XMapWindow(ob_display, app->icon_win);
145     XSync(ob_display, False);
146
147     /* specify that if we exit, the window should not be destroyed and should
148        be reparented back to root automatically */
149     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
150     XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
151
152     grab_button_full(2, 0, app->icon_win,
153                      ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
154                      GrabModeAsync, OB_CURSOR_MOVE);
155
156     g_hash_table_insert(window_map, &app->icon_win, app);
157
158     ob_debug("Managed Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
159 }
160
161 void dock_remove_all()
162 {
163     while (dock->dock_apps)
164         dock_remove(dock->dock_apps->data, TRUE);
165 }
166
167 void dock_remove(ObDockApp *app, gboolean reparent)
168 {
169     ungrab_button(2, 0, app->icon_win);
170     XSelectInput(ob_display, app->icon_win, NoEventMask);
171     /* remove the window from our save set */
172     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
173     XSync(ob_display, False);
174
175     g_hash_table_remove(window_map, &app->icon_win);
176
177     if (reparent)
178         XReparentWindow(ob_display, app->icon_win,
179                         RootWindow(ob_display, ob_screen), app->x, app->y);
180
181     dock->dock_apps = g_list_remove(dock->dock_apps, app);
182     dock_configure();
183
184     ob_debug("Unmanaged Dock App: 0x%lx (%s)\n", app->icon_win, app->class);
185
186     g_free(app->name);
187     g_free(app->class);
188     g_free(app);
189 }
190
191 void dock_configure()
192 {
193     GList *it;
194     gint spot;
195     gint gravity;
196     gint minw, minh;
197     gint strw, strh;
198     Rect *a;
199
200     RrMinsize(dock->a_frame, &minw, &minh);
201
202     dock->w = dock->h = 0;
203
204     /* get the size */
205     for (it = dock->dock_apps; it; it = it->next) {
206         ObDockApp *app = it->data;
207         switch (config_dock_orient) {
208         case OB_ORIENTATION_HORZ:
209             dock->w += app->w;
210             dock->h = MAX(dock->h, app->h);
211             break;
212         case OB_ORIENTATION_VERT:
213             dock->w = MAX(dock->w, app->w);
214             dock->h += app->h;
215             break;
216         }
217     }
218
219     spot = (config_dock_orient == OB_ORIENTATION_HORZ ? minw : minh) / 2;
220
221     /* position the apps */
222     for (it = dock->dock_apps; it; it = it->next) {
223         ObDockApp *app = it->data;
224         switch (config_dock_orient) {
225         case OB_ORIENTATION_HORZ:
226             app->x = spot;
227             app->y = (dock->h - app->h) / 2;
228             spot += app->w;
229             break;
230         case OB_ORIENTATION_VERT:
231             app->x = (dock->w - app->w) / 2;
232             app->y = spot;
233             spot += app->h;
234             break;
235         }
236
237         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
238     }
239
240     /* used for calculating offsets */
241     dock->w += ob_rr_theme->bwidth * 2;
242     dock->h += ob_rr_theme->bwidth * 2;
243
244     a = screen_physical_area();
245
246     /* calculate position */
247     if (config_dock_floating) {
248         dock->x = config_dock_x;
249         dock->y = config_dock_y;
250         gravity = NorthWestGravity;
251     } else {
252         switch (config_dock_pos) {
253         case OB_DIRECTION_NORTHWEST:
254             dock->x = 0;
255             dock->y = 0;
256             gravity = NorthWestGravity;
257             break;
258         case OB_DIRECTION_NORTH:
259             dock->x = a->width / 2;
260             dock->y = 0;
261             gravity = NorthGravity;
262             break;
263         case OB_DIRECTION_NORTHEAST:
264             dock->x = a->width;
265             dock->y = 0;
266             gravity = NorthEastGravity;
267             break;
268         case OB_DIRECTION_WEST:
269             dock->x = 0;
270             dock->y = a->height / 2;
271             gravity = WestGravity;
272             break;
273         case OB_DIRECTION_EAST:
274             dock->x = a->width;
275             dock->y = a->height / 2;
276             gravity = EastGravity;
277             break;
278         case OB_DIRECTION_SOUTHWEST:
279             dock->x = 0;
280             dock->y = a->height;
281             gravity = SouthWestGravity;
282             break;
283         case OB_DIRECTION_SOUTH:
284             dock->x = a->width / 2;
285             dock->y = a->height;
286             gravity = SouthGravity;
287             break;
288         case OB_DIRECTION_SOUTHEAST:
289             dock->x = a->width;
290             dock->y = a->height;
291             gravity = SouthEastGravity;
292             break;
293         }
294     }
295
296     switch(gravity) {
297     case NorthGravity:
298     case CenterGravity:
299     case SouthGravity:
300         dock->x -= dock->w / 2;
301         break;
302     case NorthEastGravity:
303     case EastGravity:
304     case SouthEastGravity:
305         dock->x -= dock->w;
306         break;
307     }
308     switch(gravity) {
309     case WestGravity:
310     case CenterGravity:
311     case EastGravity:
312         dock->y -= dock->h / 2;
313         break;
314     case SouthWestGravity:
315     case SouthGravity:
316     case SouthEastGravity:
317         dock->y -= dock->h;
318         break;
319     }
320
321     if (config_dock_hide && dock->hidden) {
322         if (!config_dock_floating) {
323             switch (config_dock_pos) {
324             case OB_DIRECTION_NORTHWEST:
325                 switch (config_dock_orient) {
326                 case OB_ORIENTATION_HORZ:
327                     dock->y -= dock->h - ob_rr_theme->bwidth;
328                     break;
329                 case OB_ORIENTATION_VERT:
330                     dock->x -= dock->w - ob_rr_theme->bwidth;
331                     break;
332                 }
333                 break;
334             case OB_DIRECTION_NORTH:
335                 dock->y -= dock->h - ob_rr_theme->bwidth;
336                 break;
337             case OB_DIRECTION_NORTHEAST:
338                 switch (config_dock_orient) {
339                 case OB_ORIENTATION_HORZ:
340                     dock->y -= dock->h - ob_rr_theme->bwidth;
341                     break;
342                 case OB_ORIENTATION_VERT:
343                     dock->x += dock->w - ob_rr_theme->bwidth;
344                     break;
345                 }
346                 break;
347             case OB_DIRECTION_WEST:
348                 dock->x -= dock->w - ob_rr_theme->bwidth;
349                 break;
350             case OB_DIRECTION_EAST:
351                 dock->x += dock->w - ob_rr_theme->bwidth;
352                 break;
353             case OB_DIRECTION_SOUTHWEST:
354                 switch (config_dock_orient) {
355                 case OB_ORIENTATION_HORZ:
356                     dock->y += dock->h - ob_rr_theme->bwidth;
357                     break;
358                 case OB_ORIENTATION_VERT:
359                     dock->x -= dock->w - ob_rr_theme->bwidth;
360                     break;
361                 } break;
362             case OB_DIRECTION_SOUTH:
363                 dock->y += dock->h - ob_rr_theme->bwidth;
364                 break;
365             case OB_DIRECTION_SOUTHEAST:
366                 switch (config_dock_orient) {
367                 case OB_ORIENTATION_HORZ:
368                     dock->y += dock->h - ob_rr_theme->bwidth;
369                     break;
370                 case OB_ORIENTATION_VERT:
371                     dock->x += dock->w - ob_rr_theme->bwidth;
372                     break;
373                 }
374                 break;
375             }    
376         }
377     }
378
379     if (!config_dock_floating && config_dock_hide) {
380         strw = ob_rr_theme->bwidth;
381         strh = ob_rr_theme->bwidth;
382     } else {
383         strw = dock->w;
384         strh = dock->h;
385     }
386
387     /* set the strut */
388     if (!dock->dock_apps) {
389         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
390                           0, 0, 0, 0, 0, 0, 0, 0);
391     } else if (config_dock_floating) {
392         STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0,
393                           0, 0, 0, 0, 0, 0, 0, 0);
394     } else {
395         switch (config_dock_pos) {
396         case OB_DIRECTION_NORTHWEST:
397             switch (config_dock_orient) {
398             case OB_ORIENTATION_HORZ:
399                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
400                                   0, 0, dock->x, dock->x + dock->w - 1,
401                                   0, 0, 0, 0);
402                 break;
403             case OB_ORIENTATION_VERT:
404                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
405                                   dock->y, dock->y + dock->h - 1,
406                                   0, 0, 0, 0, 0, 0);
407                 break;
408             }
409             break;
410         case OB_DIRECTION_NORTH:
411             STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
412                               dock->x, dock->x + dock->w - 1,
413                               0, 0, 0, 0, 0, 0);
414             break;
415         case OB_DIRECTION_NORTHEAST:
416             switch (config_dock_orient) {
417             case OB_ORIENTATION_HORZ:
418                 STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0,
419                                   0, 0, dock->x, dock->x + dock->w -1,
420                                   0, 0, 0, 0);
421                 break;
422             case OB_ORIENTATION_VERT:
423                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
424                                   0, 0, 0, 0,
425                                   dock->y, dock->y + dock->h - 1, 0, 0);
426                 break;
427             }
428             break;
429         case OB_DIRECTION_WEST:
430             STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
431                               dock->y, dock->y + dock->h - 1,
432                               0, 0, 0, 0, 0, 0);
433             break;
434         case OB_DIRECTION_EAST:
435             STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
436                               0, 0, 0, 0,
437                               dock->y, dock->y + dock->h - 1, 0, 0);
438             break;
439         case OB_DIRECTION_SOUTHWEST:
440             switch (config_dock_orient) {
441             case OB_ORIENTATION_HORZ:
442                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
443                                   0, 0, 0, 0, 0, 0,
444                                   dock->x, dock->x + dock->w - 1);
445                 break;
446             case OB_ORIENTATION_VERT:
447                 STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0,
448                                   dock->y, dock->y + dock->h - 1,
449                                   0, 0, 0, 0, 0, 0);
450                 break;
451             }
452             break;
453         case OB_DIRECTION_SOUTH:
454             STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
455                               0, 0, 0, 0, 0, 0,
456                               dock->x, dock->x + dock->w - 1);
457             break;
458         case OB_DIRECTION_SOUTHEAST:
459             switch (config_dock_orient) {
460             case OB_ORIENTATION_HORZ:
461                 STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh,
462                                   0, 0, 0, 0, 0, 0,
463                                   dock->x, dock->x + dock->w - 1);
464                 break;
465             case OB_ORIENTATION_VERT:
466                 STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0,
467                                   0, 0, 0, 0,
468                                   dock->y, dock->y + dock->h - 1, 0, 0);
469                 break;
470             }
471             break;
472         }
473     }
474
475     dock->w += minw;
476     dock->h += minh;
477
478     /* not used for actually sizing shit */
479     dock->w -= ob_rr_theme->bwidth * 2;
480     dock->h -= ob_rr_theme->bwidth * 2;
481
482     if (dock->dock_apps) {
483         g_assert(dock->w > 0);
484         g_assert(dock->h > 0);
485
486         XMoveResizeWindow(ob_display, dock->frame,
487                           dock->x, dock->y, dock->w, dock->h);
488
489         RrPaint(dock->a_frame, dock->frame, dock->w, dock->h);
490         XMapWindow(ob_display, dock->frame);
491     } else
492         XUnmapWindow(ob_display, dock->frame);
493
494     /* but they are useful outside of this function! */
495     dock->w += ob_rr_theme->bwidth * 2;
496     dock->h += ob_rr_theme->bwidth * 2;
497
498     screen_update_areas();
499 }
500
501 void dock_app_configure(ObDockApp *app, gint w, gint h)
502 {
503     app->w = w;
504     app->h = h;
505     dock_configure();
506 }
507
508 void dock_app_drag(ObDockApp *app, XMotionEvent *e)
509 {
510     ObDockApp *over = NULL;
511     GList *it;
512     gint x, y;
513     gboolean after;
514     gboolean stop;
515
516     x = e->x_root;
517     y = e->y_root;
518
519     /* are we on top of the dock? */
520     if (!(x >= dock->x &&
521           y >= dock->y &&
522           x < dock->x + dock->w &&
523           y < dock->y + dock->h))
524         return;
525
526     x -= dock->x;
527     y -= dock->y;
528
529     /* which dock app are we on top of? */
530     stop = FALSE;
531     for (it = dock->dock_apps; it; it = it->next) {
532         over = it->data;
533         switch (config_dock_orient) {
534         case OB_ORIENTATION_HORZ:
535             if (x >= over->x && x < over->x + over->w)
536                 stop = TRUE;
537             break;
538         case OB_ORIENTATION_VERT:
539             if (y >= over->y && y < over->y + over->h)
540                 stop = TRUE;
541             break;
542         }
543         /* dont go to it->next! */
544         if (stop) break;
545     }
546     if (!it || app == over) return;
547
548     x -= over->x;
549     y -= over->y;
550
551     switch (config_dock_orient) {
552     case OB_ORIENTATION_HORZ:
553         after = (x > over->w / 2);
554         break;
555     case OB_ORIENTATION_VERT:
556         after = (y > over->h / 2);
557         break;
558     }
559
560     /* remove before doing the it->next! */
561     dock->dock_apps = g_list_remove(dock->dock_apps, app);
562
563     if (after) it = it->next;
564
565     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
566     dock_configure();
567 }
568
569 static gboolean hide_timeout(gpointer data)
570 {
571     /* hide */
572     dock->hidden = TRUE;
573     dock_configure();
574
575     return FALSE; /* don't repeat */
576 }
577
578 void dock_hide(gboolean hide)
579 {
580     if (!hide) {
581         /* show */
582         dock->hidden = FALSE;
583         dock_configure();
584
585         /* if was hiding, stop it */
586         ob_main_loop_timeout_remove(ob_main_loop, hide_timeout);
587     } else if (!dock->hidden && config_dock_hide) {
588         ob_main_loop_timeout_add(ob_main_loop, config_dock_hide_delay,
589                                  hide_timeout, NULL, NULL);
590     }
591 }