]> icculus.org git repositories - mikachu/openbox.git/blob - otk/widget.cc
set the close protocol on the app's main widget
[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     _dirty(false),
14     _parent(parent), _style(parent->getStyle()), _direction(direction),
15     _cursor(parent->getCursor()), _bevel_width(parent->getBevelWidth()),
16     _ignore_config(0),
17     _visible(false), _focused(false), _grabbed_mouse(false),
18     _grabbed_keyboard(false), _stretchable_vert(false),
19     _stretchable_horz(false), _texture(0), _bg_pixmap(0), _bg_pixel(0),
20     _screen(parent->getScreen()), _fixed_width(false), _fixed_height(false),
21     _event_dispatcher(parent->getEventDispatcher())
22 {
23   parent->addChild(this);
24   create();
25   _event_dispatcher->registerHandler(_window, this);
26 }
27
28 OtkWidget::OtkWidget(OtkApplication *app, Direction direction,
29                      Cursor cursor, int bevel_width)
30   : OtkEventHandler(),
31     _dirty(false),
32     _parent(0), _style(app->getStyle()), _direction(direction), _cursor(cursor),
33     _bevel_width(bevel_width), _ignore_config(0), _visible(false),
34     _focused(false), _grabbed_mouse(false), _grabbed_keyboard(false),
35     _stretchable_vert(false), _stretchable_horz(false), _texture(0),
36     _bg_pixmap(0), _bg_pixel(0), _screen(app->getStyle()->getScreen()),
37     _fixed_width(false), _fixed_height(false), 
38     _event_dispatcher(app)
39 {
40   assert(app);
41   create();
42   _event_dispatcher->registerHandler(_window, this);
43   app->setMainWidget(this);
44 }
45
46 OtkWidget::OtkWidget(Style *style, Direction direction,
47                      Cursor cursor, int bevel_width)
48   : OtkEventHandler(),
49     _dirty(false),
50     _parent(0), _style(style), _direction(direction), _cursor(cursor),
51     _bevel_width(bevel_width), _ignore_config(0), _visible(false),
52     _focused(false), _grabbed_mouse(false), _grabbed_keyboard(false),
53     _stretchable_vert(false), _stretchable_horz(false), _texture(0),
54     _bg_pixmap(0), _bg_pixel(0), _screen(style->getScreen()),
55     _fixed_width(false), _fixed_height(false)
56 {
57   assert(style);
58   create();
59 }
60
61 OtkWidget::~OtkWidget()
62 {
63   if (_visible)
64     hide();
65
66   std::for_each(_children.begin(), _children.end(), PointerAssassin());
67
68   if (_parent)
69     _parent->removeChild(this);
70
71   XDestroyWindow(otk::OBDisplay::display, _window);
72 }
73
74 void OtkWidget::create(void)
75 {
76   const ScreenInfo *scr_info = otk::OBDisplay::screenInfo(_screen);
77   Window p_window = _parent ? _parent->getWindow() : scr_info->getRootWindow();
78
79   _rect.setRect(0, 0, 1, 1); // just some initial values
80
81   XSetWindowAttributes attrib_create;
82   unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWEventMask;
83
84   attrib_create.background_pixmap = None;
85   attrib_create.colormap = scr_info->getColormap();
86   attrib_create.event_mask = ButtonPressMask | ButtonReleaseMask |
87     ButtonMotionMask | ExposureMask | StructureNotifyMask;
88
89   if (_cursor) {
90     create_mask |= CWCursor;
91     attrib_create.cursor = _cursor;
92   }
93
94   _window = XCreateWindow(otk::OBDisplay::display, p_window, _rect.x(),
95                           _rect.y(), _rect.width(), _rect.height(), 0,
96                           scr_info->getDepth(), InputOutput,
97                           scr_info->getVisual(), create_mask, &attrib_create);
98   _ignore_config++;
99 }
100
101 void OtkWidget::setWidth(int w)
102 {
103   assert(w > 0);
104   _fixed_width = true;  
105   setGeometry(_rect.x(), _rect.y(), w, _rect.height());
106 }
107
108 void OtkWidget::setHeight(int h)
109 {
110   assert(h > 0);
111   _fixed_height = true;
112   setGeometry(_rect.x(), _rect.y(), _rect.width(), h);
113 }
114
115 void OtkWidget::move(const Point &to)
116 {
117   move(to.x(), to.y());
118 }
119
120 void OtkWidget::move(int x, int y)
121 {
122   _rect.setPos(x, y);
123   XMoveWindow(otk::OBDisplay::display, _window, x, y);
124   _ignore_config++;
125 }
126
127 void OtkWidget::resize(const Point &to)
128 {
129   resize(to.x(), to.y());
130 }
131
132 void OtkWidget::resize(int w, int h)
133 {
134   assert(w > 0 && h > 0);
135   _fixed_width = _fixed_height = true;
136   setGeometry(_rect.x(), _rect.y(), w, h);
137 }
138
139 void OtkWidget::setGeometry(const Rect &new_geom)
140 {
141   setGeometry(new_geom.x(), new_geom.y(), new_geom.width(), new_geom.height());
142 }
143  
144 void OtkWidget::setGeometry(const Point &topleft, int width, int height)
145 {
146   setGeometry(topleft.x(), topleft.y(), width, height);
147 }
148
149 void OtkWidget::setGeometry(int x, int y, int width, int height)
150 {
151   _rect = Rect(x, y, width, height);
152   _dirty = true;
153
154   XMoveResizeWindow(otk::OBDisplay::display, _window, x, y, width, height);
155   _ignore_config++;
156 }
157
158 void OtkWidget::show(void)
159 {
160   if (_visible)
161     return;
162
163   // make sure the internal state isn't mangled
164   if (_dirty)
165     update();
166
167   OtkWidgetList::iterator it = _children.begin(), end = _children.end();
168   for (; it != end; ++it)
169     (*it)->show();
170
171   XMapWindow(otk::OBDisplay::display, _window);
172   _visible = true;
173 }
174
175 void OtkWidget::hide(void)
176 {
177   if (! _visible)
178     return;
179
180   OtkWidgetList::iterator it = _children.begin(), end = _children.end();
181   for (; it != end; ++it)
182     (*it)->hide();
183
184   XUnmapWindow(otk::OBDisplay::display, _window);
185   _visible = false;
186 }
187
188 void OtkWidget::focus(void)
189 {
190   if (! _visible)
191     return;
192
193   XSetInputFocus(otk::OBDisplay::display, _window, RevertToPointerRoot,
194                  CurrentTime);
195 }
196
197 bool OtkWidget::grabMouse(void)
198 {
199   Status ret = XGrabPointer(otk::OBDisplay::display, _window, True,
200                             (ButtonPressMask | ButtonReleaseMask |
201                              ButtonMotionMask | EnterWindowMask |
202                              LeaveWindowMask | PointerMotionMask),
203                             GrabModeSync, GrabModeAsync, None, None,
204                             CurrentTime);
205   _grabbed_mouse = (ret == GrabSuccess);
206   return _grabbed_mouse;
207 }
208
209 void OtkWidget::ungrabMouse(void)
210 {
211   if (! _grabbed_mouse)
212     return;
213
214   XUngrabPointer(otk::OBDisplay::display, CurrentTime);
215   _grabbed_mouse = false;
216 }
217
218 bool OtkWidget::grabKeyboard(void)
219 {
220   Status ret = XGrabKeyboard(otk::OBDisplay::display, _window, True,
221                              GrabModeSync, GrabModeAsync, CurrentTime);
222   _grabbed_keyboard = (ret == GrabSuccess);
223   return _grabbed_keyboard;
224
225 }
226
227 void OtkWidget::ungrabKeyboard(void)
228 {
229   if (! _grabbed_keyboard)
230     return;
231
232   XUngrabKeyboard(otk::OBDisplay::display, CurrentTime);
233   _grabbed_keyboard = false;
234 }
235
236 void OtkWidget::render(void)
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     adjust();
377     render();
378     XClearWindow(OBDisplay::display, _window);
379   }
380
381   OtkWidgetList::iterator it = _children.begin(), end = _children.end();
382   for (; it != end; ++it)
383     (*it)->update();
384
385   _dirty = false;
386 }
387
388 void OtkWidget::internalResize(int w, int h)
389 {
390   assert(w > 0 && h > 0);
391
392   if (! _fixed_width && ! _fixed_height)
393     resize(w, h);
394   else if (! _fixed_width)
395     resize(w, _rect.height());
396   else if (! _fixed_height)
397     resize(_rect.width(), h);
398 }
399
400 void OtkWidget::addChild(OtkWidget *child, bool front)
401 {
402   assert(child);
403   if (front)
404     _children.push_front(child);
405   else
406     _children.push_back(child);
407 }
408
409 void OtkWidget::removeChild(OtkWidget *child)
410 {
411   assert(child);
412   OtkWidgetList::iterator it, end = _children.end();
413   for (it = _children.begin(); it != end; ++it) {
414     if ((*it) == child)
415       break;
416   }
417
418   if (it != _children.end())
419     _children.erase(it);
420 }
421
422 void OtkWidget::setEventDispatcher(OtkEventDispatcher *disp)
423 {
424   if (_event_dispatcher)
425     _event_dispatcher->clearHandler(_window);
426   _event_dispatcher = disp;
427   _event_dispatcher->registerHandler(_window, this);
428 }
429
430 int OtkWidget::exposeHandler(const XExposeEvent &e)
431 {
432   OtkEventHandler::exposeHandler(e);
433   _dirty = true;
434   update();
435   return true;
436 }
437
438 int 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   return true;
452 }
453
454 }