]> icculus.org git repositories - mikachu/openbox.git/blob - src/frame.cc
make labels' parent relative actually work :>
[mikachu/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     for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
453       for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
454         *dest = *src;
455     control->drawImage(*s, 0, 0, 0); // no image but draw the new background
456   } else
457     control->drawBackground(*s, *tx);
458
459   otk::ustring t = _client->title(); // the actual text to draw
460   int x = geom.bevel;                // x coord for the text
461
462   if (x * 2 < geom.label_width) {
463     // find a string that will fit inside the area for text
464     otk::ustring::size_type text_len = t.size();
465     int length;
466     int maxsize = geom.label_width - geom.bevel * 2;
467       
468     do {
469       t.resize(text_len);
470       length = font->measureString(t);// this returns an unsigned, so check < 0
471       if (length < 0) length = maxsize;// if the string's that long just adjust
472     } while (length > maxsize && text_len-- > 0);
473
474     // justify the text
475     switch (style->labelTextJustify()) {
476     case otk::RenderStyle::RightBottomJustify:
477       x += maxsize - length;
478       break;
479     case otk::RenderStyle::CenterJustify:
480       x += (maxsize - length) / 2;
481       break;
482     case otk::RenderStyle::LeftTopJustify:
483       break;
484     }
485  
486     if (text_len > 0)
487       control->drawString(*s, *font, x, 0,
488                           *(_client->focused() ? style->textFocusColor() :
489                             style->textUnfocusColor()), t);
490   }
491
492   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
493   XClearWindow(**otk::display, _label);
494   if (_label_sur) delete _label_sur;
495   s->freePixelData();
496   _label_sur = s;
497 }
498
499 static void renderButton(int screen, bool focus, bool press, Window win,
500                          otk::Surface **sur, int butsize,
501                          const otk::PixmapMask *mask, int xoffset, int yoffset,
502                          otk::Surface *bgsurface)
503 {
504   const otk::RenderStyle *style = otk::RenderStyle::style(screen);
505   const otk::RenderControl *control = otk::display->renderControl(screen);
506   otk::Surface *s = new otk::Surface(screen, otk::Size(butsize, butsize));
507
508   const otk::RenderTexture *tx = (focus ?
509                                   (press ?
510                                    style->buttonPressFocusBackground() :
511                                    style->buttonUnpressFocusBackground()) :
512                                   (press ?
513                                    style->buttonPressUnfocusBackground() :
514                                    style->buttonUnpressUnfocusBackground()));
515   const otk::RenderColor *maskcolor = (focus ?
516                                        style->buttonFocusColor() :
517                                        style->buttonUnfocusColor());
518   if (tx->parentRelative()) {
519     otk::pixel32 *dest = s->pixelData(), *src;
520     int w = bgsurface->size().width();
521   
522     src = bgsurface->pixelData() + w * yoffset + xoffset;
523     
524     // get the background under the button
525     for (int y = 0; y < butsize; ++y, src += w - butsize)
526       for (int x = 0; x < butsize; ++x, ++dest, ++src)
527         *dest = *src;
528     control->drawImage(*s, 0, 0, 0); // no image but draw the new background
529   } else
530     control->drawBackground(*s, *tx);
531   control->drawMask(*s, *maskcolor, *mask);
532
533   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
534   XClearWindow(**otk::display, win);
535   if (*sur) delete *sur;
536   s->freePixelData();
537   *sur = s;
538 }
539
540 void Frame::renderMax()
541 {
542   if (!(_decorations & Client::Decor_Maximize)) return;
543   bool press = _max_press || _client->maxVert() || _client->maxHorz();
544   renderButton(_client->screen(), _client->focused(), press, _max,
545                &_max_sur, geom.button_size,
546                otk::RenderStyle::style(_client->screen())->maximizeMask(),
547                geom.max_x, (geom.bevel + 1), _title_sur);
548 }
549
550 void Frame::renderDesk()
551 {
552   if (!(_decorations & Client::Decor_AllDesktops)) return;
553   bool press = _desk_press || _client->desktop() == 0xffffffff;
554   renderButton(_client->screen(), _client->focused(), press, _desk,
555                &_desk_sur, geom.button_size,
556                otk::RenderStyle::style(_client->screen())->alldesktopsMask(),
557                geom.desktop_x, (geom.bevel + 1), _title_sur);
558 }
559
560 void Frame::renderIconify()
561 {
562   if (!(_decorations & Client::Decor_Iconify)) return;
563   renderButton(_client->screen(), _client->focused(), _iconify_press, _iconify,
564                &_iconify_sur, geom.button_size,
565                otk::RenderStyle::style(_client->screen())->iconifyMask(),
566                geom.iconify_x, (geom.bevel + 1), _title_sur);
567 }
568
569 void Frame::renderClose()
570 {
571   if (!(_decorations & Client::Decor_Close)) return;
572   renderButton(_client->screen(), _client->focused(), _close_press, _close,
573                &_close_sur, geom.button_size,
574                otk::RenderStyle::style(_client->screen())->closeMask(),
575                geom.close_x, (geom.bevel + 1), _title_sur);
576 }
577
578 void Frame::renderIcon()
579 {
580   if (!(_decorations & Client::Decor_Icon)) return;
581   const int screen = _client->screen();
582   const otk::RenderControl *control = otk::display->renderControl(screen);
583
584   otk::Surface *s = new otk::Surface(screen, otk::Size(geom.button_size,
585                                                        geom.button_size));
586   otk::pixel32 *dest = s->pixelData(), *src;
587   int w = _title_sur->size().width();
588   
589   src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.icon_x;
590   
591   // get the background under the icon button
592   for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
593     for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
594       *dest = *src;
595   // draw the icon over it
596   const Icon *icon = _client->icon(otk::Size(geom.button_size,
597                                              geom.button_size));
598   control->drawImage(*s, icon->w, icon->h, icon->data);
599   if (!icon->data) {
600     Pixmap p = _client->pixmapIcon(), m = _client->pixmapIconMask();
601     if (p != None)
602       control->drawImage(*s, p, m);
603   }
604
605   XSetWindowBackgroundPixmap(**otk::display, _icon, s->pixmap());
606   XClearWindow(**otk::display, _icon);
607   if (_icon_sur) delete _icon_sur;
608   _icon_sur = s;
609 }
610
611 void Frame::layoutTitle()
612 {
613   // figure out whats being shown, and the width of the label
614   geom.label_width = geom.width - geom.bevel * 2;
615   bool n, d, i, t, m ,c;
616   n = d = i = t = m = c = false;
617   for (const char *l = _layout.c_str(); *l; ++l) {
618     switch (*l) {
619     case 'n':
620     case 'N':
621       if (!(_decorations & Client::Decor_Icon)) break;
622       n = true;
623       geom.label_width -= geom.button_size + geom.bevel;
624       break;
625     case 'd':
626     case 'D':
627       if (!(_decorations & Client::Decor_AllDesktops)) break;
628       d = true;
629       geom.label_width -= geom.button_size + geom.bevel;
630       break;
631     case 'i':
632     case 'I':
633       if (!(_decorations & Client::Decor_Iconify)) break;
634       i = true;
635       geom.label_width -= geom.button_size + geom.bevel;
636       break;
637     case 't':
638     case 'T':
639       t = true;
640       break;
641     case 'm':
642     case 'M':
643       if (!(_decorations & Client::Decor_Maximize)) break;
644       m = true;
645       geom.label_width -= geom.button_size + geom.bevel;
646       break;
647     case 'c':
648     case 'C':
649       if (!(_decorations & Client::Decor_Close)) break;
650       c = true;
651       geom.label_width -= geom.button_size + geom.bevel;
652       break;
653     }
654   }
655   if (geom.label_width < 1) geom.label_width = 1;
656
657   XResizeWindow(**otk::display, _label, geom.label_width, geom.font_height);
658   
659   if (!n) {
660     _decorations &= ~Client::Decor_Icon;
661     XUnmapWindow(**otk::display, _icon);
662   }
663   if (!d) {
664     _decorations &= ~Client::Decor_AllDesktops;
665     XUnmapWindow(**otk::display, _desk);
666   }
667   if (!i) {
668     _decorations &= ~Client::Decor_Iconify;
669     XUnmapWindow(**otk::display, _iconify);
670   }
671   if (!t)
672     XUnmapWindow(**otk::display, _label);
673   if (!m) {
674     _decorations &= ~Client::Decor_Maximize;
675     XUnmapWindow(**otk::display, _max);
676   }
677   if (!c) {
678     _decorations &= ~Client::Decor_Close;
679     XUnmapWindow(**otk::display, _close);
680   }
681
682   int x = geom.bevel;
683   for (const char *lc = _layout.c_str(); *lc; ++lc) {
684     switch (*lc) {
685     case 'n':
686     case 'N':
687       if (!n) break;
688       geom.icon_x = x;
689       XMapWindow(**otk::display, _icon);
690       XMoveWindow(**otk::display, _icon, x, geom.bevel + 1);
691       x += geom.button_size + geom.bevel;
692       break;
693     case 'd':
694     case 'D':
695       if (!d) break;
696       geom.desktop_x = x;
697       XMapWindow(**otk::display, _desk);
698       XMoveWindow(**otk::display, _desk, x, geom.bevel + 1);
699       x += geom.button_size + geom.bevel;
700       break;
701     case 'i':
702     case 'I':
703       if (!i) break;
704       geom.iconify_x = x;
705       XMapWindow(**otk::display, _iconify);
706       XMoveWindow(**otk::display, _iconify, x, geom.bevel + 1);
707       x += geom.button_size + geom.bevel;
708       break;
709     case 't':
710     case 'T':
711       if (!t) break;
712       geom.title_x = x;
713       XMapWindow(**otk::display, _label);
714       XMoveWindow(**otk::display, _label, x, geom.bevel);
715       x += geom.label_width + geom.bevel;
716       break;
717     case 'm':
718     case 'M':
719       if (!m) break;
720       geom.max_x = x;
721       XMapWindow(**otk::display, _max);
722       XMoveWindow(**otk::display, _max, x, geom.bevel + 1);
723       x += geom.button_size + geom.bevel;
724       break;
725     case 'c':
726     case 'C':
727       if (!c) break;
728       geom.close_x = x;
729       XMapWindow(**otk::display, _close);
730       XMoveWindow(**otk::display, _close, x, geom.bevel + 1);
731       x += geom.button_size + geom.bevel;
732       break;
733     }
734   }
735 }
736
737 void Frame::adjustPosition()
738 {
739   int x, y;
740   x = _client->area().x();
741   y = _client->area().y();
742   clientGravity(x, y);
743   XMoveWindow(**otk::display, _frame, x, y);
744   _area = otk::Rect(otk::Point(x, y), _area.size());
745 }
746
747
748 void Frame::adjustShape()
749 {
750 #ifdef SHAPE
751   if (!_client->shaped()) {
752     // clear the shape on the frame window
753     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
754                       _innersize.left,
755                       _innersize.top,
756                       None, ShapeSet);
757   } else {
758     // make the frame's shape match the clients
759     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
760                        _innersize.left,
761                        _innersize.top,
762                        _client->window(), ShapeBounding, ShapeSet);
763
764     int num = 0;
765     XRectangle xrect[2];
766
767     if (_decorations & Client::Decor_Titlebar) {
768       xrect[0].x = -geom.bevel;
769       xrect[0].y = -geom.bevel;
770       xrect[0].width = geom.width + geom.bwidth * 2;
771       xrect[0].height = geom.title_height() + geom.bwidth * 2;
772       ++num;
773     }
774
775     if (_decorations & Client::Decor_Handle) {
776       xrect[1].x = -geom.bevel;
777       xrect[1].y = geom.handle_y;
778       xrect[1].width = geom.width + geom.bwidth * 2;
779       xrect[1].height = geom.handle_height + geom.bwidth * 2;
780       ++num;
781     }
782
783     XShapeCombineRectangles(**otk::display, _frame,
784                             ShapeBounding, 0, 0, xrect, num,
785                             ShapeUnion, Unsorted);
786   }
787 #endif // SHAPE
788 }
789
790 void Frame::adjustState()
791 {
792   renderDesk();
793   renderMax();
794 }
795
796 void Frame::adjustIcon()
797 {
798   renderIcon();
799 }
800
801 void Frame::grabClient()
802 {
803   // reparent the client to the frame
804   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
805   /*
806     When reparenting the client window, it is usually not mapped yet, since
807     this occurs from a MapRequest. However, in the case where Openbox is
808     starting up, the window is already mapped, so we'll see unmap events for
809     it. There are 2 unmap events generated that we see, one with the 'event'
810     member set the root window, and one set to the client, but both get handled
811     and need to be ignored.
812   */
813   if (openbox->state() == Openbox::State_Starting)
814     _client->ignore_unmaps += 2;
815
816   // select the event mask on the client's parent (to receive config/map req's)
817   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
818
819   // map the client so it maps when the frame does
820   XMapWindow(**otk::display, _client->window());
821
822   adjustSize();
823   adjustPosition();
824 }
825
826
827 void Frame::releaseClient()
828 {
829   XEvent ev;
830
831   // check if the app has already reparented its window away
832   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
833                              ReparentNotify, &ev)) {
834     XPutBackEvent(**otk::display, &ev);
835     // re-map the window since the unmanaging process unmaps it
836     XMapWindow(**otk::display, _client->window());  
837   } else {
838     // according to the ICCCM - if the client doesn't reparent itself, then we
839     // will reparent the window to root for them
840     XReparentWindow(**otk::display, _client->window(),
841                     otk::display->screenInfo(_client->screen())->rootWindow(),
842                     _client->area().x(), _client->area().y());
843   }
844 }
845
846
847 void Frame::clientGravity(int &x, int &y)
848 {
849   // horizontal
850   switch (_client->gravity()) {
851   default:
852   case NorthWestGravity:
853   case SouthWestGravity:
854   case WestGravity:
855     break;
856
857   case NorthGravity:
858   case SouthGravity:
859   case CenterGravity:
860     x -= (_size.left + _size.right) / 2;
861     break;
862
863   case NorthEastGravity:
864   case SouthEastGravity:
865   case EastGravity:
866     x -= _size.left + _size.right;
867     break;
868
869   case ForgetGravity:
870   case StaticGravity:
871     x -= _size.left;
872     break;
873   }
874
875   // vertical
876   switch (_client->gravity()) {
877   default:
878   case NorthWestGravity:
879   case NorthEastGravity:
880   case NorthGravity:
881     break;
882
883   case CenterGravity:
884   case EastGravity:
885   case WestGravity:
886     y -= (_size.top + _size.bottom) / 2;
887     break;
888
889   case SouthWestGravity:
890   case SouthEastGravity:
891   case SouthGravity:
892     y -= _size.top + _size.bottom;
893     break;
894
895   case ForgetGravity:
896   case StaticGravity:
897     y -= _size.top;
898     break;
899   }
900 }
901
902
903 void Frame::frameGravity(int &x, int &y)
904 {
905   // horizontal
906   switch (_client->gravity()) {
907   default:
908   case NorthWestGravity:
909   case WestGravity:
910   case SouthWestGravity:
911     break;
912   case NorthGravity:
913   case CenterGravity:
914   case SouthGravity:
915     x += (_size.left + _size.right) / 2;
916     break;
917   case NorthEastGravity:
918   case EastGravity:
919   case SouthEastGravity:
920     x += _size.left + _size.right;
921     break;
922   case StaticGravity:
923   case ForgetGravity:
924     x += _size.left;
925     break;
926   }
927
928   // vertical
929   switch (_client->gravity()) {
930   default:
931   case NorthWestGravity:
932   case WestGravity:
933   case SouthWestGravity:
934     break;
935   case NorthGravity:
936   case CenterGravity:
937   case SouthGravity:
938     y += (_size.top + _size.bottom) / 2;
939     break;
940   case NorthEastGravity:
941   case EastGravity:
942   case SouthEastGravity:
943     y += _size.top + _size.bottom;
944     break;
945   case StaticGravity:
946   case ForgetGravity:
947     y += _size.top;
948     break;
949   }
950 }
951
952
953 }