Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

/src/frame.cc

Go to the documentation of this file.
00001 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 # include "../config.h"
00005 #endif
00006 
00007 extern "C" {
00008 #ifdef    SHAPE
00009 #include <X11/extensions/shape.h>
00010 #endif // SHAPE
00011 }
00012 
00013 #include "openbox.hh"
00014 #include "frame.hh"
00015 #include "client.hh"
00016 #include "python.hh"
00017 #include "bindings.hh"
00018 #include "otk/display.hh"
00019 
00020 #include <string>
00021 
00022 namespace ob {
00023 
00024 const long Frame::event_mask;
00025 
00026 Frame::Frame(Client *client, otk::RenderStyle *style)
00027   : otk::Widget(openbox, style, Horizontal, 0, 1, true),
00028     WidgetBase(WidgetBase::Type_Frame),
00029     _client(client),
00030     _screen(otk::display->screenInfo(client->screen())),
00031     _plate(this, WidgetBase::Type_Plate),
00032     _titlebar(this, WidgetBase::Type_Titlebar),
00033     _button_close(&_titlebar, WidgetBase::Type_CloseButton, client),
00034     _button_iconify(&_titlebar, WidgetBase::Type_IconifyButton, client),
00035     _button_max(&_titlebar, WidgetBase::Type_MaximizeButton, client),
00036     _button_alldesk(&_titlebar, WidgetBase::Type_AllDesktopsButton, client),
00037     _label(&_titlebar, WidgetBase::Type_Label),
00038     _handle(this, WidgetBase::Type_Handle),
00039     _grip_left(&_handle, WidgetBase::Type_LeftGrip, client),
00040     _grip_right(&_handle, WidgetBase::Type_RightGrip, client),
00041     _decorations(client->decorations())
00042 {
00043   assert(client);
00044   assert(style);
00045 
00046   XSelectInput(**otk::display, _window, Frame::event_mask);
00047 
00048   _grip_left.setCursor(openbox->cursors().ll_angle);
00049   _grip_right.setCursor(openbox->cursors().lr_angle);
00050   
00051   _label.setText(_client->title());
00052 
00053   _style = 0;
00054   setStyle(style);
00055 
00056   otk::Widget::unfocus(); // stuff starts out appearing focused in otk
00057   
00058   _plate.show(); // the other stuff is shown based on decor settings
00059 }
00060 
00061 
00062 Frame::~Frame()
00063 {
00064 }
00065 
00066 
00067 void Frame::setTitle(const otk::ustring &text)
00068 {
00069   _label.setText(text);
00070   _label.update();
00071 }
00072 
00073 
00074 void Frame::setStyle(otk::RenderStyle *style)
00075 {
00076   assert(style);
00077 
00078   // if a style was previously set, then 'replace' is true, cause we're
00079   // replacing a style
00080   bool replace = (_style);
00081 
00082   otk::Widget::setStyle(style);
00083 
00084   if (replace) {
00085     // XXX: do shit here whatever
00086   }
00087   
00088   _style = style;
00089 
00090   setBorderColor(_style->frameBorderColor());
00091 
00092   // if !replace, then adjust() will get called after the client is grabbed!
00093   if (replace) {
00094     // size/position everything
00095     adjustSize();
00096     adjustPosition();
00097   }
00098 }
00099 
00100 
00101 void Frame::focus()
00102 {
00103   otk::Widget::focus();
00104   update();
00105 }
00106 
00107 
00108 void Frame::unfocus()
00109 {
00110   otk::Widget::unfocus();
00111   update();
00112 }
00113 
00114 
00115 void Frame::adjust()
00116 {
00117   // the party all happens in adjustSize
00118 }
00119 
00120 
00121 void Frame::adjustSize()
00122 {
00123   // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
00124   _decorations = _client->decorations();
00125 
00126   // true/false for whether to show each element of the titlebar
00127   bool tit_i = false, tit_m = false, tit_s = false, tit_c = false;
00128   int width;   // the width of the client and its border
00129   int bwidth;  // width to make borders
00130   int cbwidth; // width of the inner client border
00131   int fontheight = _style->labelFont()->height(); // height of the font
00132   int butsize = fontheight - 2; // width and height of the titlebar buttons
00133   const int bevel = _style->bevelWidth();
00134   
00135   if (_decorations & Client::Decor_Border) {
00136     bwidth = _style->frameBorderWidth();
00137     cbwidth = _style->clientBorderWidth();
00138   } else
00139     bwidth = cbwidth = 0;
00140   _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
00141     cbwidth;
00142   width = _client->area().width() + cbwidth * 2;
00143 
00144   _plate.setBorderWidth(cbwidth);
00145 
00146   setBorderWidth(bwidth);
00147   _titlebar.setBorderWidth(bwidth);
00148   _grip_left.setBorderWidth(bwidth);
00149   _grip_right.setBorderWidth(bwidth);
00150   _handle.setBorderWidth(bwidth);
00151   
00152   if (_decorations & Client::Decor_Titlebar) {
00153     // set the titlebar size
00154     _titlebar.setGeometry(-bwidth,
00155                           -bwidth,
00156                           width,
00157                           _style->labelFont()->height() + (bevel * 2));
00158     _innersize.top += _titlebar.height() + bwidth;
00159 
00160     // set the label size
00161     _label.setGeometry(0, bevel, width, fontheight);
00162     // set the buttons sizes
00163     if (_decorations & Client::Decor_Iconify)
00164       _button_iconify.setGeometry(0, bevel + 1, butsize, butsize);
00165     if (_decorations & Client::Decor_Maximize)
00166       _button_max.setGeometry(0, bevel + 1, butsize, butsize);
00167     if (_decorations & Client::Decor_AllDesktops)
00168       _button_alldesk.setGeometry(0, bevel + 1, butsize, butsize);
00169     if (_decorations & Client::Decor_Close)
00170       _button_close.setGeometry(0, bevel + 1, butsize, butsize);
00171 
00172     // separation between titlebar elements
00173     const int sep = bevel + 1;
00174 
00175     otk::ustring layout;
00176     if (!python_get_string("TITLEBAR_LAYOUT", &layout))
00177       layout = "ILMC";
00178 
00179     // this code ensures that the string only has one of each possible
00180     // letter, all of the letters are valid, and L exists somewhere in the
00181     // string!
00182     bool tit_l = false;
00183   
00184     for (std::string::size_type i = 0; i < layout.size(); ++i) {
00185       switch (layout[i]) {
00186       case 'i':
00187       case 'I':
00188         if (!tit_i && (_decorations & Client::Decor_Iconify)) {
00189           tit_i = true;
00190           continue;
00191         }
00192         break;
00193       case 'l':
00194       case 'L':
00195         if (!tit_l) {
00196           tit_l = true;
00197           continue;
00198         }
00199         break;
00200       case 'm':
00201       case 'M':
00202         if (!tit_m && (_decorations & Client::Decor_Maximize)) {
00203           tit_m = true;
00204           continue;
00205         }
00206         break;
00207       case 'd':
00208       case 'D':
00209         if (!tit_s && (_decorations & Client::Decor_AllDesktops)) {
00210           tit_s = true;
00211           continue;
00212         }
00213         break;
00214       case 'c':
00215       case 'C':
00216         if (!tit_c && (_decorations & Client::Decor_Close)) {
00217           tit_c = true;
00218           continue;
00219         }
00220         break;
00221       }
00222       // if we get here then we don't want the letter, kill it
00223       layout.erase(i--, 1);
00224     }
00225     if (!tit_l)
00226       layout += "L";
00227     
00228     // the size of the label. this ASSUMES the layout has only buttons other
00229     // that the ONE LABEL!!
00230     // adds an extra sep so that there's a space on either side of the
00231     // titlebar.. note: x = sep, below.
00232     int lwidth = width - sep * 2 -
00233       (butsize + sep) * (layout.size() - 1);
00234     // quick sanity check for really small windows. if this is needed, its
00235     // obviously not going to be displayed right...
00236     // XXX: maybe we should make this look better somehow? constraints?
00237     if (lwidth <= 0) lwidth = 1;
00238     _label.setWidth(lwidth);
00239 
00240     int x = sep;
00241     for (std::string::size_type i = 0, len = layout.size(); i < len; ++i) {
00242       switch (layout[i]) {
00243       case 'i':
00244       case 'I':
00245         _button_iconify.move(x, _button_iconify.rect().y());
00246         x += _button_iconify.width();
00247         break;
00248       case 'l':
00249       case 'L':
00250         _label.move(x, _label.rect().y());
00251         x += _label.width();
00252         break;
00253       case 'm':
00254       case 'M':
00255         _button_max.move(x, _button_max.rect().y());
00256         x += _button_max.width();
00257         break;
00258       case 'd':
00259       case 'D':
00260         _button_alldesk.move(x, _button_alldesk.rect().y());
00261         x += _button_alldesk.width();
00262         break;
00263       case 'c':
00264       case 'C':
00265         _button_close.move(x, _button_close.rect().y());
00266         x += _button_close.width();
00267         break;
00268       default:
00269         assert(false); // the layout string is invalid!
00270       }
00271       x += sep;
00272     }
00273   }
00274 
00275   if (_decorations & Client::Decor_Handle) {
00276     _handle.setGeometry(-bwidth,
00277                         _innersize.top + _client->area().height() + cbwidth,
00278                         width, _style->handleWidth());
00279     _grip_left.setGeometry(-bwidth,
00280                            -bwidth,
00281                            butsize * 2,
00282                            _handle.height());
00283     _grip_right.setGeometry(((_handle.rect().right() + 1) -
00284                              butsize * 2),
00285                             -bwidth,
00286                             butsize * 2,
00287                             _handle.height());
00288     _innersize.bottom += _handle.height() + bwidth;
00289   }
00290   
00291 
00292   // position/size all the windows
00293 
00294   if (_client->shaded())
00295     resize(_innersize.left + _innersize.right + _client->area().width(),
00296            _titlebar.height());
00297   else
00298     resize(_innersize.left + _innersize.right + _client->area().width(),
00299            _innersize.top + _innersize.bottom + _client->area().height());
00300 
00301   _plate.setGeometry(_innersize.left - cbwidth, _innersize.top - cbwidth,
00302                      _client->area().width(), _client->area().height());
00303 
00304   // map/unmap all the windows
00305   if (_decorations & Client::Decor_Titlebar) {
00306     _label.show();
00307     if (tit_i)
00308       _button_iconify.show();
00309     else
00310       _button_iconify.hide();
00311     if (tit_m)
00312       _button_max.show();
00313     else
00314       _button_max.hide();
00315     if (tit_s)
00316       _button_alldesk.show();
00317     else
00318       _button_alldesk.hide();
00319     if (tit_c)
00320       _button_close.show();
00321     else
00322       _button_close.hide();
00323     _titlebar.show();
00324   } else {
00325     _titlebar.hide(true);
00326   }
00327 
00328   if (_decorations & Client::Decor_Handle)
00329     _handle.show(true);
00330   else
00331     _handle.hide(true);
00332   
00333   _size.left   = _innersize.left + bwidth;
00334   _size.right  = _innersize.right + bwidth;
00335   _size.top    = _innersize.top + bwidth;
00336   _size.bottom = _innersize.bottom + bwidth;
00337 
00338   adjustShape();
00339 
00340   update();
00341 }
00342 
00343 
00344 void Frame::adjustPosition()
00345 {
00346   int x, y;
00347   x = _client->area().x();
00348   y = _client->area().y();
00349   clientGravity(x, y);
00350   move(x, y);
00351 }
00352 
00353 
00354 void Frame::adjustShape()
00355 {
00356 #ifdef SHAPE
00357   int bwidth = (_decorations & Client::Decor_Border) ?
00358     _style->frameBorderWidth() : 0;
00359   
00360   if (!_client->shaped()) {
00361     // clear the shape on the frame window
00362     XShapeCombineMask(**otk::display, _window, ShapeBounding,
00363                       _innersize.left,
00364                       _innersize.top,
00365                       None, ShapeSet);
00366   } else {
00367     // make the frame's shape match the clients
00368     XShapeCombineShape(**otk::display, _window, ShapeBounding,
00369                        _innersize.left,
00370                        _innersize.top,
00371                        _client->window(), ShapeBounding, ShapeSet);
00372 
00373     int num = 0;
00374     XRectangle xrect[2];
00375 
00376     if (_decorations & Client::Decor_Titlebar) {
00377       xrect[0].x = _titlebar.rect().x();
00378       xrect[0].y = _titlebar.rect().y();
00379       xrect[0].width = _titlebar.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
00380       xrect[0].height = _titlebar.height() + bwidth * 2;
00381       ++num;
00382     }
00383 
00384     if (_decorations & Client::Decor_Handle) {
00385       xrect[1].x = _handle.rect().x();
00386       xrect[1].y = _handle.rect().y();
00387       xrect[1].width = _handle.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
00388       xrect[1].height = _handle.height() + bwidth * 2;
00389       ++num;
00390     }
00391 
00392     XShapeCombineRectangles(**otk::display, window(),
00393                             ShapeBounding, 0, 0, xrect, num,
00394                             ShapeUnion, Unsorted);
00395   }
00396 #endif // SHAPE
00397 }
00398 
00399 
00400 void Frame::adjustState()
00401 {
00402   _button_alldesk.update();
00403   _button_max.update();
00404 }
00405 
00406 
00407 void Frame::grabClient()
00408 {
00409   // reparent the client to the frame
00410   XReparentWindow(**otk::display, _client->window(), _plate.window(), 0, 0);
00411   /*
00412     When reparenting the client window, it is usually not mapped yet, since
00413     this occurs from a MapRequest. However, in the case where Openbox is
00414     starting up, the window is already mapped, so we'll see unmap events for
00415     it. There are 2 unmap events generated that we see, one with the 'event'
00416     member set the root window, and one set to the client, but both get handled
00417     and need to be ignored.
00418   */
00419   if (openbox->state() == Openbox::State_Starting)
00420     _client->ignore_unmaps += 2;
00421 
00422   // select the event mask on the client's parent (to receive config/map req's)
00423   XSelectInput(**otk::display, _plate.window(), SubstructureRedirectMask);
00424 
00425   // map the client so it maps when the frame does
00426   XMapWindow(**otk::display, _client->window());
00427 
00428   adjustSize();
00429   adjustPosition();
00430 }
00431 
00432 
00433 void Frame::releaseClient()
00434 {
00435   XEvent ev;
00436 
00437   // check if the app has already reparented its window away
00438   if (XCheckTypedWindowEvent(**otk::display, _client->window(),
00439                              ReparentNotify, &ev)) {
00440     XPutBackEvent(**otk::display, &ev);
00441     // re-map the window since the unmanaging process unmaps it
00442     XMapWindow(**otk::display, _client->window());  
00443   } else {
00444     // according to the ICCCM - if the client doesn't reparent itself, then we
00445     // will reparent the window to root for them
00446     XReparentWindow(**otk::display, _client->window(),
00447                     _screen->rootWindow(),
00448                     _client->area().x(), _client->area().y());
00449   }
00450 }
00451 
00452 
00453 void Frame::clientGravity(int &x, int &y)
00454 {
00455   // horizontal
00456   switch (_client->gravity()) {
00457   default:
00458   case NorthWestGravity:
00459   case SouthWestGravity:
00460   case WestGravity:
00461     break;
00462 
00463   case NorthGravity:
00464   case SouthGravity:
00465   case CenterGravity:
00466     x -= (_size.left + _size.right) / 2;
00467     break;
00468 
00469   case NorthEastGravity:
00470   case SouthEastGravity:
00471   case EastGravity:
00472     x -= _size.left + _size.right;
00473     break;
00474 
00475   case ForgetGravity:
00476   case StaticGravity:
00477     x -= _size.left;
00478     break;
00479   }
00480 
00481   // vertical
00482   switch (_client->gravity()) {
00483   default:
00484   case NorthWestGravity:
00485   case NorthEastGravity:
00486   case NorthGravity:
00487     break;
00488 
00489   case CenterGravity:
00490   case EastGravity:
00491   case WestGravity:
00492     y -= (_size.top + _size.bottom) / 2;
00493     break;
00494 
00495   case SouthWestGravity:
00496   case SouthEastGravity:
00497   case SouthGravity:
00498     y -= _size.top + _size.bottom;
00499     break;
00500 
00501   case ForgetGravity:
00502   case StaticGravity:
00503     y -= _size.top;
00504     break;
00505   }
00506 }
00507 
00508 
00509 void Frame::frameGravity(int &x, int &y)
00510 {
00511   // horizontal
00512   switch (_client->gravity()) {
00513   default:
00514   case NorthWestGravity:
00515   case WestGravity:
00516   case SouthWestGravity:
00517     break;
00518   case NorthGravity:
00519   case CenterGravity:
00520   case SouthGravity:
00521     x += (_size.left + _size.right) / 2;
00522     break;
00523   case NorthEastGravity:
00524   case EastGravity:
00525   case SouthEastGravity:
00526     x += _size.left + _size.right;
00527     break;
00528   case StaticGravity:
00529   case ForgetGravity:
00530     x += _size.left;
00531     break;
00532   }
00533 
00534   // vertical
00535   switch (_client->gravity()) {
00536   default:
00537   case NorthWestGravity:
00538   case WestGravity:
00539   case SouthWestGravity:
00540     break;
00541   case NorthGravity:
00542   case CenterGravity:
00543   case SouthGravity:
00544     y += (_size.top + _size.bottom) / 2;
00545     break;
00546   case NorthEastGravity:
00547   case EastGravity:
00548   case SouthEastGravity:
00549     y += _size.top + _size.bottom;
00550     break;
00551   case StaticGravity:
00552   case ForgetGravity:
00553     y += _size.top;
00554     break;
00555   }
00556 }
00557 
00558 
00559 }

Generated on Tue Feb 4 22:58:58 2003 for Openbox by doxygen1.3-rc2