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) {
89 if (_parent) _parent->calcDefaultSizes();
90 else resize(_area.size()); // constrain sizes
92 XMapWindow(**display, _window);
101 XUnmapWindow(**display, _window);
103 _parent->calcDefaultSizes();
109 void Widget::setEventMask(long e)
111 XSelectInput(**display, _window, e);
115 void Widget::update()
117 if (!_visible) return;
120 _parent->calcDefaultSizes();
121 _parent->layout(); // relay-out us and our siblings
128 void Widget::moveresize(const Rect &r)
131 w = std::max(std::min(r.width(), maxSize().width()), minSize().width());
132 h = std::max(std::min(r.height(), maxSize().height()), minSize().height());
134 bool sizechange = !(w == area().width() && h == area().height());
136 if (r.x() == area().x() && r.y() == area().y() && !sizechange)
137 return; // no change, don't cause a big layout chain to occur!
139 internal_moveresize(r.x(), r.y(), w, h);
145 void Widget::internal_moveresize(int x, int y, int w, int h)
149 assert(_borderwidth >= 0);
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);
157 XMoveWindow(**display, _window, x, y);
159 XResizeWindow(**display, _window, w - _borderwidth*2, h - _borderwidth*2);
162 _area = Rect(x, y, w, h);
165 void Widget::setAlignment(RenderStyle::Justify a)
171 void Widget::createWindow(bool overrideredir)
173 const ScreenInfo *info = display->screenInfo(_screen);
174 XSetWindowAttributes attrib;
175 unsigned long mask = CWEventMask | CWBorderPixel;
177 attrib.event_mask = _event_mask;
178 attrib.border_pixel = (_bordercolor ?
179 _bordercolor->pixel():
180 BlackPixel(**display, _screen));
183 mask |= CWOverrideRedirect;
184 attrib.override_redirect = true;
187 _window = XCreateWindow(**display, (_parent ?
189 RootWindow(**display, _screen)),
190 _area.x(), _area.y(),
191 _area.width(), _area.height(),
198 assert(_window != None);
202 void Widget::calcDefaultSizes()
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;
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)
220 max_sum += _bevel + max.width();
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)
228 max_sum += _bevel + max.height();
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),
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));
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;
300 for (it = _children.begin(), end = _children.end(); it != end; ++it)
301 if ((*it)->visible())
302 visible.push_back(*it);
304 if (visible.empty()) return;
306 int x, y, w, h; // working area
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!
312 int free = w - (visible.size() - 1) * _bevel;
313 if (free < 0) free = 0;
316 std::list<Widget*> adjustable;
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);
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()) {
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) {
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
342 } while (it != end && !adjustable.empty());
345 // place/size the widgets
346 if (!adjustable.empty())
347 each = free / adjustable.size();
350 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
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()) {
357 w = (*it)->minSize().width() + each;
360 w = (*it)->minSize().width();
362 // align it vertically
364 int hh = std::max(std::min(h, (*it)->_max_size.height()),
365 (*it)->_min_size.height());
368 case RenderStyle::RightBottomJustify:
371 case RenderStyle::CenterJustify:
374 case RenderStyle::LeftTopJustify:
378 (*it)->internal_moveresize(x, yy, w, hh);
385 void Widget::layoutVert()
387 std::list<Widget*>::iterator it, end;
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);
395 if (visible.empty()) return;
397 int x, y, w, h; // working area
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!
403 int free = h - (visible.size() - 1) * _bevel;
404 if (free < 0) free = 0;
407 std::list<Widget*> adjustable;
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);
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()) {
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) {
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
433 } while (it != end && !adjustable.empty());
436 // place/size the widgets
437 if (!adjustable.empty())
438 each = free / adjustable.size();
441 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
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()) {
448 h = (*it)->minSize().height() + each;
451 h = (*it)->minSize().height();
453 // align it horizontally
455 int ww = std::max(std::min(w, (*it)->_max_size.width()),
456 (*it)->_min_size.width());
459 case RenderStyle::RightBottomJustify:
462 case RenderStyle::CenterJustify:
465 case RenderStyle::LeftTopJustify:
469 (*it)->internal_moveresize(xx, y, ww, h);
476 void Widget::render()
480 // set a solid color as the default background
481 XSetWindowBackground(**display, _window,
482 RenderStyle::style(_screen)->
483 titlebarUnfocusBackground()->color().pixel());
486 if (_borderwidth * 2 > _area.width() ||
487 _borderwidth * 2 > _area.height())
488 return; // no surface to draw on
490 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
491 _area.height() - _borderwidth * 2));
492 display->renderControl(_screen)->drawBackground(*s, *_texture);
494 renderForeground(*s); // for inherited types to render onto the _surface
496 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
497 XClearWindow(**display, _window);
499 // delete the old surface *after* its pixmap isn't in use anymore
500 if (_surface) delete _surface;
502 s->freePixelData(); // done rendering with this surface
508 void Widget::renderChildren()
510 std::list<Widget*>::iterator it, end = _children.end();
511 for (it = _children.begin(); it != end; ++it)
515 void Widget::styleChanged(const RenderStyle &)
520 void Widget::exposeHandler(const XExposeEvent &e)
522 EventHandler::exposeHandler(e);
523 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
526 void Widget::configureHandler(const XConfigureEvent &e)
528 if (_ignore_config) {
531 // only interested in these for top level windows
535 ev.xconfigure.width = e.width;
536 ev.xconfigure.height = e.height;
537 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
539 if (!(ev.xconfigure.width == area().width() &&
540 ev.xconfigure.height == area().height())) {
541 _area = Rect(_area.position(), Size(e.width, e.height));