]> icculus.org git repositories - dana/openbox.git/blob - src/frame.cc
labels are not the size of buttons
[dana/openbox.git] / src / frame.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "config.h"
4
5 extern "C" {
6 #ifdef    SHAPE
7 #include <X11/extensions/shape.h>
8 #endif // SHAPE
9 }
10
11 #include "frame.hh"
12 #include "openbox.hh"
13 #include "otk/display.hh"
14 #include "otk/surface.hh"
15
16 #include <string>
17 #include <cassert>
18
19 namespace ob {
20
21 const long Frame::event_mask;
22
23 Window createWindow(const otk::ScreenInfo *info, Window parent, 
24                     unsigned long mask, XSetWindowAttributes *attrib)
25 {
26   return XCreateWindow(**otk::display, parent, 0, 0, 1, 1, 0,
27                        info->depth(), InputOutput, info->visual(),
28                        mask, attrib);
29                        
30 }
31
32 Frame::Frame(Client *client)
33   : _client(client),
34     _visible(false),
35     _plate(0),
36     _title(0),
37     _label(0),
38     _handle(0),
39     _lgrip(0),
40     _rgrip(0),
41     _max(0),
42     _desk(0),
43     _iconify(0),
44     _icon(0),
45     _close(0),
46     _frame_sur(0),
47     _title_sur(0),
48     _label_sur(0),
49     _handle_sur(0),
50     _grip_sur(0),
51     _max_sur(0),
52     _desk_sur(0),
53     _iconify_sur(0),
54     _icon_sur(0),
55     _close_sur(0),
56     _max_press(false),
57     _desk_press(false),
58     _iconify_press(false),
59     _icon_press(false),
60     _close_press(false),
61     _press_button(0)
62 {
63   assert(client);
64
65   XSetWindowAttributes attrib;
66   unsigned long mask;
67   const otk::ScreenInfo *info = otk::display->screenInfo(client->screen());
68
69   // create all of the decor windows (except title bar buttons)
70   mask = CWOverrideRedirect | CWEventMask;
71   attrib.event_mask = Frame::event_mask;
72   attrib.override_redirect = true;
73   _frame = createWindow(info, info->rootWindow(), mask, &attrib);
74
75   mask = 0;
76   _plate = createWindow(info, _frame, mask, &attrib);
77   mask = CWEventMask;
78   attrib.event_mask = (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
79                        ExposureMask);
80   _title = createWindow(info, _frame, mask, &attrib);
81   _label = createWindow(info, _title, mask, &attrib);
82   _max = createWindow(info, _title, mask, &attrib);
83   _close = createWindow(info, _title, mask, &attrib);
84   _desk = createWindow(info, _title, mask, &attrib);
85   _icon = createWindow(info, _title, mask, &attrib);
86   _iconify = createWindow(info, _title, mask, &attrib);
87   _handle = createWindow(info, _frame, mask, &attrib);
88   mask |= CWCursor;
89   attrib.cursor = openbox->cursors().ll_angle;
90   _lgrip = createWindow(info, _handle, mask, &attrib);
91   attrib.cursor = openbox->cursors().lr_angle;
92   _rgrip = createWindow(info, _handle, mask, &attrib);
93
94   // the other stuff is shown based on decor settings
95   XMapWindow(**otk::display, _plate);
96   XMapWindow(**otk::display, _lgrip);
97   XMapWindow(**otk::display, _rgrip);
98   XMapWindow(**otk::display, _label);
99
100   applyStyle(*otk::RenderStyle::style(_client->screen()));
101
102   _layout = openbox->screen(_client->screen())->config().titlebar_layout;
103
104   // register all of the windows with the event dispatcher
105   Window *w = allWindows();
106   for (unsigned int i = 0; w[i]; ++i)
107     openbox->registerHandler(w[i], this);
108   delete [] w;
109 }
110
111 Frame::~Frame()
112 {
113   // unregister all of the windows with the event dispatcher
114   Window *w = allWindows();
115   for (unsigned int i = 0; w[i]; ++i)
116     openbox->clearHandler(w[i]);
117   delete [] w;
118
119   XDestroyWindow(**otk::display, _rgrip);
120   XDestroyWindow(**otk::display, _lgrip);
121   XDestroyWindow(**otk::display, _handle);
122   XDestroyWindow(**otk::display, _max);
123   XDestroyWindow(**otk::display, _icon);
124   XDestroyWindow(**otk::display, _iconify);
125   XDestroyWindow(**otk::display, _desk);
126   XDestroyWindow(**otk::display, _close);
127   XDestroyWindow(**otk::display, _label);
128   XDestroyWindow(**otk::display, _title);
129   XDestroyWindow(**otk::display, _frame);
130
131   if (_frame_sur) delete _frame_sur;
132   if (_title_sur) delete _title_sur;
133   if (_label_sur) delete _label_sur;
134   if (_handle_sur) delete _handle_sur;
135   if (_grip_sur) delete _grip_sur;
136   if (_max_sur) delete _max_sur;
137   if (_desk_sur) delete _desk_sur;
138   if (_iconify_sur) delete _iconify_sur;
139   if (_icon_sur) delete _icon_sur;
140   if (_close_sur) delete _close_sur;
141 }
142
143 void Frame::show()
144 {
145   if (!_visible) {
146     _visible = true;
147     XMapWindow(**otk::display, _frame);
148   }
149 }
150
151 void Frame::hide()
152 {
153   if (_visible) {
154     _visible = false;
155     XUnmapWindow(**otk::display, _frame);
156   }
157 }
158
159 void Frame::buttonPressHandler(const XButtonEvent &e)
160 {
161   if (_press_button) return;
162   _press_button = e.button;
163   
164   if (e.window == _max) {
165     _max_press = true;
166     renderMax();
167   }
168   if (e.window == _close) {
169     _close_press = true;
170     renderClose();
171   }
172   if (e.window == _desk) {
173     _desk_press = true;
174     renderDesk();
175   }
176   if (e.window == _iconify) {
177     _iconify_press = true;
178     renderIconify();
179   }
180   if (e.window == _icon) {
181     _icon_press = true;
182     renderIcon();
183   }
184 }
185
186 void Frame::buttonReleaseHandler(const XButtonEvent &e)
187 {
188   if (e.button != _press_button) return;
189   _press_button = 0;
190
191   if (e.window == _max) {
192     _max_press = false;
193     renderMax();
194   }
195   if (e.window == _close) {
196     _close_press = false;
197     renderClose();
198   }
199   if (e.window == _desk) {
200     _desk_press = false;
201     renderDesk();
202   }
203   if (e.window == _iconify) {
204     _iconify_press = false;
205     renderIconify();
206   }
207   if (e.window == _icon) {
208     _icon_press = false;
209     renderIcon();
210   }
211 }
212
213 MouseContext::MC Frame::mouseContext(Window win) const
214 {
215   if (win == _frame)  return MouseContext::Frame;
216   if (win == _title ||
217       win == _label)  return MouseContext::Titlebar;
218   if (win == _handle) return MouseContext::Handle;
219   if (win == _plate)  return MouseContext::Window;
220   if (win == _lgrip ||
221       win == _rgrip)  return MouseContext::Grip;
222   if (win == _max)    return MouseContext::MaximizeButton;
223   if (win == _close)  return MouseContext::CloseButton;
224   if (win == _desk)   return MouseContext::AllDesktopsButton;
225   if (win == _iconify)return MouseContext::IconifyButton;
226   if (win == _icon)   return MouseContext::IconButton;
227   return (MouseContext::MC) -1;
228 }
229
230 Window *Frame::allWindows() const
231 {
232   Window *w = new Window[12 + 1];
233   unsigned int i = 0;
234   w[i++] = _frame;
235   w[i++] = _plate;
236   w[i++] = _title;
237   w[i++] = _label;
238   w[i++] = _handle;
239   w[i++] = _lgrip;
240   w[i++] = _rgrip;
241   w[i++] = _max;
242   w[i++] = _desk;
243   w[i++] = _close;
244   w[i++] = _icon;
245   w[i++] = _iconify;
246   w[i] = 0;
247   return w;
248 }
249
250 void Frame::applyStyle(const otk::RenderStyle &style)
251 {
252   // set static border colors
253   XSetWindowBorder(**otk::display, _frame, style.frameBorderColor()->pixel());
254   XSetWindowBorder(**otk::display, _title, style.frameBorderColor()->pixel());
255   XSetWindowBorder(**otk::display, _handle, style.frameBorderColor()->pixel());
256   XSetWindowBorder(**otk::display, _lgrip, style.frameBorderColor()->pixel());
257   XSetWindowBorder(**otk::display, _rgrip, style.frameBorderColor()->pixel());
258
259   // size all the fixed-size elements
260   geom.font_height = style.labelFont()->height();
261   if (geom.font_height < 1) geom.font_height = 1;
262   geom.button_size = geom.font_height - 2;
263   if (geom.button_size < 1) geom.button_size = 1;
264   geom.handle_height = style.handleWidth();
265   if (geom.handle_height < 1) geom.handle_height = 1;
266   geom.bevel = style.bevelWidth();
267   
268   XResizeWindow(**otk::display, _lgrip, geom.grip_width(), geom.handle_height);
269   XResizeWindow(**otk::display, _rgrip, geom.grip_width(), geom.handle_height);
270   
271   XResizeWindow(**otk::display, _max, geom.button_size, geom.button_size);
272   XResizeWindow(**otk::display, _close, geom.button_size, geom.button_size);
273   XResizeWindow(**otk::display, _desk, geom.button_size, geom.button_size);
274   XResizeWindow(**otk::display, _iconify, geom.button_size, geom.button_size);
275   XResizeWindow(**otk::display, _icon, geom.button_size, geom.button_size);
276 }
277
278 void Frame::styleChanged(const otk::RenderStyle &style)
279 {
280   applyStyle(style);
281   
282   // size/position everything
283   adjustSize();
284   adjustPosition();
285 }
286
287 void Frame::adjustFocus()
288 {
289   // XXX optimizations later...
290   adjustSize();
291 }
292
293 void Frame::adjustTitle()
294 {
295   // XXX optimizations later...
296   adjustSize();
297 }
298
299 static void render(int screen, const otk::Size &size, Window win,
300                    otk::Surface **surface,
301                    const otk::RenderTexture &texture, bool freedata=true)
302 {
303   otk::Surface *s = new otk::Surface(screen, size);
304   if (texture.parentRelative())
305     XSetWindowBackgroundPixmap(**otk::display, win, ParentRelative);
306   else {
307     otk::display->renderControl(screen)->drawBackground(*s, texture);
308     XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
309   }
310   XClearWindow(**otk::display, win);
311   if (*surface) delete *surface;
312   if (freedata) s->freePixelData();
313   *surface = s;
314 }
315
316 void Frame::adjustSize()
317 {
318   _decorations = _client->decorations();
319   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
320
321   if (_decorations & Client::Decor_Border) {
322     geom.bwidth = style->frameBorderWidth();
323     geom.cbwidth = style->clientBorderWidth();
324   } else {
325     geom.bwidth = geom.cbwidth = 0;
326   }
327   _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
328     geom.cbwidth;
329   geom.width = _client->area().width() + geom.cbwidth * 2;
330   assert(geom.width > 0);
331
332   // set border widths
333   XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
334   XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
335   XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
336   XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
337   XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
338   XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
339   
340   // position/size and map/unmap all the windows
341
342   if (_decorations & Client::Decor_Titlebar) {
343     XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
344                       geom.width, geom.title_height());
345     _innersize.top += geom.title_height() + geom.bwidth;
346     XMapWindow(**otk::display, _title);
347
348     // layout the title bar elements
349     layoutTitle();
350   } else {
351     XUnmapWindow(**otk::display, _title);
352     // make all the titlebar stuff not render
353     _decorations &= ~(Client::Decor_Icon | Client::Decor_Iconify |
354                       Client::Decor_Maximize | Client::Decor_Close |
355                       Client::Decor_AllDesktops);
356   }
357
358   if (_decorations & Client::Decor_Handle) {
359     geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
360     XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
361                       geom.width, geom.handle_height);
362     XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
363     XMoveWindow(**otk::display, _rgrip,
364                 -geom.bwidth + geom.width - geom.grip_width(),
365                 -geom.bwidth);
366     _innersize.bottom += geom.handle_height + geom.bwidth;
367     XMapWindow(**otk::display, _handle);
368   } else
369     XUnmapWindow(**otk::display, _handle);
370   
371   XResizeWindow(**otk::display, _frame, geom.width,
372                 (_client->shaded() ? geom.title_height() :
373                  _innersize.top + _innersize.bottom +
374                  _client->area().height()));
375
376   // do this in two steps because clients whose gravity is set to
377   // 'Static' don't end up getting moved at all with an XMoveResizeWindow
378   XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
379               _innersize.top - geom.cbwidth);
380   XResizeWindow(**otk::display, _plate, _client->area().width(),
381                 _client->area().height());
382
383   _size.left   = _innersize.left + geom.bwidth;
384   _size.right  = _innersize.right + geom.bwidth;
385   _size.top    = _innersize.top + geom.bwidth;
386   _size.bottom = _innersize.bottom + geom.bwidth;
387
388   _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
389                                                 _size.left + _size.right,
390                                                 _client->area().height() +
391                                                 _size.top + _size.bottom));
392
393   // render all the elements
394   int screen = _client->screen();
395   bool focus = _client->focused();
396   if (_decorations & Client::Decor_Titlebar) {
397     render(screen, otk::Size(geom.width, geom.title_height()), _title,
398            &_title_sur, *(focus ? style->titlebarFocusBackground() :
399                           style->titlebarUnfocusBackground()), false);
400     
401     renderLabel();
402     renderMax();
403     renderDesk();
404     renderIconify();
405     renderIcon();
406     renderClose();
407   }
408
409   if (_decorations & Client::Decor_Handle) {
410     render(screen, otk::Size(geom.width, geom.handle_height), _handle,
411            &_handle_sur, *(focus ? style->handleFocusBackground() :
412                            style->handleUnfocusBackground()));
413     render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
414            &_grip_sur, *(focus ? style->gripFocusBackground() :
415                          style->gripUnfocusBackground()));
416     if ((focus ? style->gripFocusBackground() :
417          style->gripUnfocusBackground())->parentRelative())
418       XSetWindowBackgroundPixmap(**otk::display, _rgrip, ParentRelative);
419     else {
420       XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
421     }
422     XClearWindow(**otk::display, _rgrip);
423   }
424
425   XSetWindowBorder(**otk::display, _plate,
426                    focus ? style->clientBorderFocusColor()->pixel() :
427                    style->clientBorderUnfocusColor()->pixel());
428   
429   adjustShape();
430 }
431
432 void Frame::renderLabel()
433 {
434   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
435   const otk::RenderControl *control =
436     otk::display->renderControl(_client->screen());
437   const otk::Font *font = style->labelFont();
438
439   otk::Surface *s = new otk::Surface(_client->screen(),
440                                      otk::Size(geom.label_width,
441                                                geom.label_height()));
442   const otk::RenderTexture *tx = (_client->focused() ?
443                                   style->labelFocusBackground() :
444                                   style->labelUnfocusBackground());
445   if (tx->parentRelative()) {
446     otk::pixel32 *dest = s->pixelData(), *src;
447     int w = _title_sur->size().width();
448   
449     src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.title_x;
450     
451     // get the background under the label
452     int xd = s->width();
453     int yd = s->height();
454     for (int y = 0; y < yd; ++y, src += w - xd)
455       for (int x = 0; x < xd; ++x, ++dest, ++src)
456         *dest = *src;
457     control->drawImage(*s, 0, 0, 0); // no image but draw the new background
458   } else
459     control->drawBackground(*s, *tx);
460
461   otk::ustring t = _client->title(); // the actual text to draw
462   int x = geom.bevel;                // x coord for the text
463
464   if (x * 2 < geom.label_width) {
465     // find a string that will fit inside the area for text
466     otk::ustring::size_type text_len = t.size();
467     int length;
468     int maxsize = geom.label_width - geom.bevel * 2;
469       
470     do {
471       t.resize(text_len);
472       length = font->measureString(t);// this returns an unsigned, so check < 0
473       if (length < 0) length = maxsize;// if the string's that long just adjust
474     } while (length > maxsize && text_len-- > 0);
475
476     // justify the text
477     switch (style->labelTextJustify()) {
478     case otk::RenderStyle::RightBottomJustify:
479       x += maxsize - length;
480       break;
481     case otk::RenderStyle::CenterJustify:
482       x += (maxsize - length) / 2;
483       break;
484     case otk::RenderStyle::LeftTopJustify:
485       break;
486     }
487  
488     if (text_len > 0)
489       control->drawString(*s, *font, x, 0,
490                           *(_client->focused() ? style->textFocusColor() :
491                             style->textUnfocusColor()), t);
492   }
493
494   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
495   XClearWindow(**otk::display, _label);
496   if (_label_sur) delete _label_sur;
497   s->freePixelData();
498   _label_sur = s;
499 }
500
501 static void renderButton(int screen, bool focus, bool press, Window win,
502                          otk::Surface **sur, int butsize,
503                          const otk::PixmapMask *mask, int xoffset, int yoffset,
504                          otk::Surface *bgsurface)
505 {
506   const otk::RenderStyle *style = otk::RenderStyle::style(screen);
507   const otk::RenderControl *control = otk::display->renderControl(screen);
508   otk::Surface *s = new otk::Surface(screen, otk::Size(butsize, butsize));
509
510   const otk::RenderTexture *tx = (focus ?
511                                   (press ?
512                                    style->buttonPressFocusBackground() :
513                                    style->buttonUnpressFocusBackground()) :
514                                   (press ?
515                                    style->buttonPressUnfocusBackground() :
516                                    style->buttonUnpressUnfocusBackground()));
517   const otk::RenderColor *maskcolor = (focus ?
518                                        style->buttonFocusColor() :
519                                        style->buttonUnfocusColor());
520   if (tx->parentRelative()) {
521     otk::pixel32 *dest = s->pixelData(), *src;
522     int w = bgsurface->size().width();
523   
524     src = bgsurface->pixelData() + w * yoffset + xoffset;
525     
526     // get the background under the button
527     for (int y = 0; y < butsize; ++y, src += w - butsize)
528       for (int x = 0; x < butsize; ++x, ++dest, ++src)
529         *dest = *src;
530     control->drawImage(*s, 0, 0, 0); // no image but draw the new background
531   } else
532     control->drawBackground(*s, *tx);
533   control->drawMask(*s, *maskcolor, *mask);
534
535   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
536   XClearWindow(**otk::display, win);
537   if (*sur) delete *sur;
538   s->freePixelData();
539   *sur = s;
540 }
541
542 void Frame::renderMax()
543 {
544   if (!(_decorations & Client::Decor_Maximize)) return;
545   bool press = _max_press || _client->maxVert() || _client->maxHorz();
546   renderButton(_client->screen(), _client->focused(), press, _max,
547                &_max_sur, geom.button_size,
548                otk::RenderStyle::style(_client->screen())->maximizeMask(),
549                geom.max_x, (geom.bevel + 1), _title_sur);
550 }
551
552 void Frame::renderDesk()
553 {
554   if (!(_decorations & Client::Decor_AllDesktops)) return;
555   bool press = _desk_press || _client->desktop() == 0xffffffff;
556   renderButton(_client->screen(), _client->focused(), press, _desk,
557                &_desk_sur, geom.button_size,
558                otk::RenderStyle::style(_client->screen())->alldesktopsMask(),
559                geom.desktop_x, (geom.bevel + 1), _title_sur);
560 }
561
562 void Frame::renderIconify()
563 {
564   if (!(_decorations & Client::Decor_Iconify)) return;
565   renderButton(_client->screen(), _client->focused(), _iconify_press, _iconify,
566                &_iconify_sur, geom.button_size,
567                otk::RenderStyle::style(_client->screen())->iconifyMask(),
568                geom.iconify_x, (geom.bevel + 1), _title_sur);
569 }
570
571 void Frame::renderClose()
572 {
573   if (!(_decorations & Client::Decor_Close)) return;
574   renderButton(_client->screen(), _client->focused(), _close_press, _close,
575                &_close_sur, geom.button_size,
576                otk::RenderStyle::style(_client->screen())->closeMask(),
577                geom.close_x, (geom.bevel + 1), _title_sur);
578 }
579
580 void Frame::renderIcon()
581 {
582   if (!(_decorations & Client::Decor_Icon)) return;
583   const int screen = _client->screen();
584   const otk::RenderControl *control = otk::display->renderControl(screen);
585
586   otk::Surface *s = new otk::Surface(screen, otk::Size(geom.button_size,
587                                                        geom.button_size));
588   otk::pixel32 *dest = s->pixelData(), *src;
589   int w = _title_sur->size().width();
590   
591   src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.icon_x;
592   
593   // get the background under the icon button
594   for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
595     for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
596       *dest = *src;
597   // draw the icon over it
598   const Icon *icon = _client->icon(otk::Size(geom.button_size,
599                                              geom.button_size));
600   control->drawImage(*s, icon->w, icon->h, icon->data);
601   if (!icon->data) {
602     Pixmap p = _client->pixmapIcon(), m = _client->pixmapIconMask();
603     if (p != None)
604       control->drawImage(*s, p, m);
605   }
606
607   XSetWindowBackgroundPixmap(**otk::display, _icon, s->pixmap());
608   XClearWindow(**otk::display, _icon);
609   if (_icon_sur) delete _icon_sur;
610   _icon_sur = s;
611 }
612
613 void Frame::layoutTitle()
614 {
615   // figure out whats being shown, and the width of the label
616   geom.label_width = geom.width - geom.bevel * 2;
617   bool n, d, i, t, m ,c;
618   n = d = i = t = m = c = false;
619   for (const char *l = _layout.c_str(); *l; ++l) {
620     switch (*l) {
621     case 'n':
622     case 'N':
623       if (!(_decorations & Client::Decor_Icon)) break;
624       n = true;
625       geom.label_width -= geom.button_size + geom.bevel;
626       break;
627     case 'd':
628     case 'D':
629       if (!(_decorations & Client::Decor_AllDesktops)) break;
630       d = true;
631       geom.label_width -= geom.button_size + geom.bevel;
632       break;
633     case 'i':
634     case 'I':
635       if (!(_decorations & Client::Decor_Iconify)) break;
636       i = true;
637       geom.label_width -= geom.button_size + geom.bevel;
638       break;
639     case 't':
640     case 'T':
641       t = true;
642       break;
643     case 'm':
644     case 'M':
645       if (!(_decorations & Client::Decor_Maximize)) break;
646       m = true;
647       geom.label_width -= geom.button_size + geom.bevel;
648       break;
649     case 'c':
650     case 'C':
651       if (!(_decorations & Client::Decor_Close)) break;
652       c = true;
653       geom.label_width -= geom.button_size + geom.bevel;
654       break;
655     }
656   }
657   if (geom.label_width < 1) geom.label_width = 1;
658
659   XResizeWindow(**otk::display, _label, geom.label_width, geom.font_height);
660   
661   if (!n) {
662     _decorations &= ~Client::Decor_Icon;
663     XUnmapWindow(**otk::display, _icon);
664   }
665   if (!d) {
666     _decorations &= ~Client::Decor_AllDesktops;
667     XUnmapWindow(**otk::display, _desk);
668   }
669   if (!i) {
670     _decorations &= ~Client::Decor_Iconify;
671     XUnmapWindow(**otk::display, _iconify);
672   }
673   if (!t)
674     XUnmapWindow(**otk::display, _label);
675   if (!m) {
676     _decorations &= ~Client::Decor_Maximize;
677     XUnmapWindow(**otk::display, _max);
678   }
679   if (!c) {
680     _decorations &= ~Client::Decor_Close;
681     XUnmapWindow(**otk::display, _close);
682   }
683
684   int x = geom.bevel;
685   for (const char *lc = _layout.c_str(); *lc; ++lc) {
686     switch (*lc) {
687     case 'n':
688     case 'N':
689       if (!n) break;
690       geom.icon_x = x;
691       XMapWindow(**otk::display, _icon);
692       XMoveWindow(**otk::display, _icon, x, geom.bevel + 1);
693       x += geom.button_size + geom.bevel;
694       break;
695     case 'd':
696     case 'D':
697       if (!d) break;
698       geom.desktop_x = x;
699       XMapWindow(**otk::display, _desk);
700       XMoveWindow(**otk::display, _desk, x, geom.bevel + 1);
701       x += geom.button_size + geom.bevel;
702       break;
703     case 'i':
704     case 'I':
705       if (!i) break;
706       geom.iconify_x = x;
707       XMapWindow(**otk::display, _iconify);
708       XMoveWindow(**otk::display, _iconify, x, geom.bevel + 1);
709       x += geom.button_size + geom.bevel;
710       break;
711     case 't':
712     case 'T':
713       if (!t) break;
714       geom.title_x = x;
715       XMapWindow(**otk::display, _label);
716       XMoveWindow(**otk::display, _label, x, geom.bevel);
717       x += geom.label_width + geom.bevel;
718       break;
719     case 'm':
720     case 'M':
721       if (!m) break;
722       geom.max_x = x;
723       XMapWindow(**otk::display, _max);
724       XMoveWindow(**otk::display, _max, x, geom.bevel + 1);
725       x += geom.button_size + geom.bevel;
726       break;
727     case 'c':
728     case 'C':
729       if (!c) break;
730       geom.close_x = x;
731       XMapWindow(**otk::display, _close);
732       XMoveWindow(**otk::display, _close, x, geom.bevel + 1);
733       x += geom.button_size + geom.bevel;
734       break;
735     }
736   }
737 }
738
739 void Frame::adjustPosition()
740 {
741   int x, y;
742   x = _client->area().x();
743   y = _client->area().y();
744   clientGravity(x, y);
745   XMoveWindow(**otk::display, _frame, x, y);
746   _area = otk::Rect(otk::Point(x, y), _area.size());
747 }
748
749
750 void Frame::adjustShape()
751 {
752 #ifdef SHAPE
753   if (!_client->shaped()) {
754     // clear the shape on the frame window
755     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
756                       _innersize.left,
757                       _innersize.top,
758                       None, ShapeSet);
759   } else {
760     // make the frame's shape match the clients
761     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
762                        _innersize.left,
763                        _innersize.top,
764                        _client->window(), ShapeBounding, ShapeSet);
765
766     int num = 0;
767     XRectangle xrect[2];
768
769     if (_decorations & Client::Decor_Titlebar) {
770       xrect[0].x = -geom.bevel;
771       xrect[0].y = -geom.bevel;
772       xrect[0].width = geom.width + geom.bwidth * 2;
773       xrect[0].height = geom.title_height() + geom.bwidth * 2;
774       ++num;
775     }
776
777     if (_decorations & Client::Decor_Handle) {
778       xrect[1].x = -geom.bevel;
779       xrect[1].y = geom.handle_y;
780       xrect[1].width = geom.width + geom.bwidth * 2;
781       xrect[1].height = geom.handle_height + geom.bwidth * 2;
782       ++num;
783     }
784
785     XShapeCombineRectangles(**otk::display, _frame,
786                             ShapeBounding, 0, 0, xrect, num,
787                             ShapeUnion, Unsorted);
788   }
789 #endif // SHAPE
790 }
791
792 void Frame::adjustState()
793 {
794   renderDesk();
795   renderMax();
796 }
797
798 void Frame::adjustIcon()
799 {
800   renderIcon();
801 }
802
803 void Frame::grabClient()
804 {
805   // reparent the client to the frame
806   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
807   /*
808     When reparenting the client window, it is usually not mapped yet, since
809     this occurs from a MapRequest. However, in the case where Openbox is
810     starting up, the window is already mapped, so we'll see unmap events for
811     it. There are 2 unmap events generated that we see, one with the 'event'
812     member set the root window, and one set to the client, but both get handled
813     and need to be ignored.
814   */
815   if (openbox->state() == Openbox::State_Starting)
816     _client->ignore_unmaps += 2;
817
818   // select the event mask on the client's parent (to receive config/map req's)
819   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
820
821   // map the client so it maps when the frame does
822   XMapWindow(**otk::display, _client->window());
823
824   adjustSize();
825   adjustPosition();
826 }
827
828
829 void Frame::releaseClient()
830 {
831   XEvent ev;
832
833   // check if the app has already reparented its window away
834   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
835                              ReparentNotify, &ev)) {
836     XPutBackEvent(**otk::display, &ev);
837     // re-map the window since the unmanaging process unmaps it
838     XMapWindow(**otk::display, _client->window());  
839   } else {
840     // according to the ICCCM - if the client doesn't reparent itself, then we
841     // will reparent the window to root for them
842     XReparentWindow(**otk::display, _client->window(),
843                     otk::display->screenInfo(_client->screen())->rootWindow(),
844                     _client->area().x(), _client->area().y());
845   }
846 }
847
848
849 void Frame::clientGravity(int &x, int &y)
850 {
851   // horizontal
852   switch (_client->gravity()) {
853   default:
854   case NorthWestGravity:
855   case SouthWestGravity:
856   case WestGravity:
857     break;
858
859   case NorthGravity:
860   case SouthGravity:
861   case CenterGravity:
862     x -= (_size.left + _size.right) / 2;
863     break;
864
865   case NorthEastGravity:
866   case SouthEastGravity:
867   case EastGravity:
868     x -= _size.left + _size.right;
869     break;
870
871   case ForgetGravity:
872   case StaticGravity:
873     x -= _size.left;
874     break;
875   }
876
877   // vertical
878   switch (_client->gravity()) {
879   default:
880   case NorthWestGravity:
881   case NorthEastGravity:
882   case NorthGravity:
883     break;
884
885   case CenterGravity:
886   case EastGravity:
887   case WestGravity:
888     y -= (_size.top + _size.bottom) / 2;
889     break;
890
891   case SouthWestGravity:
892   case SouthEastGravity:
893   case SouthGravity:
894     y -= _size.top + _size.bottom;
895     break;
896
897   case ForgetGravity:
898   case StaticGravity:
899     y -= _size.top;
900     break;
901   }
902 }
903
904
905 void Frame::frameGravity(int &x, int &y)
906 {
907   // horizontal
908   switch (_client->gravity()) {
909   default:
910   case NorthWestGravity:
911   case WestGravity:
912   case SouthWestGravity:
913     break;
914   case NorthGravity:
915   case CenterGravity:
916   case SouthGravity:
917     x += (_size.left + _size.right) / 2;
918     break;
919   case NorthEastGravity:
920   case EastGravity:
921   case SouthEastGravity:
922     x += _size.left + _size.right;
923     break;
924   case StaticGravity:
925   case ForgetGravity:
926     x += _size.left;
927     break;
928   }
929
930   // vertical
931   switch (_client->gravity()) {
932   default:
933   case NorthWestGravity:
934   case WestGravity:
935   case SouthWestGravity:
936     break;
937   case NorthGravity:
938   case CenterGravity:
939   case SouthGravity:
940     y += (_size.top + _size.bottom) / 2;
941     break;
942   case NorthEastGravity:
943   case EastGravity:
944   case SouthEastGravity:
945     y += _size.top + _size.bottom;
946     break;
947   case StaticGravity:
948   case ForgetGravity:
949     y += _size.top;
950     break;
951   }
952 }
953
954
955 }