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