]> icculus.org git repositories - mikachu/openbox.git/blob - src/frame.cc
rm old junk
[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 "python.hh"
17 #include "bindings.hh"
18 #include "otk/display.hh"
19
20 #include <string>
21
22 namespace ob {
23
24 const long OBFrame::event_mask;
25
26 OBFrame::OBFrame(OBClient *client, otk::Style *style)
27   : otk::OtkWidget(Openbox::instance, style, Horizontal, 0, 1, true),
28     OBWidget(Type_Frame),
29     _client(client),
30     _screen(otk::OBDisplay::screenInfo(client->screen())),
31     _plate(this, OBWidget::Type_Plate),
32     _titlebar(this, OBWidget::Type_Titlebar),
33     _button_close(&_titlebar, OBWidget::Type_CloseButton),
34     _button_iconify(&_titlebar, OBWidget::Type_IconifyButton),
35     _button_max(&_titlebar, OBWidget::Type_MaximizeButton),
36     _button_stick(&_titlebar, OBWidget::Type_StickyButton),
37     _label(&_titlebar, OBWidget::Type_Label),
38     _handle(this, OBWidget::Type_Handle),
39     _grip_left(&_handle, OBWidget::Type_LeftGrip),
40     _grip_right(&_handle, OBWidget::Type_RightGrip),
41     _decorations(client->decorations())
42 {
43   assert(client);
44   assert(style);
45
46   XSelectInput(otk::OBDisplay::display, _window, OBFrame::event_mask);
47
48   _grip_left.setCursor(Openbox::instance->cursors().ll_angle);
49   _grip_right.setCursor(Openbox::instance->cursors().lr_angle);
50   
51   _label.setText(_client->title());
52
53   _style = 0;
54   setStyle(style);
55
56   otk::OtkWidget::unfocus(); // stuff starts out appearing focused in otk
57   
58   _plate.show(); // the other stuff is shown based on decor settings
59   
60   grabClient();
61 }
62
63
64 OBFrame::~OBFrame()
65 {
66   releaseClient();
67 }
68
69
70 void OBFrame::setTitle(const std::string &text)
71 {
72   _label.setText(text);
73   _label.update();
74 }
75
76
77 void OBFrame::setStyle(otk::Style *style)
78 {
79   assert(style);
80
81   otk::OtkWidget::setStyle(style);
82
83   // if a style was previously set, then 'replace' is true, cause we're
84   // replacing a style
85   bool replace = (_style);
86
87   if (replace) {
88     // XXX: do shit here whatever
89   }
90   
91   _style = style;
92
93   setBorderColor(_style->getBorderColor());
94
95   // if !replace, then adjust() will get called after the client is grabbed!
96   if (replace) {
97     // size/position everything
98     adjustSize();
99     adjustPosition();
100   }
101 }
102
103
104 void OBFrame::focus()
105 {
106   otk::OtkWidget::focus();
107   update();
108 }
109
110
111 void OBFrame::unfocus()
112 {
113   otk::OtkWidget::unfocus();
114   update();
115 }
116
117
118 void OBFrame::adjust()
119 {
120   // the party all happens in adjustSize
121 }
122
123
124 void OBFrame::adjustSize()
125 {
126   // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
127   _decorations = _client->decorations();
128
129   // true/false for whether to show each element of the titlebar
130   bool tit_i = false, tit_m = false, tit_s = false, tit_c = false;
131   int width;   // the width of the client and its border
132   int bwidth;  // width to make borders
133   int cbwidth; // width of the inner client border
134   int butsize=0; // width and height of the titlebar buttons
135   const int bevel = _style->getBevelWidth();
136   
137   if (_decorations & OBClient::Decor_Border) {
138     bwidth = _style->getBorderWidth();
139     cbwidth = _style->getFrameWidth();
140   } else
141     bwidth = cbwidth = 0;
142   _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
143     cbwidth;
144   width = _client->area().width() + cbwidth * 2;
145
146   _plate.setBorderWidth(cbwidth);
147
148   setBorderWidth(bwidth);
149   _titlebar.setBorderWidth(bwidth);
150   _grip_left.setBorderWidth(bwidth);
151   _grip_right.setBorderWidth(bwidth);
152   _handle.setBorderWidth(bwidth);
153   
154   if (_decorations & OBClient::Decor_Titlebar) {
155     // set the titlebar size
156     _titlebar.setGeometry(-bwidth,
157                           -bwidth,
158                           width,
159                           _style->getFont()->height() + bevel * 2);
160     _innersize.top += _titlebar.height() + bwidth;
161
162     // set the label size
163     _label.setGeometry(0, bevel, width, _style->getFont()->height());
164     // set the buttons sizes
165     butsize = _label.height() - 2;
166     if (_decorations & OBClient::Decor_Iconify)
167       _button_iconify.setGeometry(0, bevel + 1, butsize, butsize);
168     if (_decorations & OBClient::Decor_Maximize)
169       _button_max.setGeometry(0, bevel + 1, butsize, butsize);
170     if (_decorations & OBClient::Decor_Sticky)
171       _button_stick.setGeometry(0, bevel + 1, butsize, butsize);
172     if (_decorations & OBClient::Decor_Close)
173       _button_close.setGeometry(0, bevel + 1, butsize, butsize);
174
175     // separation between titlebar elements
176     const int sep = bevel + 1;
177
178     std::string layout;
179     if (!python_get_string("titlebar_layout", &layout))
180       layout = "ILMC";
181
182     // this code ensures that the string only has one of each possible
183     // letter, all of the letters are valid, and L exists somewhere in the
184     // string!
185     bool tit_l = false;
186   
187     for (std::string::size_type i = 0; i < layout.size(); ++i) {
188       switch (layout[i]) {
189       case 'i':
190       case 'I':
191         if (!tit_i && (_decorations & OBClient::Decor_Iconify)) {
192           tit_i = true;
193           continue;
194         }
195         break;
196       case 'l':
197       case 'L':
198         if (!tit_l) {
199           tit_l = true;
200           continue;
201         }
202         break;
203       case 'm':
204       case 'M':
205         if (!tit_m && (_decorations & OBClient::Decor_Maximize)) {
206           tit_m = true;
207           continue;
208         }
209         break;
210       case 's':
211       case 'S':
212         if (!tit_s && (_decorations & OBClient::Decor_Sticky)) {
213           tit_s = true;
214           continue;
215         }
216         break;
217       case 'c':
218       case 'C':
219         if (!tit_c && (_decorations & OBClient::Decor_Close)) {
220           tit_c = true;
221           continue;
222         }
223         break;
224       }
225       // if we get here then we don't want the letter, kill it
226       layout.erase(i--, 1);
227     }
228     if (!tit_l)
229       layout.append(1, 'L');
230     
231     // the size of the label. this ASSUMES the layout has only buttons other
232     // that the ONE LABEL!!
233     // adds an extra sep so that there's a space on either side of the
234     // titlebar.. note: x = sep, below.
235     int lwidth = width - sep * 2 -
236       (butsize + sep) * (layout.size() - 1);
237     // quick sanity check for really small windows. if this is needed, its
238     // obviously not going to be displayed right...
239     // XXX: maybe we should make this look better somehow? constraints?
240     if (lwidth <= 0) lwidth = 1;
241     _label.setWidth(lwidth);
242
243     int x = sep;
244     for (std::string::size_type i = 0, len = layout.size(); i < len; ++i) {
245       switch (layout[i]) {
246       case 'i':
247       case 'I':
248         _button_iconify.move(x, _button_iconify.rect().y());
249         x += _button_iconify.width();
250         break;
251       case 'l':
252       case 'L':
253         _label.move(x, _label.rect().y());
254         x += _label.width();
255         break;
256       case 'm':
257       case 'M':
258         _button_max.move(x, _button_max.rect().y());
259         x += _button_max.width();
260         break;
261       case 's':
262       case 'S':
263         _button_stick.move(x, _button_stick.rect().y());
264         x += _button_stick.width();
265         break;
266       case 'c':
267       case 'C':
268         _button_close.move(x, _button_close.rect().y());
269         x += _button_close.width();
270         break;
271       default:
272         assert(false); // the layout string is invalid!
273       }
274       x += sep;
275     }
276   }
277
278   if (_decorations & OBClient::Decor_Handle) {
279     _handle.setGeometry(-bwidth,
280                         _innersize.top + _client->area().height() + cbwidth,
281                         width, _style->getHandleWidth());
282     _grip_left.setGeometry(-bwidth,
283                            -bwidth,
284                            // XXX: get a Point class in otk and use that for
285                            // the 'buttons size' since theyre all the same
286                            butsize * 2,
287                            _handle.height());
288     _grip_right.setGeometry(((_handle.rect().right() + 1) -
289                              butsize * 2),
290                             -bwidth,
291                             // XXX: get a Point class in otk and use that for
292                             // the 'buttons size' since theyre all the same
293                             butsize * 2,
294                             _handle.height());
295     _innersize.bottom += _handle.height() + bwidth;
296   }
297   
298
299   // position/size all the windows
300
301   if (_client->shaded())
302     resize(_innersize.left + _innersize.right + _client->area().width(),
303            _titlebar.height());
304   else
305     resize(_innersize.left + _innersize.right + _client->area().width(),
306            _innersize.top + _innersize.bottom + _client->area().height());
307
308   _plate.setGeometry(_innersize.left - cbwidth, _innersize.top - cbwidth,
309                      _client->area().width(), _client->area().height());
310
311   // map/unmap all the windows
312   if (_decorations & OBClient::Decor_Titlebar) {
313     _label.show();
314     if (tit_i)
315       _button_iconify.show();
316     else
317       _button_iconify.hide();
318     if (tit_m)
319       _button_max.show();
320     else
321       _button_max.hide();
322     if (tit_s)
323       _button_stick.show();
324     else
325       _button_stick.hide();
326     if (tit_c)
327       _button_close.show();
328     else
329       _button_close.hide();
330     _titlebar.show();
331   } else {
332     _titlebar.hide(true);
333   }
334
335   if (_decorations & OBClient::Decor_Handle)
336     _handle.show(true);
337   else
338     _handle.hide(true);
339   
340   _size.left   = _innersize.left + bwidth;
341   _size.right  = _innersize.right + bwidth;
342   _size.top    = _innersize.top + bwidth;
343   _size.bottom = _innersize.bottom + bwidth;
344
345   adjustShape();
346
347   update();
348 }
349
350
351 void OBFrame::adjustPosition()
352 {
353   int x, y;
354   clientGravity(x, y);
355   move(x, y);
356 }
357
358
359 void OBFrame::adjustShape()
360 {
361 #ifdef SHAPE
362   int bwidth = (_decorations & OBClient::Decor_Border) ?
363     _style->getBorderWidth() : 0;
364   
365   if (!_client->shaped()) {
366     // clear the shape on the frame window
367     XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
368                       _innersize.left,
369                       _innersize.top,
370                       None, ShapeSet);
371   } else {
372     // make the frame's shape match the clients
373     XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
374                        _innersize.left,
375                        _innersize.top,
376                        _client->window(), ShapeBounding, ShapeSet);
377
378     int num = 0;
379     XRectangle xrect[2];
380
381     if (_decorations & OBClient::Decor_Titlebar) {
382       xrect[0].x = _titlebar.rect().x();
383       xrect[0].y = _titlebar.rect().y();
384       xrect[0].width = _titlebar.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
385       xrect[0].height = _titlebar.height() + bwidth * 2;
386       ++num;
387     }
388
389     if (_decorations & OBClient::Decor_Handle) {
390       xrect[1].x = _handle.rect().x();
391       xrect[1].y = _handle.rect().y();
392       xrect[1].width = _handle.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
393       xrect[1].height = _handle.height() + bwidth * 2;
394       ++num;
395     }
396
397     XShapeCombineRectangles(otk::OBDisplay::display, window(),
398                             ShapeBounding, 0, 0, xrect, num,
399                             ShapeUnion, Unsorted);
400   }
401 #endif // SHAPE
402 }
403
404
405 void OBFrame::grabClient()
406 {
407   // reparent the client to the frame
408   XReparentWindow(otk::OBDisplay::display, _client->window(),
409                   _plate.window(), 0, 0);
410   /*
411     When reparenting the client window, it is usually not mapped yet, since
412     this occurs from a MapRequest. However, in the case where Openbox is
413     starting up, the window is already mapped, so we'll see unmap events for
414     it. There are 2 unmap events generated that we see, one with the 'event'
415     member set the root window, and one set to the client, but both get handled
416     and need to be ignored.
417   */
418   if (Openbox::instance->state() == Openbox::State_Starting)
419     _client->ignore_unmaps += 2;
420
421   // select the event mask on the client's parent (to receive config req's)
422   XSelectInput(otk::OBDisplay::display, _plate.window(),
423                SubstructureRedirectMask);
424
425   // map the client so it maps when the frame does
426   XMapWindow(otk::OBDisplay::display, _client->window());
427
428   adjustSize();
429   adjustPosition();
430 }
431
432
433 void OBFrame::releaseClient()
434 {
435   // check if the app has already reparented its window to the root window
436   XEvent ev;
437   if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
438                              ReparentNotify, &ev)) {
439     /*
440       If the app reparented itself, then we unmanage the window. This causes
441       the window to be unmapped, so to be nice to it, we remap the window
442       here. We don't put the event back onto the stack because we put it there
443       in the first place.
444     */
445     XMapWindow(otk::OBDisplay::display, _client->window());
446   } else {
447     // according to the ICCCM - if the client doesn't reparent to
448     // root, then we have to do it for them
449     XReparentWindow(otk::OBDisplay::display, _client->window(),
450                     _screen->rootWindow(),
451                     _client->area().x(), _client->area().y());
452   }
453 }
454
455
456 void OBFrame::clientGravity(int &x, int &y)
457 {
458   x = _client->area().x();
459   y = _client->area().y();
460
461   // horizontal
462   switch (_client->gravity()) {
463   default:
464   case NorthWestGravity:
465   case SouthWestGravity:
466   case WestGravity:
467     break;
468
469   case NorthGravity:
470   case SouthGravity:
471   case CenterGravity:
472     x -= (_size.left + _size.right) / 2;
473     break;
474
475   case NorthEastGravity:
476   case SouthEastGravity:
477   case EastGravity:
478     x -= _size.left + _size.right;
479     break;
480
481   case ForgetGravity:
482   case StaticGravity:
483     x -= _size.left;
484     break;
485   }
486
487   // vertical
488   switch (_client->gravity()) {
489   default:
490   case NorthWestGravity:
491   case NorthEastGravity:
492   case NorthGravity:
493     break;
494
495   case CenterGravity:
496   case EastGravity:
497   case WestGravity:
498     y -= (_size.top + _size.bottom) / 2;
499     break;
500
501   case SouthWestGravity:
502   case SouthEastGravity:
503   case SouthGravity:
504     y -= _size.top + _size.bottom;
505     break;
506
507   case ForgetGravity:
508   case StaticGravity:
509     y -= _size.top;
510     break;
511   }
512 }
513
514
515 void OBFrame::frameGravity(int &x, int &y)
516 {
517   x = rect().x();
518   y = rect().y();
519   
520   // horizontal
521   switch (_client->gravity()) {
522   default:
523   case NorthWestGravity:
524   case WestGravity:
525   case SouthWestGravity:
526     break;
527   case NorthGravity:
528   case CenterGravity:
529   case SouthGravity:
530     x += (_size.left + _size.right) / 2;
531     break;
532   case NorthEastGravity:
533   case EastGravity:
534   case SouthEastGravity:
535     x += _size.left + _size.right;
536     break;
537   case StaticGravity:
538   case ForgetGravity:
539     x += _size.left;
540     break;
541   }
542
543   // vertical
544   switch (_client->gravity()) {
545   default:
546   case NorthWestGravity:
547   case WestGravity:
548   case SouthWestGravity:
549     break;
550   case NorthGravity:
551   case CenterGravity:
552   case SouthGravity:
553     y += (_size.top + _size.bottom) / 2;
554     break;
555   case NorthEastGravity:
556   case EastGravity:
557   case SouthEastGravity:
558     y += _size.top + _size.bottom;
559     break;
560   case StaticGravity:
561   case ForgetGravity:
562     y += _size.top;
563     break;
564   }
565 }
566
567
568 }