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