]> Dogcows Code - chaz/openbox/blob - otk/widget.cc
1a0c7d9ea40828b9a81daddb5e54f6b0d2bd38e8
[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 if (_parent) _parent->calcDefaultSizes();
90 else resize(_area.size()); // constrain sizes
91 _visible = true;
92 XMapWindow(**display, _window);
93 update();
94 }
95 }
96
97 void Widget::hide()
98 {
99 if (_visible) {
100 _visible = false;
101 XUnmapWindow(**display, _window);
102 if (_parent) {
103 _parent->calcDefaultSizes();
104 _parent->layout();
105 }
106 }
107 }
108
109 void Widget::setEventMask(long e)
110 {
111 XSelectInput(**display, _window, e);
112 _event_mask = e;
113 }
114
115 void Widget::update()
116 {
117 if (!_visible) return;
118 _dirty = true;
119 if (_parent) {
120 _parent->calcDefaultSizes();
121 _parent->layout(); // relay-out us and our siblings
122 } else {
123 render();
124 layout();
125 }
126 }
127
128 void Widget::moveresize(const Rect &r)
129 {
130 int w, h;
131 w = std::max(std::min(r.width(), maxSize().width()), minSize().width());
132 h = std::max(std::min(r.height(), maxSize().height()), minSize().height());
133
134 bool sizechange = !(w == area().width() && h == area().height());
135
136 if (r.x() == area().x() && r.y() == area().y() && !sizechange)
137 return; // no change, don't cause a big layout chain to occur!
138
139 internal_moveresize(r.x(), r.y(), w, h);
140
141 if (sizechange)
142 update();
143 }
144
145 void Widget::internal_moveresize(int x, int y, int w, int h)
146 {
147 assert(w > 0);
148 assert(h > 0);
149 assert(_borderwidth >= 0);
150 _dirty = true;
151 if (!(x == _area.x() && y == _area.y())) {
152 if (!(w == _area.width() && h == _area.height()))
153 XMoveResizeWindow(**display, _window, x, y,
154 w - _borderwidth * 2,
155 h - _borderwidth * 2);
156 else
157 XMoveWindow(**display, _window, x, y);
158 } else
159 XResizeWindow(**display, _window, w - _borderwidth*2, h - _borderwidth*2);
160 _ignore_config++;
161
162 _area = Rect(x, y, w, h);
163 }
164
165 void Widget::setAlignment(RenderStyle::Justify a)
166 {
167 _alignment = a;
168 layout();
169 }
170
171 void Widget::createWindow(bool overrideredir)
172 {
173 const ScreenInfo *info = display->screenInfo(_screen);
174 XSetWindowAttributes attrib;
175 unsigned long mask = CWEventMask | CWBorderPixel;
176
177 attrib.event_mask = _event_mask;
178 attrib.border_pixel = (_bordercolor ?
179 _bordercolor->pixel():
180 BlackPixel(**display, _screen));
181
182 if (overrideredir) {
183 mask |= CWOverrideRedirect;
184 attrib.override_redirect = true;
185 }
186
187 _window = XCreateWindow(**display, (_parent ?
188 _parent->_window :
189 RootWindow(**display, _screen)),
190 _area.x(), _area.y(),
191 _area.width(), _area.height(),
192 _borderwidth,
193 info->depth(),
194 InputOutput,
195 info->visual(),
196 mask,
197 &attrib);
198 assert(_window != None);
199 ++_ignore_config;
200 }
201
202 void Widget::calcDefaultSizes()
203 {
204 std::list<Widget*>::const_iterator it, end = _children.end();
205 int min_biggest = 0, max_biggest = 0;
206 int min_sum = _bevel + _borderwidth * 2;
207 int max_sum = _bevel + _borderwidth * 2;
208 bool fullmax = false;
209
210 for (it = _children.begin(); it != end; ++it) {
211 const otk::Size &min = (*it)->minSize();
212 const otk::Size &max = (*it)->maxSize();
213 if (_direction == Horizontal) {
214 if (min.height() > min_biggest) min_biggest = min.height();
215 if (max.height() > max_biggest) max_biggest = max.height();
216 min_sum += _bevel + min.width();
217 if (max.width() == INT_MAX)
218 fullmax = true;
219 else if (!fullmax)
220 max_sum += _bevel + max.width();
221 } else {
222 if (min.width() > min_biggest) min_biggest = min.width();
223 if (max.width() > max_biggest) max_biggest = max.width();
224 min_sum += _bevel + min.height();
225 if (max.height() == INT_MAX)
226 fullmax = true;
227 else if (!fullmax)
228 max_sum += _bevel + max.height();
229 }
230 }
231 if (_direction == Horizontal) {
232 _min_size = otk::Size(min_sum + (_bevel + _borderwidth) * 2,
233 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 + (_bevel + _borderwidth) * 2,
239 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;
300 for (it = _children.begin(), end = _children.end(); it != end; ++it)
301 if ((*it)->visible())
302 visible.push_back(*it);
303
304 if (visible.empty()) return;
305
306 int x, y, w, h; // working area
307 x = y = _bevel;
308 w = _area.width() - _borderwidth * 2 - _bevel * 2;
309 h = _area.height() - _borderwidth * 2 - _bevel * 2;
310 if (w < 0 || h < 0) return; // not worth laying anything out!
311
312 int free = w - (visible.size() - 1) * _bevel;
313 if (free < 0) free = 0;
314 int each;
315
316 std::list<Widget*> adjustable;
317
318 // find the 'free' space, and how many children will be using it
319 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
320 free -= (*it)->minSize().width();
321 if (free < 0) free = 0;
322 if ((*it)->maxSize().width() - (*it)->minSize().width() > 0)
323 adjustable.push_back(*it);
324 }
325 // some widgets may have max widths that restrict them, find the 'true'
326 // amount of free space after these widgets are not included
327 if (!adjustable.empty()) {
328 do {
329 each = free / adjustable.size();
330 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
331 std::list<Widget*>::iterator next = it; ++next;
332 int m = (*it)->maxSize().width() - (*it)->minSize().width();
333 if (m > 0 && m < each) {
334 free -= m;
335 if (free < 0) free = 0;
336 adjustable.erase(it);
337 break; // if one is found to be fixed, then the free space needs to
338 // change, and the rest need to be reexamined
339 }
340 it = next;
341 }
342 } while (it != end && !adjustable.empty());
343 }
344
345 // place/size the widgets
346 if (!adjustable.empty())
347 each = free / adjustable.size();
348 else
349 each = 0;
350 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
351 int w;
352 // is the widget adjustable?
353 std::list<Widget*>::const_iterator
354 found = std::find(adjustable.begin(), adjustable.end(), *it);
355 if (found != adjustable.end()) {
356 // adjustable
357 w = (*it)->minSize().width() + each;
358 } else {
359 // fixed
360 w = (*it)->minSize().width();
361 }
362 // align it vertically
363 int yy = y;
364 int hh = std::max(std::min(h, (*it)->_max_size.height()),
365 (*it)->_min_size.height());
366 if (hh < h) {
367 switch(_alignment) {
368 case RenderStyle::RightBottomJustify:
369 yy += h - hh;
370 break;
371 case RenderStyle::CenterJustify:
372 yy += (h - hh) / 2;
373 break;
374 case RenderStyle::LeftTopJustify:
375 break;
376 }
377 }
378 (*it)->internal_moveresize(x, yy, w, hh);
379 (*it)->render();
380 (*it)->layout();
381 x += w + _bevel;
382 }
383 }
384
385 void Widget::layoutVert()
386 {
387 std::list<Widget*>::iterator it, end;
388
389 // work with just the visible children
390 std::list<Widget*> visible;
391 for (it = _children.begin(), end = _children.end(); it != end; ++it)
392 if ((*it)->visible())
393 visible.push_back(*it);
394
395 if (visible.empty()) return;
396
397 int x, y, w, h; // working area
398 x = y = _bevel;
399 w = _area.width() - _borderwidth * 2 - _bevel * 2;
400 h = _area.height() - _borderwidth * 2 - _bevel * 2;
401 if (w < 0 || h < 0) return; // not worth laying anything out!
402
403 int free = h - (visible.size() - 1) * _bevel;
404 if (free < 0) free = 0;
405 int each;
406
407 std::list<Widget*> adjustable;
408
409 // find the 'free' space, and how many children will be using it
410 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
411 free -= (*it)->minSize().height();
412 if (free < 0) free = 0;
413 if ((*it)->maxSize().height() - (*it)->minSize().height() > 0)
414 adjustable.push_back(*it);
415 }
416 // some widgets may have max heights that restrict them, find the 'true'
417 // amount of free space after these widgets are not included
418 if (!adjustable.empty()) {
419 do {
420 each = free / adjustable.size();
421 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
422 std::list<Widget*>::iterator next = it; ++next;
423 int m = (*it)->maxSize().height() - (*it)->minSize().height();
424 if (m > 0 && m < each) {
425 free -= m;
426 if (free < 0) free = 0;
427 adjustable.erase(it);
428 break; // if one is found to be fixed, then the free space needs to
429 // change, and the rest need to be reexamined
430 }
431 it = next;
432 }
433 } while (it != end && !adjustable.empty());
434 }
435
436 // place/size the widgets
437 if (!adjustable.empty())
438 each = free / adjustable.size();
439 else
440 each = 0;
441 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
442 int h;
443 // is the widget adjustable?
444 std::list<Widget*>::const_iterator
445 found = std::find(adjustable.begin(), adjustable.end(), *it);
446 if (found != adjustable.end()) {
447 // adjustable
448 h = (*it)->minSize().height() + each;
449 } else {
450 // fixed
451 h = (*it)->minSize().height();
452 }
453 // align it horizontally
454 int xx = x;
455 int ww = std::max(std::min(w, (*it)->_max_size.width()),
456 (*it)->_min_size.width());
457 if (ww < w) {
458 switch(_alignment) {
459 case RenderStyle::RightBottomJustify:
460 xx += w - ww;
461 break;
462 case RenderStyle::CenterJustify:
463 xx += (w - ww) / 2;
464 break;
465 case RenderStyle::LeftTopJustify:
466 break;
467 }
468 }
469 (*it)->internal_moveresize(xx, y, ww, h);
470 (*it)->render();
471 (*it)->layout();
472 y += h + _bevel;
473 }
474 }
475
476 void Widget::render()
477 {
478 if (!_dirty) return;
479 if (!_texture) {
480 // set a solid color as the default background
481 XSetWindowBackground(**display, _window,
482 RenderStyle::style(_screen)->
483 titlebarUnfocusBackground()->color().pixel());
484 return;
485 }
486 if (_borderwidth * 2 > _area.width() ||
487 _borderwidth * 2 > _area.height())
488 return; // no surface to draw on
489
490 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
491 _area.height() - _borderwidth * 2));
492 display->renderControl(_screen)->drawBackground(*s, *_texture);
493
494 renderForeground(*s); // for inherited types to render onto the _surface
495
496 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
497 XClearWindow(**display, _window);
498
499 // delete the old surface *after* its pixmap isn't in use anymore
500 if (_surface) delete _surface;
501
502 s->freePixelData(); // done rendering with this surface
503 _surface = s;
504
505 _dirty = false;
506 }
507
508 void Widget::renderChildren()
509 {
510 std::list<Widget*>::iterator it, end = _children.end();
511 for (it = _children.begin(); it != end; ++it)
512 (*it)->render();
513 }
514
515 void Widget::styleChanged(const RenderStyle &)
516 {
517 refresh();
518 }
519
520 void Widget::exposeHandler(const XExposeEvent &e)
521 {
522 EventHandler::exposeHandler(e);
523 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
524 }
525
526 void Widget::configureHandler(const XConfigureEvent &e)
527 {
528 if (_ignore_config) {
529 _ignore_config--;
530 } else {
531 // only interested in these for top level windows
532 if (_parent) return;
533
534 XEvent ev;
535 ev.xconfigure.width = e.width;
536 ev.xconfigure.height = e.height;
537 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
538
539 if (!(ev.xconfigure.width == area().width() &&
540 ev.xconfigure.height == area().height())) {
541 _area = Rect(_area.position(), Size(e.width, e.height));
542 update();
543 }
544 }
545 }
546
547 }
This page took 0.059344 seconds and 4 git commands to generate.