still draw the background when no text will fit
[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   otk::display->renderControl(screen)->drawBackground(*s, texture);
305   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
306   XClearWindow(**otk::display, win);
307   if (*surface) delete *surface;
308   if (freedata) s->freePixelData();
309   *surface = s;
310 }
311
312 void Frame::adjustSize()
313 {
314   _decorations = _client->decorations();
315   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
316
317   if (_decorations & Client::Decor_Border) {
318     geom.bwidth = style->frameBorderWidth();
319     geom.cbwidth = style->clientBorderWidth();
320   } else {
321     geom.bwidth = geom.cbwidth = 0;
322   }
323   _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
324     geom.cbwidth;
325   geom.width = _client->area().width() + geom.cbwidth * 2;
326   assert(geom.width > 0);
327
328   // set border widths
329   XSetWindowBorderWidth(**otk::display, _plate, geom.cbwidth);
330   XSetWindowBorderWidth(**otk::display, _frame, geom.bwidth);
331   XSetWindowBorderWidth(**otk::display, _title, geom.bwidth);
332   XSetWindowBorderWidth(**otk::display, _handle, geom.bwidth);
333   XSetWindowBorderWidth(**otk::display, _lgrip, geom.bwidth);
334   XSetWindowBorderWidth(**otk::display, _rgrip, geom.bwidth);
335   
336   // position/size and map/unmap all the windows
337
338   if (_decorations & Client::Decor_Titlebar) {
339     XMoveResizeWindow(**otk::display, _title, -geom.bwidth, -geom.bwidth,
340                       geom.width, geom.title_height());
341     _innersize.top += geom.title_height() + geom.bwidth;
342     XMapWindow(**otk::display, _title);
343
344     // layout the title bar elements
345     layoutTitle();
346   } else {
347     XUnmapWindow(**otk::display, _title);
348     // make all the titlebar stuff not render
349     _decorations &= ~(Client::Decor_Icon | Client::Decor_Iconify |
350                       Client::Decor_Maximize | Client::Decor_Close |
351                       Client::Decor_AllDesktops);
352   }
353
354   if (_decorations & Client::Decor_Handle) {
355     geom.handle_y = _innersize.top + _client->area().height() + geom.cbwidth;
356     XMoveResizeWindow(**otk::display, _handle, -geom.bwidth, geom.handle_y,
357                       geom.width, geom.handle_height);
358     XMoveWindow(**otk::display, _lgrip, -geom.bwidth, -geom.bwidth);
359     XMoveWindow(**otk::display, _rgrip,
360                 -geom.bwidth + geom.width - geom.grip_width(),
361                 -geom.bwidth);
362     _innersize.bottom += geom.handle_height + geom.bwidth;
363     XMapWindow(**otk::display, _handle);
364   } else
365     XUnmapWindow(**otk::display, _handle);
366   
367   XResizeWindow(**otk::display, _frame, geom.width,
368                 (_client->shaded() ? geom.title_height() :
369                  _innersize.top + _innersize.bottom +
370                  _client->area().height()));
371
372   // do this in two steps because clients whose gravity is set to
373   // 'Static' don't end up getting moved at all with an XMoveResizeWindow
374   XMoveWindow(**otk::display, _plate, _innersize.left - geom.cbwidth,
375               _innersize.top - geom.cbwidth);
376   XResizeWindow(**otk::display, _plate, _client->area().width(),
377                 _client->area().height());
378
379   _size.left   = _innersize.left + geom.bwidth;
380   _size.right  = _innersize.right + geom.bwidth;
381   _size.top    = _innersize.top + geom.bwidth;
382   _size.bottom = _innersize.bottom + geom.bwidth;
383
384   _area = otk::Rect(_area.position(), otk::Size(_client->area().width() +
385                                                 _size.left + _size.right,
386                                                 _client->area().height() +
387                                                 _size.top + _size.bottom));
388
389   // render all the elements
390   int screen = _client->screen();
391   bool focus = _client->focused();
392   if (_decorations & Client::Decor_Titlebar) {
393     render(screen, otk::Size(geom.width, geom.title_height()), _title,
394            &_title_sur, *(focus ? style->titlebarFocusBackground() :
395                           style->titlebarUnfocusBackground()), false);
396     
397     renderLabel();
398     renderMax();
399     renderDesk();
400     renderIconify();
401     renderIcon();
402     renderClose();
403   }
404
405   if (_decorations & Client::Decor_Handle) {
406     render(screen, otk::Size(geom.width, geom.handle_height), _handle,
407            &_handle_sur, *(focus ? style->handleFocusBackground() :
408                            style->handleUnfocusBackground()));
409     render(screen, otk::Size(geom.grip_width(), geom.handle_height), _lgrip,
410            &_grip_sur, *(focus ? style->gripFocusBackground() :
411                          style->gripUnfocusBackground()));
412     XSetWindowBackgroundPixmap(**otk::display, _rgrip, _grip_sur->pixmap());
413     XClearWindow(**otk::display, _rgrip);
414   }
415
416   XSetWindowBorder(**otk::display, _plate,
417                    focus ? style->clientBorderFocusColor()->pixel() :
418                    style->clientBorderUnfocusColor()->pixel());
419   
420   adjustShape();
421 }
422
423 void Frame::renderLabel()
424 {
425   const otk::RenderStyle *style = otk::RenderStyle::style(_client->screen());
426   const otk::RenderControl *control =
427     otk::display->renderControl(_client->screen());
428   const otk::Font *font = style->labelFont();
429
430   otk::Surface *s = new otk::Surface(_client->screen(),
431                                      otk::Size(geom.label_width,
432                                                geom.label_height()));
433   control->drawBackground(*s, *(_client->focused() ?
434                                 style->labelFocusBackground() :
435                                 style->labelUnfocusBackground()));
436
437   otk::ustring t = _client->title(); // the actual text to draw
438   int x = geom.bevel;                // x coord for the text
439
440   if (x * 2 < geom.label_width) {
441     // find a string that will fit inside the area for text
442     otk::ustring::size_type text_len = t.size();
443     int length;
444     int maxsize = geom.label_width - geom.bevel * 2;
445       
446     do {
447       t.resize(text_len);
448       length = font->measureString(t);// this returns an unsigned, so check < 0
449       if (length < 0) length = maxsize;// if the string's that long just adjust
450     } while (length > maxsize && text_len-- > 0);
451
452     // justify the text
453     switch (style->labelTextJustify()) {
454     case otk::RenderStyle::RightBottomJustify:
455       x += maxsize - length;
456       break;
457     case otk::RenderStyle::CenterJustify:
458       x += (maxsize - length) / 2;
459       break;
460     case otk::RenderStyle::LeftTopJustify:
461       break;
462     }
463  
464     if (text_len > 0)
465       control->drawString(*s, *font, x, 0,
466                           *(_client->focused() ? style->textFocusColor() :
467                             style->textUnfocusColor()), t);
468   }
469
470   XSetWindowBackgroundPixmap(**otk::display, _label, s->pixmap());
471   XClearWindow(**otk::display, _label);
472   if (_label_sur) delete _label_sur;
473   s->freePixelData();
474   _label_sur = s;
475 }
476
477 static void renderButton(int screen, bool focus, bool press, Window win,
478                          otk::Surface **sur, int butsize,
479                          const otk::PixmapMask *mask)
480 {
481   const otk::RenderStyle *style = otk::RenderStyle::style(screen);
482   const otk::RenderControl *control = otk::display->renderControl(screen);
483   otk::Surface *s = new otk::Surface(screen, otk::Size(butsize, butsize));
484
485   const otk::RenderTexture *tx = (focus ?
486                                   (press ?
487                                    style->buttonPressFocusBackground() :
488                                    style->buttonUnpressFocusBackground()) :
489                                   (press ?
490                                    style->buttonPressUnfocusBackground() :
491                                    style->buttonUnpressUnfocusBackground()));
492   const otk::RenderColor *maskcolor = (focus ?
493                                        style->buttonFocusColor() :
494                                        style->buttonUnfocusColor());
495   control->drawBackground(*s, *tx);
496   control->drawMask(*s, *maskcolor, *mask);
497
498   XSetWindowBackgroundPixmap(**otk::display, win, s->pixmap());
499   XClearWindow(**otk::display, win);
500   if (*sur) delete *sur;
501   s->freePixelData();
502   *sur = s;
503 }
504
505 void Frame::renderMax()
506 {
507   if (!(_decorations & Client::Decor_Maximize)) return;
508   bool press = _max_press || _client->maxVert() || _client->maxHorz();
509   renderButton(_client->screen(), _client->focused(), press, _max,
510                &_max_sur, geom.button_size,
511                otk::RenderStyle::style(_client->screen())->maximizeMask());
512 }
513
514 void Frame::renderDesk()
515 {
516   if (!(_decorations & Client::Decor_AllDesktops)) return;
517   bool press = _desk_press || _client->desktop() == 0xffffffff;
518   renderButton(_client->screen(), _client->focused(), press, _desk,
519                &_desk_sur, geom.button_size,
520                otk::RenderStyle::style(_client->screen())->alldesktopsMask());
521 }
522
523 void Frame::renderIconify()
524 {
525   if (!(_decorations & Client::Decor_Iconify)) return;
526   renderButton(_client->screen(), _client->focused(), _iconify_press, _iconify,
527                &_iconify_sur, geom.button_size,
528                otk::RenderStyle::style(_client->screen())->iconifyMask());
529 }
530
531 void Frame::renderClose()
532 {
533   if (!(_decorations & Client::Decor_Close)) return;
534   renderButton(_client->screen(), _client->focused(), _close_press, _close,
535                &_close_sur, geom.button_size,
536                otk::RenderStyle::style(_client->screen())->closeMask());
537 }
538
539 void Frame::renderIcon()
540 {
541   if (!(_decorations & Client::Decor_Icon)) return;
542   const int screen = _client->screen();
543   const otk::RenderControl *control = otk::display->renderControl(screen);
544
545   otk::Surface *s = new otk::Surface(screen, otk::Size(geom.button_size,
546                                                        geom.button_size));
547   otk::pixel32 *dest = s->pixelData(), *src;
548   int w = _title_sur->size().width();
549   
550   src = _title_sur->pixelData() + w * (geom.bevel + 1) + geom.icon_x;
551   
552   // get the background under the icon button
553   for (int y = 0; y < geom.button_size; ++y, src += w - geom.button_size)
554     for (int x = 0; x < geom.button_size; ++x, ++dest, ++src)
555       *dest = *src;
556   // draw the icon over it
557   const Icon *icon = _client->icon(otk::Size(geom.button_size,
558                                              geom.button_size));
559   control->drawImage(*s, icon->w, icon->h, icon->data);
560
561   XSetWindowBackgroundPixmap(**otk::display, _icon, s->pixmap());
562   XClearWindow(**otk::display, _icon);
563   if (_icon_sur) delete _icon_sur;
564   _icon_sur = s;
565 }
566
567 void Frame::layoutTitle()
568 {
569   // figure out whats being shown, and the width of the label
570   geom.label_width = geom.width - geom.bevel * 2;
571   bool n, d, i, t, m ,c;
572   n = d = i = t = m = c = false;
573   for (const char *l = _layout.c_str(); *l; ++l) {
574     switch (*l) {
575     case 'n':
576     case 'N':
577       if (!(_decorations & Client::Decor_Icon)) break;
578       n = true;
579       geom.label_width -= geom.button_size + geom.bevel;
580       break;
581     case 'd':
582     case 'D':
583       if (!(_decorations & Client::Decor_AllDesktops)) break;
584       d = true;
585       geom.label_width -= geom.button_size + geom.bevel;
586       break;
587     case 'i':
588     case 'I':
589       if (!(_decorations & Client::Decor_Iconify)) break;
590       i = true;
591       geom.label_width -= geom.button_size + geom.bevel;
592       break;
593     case 't':
594     case 'T':
595       t = true;
596       break;
597     case 'm':
598     case 'M':
599       if (!(_decorations & Client::Decor_Maximize)) break;
600       m = true;
601       geom.label_width -= geom.button_size + geom.bevel;
602       break;
603     case 'c':
604     case 'C':
605       if (!(_decorations & Client::Decor_Close)) break;
606       c = true;
607       geom.label_width -= geom.button_size + geom.bevel;
608       break;
609     }
610   }
611   if (geom.label_width < 1) geom.label_width = 1;
612
613   XResizeWindow(**otk::display, _label, geom.label_width, geom.font_height);
614   
615   if (!n) {
616     _decorations &= ~Client::Decor_Icon;
617     XUnmapWindow(**otk::display, _icon);
618   }
619   if (!d) {
620     _decorations &= ~Client::Decor_AllDesktops;
621     XUnmapWindow(**otk::display, _desk);
622   }
623   if (!i) {
624     _decorations &= ~Client::Decor_Iconify;
625     XUnmapWindow(**otk::display, _iconify);
626   }
627   if (!t)
628     XUnmapWindow(**otk::display, _label);
629   if (!m) {
630     _decorations &= ~Client::Decor_Maximize;
631     XUnmapWindow(**otk::display, _max);
632   }
633   if (!c) {
634     _decorations &= ~Client::Decor_Close;
635     XUnmapWindow(**otk::display, _close);
636   }
637
638   int x = geom.bevel;
639   for (const char *lc = _layout.c_str(); *lc; ++lc) {
640     switch (*lc) {
641     case 'n':
642     case 'N':
643       if (!n) break;
644       geom.icon_x = x;
645       XMapWindow(**otk::display, _icon);
646       XMoveWindow(**otk::display, _icon, x, geom.bevel + 1);
647       x += geom.button_size + geom.bevel;
648       break;
649     case 'd':
650     case 'D':
651       if (!d) break;
652       XMapWindow(**otk::display, _desk);
653       XMoveWindow(**otk::display, _desk, x, geom.bevel + 1);
654       x += geom.button_size + geom.bevel;
655       break;
656     case 'i':
657     case 'I':
658       if (!i) break;
659       XMapWindow(**otk::display, _iconify);
660       XMoveWindow(**otk::display, _iconify, x, geom.bevel + 1);
661       x += geom.button_size + geom.bevel;
662       break;
663     case 't':
664     case 'T':
665       if (!t) break;
666       XMapWindow(**otk::display, _label);
667       XMoveWindow(**otk::display, _label, x, geom.bevel);
668       x += geom.label_width + geom.bevel;
669       break;
670     case 'm':
671     case 'M':
672       if (!m) break;
673       XMapWindow(**otk::display, _max);
674       XMoveWindow(**otk::display, _max, x, geom.bevel + 1);
675       x += geom.button_size + geom.bevel;
676       break;
677     case 'c':
678     case 'C':
679       if (!c) break;
680       XMapWindow(**otk::display, _close);
681       XMoveWindow(**otk::display, _close, x, geom.bevel + 1);
682       x += geom.button_size + geom.bevel;
683       break;
684     }
685   }
686 }
687
688 void Frame::adjustPosition()
689 {
690   int x, y;
691   x = _client->area().x();
692   y = _client->area().y();
693   clientGravity(x, y);
694   XMoveWindow(**otk::display, _frame, x, y);
695   _area = otk::Rect(otk::Point(x, y), _area.size());
696 }
697
698
699 void Frame::adjustShape()
700 {
701 #ifdef SHAPE
702   if (!_client->shaped()) {
703     // clear the shape on the frame window
704     XShapeCombineMask(**otk::display, _frame, ShapeBounding,
705                       _innersize.left,
706                       _innersize.top,
707                       None, ShapeSet);
708   } else {
709     // make the frame's shape match the clients
710     XShapeCombineShape(**otk::display, _frame, ShapeBounding,
711                        _innersize.left,
712                        _innersize.top,
713                        _client->window(), ShapeBounding, ShapeSet);
714
715     int num = 0;
716     XRectangle xrect[2];
717
718     if (_decorations & Client::Decor_Titlebar) {
719       xrect[0].x = -geom.bevel;
720       xrect[0].y = -geom.bevel;
721       xrect[0].width = geom.width + geom.bwidth * 2;
722       xrect[0].height = geom.title_height() + geom.bwidth * 2;
723       ++num;
724     }
725
726     if (_decorations & Client::Decor_Handle) {
727       xrect[1].x = -geom.bevel;
728       xrect[1].y = geom.handle_y;
729       xrect[1].width = geom.width + geom.bwidth * 2;
730       xrect[1].height = geom.handle_height + geom.bwidth * 2;
731       ++num;
732     }
733
734     XShapeCombineRectangles(**otk::display, _frame,
735                             ShapeBounding, 0, 0, xrect, num,
736                             ShapeUnion, Unsorted);
737   }
738 #endif // SHAPE
739 }
740
741 void Frame::adjustState()
742 {
743   renderDesk();
744   renderMax();
745 }
746
747 void Frame::adjustIcon()
748 {
749   renderIcon();
750 }
751
752 void Frame::grabClient()
753 {
754   // reparent the client to the frame
755   XReparentWindow(**otk::display, _client->window(), _plate, 0, 0);
756   /*
757     When reparenting the client window, it is usually not mapped yet, since
758     this occurs from a MapRequest. However, in the case where Openbox is
759     starting up, the window is already mapped, so we'll see unmap events for
760     it. There are 2 unmap events generated that we see, one with the 'event'
761     member set the root window, and one set to the client, but both get handled
762     and need to be ignored.
763   */
764   if (openbox->state() == Openbox::State_Starting)
765     _client->ignore_unmaps += 2;
766
767   // select the event mask on the client's parent (to receive config/map req's)
768   XSelectInput(**otk::display, _plate, SubstructureRedirectMask);
769
770   // map the client so it maps when the frame does
771   XMapWindow(**otk::display, _client->window());
772
773   adjustSize();
774   adjustPosition();
775 }
776
777
778 void Frame::releaseClient()
779 {
780   XEvent ev;
781
782   // check if the app has already reparented its window away
783   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
784                              ReparentNotify, &ev)) {
785     XPutBackEvent(**otk::display, &ev);
786     // re-map the window since the unmanaging process unmaps it
787     XMapWindow(**otk::display, _client->window());  
788   } else {
789     // according to the ICCCM - if the client doesn't reparent itself, then we
790     // will reparent the window to root for them
791     XReparentWindow(**otk::display, _client->window(),
792                     otk::display->screenInfo(_client->screen())->rootWindow(),
793                     _client->area().x(), _client->area().y());
794   }
795 }
796
797
798 void Frame::clientGravity(int &x, int &y)
799 {
800   // horizontal
801   switch (_client->gravity()) {
802   default:
803   case NorthWestGravity:
804   case SouthWestGravity:
805   case WestGravity:
806     break;
807
808   case NorthGravity:
809   case SouthGravity:
810   case CenterGravity:
811     x -= (_size.left + _size.right) / 2;
812     break;
813
814   case NorthEastGravity:
815   case SouthEastGravity:
816   case EastGravity:
817     x -= _size.left + _size.right;
818     break;
819
820   case ForgetGravity:
821   case StaticGravity:
822     x -= _size.left;
823     break;
824   }
825
826   // vertical
827   switch (_client->gravity()) {
828   default:
829   case NorthWestGravity:
830   case NorthEastGravity:
831   case NorthGravity:
832     break;
833
834   case CenterGravity:
835   case EastGravity:
836   case WestGravity:
837     y -= (_size.top + _size.bottom) / 2;
838     break;
839
840   case SouthWestGravity:
841   case SouthEastGravity:
842   case SouthGravity:
843     y -= _size.top + _size.bottom;
844     break;
845
846   case ForgetGravity:
847   case StaticGravity:
848     y -= _size.top;
849     break;
850   }
851 }
852
853
854 void Frame::frameGravity(int &x, int &y)
855 {
856   // horizontal
857   switch (_client->gravity()) {
858   default:
859   case NorthWestGravity:
860   case WestGravity:
861   case SouthWestGravity:
862     break;
863   case NorthGravity:
864   case CenterGravity:
865   case SouthGravity:
866     x += (_size.left + _size.right) / 2;
867     break;
868   case NorthEastGravity:
869   case EastGravity:
870   case SouthEastGravity:
871     x += _size.left + _size.right;
872     break;
873   case StaticGravity:
874   case ForgetGravity:
875     x += _size.left;
876     break;
877   }
878
879   // vertical
880   switch (_client->gravity()) {
881   default:
882   case NorthWestGravity:
883   case WestGravity:
884   case SouthWestGravity:
885     break;
886   case NorthGravity:
887   case CenterGravity:
888   case SouthGravity:
889     y += (_size.top + _size.bottom) / 2;
890     break;
891   case NorthEastGravity:
892   case EastGravity:
893   case SouthEastGravity:
894     y += _size.top + _size.bottom;
895     break;
896   case StaticGravity:
897   case ForgetGravity:
898     y += _size.top;
899     break;
900   }
901 }
902
903
904 }