]> Dogcows Code - chaz/openbox/blob - src/frame.cc
6d46d7b948ee6faf28c1e9be17528b10eeab6828
[chaz/openbox] / 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 // do an extra map here .. ? XXX
455 // XMapWindow(otk::OBDisplay::display, _client->window());
456 }
457
458
459 void OBFrame::clientGravity(int &x, int &y)
460 {
461 x = _client->area().x();
462 y = _client->area().y();
463
464 // horizontal
465 switch (_client->gravity()) {
466 default:
467 case NorthWestGravity:
468 case SouthWestGravity:
469 case WestGravity:
470 break;
471
472 case NorthGravity:
473 case SouthGravity:
474 case CenterGravity:
475 x -= (_size.left + _size.right) / 2;
476 break;
477
478 case NorthEastGravity:
479 case SouthEastGravity:
480 case EastGravity:
481 x -= _size.left + _size.right;
482 break;
483
484 case ForgetGravity:
485 case StaticGravity:
486 x -= _size.left;
487 break;
488 }
489
490 // vertical
491 switch (_client->gravity()) {
492 default:
493 case NorthWestGravity:
494 case NorthEastGravity:
495 case NorthGravity:
496 break;
497
498 case CenterGravity:
499 case EastGravity:
500 case WestGravity:
501 y -= (_size.top + _size.bottom) / 2;
502 break;
503
504 case SouthWestGravity:
505 case SouthEastGravity:
506 case SouthGravity:
507 y -= _size.top + _size.bottom;
508 break;
509
510 case ForgetGravity:
511 case StaticGravity:
512 y -= _size.top;
513 break;
514 }
515 }
516
517
518 void OBFrame::frameGravity(int &x, int &y)
519 {
520 x = rect().x();
521 y = rect().y();
522
523 // horizontal
524 switch (_client->gravity()) {
525 default:
526 case NorthWestGravity:
527 case WestGravity:
528 case SouthWestGravity:
529 break;
530 case NorthGravity:
531 case CenterGravity:
532 case SouthGravity:
533 x += (_size.left + _size.right) / 2;
534 break;
535 case NorthEastGravity:
536 case EastGravity:
537 case SouthEastGravity:
538 x += _size.left + _size.right;
539 break;
540 case StaticGravity:
541 case ForgetGravity:
542 x += _size.left;
543 break;
544 }
545
546 // vertical
547 switch (_client->gravity()) {
548 default:
549 case NorthWestGravity:
550 case WestGravity:
551 case SouthWestGravity:
552 break;
553 case NorthGravity:
554 case CenterGravity:
555 case SouthGravity:
556 y += (_size.top + _size.bottom) / 2;
557 break;
558 case NorthEastGravity:
559 case EastGravity:
560 case SouthEastGravity:
561 y += _size.top + _size.bottom;
562 break;
563 case StaticGravity:
564 case ForgetGravity:
565 y += _size.top;
566 break;
567 }
568 }
569
570
571 }
This page took 0.060486 seconds and 4 git commands to generate.