]> Dogcows Code - chaz/openbox/blob - src/frame.cc
make some funcs const, and don't try focus a client if it is not visible
[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
61
62 OBFrame::~OBFrame()
63 {
64 }
65
66
67 void OBFrame::setTitle(const std::string &text)
68 {
69 _label.setText(text);
70 _label.update();
71 }
72
73
74 void OBFrame::setStyle(otk::Style *style)
75 {
76 assert(style);
77
78 otk::OtkWidget::setStyle(style);
79
80 // if a style was previously set, then 'replace' is true, cause we're
81 // replacing a style
82 bool replace = (_style);
83
84 if (replace) {
85 // XXX: do shit here whatever
86 }
87
88 _style = style;
89
90 setBorderColor(_style->getBorderColor());
91
92 // if !replace, then adjust() will get called after the client is grabbed!
93 if (replace) {
94 // size/position everything
95 adjustSize();
96 adjustPosition();
97 }
98 }
99
100
101 void OBFrame::focus()
102 {
103 otk::OtkWidget::focus();
104 update();
105 }
106
107
108 void OBFrame::unfocus()
109 {
110 otk::OtkWidget::unfocus();
111 update();
112 }
113
114
115 void OBFrame::adjust()
116 {
117 // the party all happens in adjustSize
118 }
119
120
121 void OBFrame::adjustSize()
122 {
123 // XXX: only if not overridden or something!!! MORE LOGIC HERE!!
124 _decorations = _client->decorations();
125
126 // true/false for whether to show each element of the titlebar
127 bool tit_i = false, tit_m = false, tit_s = false, tit_c = false;
128 int width; // the width of the client and its border
129 int bwidth; // width to make borders
130 int cbwidth; // width of the inner client border
131 int butsize=0; // width and height of the titlebar buttons
132 const int bevel = _style->getBevelWidth();
133
134 if (_decorations & OBClient::Decor_Border) {
135 bwidth = _style->getBorderWidth();
136 cbwidth = _style->getFrameWidth();
137 } else
138 bwidth = cbwidth = 0;
139 _innersize.left = _innersize.top = _innersize.bottom = _innersize.right =
140 cbwidth;
141 width = _client->area().width() + cbwidth * 2;
142
143 _plate.setBorderWidth(cbwidth);
144
145 setBorderWidth(bwidth);
146 _titlebar.setBorderWidth(bwidth);
147 _grip_left.setBorderWidth(bwidth);
148 _grip_right.setBorderWidth(bwidth);
149 _handle.setBorderWidth(bwidth);
150
151 if (_decorations & OBClient::Decor_Titlebar) {
152 // set the titlebar size
153 _titlebar.setGeometry(-bwidth,
154 -bwidth,
155 width,
156 _style->getFont()->height() + bevel * 2);
157 _innersize.top += _titlebar.height() + bwidth;
158
159 // set the label size
160 _label.setGeometry(0, bevel, width, _style->getFont()->height());
161 // set the buttons sizes
162 butsize = _label.height() - 2;
163 if (_decorations & OBClient::Decor_Iconify)
164 _button_iconify.setGeometry(0, bevel + 1, butsize, butsize);
165 if (_decorations & OBClient::Decor_Maximize)
166 _button_max.setGeometry(0, bevel + 1, butsize, butsize);
167 if (_decorations & OBClient::Decor_Sticky)
168 _button_stick.setGeometry(0, bevel + 1, butsize, butsize);
169 if (_decorations & OBClient::Decor_Close)
170 _button_close.setGeometry(0, bevel + 1, butsize, butsize);
171
172 // separation between titlebar elements
173 const int sep = bevel + 1;
174
175 std::string layout;
176 if (!python_get_string("titlebar_layout", &layout))
177 layout = "ILMC";
178
179 // this code ensures that the string only has one of each possible
180 // letter, all of the letters are valid, and L exists somewhere in the
181 // string!
182 bool tit_l = false;
183
184 for (std::string::size_type i = 0; i < layout.size(); ++i) {
185 switch (layout[i]) {
186 case 'i':
187 case 'I':
188 if (!tit_i && (_decorations & OBClient::Decor_Iconify)) {
189 tit_i = true;
190 continue;
191 }
192 break;
193 case 'l':
194 case 'L':
195 if (!tit_l) {
196 tit_l = true;
197 continue;
198 }
199 break;
200 case 'm':
201 case 'M':
202 if (!tit_m && (_decorations & OBClient::Decor_Maximize)) {
203 tit_m = true;
204 continue;
205 }
206 break;
207 case 's':
208 case 'S':
209 if (!tit_s && (_decorations & OBClient::Decor_Sticky)) {
210 tit_s = true;
211 continue;
212 }
213 break;
214 case 'c':
215 case 'C':
216 if (!tit_c && (_decorations & OBClient::Decor_Close)) {
217 tit_c = true;
218 continue;
219 }
220 break;
221 }
222 // if we get here then we don't want the letter, kill it
223 layout.erase(i--, 1);
224 }
225 if (!tit_l)
226 layout.append(1, 'L');
227
228 // the size of the label. this ASSUMES the layout has only buttons other
229 // that the ONE LABEL!!
230 // adds an extra sep so that there's a space on either side of the
231 // titlebar.. note: x = sep, below.
232 int lwidth = width - sep * 2 -
233 (butsize + sep) * (layout.size() - 1);
234 // quick sanity check for really small windows. if this is needed, its
235 // obviously not going to be displayed right...
236 // XXX: maybe we should make this look better somehow? constraints?
237 if (lwidth <= 0) lwidth = 1;
238 _label.setWidth(lwidth);
239
240 int x = sep;
241 for (std::string::size_type i = 0, len = layout.size(); i < len; ++i) {
242 switch (layout[i]) {
243 case 'i':
244 case 'I':
245 _button_iconify.move(x, _button_iconify.rect().y());
246 x += _button_iconify.width();
247 break;
248 case 'l':
249 case 'L':
250 _label.move(x, _label.rect().y());
251 x += _label.width();
252 break;
253 case 'm':
254 case 'M':
255 _button_max.move(x, _button_max.rect().y());
256 x += _button_max.width();
257 break;
258 case 's':
259 case 'S':
260 _button_stick.move(x, _button_stick.rect().y());
261 x += _button_stick.width();
262 break;
263 case 'c':
264 case 'C':
265 _button_close.move(x, _button_close.rect().y());
266 x += _button_close.width();
267 break;
268 default:
269 assert(false); // the layout string is invalid!
270 }
271 x += sep;
272 }
273 }
274
275 if (_decorations & OBClient::Decor_Handle) {
276 _handle.setGeometry(-bwidth,
277 _innersize.top + _client->area().height() + cbwidth,
278 width, _style->getHandleWidth());
279 _grip_left.setGeometry(-bwidth,
280 -bwidth,
281 // XXX: get a Point class in otk and use that for
282 // the 'buttons size' since theyre all the same
283 butsize * 2,
284 _handle.height());
285 _grip_right.setGeometry(((_handle.rect().right() + 1) -
286 butsize * 2),
287 -bwidth,
288 // XXX: get a Point class in otk and use that for
289 // the 'buttons size' since theyre all the same
290 butsize * 2,
291 _handle.height());
292 _innersize.bottom += _handle.height() + bwidth;
293 }
294
295
296 // position/size all the windows
297
298 if (_client->shaded())
299 resize(_innersize.left + _innersize.right + _client->area().width(),
300 _titlebar.height());
301 else
302 resize(_innersize.left + _innersize.right + _client->area().width(),
303 _innersize.top + _innersize.bottom + _client->area().height());
304
305 _plate.setGeometry(_innersize.left - cbwidth, _innersize.top - cbwidth,
306 _client->area().width(), _client->area().height());
307
308 // map/unmap all the windows
309 if (_decorations & OBClient::Decor_Titlebar) {
310 _label.show();
311 if (tit_i)
312 _button_iconify.show();
313 else
314 _button_iconify.hide();
315 if (tit_m)
316 _button_max.show();
317 else
318 _button_max.hide();
319 if (tit_s)
320 _button_stick.show();
321 else
322 _button_stick.hide();
323 if (tit_c)
324 _button_close.show();
325 else
326 _button_close.hide();
327 _titlebar.show();
328 } else {
329 _titlebar.hide(true);
330 }
331
332 if (_decorations & OBClient::Decor_Handle)
333 _handle.show(true);
334 else
335 _handle.hide(true);
336
337 _size.left = _innersize.left + bwidth;
338 _size.right = _innersize.right + bwidth;
339 _size.top = _innersize.top + bwidth;
340 _size.bottom = _innersize.bottom + bwidth;
341
342 adjustShape();
343
344 update();
345 }
346
347
348 void OBFrame::adjustPosition()
349 {
350 int x, y;
351 clientGravity(x, y);
352 move(x, y);
353 }
354
355
356 void OBFrame::adjustShape()
357 {
358 #ifdef SHAPE
359 int bwidth = (_decorations & OBClient::Decor_Border) ?
360 _style->getBorderWidth() : 0;
361
362 if (!_client->shaped()) {
363 // clear the shape on the frame window
364 XShapeCombineMask(otk::OBDisplay::display, _window, ShapeBounding,
365 _innersize.left,
366 _innersize.top,
367 None, ShapeSet);
368 } else {
369 // make the frame's shape match the clients
370 XShapeCombineShape(otk::OBDisplay::display, _window, ShapeBounding,
371 _innersize.left,
372 _innersize.top,
373 _client->window(), ShapeBounding, ShapeSet);
374
375 int num = 0;
376 XRectangle xrect[2];
377
378 if (_decorations & OBClient::Decor_Titlebar) {
379 xrect[0].x = _titlebar.rect().x();
380 xrect[0].y = _titlebar.rect().y();
381 xrect[0].width = _titlebar.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
382 xrect[0].height = _titlebar.height() + bwidth * 2;
383 ++num;
384 }
385
386 if (_decorations & OBClient::Decor_Handle) {
387 xrect[1].x = _handle.rect().x();
388 xrect[1].y = _handle.rect().y();
389 xrect[1].width = _handle.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
390 xrect[1].height = _handle.height() + bwidth * 2;
391 ++num;
392 }
393
394 XShapeCombineRectangles(otk::OBDisplay::display, window(),
395 ShapeBounding, 0, 0, xrect, num,
396 ShapeUnion, Unsorted);
397 }
398 #endif // SHAPE
399 }
400
401
402 void OBFrame::grabClient()
403 {
404 // reparent the client to the frame
405 XReparentWindow(otk::OBDisplay::display, _client->window(),
406 _plate.window(), 0, 0);
407 /*
408 When reparenting the client window, it is usually not mapped yet, since
409 this occurs from a MapRequest. However, in the case where Openbox is
410 starting up, the window is already mapped, so we'll see unmap events for
411 it. There are 2 unmap events generated that we see, one with the 'event'
412 member set the root window, and one set to the client, but both get handled
413 and need to be ignored.
414 */
415 if (Openbox::instance->state() == Openbox::State_Starting)
416 _client->ignore_unmaps += 2;
417
418 // select the event mask on the client's parent (to receive config req's)
419 XSelectInput(otk::OBDisplay::display, _plate.window(),
420 SubstructureRedirectMask);
421
422 // map the client so it maps when the frame does
423 XMapWindow(otk::OBDisplay::display, _client->window());
424
425 adjustSize();
426 adjustPosition();
427 }
428
429
430 void OBFrame::releaseClient()
431 {
432 XEvent ev;
433
434 // check if the app has already reparented its window away
435 if (XCheckTypedWindowEvent(otk::OBDisplay::display, _client->window(),
436 ReparentNotify, &ev)) {
437 XPutBackEvent(otk::OBDisplay::display, &ev);
438 } else {
439 // according to the ICCCM - if the client doesn't reparent itself, then we
440 // will reparent the window to root for them
441 XReparentWindow(otk::OBDisplay::display, _client->window(),
442 _screen->rootWindow(),
443 _client->area().x(), _client->area().y());
444 }
445 }
446
447
448 void OBFrame::clientGravity(int &x, int &y)
449 {
450 x = _client->area().x();
451 y = _client->area().y();
452
453 // horizontal
454 switch (_client->gravity()) {
455 default:
456 case NorthWestGravity:
457 case SouthWestGravity:
458 case WestGravity:
459 break;
460
461 case NorthGravity:
462 case SouthGravity:
463 case CenterGravity:
464 x -= (_size.left + _size.right) / 2;
465 break;
466
467 case NorthEastGravity:
468 case SouthEastGravity:
469 case EastGravity:
470 x -= _size.left + _size.right;
471 break;
472
473 case ForgetGravity:
474 case StaticGravity:
475 x -= _size.left;
476 break;
477 }
478
479 // vertical
480 switch (_client->gravity()) {
481 default:
482 case NorthWestGravity:
483 case NorthEastGravity:
484 case NorthGravity:
485 break;
486
487 case CenterGravity:
488 case EastGravity:
489 case WestGravity:
490 y -= (_size.top + _size.bottom) / 2;
491 break;
492
493 case SouthWestGravity:
494 case SouthEastGravity:
495 case SouthGravity:
496 y -= _size.top + _size.bottom;
497 break;
498
499 case ForgetGravity:
500 case StaticGravity:
501 y -= _size.top;
502 break;
503 }
504 }
505
506
507 void OBFrame::frameGravity(int &x, int &y)
508 {
509 x = rect().x();
510 y = rect().y();
511
512 // horizontal
513 switch (_client->gravity()) {
514 default:
515 case NorthWestGravity:
516 case WestGravity:
517 case SouthWestGravity:
518 break;
519 case NorthGravity:
520 case CenterGravity:
521 case SouthGravity:
522 x += (_size.left + _size.right) / 2;
523 break;
524 case NorthEastGravity:
525 case EastGravity:
526 case SouthEastGravity:
527 x += _size.left + _size.right;
528 break;
529 case StaticGravity:
530 case ForgetGravity:
531 x += _size.left;
532 break;
533 }
534
535 // vertical
536 switch (_client->gravity()) {
537 default:
538 case NorthWestGravity:
539 case WestGravity:
540 case SouthWestGravity:
541 break;
542 case NorthGravity:
543 case CenterGravity:
544 case SouthGravity:
545 y += (_size.top + _size.bottom) / 2;
546 break;
547 case NorthEastGravity:
548 case EastGravity:
549 case SouthEastGravity:
550 y += _size.top + _size.bottom;
551 break;
552 case StaticGravity:
553 case ForgetGravity:
554 y += _size.top;
555 break;
556 }
557 }
558
559
560 }
This page took 0.061897 seconds and 4 git commands to generate.