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