]> Dogcows Code - chaz/openbox/blob - src/frame.cc
20e65932273af6c37166d9b2bf134f722739e568
[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 Frame::event_mask;
25
26 Frame::Frame(Client *client, otk::RenderStyle *style)
27 : otk::Widget(openbox, style, Horizontal, 0, 1, true),
28 WidgetBase(WidgetBase::Type_Frame),
29 _client(client),
30 _screen(otk::display->screenInfo(client->screen())),
31 _plate(this, WidgetBase::Type_Plate),
32 _titlebar(this, WidgetBase::Type_Titlebar),
33 _button_close(&_titlebar, WidgetBase::Type_CloseButton, client),
34 _button_iconify(&_titlebar, WidgetBase::Type_IconifyButton, client),
35 _button_max(&_titlebar, WidgetBase::Type_MaximizeButton, client),
36 _button_alldesk(&_titlebar, WidgetBase::Type_AllDesktopsButton, client),
37 _label(&_titlebar, WidgetBase::Type_Label),
38 _handle(this, WidgetBase::Type_Handle),
39 _grip_left(&_handle, WidgetBase::Type_LeftGrip, client),
40 _grip_right(&_handle, WidgetBase::Type_RightGrip, client),
41 _decorations(client->decorations())
42 {
43 assert(client);
44 assert(style);
45
46 XSelectInput(**otk::display, _window, Frame::event_mask);
47
48 _grip_left.setCursor(openbox->cursors().ll_angle);
49 _grip_right.setCursor(openbox->cursors().lr_angle);
50
51 _label.setText(_client->title());
52
53 _style = 0;
54 setStyle(style);
55
56 otk::Widget::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 Frame::~Frame()
63 {
64 }
65
66
67 void Frame::setTitle(const otk::ustring &text)
68 {
69 _label.setText(text);
70 _label.update();
71 }
72
73
74 void Frame::setStyle(otk::RenderStyle *style)
75 {
76 assert(style);
77
78 // if a style was previously set, then 'replace' is true, cause we're
79 // replacing a style
80 bool replace = (_style);
81
82 otk::Widget::setStyle(style);
83
84 if (replace) {
85 // XXX: do shit here whatever
86 }
87
88 _style = style;
89
90 setBorderColor(_style->frameBorderColor());
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 Frame::focus()
102 {
103 otk::Widget::focus();
104 update();
105 }
106
107
108 void Frame::unfocus()
109 {
110 otk::Widget::unfocus();
111 update();
112 }
113
114
115 void Frame::adjust()
116 {
117 // the party all happens in adjustSize
118 }
119
120
121 void Frame::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->bevelWidth();
133
134 if (_decorations & Client::Decor_Border) {
135 bwidth = _style->frameBorderWidth();
136 cbwidth = _style->clientBorderWidth();
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 & Client::Decor_Titlebar) {
152 // set the titlebar size
153 _titlebar.setGeometry(-bwidth,
154 -bwidth,
155 width,
156 _style->labelFont()->height() + (bevel * 2));
157 _innersize.top += _titlebar.height() + bwidth;
158
159 // set the label size
160 _label.setGeometry(0, bevel, width, _style->labelFont()->height());
161 // set the buttons sizes
162 butsize = _label.height() - 2;
163 if (_decorations & Client::Decor_Iconify)
164 _button_iconify.setGeometry(0, bevel + 1, butsize, butsize);
165 if (_decorations & Client::Decor_Maximize)
166 _button_max.setGeometry(0, bevel + 1, butsize, butsize);
167 if (_decorations & Client::Decor_AllDesktops)
168 _button_alldesk.setGeometry(0, bevel + 1, butsize, butsize);
169 if (_decorations & Client::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 otk::ustring 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 & Client::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 & Client::Decor_Maximize)) {
203 tit_m = true;
204 continue;
205 }
206 break;
207 case 'd':
208 case 'D':
209 if (!tit_s && (_decorations & Client::Decor_AllDesktops)) {
210 tit_s = true;
211 continue;
212 }
213 break;
214 case 'c':
215 case 'C':
216 if (!tit_c && (_decorations & Client::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 += "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 'd':
259 case 'D':
260 _button_alldesk.move(x, _button_alldesk.rect().y());
261 x += _button_alldesk.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 & Client::Decor_Handle) {
276 _handle.setGeometry(-bwidth,
277 _innersize.top + _client->area().height() + cbwidth,
278 width, _style->handleWidth());
279 _grip_left.setGeometry(-bwidth,
280 -bwidth,
281 butsize * 2,
282 _handle.height());
283 _grip_right.setGeometry(((_handle.rect().right() + 1) -
284 butsize * 2),
285 -bwidth,
286 butsize * 2,
287 _handle.height());
288 _innersize.bottom += _handle.height() + bwidth;
289 }
290
291
292 // position/size all the windows
293
294 if (_client->shaded())
295 resize(_innersize.left + _innersize.right + _client->area().width(),
296 _titlebar.height());
297 else
298 resize(_innersize.left + _innersize.right + _client->area().width(),
299 _innersize.top + _innersize.bottom + _client->area().height());
300
301 _plate.setGeometry(_innersize.left - cbwidth, _innersize.top - cbwidth,
302 _client->area().width(), _client->area().height());
303
304 // map/unmap all the windows
305 if (_decorations & Client::Decor_Titlebar) {
306 _label.show();
307 if (tit_i)
308 _button_iconify.show();
309 else
310 _button_iconify.hide();
311 if (tit_m)
312 _button_max.show();
313 else
314 _button_max.hide();
315 if (tit_s)
316 _button_alldesk.show();
317 else
318 _button_alldesk.hide();
319 if (tit_c)
320 _button_close.show();
321 else
322 _button_close.hide();
323 _titlebar.show();
324 } else {
325 _titlebar.hide(true);
326 }
327
328 if (_decorations & Client::Decor_Handle)
329 _handle.show(true);
330 else
331 _handle.hide(true);
332
333 _size.left = _innersize.left + bwidth;
334 _size.right = _innersize.right + bwidth;
335 _size.top = _innersize.top + bwidth;
336 _size.bottom = _innersize.bottom + bwidth;
337
338 adjustShape();
339
340 update();
341 }
342
343
344 void Frame::adjustPosition()
345 {
346 int x, y;
347 clientGravity(x, y);
348 move(x, y);
349 }
350
351
352 void Frame::adjustShape()
353 {
354 #ifdef SHAPE
355 int bwidth = (_decorations & Client::Decor_Border) ?
356 _style->frameBorderWidth() : 0;
357
358 if (!_client->shaped()) {
359 // clear the shape on the frame window
360 XShapeCombineMask(**otk::display, _window, ShapeBounding,
361 _innersize.left,
362 _innersize.top,
363 None, ShapeSet);
364 } else {
365 // make the frame's shape match the clients
366 XShapeCombineShape(**otk::display, _window, ShapeBounding,
367 _innersize.left,
368 _innersize.top,
369 _client->window(), ShapeBounding, ShapeSet);
370
371 int num = 0;
372 XRectangle xrect[2];
373
374 if (_decorations & Client::Decor_Titlebar) {
375 xrect[0].x = _titlebar.rect().x();
376 xrect[0].y = _titlebar.rect().y();
377 xrect[0].width = _titlebar.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
378 xrect[0].height = _titlebar.height() + bwidth * 2;
379 ++num;
380 }
381
382 if (_decorations & Client::Decor_Handle) {
383 xrect[1].x = _handle.rect().x();
384 xrect[1].y = _handle.rect().y();
385 xrect[1].width = _handle.width() + bwidth * 2; // XXX: this is useless once the widget handles borders!
386 xrect[1].height = _handle.height() + bwidth * 2;
387 ++num;
388 }
389
390 XShapeCombineRectangles(**otk::display, window(),
391 ShapeBounding, 0, 0, xrect, num,
392 ShapeUnion, Unsorted);
393 }
394 #endif // SHAPE
395 }
396
397
398 void Frame::adjustState()
399 {
400 _button_alldesk.update();
401 _button_max.update();
402 }
403
404
405 void Frame::grabClient()
406 {
407 // reparent the client to the frame
408 XReparentWindow(**otk::display, _client->window(), _plate.window(), 0, 0);
409 /*
410 When reparenting the client window, it is usually not mapped yet, since
411 this occurs from a MapRequest. However, in the case where Openbox is
412 starting up, the window is already mapped, so we'll see unmap events for
413 it. There are 2 unmap events generated that we see, one with the 'event'
414 member set the root window, and one set to the client, but both get handled
415 and need to be ignored.
416 */
417 if (openbox->state() == Openbox::State_Starting)
418 _client->ignore_unmaps += 2;
419
420 // select the event mask on the client's parent (to receive config/map req's)
421 XSelectInput(**otk::display, _plate.window(), SubstructureRedirectMask);
422
423 // map the client so it maps when the frame does
424 XMapWindow(**otk::display, _client->window());
425
426 adjustSize();
427 adjustPosition();
428 }
429
430
431 void Frame::releaseClient()
432 {
433 XEvent ev;
434
435 // check if the app has already reparented its window away
436 if (XCheckTypedWindowEvent(**otk::display, _client->window(),
437 ReparentNotify, &ev)) {
438 XPutBackEvent(**otk::display, &ev);
439 // re-map the window since the unmanaging process unmaps it
440 XMapWindow(**otk::display, _client->window());
441 } else {
442 // according to the ICCCM - if the client doesn't reparent itself, then we
443 // will reparent the window to root for them
444 XReparentWindow(**otk::display, _client->window(),
445 _screen->rootWindow(),
446 _client->area().x(), _client->area().y());
447 }
448 }
449
450
451 void Frame::clientGravity(int &x, int &y)
452 {
453 x = _client->area().x();
454 y = _client->area().y();
455
456 // horizontal
457 switch (_client->gravity()) {
458 default:
459 case NorthWestGravity:
460 case SouthWestGravity:
461 case WestGravity:
462 break;
463
464 case NorthGravity:
465 case SouthGravity:
466 case CenterGravity:
467 x -= (_size.left + _size.right) / 2;
468 break;
469
470 case NorthEastGravity:
471 case SouthEastGravity:
472 case EastGravity:
473 x -= _size.left + _size.right;
474 break;
475
476 case ForgetGravity:
477 case StaticGravity:
478 x -= _size.left;
479 break;
480 }
481
482 // vertical
483 switch (_client->gravity()) {
484 default:
485 case NorthWestGravity:
486 case NorthEastGravity:
487 case NorthGravity:
488 break;
489
490 case CenterGravity:
491 case EastGravity:
492 case WestGravity:
493 y -= (_size.top + _size.bottom) / 2;
494 break;
495
496 case SouthWestGravity:
497 case SouthEastGravity:
498 case SouthGravity:
499 y -= _size.top + _size.bottom;
500 break;
501
502 case ForgetGravity:
503 case StaticGravity:
504 y -= _size.top;
505 break;
506 }
507 }
508
509
510 void Frame::frameGravity(int &x, int &y)
511 {
512 x = rect().x();
513 y = rect().y();
514
515 // horizontal
516 switch (_client->gravity()) {
517 default:
518 case NorthWestGravity:
519 case WestGravity:
520 case SouthWestGravity:
521 break;
522 case NorthGravity:
523 case CenterGravity:
524 case SouthGravity:
525 x += (_size.left + _size.right) / 2;
526 break;
527 case NorthEastGravity:
528 case EastGravity:
529 case SouthEastGravity:
530 x += _size.left + _size.right;
531 break;
532 case StaticGravity:
533 case ForgetGravity:
534 x += _size.left;
535 break;
536 }
537
538 // vertical
539 switch (_client->gravity()) {
540 default:
541 case NorthWestGravity:
542 case WestGravity:
543 case SouthWestGravity:
544 break;
545 case NorthGravity:
546 case CenterGravity:
547 case SouthGravity:
548 y += (_size.top + _size.bottom) / 2;
549 break;
550 case NorthEastGravity:
551 case EastGravity:
552 case SouthEastGravity:
553 y += _size.top + _size.bottom;
554 break;
555 case StaticGravity:
556 case ForgetGravity:
557 y += _size.top;
558 break;
559 }
560 }
561
562
563 }
This page took 0.059089 seconds and 3 git commands to generate.