remove non-planar surfaces
[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.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.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 window_map */
434     g_hash_table_insert(window_map, &self->window, client);
435     g_hash_table_insert(window_map, &self->plate, client);
436     g_hash_table_insert(window_map, &self->title, client);
437     g_hash_table_insert(window_map, &self->label, client);
438     g_hash_table_insert(window_map, &self->max, client);
439     g_hash_table_insert(window_map, &self->close, client);
440     g_hash_table_insert(window_map, &self->desk, client);
441     g_hash_table_insert(window_map, &self->shade, client);
442     g_hash_table_insert(window_map, &self->icon, client);
443     g_hash_table_insert(window_map, &self->iconify, client);
444     g_hash_table_insert(window_map, &self->handle, client);
445     g_hash_table_insert(window_map, &self->lgrip, client);
446     g_hash_table_insert(window_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
460         /* re-map the window since the unmanaging process unmaps it */
461
462         /* XXX ... um no it doesnt it unmaps its parent, the window itself
463            retains its mapped state, no?! XXX
464            XMapWindow(ob_display, client->window); */
465     } else {
466         /* according to the ICCCM - if the client doesn't reparent itself,
467            then we will reparent the window to root for them */
468         XReparentWindow(ob_display, client->window, ob_root,
469                         client->area.x,
470                         client->area.y);
471     }
472
473     /* remove all the windows for the frame from the window_map */
474     g_hash_table_remove(window_map, &self->window);
475     g_hash_table_remove(window_map, &self->plate);
476     g_hash_table_remove(window_map, &self->title);
477     g_hash_table_remove(window_map, &self->label);
478     g_hash_table_remove(window_map, &self->max);
479     g_hash_table_remove(window_map, &self->close);
480     g_hash_table_remove(window_map, &self->desk);
481     g_hash_table_remove(window_map, &self->shade);
482     g_hash_table_remove(window_map, &self->icon);
483     g_hash_table_remove(window_map, &self->iconify);
484     g_hash_table_remove(window_map, &self->handle);
485     g_hash_table_remove(window_map, &self->lgrip);
486     g_hash_table_remove(window_map, &self->rgrip);
487
488     frame_free(self);
489 }
490
491 static void layout_title(Frame *self)
492 {
493     char *lc;
494     int x;
495     gboolean n, d, i, l, m, c, s;
496
497     n = d = i = l = m = c = s = FALSE;
498
499     /* figure out whats being shown, and the width of the label */
500     self->label_width = self->width - (theme_bevel + 1) * 2;
501     for (lc = theme_title_layout; *lc != '\0'; ++lc) {
502         switch (*lc) {
503         case 'N':
504             if (!(self->client->decorations & Decor_Icon)) break;
505             if (n) { *lc = ' '; break; } /* rm duplicates */
506             n = TRUE;
507             self->label_width -= theme_button_size + 2 + theme_bevel + 1;
508             break;
509         case 'D':
510             if (!(self->client->decorations & Decor_AllDesktops)) break;
511             if (d) { *lc = ' '; break; } /* rm duplicates */
512             d = TRUE;
513             self->label_width -= theme_button_size + theme_bevel + 1;
514             break;
515         case 'S':
516             if (!(self->client->decorations & Decor_Shade)) break;
517             if (s) { *lc = ' '; break; } /* rm duplicates */
518             s = TRUE;
519             self->label_width -= theme_button_size + theme_bevel + 1;
520             break;
521         case 'I':
522             if (!(self->client->decorations & Decor_Iconify)) break;
523             if (i) { *lc = ' '; break; } /* rm duplicates */
524             i = TRUE;
525             self->label_width -= theme_button_size + theme_bevel + 1;
526             break;
527         case 'L':
528             if (l) { *lc = ' '; break; } /* rm duplicates */
529             l = TRUE;
530             break;
531         case 'M':
532             if (!(self->client->decorations & Decor_Maximize)) break;
533             if (m) { *lc = ' '; break; } /* rm duplicates */
534             m = TRUE;
535             self->label_width -= theme_button_size + theme_bevel + 1;
536             break;
537         case 'C':
538             if (!(self->client->decorations & Decor_Close)) break;
539             if (c) { *lc = ' '; break; } /* rm duplicates */
540             c = TRUE;
541             self->label_width -= theme_button_size + theme_bevel + 1;
542             break;
543         }
544     }
545     if (self->label_width < 1) self->label_width = 1;
546
547     XResizeWindow(ob_display, self->label, self->label_width,
548                   theme_label_height);
549   
550     if (!n) XUnmapWindow(ob_display, self->icon);
551     if (!d) XUnmapWindow(ob_display, self->desk);
552     if (!s) XUnmapWindow(ob_display, self->shade);
553     if (!i) XUnmapWindow(ob_display, self->iconify);
554     if (!l) XUnmapWindow(ob_display, self->label);
555     if (!m) XUnmapWindow(ob_display, self->max);
556     if (!c) XUnmapWindow(ob_display, self->close);
557
558     x = theme_bevel + 1;
559     for (lc = theme_title_layout; *lc != '\0'; ++lc) {
560         switch (*lc) {
561         case 'N':
562             if (!n) break;
563             self->icon_x = x;
564             RECT_SET(self->a_icon->area, 0, 0,
565                      theme_button_size + 2, theme_button_size + 2);
566             XMapWindow(ob_display, self->icon);
567             XMoveWindow(ob_display, self->icon, x, theme_bevel);
568             x += theme_button_size + 2 + theme_bevel + 1;
569             break;
570         case 'D':
571             if (!d) break;
572             self->desk_x = x;
573             XMapWindow(ob_display, self->desk);
574             XMoveWindow(ob_display, self->desk, x, theme_bevel + 1);
575             x += theme_button_size + theme_bevel + 1;
576             break;
577         case 'S':
578             if (!s) break;
579             self->shade_x = x;
580             XMapWindow(ob_display, self->shade);
581             XMoveWindow(ob_display, self->shade, x, theme_bevel + 1);
582             x += theme_button_size + theme_bevel + 1;
583             break;
584         case 'I':
585             if (!i) break;
586             self->iconify_x = x;
587             XMapWindow(ob_display, self->iconify);
588             XMoveWindow(ob_display, self->iconify, x, theme_bevel + 1);
589             x += theme_button_size + theme_bevel + 1;
590             break;
591         case 'L':
592             if (!l) break;
593             self->label_x = x;
594             XMapWindow(ob_display, self->label);
595             XMoveWindow(ob_display, self->label, x, theme_bevel);
596             x += self->label_width + theme_bevel + 1;
597             break;
598         case 'M':
599             if (!m) break;
600             self->max_x = x;
601             XMapWindow(ob_display, self->max);
602             XMoveWindow(ob_display, self->max, x, theme_bevel + 1);
603             x += theme_button_size + theme_bevel + 1;
604             break;
605         case 'C':
606             if (!c) break;
607             self->close_x = x;
608             XMapWindow(ob_display, self->close);
609             XMoveWindow(ob_display, self->close, x, theme_bevel + 1);
610             x += theme_button_size + theme_bevel + 1;
611             break;
612         }
613     }
614
615     RECT_SET(self->a_focused_label->area, 0, 0,
616              self->label_width, theme_label_height);
617     RECT_SET(self->a_unfocused_label->area, 0, 0,
618              self->label_width, theme_label_height);
619 }
620
621 Context frame_context_from_string(char *name)
622 {
623     if (!g_ascii_strcasecmp("root", name))
624         return Context_Root;
625     else if (!g_ascii_strcasecmp("client", name))
626         return Context_Client;
627     else if (!g_ascii_strcasecmp("titlebar", name))
628         return Context_Titlebar;
629     else if (!g_ascii_strcasecmp("handle", name))
630         return Context_Handle;
631     else if (!g_ascii_strcasecmp("frame", name))
632         return Context_Frame;
633     else if (!g_ascii_strcasecmp("blcorner", name))
634         return Context_BLCorner;
635     else if (!g_ascii_strcasecmp("tlcorner", name))
636         return Context_TLCorner;
637     else if (!g_ascii_strcasecmp("brcorner", name))
638         return Context_BRCorner;
639     else if (!g_ascii_strcasecmp("trcorner", name))
640         return Context_TRCorner;
641     else if (!g_ascii_strcasecmp("maximize", name))
642         return Context_Maximize;
643     else if (!g_ascii_strcasecmp("alldesktops", name))
644         return Context_AllDesktops;
645     else if (!g_ascii_strcasecmp("shade", name))
646         return Context_Shade;
647     else if (!g_ascii_strcasecmp("iconify", name))
648         return Context_Iconify;
649     else if (!g_ascii_strcasecmp("icon", name))
650         return Context_Icon;
651     else if (!g_ascii_strcasecmp("close", name))
652         return Context_Close;
653     return Context_None;
654 }
655
656 Context frame_context(Client *client, Window win)
657 {
658     Frame *self;
659
660     if (win == ob_root) return Context_Root;
661     if (client == NULL) return Context_None;
662     if (win == client->window) return Context_Client;
663
664     self = client->frame;
665     if (win == self->window) return Context_Frame;
666     if (win == self->plate)  return Context_Client;
667     if (win == self->title)  return Context_Titlebar;
668     if (win == self->label)  return Context_Titlebar;
669     if (win == self->handle) return Context_Handle;
670     if (win == self->lgrip)  return Context_BLCorner;
671     if (win == self->rgrip)  return Context_BRCorner;
672     if (win == self->max)    return Context_Maximize;
673     if (win == self->iconify)return Context_Iconify;
674     if (win == self->close)  return Context_Close;
675     if (win == self->icon)   return Context_Icon;
676     if (win == self->desk)   return Context_AllDesktops;
677     if (win == self->shade)  return Context_Shade;
678
679     return Context_None;
680 }
681
682 void frame_client_gravity(Frame *self, int *x, int *y)
683 {
684     /* horizontal */
685     switch (self->client->gravity) {
686     default:
687     case NorthWestGravity:
688     case SouthWestGravity:
689     case WestGravity:
690         break;
691
692     case NorthGravity:
693     case SouthGravity:
694     case CenterGravity:
695         *x -= (self->size.left + self->size.right) / 2;
696         break;
697
698     case NorthEastGravity:
699     case SouthEastGravity:
700     case EastGravity:
701         *x -= self->size.left + self->size.right;
702         break;
703
704     case ForgetGravity:
705     case StaticGravity:
706         *x -= self->size.left;
707         break;
708     }
709
710     /* vertical */
711     switch (self->client->gravity) {
712     default:
713     case NorthWestGravity:
714     case NorthEastGravity:
715     case NorthGravity:
716         break;
717
718     case CenterGravity:
719     case EastGravity:
720     case WestGravity:
721         *y -= (self->size.top + self->size.bottom) / 2;
722         break;
723
724     case SouthWestGravity:
725     case SouthEastGravity:
726     case SouthGravity:
727         *y -= self->size.top + self->size.bottom;
728         break;
729
730     case ForgetGravity:
731     case StaticGravity:
732         *y -= self->size.top;
733         break;
734     }
735 }
736
737 void frame_frame_gravity(Frame *self, int *x, int *y)
738 {
739     /* horizontal */
740     switch (self->client->gravity) {
741     default:
742     case NorthWestGravity:
743     case WestGravity:
744     case SouthWestGravity:
745         break;
746     case NorthGravity:
747     case CenterGravity:
748     case SouthGravity:
749         *x += (self->size.left + self->size.right) / 2;
750         break;
751     case NorthEastGravity:
752     case EastGravity:
753     case SouthEastGravity:
754         *x += self->size.left + self->size.right;
755         break;
756     case StaticGravity:
757     case ForgetGravity:
758         *x += self->size.left;
759         break;
760     }
761
762     /* vertical */
763     switch (self->client->gravity) {
764     default:
765     case NorthWestGravity:
766     case WestGravity:
767     case SouthWestGravity:
768         break;
769     case NorthGravity:
770     case CenterGravity:
771     case SouthGravity:
772         *y += (self->size.top + self->size.bottom) / 2;
773         break;
774     case NorthEastGravity:
775     case EastGravity:
776     case SouthEastGravity:
777         *y += self->size.top + self->size.bottom;
778         break;
779     case StaticGravity:
780     case ForgetGravity:
781         *y += self->size.top;
782         break;
783     }
784 }