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