1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
7 #include "rendertexture.hh"
8 #include "rendercolor.hh"
9 #include "eventdispatcher.hh"
10 #include "screeninfo.hh"
18 Widget::Widget(int screen, EventDispatcher *ed, Direction direction, int bevel,
25 _event_mask(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
26 ExposureMask | StructureNotifyMask),
27 _alignment(RenderStyle::CenterJustify),
28 _direction(direction),
29 _max_size(INT_MAX, INT_MAX),
38 createWindow(overrideredir);
39 _dispatcher->registerHandler(_window, this);
42 Widget::Widget(Widget *parent, Direction direction, int bevel)
44 _screen(parent->_screen),
48 _event_mask(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
49 ExposureMask | StructureNotifyMask),
50 _alignment(RenderStyle::CenterJustify),
51 _direction(direction),
52 _max_size(INT_MAX, INT_MAX),
58 _dispatcher(parent->_dispatcher),
63 parent->addChild(this);
64 if (parent->visible()) parent->layout();
65 _dispatcher->registerHandler(_window, this);
66 styleChanged(*RenderStyle::style(_screen));
71 assert(_children.empty()); // this would be bad. theyd have a hanging _parent
73 if (_surface) delete _surface;
74 if (_parent) _parent->removeChild(this);
76 _dispatcher->clearHandler(_window);
77 XDestroyWindow(**display, _window);
80 void Widget::show(bool children)
83 std::list<Widget*>::iterator it , end = _children.end();
84 for (it = _children.begin(); it != end; ++it) {
90 if (_parent) _parent->calcDefaultSizes();
94 XMapWindow(**display, _window);
103 XUnmapWindow(**display, _window);
105 _parent->calcDefaultSizes();
111 void Widget::setEventMask(long e)
113 XSelectInput(**display, _window, e);
117 void Widget::update()
119 if (!_visible) return;
122 _parent->calcDefaultSizes();
123 _parent->layout(); // relay-out us and our siblings
130 void Widget::moveresize(const Rect &r)
133 w = std::max(std::min(r.width(), maxSize().width()), minSize().width());
134 h = std::max(std::min(r.height(), maxSize().height()), minSize().height());
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!
141 internal_moveresize(r.x(), r.y(), w, h);
146 void Widget::internal_moveresize(int x, int y, int w, int h)
150 assert(_borderwidth >= 0);
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);
158 XMoveWindow(**display, _window, x, y);
160 XResizeWindow(**display, _window, w - _borderwidth*2, h - _borderwidth*2);
163 _area = Rect(x, y, w, h);
166 void Widget::setAlignment(RenderStyle::Justify a)
172 void Widget::createWindow(bool overrideredir)
174 const ScreenInfo *info = display->screenInfo(_screen);
175 XSetWindowAttributes attrib;
176 unsigned long mask = CWEventMask | CWBorderPixel;
178 attrib.event_mask = _event_mask;
179 attrib.border_pixel = (_bordercolor ?
180 _bordercolor->pixel():
181 BlackPixel(**display, _screen));
184 mask |= CWOverrideRedirect;
185 attrib.override_redirect = true;
188 _window = XCreateWindow(**display, (_parent ?
190 RootWindow(**display, _screen)),
191 _area.x(), _area.y(),
192 _area.width(), _area.height(),
199 assert(_window != None);
203 void Widget::calcDefaultSizes()
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;
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)
221 max_sum += _bevel + max.width();
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)
229 max_sum += _bevel + max.height();
232 if (_direction == Horizontal) {
233 _min_size = otk::Size(min_sum + (_bevel + _borderwidth) * 2,
234 min_biggest + (_bevel + _borderwidth) * 2);
235 _max_size = otk::Size((fullmax ? INT_MAX :
236 max_sum + (_bevel + _borderwidth) * 2),
239 _min_size = otk::Size(min_biggest + (_bevel + _borderwidth) * 2,
240 min_sum + (_bevel + _borderwidth) * 2);
241 _max_size = otk::Size(max_biggest, (fullmax ? INT_MAX : max_sum +
242 (_bevel + _borderwidth) * 2));
247 void Widget::setBorderWidth(int w)
250 if (!parent()) return; // top-level windows cannot have borders
251 if (w == borderWidth()) return; // no change
254 XSetWindowBorderWidth(**display, _window, _borderwidth);
260 void Widget::setMinSize(const Size &s)
266 void Widget::setMaxSize(const Size &s)
272 void Widget::setBorderColor(const RenderColor *c)
275 XSetWindowBorder(**otk::display, _window,
276 c ? c->pixel() : BlackPixel(**otk::display, _screen));
279 void Widget::setBevel(int b)
286 void Widget::layout()
288 if (_children.empty() || !_visible) return;
289 if (_direction == Horizontal)
295 void Widget::layoutHorz()
297 std::list<Widget*>::iterator it, end;
299 // work with just the visible children
300 std::list<Widget*> visible = _children;
301 for (it = visible.begin(), end = visible.end(); it != end;) {
302 std::list<Widget*>::iterator next = it; ++next;
303 if (!(*it)->visible())
308 if (visible.empty()) return;
310 int x, y, w, h; // working area
312 w = _area.width() - _borderwidth * 2 - _bevel * 2;
313 h = _area.height() - _borderwidth * 2 - _bevel * 2;
314 if (w < 0 || h < 0) return; // not worth laying anything out!
316 int free = w - (visible.size() - 1) * _bevel;
317 if (free < 0) free = 0;
320 std::list<Widget*> adjustable = visible;
322 // find the 'free' space, and how many children will be using it
323 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
324 std::list<Widget*>::iterator next = it; ++next;
325 free -= (*it)->minSize().width();
326 if (free < 0) free = 0;
327 if ((*it)->maxSize().width() - (*it)->minSize().width() <= 0)
328 adjustable.erase(it);
331 // some widgets may have max widths that restrict them, find the 'true'
332 // amount of free space after these widgets are not included
333 if (!adjustable.empty()) {
335 each = free / adjustable.size();
336 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
337 std::list<Widget*>::iterator next = it; ++next;
338 int m = (*it)->maxSize().width() - (*it)->minSize().width();
339 if (m > 0 && m < each) {
341 if (free < 0) free = 0;
342 adjustable.erase(it);
343 break; // if one is found to be fixed, then the free space needs to
344 // change, and the rest need to be reexamined
348 } while (it != end && !adjustable.empty());
351 // place/size the widgets
352 if (!adjustable.empty())
353 each = free / adjustable.size();
356 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
358 // is the widget adjustable?
359 std::list<Widget*>::const_iterator
360 found = std::find(adjustable.begin(), adjustable.end(), *it);
361 if (found != adjustable.end()) {
363 w = (*it)->minSize().width() + each;
366 w = (*it)->minSize().width();
368 // align it vertically
370 int hh = std::max(std::min(h, (*it)->_max_size.height()),
371 (*it)->_min_size.height());
374 case RenderStyle::RightBottomJustify:
377 case RenderStyle::CenterJustify:
380 case RenderStyle::LeftTopJustify:
384 (*it)->internal_moveresize(x, yy, w, hh);
391 void Widget::layoutVert()
393 std::list<Widget*>::iterator it, end;
395 // work with just the visible children
396 std::list<Widget*> visible = _children;
397 for (it = visible.begin(), end = visible.end(); it != end;) {
398 std::list<Widget*>::iterator next = it; ++next;
399 if (!(*it)->visible())
404 if (visible.empty()) return;
406 int x, y, w, h; // working area
408 w = _area.width() - _borderwidth * 2 - _bevel * 2;
409 h = _area.height() - _borderwidth * 2 - _bevel * 2;
410 if (w < 0 || h < 0) return; // not worth laying anything out!
412 int free = h - (visible.size() - 1) * _bevel;
413 if (free < 0) free = 0;
416 std::list<Widget*> adjustable = visible;
418 // find the 'free' space, and how many children will be using it
419 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
420 std::list<Widget*>::iterator next = it; ++next;
421 free -= (*it)->minSize().height();
422 if (free < 0) free = 0;
423 if ((*it)->maxSize().height() - (*it)->minSize().height() <= 0)
424 adjustable.erase(it);
427 // some widgets may have max heights that restrict them, find the 'true'
428 // amount of free space after these widgets are not included
429 if (!adjustable.empty()) {
431 each = free / adjustable.size();
432 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
433 std::list<Widget*>::iterator next = it; ++next;
434 int m = (*it)->maxSize().height() - (*it)->minSize().height();
435 if (m > 0 && m < each) {
437 if (free < 0) free = 0;
438 adjustable.erase(it);
439 break; // if one is found to be fixed, then the free space needs to
440 // change, and the rest need to be reexamined
444 } while (it != end && !adjustable.empty());
447 // place/size the widgets
448 if (!adjustable.empty())
449 each = free / adjustable.size();
452 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
454 // is the widget adjustable?
455 std::list<Widget*>::const_iterator
456 found = std::find(adjustable.begin(), adjustable.end(), *it);
457 if (found != adjustable.end()) {
459 h = (*it)->minSize().height() + each;
462 h = (*it)->minSize().height();
464 // align it horizontally
466 int ww = std::max(std::min(w, (*it)->_max_size.width()),
467 (*it)->_min_size.width());
470 case RenderStyle::RightBottomJustify:
473 case RenderStyle::CenterJustify:
476 case RenderStyle::LeftTopJustify:
480 (*it)->internal_moveresize(xx, y, ww, h);
487 void Widget::render()
491 // set a solid color as the default background
492 XSetWindowBackground(**display, _window,
493 RenderStyle::style(_screen)->
494 titlebarUnfocusBackground()->color().pixel());
497 if (_borderwidth * 2 > _area.width() ||
498 _borderwidth * 2 > _area.height())
499 return; // no surface to draw on
501 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
502 _area.height() - _borderwidth * 2));
503 display->renderControl(_screen)->drawBackground(*s, *_texture);
505 renderForeground(*s); // for inherited types to render onto the _surface
507 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
508 XClearWindow(**display, _window);
510 // delete the old surface *after* its pixmap isn't in use anymore
511 if (_surface) delete _surface;
513 s->freePixelData(); // done rendering with this surface
519 void Widget::renderChildren()
521 std::list<Widget*>::iterator it, end = _children.end();
522 for (it = _children.begin(); it != end; ++it)
526 void Widget::styleChanged(const RenderStyle &)
531 void Widget::exposeHandler(const XExposeEvent &e)
533 EventHandler::exposeHandler(e);
534 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
537 void Widget::configureHandler(const XConfigureEvent &e)
539 if (_ignore_config) {
542 // only interested in these for top level windows
546 ev.xconfigure.width = e.width;
547 ev.xconfigure.height = e.height;
548 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
550 if (!(ev.xconfigure.width == area().width() &&
551 ev.xconfigure.height == area().height())) {
552 _area = Rect(_area.position(), Size(e.width, e.height));