00001
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();
00057
00058 _plate.show();
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
00079
00080 bool replace = (_style);
00081
00082 otk::Widget::setStyle(style);
00083
00084 if (replace) {
00085
00086 }
00087
00088 _style = style;
00089
00090 setBorderColor(_style->frameBorderColor());
00091
00092
00093 if (replace) {
00094
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
00118 }
00119
00120
00121 void Frame::adjustSize()
00122 {
00123
00124 _decorations = _client->decorations();
00125
00126
00127 bool tit_i = false, tit_m = false, tit_s = false, tit_c = false;
00128 int width;
00129 int bwidth;
00130 int cbwidth;
00131 int fontheight = _style->labelFont()->height();
00132 int butsize = fontheight - 2;
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
00154 _titlebar.setGeometry(-bwidth,
00155 -bwidth,
00156 width,
00157 _style->labelFont()->height() + (bevel * 2));
00158 _innersize.top += _titlebar.height() + bwidth;
00159
00160
00161 _label.setGeometry(0, bevel, width, fontheight);
00162
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
00173 const int sep = bevel + 1;
00174
00175 otk::ustring layout;
00176 if (!python_get_string("TITLEBAR_LAYOUT", &layout))
00177 layout = "ILMC";
00178
00179
00180
00181
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
00223 layout.erase(i--, 1);
00224 }
00225 if (!tit_l)
00226 layout += "L";
00227
00228
00229
00230
00231
00232 int lwidth = width - sep * 2 -
00233 (butsize + sep) * (layout.size() - 1);
00234
00235
00236
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);
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
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
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
00362 XShapeCombineMask(**otk::display, _window, ShapeBounding,
00363 _innersize.left,
00364 _innersize.top,
00365 None, ShapeSet);
00366 } else {
00367
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;
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;
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
00410 XReparentWindow(**otk::display, _client->window(), _plate.window(), 0, 0);
00411
00412
00413
00414
00415
00416
00417
00418
00419 if (openbox->state() == Openbox::State_Starting)
00420 _client->ignore_unmaps += 2;
00421
00422
00423 XSelectInput(**otk::display, _plate.window(), SubstructureRedirectMask);
00424
00425
00426 XMapWindow(**otk::display, _client->window());
00427
00428 adjustSize();
00429 adjustPosition();
00430 }
00431
00432
00433 void Frame::releaseClient()
00434 {
00435 XEvent ev;
00436
00437
00438 if (XCheckTypedWindowEvent(**otk::display, _client->window(),
00439 ReparentNotify, &ev)) {
00440 XPutBackEvent(**otk::display, &ev);
00441
00442 XMapWindow(**otk::display, _client->window());
00443 } else {
00444
00445
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
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
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
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
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 }