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