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