add helper functions for manipulating the focus_order list.
[mikachu/openbox.git] / openbox / frame.c
1 #include "frame.h"
2 #include "openbox.h"
3 #include "extensions.h"
4 #include "framerender.h"
5 #include "render/theme.h"
6
7 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
8 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
9                          ButtonPressMask | ButtonReleaseMask)
10 #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \
11                            ButtonMotionMask | ExposureMask)
12
13 static void layout_title(Frame *self);
14
15 void frame_startup()
16 {
17     RECT_SET(theme_a_focused_pressed_desk->area, 0, 0,
18              theme_button_size, theme_button_size);
19     RECT_SET(theme_a_focused_pressed_set_desk->area, 0, 0,
20              theme_button_size, theme_button_size);
21     RECT_SET(theme_a_focused_unpressed_desk->area, 0, 0,
22              theme_button_size, theme_button_size);
23     RECT_SET(theme_a_unfocused_pressed_desk->area, 0, 0,
24              theme_button_size, theme_button_size);
25     RECT_SET(theme_a_unfocused_pressed_set_desk->area, 0, 0,
26              theme_button_size, theme_button_size);
27     RECT_SET(theme_a_unfocused_unpressed_desk->area, 0, 0,
28              theme_button_size, theme_button_size);
29     RECT_SET(theme_a_focused_pressed_shade->area, 0, 0,
30              theme_button_size, theme_button_size);
31     RECT_SET(theme_a_focused_pressed_set_shade->area, 0, 0,
32              theme_button_size, theme_button_size);
33     RECT_SET(theme_a_focused_unpressed_shade->area, 0, 0,
34              theme_button_size, theme_button_size);
35     RECT_SET(theme_a_unfocused_pressed_shade->area, 0, 0,
36              theme_button_size, theme_button_size);
37     RECT_SET(theme_a_unfocused_pressed_set_shade->area, 0, 0,
38              theme_button_size, theme_button_size);
39     RECT_SET(theme_a_unfocused_unpressed_shade->area, 0, 0,
40              theme_button_size, theme_button_size);
41     RECT_SET(theme_a_focused_pressed_iconify->area, 0, 0,
42              theme_button_size, theme_button_size);
43     RECT_SET(theme_a_focused_unpressed_iconify->area, 0, 0,
44              theme_button_size, theme_button_size);
45     RECT_SET(theme_a_unfocused_pressed_iconify->area, 0, 0,
46              theme_button_size, theme_button_size);
47     RECT_SET(theme_a_unfocused_unpressed_iconify->area, 0, 0,
48              theme_button_size, theme_button_size);
49     RECT_SET(theme_a_unfocused_unpressed_iconify->area, 0, 0,
50              theme_button_size, theme_button_size);
51     RECT_SET(theme_a_focused_pressed_max->area, 0, 0,
52              theme_button_size, theme_button_size);
53     RECT_SET(theme_a_focused_pressed_set_max->area, 0, 0,
54              theme_button_size, theme_button_size);
55     RECT_SET(theme_a_focused_unpressed_max->area, 0, 0,
56              theme_button_size, theme_button_size);
57     RECT_SET(theme_a_unfocused_pressed_max->area, 0, 0,
58              theme_button_size, theme_button_size);
59     RECT_SET(theme_a_unfocused_pressed_set_max->area, 0, 0,
60              theme_button_size, theme_button_size);
61     RECT_SET(theme_a_unfocused_unpressed_max->area, 0, 0,
62              theme_button_size, theme_button_size);
63     RECT_SET(theme_a_focused_pressed_close->area, 0, 0,
64              theme_button_size, theme_button_size);
65     RECT_SET(theme_a_focused_unpressed_close->area, 0, 0,
66              theme_button_size, theme_button_size);
67     RECT_SET(theme_a_unfocused_pressed_close->area, 0, 0,
68              theme_button_size, theme_button_size);
69     RECT_SET(theme_a_unfocused_unpressed_close->area, 0, 0,
70              theme_button_size, theme_button_size);
71
72     RECT_SET(theme_a_focused_grip->area, 0, 0,
73              theme_grip_width, theme_handle_height);
74     RECT_SET(theme_a_unfocused_grip->area, 0, 0,
75              theme_grip_width, theme_handle_height);
76 }
77
78 void frame_shutdown()
79 {
80 }
81
82 static Window createWindow(Window parent, unsigned long mask,
83                            XSetWindowAttributes *attrib)
84 {
85     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
86                          render_depth, InputOutput, render_visual,
87                          mask, attrib);
88                        
89 }
90
91 Frame *frame_new()
92 {
93     XSetWindowAttributes attrib;
94     unsigned long mask;
95     Frame *self;
96
97     self = g_new(Frame, 1);
98
99     self->visible = FALSE;
100
101     /* create all of the decor windows */
102     mask = CWOverrideRedirect | CWEventMask;
103     attrib.event_mask = FRAME_EVENTMASK;
104     attrib.override_redirect = TRUE;
105     self->window = createWindow(ob_root, mask, &attrib);
106
107     mask = 0;
108     self->plate = createWindow(self->window, mask, &attrib);
109
110     mask = CWEventMask;
111     attrib.event_mask = ELEMENT_EVENTMASK;
112     self->title = createWindow(self->window, mask, &attrib);
113     self->label = createWindow(self->title, mask, &attrib);
114     self->max = createWindow(self->title, mask, &attrib);
115     self->close = createWindow(self->title, mask, &attrib);
116     self->desk = createWindow(self->title, mask, &attrib);
117     self->shade = createWindow(self->title, mask, &attrib);
118     self->icon = createWindow(self->title, mask, &attrib);
119     self->iconify = createWindow(self->title, mask, &attrib);
120     self->handle = createWindow(self->window, mask, &attrib);
121     mask |= CWCursor;
122     attrib.cursor = ob_cursors.bl;
123     self->lgrip = createWindow(self->handle, mask, &attrib);
124     attrib.cursor = ob_cursors.br;
125     self->rgrip = createWindow(self->handle, mask, &attrib);
126
127     self->focused = FALSE;
128
129     /* the other stuff is shown based on decor settings */
130     XMapWindow(ob_display, self->plate);
131     XMapWindow(ob_display, self->lgrip);
132     XMapWindow(ob_display, self->rgrip);
133     XMapWindow(ob_display, self->label);
134
135     /* set colors/appearance/sizes for stuff that doesn't change */
136     XSetWindowBorder(ob_display, self->window, theme_b_color->pixel);
137     XSetWindowBorder(ob_display, self->label, theme_b_color->pixel);
138     XSetWindowBorder(ob_display, self->rgrip, theme_b_color->pixel);
139     XSetWindowBorder(ob_display, self->lgrip, theme_b_color->pixel);
140
141     XResizeWindow(ob_display, self->max, theme_button_size, theme_button_size);
142     XResizeWindow(ob_display, self->iconify,
143                   theme_button_size, theme_button_size);
144     XResizeWindow(ob_display, self->icon,
145                   theme_button_size + 2, theme_button_size + 2);
146     XResizeWindow(ob_display, self->close,
147                   theme_button_size, theme_button_size);
148     XResizeWindow(ob_display, self->desk,
149                   theme_button_size, theme_button_size);
150     XResizeWindow(ob_display, self->shade,
151                   theme_button_size, theme_button_size);
152     XResizeWindow(ob_display, self->lgrip,
153                   theme_grip_width, theme_handle_height);
154     XResizeWindow(ob_display, self->rgrip,
155                   theme_grip_width, theme_handle_height);
156
157     /* set up the dynamic appearances */
158     self->a_unfocused_title = appearance_copy(theme_a_unfocused_title);
159     self->a_focused_title = appearance_copy(theme_a_focused_title);
160     self->a_unfocused_label = appearance_copy(theme_a_unfocused_label);
161     self->a_focused_label = appearance_copy(theme_a_focused_label);
162     self->a_unfocused_handle = appearance_copy(theme_a_unfocused_handle);
163     self->a_focused_handle = appearance_copy(theme_a_focused_handle);
164     self->a_icon = appearance_copy(theme_a_icon);
165
166     self->max_press = self->close_press = self->desk_press = 
167         self->iconify_press = self->shade_press = FALSE;
168
169     return (Frame*)self;
170 }
171
172 static void frame_free(Frame *self)
173 {
174     appearance_free(self->a_unfocused_title); 
175     appearance_free(self->a_focused_title);
176     appearance_free(self->a_unfocused_label);
177     appearance_free(self->a_focused_label);
178     appearance_free(self->a_unfocused_handle);
179     appearance_free(self->a_focused_handle);
180     appearance_free(self->a_icon);
181
182     XDestroyWindow(ob_display, self->window);
183
184     g_free(self);
185 }
186
187 void frame_show(Frame *self)
188 {
189     if (!self->visible) {
190         self->visible = TRUE;
191         XMapWindow(ob_display, self->window);
192     }
193 }
194
195 void frame_hide(Frame *self)
196 {
197     if (self->visible) {
198         self->visible = FALSE;
199         self->client->ignore_unmaps++;
200         XUnmapWindow(ob_display, self->window);
201     }
202 }
203
204 void frame_adjust_shape(Frame *self)
205 {
206 #ifdef SHAPE
207     int num;
208     XRectangle xrect[2];
209
210     if (!self->client->shaped) {
211         /* clear the shape on the frame window */
212         XShapeCombineMask(ob_display, self->window, ShapeBounding,
213                           self->innersize.left,
214                           self->innersize.top,
215                           None, ShapeSet);
216     } else {
217         /* make the frame's shape match the clients */
218         XShapeCombineShape(ob_display, self->window, ShapeBounding,
219                            self->innersize.left,
220                            self->innersize.top,
221                            self->client->window,
222                            ShapeBounding, ShapeSet);
223
224         num = 0;
225         if (self->client->decorations & Decor_Titlebar) {
226             xrect[0].x = -theme_bevel;
227             xrect[0].y = -theme_bevel;
228             xrect[0].width = self->width + self->bwidth * 2;
229             xrect[0].height = theme_title_height +
230                 self->bwidth * 2;
231             ++num;
232         }
233
234         if (self->client->decorations & Decor_Handle) {
235             xrect[1].x = -theme_bevel;
236             xrect[1].y = FRAME_HANDLE_Y(self);
237             xrect[1].width = self->width + self->bwidth * 2;
238             xrect[1].height = theme_handle_height +
239                 self->bwidth * 2;
240             ++num;
241         }
242
243         XShapeCombineRectangles(ob_display, self->window,
244                                 ShapeBounding, 0, 0, xrect, num,
245                                 ShapeUnion, Unsorted);
246     }
247 #endif
248 }
249
250 void frame_adjust_area(Frame *self, gboolean moved, gboolean resized)
251 {
252     if (resized) {
253         if (self->client->decorations & Decor_Border) {
254             self->bwidth = theme_bwidth;
255             self->cbwidth = theme_cbwidth;
256         } else {
257             self->bwidth = self->cbwidth = 0;
258         }
259         STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
260                   self->cbwidth, self->cbwidth);
261         self->width = self->client->area.width + self->cbwidth * 2;
262         g_assert(self->width > 0);
263
264         /* set border widths */
265         XSetWindowBorderWidth(ob_display, self->plate,  self->cbwidth);
266         XSetWindowBorderWidth(ob_display, self->window, self->bwidth);
267         XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
268         XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
269         XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
270         XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
271   
272         /* position/size and map/unmap all the windows */
273
274         /* they all default off, they're turned on in layout_title */
275         self->icon_x = -1;
276         self->desk_x = -1;
277         self->shade_x = -1;
278         self->iconify_x = -1;
279         self->label_x = -1;
280         self->max_x = -1;
281         self->close_x = -1;
282
283         if (self->client->decorations & Decor_Titlebar) {
284             XMoveResizeWindow(ob_display, self->title,
285                               -self->bwidth, -self->bwidth,
286                               self->width, theme_title_height);
287             self->innersize.top += theme_title_height + self->bwidth;
288             XMapWindow(ob_display, self->title);
289
290             RECT_SET(self->a_focused_title->area, 0, 0,
291                      self->width, theme_title_height);
292             RECT_SET(self->a_unfocused_title->area, 0, 0,
293                      self->width, theme_title_height);
294
295             /* layout the title bar elements */
296             layout_title(self);
297         } else
298             XUnmapWindow(ob_display, self->title);
299
300         if (self->client->decorations & Decor_Handle) {
301             XMoveResizeWindow(ob_display, self->handle,
302                               -self->bwidth, FRAME_HANDLE_Y(self),
303                               self->width, theme_handle_height);
304             XMoveWindow(ob_display, self->lgrip,
305                         -self->bwidth, -self->bwidth);
306             XMoveWindow(ob_display, self->rgrip,
307                         -self->bwidth + self->width -
308                         theme_grip_width, -self->bwidth);
309             self->innersize.bottom += theme_handle_height +
310                 self->bwidth;
311             XMapWindow(ob_display, self->handle);
312
313             if (theme_a_focused_grip->surface.data.planar.grad ==
314                 Background_ParentRelative)
315                 RECT_SET(self->a_focused_handle->area, 0, 0,
316                          self->width, theme_handle_height);
317             else
318                 RECT_SET(self->a_focused_handle->area,
319                          theme_grip_width + self->bwidth, 0,
320                          self->width - (theme_grip_width + self->bwidth) * 2,
321                          theme_handle_height);
322             if (theme_a_unfocused_grip->surface.data.planar.grad ==
323                 Background_ParentRelative)
324                 RECT_SET(self->a_unfocused_handle->area, 0, 0,
325                          self->width, theme_handle_height);
326             else
327                 RECT_SET(self->a_unfocused_handle->area,
328                          theme_grip_width + self->bwidth, 0,
329                          self->width - (theme_grip_width + self->bwidth) * 2,
330                          theme_handle_height);
331
332         } else
333             XUnmapWindow(ob_display, self->handle);
334     }
335
336     if (resized) {
337         /* move and resize the plate */
338         XMoveResizeWindow(ob_display, self->plate,
339                           self->innersize.left - self->cbwidth,
340                           self->innersize.top - self->cbwidth,
341                           self->client->area.width,
342                           self->client->area.height);
343         /* when the client has StaticGravity, it likes to move around. */
344         XMoveWindow(ob_display, self->client->window, 0, 0);
345     }
346
347     if (resized) {
348         STRUT_SET(self->size,
349                   self->innersize.left + self->bwidth,
350                   self->innersize.top + self->bwidth,
351                   self->innersize.right + self->bwidth,
352                   self->innersize.bottom + self->bwidth);
353     }
354
355     /* shading can change without being moved or resized */
356     RECT_SET_SIZE(self->area,
357                   self->client->area.width +
358                   self->size.left + self->size.right,
359                   (self->client->shaded ? theme_title_height + self->bwidth*2:
360                    self->client->area.height +
361                    self->size.top + self->size.bottom));
362
363     if (moved) {
364         /* find the new coordinates, done after setting the frame.size, for
365            frame_client_gravity. */
366         self->area.x = self->client->area.x;
367         self->area.y = self->client->area.y;
368         frame_client_gravity((Frame*)self,
369                              &self->area.x, &self->area.y);
370     }
371
372     /* move and resize the top level frame.
373        shading can change without being moved or resized */
374     XMoveResizeWindow(ob_display, self->window,
375                       self->area.x, self->area.y,
376                       self->width,
377                       self->area.height - self->bwidth * 2);
378
379     if (resized) {
380         framerender_frame(self);
381
382         frame_adjust_shape(self);
383     }
384 }
385
386 void frame_adjust_state(Frame *self)
387 {
388     framerender_frame(self);
389 }
390
391 void frame_adjust_focus(Frame *self, gboolean hilite)
392 {
393     self->focused = hilite;
394     framerender_frame(self);
395 }
396
397 void frame_adjust_title(Frame *self)
398 {
399     framerender_frame(self);
400 }
401
402 void frame_adjust_icon(Frame *self)
403 {
404     framerender_frame(self);
405 }
406
407 void frame_grab_client(Frame *self, Client *client)
408 {
409     self->client = client;
410
411     /* reparent the client to the frame */
412     XReparentWindow(ob_display, client->window, self->plate, 0, 0);
413     /*
414       When reparenting the client window, it is usually not mapped yet, since
415       this occurs from a MapRequest. However, in the case where Openbox is
416       starting up, the window is already mapped, so we'll see unmap events for
417       it. There are 2 unmap events generated that we see, one with the 'event'
418       member set the root window, and one set to the client, but both get
419       handled and need to be ignored.
420     */
421     if (ob_state == State_Starting)
422         client->ignore_unmaps += 2;
423
424     /* select the event mask on the client's parent (to receive config/map
425        req's) the ButtonPress is to catch clicks on the client border */
426     XSelectInput(ob_display, self->plate, PLATE_EVENTMASK);
427
428     /* map the client so it maps when the frame does */
429     XMapWindow(ob_display, client->window);
430
431     frame_adjust_area(self, TRUE, TRUE);
432
433     /* set all the windows for the frame in the client_map */
434     g_hash_table_insert(client_map, &self->window, client);
435     g_hash_table_insert(client_map, &self->plate, client);
436     g_hash_table_insert(client_map, &self->title, client);
437     g_hash_table_insert(client_map, &self->label, client);
438     g_hash_table_insert(client_map, &self->max, client);
439     g_hash_table_insert(client_map, &self->close, client);
440     g_hash_table_insert(client_map, &self->desk, client);
441     g_hash_table_insert(client_map, &self->shade, client);
442     g_hash_table_insert(client_map, &self->icon, client);
443     g_hash_table_insert(client_map, &self->iconify, client);
444     g_hash_table_insert(client_map, &self->handle, client);
445     g_hash_table_insert(client_map, &self->lgrip, client);
446     g_hash_table_insert(client_map, &self->rgrip, client);
447 }
448
449 void frame_release_client(Frame *self, Client *client)
450 {
451     XEvent ev;
452
453     g_assert(self->client == client);
454
455     /* check if the app has already reparented its window away */
456     if (XCheckTypedWindowEvent(ob_display, client->window,
457                                ReparentNotify, &ev)) {
458         XPutBackEvent(ob_display, &ev);
459         /* re-map the window since the unmanaging process unmaps it */
460         XMapWindow(ob_display, client->window);
461     } else {
462         /* according to the ICCCM - if the client doesn't reparent itself,
463            then we will reparent the window to root for them */
464         XReparentWindow(ob_display, client->window, ob_root,
465                         client->area.x,
466                         client->area.y);
467     }
468
469     /* remove all the windows for the frame from the client_map */
470     g_hash_table_remove(client_map, &self->window);
471     g_hash_table_remove(client_map, &self->plate);
472     g_hash_table_remove(client_map, &self->title);
473     g_hash_table_remove(client_map, &self->label);
474     g_hash_table_remove(client_map, &self->max);
475     g_hash_table_remove(client_map, &self->close);
476     g_hash_table_remove(client_map, &self->desk);
477     g_hash_table_remove(client_map, &self->shade);
478     g_hash_table_remove(client_map, &self->icon);
479     g_hash_table_remove(client_map, &self->iconify);
480     g_hash_table_remove(client_map, &self->handle);
481     g_hash_table_remove(client_map, &self->lgrip);
482     g_hash_table_remove(client_map, &self->rgrip);
483
484     frame_free(self);
485 }
486
487 static void layout_title(Frame *self)
488 {
489     char *lc;
490     int x;
491     gboolean n, d, i, l, m, c, s;
492
493     n = d = i = l = m = c = s = FALSE;
494
495     /* figure out whats being shown, and the width of the label */
496     self->label_width = self->width - (theme_bevel + 1) * 2;
497     for (lc = theme_title_layout; *lc != '\0'; ++lc) {
498         switch (*lc) {
499         case 'N':
500             if (!(self->client->decorations & Decor_Icon)) break;
501             if (n) { *lc = ' '; break; } /* rm duplicates */
502             n = TRUE;
503             self->label_width -= theme_button_size + 2 + theme_bevel + 1;
504             break;
505         case 'D':
506             if (!(self->client->decorations & Decor_AllDesktops)) break;
507             if (d) { *lc = ' '; break; } /* rm duplicates */
508             d = TRUE;
509             self->label_width -= theme_button_size + theme_bevel + 1;
510             break;
511         case 'S':
512             if (!(self->client->decorations & Decor_Shade)) break;
513             if (s) { *lc = ' '; break; } /* rm duplicates */
514             s = TRUE;
515             self->label_width -= theme_button_size + theme_bevel + 1;
516             break;
517         case 'I':
518             if (!(self->client->decorations & Decor_Iconify)) break;
519             if (i) { *lc = ' '; break; } /* rm duplicates */
520             i = TRUE;
521             self->label_width -= theme_button_size + theme_bevel + 1;
522             break;
523         case 'L':
524             if (l) { *lc = ' '; break; } /* rm duplicates */
525             l = TRUE;
526             break;
527         case 'M':
528             if (!(self->client->decorations & Decor_Maximize)) break;
529             if (m) { *lc = ' '; break; } /* rm duplicates */
530             m = TRUE;
531             self->label_width -= theme_button_size + theme_bevel + 1;
532             break;
533         case 'C':
534             if (!(self->client->decorations & Decor_Close)) break;
535             if (c) { *lc = ' '; break; } /* rm duplicates */
536             c = TRUE;
537             self->label_width -= theme_button_size + theme_bevel + 1;
538             break;
539         }
540     }
541     if (self->label_width < 1) self->label_width = 1;
542
543     XResizeWindow(ob_display, self->label, self->label_width,
544                   theme_label_height);
545   
546     if (!n) XUnmapWindow(ob_display, self->icon);
547     if (!d) XUnmapWindow(ob_display, self->desk);
548     if (!s) XUnmapWindow(ob_display, self->shade);
549     if (!i) XUnmapWindow(ob_display, self->iconify);
550     if (!l) XUnmapWindow(ob_display, self->label);
551     if (!m) XUnmapWindow(ob_display, self->max);
552     if (!c) XUnmapWindow(ob_display, self->close);
553
554     x = theme_bevel + 1;
555     for (lc = theme_title_layout; *lc != '\0'; ++lc) {
556         switch (*lc) {
557         case 'N':
558             if (!n) break;
559             self->icon_x = x;
560             RECT_SET(self->a_icon->area, 0, 0,
561                      theme_button_size + 2, theme_button_size + 2);
562             XMapWindow(ob_display, self->icon);
563             XMoveWindow(ob_display, self->icon, x, theme_bevel);
564             x += theme_button_size + 2 + theme_bevel + 1;
565             break;
566         case 'D':
567             if (!d) break;
568             self->desk_x = x;
569             XMapWindow(ob_display, self->desk);
570             XMoveWindow(ob_display, self->desk, x, theme_bevel + 1);
571             x += theme_button_size + theme_bevel + 1;
572             break;
573         case 'S':
574             if (!s) break;
575             self->shade_x = x;
576             XMapWindow(ob_display, self->shade);
577             XMoveWindow(ob_display, self->shade, x, theme_bevel + 1);
578             x += theme_button_size + theme_bevel + 1;
579             break;
580         case 'I':
581             if (!i) break;
582             self->iconify_x = x;
583             XMapWindow(ob_display, self->iconify);
584             XMoveWindow(ob_display, self->iconify, x, theme_bevel + 1);
585             x += theme_button_size + theme_bevel + 1;
586             break;
587         case 'L':
588             if (!l) break;
589             self->label_x = x;
590             XMapWindow(ob_display, self->label);
591             XMoveWindow(ob_display, self->label, x, theme_bevel);
592             x += self->label_width + theme_bevel + 1;
593             break;
594         case 'M':
595             if (!m) break;
596             self->max_x = x;
597             XMapWindow(ob_display, self->max);
598             XMoveWindow(ob_display, self->max, x, theme_bevel + 1);
599             x += theme_button_size + theme_bevel + 1;
600             break;
601         case 'C':
602             if (!c) break;
603             self->close_x = x;
604             XMapWindow(ob_display, self->close);
605             XMoveWindow(ob_display, self->close, x, theme_bevel + 1);
606             x += theme_button_size + theme_bevel + 1;
607             break;
608         }
609     }
610
611     RECT_SET(self->a_focused_label->area, 0, 0,
612              self->label_width, theme_label_height);
613     RECT_SET(self->a_unfocused_label->area, 0, 0,
614              self->label_width, theme_label_height);
615 }
616
617 Context frame_context_from_string(char *name)
618 {
619     if (!g_ascii_strcasecmp("root", name))
620         return Context_Root;
621     else if (!g_ascii_strcasecmp("client", name))
622         return Context_Client;
623     else if (!g_ascii_strcasecmp("titlebar", name))
624         return Context_Titlebar;
625     else if (!g_ascii_strcasecmp("handle", name))
626         return Context_Handle;
627     else if (!g_ascii_strcasecmp("frame", name))
628         return Context_Frame;
629     else if (!g_ascii_strcasecmp("blcorner", name))
630         return Context_BLCorner;
631     else if (!g_ascii_strcasecmp("tlcorner", name))
632         return Context_TLCorner;
633     else if (!g_ascii_strcasecmp("brcorner", name))
634         return Context_BRCorner;
635     else if (!g_ascii_strcasecmp("trcorner", name))
636         return Context_TRCorner;
637     else if (!g_ascii_strcasecmp("maximize", name))
638         return Context_Maximize;
639     else if (!g_ascii_strcasecmp("alldesktops", name))
640         return Context_AllDesktops;
641     else if (!g_ascii_strcasecmp("shade", name))
642         return Context_Shade;
643     else if (!g_ascii_strcasecmp("iconify", name))
644         return Context_Iconify;
645     else if (!g_ascii_strcasecmp("icon", name))
646         return Context_Icon;
647     else if (!g_ascii_strcasecmp("close", name))
648         return Context_Close;
649     return Context_None;
650 }
651
652 Context frame_context(Client *client, Window win)
653 {
654     Frame *self;
655
656     if (win == ob_root) return Context_Root;
657     if (client == NULL) return Context_None;
658     if (win == client->window) return Context_Client;
659
660     self = client->frame;
661     if (win == self->window) return Context_Frame;
662     if (win == self->plate)  return Context_Client;
663     if (win == self->title)  return Context_Titlebar;
664     if (win == self->label)  return Context_Titlebar;
665     if (win == self->handle) return Context_Handle;
666     if (win == self->lgrip)  return Context_BLCorner;
667     if (win == self->rgrip)  return Context_BRCorner;
668     if (win == self->max)    return Context_Maximize;
669     if (win == self->iconify)return Context_Iconify;
670     if (win == self->close)  return Context_Close;
671     if (win == self->icon)   return Context_Icon;
672     if (win == self->desk)   return Context_AllDesktops;
673     if (win == self->shade)  return Context_Shade;
674
675     return Context_None;
676 }
677
678 void frame_client_gravity(Frame *self, int *x, int *y)
679 {
680     /* horizontal */
681     switch (self->client->gravity) {
682     default:
683     case NorthWestGravity:
684     case SouthWestGravity:
685     case WestGravity:
686         break;
687
688     case NorthGravity:
689     case SouthGravity:
690     case CenterGravity:
691         *x -= (self->size.left + self->size.right) / 2;
692         break;
693
694     case NorthEastGravity:
695     case SouthEastGravity:
696     case EastGravity:
697         *x -= self->size.left + self->size.right;
698         break;
699
700     case ForgetGravity:
701     case StaticGravity:
702         *x -= self->size.left;
703         break;
704     }
705
706     /* vertical */
707     switch (self->client->gravity) {
708     default:
709     case NorthWestGravity:
710     case NorthEastGravity:
711     case NorthGravity:
712         break;
713
714     case CenterGravity:
715     case EastGravity:
716     case WestGravity:
717         *y -= (self->size.top + self->size.bottom) / 2;
718         break;
719
720     case SouthWestGravity:
721     case SouthEastGravity:
722     case SouthGravity:
723         *y -= self->size.top + self->size.bottom;
724         break;
725
726     case ForgetGravity:
727     case StaticGravity:
728         *y -= self->size.top;
729         break;
730     }
731 }
732
733 void frame_frame_gravity(Frame *self, int *x, int *y)
734 {
735     /* horizontal */
736     switch (self->client->gravity) {
737     default:
738     case NorthWestGravity:
739     case WestGravity:
740     case SouthWestGravity:
741         break;
742     case NorthGravity:
743     case CenterGravity:
744     case SouthGravity:
745         *x += (self->size.left + self->size.right) / 2;
746         break;
747     case NorthEastGravity:
748     case EastGravity:
749     case SouthEastGravity:
750         *x += self->size.left + self->size.right;
751         break;
752     case StaticGravity:
753     case ForgetGravity:
754         *x += self->size.left;
755         break;
756     }
757
758     /* vertical */
759     switch (self->client->gravity) {
760     default:
761     case NorthWestGravity:
762     case WestGravity:
763     case SouthWestGravity:
764         break;
765     case NorthGravity:
766     case CenterGravity:
767     case SouthGravity:
768         *y += (self->size.top + self->size.bottom) / 2;
769         break;
770     case NorthEastGravity:
771     case EastGravity:
772     case SouthEastGravity:
773         *y += self->size.top + self->size.bottom;
774         break;
775     case StaticGravity:
776     case ForgetGravity:
777         *y += self->size.top;
778         break;
779     }
780 }