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