dockapps are ObWindows now.
[mikachu/openbox.git] / openbox / dock.c
1 #include "dock.h"
2 #include "screen.h"
3 #include "config.h"
4 #include "grab.h"
5 #include "openbox.h"
6 #include "render/theme.h"
7
8 #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \
9                          EnterWindowMask | LeaveWindowMask)
10 #define DOCKAPP_EVENT_MASK (StructureNotifyMask)
11
12 static Dock *dock;
13
14 void dock_startup()
15 {
16     XSetWindowAttributes attrib;
17     int i;
18
19     dock = g_new0(struct Dock, 1);
20     dock->obwin.type = Window_Dock;
21
22     dock->hidden = TRUE;
23
24     attrib.event_mask = DOCK_EVENT_MASK;
25     attrib.override_redirect = True;
26     dock->frame = XCreateWindow(ob_display, ob_root, 0, 0, 1, 1, 0,
27                                 render_depth, InputOutput, render_visual,
28                                 CWOverrideRedirect | CWEventMask,
29                                 &attrib);
30     dock->a_frame = appearance_copy(theme_a_unfocused_title);
31     XSetWindowBorder(ob_display, dock->frame, theme_b_color->pixel);
32     XSetWindowBorderWidth(ob_display, dock->frame, theme_bwidth);
33
34     g_hash_table_insert(window_map, &dock->frame, dock);
35     stacking_add(DOCK_AS_WINDOW(&dock[i]));
36     stacking_raise(DOCK_AS_WINDOW(&dock[i]));
37 }
38
39 void dock_shutdown()
40 {
41     XDestroyWindow(ob_display, dock->frame);
42     appearance_free(dock->a_frame);
43     g_hash_table_remove(window_map, &dock->frame);
44     stacking_remove(dock);
45 }
46
47 void dock_add(Window win, XWMHints *wmhints)
48 {
49     DockApp *app;
50     XWindowAttributes attrib;
51
52     app = g_new0(DockApp, 1);
53     app->obwin.type = Window_DockApp;
54     app->win = win;
55     app->icon_win = (wmhints->flags & IconWindowHint) ?
56         wmhints->icon_window : win;
57     
58     if (XGetWindowAttributes(ob_display, app->icon_win, &attrib)) {
59         app->w = attrib.width;
60         app->h = attrib.height;
61     } else {
62         app->w = app->h = 64;
63     }
64
65     dock->dock_apps = g_list_append(dock->dock_apps, app);
66     dock_configure();
67
68     XReparentWindow(ob_display, app->icon_win, dock->frame, app->x, app->y);
69     /*
70       This is the same case as in frame.c for client windows. When Openbox is
71       starting, the window is already mapped so we see unmap events occur for
72       it. There are 2 unmap events generated that we see, one with the 'event'
73       member set the root window, and one set to the client, but both get
74       handled and need to be ignored.
75     */
76     if (ob_state == State_Starting)
77         app->ignore_unmaps += 2;
78
79     if (app->win != app->icon_win) {
80         /* have to map it so that it can be re-managed on a restart */
81         XMoveWindow(ob_display, app->win, -1000, -1000);
82         XMapWindow(ob_display, app->win);
83     }
84     XMapWindow(ob_display, app->icon_win);
85     XSync(ob_display, False);
86
87     /* specify that if we exit, the window should not be destroyed and should
88        be reparented back to root automatically */
89     XChangeSaveSet(ob_display, app->icon_win, SetModeInsert);
90     XSelectInput(ob_display, app->icon_win, DOCKAPP_EVENT_MASK);
91
92     grab_button_full(2, 0, app->icon_win,
93                      ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
94                      GrabModeAsync, ob_cursors.move);
95
96     g_hash_table_insert(window_map, &app->icon_win, app);
97
98     g_message("Managed Dock App: 0x%lx", app->icon_win);
99 }
100
101 void dock_remove_all()
102 {
103     while (dock->dock_apps)
104         dock_remove(dock->dock_apps->data, TRUE);
105 }
106
107 void dock_remove(DockApp *app, gboolean reparent)
108 {
109     ungrab_button(2, 0, app->icon_win);
110     XSelectInput(ob_display, app->icon_win, NoEventMask);
111     /* remove the window from our save set */
112     XChangeSaveSet(ob_display, app->icon_win, SetModeDelete);
113     XSync(ob_display, False);
114
115     g_hash_table_remove(window_map, &app->icon_win);
116
117     if (reparent)
118         XReparentWindow(ob_display, app->icon_win, ob_root, app->x, app->y);
119
120     dock->dock_apps = g_list_remove(dock->dock_apps, app);
121     dock_configure();
122
123     g_message("Unmanaged Dock App: 0x%lx", app->icon_win);
124
125     g_free(app);
126 }
127
128 void dock_configure()
129 {
130     GList *it;
131     int spot;
132     int gravity;
133
134     dock->w = dock->h = spot = 0;
135
136     for (it = dock->dock_apps; it; it = it->next) {
137         struct DockApp *app = it->data;
138         if (config_dock_horz) {
139             app->x = spot;
140             app->y = 0;
141             dock->w += app->w;
142             dock->h = MAX(dock->h, app->h);
143             spot += app->w;
144         } else {
145             app->x = 0;
146             app->y = spot;
147             dock->w = MAX(dock->w, app->w);
148             dock->h += app->h;
149             spot += app->h;
150         }
151
152         XMoveWindow(ob_display, app->icon_win, app->x, app->y);
153     }
154
155     /* used for calculating offsets */
156     dock->w += theme_bwidth * 2;
157     dock->h += theme_bwidth * 2;
158
159     /* calculate position */
160     switch (config_dock_pos) {
161     case DockPos_Floating:
162         dock->x = config_dock_x;
163         dock->y = config_dock_y;
164         gravity = NorthWestGravity;
165         break;
166     case DockPos_TopLeft:
167         dock->x = 0;
168         dock->y = 0;
169         gravity = NorthWestGravity;
170         break;
171     case DockPos_Top:
172         dock->x = screen_physical_size.width / 2;
173         dock->y = 0;
174         gravity = NorthGravity;
175         break;
176     case DockPos_TopRight:
177         dock->x = screen_physical_size.width;
178         dock->y = 0;
179         gravity = NorthEastGravity;
180         break;
181     case DockPos_Left:
182         dock->x = 0;
183         dock->y = screen_physical_size.height / 2;
184         gravity = WestGravity;
185         break;
186     case DockPos_Right:
187         dock->x = screen_physical_size.width;
188         dock->y = screen_physical_size.height / 2;
189         gravity = EastGravity;
190         break;
191     case DockPos_BottomLeft:
192         dock->x = 0;
193         dock->y = screen_physical_size.height;
194         gravity = SouthWestGravity;
195         break;
196     case DockPos_Bottom:
197         dock->x = screen_physical_size.width / 2;
198         dock->y = screen_physical_size.height;
199         gravity = SouthGravity;
200         break;
201     case DockPos_BottomRight:
202         dock->x = screen_physical_size.width;
203         dock->y = screen_physical_size.height;
204         gravity = SouthEastGravity;
205         break;
206     }
207
208     switch(gravity) {
209     case NorthGravity:
210     case CenterGravity:
211     case SouthGravity:
212         dock->x -= dock->w / 2;
213         break;
214     case NorthEastGravity:
215     case EastGravity:
216     case SouthEastGravity:
217         dock->x -= dock->w;
218         break;
219     }
220     switch(gravity) {
221     case WestGravity:
222     case CenterGravity:
223     case EastGravity:
224         dock->y -= dock->h / 2;
225         break;
226     case SouthWestGravity:
227     case SouthGravity:
228     case SouthEastGravity:
229         dock->y -= dock->h;
230         break;
231     }
232
233     if (config_dock_hide && dock->hidden) {
234         switch (config_dock_pos) {
235         case DockPos_Floating:
236             break;
237         case DockPos_TopLeft:
238             if (config_dock_horz)
239                 dock->y -= dock->h - theme_bwidth;
240             else
241                 dock->x -= dock->w - theme_bwidth;
242             break;
243         case DockPos_Top:
244             dock->y -= dock->h - theme_bwidth;
245             break;
246         case DockPos_TopRight:
247             if (config_dock_horz)
248                 dock->y -= dock->h - theme_bwidth;
249             else
250                 dock->x += dock->w - theme_bwidth;
251             break;
252         case DockPos_Left:
253             dock->x -= dock->w - theme_bwidth;
254             break;
255         case DockPos_Right:
256             dock->x += dock->w - theme_bwidth;
257             break;
258         case DockPos_BottomLeft:
259             if (config_dock_horz)
260                 dock->y += dock->h - theme_bwidth;
261             else
262                 dock->x -= dock->w - theme_bwidth;
263             break;
264         case DockPos_Bottom:
265             dock->y += dock->h - theme_bwidth;
266             break;
267         case DockPos_BottomRight:
268             if (config_dock_horz)
269                 dock->y += dock->h - theme_bwidth;
270             else
271                 dock->x += dock->w - theme_bwidth;
272             break;
273         }    
274     }
275
276     /* not used for actually sizing shit */
277     dock->w -= theme_bwidth * 2;
278     dock->h -= theme_bwidth * 2;
279
280     if (dock->w > 0 && dock->h > 0) {
281         RECT_SET(dock->a_frame->area, 0, 0, dock->w, dock->h);
282         XMoveResizeWindow(ob_display, dock->frame,
283                           dock->x, dock->y, dock->w, dock->h);
284
285         paint(dock->frame, dock->a_frame);
286         XMapWindow(ob_display, dock->frame);
287     } else
288         XUnmapWindow(ob_display, dock->frame);
289
290     /* but they are useful outside of this function! */
291     dock->w += theme_bwidth * 2;
292     dock->h += theme_bwidth * 2;
293 }
294
295 void dock_app_configure(DockApp *app, int w, int h)
296 {
297     app->w = w;
298     app->h = h;
299     dock_configure();
300 }
301
302 void dock_app_drag(DockApp *app, XMotionEvent *e)
303 {
304     DockApp *over = NULL;
305     GList *it;
306     int x, y;
307     gboolean after;
308
309     x = e->x_root;
310     y = e->y_root;
311
312     /* are we on top of the dock? */
313     if (!(x >= dock->x &&
314           y >= dock->y &&
315           x < dock->x + dock->w &&
316           y < dock->y + dock->h))
317         return;
318
319     x -= dock->x;
320     y -= dock->y;
321
322     /* which dock app are we on top of? */
323     for (it = dock->dock_apps; it; it = it->next) {
324         over = it->data;
325         if (config_dock_horz) {
326             if (x >= over->x && x < over->x + over->w)
327                 break;
328         } else {
329             if (y >= over->y && y < over->y + over->h)
330                 break;
331         }
332     }
333     if (!it || app == over) return;
334
335     x -= over->x;
336     y -= over->y;
337
338     if (config_dock_horz)
339         after = (x > over->w / 2);
340     else
341         after = (y > over->h / 2);
342
343     /* remove before doing the it->next! */
344     dock->dock_apps = g_list_remove(dock->dock_apps, app);
345
346     if (after) it = it->next;
347
348     dock->dock_apps = g_list_insert_before(dock->dock_apps, it, app);
349     dock_configure();
350 }
351
352 static void hide_timeout(void *n)
353 {
354     /* dont repeat */
355     timer_stop(dock->hide_timer);
356     dock->hide_timer = NULL;
357
358     /* hide */
359     dock->hidden = TRUE;
360     dock_configure();
361 }
362
363 void dock_hide(gboolean hide)
364 {
365     if (dock->hidden == hide || !config_dock_hide)
366         return;
367     if (!hide) {
368         /* show */
369         dock->hidden = FALSE;
370         dock_configure();
371
372         /* if was hiding, stop it */
373         if (dock->hide_timer) {
374             timer_stop(dock->hide_timer);
375             dock->hide_timer = NULL;
376         }
377     } else {
378         g_assert(!dock->hide_timer);
379         dock->hide_timer = timer_start(config_dock_hide_timeout * 1000,
380                                        (TimeoutHandler)hide_timeout,
381                                        NULL);
382     }
383 }