change how the widgets' _dirty flag works so that all inheritence levels of the widge...
[mikachu/openbox.git] / src / frame.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 extern "C" {
8 #ifdef    SHAPE
9 #include <X11/extensions/shape.h>
10 #endif // SHAPE
11 }
12
13 #include "frame.hh"
14 #include "client.hh"
15 #include "otk/display.hh"
16
17 #include <string>
18
19 namespace ob {
20
21 OBFrame::OBFrame(OBClient *client, const otk::Style *style)
22   : _client(client),
23     _screen(otk::OBDisplay::screenInfo(client->screen()))
24 {
25   assert(client);
26   assert(style);
27  
28   _decorations = client->decorations();
29  
30   // create the base frame parent window
31   _window = createFrame();
32   assert(_window);
33
34   // create all of the style element child windows
35   _titlebar = createChild(_window, 0);
36   assert(_titlebar);
37   _button_iconify = createChild(_titlebar, 0);
38   assert(_button_iconify);
39   _button_max = createChild(_titlebar, 0);
40   assert(_button_max);
41   _button_stick = createChild(_titlebar, 0);
42   assert(_button_stick);
43   _button_close = createChild(_titlebar, 0);
44   assert(_button_close);
45   _label = createChild(_titlebar, 0);
46   assert(_label);
47   XMapSubwindows(otk::OBDisplay::display, _titlebar);
48
49   _handle = createChild(_window, 0);
50   assert(_handle);
51   _grip_left = createChild(_handle, 0);
52   assert(_grip_left);
53   _grip_right = createChild(_handle, 0);
54   assert(_grip_right);
55   XMapSubwindows(otk::OBDisplay::display, _handle);
56   
57   _style = 0;
58   loadStyle(style);
59
60   grabClient();
61 }
62
63
64 OBFrame::~OBFrame()
65 {
66   XDestroyWindow(otk::OBDisplay::display, _button_iconify);
67   XDestroyWindow(otk::OBDisplay::display, _button_max);
68   XDestroyWindow(otk::OBDisplay::display, _button_stick);
69   XDestroyWindow(otk::OBDisplay::display, _button_close);
70   XDestroyWindow(otk::OBDisplay::display, _label);
71   XDestroyWindow(otk::OBDisplay::display, _titlebar);
72   XDestroyWindow(otk::OBDisplay::display, _grip_left);
73   XDestroyWindow(otk::OBDisplay::display, _grip_right);
74   XDestroyWindow(otk::OBDisplay::display, _handle);
75
76   releaseClient(false);
77
78   XDestroyWindow(otk::OBDisplay::display, _window);
79 }
80
81
82 void OBFrame::loadStyle(const otk::Style *style)
83 {
84   assert(style);
85
86   // if a style was previously set, then 'replace' is true, cause we're
87   // replacing a style
88   bool replace = (_style);
89
90   if (replace) {
91     // XXX: do shit here whatever
92   }
93   
94   _style = style;
95
96   XSetWindowBorderWidth(otk::OBDisplay::display, _window,
97                         _style->getBorderWidth());
98   XSetWindowBorder(otk::OBDisplay::display, _window,
99                    _style->getBorderColor().pixel());
100   XSetWindowBorderWidth(otk::OBDisplay::display, _titlebar,
101                         _style->getBorderWidth());
102   XSetWindowBorder(otk::OBDisplay::display, _titlebar,
103                    _style->getBorderColor().pixel());
104   XSetWindowBorderWidth(otk::OBDisplay::display, _grip_left,
105                         _style->getBorderWidth());
106   XSetWindowBorder(otk::OBDisplay::display, _grip_left,
107                    _style->getBorderColor().pixel());
108   XSetWindowBorderWidth(otk::OBDisplay::display, _grip_right,
109                         _style->getBorderWidth());
110   XSetWindowBorder(otk::OBDisplay::display, _grip_right,
111                    _style->getBorderColor().pixel());
112   XSetWindowBorderWidth(otk::OBDisplay::display, _handle,
113                         _style->getBorderWidth());
114   XSetWindowBorder(otk::OBDisplay::display, _handle,
115                    _style->getBorderColor().pixel());
116   
117   // XXX: if (focused)
118     XSetWindowBackground(otk::OBDisplay::display, _window,
119                          _style->getFrameFocus().color().pixel());
120   // XXX: else  
121   // XXX:  XSetWindowBackground(otk::OBDisplay::display, _window,
122   // XXX:                       _style->getFrameUnfocus().color().pixel());
123
124   // if !replace, then update() will get called after the client is grabbed!
125   if (replace) {
126     update();
127     
128     // XXX: make everything redraw
129   }
130 }
131
132
133 void OBFrame::update()
134 {
135   // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
136   _decorations = _client->decorations();
137
138   int width;   // the width of the client window and the border around it
139   
140   if (_decorations & OBClient::Decor_Border) {
141     _size.left = _size.top = _size.bottom = _size.right =
142       _style->getFrameWidth();
143     width = _client->area().width() + _style->getFrameWidth() * 2;
144   } else {
145     _size.left = _size.top = _size.bottom = _size.right = 0;
146     width = _client->area().width();
147   }
148
149   if (_decorations & OBClient::Decor_Titlebar) {
150     // set the titlebar size
151     _titlebar_area.setRect(-_style->getBorderWidth(),
152                            -_style->getBorderWidth(),
153                            width,
154                            (_style->getFont().height() +
155                             _style->getBevelWidth() * 2));
156     _size.top += _titlebar_area.height() + _style->getBorderWidth();
157
158     // set the label size
159     _label_area.setRect(0, _style->getBevelWidth(),
160                         width, _style->getFont().height());
161     // set the buttons sizes
162     if (_decorations & OBClient::Decor_Iconify)
163       _button_iconify_area.setRect(0, _style->getBevelWidth() + 1,
164                                    _label_area.height() - 2,
165                                    _label_area.height() - 2);
166     if (_decorations & OBClient::Decor_Maximize)
167       _button_max_area.setRect(0, _style->getBevelWidth() + 1,
168                                _label_area.height() - 2,
169                                _label_area.height() - 2);
170     if (_decorations & OBClient::Decor_Sticky)
171       _button_stick_area.setRect(0, _style->getBevelWidth() + 1,
172                                  _label_area.height() - 2,
173                                  _label_area.height() - 2);
174     if (_decorations & OBClient::Decor_Close)
175       _button_close_area.setRect(0, _style->getBevelWidth() + 1,
176                                  _label_area.height() - 2,
177                                  _label_area.height() - 2);
178
179     // separation between titlebar elements
180     const int sep = _style->getBevelWidth() + 1;
181
182     std::string layout = "ILMC"; // XXX: get this from somewhere
183     // XXX: it is REQUIRED that by this point, the string only has one of each
184     // possible letter, all of the letters are valid, and L exists somewhere in
185     // the string!
186
187     // the size of the label. this ASSUMES the layout has only buttons other
188     // that the ONE LABEL!!
189     // adds an extra sep so that there's a space on either side of the
190     // titlebar.. note: x = sep, below.
191     _label_area.setWidth(_label_area.width() -
192                          ((_button_iconify_area.width() + sep) *
193                           (layout.size() - 1) + sep));
194
195     int x = sep;
196     for (int i = 0, len = layout.size(); i < len; ++i) {
197       otk::Rect *area;
198       switch (layout[i]) {
199       case 'I':
200         if (!(_decorations & OBClient::Decor_Iconify))
201           continue; // skip it
202         area = &_button_iconify_area;
203         break;
204       case 'L':
205         area = &_label_area;
206         break;
207       case 'M':
208         if (!(_decorations & OBClient::Decor_Maximize))
209           continue; // skip it
210         area = &_button_max_area;
211         break;
212       case 'S':
213         if (!(_decorations & OBClient::Decor_Sticky))
214           continue; // skip it
215         area = &_button_stick_area;
216         break;
217       case 'C':
218         if (!(_decorations & OBClient::Decor_Close))
219           continue; // skip it
220         area = &_button_close_area;
221         break;
222       default:
223         assert(false); // the layout string is invalid!
224         continue; // just to fuck with g++
225       }
226       area->setX(x);
227       x += sep + area->width();
228     }
229   }
230
231   if (_decorations & OBClient::Decor_Handle) {
232     _handle_area.setRect(-_style->getBorderWidth(),
233                          _size.top + _client->area().height() +
234                          _style->getFrameWidth(),
235                          width, _style->getHandleWidth());
236     _grip_left_area.setRect(-_style->getBorderWidth(),
237                             -_style->getBorderWidth(),
238                             // XXX: get a Point class in otk and use that for
239                             // the 'buttons size' since theyre all the same
240                             _button_iconify_area.width() * 2,
241                             _handle_area.height());
242     _grip_right_area.setRect(((_handle_area.right() + 1) -
243                               _button_iconify_area.width() * 2),
244                              -_style->getBorderWidth(),
245                              // XXX: get a Point class in otk and use that for
246                              // the 'buttons size' since theyre all the same
247                              _button_iconify_area.width() * 2,
248                              _handle_area.height());
249     _size.bottom += _handle_area.height() + _style->getBorderWidth();
250   }
251   
252
253   // position/size all the windows
254
255   XResizeWindow(otk::OBDisplay::display, _window,
256                 _size.left + _size.right + _client->area().width(),
257                 _size.top + _size.bottom + _client->area().height());
258
259   XMoveWindow(otk::OBDisplay::display, _client->window(),
260               _size.left, _size.top);
261
262   if (_decorations & OBClient::Decor_Titlebar) {
263     XMoveResizeWindow(otk::OBDisplay::display, _titlebar,
264                       _titlebar_area.x(), _titlebar_area.y(),
265                       _titlebar_area.width(), _titlebar_area.height());
266     XMoveResizeWindow(otk::OBDisplay::display, _label,
267                       _label_area.x(), _label_area.y(),
268                       _label_area.width(), _label_area.height());
269     if (_decorations & OBClient::Decor_Iconify)
270       XMoveResizeWindow(otk::OBDisplay::display, _button_iconify,
271                         _button_iconify_area.x(), _button_iconify_area.y(),
272                         _button_iconify_area.width(),
273                         _button_iconify_area.height());
274     if (_decorations & OBClient::Decor_Maximize)
275       XMoveResizeWindow(otk::OBDisplay::display, _button_max,
276                         _button_max_area.x(), _button_max_area.y(),
277                         _button_max_area.width(),
278                         _button_max_area.height());
279     if (_decorations & OBClient::Decor_Sticky)
280       XMoveResizeWindow(otk::OBDisplay::display, _button_stick,
281                         _button_stick_area.x(), _button_stick_area.y(),
282                         _button_stick_area.width(),
283                         _button_stick_area.height());
284     if (_decorations & OBClient::Decor_Close)
285       XMoveResizeWindow(otk::OBDisplay::display, _button_close,
286                         _button_close_area.x(), _button_close_area.y(),
287                         _button_close_area.width(),
288                         _button_close_area.height());
289   }
290
291   if (_decorations & OBClient::Decor_Handle) {
292     XMoveResizeWindow(otk::OBDisplay::display, _handle,
293                       _handle_area.x(), _handle_area.y(),
294                       _handle_area.width(), _handle_area.height());
295     XMoveResizeWindow(otk::OBDisplay::display, _grip_left,
296                       _grip_left_area.x(), _grip_left_area.y(),
297                       _grip_left_area.width(), _grip_left_area.height());
298     XMoveResizeWindow(otk::OBDisplay::display, _grip_right,
299                       _grip_right_area.x(), _grip_right_area.y(),
300                       _grip_right_area.width(), _grip_right_area.height());
301   }
302
303   // map/unmap all the windows
304   if (_decorations & OBClient::Decor_Titlebar) {
305     XMapWindow(otk::OBDisplay::display, _label);
306     if (_decorations & OBClient::Decor_Iconify)
307       XMapWindow(otk::OBDisplay::display, _button_iconify);
308     else
309       XUnmapWindow(otk::OBDisplay::display, _button_iconify);
310     if (_decorations & OBClient::Decor_Maximize)
311       XMapWindow(otk::OBDisplay::display, _button_max);
312     else
313       XUnmapWindow(otk::OBDisplay::display, _button_max);
314     if (_decorations & OBClient::Decor_Sticky)
315       XMapWindow(otk::OBDisplay::display, _button_stick);
316     else
317       XUnmapWindow(otk::OBDisplay::display, _button_stick);
318     if (_decorations & OBClient::Decor_Close)
319       XMapWindow(otk::OBDisplay::display, _button_close);
320     else
321       XUnmapWindow(otk::OBDisplay::display, _button_close);
322     XMapWindow(otk::OBDisplay::display, _titlebar);
323   } else {
324     XUnmapWindow(otk::OBDisplay::display, _titlebar);
325     XUnmapWindow(otk::OBDisplay::display, _label);
326     XUnmapWindow(otk::OBDisplay::display, _button_iconify);
327     XUnmapWindow(otk::OBDisplay::display, _button_max);
328     XUnmapWindow(otk::OBDisplay::display, _button_stick);
329     XUnmapWindow(otk::OBDisplay::display, _button_close);
330   }
331
332   if (_decorations & OBClient::Decor_Handle) {
333     XMapWindow(otk::OBDisplay::display, _grip_left);
334     XMapWindow(otk::OBDisplay::display, _grip_right);
335     XMapWindow(otk::OBDisplay::display, _handle);
336   } else {
337     XUnmapWindow(otk::OBDisplay::display, _handle);
338     XUnmapWindow(otk::OBDisplay::display, _grip_left);
339     XUnmapWindow(otk::OBDisplay::display, _grip_right);
340   }
341   
342   // XXX: more is gunna have to happen here
343
344   updateShape();
345 }
346
347
348 void OBFrame::updateShape()
349 {
350 #ifdef SHAPE
351   if (!_client->shaped()) {
352     // clear the shape on the frame window
353     XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
354                       _size.left,
355                       _size.top,
356                       None, ShapeSet);
357   } else {
358     // make the frame's shape match the clients
359     XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
360                        _size.left,
361                        _size.top,
362                        _client->window(), ShapeBounding, ShapeSet);
363
364   int num = 0;
365     XRectangle xrect[2];
366
367     /*
368     if (decorations & Decor_Titlebar) {
369     xrect[0].x = xrect[0].y = -frame.border_w;
370     xrect[0].width = frame.rect.width();
371     xrect[0].height = frame.title_h + (frame.border_w * 2);
372     ++num;
373     }
374
375     if (decorations & Decor_Handle) {
376     xrect[1].x = -frame.border_w;
377     xrect[1].y = frame.rect.height() - frame.margin.bottom +
378     frame.mwm_border_w - frame.border_w;
379     xrect[1].width = frame.rect.width();
380     xrect[1].height = frame.handle_h + (frame.border_w * 2);
381     ++num;
382     }*/
383
384     XShapeCombineRectangles(otk::OBDisplay::display, _window,
385                             ShapeBounding, 0, 0, xrect, num,
386                             ShapeUnion, Unsorted);
387   }
388 #endif // SHAPE
389 }
390
391
392 void OBFrame::grabClient()
393 {
394   
395   // select the event mask on the frame
396   //XSelectInput(otk::OBDisplay::display, _window, SubstructureRedirectMask);
397
398   // reparent the client to the frame
399   XReparentWindow(otk::OBDisplay::display, _client->window(), _window, 0, 0);
400   _client->ignore_unmaps++;
401
402   // raise the client above the frame
403   //XRaiseWindow(otk::OBDisplay::display, _client->window());
404   // map the client so it maps when the frame does
405   XMapWindow(otk::OBDisplay::display, _client->window());
406   
407   update();
408 }
409
410
411 void OBFrame::releaseClient(bool remap)
412 {
413   // check if the app has already reparented its window to the root window
414   XEvent ev;
415   if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
416                              ReparentNotify, &ev)) {
417     remap = true; // XXX: why do we remap the window if they already
418                   // reparented to root?
419   } else {
420     // according to the ICCCM - if the client doesn't reparent to
421     // root, then we have to do it for them
422     XReparentWindow(otk::OBDisplay::display, _client->window(),
423                     _screen->getRootWindow(),
424                     _client->area().x(), _client->area().y());
425   }
426
427   // if we want to remap the window, do so now
428   if (remap)
429     XMapWindow(otk::OBDisplay::display, _client->window());
430 }
431
432
433 Window OBFrame::createChild(Window parent, Cursor cursor)
434 {
435   XSetWindowAttributes attrib_create;
436   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
437
438   attrib_create.background_pixmap = None;
439   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
440                              ButtonMotionMask | ExposureMask;
441
442   if (cursor) {
443     create_mask |= CWCursor;
444     attrib_create.cursor = cursor;
445   }
446
447   Window w = XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0,
448                            _screen->getDepth(), InputOutput,
449                            _screen->getVisual(), create_mask, &attrib_create);
450   return w;
451 }
452
453
454 Window OBFrame::createFrame()
455 {
456   XSetWindowAttributes attrib_create;
457   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
458                               CWOverrideRedirect | CWEventMask;
459
460   attrib_create.background_pixmap = None;
461   attrib_create.colormap = _screen->getColormap();
462   attrib_create.override_redirect = True;
463   attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | ButtonPress;
464   /*
465     We catch button presses because other wise they get passed down to the
466     root window, which will then cause root menus to show when you click the
467     window's frame.
468   */
469
470   return XCreateWindow(otk::OBDisplay::display, _screen->getRootWindow(),
471                        0, 0, 1, 1, 0,
472                        _screen->getDepth(), InputOutput, _screen->getVisual(),
473                        create_mask, &attrib_create);
474 }
475
476 }