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