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