]> Dogcows Code - chaz/openbox/blob - otk/widget.cc
690224d897d7c13124cda435d717503ff1724fe0
[chaz/openbox] / otk / widget.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "config.h"
4 #include "widget.hh"
5 #include "display.hh"
6 #include "surface.hh"
7 #include "rendertexture.hh"
8 #include "rendercolor.hh"
9 #include "eventdispatcher.hh"
10 #include "screeninfo.hh"
11
12 #include <climits>
13 #include <cassert>
14 #include <algorithm>
15
16 namespace otk {
17
18 Widget::Widget(int screen, EventDispatcher *ed, Direction direction, int bevel,
19 bool overrideredir)
20 : _texture(0),
21 _screen(screen),
22 _parent(0),
23 _window(0),
24 _surface(0),
25 _event_mask(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
26 ExposureMask | StructureNotifyMask),
27 _alignment(RenderStyle::CenterJustify),
28 _direction(direction),
29 _max_size(INT_MAX, INT_MAX),
30 _visible(false),
31 _bordercolor(0),
32 _borderwidth(0),
33 _bevel(bevel),
34 _dirty(true),
35 _dispatcher(ed),
36 _ignore_config(0)
37 {
38 createWindow(overrideredir);
39 _dispatcher->registerHandler(_window, this);
40 }
41
42 Widget::Widget(Widget *parent, Direction direction, int bevel)
43 : _texture(0),
44 _screen(parent->_screen),
45 _parent(parent),
46 _window(0),
47 _surface(0),
48 _event_mask(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
49 ExposureMask | StructureNotifyMask),
50 _alignment(RenderStyle::CenterJustify),
51 _direction(direction),
52 _max_size(INT_MAX, INT_MAX),
53 _visible(false),
54 _bordercolor(0),
55 _borderwidth(0),
56 _bevel(bevel),
57 _dirty(true),
58 _dispatcher(parent->_dispatcher),
59 _ignore_config(0)
60 {
61 assert(parent);
62 createWindow(false);
63 parent->addChild(this);
64 if (parent->visible()) parent->layout();
65 _dispatcher->registerHandler(_window, this);
66 styleChanged(*RenderStyle::style(_screen));
67 }
68
69 Widget::~Widget()
70 {
71 assert(_children.empty()); // this would be bad. theyd have a hanging _parent
72
73 if (_surface) delete _surface;
74 if (_parent) _parent->removeChild(this);
75
76 _dispatcher->clearHandler(_window);
77 XDestroyWindow(**display, _window);
78 }
79
80 void Widget::show(bool children)
81 {
82 if (children) {
83 std::list<Widget*>::iterator it , end = _children.end();
84 for (it = _children.begin(); it != end; ++it) {
85 (*it)->show(true);
86 }
87 }
88 if (!_visible) {
89 _visible = true;
90 if (_parent) _parent->calcDefaultSizes();
91 else {
92 resize(_min_size);
93 }
94 XMapWindow(**display, _window);
95 update();
96 }
97 }
98
99 void Widget::hide()
100 {
101 if (_visible) {
102 _visible = false;
103 XUnmapWindow(**display, _window);
104 if (_parent) {
105 _parent->calcDefaultSizes();
106 _parent->layout();
107 }
108 }
109 }
110
111 void Widget::setEventMask(long e)
112 {
113 XSelectInput(**display, _window, e);
114 _event_mask = e;
115 }
116
117 void Widget::update()
118 {
119 if (!_visible) return;
120 _dirty = true;
121 if (_parent) {
122 _parent->calcDefaultSizes();
123 _parent->layout(); // relay-out us and our siblings
124 } else {
125 render();
126 layout();
127 }
128 }
129
130 void Widget::moveresize(const Rect &r)
131 {
132 int w, h;
133 w = std::max(std::min(r.width(), maxSize().width()), minSize().width());
134 h = std::max(std::min(r.height(), maxSize().height()), minSize().height());
135
136 if (r.x() == area().x() && r.y() == area().y() &&
137 w == area().width() && h == area().height()) {
138 return; // no change, don't cause a big layout chain to occur!
139 }
140
141 internal_moveresize(r.x(), r.y(), w, h);
142
143 update();
144 }
145
146 void Widget::internal_moveresize(int x, int y, int w, int h)
147 {
148 assert(w > 0);
149 assert(h > 0);
150 assert(_borderwidth >= 0);
151 _dirty = true;
152 if (!(x == _area.x() && y == _area.y())) {
153 if (!(w == _area.width() && h == _area.height()))
154 XMoveResizeWindow(**display, _window, x, y,
155 w - _borderwidth * 2,
156 h - _borderwidth * 2);
157 else
158 XMoveWindow(**display, _window, x, y);
159 } else
160 XResizeWindow(**display, _window, w - _borderwidth*2, h - _borderwidth*2);
161 _ignore_config++;
162
163 _area = Rect(x, y, w, h);
164 }
165
166 void Widget::setAlignment(RenderStyle::Justify a)
167 {
168 _alignment = a;
169 layout();
170 }
171
172 void Widget::createWindow(bool overrideredir)
173 {
174 const ScreenInfo *info = display->screenInfo(_screen);
175 XSetWindowAttributes attrib;
176 unsigned long mask = CWEventMask | CWBorderPixel;
177
178 attrib.event_mask = _event_mask;
179 attrib.border_pixel = (_bordercolor ?
180 _bordercolor->pixel():
181 BlackPixel(**display, _screen));
182
183 if (overrideredir) {
184 mask |= CWOverrideRedirect;
185 attrib.override_redirect = true;
186 }
187
188 _window = XCreateWindow(**display, (_parent ?
189 _parent->_window :
190 RootWindow(**display, _screen)),
191 _area.x(), _area.y(),
192 _area.width(), _area.height(),
193 _borderwidth,
194 info->depth(),
195 InputOutput,
196 info->visual(),
197 mask,
198 &attrib);
199 assert(_window != None);
200 ++_ignore_config;
201 }
202
203 void Widget::calcDefaultSizes()
204 {
205 std::list<Widget*>::const_iterator it, end = _children.end();
206 int min_biggest = 0, max_biggest = 0;
207 int min_sum = _bevel + _borderwidth * 2;
208 int max_sum = _bevel + _borderwidth * 2;
209 bool fullmax = false;
210
211 for (it = _children.begin(); it != end; ++it) {
212 const otk::Size &min = (*it)->minSize();
213 const otk::Size &max = (*it)->maxSize();
214 if (_direction == Horizontal) {
215 if (min.height() > min_biggest) min_biggest = min.height();
216 if (max.height() > max_biggest) max_biggest = max.height();
217 min_sum += _bevel + min.width();
218 if (max.width() == INT_MAX)
219 fullmax = true;
220 else if (!fullmax)
221 max_sum += _bevel + max.width();
222 } else {
223 if (min.width() > min_biggest) min_biggest = min.width();
224 if (max.width() > max_biggest) max_biggest = max.width();
225 min_sum += _bevel + min.height();
226 if (max.height() == INT_MAX)
227 fullmax = true;
228 else if (!fullmax)
229 max_sum += _bevel + max.height();
230 }
231 }
232 if (_direction == Horizontal) {
233 _min_size = otk::Size(min_sum, min_biggest + (_bevel + _borderwidth) * 2);
234 _max_size = otk::Size((fullmax ? INT_MAX :
235 max_sum + (_bevel + _borderwidth) * 2),
236 max_biggest);
237 } else {
238 _min_size = otk::Size(min_biggest, min_sum + (_bevel + _borderwidth) * 2);
239 _max_size = otk::Size(max_biggest, (fullmax ? INT_MAX : max_sum +
240 (_bevel + _borderwidth) * 2));
241 }
242 update();
243 }
244
245 void Widget::setBorderWidth(int w)
246 {
247 assert(w >= 0);
248 if (!parent()) return; // top-level windows cannot have borders
249 if (w == borderWidth()) return; // no change
250
251 _borderwidth = w;
252 XSetWindowBorderWidth(**display, _window, _borderwidth);
253
254 calcDefaultSizes();
255 update();
256 }
257
258 void Widget::setMinSize(const Size &s)
259 {
260 _min_size = s;
261 update();
262 }
263
264 void Widget::setMaxSize(const Size &s)
265 {
266 _max_size = s;
267 update();
268 }
269
270 void Widget::setBorderColor(const RenderColor *c)
271 {
272 _bordercolor = c;
273 XSetWindowBorder(**otk::display, _window,
274 c ? c->pixel() : BlackPixel(**otk::display, _screen));
275 }
276
277 void Widget::setBevel(int b)
278 {
279 _bevel = b;
280 calcDefaultSizes();
281 layout();
282 }
283
284 void Widget::layout()
285 {
286 if (_children.empty() || !_visible) return;
287 if (_direction == Horizontal)
288 layoutHorz();
289 else
290 layoutVert();
291 }
292
293 void Widget::layoutHorz()
294 {
295 std::list<Widget*>::iterator it, end;
296
297 // work with just the visible children
298 std::list<Widget*> visible = _children;
299 for (it = visible.begin(), end = visible.end(); it != end;) {
300 std::list<Widget*>::iterator next = it; ++next;
301 if (!(*it)->visible())
302 visible.erase(it);
303 it = next;
304 }
305
306 if (visible.empty()) return;
307
308 int x, y, w, h; // working area
309 x = y = _bevel;
310 w = _area.width() - _borderwidth * 2 - _bevel * 2;
311 h = _area.height() - _borderwidth * 2 - _bevel * 2;
312 if (w < 0 || h < 0) return; // not worth laying anything out!
313
314 int free = w - (visible.size() - 1) * _bevel;
315 if (free < 0) free = 0;
316 int each;
317
318 std::list<Widget*> adjustable = visible;
319
320 // find the 'free' space, and how many children will be using it
321 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
322 std::list<Widget*>::iterator next = it; ++next;
323 free -= (*it)->minSize().width();
324 if (free < 0) free = 0;
325 if ((*it)->maxSize().width() - (*it)->minSize().width() <= 0)
326 adjustable.erase(it);
327 it = next;
328 }
329 // some widgets may have max widths that restrict them, find the 'true'
330 // amount of free space after these widgets are not included
331 if (!adjustable.empty()) {
332 do {
333 each = free / adjustable.size();
334 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
335 std::list<Widget*>::iterator next = it; ++next;
336 int m = (*it)->maxSize().width() - (*it)->minSize().width();
337 if (m > 0 && m < each) {
338 free -= m;
339 if (free < 0) free = 0;
340 adjustable.erase(it);
341 break; // if one is found to be fixed, then the free space needs to
342 // change, and the rest need to be reexamined
343 }
344 it = next;
345 }
346 } while (it != end && !adjustable.empty());
347 }
348
349 // place/size the widgets
350 if (!adjustable.empty())
351 each = free / adjustable.size();
352 else
353 each = 0;
354 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
355 int w;
356 // is the widget adjustable?
357 std::list<Widget*>::const_iterator
358 found = std::find(adjustable.begin(), adjustable.end(), *it);
359 if (found != adjustable.end()) {
360 // adjustable
361 w = (*it)->minSize().width() + each;
362 } else {
363 // fixed
364 w = (*it)->minSize().width();
365 }
366 // align it vertically
367 int yy = y;
368 int hh = std::max(std::min(h, (*it)->_max_size.height()),
369 (*it)->_min_size.height());
370 if (hh < h) {
371 switch(_alignment) {
372 case RenderStyle::RightBottomJustify:
373 yy += h - hh;
374 break;
375 case RenderStyle::CenterJustify:
376 yy += (h - hh) / 2;
377 break;
378 case RenderStyle::LeftTopJustify:
379 break;
380 }
381 }
382 (*it)->internal_moveresize(x, yy, w, hh);
383 (*it)->render();
384 (*it)->layout();
385 x += w + _bevel;
386 }
387 }
388
389 void Widget::layoutVert()
390 {
391 std::list<Widget*>::iterator it, end;
392
393 // work with just the visible children
394 std::list<Widget*> visible = _children;
395 for (it = visible.begin(), end = visible.end(); it != end;) {
396 std::list<Widget*>::iterator next = it; ++next;
397 if (!(*it)->visible())
398 visible.erase(it);
399 it = next;
400 }
401
402 if (visible.empty()) return;
403
404 int x, y, w, h; // working area
405 x = y = _bevel;
406 w = _area.width() - _borderwidth * 2 - _bevel * 2;
407 h = _area.height() - _borderwidth * 2 - _bevel * 2;
408 if (w < 0 || h < 0) return; // not worth laying anything out!
409
410 int free = h - (visible.size() - 1) * _bevel;
411 if (free < 0) free = 0;
412 int each;
413
414 std::list<Widget*> adjustable = visible;
415
416 // find the 'free' space, and how many children will be using it
417 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
418 std::list<Widget*>::iterator next = it; ++next;
419 free -= (*it)->minSize().height();
420 if (free < 0) free = 0;
421 if ((*it)->maxSize().height() - (*it)->minSize().height() <= 0)
422 adjustable.erase(it);
423 it = next;
424 }
425 // some widgets may have max heights that restrict them, find the 'true'
426 // amount of free space after these widgets are not included
427 if (!adjustable.empty()) {
428 do {
429 each = free / adjustable.size();
430 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
431 std::list<Widget*>::iterator next = it; ++next;
432 int m = (*it)->maxSize().height() - (*it)->minSize().height();
433 if (m > 0 && m < each) {
434 free -= m;
435 if (free < 0) free = 0;
436 adjustable.erase(it);
437 break; // if one is found to be fixed, then the free space needs to
438 // change, and the rest need to be reexamined
439 }
440 it = next;
441 }
442 } while (it != end && !adjustable.empty());
443 }
444
445 // place/size the widgets
446 if (!adjustable.empty())
447 each = free / adjustable.size();
448 else
449 each = 0;
450 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
451 int h;
452 // is the widget adjustable?
453 std::list<Widget*>::const_iterator
454 found = std::find(adjustable.begin(), adjustable.end(), *it);
455 if (found != adjustable.end()) {
456 // adjustable
457 h = (*it)->minSize().height() + each;
458 } else {
459 // fixed
460 h = (*it)->minSize().height();
461 }
462 // align it horizontally
463 int xx = x;
464 int ww = std::max(std::min(w, (*it)->_max_size.width()),
465 (*it)->_min_size.width());
466 if (ww < w) {
467 switch(_alignment) {
468 case RenderStyle::RightBottomJustify:
469 xx += w - ww;
470 break;
471 case RenderStyle::CenterJustify:
472 xx += (w - ww) / 2;
473 break;
474 case RenderStyle::LeftTopJustify:
475 break;
476 }
477 }
478 (*it)->internal_moveresize(xx, y, ww, h);
479 (*it)->render();
480 (*it)->layout();
481 y += h + _bevel;
482 }
483 }
484
485 void Widget::render()
486 {
487 if (!_dirty) return;
488 if (!_texture) {
489 // set a solid color as the default background
490 XSetWindowBackground(**display, _window,
491 RenderStyle::style(_screen)->
492 titlebarUnfocusBackground()->color().pixel());
493 return;
494 }
495 if (_borderwidth * 2 > _area.width() ||
496 _borderwidth * 2 > _area.height())
497 return; // no surface to draw on
498
499 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
500 _area.height() - _borderwidth * 2));
501 display->renderControl(_screen)->drawBackground(*s, *_texture);
502
503 renderForeground(*s); // for inherited types to render onto the _surface
504
505 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
506 XClearWindow(**display, _window);
507
508 // delete the old surface *after* its pixmap isn't in use anymore
509 if (_surface) delete _surface;
510
511 s->freePixelData(); // done rendering with this surface
512 _surface = s;
513
514 _dirty = false;
515 }
516
517 void Widget::renderChildren()
518 {
519 std::list<Widget*>::iterator it, end = _children.end();
520 for (it = _children.begin(); it != end; ++it)
521 (*it)->render();
522 }
523
524 void Widget::styleChanged(const RenderStyle &)
525 {
526 refresh();
527 }
528
529 void Widget::exposeHandler(const XExposeEvent &e)
530 {
531 EventHandler::exposeHandler(e);
532 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
533 }
534
535 void Widget::configureHandler(const XConfigureEvent &e)
536 {
537 if (_ignore_config) {
538 _ignore_config--;
539 } else {
540 // only interested in these for top level windows
541 if (_parent) return;
542
543 XEvent ev;
544 ev.xconfigure.width = e.width;
545 ev.xconfigure.height = e.height;
546 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
547
548 if (!(ev.xconfigure.width == area().width() &&
549 ev.xconfigure.height == area().height())) {
550 _area = Rect(_area.position(), Size(e.width, e.height));
551 update();
552 }
553 }
554 }
555
556 }
This page took 0.05731 seconds and 4 git commands to generate.