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