]> icculus.org git repositories - dana/openbox.git/blob - engines/openbox/openbox.c
use the unfocused label when not focused
[dana/openbox.git] / engines / openbox / openbox.c
1 #include "theme.h"
2 #include "../../kernel/openbox.h"
3 #include "../../kernel/screen.h"
4 #include "../../kernel/extensions.h"
5 #include "../../kernel/themerc.h"
6 #include "../../kernel/frame.h"
7 #include "../../render/render.h"
8 #include "../../render/color.h"
9 #include "../../render/font.h"
10 #include "../../render/mask.h"
11
12 #include <X11/Xlib.h>
13 #include <glib.h>
14
15 #define TITLE_HEIGHT    (s_winfont_height + s_bevel * 2)
16 #define LABEL_HEIGHT    (s_winfont_height)
17 #define HANDLE_Y(f)     (f->innersize.top + f->frame.client->area.height + \
18                          f->cbwidth)
19 #define BUTTON_SIZE     (LABEL_HEIGHT - 2)
20 #define GRIP_WIDTH      (BUTTON_SIZE * 2)
21 #define HANDLE_WIDTH(f) (f->width - (GRIP_WIDTH + f->bwidth) * 2)
22
23 #define PLATE_EVENTMASK (SubstructureRedirectMask | ButtonPressMask)
24 #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask)
25
26 /* style settings - geometry */
27 int s_bevel;
28 int s_handle_height;
29 int s_bwidth;
30 int s_cbwidth;
31 /* style settings - colors */
32 color_rgb *s_b_color;
33 color_rgb *s_cb_focused_color;
34 color_rgb *s_cb_unfocused_color;
35 color_rgb *s_title_focused_color;
36 color_rgb *s_title_unfocused_color;
37 color_rgb *s_titlebut_focused_color;
38 color_rgb *s_titlebut_unfocused_color;
39 /* style settings - fonts */
40 int s_winfont_height;
41 int s_winfont_shadow;
42 int s_winfont_shadow_offset;
43 ObFont *s_winfont;
44 /* style settings - masks */
45 pixmap_mask *s_max_mask;
46 pixmap_mask *s_icon_mask;
47 pixmap_mask *s_desk_mask;
48 pixmap_mask *s_close_mask;
49
50 /* global appearances */
51 Appearance *a_focused_unpressed_max;
52 Appearance *a_focused_pressed_max;
53 Appearance *a_unfocused_unpressed_max;
54 Appearance *a_unfocused_pressed_max;
55 Appearance *a_focused_unpressed_close;
56 Appearance *a_focused_pressed_close;
57 Appearance *a_unfocused_unpressed_close;
58 Appearance *a_unfocused_pressed_close;
59 Appearance *a_focused_unpressed_desk;
60 Appearance *a_focused_pressed_desk;
61 Appearance *a_unfocused_unpressed_desk;
62 Appearance *a_unfocused_pressed_desk;
63 Appearance *a_focused_unpressed_iconify;
64 Appearance *a_focused_pressed_iconify;
65 Appearance *a_unfocused_unpressed_iconify;
66 Appearance *a_unfocused_pressed_iconify;
67 Appearance *a_focused_grip;
68 Appearance *a_unfocused_grip;
69 Appearance *a_focused_title;
70 Appearance *a_unfocused_title;
71 Appearance *a_focused_label;
72 Appearance *a_unfocused_label;
73 Appearance *a_icon; /* always parentrelative, so no focused/unfocused */
74 Appearance *a_focused_handle;
75 Appearance *a_unfocused_handle;
76
77 typedef struct ObFrame {
78     Frame frame;
79
80     Window title;
81     Window label;
82     Window max;
83     Window close;
84     Window desk;
85     Window icon;
86     Window iconify;
87     Window handle;
88     Window lgrip;
89     Window rgrip;
90
91     Appearance *a_unfocused_title;
92     Appearance *a_focused_title;
93     Appearance *a_unfocused_label;
94     Appearance *a_focused_label;
95     Appearance *a_icon;
96     Appearance *a_unfocused_handle;
97     Appearance *a_focused_handle;
98
99     Strut  innersize;
100
101     GSList *clients;
102
103     int width; /* title and handle */
104     int label_width;
105     int icon_x;        /* x-position of the window icon button */
106     int label_x;       /* x-position of the window title */
107     int iconify_x;     /* x-position of the window iconify button */
108     int desk_x;         /* x-position of the window all-desktops button */
109     int max_x;         /* x-position of the window maximize button */
110     int close_x;       /* x-position of the window close button */
111     int bwidth;        /* border width */
112     int cbwidth;       /* client border width */
113
114     gboolean max_press;
115     gboolean close_press;
116     gboolean desk_press;
117     gboolean iconify_press;
118 } ObFrame;
119
120 static void layout_title(ObFrame *self);
121 static void render(ObFrame *self);
122 static void render_label(ObFrame *self);
123 static void render_max(ObFrame *self);
124 static void render_icon(ObFrame *self);
125 static void render_iconify(ObFrame *self);
126 static void render_desk(ObFrame *self);
127 static void render_close(ObFrame *self);
128
129 gboolean startup()
130 {
131     g_quark_from_string("none");
132     g_quark_from_string("root");
133     g_quark_from_string("client");
134     g_quark_from_string("titlebar");
135     g_quark_from_string("handle");
136     g_quark_from_string("frame");
137     g_quark_from_string("blcorner");
138     g_quark_from_string("brcorner");
139     g_quark_from_string("maximize");
140     g_quark_from_string("alldesktops");
141     g_quark_from_string("iconify");
142     g_quark_from_string("icon");
143     g_quark_from_string("close");
144
145     s_b_color = s_cb_unfocused_color = s_cb_focused_color = 
146         s_title_unfocused_color = s_title_focused_color = 
147         s_titlebut_unfocused_color = s_titlebut_focused_color = NULL;
148     s_winfont = NULL;
149     s_max_mask = s_icon_mask = s_desk_mask = s_close_mask = NULL;
150
151     a_focused_unpressed_max = appearance_new(Surface_Planar, 1);
152     a_focused_pressed_max = appearance_new(Surface_Planar, 1);
153     a_unfocused_unpressed_max = appearance_new(Surface_Planar, 1);
154     a_unfocused_pressed_max = appearance_new(Surface_Planar, 1);
155     a_focused_unpressed_close = NULL;
156     a_focused_pressed_close = NULL;
157     a_unfocused_unpressed_close = NULL;
158     a_unfocused_pressed_close = NULL;
159     a_focused_unpressed_desk = NULL;
160     a_focused_pressed_desk = NULL;
161     a_unfocused_unpressed_desk = NULL;
162     a_unfocused_pressed_desk = NULL;
163     a_focused_unpressed_iconify = NULL;
164     a_focused_pressed_iconify = NULL;
165     a_unfocused_unpressed_iconify = NULL;
166     a_unfocused_pressed_iconify = NULL;
167     a_focused_grip = appearance_new(Surface_Planar, 0);
168     a_unfocused_grip = appearance_new(Surface_Planar, 0);
169     a_focused_title = appearance_new(Surface_Planar, 0);
170     a_unfocused_title = appearance_new(Surface_Planar, 0);
171     a_focused_label = appearance_new(Surface_Planar, 1);
172     a_unfocused_label = appearance_new(Surface_Planar, 1);
173     a_icon = appearance_new(Surface_Planar, 0);//1);
174     a_focused_handle = appearance_new(Surface_Planar, 0);
175     a_unfocused_handle = appearance_new(Surface_Planar, 0);
176
177     return load();
178 }
179
180 void shutdown()
181 {
182     if (s_b_color != NULL) color_free(s_b_color);
183     if (s_cb_unfocused_color != NULL) color_free(s_cb_unfocused_color);
184     if (s_cb_focused_color != NULL) color_free(s_cb_focused_color);
185     if (s_title_unfocused_color != NULL) color_free(s_title_unfocused_color);
186     if (s_title_focused_color != NULL) color_free(s_title_focused_color);
187     if (s_titlebut_unfocused_color != NULL)
188         color_free(s_titlebut_unfocused_color);
189     if (s_titlebut_focused_color != NULL)
190         color_free(s_titlebut_focused_color);
191
192     if (s_max_mask != NULL) pixmap_mask_free(s_max_mask);
193     if (s_desk_mask != NULL) pixmap_mask_free(s_desk_mask);
194     if (s_icon_mask != NULL) pixmap_mask_free(s_icon_mask);
195     if (s_close_mask != NULL) pixmap_mask_free(s_close_mask);
196
197     if (s_winfont != NULL) font_close(s_winfont);
198
199     appearance_free(a_focused_unpressed_max);
200     appearance_free(a_focused_pressed_max);
201     appearance_free(a_unfocused_unpressed_max);
202     appearance_free(a_unfocused_pressed_max);
203     if (a_focused_unpressed_close != NULL)
204         appearance_free(a_focused_unpressed_close);
205     if (a_focused_pressed_close != NULL)
206         appearance_free(a_focused_pressed_close);
207     if (a_unfocused_unpressed_close != NULL)
208         appearance_free(a_unfocused_unpressed_close);
209     if (a_unfocused_pressed_close != NULL)
210         appearance_free(a_unfocused_pressed_close);
211     if (a_focused_unpressed_desk != NULL)
212         appearance_free(a_focused_unpressed_desk);
213     if (a_focused_pressed_desk != NULL)
214         appearance_free(a_focused_pressed_desk);
215     if (a_unfocused_unpressed_desk != NULL)
216         appearance_free(a_unfocused_unpressed_desk);
217     if (a_unfocused_pressed_desk != NULL)
218         appearance_free(a_unfocused_pressed_desk);
219     if (a_focused_unpressed_iconify != NULL)
220         appearance_free(a_focused_unpressed_iconify);
221     if (a_focused_pressed_iconify != NULL)
222         appearance_free(a_focused_pressed_iconify);
223     if (a_unfocused_unpressed_iconify != NULL)
224         appearance_free(a_unfocused_unpressed_iconify);
225     if (a_unfocused_pressed_iconify != NULL)
226         appearance_free(a_unfocused_pressed_iconify);
227     appearance_free(a_focused_grip);
228     appearance_free(a_unfocused_grip);
229     appearance_free(a_focused_title);
230     appearance_free(a_unfocused_title);
231     appearance_free(a_focused_label);
232     appearance_free(a_unfocused_label);
233     appearance_free(a_icon);
234     appearance_free(a_focused_handle);
235     appearance_free(a_unfocused_handle);
236 }
237
238 static Window createWindow(Window parent, unsigned long mask,
239                            XSetWindowAttributes *attrib)
240 {
241     /* XXX DONT USE THE DEFAULT SHIT */
242     return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
243                          DefaultDepth(ob_display, ob_screen), InputOutput,
244                          DefaultVisual(ob_display, ob_screen),
245                          mask, attrib);
246                        
247 }
248
249 Frame *frame_new()
250 {
251     XSetWindowAttributes attrib;
252     unsigned long mask;
253     ObFrame *self;
254
255     self = g_new(ObFrame, 1);
256
257     self->frame.visible = FALSE;
258
259     /* create all of the decor windows */
260     mask = CWOverrideRedirect | CWEventMask;
261     attrib.event_mask = FRAME_EVENTMASK;
262     attrib.override_redirect = TRUE;
263     self->frame.window = createWindow(ob_root, mask, &attrib);
264
265     mask = 0;
266     self->frame.plate = createWindow(self->frame.window, mask, &attrib);
267
268     mask = CWEventMask;
269     attrib.event_mask = (ButtonPressMask | ButtonReleaseMask |
270                          ButtonMotionMask | ExposureMask);
271     self->title = createWindow(self->frame.window, mask, &attrib);
272     self->label = createWindow(self->title, mask, &attrib);
273     self->max = createWindow(self->title, mask, &attrib);
274     self->close = createWindow(self->title, mask, &attrib);
275     self->desk = createWindow(self->title, mask, &attrib);
276     self->icon = createWindow(self->title, mask, &attrib);
277     self->iconify = createWindow(self->title, mask, &attrib);
278     self->handle = createWindow(self->frame.window, mask, &attrib);
279     mask |= CWCursor;
280     attrib.cursor = ob_cursors.ll_angle;
281     self->lgrip = createWindow(self->handle, mask, &attrib);
282     attrib.cursor = ob_cursors.lr_angle;
283     self->rgrip = createWindow(self->handle, mask, &attrib);
284
285     /* the other stuff is shown based on decor settings */
286     XMapWindow(ob_display, self->frame.plate);
287     XMapWindow(ob_display, self->lgrip);
288     XMapWindow(ob_display, self->rgrip);
289     XMapWindow(ob_display, self->label);
290
291     /* set colors/appearance/sizes for stuff that doesn't change */
292     XSetWindowBorder(ob_display, self->frame.window, s_b_color->pixel);
293     XSetWindowBorder(ob_display, self->label, s_b_color->pixel);
294     XSetWindowBorder(ob_display, self->rgrip, s_b_color->pixel);
295     XSetWindowBorder(ob_display, self->lgrip, s_b_color->pixel);
296
297     XResizeWindow(ob_display, self->max, BUTTON_SIZE, BUTTON_SIZE);
298     XResizeWindow(ob_display, self->iconify, BUTTON_SIZE, BUTTON_SIZE);
299     XResizeWindow(ob_display, self->icon, BUTTON_SIZE, BUTTON_SIZE);
300     XResizeWindow(ob_display, self->close, BUTTON_SIZE, BUTTON_SIZE);
301     XResizeWindow(ob_display, self->desk, BUTTON_SIZE, BUTTON_SIZE);
302     XResizeWindow(ob_display, self->lgrip, GRIP_WIDTH, s_handle_height);
303     XResizeWindow(ob_display, self->rgrip, GRIP_WIDTH, s_handle_height);
304
305     /* set up the dynamic appearances */
306     self->a_unfocused_title = appearance_copy(a_unfocused_title);
307     self->a_focused_title = appearance_copy(a_focused_title);
308     self->a_unfocused_label = appearance_copy(a_unfocused_label);
309     self->a_focused_label = appearance_copy(a_focused_label);
310     self->a_unfocused_handle = appearance_copy(a_unfocused_handle);
311     self->a_focused_handle = appearance_copy(a_focused_handle);
312     self->a_icon = appearance_copy(a_icon);
313
314     self->max_press = self->close_press = self->desk_press = 
315         self->iconify_press = FALSE;
316
317     return (Frame*)self;
318 }
319
320 static void frame_free(ObFrame *self)
321 {
322     appearance_free(self->a_unfocused_title); 
323     appearance_free(self->a_focused_title);
324     appearance_free(self->a_unfocused_label);
325     appearance_free(self->a_focused_label);
326     appearance_free(self->a_unfocused_handle);
327     appearance_free(self->a_focused_handle);
328     appearance_free(self->a_icon);
329
330     XDestroyWindow(ob_display, self->frame.window);
331
332     g_free(self);
333 }
334
335 void frame_show(ObFrame *self)
336 {
337     if (!self->frame.visible) {
338         self->frame.visible = TRUE;
339         XMapWindow(ob_display, self->frame.window);
340     }
341 }
342
343 void frame_hide(ObFrame *self)
344 {
345     if (self->frame.visible) {
346         self->frame.visible = FALSE;
347         self->frame.client->ignore_unmaps++;
348         XUnmapWindow(ob_display, self->frame.window);
349     }
350 }
351
352 void frame_adjust_shape(ObFrame *self)
353 {
354 #ifdef SHAPE
355     int num;
356     XRectangle xrect[2];
357
358     if (!self->frame.client->shaped) {
359         /* clear the shape on the frame window */
360         XShapeCombineMask(ob_display, self->frame.window, ShapeBounding,
361                           self->innersize.left,
362                           self->innersize.top,
363                           None, ShapeSet);
364     } else {
365         /* make the frame's shape match the clients */
366         XShapeCombineShape(ob_display, self->frame.window, ShapeBounding,
367                            self->innersize.left,
368                            self->innersize.top,
369                            self->frame.client->window,
370                            ShapeBounding, ShapeSet);
371
372         num = 0;
373         if (self->frame.client->decorations & Decor_Titlebar) {
374             xrect[0].x = -s_bevel;
375             xrect[0].y = -s_bevel;
376             xrect[0].width = self->width + self->bwidth * 2;
377             xrect[0].height = TITLE_HEIGHT +
378                 self->bwidth * 2;
379             ++num;
380         }
381
382         if (self->frame.client->decorations & Decor_Handle) {
383             xrect[1].x = -s_bevel;
384             xrect[1].y = HANDLE_Y(self);
385             xrect[1].width = self->width + self->bwidth * 2;
386             xrect[1].height = s_handle_height +
387                 self->bwidth * 2;
388             ++num;
389         }
390
391         XShapeCombineRectangles(ob_display, self->frame.window,
392                                 ShapeBounding, 0, 0, xrect, num,
393                                 ShapeUnion, Unsorted);
394     }
395 #endif
396 }
397
398 void frame_adjust_size(ObFrame *self)
399 {
400     if (self->frame.client->decorations & Decor_Border) {
401         self->bwidth = s_bwidth;
402         self->cbwidth = s_cbwidth;
403     } else {
404         self->bwidth = self->cbwidth = 0;
405     }
406     STRUT_SET(self->innersize, self->cbwidth, self->cbwidth,
407               self->cbwidth, self->cbwidth);
408     self->width = self->frame.client->area.width + self->cbwidth * 2;
409     g_assert(self->width > 0);
410
411     /* set border widths */
412     XSetWindowBorderWidth(ob_display, self->frame.plate,  self->cbwidth);
413     XSetWindowBorderWidth(ob_display, self->frame.window, self->bwidth);
414     XSetWindowBorderWidth(ob_display, self->title,  self->bwidth);
415     XSetWindowBorderWidth(ob_display, self->handle, self->bwidth);
416     XSetWindowBorderWidth(ob_display, self->lgrip,  self->bwidth);
417     XSetWindowBorderWidth(ob_display, self->rgrip,  self->bwidth);
418   
419     /* position/size and map/unmap all the windows */
420
421     if (self->frame.client->decorations & Decor_Titlebar) {
422         XMoveResizeWindow(ob_display, self->title,
423                           -self->bwidth, -self->bwidth,
424                           self->width, TITLE_HEIGHT);
425         self->innersize.top += TITLE_HEIGHT + self->bwidth;
426         XMapWindow(ob_display, self->title);
427
428         /* layout the title bar elements */
429         layout_title(self);
430     } else {
431         XUnmapWindow(ob_display, self->title);
432         /* make all the titlebar stuff not render */
433         self->frame.client->decorations &= ~(Decor_Icon | Decor_Iconify |
434                                Decor_Maximize | Decor_Close |
435                                Decor_AllDesktops);
436     }
437
438     if (self->frame.client->decorations & Decor_Handle) {
439         XMoveResizeWindow(ob_display, self->handle,
440                           -self->bwidth, HANDLE_Y(self),
441                           self->width, s_handle_height);
442         XMoveWindow(ob_display, self->lgrip,
443                     -self->bwidth, -self->bwidth);
444         XMoveWindow(ob_display, self->rgrip,
445                     -self->bwidth + self->width -
446                     GRIP_WIDTH, -self->bwidth);
447         self->innersize.bottom += s_handle_height +
448             self->bwidth;
449         XMapWindow(ob_display, self->handle);
450     } else
451         XUnmapWindow(ob_display, self->handle);
452   
453     XResizeWindow(ob_display, self->frame.window, self->width,
454                   (self->frame.client->shaded ? TITLE_HEIGHT :
455                    self->innersize.top + self->innersize.bottom +
456                    self->frame.client->area.height));
457
458     /* do this in two steps because clients whose gravity is set to
459        'Static' don't end up getting moved at all with an XMoveResizeWindow */
460     XMoveWindow(ob_display, self->frame.plate,
461                 self->innersize.left - self->cbwidth,
462                 self->innersize.top - self->cbwidth);
463     XResizeWindow(ob_display, self->frame.plate,
464                   self->frame.client->area.width,
465                   self->frame.client->area.height);
466
467     STRUT_SET(self->frame.size,
468               self->innersize.left + self->bwidth,
469               self->innersize.top + self->bwidth,
470               self->innersize.right + self->bwidth,
471               self->innersize.bottom + self->bwidth);
472
473     RECT_SET_SIZE(self->frame.area,
474                   self->frame.client->area.width +
475                   self->frame.size.left + self->frame.size.right,
476                   self->frame.client->area.height +
477                   self->frame.size.top + self->frame.size.bottom);
478
479     render(self);
480      
481     frame_adjust_shape(self);
482 }
483
484 void frame_adjust_position(ObFrame *self)
485 {
486     self->frame.area.x = self->frame.client->area.x;
487     self->frame.area.y = self->frame.client->area.y;
488     frame_client_gravity((Frame*)self,
489                          &self->frame.area.x, &self->frame.area.y);
490     XMoveWindow(ob_display, self->frame.window,
491                 self->frame.area.x, self->frame.area.y);
492 }
493
494 void frame_adjust_state(ObFrame *self)
495 {
496     render_max(self);
497     render_desk(self);
498 }
499
500 void frame_adjust_focus(ObFrame *self)
501 {
502     render(self);
503 }
504
505 void frame_adjust_title(ObFrame *self)
506 {
507     render_label(self);
508 }
509
510 void frame_adjust_icon(ObFrame *self)
511 {
512     render_icon(self);
513 }
514
515 void frame_grab_client(ObFrame *self, Client *client)
516 {
517     self->frame.client = client;
518
519     /* reparent the client to the frame */
520     XReparentWindow(ob_display, client->window, self->frame.plate, 0, 0);
521     /*
522       When reparenting the client window, it is usually not mapped yet, since
523       this occurs from a MapRequest. However, in the case where Openbox is
524       starting up, the window is already mapped, so we'll see unmap events for
525       it. There are 2 unmap events generated that we see, one with the 'event'
526       member set the root window, and one set to the client, but both get
527       handled and need to be ignored.
528     */
529     if (ob_state == State_Starting)
530         client->ignore_unmaps += 2;
531
532     /* select the event mask on the client's parent (to receive config/map
533        req's) the ButtonPress is to catch clicks on the client border */
534     XSelectInput(ob_display, self->frame.plate, PLATE_EVENTMASK);
535
536     /* map the client so it maps when the frame does */
537     XMapWindow(ob_display, client->window);
538
539     frame_adjust_size(self);
540     frame_adjust_position(self);
541
542     /* set all the windows for the frame in the client_map */
543     g_hash_table_insert(client_map, (gpointer)self->frame.window, client);
544     g_hash_table_insert(client_map, (gpointer)self->frame.plate, client);
545     g_hash_table_insert(client_map, (gpointer)self->title, client);
546     g_hash_table_insert(client_map, (gpointer)self->label, client);
547     g_hash_table_insert(client_map, (gpointer)self->max, client);
548     g_hash_table_insert(client_map, (gpointer)self->close, client);
549     g_hash_table_insert(client_map, (gpointer)self->desk, client);
550     g_hash_table_insert(client_map, (gpointer)self->icon, client);
551     g_hash_table_insert(client_map, (gpointer)self->iconify, client);
552     g_hash_table_insert(client_map, (gpointer)self->handle, client);
553     g_hash_table_insert(client_map, (gpointer)self->lgrip, client);
554     g_hash_table_insert(client_map, (gpointer)self->rgrip, client);
555 }
556
557 void frame_release_client(ObFrame *self, Client *client)
558 {
559     XEvent ev;
560
561     g_assert(self->frame.client == client);
562
563     /* check if the app has already reparented its window away */
564     if (XCheckTypedWindowEvent(ob_display, client->window,
565                                ReparentNotify, &ev)) {
566         XPutBackEvent(ob_display, &ev);
567         /* re-map the window since the unmanaging process unmaps it */
568         XMapWindow(ob_display, client->window);
569     } else {
570         /* according to the ICCCM - if the client doesn't reparent itself,
571            then we will reparent the window to root for them */
572         XReparentWindow(ob_display, client->window, ob_root,
573                         client->area.x,
574                         client->area.y);
575     }
576
577     /* remove all the windows for the frame from the client_map */
578     g_hash_table_remove(client_map, (gpointer)self->frame.window);
579     g_hash_table_remove(client_map, (gpointer)self->frame.plate);
580     g_hash_table_remove(client_map, (gpointer)self->title);
581     g_hash_table_remove(client_map, (gpointer)self->label);
582     g_hash_table_remove(client_map, (gpointer)self->max);
583     g_hash_table_remove(client_map, (gpointer)self->close);
584     g_hash_table_remove(client_map, (gpointer)self->desk);
585     g_hash_table_remove(client_map, (gpointer)self->icon);
586     g_hash_table_remove(client_map, (gpointer)self->iconify);
587     g_hash_table_remove(client_map, (gpointer)self->handle);
588     g_hash_table_remove(client_map, (gpointer)self->lgrip);
589     g_hash_table_remove(client_map, (gpointer)self->rgrip);
590
591     frame_free(self);
592 }
593
594 static void layout_title(ObFrame *self)
595 {
596     const char *lc;
597     int x;
598     gboolean n, d, i, l, m ,c;
599
600     n = d = i = l = m = c = FALSE;
601
602     /* figure out whats being shown, and the width of the label */
603     self->label_width = self->width - (s_bevel + 1) * 2;
604     for (lc = themerc_titlebar_layout; *lc != '\0'; ++lc) {
605         switch (*lc) {
606         case 'N':
607             if (!(self->frame.client->decorations & Decor_Icon)) break;
608             n = TRUE;
609             self->label_width -= BUTTON_SIZE + s_bevel + 1;
610             break;
611         case 'D':
612             if (!(self->frame.client->decorations & Decor_AllDesktops)) break;
613             d = TRUE;
614             self->label_width -= BUTTON_SIZE + s_bevel + 1;
615             break;
616         case 'I':
617             if (!(self->frame.client->decorations & Decor_Iconify)) break;
618             i = TRUE;
619             self->label_width -= BUTTON_SIZE + s_bevel + 1;
620             break;
621         case 'L':
622             l = TRUE;
623             break;
624         case 'M':
625             if (!(self->frame.client->decorations & Decor_Maximize)) break;
626             m = TRUE;
627             self->label_width -= BUTTON_SIZE + s_bevel + 1;
628             break;
629         case 'C':
630             if (!(self->frame.client->decorations & Decor_Close)) break;
631             c = TRUE;
632             self->label_width -= BUTTON_SIZE + s_bevel + 1;
633             break;
634         }
635     }
636     if (self->label_width < 1) self->label_width = 1;
637
638     XResizeWindow(ob_display, self->label, self->label_width,
639                   s_winfont_height);
640   
641     if (!n) {
642         self->frame.client->decorations &= ~Decor_Icon;
643         XUnmapWindow(ob_display, self->icon);
644         self->icon_x = -1;
645     }
646     if (!d) {
647         self->frame.client->decorations &= ~Decor_AllDesktops;
648         XUnmapWindow(ob_display, self->desk);
649         self->desk_x = -1;
650     }
651     if (!i) {
652         self->frame.client->decorations &= ~Decor_Iconify;
653         XUnmapWindow(ob_display, self->iconify);
654         self->icon_x = -1;
655     }
656     if (!l) {
657         XUnmapWindow(ob_display, self->label);
658         self->label_x = -1;
659     }
660     if (!m) {
661         self->frame.client->decorations &= ~Decor_Maximize;
662         XUnmapWindow(ob_display, self->max);
663         self->max_x = -1;
664     }
665     if (!c) {
666         self->frame.client->decorations &= ~Decor_Close;
667         XUnmapWindow(ob_display, self->close);
668         self->close_x = -1;
669     }
670
671     x = s_bevel + 1;
672     for (lc = themerc_titlebar_layout; *lc != '\0'; ++lc) {
673         switch (*lc) {
674         case 'N':
675             if (!n) break;
676             self->icon_x = x;
677             XMapWindow(ob_display, self->icon);
678             XMoveWindow(ob_display, self->icon, x, s_bevel + 1);
679             x += BUTTON_SIZE + s_bevel + 1;
680             break;
681         case 'D':
682             if (!d) break;
683             self->desk_x = x;
684             XMapWindow(ob_display, self->desk);
685             XMoveWindow(ob_display, self->desk, x, s_bevel + 1);
686             x += BUTTON_SIZE + s_bevel + 1;
687             break;
688         case 'I':
689             if (!i) break;
690             self->iconify_x = x;
691             XMapWindow(ob_display, self->iconify);
692             XMoveWindow(ob_display, self->iconify, x, s_bevel + 1);
693             x += BUTTON_SIZE + s_bevel + 1;
694             break;
695         case 'L':
696             if (!l) break;
697             self->label_x = x;
698             XMapWindow(ob_display, self->label);
699             XMoveWindow(ob_display, self->label, x, s_bevel);
700             x += self->label_width + s_bevel + 1;
701             break;
702         case 'M':
703             if (!m) break;
704             self->max_x = x;
705             XMapWindow(ob_display, self->max);
706             XMoveWindow(ob_display, self->max, x, s_bevel + 1);
707             x += BUTTON_SIZE + s_bevel + 1;
708             break;
709         case 'C':
710             if (!c) break;
711             self->close_x = x;
712             XMapWindow(ob_display, self->close);
713             XMoveWindow(ob_display, self->close, x, s_bevel + 1);
714             x += BUTTON_SIZE + s_bevel + 1;
715             break;
716         }
717     }
718 }
719
720 static void render(ObFrame *self)
721 {
722     if (self->frame.client->focused) {
723         XSetWindowBorder(ob_display, self->frame.plate,
724                          s_cb_focused_color->pixel);
725     } else {
726         XSetWindowBorder(ob_display, self->frame.plate,
727                          s_cb_unfocused_color->pixel);
728     }
729
730     if (self->frame.client->decorations & Decor_Titlebar) {
731         paint(self->title, (self->frame.client->focused ?
732                             self->a_focused_title :
733                             self->a_unfocused_title),
734               0, 0, self->width, TITLE_HEIGHT);
735         render_label(self);
736         render_max(self);
737         render_icon(self);
738         render_iconify(self);
739         render_desk(self);
740         render_close(self);
741     }
742
743     if (self->frame.client->decorations & Decor_Handle) {
744         paint(self->handle, (self->frame.client->focused ?
745                              self->a_focused_handle :
746                              self->a_unfocused_handle),
747               GRIP_WIDTH + self->bwidth, 0,
748               HANDLE_WIDTH(self), s_handle_height);
749         paint(self->lgrip, (self->frame.client->focused ?
750                             a_focused_grip :
751                             a_unfocused_grip),
752               0, 0, GRIP_WIDTH, s_handle_height);
753         paint(self->rgrip, (self->frame.client->focused ?
754                             a_focused_grip :
755                             a_unfocused_grip),
756               0, 0, GRIP_WIDTH, s_handle_height);
757     }
758 }
759
760 static void render_label(ObFrame *self)
761 {
762     Appearance *a;
763
764     if (self->label_x < 0) return;
765
766     a = (self->frame.client->focused ?
767          self->a_focused_label : self->a_unfocused_label);
768
769     /* set the texture's text! */
770     a->texture[0].data.text.string = self->frame.client->title;
771
772     paint(self->label, a, 0, 0, self->label_width, LABEL_HEIGHT);
773 }
774
775 static void render_icon(ObFrame *self)
776 {
777     if (self->icon_x < 0) return;
778
779     /* XXX set the texture's icon picture! */
780     paint(self->icon, self->a_icon, 0, 0, BUTTON_SIZE, BUTTON_SIZE);
781 }
782
783 static void render_max(ObFrame *self)
784 {
785     gboolean press = self->max_press ||
786         self->frame.client->max_vert || self->frame.client->max_horz;
787     
788     if (self->max_x < 0) return;
789
790     paint(self->max, (self->frame.client->focused ?
791                       (press ?
792                        a_focused_pressed_max :
793                        a_focused_unpressed_max) :
794                       (press ?
795                        a_unfocused_pressed_max :
796                        a_unfocused_unpressed_max)),
797           0, 0, BUTTON_SIZE, BUTTON_SIZE);
798 }
799
800 static void render_iconify(ObFrame *self)
801 {
802     if (self->iconify_x < 0) return;
803
804     paint(self->iconify, (self->frame.client->focused ?
805                           (self->iconify_press ?
806                            a_focused_pressed_iconify :
807                            a_focused_unpressed_iconify) :
808                           (self->iconify_press ?
809                            a_unfocused_pressed_iconify :
810                            a_unfocused_unpressed_iconify)),
811           0, 0, BUTTON_SIZE, BUTTON_SIZE);
812 }
813
814 static void render_desk(ObFrame *self)
815 {
816     gboolean press = self->desk_press ||
817         self->frame.client->desktop == DESKTOP_ALL;
818     
819     if (self->desk_x < 0) return;
820
821     paint(self->desk, (self->frame.client->focused ?
822                        (press ?
823                         a_focused_pressed_desk :
824                         a_focused_unpressed_desk) :
825                        (press ?
826                         a_unfocused_pressed_desk :
827                         a_unfocused_unpressed_desk)),
828           0, 0, BUTTON_SIZE, BUTTON_SIZE);
829 }
830
831 static void render_close(ObFrame *self)
832 {
833     if (self->close_x < 0) return;
834
835     paint(self->close, (self->frame.client->focused ?
836                           (self->close_press ?
837                            a_focused_pressed_close :
838                            a_focused_unpressed_close) :
839                           (self->close_press ?
840                            a_unfocused_pressed_close :
841                            a_unfocused_unpressed_close)),
842           0, 0, BUTTON_SIZE, BUTTON_SIZE);
843 }
844
845 GQuark get_context(Client *client, Window win)
846 {
847     ObFrame *self;
848
849     if (win == ob_root) return g_quark_try_string("root");
850     if (client == NULL) return g_quark_try_string("none");
851     if (win == client->window) return g_quark_try_string("client");
852
853     self = (ObFrame*) client->frame;
854     if (win == self->frame.window) return g_quark_try_string("frame");
855     if (win == self->frame.plate)  return g_quark_try_string("client");
856     if (win == self->title)  return g_quark_try_string("titlebar");
857     if (win == self->label)  return g_quark_try_string("titlebar");
858     if (win == self->handle) return g_quark_try_string("handle");
859     if (win == self->lgrip)  return g_quark_try_string("blcorner");
860     if (win == self->rgrip)  return g_quark_try_string("brcorner");
861     if (win == self->max)  return g_quark_try_string("maximize");
862     if (win == self->iconify)  return g_quark_try_string("iconify");
863     if (win == self->close)  return g_quark_try_string("close");
864     if (win == self->icon)  return g_quark_try_string("icon");
865     if (win == self->desk)  return g_quark_try_string("alldesktops");
866
867     return g_quark_try_string("none");
868 }
869
870 void frame_mouse_enter(ObFrame *self, Window win)
871 {
872 }
873
874 void frame_mouse_leave(ObFrame *self, Window win)
875 {
876 }
877
878 void frame_mouse_press(ObFrame *self, Window win, int x, int y)
879 {
880     if (win == self->max) {
881         self->max_press = TRUE;
882         render_max(self);
883     }
884     else if (win == self->close) {
885         self->close_press = TRUE;
886         render_close(self);
887     }
888     else if (win == self->iconify) {
889         self->iconify_press = TRUE;
890         render_iconify(self);
891     }
892     else if (win == self->desk) { 
893         self->desk_press = TRUE;
894         render_desk(self);
895     }
896 }
897
898 void frame_mouse_release(ObFrame *self, Window win, int x, int y)
899 {
900     if (win == self->max) {
901         self->max_press = FALSE;
902         render_max(self);
903     }
904     else if (win == self->close) {
905         self->close_press = FALSE; 
906         render_close(self);
907     }
908     else if (win == self->iconify) {
909         self->iconify_press = FALSE;
910         render_iconify(self);
911     }
912     else if (win == self->desk) {
913         self->desk_press = FALSE;
914         render_desk(self);
915     }
916 }