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);
40 styleChanged(*RenderStyle::style(_screen));
43 Widget::Widget(Widget *parent, Direction direction, int bevel)
45 _screen(parent->_screen),
49 _event_mask(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
50 ExposureMask | StructureNotifyMask),
51 _alignment(RenderStyle::CenterJustify),
52 _direction(direction),
53 _max_size(INT_MAX, INT_MAX),
59 _dispatcher(parent->_dispatcher),
64 parent->addChild(this);
65 if (parent->visible()) parent->layout();
66 _dispatcher->registerHandler(_window, this);
67 styleChanged(*RenderStyle::style(_screen));
72 assert(_children.empty()); // this would be bad. theyd have a hanging _parent
74 if (_surface) delete _surface;
75 if (_parent) _parent->removeChild(this);
77 _dispatcher->clearHandler(_window);
78 XDestroyWindow(**display, _window);
81 void Widget::show(bool children)
84 std::list<Widget*>::iterator it , end = _children.end();
85 for (it = _children.begin(); it != end; ++it) {
91 if (_parent) _parent->calcDefaultSizes();
95 XMapWindow(**display, _window);
104 XUnmapWindow(**display, _window);
106 _parent->calcDefaultSizes();
112 void Widget::setEventMask(long e)
114 XSelectInput(**display, _window, e);
118 void Widget::update()
120 if (!_visible) return;
123 _parent->calcDefaultSizes();
124 _parent->layout(); // relay-out us and our siblings
131 void Widget::moveresize(const Rect &r)
134 w = std::max(std::min(r.width(), maxSize().width()), minSize().width());
135 h = std::max(std::min(r.height(), maxSize().height()), minSize().height());
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!
142 internal_moveresize(r.x(), r.y(), w, h);
147 void Widget::internal_moveresize(int x, int y, int w, int h)
151 assert(_borderwidth >= 0);
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);
159 XMoveWindow(**display, _window, x, y);
161 XResizeWindow(**display, _window, w - _borderwidth*2, h - _borderwidth*2);
164 _area = Rect(x, y, w, h);
167 void Widget::setAlignment(RenderStyle::Justify a)
173 void Widget::createWindow(bool overrideredir)
175 const ScreenInfo *info = display->screenInfo(_screen);
176 XSetWindowAttributes attrib;
177 unsigned long mask = CWEventMask | CWBorderPixel;
179 attrib.event_mask = _event_mask;
180 attrib.border_pixel = (_bordercolor ?
181 _bordercolor->pixel():
182 BlackPixel(**display, _screen));
185 mask |= CWOverrideRedirect;
186 attrib.override_redirect = true;
189 _window = XCreateWindow(**display, (_parent ?
191 RootWindow(**display, _screen)),
192 _area.x(), _area.y(),
193 _area.width(), _area.height(),
200 assert(_window != None);
204 void Widget::calcDefaultSizes()
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;
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)
222 max_sum += _bevel + max.width();
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)
230 max_sum += _bevel + max.height();
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),
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));
246 void Widget::setBorderWidth(int w)
249 if (!parent()) return; // top-level windows cannot have borders
250 if (w == borderWidth()) return; // no change
253 XSetWindowBorderWidth(**display, _window, _borderwidth);
259 void Widget::setMinSize(const Size &s)
265 void Widget::setMaxSize(const Size &s)
271 void Widget::setBorderColor(const RenderColor *c)
274 XSetWindowBorder(**otk::display, _window,
275 c ? c->pixel() : BlackPixel(**otk::display, _screen));
278 void Widget::setBevel(int b)
285 void Widget::layout()
287 if (_children.empty() || !_visible) return;
288 if (_direction == Horizontal)
294 void Widget::layoutHorz()
296 std::list<Widget*>::iterator it, end;
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())
307 if (visible.empty()) return;
309 int x, y, w, h; // working area
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!
315 int free = w - (visible.size() - 1) * _bevel;
316 if (free < 0) free = 0;
319 std::list<Widget*> adjustable = visible;
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);
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()) {
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) {
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
347 } while (it != end && !adjustable.empty());
350 // place/size the widgets
351 if (!adjustable.empty())
352 each = free / adjustable.size();
355 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
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()) {
362 w = (*it)->minSize().width() + each;
365 w = (*it)->minSize().width();
367 // align it vertically
369 int hh = std::max(std::min(h, (*it)->_max_size.height()),
370 (*it)->_min_size.height());
373 case RenderStyle::RightBottomJustify:
376 case RenderStyle::CenterJustify:
379 case RenderStyle::LeftTopJustify:
383 (*it)->internal_moveresize(x, yy, w, hh);
390 void Widget::layoutVert()
392 std::list<Widget*>::iterator it, end;
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())
403 if (visible.empty()) return;
405 int x, y, w, h; // working area
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!
411 int free = h - (visible.size() - 1) * _bevel;
412 if (free < 0) free = 0;
415 std::list<Widget*> adjustable = visible;
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);
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()) {
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) {
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
443 } while (it != end && !adjustable.empty());
446 // place/size the widgets
447 if (!adjustable.empty())
448 each = free / adjustable.size();
451 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
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()) {
458 h = (*it)->minSize().height() + each;
461 h = (*it)->minSize().height();
463 // align it horizontally
465 int ww = std::max(std::min(w, (*it)->_max_size.width()),
466 (*it)->_min_size.width());
469 case RenderStyle::RightBottomJustify:
472 case RenderStyle::CenterJustify:
475 case RenderStyle::LeftTopJustify:
479 (*it)->internal_moveresize(xx, y, ww, h);
486 void Widget::render()
488 if (!_texture || !_dirty) return;
489 if (_borderwidth * 2 > _area.width() ||
490 _borderwidth * 2 > _area.height())
491 return; // no surface to draw on
493 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
494 _area.height() - _borderwidth * 2));
495 display->renderControl(_screen)->drawBackground(*s, *_texture);
497 renderForeground(*s); // for inherited types to render onto the _surface
499 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
500 XClearWindow(**display, _window);
502 // delete the old surface *after* its pixmap isn't in use anymore
503 if (_surface) delete _surface;
510 void Widget::renderChildren()
512 std::list<Widget*>::iterator it, end = _children.end();
513 for (it = _children.begin(); it != end; ++it)
517 void Widget::styleChanged(const RenderStyle &style)
519 _texture = style.titlebarUnfocusBackground();
522 void Widget::exposeHandler(const XExposeEvent &e)
524 EventHandler::exposeHandler(e);
525 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
528 void Widget::configureHandler(const XConfigureEvent &e)
530 if (_ignore_config) {
533 // only interested in these for top level windows
537 ev.xconfigure.width = e.width;
538 ev.xconfigure.height = e.height;
539 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
541 if (!(ev.xconfigure.width == area().width() &&
542 ev.xconfigure.height == area().height())) {
543 _area = Rect(_area.position(), Size(e.width, e.height));