]> icculus.org git repositories - dana/openbox.git/blob - otk/widget.cc
make parents dirty when a widget resizes
[dana/openbox.git] / otk / widget.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef    HAVE_CONFIG_H
4 #  include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 #include "widget.hh"
8 #include "display.hh"
9 #include "assassin.hh"
10 #include "screeninfo.hh"
11 #include "focuslabel.hh"
12 #include <algorithm>
13 #include <iostream>
14
15 namespace otk {
16
17 Widget::Widget(Widget *parent, Direction direction)
18   : EventHandler(),
19     _dirty(false), _focused(false),
20     _parent(parent), _style(parent->style()), _direction(direction),
21     _cursor(parent->cursor()), _bevel_width(parent->bevelWidth()),
22     _ignore_config(0),
23     _visible(false), _grabbed_mouse(false),
24     _grabbed_keyboard(false), _stretchable_vert(false),
25     _stretchable_horz(false), _texture(0), _bg_pixmap(0), _bg_pixel(0),
26     _bcolor(0), _bwidth(0), _rect(0, 0, 1, 1), _screen(parent->screen()),
27     _fixed_width(false), _fixed_height(false),
28     _surface(0),
29     _event_dispatcher(parent->eventDispatcher())
30 {
31   assert(parent);
32   parent->addChild(this);
33   create();
34   _event_dispatcher->registerHandler(_window, this);
35 }
36
37 Widget::Widget(EventDispatcher *event_dispatcher, RenderStyle *style,
38                Direction direction, Cursor cursor, int bevel_width,
39                bool override_redirect)
40   : EventHandler(),
41     _dirty(false),_focused(false),
42     _parent(0), _style(style), _direction(direction), _cursor(cursor),
43     _bevel_width(bevel_width), _ignore_config(0), _visible(false),
44     _grabbed_mouse(false), _grabbed_keyboard(false),
45     _stretchable_vert(false), _stretchable_horz(false), _texture(0),
46     _bg_pixmap(0), _bg_pixel(0), _bcolor(0), _bwidth(0), _rect(0, 0, 1, 1),
47     _screen(style->screen()), _fixed_width(false), _fixed_height(false),
48     _surface(0),
49     _event_dispatcher(event_dispatcher)
50 {
51   assert(event_dispatcher);
52   assert(style);
53   create(override_redirect);
54   _event_dispatcher->registerHandler(_window, this);
55 }
56
57 Widget::~Widget()
58 {
59   if (_visible)
60     hide();
61
62   _event_dispatcher->clearHandler(_window);
63
64   std::for_each(_children.begin(), _children.end(), PointerAssassin());
65
66   if (_parent)
67     _parent->removeChild(this);
68
69   XDestroyWindow(**display, _window);
70 }
71
72 void Widget::create(bool override_redirect)
73 {
74   const ScreenInfo *scr_info = display->screenInfo(_screen);
75   Window p_window = _parent ? _parent->window() : scr_info->rootWindow();
76
77   _rect.setRect(0, 0, 1, 1); // just some initial values
78
79   XSetWindowAttributes attrib_create;
80   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
81
82   attrib_create.background_pixmap = None;
83   attrib_create.colormap = scr_info->colormap();
84   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
85     ButtonMotionMask | ExposureMask | StructureNotifyMask;
86
87   if (override_redirect) {
88     create_mask |= CWOverrideRedirect;
89     attrib_create.override_redirect = true;
90   }
91
92   if (_cursor) {
93     create_mask |= CWCursor;
94     attrib_create.cursor = _cursor;
95   }
96
97   _window = XCreateWindow(**display, p_window, _rect.x(),
98                           _rect.y(), _rect.width(), _rect.height(), 0,
99                           scr_info->depth(), InputOutput,
100                           scr_info->visual(), create_mask, &attrib_create);
101   _ignore_config++;
102 }
103
104 void Widget::setWidth(int w)
105 {
106   assert(w > 0);
107   _fixed_width = true;  
108   setGeometry(_rect.x(), _rect.y(), w, _rect.height());
109 }
110
111 void Widget::setHeight(int h)
112 {
113   assert(h > 0);
114   _fixed_height = true;
115   setGeometry(_rect.x(), _rect.y(), _rect.width(), h);
116 }
117
118 void Widget::move(const Point &to)
119 {
120   move(to.x(), to.y());
121 }
122
123 void Widget::move(int x, int y)
124 {
125   _rect.setPos(x, y);
126   XMoveWindow(**display, _window, x, y);
127   _ignore_config++;
128 }
129
130 void Widget::resize(const Point &to)
131 {
132   resize(to.x(), to.y());
133 }
134
135 void Widget::resize(int w, int h)
136 {
137   assert(w > 0 && h > 0);
138   _fixed_width = _fixed_height = true;
139   setGeometry(_rect.x(), _rect.y(), w, h);
140 }
141
142 void Widget::setGeometry(const Rect &new_geom)
143 {
144   setGeometry(new_geom.x(), new_geom.y(), new_geom.width(), new_geom.height());
145 }
146  
147 void Widget::setGeometry(const Point &topleft, int width, int height)
148 {
149   setGeometry(topleft.x(), topleft.y(), width, height);
150 }
151
152 void Widget::setGeometry(int x, int y, int width, int height)
153 {
154   _rect = Rect(x, y, width, height);
155   _dirty = true;
156
157   // make all parents dirty too
158   Widget *p = _parent;
159   while (p) {
160     p->_dirty = true;
161     p = p->_parent;
162   }
163
164   // don't use an XMoveResizeWindow here, because it doesn't seem to move
165   // windows with StaticGravity? This works, that didn't.
166   XResizeWindow(**display, _window, width, height);
167   XMoveWindow(**display, _window, x, y);
168   _ignore_config+=2;
169 }
170
171 void Widget::show(bool recursive)
172 {
173   if (_visible)
174     return;
175
176   // make sure the internal state isn't mangled
177   if (_dirty)
178     update();
179
180   if (recursive) {
181     WidgetList::iterator it = _children.begin(), end = _children.end();
182     for (; it != end; ++it)
183       (*it)->show(recursive);
184   }
185
186   XMapWindow(**display, _window);
187   _visible = true;
188 }
189
190 void Widget::hide(bool recursive)
191 {
192   if (! _visible)
193     return;
194
195   if (recursive) {
196     WidgetList::iterator it = _children.begin(), end = _children.end();
197     for (; it != end; ++it)
198       (*it)->hide();
199   }
200   
201   XUnmapWindow(**display, _window);
202   _visible = false;
203 }
204
205 void Widget::focus(void)
206 {
207   _focused = true;
208   
209   Widget::WidgetList::iterator it = _children.begin(),
210     end = _children.end();
211   for (; it != end; ++it)
212     (*it)->focus();
213 }
214
215 void Widget::unfocus(void)
216 {
217   _focused = false;
218   
219   Widget::WidgetList::iterator it = _children.begin(),
220     end = _children.end();
221   for (; it != end; ++it)
222     (*it)->unfocus();
223 }
224
225 bool Widget::grabMouse(void)
226 {
227   Status ret = XGrabPointer(**display, _window, True,
228                             (ButtonPressMask | ButtonReleaseMask |
229                              ButtonMotionMask | EnterWindowMask |
230                              LeaveWindowMask | PointerMotionMask),
231                             GrabModeSync, GrabModeAsync, None, None,
232                             CurrentTime);
233   _grabbed_mouse = (ret == GrabSuccess);
234   return _grabbed_mouse;
235 }
236
237 void Widget::ungrabMouse(void)
238 {
239   if (! _grabbed_mouse)
240     return;
241
242   XUngrabPointer(**display, CurrentTime);
243   _grabbed_mouse = false;
244 }
245
246 bool Widget::grabKeyboard(void)
247 {
248   Status ret = XGrabKeyboard(**display, _window, True,
249                              GrabModeSync, GrabModeAsync, CurrentTime);
250   _grabbed_keyboard = (ret == GrabSuccess);
251   return _grabbed_keyboard;
252
253 }
254
255 void Widget::ungrabKeyboard(void)
256 {
257   if (! _grabbed_keyboard)
258     return;
259
260   XUngrabKeyboard(**display, CurrentTime);
261   _grabbed_keyboard = false;
262 }
263
264 void Widget::render(void)
265 {
266   if (!_texture) return;
267
268   Surface *s = _surface; // save the current surface
269   
270   _surface = new Surface(_screen, _rect.size());
271   display->renderControl(_screen)->drawBackground(*_surface, *_texture);
272
273   renderForeground(); // for inherited types to render onto the _surface
274
275   XSetWindowBackgroundPixmap(**display, _window, _surface->pixmap());
276
277   delete s; // delete the old surface *after* its pixmap isn't in use anymore
278 }
279
280 void Widget::adjust(void)
281 {
282   if (_direction == Horizontal)
283     adjustHorz();
284   else
285     adjustVert();
286 }
287
288 void Widget::adjustHorz(void)
289 {
290   if (_children.size() == 0)
291     return;
292
293   Widget *tmp;
294   WidgetList::iterator it, end = _children.end();
295
296   int tallest = 0;
297   int width = _bevel_width;
298   WidgetList stretchable;
299
300   for (it = _children.begin(); it != end; ++it) {
301     tmp = *it;
302     if (tmp->isStretchableVert())
303       tmp->setHeight(_rect.height() > _bevel_width * 2 ?
304                      _rect.height() - _bevel_width * 2 : _bevel_width);
305     if (tmp->isStretchableHorz())
306       stretchable.push_back(tmp);
307     else
308       width += tmp->_rect.width() + _bevel_width;
309
310     if (tmp->_rect.height() > tallest)
311       tallest = tmp->_rect.height();
312   }
313
314   if (stretchable.size() > 0) {
315     WidgetList::iterator str_it = stretchable.begin(),
316       str_end = stretchable.end();
317
318     int str_width = _rect.width() - width / stretchable.size();
319
320     for (; str_it != str_end; ++str_it)
321       (*str_it)->setWidth(str_width > _bevel_width ? str_width - _bevel_width
322                           : _bevel_width);
323   }
324
325   Widget *prev_widget = 0;
326
327   for (it = _children.begin(); it != end; ++it) {
328     tmp = *it;
329     int x, y;
330
331     if (prev_widget)
332       x = prev_widget->_rect.x() + prev_widget->_rect.width() + _bevel_width;
333     else
334       x = _bevel_width;
335     y = (tallest - tmp->_rect.height()) / 2 + _bevel_width;
336
337     tmp->move(x, y);
338
339     prev_widget = tmp;
340   }
341   internalResize(width, tallest + _bevel_width * 2);
342 }
343
344 void Widget::adjustVert(void)
345 {
346   if (_children.size() == 0)
347     return;
348
349   Widget *tmp;
350   WidgetList::iterator it, end = _children.end();
351
352   int widest = 0;
353   int height = _bevel_width;
354   WidgetList stretchable;
355
356   for (it = _children.begin(); it != end; ++it) {
357     tmp = *it;
358     if (tmp->isStretchableHorz())
359       tmp->setWidth(_rect.width() > _bevel_width * 2 ?
360                     _rect.width() - _bevel_width * 2 : _bevel_width);
361     if (tmp->isStretchableVert())
362       stretchable.push_back(tmp);
363     else
364       height += tmp->_rect.height() + _bevel_width;
365
366     if (tmp->_rect.width() > widest)
367       widest = tmp->_rect.width();
368   }
369
370   if (stretchable.size() > 0) {
371     WidgetList::iterator str_it = stretchable.begin(),
372       str_end = stretchable.end();
373
374     int str_height = _rect.height() - height / stretchable.size();
375
376     for (; str_it != str_end; ++str_it)
377       (*str_it)->setHeight(str_height > _bevel_width ?
378                            str_height - _bevel_width : _bevel_width);
379   }
380
381   Widget *prev_widget = 0;
382
383   for (it = _children.begin(); it != end; ++it) {
384     tmp = *it;
385     int x, y;
386
387     if (prev_widget)
388       y = prev_widget->_rect.y() + prev_widget->_rect.height() + _bevel_width;
389     else
390       y = _bevel_width;
391     x = (widest - tmp->_rect.width()) / 2 + _bevel_width;
392
393     tmp->move(x, y);
394
395     prev_widget = tmp;
396   }
397
398   internalResize(widest + _bevel_width * 2, height);
399 }
400
401 void Widget::update()
402 {
403   if (_dirty) {
404     adjust();
405     render();
406     XClearWindow(**display, _window);
407   }
408
409   WidgetList::iterator it = _children.begin(), end = _children.end();
410   for (; it != end; ++it)
411     (*it)->update();
412
413   _dirty = false;
414 }
415
416 void Widget::internalResize(int w, int h)
417 {
418   assert(w > 0 && h > 0);
419
420   bool fw = _fixed_width, fh = _fixed_height;
421   
422   if (! fw && ! fh)
423     resize(w, h);
424   else if (! fw)
425     resize(w, _rect.height());
426   else if (! fh)
427     resize(_rect.width(), h);
428
429   _fixed_width = fw;
430   _fixed_height = fh;
431 }
432
433 void Widget::addChild(Widget *child, bool front)
434 {
435   assert(child);
436   if (front)
437     _children.push_front(child);
438   else
439     _children.push_back(child);
440 }
441
442 void Widget::removeChild(Widget *child)
443 {
444   assert(child);
445   WidgetList::iterator it, end = _children.end();
446   for (it = _children.begin(); it != end; ++it) {
447     if ((*it) == child)
448       break;
449   }
450
451   if (it != _children.end())
452     _children.erase(it);
453 }
454
455 void Widget::setStyle(RenderStyle *style)
456 {
457   assert(style);
458   _style = style;
459   _dirty = true;
460
461   WidgetList::iterator it, end = _children.end();
462   for (it = _children.begin(); it != end; ++it)
463     (*it)->setStyle(style);
464 }
465
466
467 void Widget::setEventDispatcher(EventDispatcher *disp)
468 {
469   if (_event_dispatcher)
470     _event_dispatcher->clearHandler(_window);
471   _event_dispatcher = disp;
472   _event_dispatcher->registerHandler(_window, this);
473 }
474
475 void Widget::exposeHandler(const XExposeEvent &e)
476 {
477   EventHandler::exposeHandler(e);
478 //  XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
479 }
480
481 void Widget::configureHandler(const XConfigureEvent &e)
482 {
483   EventHandler::configureHandler(e);
484
485   if (_ignore_config) {
486     _ignore_config--;
487   } else {
488     int width = e.width;
489     int height = e.height;
490
491     XEvent ev;
492     while (XCheckTypedWindowEvent(**display, _window, ConfigureNotify, &ev)) {
493       width = ev.xconfigure.width;
494       height = ev.xconfigure.height;
495     }
496
497     if (!(width == _rect.width() && height == _rect.height())) {
498       _dirty = true;
499       _rect.setSize(width, height);
500     }
501     update();
502   }
503 }
504
505 }