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