unregister for events when destroyed
[mikachu/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
12 #include <algorithm>
13 #include <iostream>
14
15 namespace otk {
16
17 OtkWidget::OtkWidget(OtkWidget *parent, Direction direction)
18   : OtkEventHandler(),
19     _dirty(false),
20     _parent(parent), _style(parent->getStyle()), _direction(direction),
21     _cursor(parent->getCursor()), _bevel_width(parent->getBevelWidth()),
22     _ignore_config(0),
23     _visible(false), _focused(false), _grabbed_mouse(false),
24     _grabbed_keyboard(false), _stretchable_vert(false),
25     _stretchable_horz(false), _texture(0), _bg_pixmap(0), _bg_pixel(0),
26     _screen(parent->getScreen()), _fixed_width(false), _fixed_height(false),
27     _event_dispatcher(parent->getEventDispatcher()), _application(0)
28 {
29   assert(parent);
30   parent->addChild(this);
31   create();
32   _event_dispatcher->registerHandler(_window, this);
33 }
34
35 OtkWidget::OtkWidget(OtkEventDispatcher *event_dispatcher, Style *style,
36                      Direction direction, Cursor cursor, int bevel_width)
37   : OtkEventHandler(),
38     _dirty(false),
39     _parent(0), _style(style), _direction(direction), _cursor(cursor),
40     _bevel_width(bevel_width), _ignore_config(0), _visible(false),
41     _focused(false), _grabbed_mouse(false), _grabbed_keyboard(false),
42     _stretchable_vert(false), _stretchable_horz(false), _texture(0),
43     _bg_pixmap(0), _bg_pixel(0), _screen(style->getScreen()),
44     _fixed_width(false), _fixed_height(false),
45     _event_dispatcher(event_dispatcher), _application(0)
46 {
47   assert(event_dispatcher);
48   assert(style);
49   create();
50   _event_dispatcher->registerHandler(_window, this);
51 }
52
53 OtkWidget::~OtkWidget()
54 {
55   if (_visible)
56     hide();
57
58   _event_dispatcher->clearHandler(_window);
59
60   std::for_each(_children.begin(), _children.end(), PointerAssassin());
61
62   if (_parent)
63     _parent->removeChild(this);
64
65   XDestroyWindow(otk::OBDisplay::display, _window);
66 }
67
68 void OtkWidget::create(void)
69 {
70   const ScreenInfo *scr_info = otk::OBDisplay::screenInfo(_screen);
71   Window p_window = _parent ? _parent->getWindow() : scr_info->getRootWindow();
72
73   _rect.setRect(0, 0, 1, 1); // just some initial values
74
75   XSetWindowAttributes attrib_create;
76   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
77
78   attrib_create.background_pixmap = None;
79   attrib_create.colormap = scr_info->getColormap();
80   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
81     ButtonMotionMask | ExposureMask | StructureNotifyMask;
82
83   if (_cursor) {
84     create_mask |= CWCursor;
85     attrib_create.cursor = _cursor;
86   }
87
88   _window = XCreateWindow(otk::OBDisplay::display, p_window, _rect.x(),
89                           _rect.y(), _rect.width(), _rect.height(), 0,
90                           scr_info->getDepth(), InputOutput,
91                           scr_info->getVisual(), create_mask, &attrib_create);
92   _ignore_config++;
93 }
94
95 void OtkWidget::setWidth(int w)
96 {
97   assert(w > 0);
98   _fixed_width = true;  
99   setGeometry(_rect.x(), _rect.y(), w, _rect.height());
100 }
101
102 void OtkWidget::setHeight(int h)
103 {
104   assert(h > 0);
105   _fixed_height = true;
106   setGeometry(_rect.x(), _rect.y(), _rect.width(), h);
107 }
108
109 void OtkWidget::move(const Point &to)
110 {
111   move(to.x(), to.y());
112 }
113
114 void OtkWidget::move(int x, int y)
115 {
116   _rect.setPos(x, y);
117   XMoveWindow(otk::OBDisplay::display, _window, x, y);
118   _ignore_config++;
119 }
120
121 void OtkWidget::resize(const Point &to)
122 {
123   resize(to.x(), to.y());
124 }
125
126 void OtkWidget::resize(int w, int h)
127 {
128   assert(w > 0 && h > 0);
129   _fixed_width = _fixed_height = true;
130   setGeometry(_rect.x(), _rect.y(), w, h);
131 }
132
133 void OtkWidget::setGeometry(const Rect &new_geom)
134 {
135   setGeometry(new_geom.x(), new_geom.y(), new_geom.width(), new_geom.height());
136 }
137  
138 void OtkWidget::setGeometry(const Point &topleft, int width, int height)
139 {
140   setGeometry(topleft.x(), topleft.y(), width, height);
141 }
142
143 void OtkWidget::setGeometry(int x, int y, int width, int height)
144 {
145   _rect = Rect(x, y, width, height);
146   _dirty = true;
147
148   XMoveResizeWindow(otk::OBDisplay::display, _window, x, y, width, height);
149   _ignore_config++;
150 }
151
152 void OtkWidget::show(bool recursive)
153 {
154   if (_visible)
155     return;
156
157   // make sure the internal state isn't mangled
158   if (_dirty)
159     update();
160
161   if (recursive) {
162     OtkWidgetList::iterator it = _children.begin(), end = _children.end();
163     for (; it != end; ++it)
164       (*it)->show();
165   }
166
167   XMapWindow(otk::OBDisplay::display, _window);
168   _visible = true;
169 }
170
171 void OtkWidget::hide(bool recursive)
172 {
173   if (! _visible)
174     return;
175
176   if (recursive) {
177     OtkWidgetList::iterator it = _children.begin(), end = _children.end();
178     for (; it != end; ++it)
179       (*it)->hide();
180   }
181   
182   XUnmapWindow(otk::OBDisplay::display, _window);
183   _visible = false;
184 }
185
186 void OtkWidget::focus(void)
187 {
188   if (! _visible)
189     return;
190
191   XSetInputFocus(otk::OBDisplay::display, _window, RevertToPointerRoot,
192                  CurrentTime);
193 }
194
195 bool OtkWidget::grabMouse(void)
196 {
197   Status ret = XGrabPointer(otk::OBDisplay::display, _window, True,
198                             (ButtonPressMask | ButtonReleaseMask |
199                              ButtonMotionMask | EnterWindowMask |
200                              LeaveWindowMask | PointerMotionMask),
201                             GrabModeSync, GrabModeAsync, None, None,
202                             CurrentTime);
203   _grabbed_mouse = (ret == GrabSuccess);
204   return _grabbed_mouse;
205 }
206
207 void OtkWidget::ungrabMouse(void)
208 {
209   if (! _grabbed_mouse)
210     return;
211
212   XUngrabPointer(otk::OBDisplay::display, CurrentTime);
213   _grabbed_mouse = false;
214 }
215
216 bool OtkWidget::grabKeyboard(void)
217 {
218   Status ret = XGrabKeyboard(otk::OBDisplay::display, _window, True,
219                              GrabModeSync, GrabModeAsync, CurrentTime);
220   _grabbed_keyboard = (ret == GrabSuccess);
221   return _grabbed_keyboard;
222
223 }
224
225 void OtkWidget::ungrabKeyboard(void)
226 {
227   if (! _grabbed_keyboard)
228     return;
229
230   XUngrabKeyboard(otk::OBDisplay::display, CurrentTime);
231   _grabbed_keyboard = false;
232 }
233
234 void OtkWidget::render(void)
235 {
236   if (!_texture) return;
237   
238   _bg_pixmap = _texture->render(_rect.width(), _rect.height(), _bg_pixmap);
239
240   if (_bg_pixmap)
241     XSetWindowBackgroundPixmap(otk::OBDisplay::display, _window, _bg_pixmap);
242   else {
243     unsigned int pix = _texture->color().pixel();
244     if (pix != _bg_pixel) {
245       _bg_pixel = pix;
246       XSetWindowBackground(otk::OBDisplay::display, _window, pix);
247     }
248   }
249 }
250
251 void OtkWidget::adjust(void)
252 {
253   if (_direction == Horizontal)
254     adjustHorz();
255   else
256     adjustVert();
257 }
258
259 void OtkWidget::adjustHorz(void)
260 {
261   if (_children.size() == 0)
262     return;
263
264   OtkWidget *tmp;
265   OtkWidgetList::iterator it, end = _children.end();
266
267   int tallest = 0;
268   int width = _bevel_width;
269   OtkWidgetList stretchable;
270
271   for (it = _children.begin(); it != end; ++it) {
272     tmp = *it;
273     if (tmp->isStretchableVert())
274       tmp->setHeight(_rect.height() > _bevel_width * 2 ?
275                      _rect.height() - _bevel_width * 2 : _bevel_width);
276     if (tmp->isStretchableHorz())
277       stretchable.push_back(tmp);
278     else
279       width += tmp->_rect.width() + _bevel_width;
280
281     if (tmp->_rect.height() > tallest)
282       tallest = tmp->_rect.height();
283   }
284
285   if (stretchable.size() > 0) {
286     OtkWidgetList::iterator str_it = stretchable.begin(),
287       str_end = stretchable.end();
288
289     int str_width = _rect.width() - width / stretchable.size();
290
291     for (; str_it != str_end; ++str_it)
292       (*str_it)->setWidth(str_width > _bevel_width ? str_width - _bevel_width
293                           : _bevel_width);
294   }
295
296   OtkWidget *prev_widget = 0;
297
298   for (it = _children.begin(); it != end; ++it) {
299     tmp = *it;
300     int x, y;
301
302     if (prev_widget)
303       x = prev_widget->_rect.x() + prev_widget->_rect.width() + _bevel_width;
304     else
305       x = _rect.x() + _bevel_width;
306     y = (tallest - tmp->_rect.height()) / 2 + _bevel_width;
307
308     tmp->move(x, y);
309
310     prev_widget = tmp;
311   }
312
313   internalResize(width, tallest + _bevel_width * 2);
314 }
315
316 void OtkWidget::adjustVert(void)
317 {
318   if (_children.size() == 0)
319     return;
320
321   OtkWidget *tmp;
322   OtkWidgetList::iterator it, end = _children.end();
323
324   int widest = 0;
325   int height = _bevel_width;
326   OtkWidgetList stretchable;
327
328   for (it = _children.begin(); it != end; ++it) {
329     tmp = *it;
330     if (tmp->isStretchableHorz())
331       tmp->setWidth(_rect.width() > _bevel_width * 2 ?
332                     _rect.width() - _bevel_width * 2 : _bevel_width);
333     if (tmp->isStretchableVert())
334       stretchable.push_back(tmp);
335     else
336       height += tmp->_rect.height() + _bevel_width;
337
338     if (tmp->_rect.width() > widest)
339       widest = tmp->_rect.width();
340   }
341
342   if (stretchable.size() > 0) {
343     OtkWidgetList::iterator str_it = stretchable.begin(),
344       str_end = stretchable.end();
345
346     int str_height = _rect.height() - height / stretchable.size();
347
348     for (; str_it != str_end; ++str_it)
349       (*str_it)->setHeight(str_height > _bevel_width ?
350                            str_height - _bevel_width : _bevel_width);
351   }
352
353   OtkWidget *prev_widget = 0;
354
355   for (it = _children.begin(); it != end; ++it) {
356     tmp = *it;
357     int x, y;
358
359     if (prev_widget)
360       y = prev_widget->_rect.y() + prev_widget->_rect.height() + _bevel_width;
361     else
362       y = _rect.y() + _bevel_width;
363     x = (widest - tmp->_rect.width()) / 2 + _bevel_width;
364
365     tmp->move(x, y);
366
367     prev_widget = tmp;
368   }
369
370   internalResize(widest + _bevel_width * 2, height);
371 }
372
373 void OtkWidget::update(void)
374 {
375   if (_dirty) {
376     if (! _unmanaged)
377     adjust();
378     render();
379     XClearWindow(OBDisplay::display, _window);
380   }
381
382   OtkWidgetList::iterator it = _children.begin(), end = _children.end();
383   for (; it != end; ++it)
384     (*it)->update();
385
386   _dirty = false;
387 }
388
389 void OtkWidget::internalResize(int w, int h)
390 {
391   assert(w > 0 && h > 0);
392
393   if (! _fixed_width && ! _fixed_height)
394     resize(w, h);
395   else if (! _fixed_width)
396     resize(w, _rect.height());
397   else if (! _fixed_height)
398     resize(_rect.width(), h);
399 }
400
401 void OtkWidget::addChild(OtkWidget *child, bool front)
402 {
403   assert(child);
404   if (front)
405     _children.push_front(child);
406   else
407     _children.push_back(child);
408 }
409
410 void OtkWidget::removeChild(OtkWidget *child)
411 {
412   assert(child);
413   OtkWidgetList::iterator it, end = _children.end();
414   for (it = _children.begin(); it != end; ++it) {
415     if ((*it) == child)
416       break;
417   }
418
419   if (it != _children.end())
420     _children.erase(it);
421 }
422
423 void OtkWidget::setEventDispatcher(OtkEventDispatcher *disp)
424 {
425   if (_event_dispatcher)
426     _event_dispatcher->clearHandler(_window);
427   _event_dispatcher = disp;
428   _event_dispatcher->registerHandler(_window, this);
429 }
430
431 void OtkWidget::exposeHandler(const XExposeEvent &e)
432 {
433   OtkEventHandler::exposeHandler(e);
434   _dirty = true;
435   update();
436 }
437
438 void OtkWidget::configureHandler(const XConfigureEvent &e)
439 {
440   OtkEventHandler::configureHandler(e);
441   if (_ignore_config) {
442     _ignore_config--;
443   } else {
444     if (!(e.width == _rect.width() && e.height == _rect.height())) {
445       _dirty = true;
446       _rect.setSize(e.width, e.height);
447     }
448     update();
449   }
450 }
451
452 }