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