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);
70 assert(_children.empty()); // this would be bad. theyd have a hanging _parent
72 if (_surface) delete _surface;
73 if (_parent) _parent->removeChild(this);
75 _dispatcher->clearHandler(_window);
76 XDestroyWindow(**display, _window);
79 void Widget::show(bool children)
82 std::list<Widget*>::iterator it , end = _children.end();
83 for (it = _children.begin(); it != end; ++it) {
89 XMapWindow(**display, _window);
98 XUnmapWindow(**display, _window);
99 if (_parent) _parent->layout();
103 void Widget::setEventMask(long e)
105 XSelectInput(**display, _window, e);
109 void Widget::update()
111 if (!_visible) return;
114 parent()->layout(); // relay-out us and our siblings
121 void Widget::moveresize(const Rect &r)
124 w = std::min(std::max(r.width(), minSize().width()), maxSize().width());
125 h = std::min(std::max(r.height(), minSize().height()), maxSize().height());
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!
132 internal_moveresize(r.x(), r.y(), w, h);
137 void Widget::internal_moveresize(int x, int y, int w, int h)
141 assert(_borderwidth >= 0);
143 XMoveResizeWindow(**display, _window, x, y,
144 w - _borderwidth * 2,
145 h - _borderwidth * 2);
148 _area = Rect(x, y, w, h);
151 void Widget::setAlignment(RenderStyle::Justify a)
157 void Widget::createWindow(bool overrideredir)
159 const ScreenInfo *info = display->screenInfo(_screen);
160 XSetWindowAttributes attrib;
161 unsigned long mask = CWEventMask | CWBorderPixel;
163 attrib.event_mask = _event_mask;
164 attrib.border_pixel = (_bordercolor ?
165 _bordercolor->pixel():
166 BlackPixel(**display, _screen));
169 mask |= CWOverrideRedirect;
170 attrib.override_redirect = true;
173 _window = XCreateWindow(**display, (_parent ?
175 RootWindow(**display, _screen)),
176 _area.x(), _area.y(),
177 _area.width(), _area.height(),
184 assert(_window != None);
188 void Widget::setBorderWidth(int w)
191 if (!parent()) return; // top-level windows cannot have borders
192 if (w == borderWidth()) return; // no change
195 XSetWindowBorderWidth(**display, _window, _borderwidth);
201 void Widget::setMinSize(const Size &s)
207 void Widget::setMaxSize(const Size &s)
213 void Widget::setBorderColor(const RenderColor *c)
216 XSetWindowBorder(**otk::display, _window,
217 c ? c->pixel() : BlackPixel(**otk::display, _screen));
220 void Widget::setBevel(int b)
227 void Widget::layout()
229 if (_children.empty() || !_visible) return;
230 if (_direction == Horizontal)
236 void Widget::layoutHorz()
238 std::list<Widget*>::iterator it, end;
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())
249 if (visible.empty()) return;
251 int x, y, w, h; // working area
253 w = _area.width() - _borderwidth * 2 - _bevel * 2;
254 h = _area.height() - _borderwidth * 2 - _bevel * 2;
255 if (w < 0 || h < 0) return; // not worth laying anything out!
257 int free = w - (visible.size() - 1) * _bevel;
258 if (free < 0) free = 0;
261 std::list<Widget*> adjustable = visible;
263 // find the 'free' space, and how many children will be using it
264 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
265 std::list<Widget*>::iterator next = it; ++next;
266 free -= (*it)->minSize().width();
267 if (free < 0) free = 0;
268 if ((*it)->maxSize().width() - (*it)->minSize().width() <= 0)
269 adjustable.erase(it);
272 // some widgets may have max widths that restrict them, find the 'true'
273 // amount of free space after these widgets are not included
274 if (!adjustable.empty()) {
276 each = free / adjustable.size();
277 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
278 std::list<Widget*>::iterator next = it; ++next;
279 int m = (*it)->maxSize().width() - (*it)->minSize().width();
280 if (m > 0 && m < each) {
282 if (free < 0) free = 0;
283 adjustable.erase(it);
284 break; // if one is found to be fixed, then the free space needs to
285 // change, and the rest need to be reexamined
289 } while (it != end && !adjustable.empty());
292 // place/size the widgets
293 if (!adjustable.empty())
294 each = free / adjustable.size();
297 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
299 // is the widget adjustable?
300 std::list<Widget*>::const_iterator
301 found = std::find(adjustable.begin(), adjustable.end(), *it);
302 if (found != adjustable.end()) {
304 w = (*it)->minSize().width() + each;
307 w = (*it)->minSize().width();
309 // align it vertically
311 int hh = std::max(std::min(h, (*it)->_max_size.height()),
312 (*it)->_min_size.height());
315 case RenderStyle::RightBottomJustify:
318 case RenderStyle::CenterJustify:
321 case RenderStyle::LeftTopJustify:
325 (*it)->internal_moveresize(x, yy, w, hh);
332 void Widget::layoutVert()
334 std::list<Widget*>::iterator it, end;
336 // work with just the visible children
337 std::list<Widget*> visible = _children;
338 for (it = visible.begin(), end = visible.end(); it != end;) {
339 std::list<Widget*>::iterator next = it; ++next;
340 if (!(*it)->visible())
345 if (visible.empty()) return;
347 int x, y, w, h; // working area
349 w = _area.width() - _borderwidth * 2 - _bevel * 2;
350 h = _area.height() - _borderwidth * 2 - _bevel * 2;
351 if (w < 0 || h < 0) return; // not worth laying anything out!
353 int free = h - (visible.size() - 1) * _bevel;
354 if (free < 0) free = 0;
357 std::list<Widget*> adjustable = visible;
359 // find the 'free' space, and how many children will be using it
360 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
361 std::list<Widget*>::iterator next = it; ++next;
362 free -= (*it)->minSize().height();
363 if (free < 0) free = 0;
364 if ((*it)->maxSize().height() - (*it)->minSize().height() <= 0)
365 adjustable.erase(it);
368 // some widgets may have max heights that restrict them, find the 'true'
369 // amount of free space after these widgets are not included
370 if (!adjustable.empty()) {
372 each = free / adjustable.size();
373 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
374 std::list<Widget*>::iterator next = it; ++next;
375 int m = (*it)->maxSize().height() - (*it)->minSize().height();
376 if (m > 0 && m < each) {
378 if (free < 0) free = 0;
379 adjustable.erase(it);
380 break; // if one is found to be fixed, then the free space needs to
381 // change, and the rest need to be reexamined
385 } while (it != end && !adjustable.empty());
388 // place/size the widgets
389 if (!adjustable.empty())
390 each = free / adjustable.size();
393 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
395 // is the widget adjustable?
396 std::list<Widget*>::const_iterator
397 found = std::find(adjustable.begin(), adjustable.end(), *it);
398 if (found != adjustable.end()) {
400 h = (*it)->minSize().height() + each;
403 h = (*it)->minSize().height();
405 // align it horizontally
407 int ww = std::max(std::min(w, (*it)->_max_size.width()),
408 (*it)->_min_size.width());
411 case RenderStyle::RightBottomJustify:
414 case RenderStyle::CenterJustify:
417 case RenderStyle::LeftTopJustify:
421 (*it)->internal_moveresize(xx, y, ww, h);
428 void Widget::render()
430 if (!_texture || !_dirty) return;
431 if (_borderwidth * 2 > _area.width() ||
432 _borderwidth * 2 > _area.height())
433 return; // no surface to draw on
435 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
436 _area.height() - _borderwidth * 2));
437 display->renderControl(_screen)->drawBackground(*s, *_texture);
439 renderForeground(*s); // for inherited types to render onto the _surface
441 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
442 XClearWindow(**display, _window);
444 // delete the old surface *after* its pixmap isn't in use anymore
445 if (_surface) delete _surface;
452 void Widget::renderChildren()
454 std::list<Widget*>::iterator it, end = _children.end();
455 for (it = _children.begin(); it != end; ++it)
459 void Widget::exposeHandler(const XExposeEvent &e)
461 EventHandler::exposeHandler(e);
462 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
465 void Widget::configureHandler(const XConfigureEvent &e)
467 if (_ignore_config) {
470 // only interested in these for top level windows
474 ev.xconfigure.width = e.width;
475 ev.xconfigure.height = e.height;
476 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
478 if (!(ev.xconfigure.width == area().width() &&
479 ev.xconfigure.height == area().height())) {
480 _area = Rect(_area.position(), Size(e.width, e.height));