X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fframe.cc;h=dc126ea3b8f43d3dfe32f71d46c7ddc13b9ac251;hb=c33ccf60cba94249b4a4d1e1832aa0ea0e28a22c;hp=9e4a8edb19199d828b85966f60c0086d1e63936b;hpb=b9cac2146e1dfe54cb6c0ce647d6c7d58d17de54;p=chaz%2Fopenbox diff --git a/src/frame.cc b/src/frame.cc index 9e4a8edb..dc126ea3 100644 --- a/src/frame.cc +++ b/src/frame.cc @@ -4,25 +4,60 @@ # include "../config.h" #endif +extern "C" { +#ifdef SHAPE +#include +#endif // SHAPE +} + +#include "openbox.hh" #include "frame.hh" #include "client.hh" +#include "python.hh" +#include "bindings.hh" #include "otk/display.hh" +#include + namespace ob { -OBFrame::OBFrame(const OBClient *client, const otk::Style *style) - : _client(client), - _screen(otk::OBDisplay::screenInfo(client->screen())) +const long OBFrame::event_mask; + +OBFrame::OBFrame(OBClient *client, otk::Style *style) + : otk::OtkWidget(Openbox::instance, style, Horizontal, 0, 1, + CWOverrideRedirect), + OBWidget(Type_Frame), + _client(client), + _screen(otk::OBDisplay::screenInfo(client->screen())), + _plate(this, OBWidget::Type_Plate), + _titlebar(this, OBWidget::Type_Titlebar), + _button_close(&_titlebar, OBWidget::Type_CloseButton), + _button_iconify(&_titlebar, OBWidget::Type_IconifyButton), + _button_max(&_titlebar, OBWidget::Type_MaximizeButton), + _button_stick(&_titlebar, OBWidget::Type_StickyButton), + _label(&_titlebar, OBWidget::Type_Label), + _handle(this, OBWidget::Type_Handle), + _grip_left(&_handle, OBWidget::Type_LeftGrip), + _grip_right(&_handle, OBWidget::Type_RightGrip), + _decorations(client->decorations()) { assert(client); assert(style); + + XSelectInput(otk::OBDisplay::display, _window, OBFrame::event_mask); + + _grip_left.setCursor(Openbox::instance->cursors().ll_angle); + _grip_right.setCursor(Openbox::instance->cursors().lr_angle); - _style = 0; - loadStyle(style); + _label.setText(_client->title()); - _window = createFrame(); - assert(_window); + _style = 0; + setStyle(style); + otk::OtkWidget::unfocus(); // stuff starts out appearing focused in otk + + _plate.show(); // the other stuff is shown based on decor settings + grabClient(); } @@ -33,13 +68,21 @@ OBFrame::~OBFrame() } -void OBFrame::loadStyle(const otk::Style *style) +void OBFrame::setTitle(const std::string &text) +{ + _label.setText(text); + _label.update(); +} + + +void OBFrame::setStyle(otk::Style *style) { assert(style); + otk::OtkWidget::setStyle(style); + // if a style was previously set, then 'replace' is true, cause we're // replacing a style - // NOTE: if this is false, then DO NOT DO SHIT WITH _window, it doesnt exist bool replace = (_style); if (replace) { @@ -48,57 +91,332 @@ void OBFrame::loadStyle(const otk::Style *style) _style = style; - // XXX: load shit like this from the style! - _size.left = _size.top = _size.bottom = _size.right = 2; + setBorderColor(_style->getBorderColor()); + // if !replace, then adjust() will get called after the client is grabbed! if (replace) { - XSetWindowBorderWidth(otk::OBDisplay::display, _window, - _style->getBorderWidth()); + // size/position everything + adjustSize(); + adjustPosition(); + } +} - // XXX: make everything redraw + +void OBFrame::focus() +{ + otk::OtkWidget::focus(); + update(); + _handle.update(); +} + + +void OBFrame::unfocus() +{ + otk::OtkWidget::unfocus(); + update(); +} + + +void OBFrame::adjust() +{ + // the party all happens in adjustSize +} + + +void OBFrame::adjustSize() +{ + // XXX: only if not overridden or something!!! MORE LOGIC HERE!! + _decorations = _client->decorations(); + + // true/false for whether to show each element of the titlebar + bool tit_i = false, tit_m = false, tit_s = false, tit_c = false; + int width; // the width of the client and its border + int bwidth; // width to make borders + int cbwidth; // width of the inner client border + const int bevel = _style->getBevelWidth(); + + if (_decorations & OBClient::Decor_Border) { + bwidth = _style->getBorderWidth(); + cbwidth = _style->getFrameWidth(); + } else + bwidth = cbwidth = 0; + _innersize.left = _innersize.top = _innersize.bottom = _innersize.right = + cbwidth; + width = _client->area().width() + cbwidth * 2; + + _plate.setBorderWidth(cbwidth); + + setBorderWidth(bwidth); + _titlebar.setBorderWidth(bwidth); + _grip_left.setBorderWidth(bwidth); + _grip_right.setBorderWidth(bwidth); + _handle.setBorderWidth(bwidth); + + if (_decorations & OBClient::Decor_Titlebar) { + // set the titlebar size + _titlebar.setGeometry(-bwidth, + -bwidth, + width, + _style->getFont()->height() + bevel * 2); + _innersize.top += _titlebar.height() + bwidth; + + // set the label size + _label.setGeometry(0, bevel, width, _style->getFont()->height()); + // set the buttons sizes + if (_decorations & OBClient::Decor_Iconify) + _button_iconify.setGeometry(0, bevel + 1, + _label.height() - 2, + _label.height() - 2); + if (_decorations & OBClient::Decor_Maximize) + _button_max.setGeometry(0, bevel + 1, + _label.height() - 2, + _label.height() - 2); + if (_decorations & OBClient::Decor_Sticky) + _button_stick.setGeometry(0, bevel + 1, + _label.height() - 2, + _label.height() - 2); + if (_decorations & OBClient::Decor_Close) + _button_close.setGeometry(0, bevel + 1, + _label.height() - 2, + _label.height() - 2); + + // separation between titlebar elements + const int sep = bevel + 1; + + std::string layout; + if (!python_get_string("titlebar_layout", &layout)) + layout = "ILMC"; + + // this code ensures that the string only has one of each possible + // letter, all of the letters are valid, and L exists somewhere in the + // string! + bool tit_l = false; + + for (std::string::size_type i = 0; i < layout.size(); ++i) { + switch(layout[i]) { + case 'i': + case 'I': + if (!tit_i && (_decorations & OBClient::Decor_Iconify)) { + tit_i = true; + continue; + } + case 'l': + case 'L': + if (!tit_l) { + tit_l = true; + continue; + } + case 'm': + case 'M': + if (!tit_m && (_decorations & OBClient::Decor_Maximize)) { + tit_m = true; + continue; + } + case 's': + case 'S': + if (!tit_s && (_decorations & OBClient::Decor_Sticky)) { + tit_s = true; + continue; + } + case 'c': + case 'C': + if (!tit_c && (_decorations & OBClient::Decor_Close)) { + tit_c = true; + continue; + } + } + // if we get here then we don't want the letter, kill it + layout.erase(i--, 1); + } + if (!tit_l) + layout += 'L'; + + // the size of the label. this ASSUMES the layout has only buttons other + // that the ONE LABEL!! + // adds an extra sep so that there's a space on either side of the + // titlebar.. note: x = sep, below. + int lwidth = width - sep * 2 - + (_button_iconify.width() + sep) * (layout.size() - 1); + // quick sanity check for really small windows. if this is needed, its + // obviously not going to be displayed right... + // XXX: maybe we should make this look better somehow? constraints? + if (lwidth <= 0) lwidth = 1; + _label.setWidth(lwidth); + + int x = sep; + for (int i = 0, len = layout.size(); i < len; ++i) { + switch (layout[i]) { + case 'I': + _button_iconify.move(x, _button_iconify.rect().y()); + x += _button_iconify.width(); + break; + case 'L': + _label.move(x, _label.rect().y()); + x += _label.width(); + break; + case 'M': + _button_max.move(x, _button_max.rect().y()); + x += _button_max.width(); + break; + case 'S': + _button_stick.move(x, _button_stick.rect().y()); + x += _button_stick.width(); + break; + case 'C': + _button_close.move(x, _button_close.rect().y()); + x += _button_close.width(); + break; + default: + assert(false); // the layout string is invalid! + } + x += sep; + } + } + + if (_decorations & OBClient::Decor_Handle) { + _handle.setGeometry(-bwidth, + _innersize.top + _client->area().height() + cbwidth, + width, _style->getHandleWidth()); + _grip_left.setGeometry(-bwidth, + -bwidth, + // XXX: get a Point class in otk and use that for + // the 'buttons size' since theyre all the same + _button_iconify.width() * 2, + _handle.height()); + _grip_right.setGeometry(((_handle.rect().right() + 1) - + _button_iconify.width() * 2), + -bwidth, + // XXX: get a Point class in otk and use that for + // the 'buttons size' since theyre all the same + _button_iconify.width() * 2, + _handle.height()); + _innersize.bottom += _handle.height() + bwidth; + } + + + // position/size all the windows + + if (_client->shaded()) + resize(_innersize.left + _innersize.right + _client->area().width(), + _titlebar.height()); + else + resize(_innersize.left + _innersize.right + _client->area().width(), + _innersize.top + _innersize.bottom + _client->area().height()); + + _plate.setGeometry(_innersize.left - cbwidth, _innersize.top - cbwidth, + _client->area().width(), _client->area().height()); + + // map/unmap all the windows + if (_decorations & OBClient::Decor_Titlebar) { + _label.show(); + if (tit_i) + _button_iconify.show(); + else + _button_iconify.hide(); + if (tit_m) + _button_max.show(); + else + _button_max.hide(); + if (tit_s) + _button_stick.show(); + else + _button_stick.hide(); + if (tit_c) + _button_close.show(); + else + _button_close.hide(); + _titlebar.show(); + } else { + _titlebar.hide(true); } + + if (_decorations & OBClient::Decor_Handle) + _handle.show(true); + else + _handle.hide(true); + + _size.left = _innersize.left + bwidth; + _size.right = _innersize.right + bwidth; + _size.top = _innersize.top + bwidth; + _size.bottom = _innersize.bottom + bwidth; + + adjustShape(); + + update(); } -void OBFrame::resize() +void OBFrame::adjustPosition() { - XResizeWindow(otk::OBDisplay::display, _window, - _size.left + _size.right + _client->area().width(), - _size.top + _size.bottom + _client->area().height()); - // XXX: more is gunna have to happen here + int x, y; + clientGravity(x, y); + move(x, y); } -void OBFrame::shape() +void OBFrame::adjustShape() { - // XXX: if shaped, shape the frame to the client.. +#ifdef SHAPE + int bwidth = (_decorations & OBClient::Decor_Border) ? + _style->getBorderWidth() : 0; + + if (!_client->shaped()) { + // clear the shape on the frame window + XShapeCombineMask(otk::OBDisplay::display, window(), ShapeBounding, + _innersize.left, + _innersize.top, + None, ShapeSet); + } else { + // make the frame's shape match the clients + XShapeCombineShape(otk::OBDisplay::display, window(), ShapeBounding, + _innersize.left, + _innersize.top, + _client->window(), ShapeBounding, ShapeSet); + + int num = 0; + XRectangle xrect[2]; + + if (_decorations & OBClient::Decor_Titlebar) { + xrect[0].x = _titlebar.rect().x(); + xrect[0].y = _titlebar.rect().y(); + xrect[0].width = _titlebar.width() + bwidth * 2; // XXX: this is useless once the widget handles borders! + xrect[0].height = _titlebar.height() + bwidth * 2; + ++num; + } + + if (_decorations & OBClient::Decor_Handle) { + xrect[1].x = _handle.rect().x(); + xrect[1].y = _handle.rect().y(); + xrect[1].width = _handle.width() + bwidth * 2; // XXX: this is useless once the widget handles borders! + xrect[1].height = _handle.height() + bwidth * 2; + ++num; + } + + XShapeCombineRectangles(otk::OBDisplay::display, window(), + ShapeBounding, 0, 0, xrect, num, + ShapeUnion, Unsorted); + } +#endif // SHAPE } void OBFrame::grabClient() { - XGrabServer(otk::OBDisplay::display); + // reparent the client to the frame + XReparentWindow(otk::OBDisplay::display, _client->window(), + _plate.window(), 0, 0); + _client->ignore_unmaps++; - // select the event mask on the frame - XSelectInput(otk::OBDisplay::display, _window, SubstructureRedirectMask); + // select the event mask on the client's parent (to receive config req's) + XSelectInput(otk::OBDisplay::display, _plate.window(), + SubstructureRedirectMask); - // reparent the client to the frame - XSelectInput(otk::OBDisplay::display, _client->window(), - OBClient::event_mask & ~StructureNotifyMask); - XReparentWindow(otk::OBDisplay::display, _client->window(), _window, 0, 0); - XSelectInput(otk::OBDisplay::display, _client->window(), - OBClient::event_mask); - - // raise the client above the frame - XRaiseWindow(otk::OBDisplay::display, _client->window()); // map the client so it maps when the frame does XMapWindow(otk::OBDisplay::display, _client->window()); - - XUngrabServer(otk::OBDisplay::display); - resize(); - shape(); + adjustSize(); + adjustPosition(); } @@ -114,7 +432,7 @@ void OBFrame::releaseClient(bool remap) // according to the ICCCM - if the client doesn't reparent to // root, then we have to do it for them XReparentWindow(otk::OBDisplay::display, _client->window(), - _screen->getRootWindow(), + _screen->rootWindow(), _client->area().x(), _client->area().y()); } @@ -124,26 +442,116 @@ void OBFrame::releaseClient(bool remap) } -Window OBFrame::createFrame() +void OBFrame::clientGravity(int &x, int &y) { - XSetWindowAttributes attrib_create; - unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap | - CWOverrideRedirect | CWEventMask; - - attrib_create.background_pixmap = None; - attrib_create.colormap = _screen->getColormap(); - attrib_create.override_redirect = True; - attrib_create.event_mask = EnterWindowMask | LeaveWindowMask | ButtonPress; - /* - We catch button presses because other wise they get passed down to the - root window, which will then cause root menus to show when you click the - window's frame. - */ - - return XCreateWindow(otk::OBDisplay::display, _screen->getRootWindow(), - 0, 0, 1, 1, _style->getBorderWidth(), - _screen->getDepth(), InputOutput, _screen->getVisual(), - create_mask, &attrib_create); + x = _client->area().x(); + y = _client->area().y(); + + // horizontal + switch (_client->gravity()) { + default: + case NorthWestGravity: + case SouthWestGravity: + case WestGravity: + break; + + case NorthGravity: + case SouthGravity: + case CenterGravity: + x -= (_size.left + _size.right) / 2; + break; + + case NorthEastGravity: + case SouthEastGravity: + case EastGravity: + x -= _size.left + _size.right; + break; + + case ForgetGravity: + case StaticGravity: + x -= _size.left; + break; + } + + // vertical + switch (_client->gravity()) { + default: + case NorthWestGravity: + case NorthEastGravity: + case NorthGravity: + break; + + case CenterGravity: + case EastGravity: + case WestGravity: + y -= (_size.top + _size.bottom) / 2; + break; + + case SouthWestGravity: + case SouthEastGravity: + case SouthGravity: + y -= _size.top + _size.bottom; + break; + + case ForgetGravity: + case StaticGravity: + y -= _size.top; + break; + } } + +void OBFrame::frameGravity(int &x, int &y) +{ + x = rect().x(); + y = rect().y(); + + // horizontal + switch (_client->gravity()) { + default: + case NorthWestGravity: + case WestGravity: + case SouthWestGravity: + break; + case NorthGravity: + case CenterGravity: + case SouthGravity: + x += (_size.left + _size.right) / 2; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + x += _size.left + _size.right; + break; + case StaticGravity: + case ForgetGravity: + x += _size.left; + break; + } + + // vertical + switch (_client->gravity()) { + default: + case NorthWestGravity: + case WestGravity: + case SouthWestGravity: + break; + case NorthGravity: + case CenterGravity: + case SouthGravity: + y += (_size.top + _size.bottom) / 2; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + y += _size.top + _size.bottom; + break; + case StaticGravity: + case ForgetGravity: + y += _size.top; + break; + } +} + + }