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