openbox/src/frame.cc

569 lines
15 KiB
C++
Raw Normal View History

// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif
2002-11-10 05:54:12 +00:00
extern "C" {
#ifdef SHAPE
#include <X11/extensions/shape.h>
#endif // SHAPE
}
#include "openbox.hh"
#include "frame.hh"
#include "client.hh"
2003-01-02 22:58:32 +00:00
#include "python.hh"
#include "bindings.hh"
#include "otk/display.hh"
#include <string>
namespace ob {
2002-12-04 08:30:19 +00:00
const long OBFrame::event_mask;
OBFrame::OBFrame(OBClient *client, otk::Style *style)
: otk::OtkWidget(Openbox::instance, style, Horizontal, 0, 1, true),
2002-12-18 11:34:29 +00:00
OBWidget(Type_Frame),
_client(client),
_screen(otk::OBDisplay::screenInfo(client->screen())),
_plate(this, OBWidget::Type_Plate),
_titlebar(this, OBWidget::Type_Titlebar),
2002-12-18 09:46:24 +00:00
_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),
2002-12-18 09:46:24 +00:00
_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);
2002-12-04 03:47:59 +00:00
_grip_left.setCursor(Openbox::instance->cursors().ll_angle);
_grip_right.setCursor(Openbox::instance->cursors().lr_angle);
_label.setText(_client->title());
_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();
}
OBFrame::~OBFrame()
{
releaseClient();
}
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
bool replace = (_style);
if (replace) {
// XXX: do shit here whatever
}
_style = style;
setBorderColor(_style->getBorderColor());
// if !replace, then adjust() will get called after the client is grabbed!
if (replace) {
// size/position everything
adjustSize();
adjustPosition();
}
}
void OBFrame::focus()
{
otk::OtkWidget::focus();
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();
2003-01-03 15:55:42 +00:00
// 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
int butsize=0; // width and height of the titlebar buttons
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
butsize = _label.height() - 2;
if (_decorations & OBClient::Decor_Iconify)
_button_iconify.setGeometry(0, bevel + 1, butsize, butsize);
if (_decorations & OBClient::Decor_Maximize)
_button_max.setGeometry(0, bevel + 1, butsize, butsize);
if (_decorations & OBClient::Decor_Sticky)
_button_stick.setGeometry(0, bevel + 1, butsize, butsize);
if (_decorations & OBClient::Decor_Close)
_button_close.setGeometry(0, bevel + 1, butsize, butsize);
// separation between titlebar elements
const int sep = bevel + 1;
2003-01-03 15:55:42 +00:00
std::string layout;
if (!python_get_string("titlebar_layout", &layout))
layout = "ILMC";
2003-01-03 15:55:42 +00:00
// 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]) {
2003-01-03 15:55:42 +00:00
case 'i':
case 'I':
if (!tit_i && (_decorations & OBClient::Decor_Iconify)) {
tit_i = true;
continue;
}
break;
2003-01-03 15:55:42 +00:00
case 'l':
case 'L':
if (!tit_l) {
tit_l = true;
continue;
}
break;
2003-01-03 15:55:42 +00:00
case 'm':
case 'M':
if (!tit_m && (_decorations & OBClient::Decor_Maximize)) {
tit_m = true;
continue;
}
break;
2003-01-03 15:55:42 +00:00
case 's':
case 'S':
if (!tit_s && (_decorations & OBClient::Decor_Sticky)) {
tit_s = true;
continue;
}
break;
2003-01-03 15:55:42 +00:00
case 'c':
case 'C':
if (!tit_c && (_decorations & OBClient::Decor_Close)) {
tit_c = true;
continue;
}
break;
2003-01-03 15:55:42 +00:00
}
// if we get here then we don't want the letter, kill it
layout.erase(i--, 1);
}
if (!tit_l)
layout.append(1, 'L');
2003-01-03 15:55:42 +00:00
2002-11-10 12:58:29 +00:00
// 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.
2002-12-04 00:26:45 +00:00
int lwidth = width - sep * 2 -
(butsize + sep) * (layout.size() - 1);
2002-12-04 00:26:45 +00:00
// 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);
2002-11-10 12:58:29 +00:00
int x = sep;
for (std::string::size_type i = 0, len = layout.size(); i < len; ++i) {
switch (layout[i]) {
case 'i':
case 'I':
2002-12-20 15:38:49 +00:00
_button_iconify.move(x, _button_iconify.rect().y());
x += _button_iconify.width();
break;
case 'l':
case 'L':
2002-12-20 15:38:49 +00:00
_label.move(x, _label.rect().y());
x += _label.width();
break;
case 'm':
case 'M':
2002-12-20 15:38:49 +00:00
_button_max.move(x, _button_max.rect().y());
x += _button_max.width();
break;
case 's':
case 'S':
2002-12-20 15:38:49 +00:00
_button_stick.move(x, _button_stick.rect().y());
x += _button_stick.width();
break;
case 'c':
case 'C':
2002-12-20 15:38:49 +00:00
_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
butsize * 2,
_handle.height());
2002-12-20 15:38:49 +00:00
_grip_right.setGeometry(((_handle.rect().right() + 1) -
butsize * 2),
-bwidth,
// XXX: get a Point class in otk and use that for
// the 'buttons size' since theyre all the same
butsize * 2,
_handle.height());
_innersize.bottom += _handle.height() + bwidth;
}
// position/size all the windows
2003-01-03 23:51:06 +00:00
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();
2003-01-03 15:55:42 +00:00
if (tit_i)
_button_iconify.show();
else
_button_iconify.hide();
2003-01-03 15:55:42 +00:00
if (tit_m)
_button_max.show();
else
_button_max.hide();
2003-01-03 15:55:42 +00:00
if (tit_s)
_button_stick.show();
else
_button_stick.hide();
2003-01-03 15:55:42 +00:00
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::adjustPosition()
{
int x, y;
clientGravity(x, y);
move(x, y);
}
void OBFrame::adjustShape()
{
2002-11-10 05:54:12 +00:00
#ifdef SHAPE
int bwidth = (_decorations & OBClient::Decor_Border) ?
_style->getBorderWidth() : 0;
2002-11-10 05:54:12 +00:00
if (!_client->shaped()) {
// clear the shape on the frame window
XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
_innersize.left,
_innersize.top,
2002-11-10 05:54:12 +00:00
None, ShapeSet);
} else {
// make the frame's shape match the clients
XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
_innersize.left,
_innersize.top,
2002-11-10 05:54:12 +00:00
_client->window(), ShapeBounding, ShapeSet);
int num = 0;
2002-11-10 05:54:12 +00:00
XRectangle xrect[2];
if (_decorations & OBClient::Decor_Titlebar) {
2002-12-20 15:38:49 +00:00
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;
2002-11-10 05:54:12 +00:00
}
if (_decorations & OBClient::Decor_Handle) {
2002-12-20 15:38:49 +00:00
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;
}
2002-11-10 05:54:12 +00:00
2002-12-20 15:38:49 +00:00
XShapeCombineRectangles(otk::OBDisplay::display, window(),
2002-11-10 05:54:12 +00:00
ShapeBounding, 0, 0, xrect, num,
ShapeUnion, Unsorted);
}
#endif // SHAPE
}
void OBFrame::grabClient()
{
// reparent the client to the frame
XReparentWindow(otk::OBDisplay::display, _client->window(),
2002-12-20 15:38:49 +00:00
_plate.window(), 0, 0);
/*
When reparenting the client window, it is usually not mapped yet, since
this occurs from a MapRequest. However, in the case where Openbox is
starting up, the window is already mapped, so we'll see unmap events for
it. There are 2 unmap events generated that we see, one with the 'event'
member set the root window, and one set to the client, but both get handled
and need to be ignored.
*/
if (Openbox::instance->state() == Openbox::State_Starting)
_client->ignore_unmaps += 2;
// select the event mask on the client's parent (to receive config req's)
2002-12-20 15:38:49 +00:00
XSelectInput(otk::OBDisplay::display, _plate.window(),
SubstructureRedirectMask);
2002-12-04 01:04:31 +00:00
// map the client so it maps when the frame does
XMapWindow(otk::OBDisplay::display, _client->window());
adjustSize();
adjustPosition();
}
void OBFrame::releaseClient()
{
// check if the app has already reparented its window to the root window
XEvent ev;
if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
ReparentNotify, &ev)) {
/*
If the app reparented itself, then we unmanage the window. This causes
the window to be unmapped, so to be nice to it, we remap the window
here. We don't put the event back onto the stack because we put it there
in the first place.
*/
XMapWindow(otk::OBDisplay::display, _client->window());
} else {
// 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(),
2002-12-20 15:38:49 +00:00
_screen->rootWindow(),
_client->area().x(), _client->area().y());
}
}
void OBFrame::clientGravity(int &x, int &y)
{
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)
2002-12-04 00:26:45 +00:00
{
2002-12-20 15:38:49 +00:00
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;
}
2002-12-04 00:26:45 +00:00
}
}