]> git.tdb.fi Git - libs/gltk.git/blob - source/container.cpp
Move all child widget handling into Container
[libs/gltk.git] / source / container.cpp
1 #include "container.h"
2
3 using namespace std;
4
5 namespace Msp {
6 namespace GLtk {
7
8 hierarchy_error::hierarchy_error(const string &w):
9         logic_error(w)
10 { }
11
12
13 Container::Container():
14         click_focus(0),
15         click_button(0),
16         pointer_focus(0),
17         pointer_grabbed(false),
18         input_focus(0)
19 { }
20
21 Container::~Container()
22 {
23         while(!children.empty())
24                 delete children.front()->widget;
25 }
26
27 void Container::add(Widget &wdg)
28 {
29         wdg.set_parent(this);
30         children.push_back(create_child(&wdg));
31         on_child_added(wdg);
32 }
33
34 void Container::remove(Widget &wdg)
35 {
36         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
37                 if((*i)->widget==&wdg)
38                 {
39                         wdg.set_parent(0);
40                         delete *i;
41                         children.erase(i);
42                         on_child_removed(wdg);
43                         return;
44                 }
45
46         throw hierarchy_error("widget not in container");
47 }
48
49 Container::Child *Container::create_child(Widget *wdg)
50 {
51         return new Child(*this, wdg);
52 }
53
54 list<Widget *> Container::get_children() const
55 {
56         list<Widget *> result;
57         for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
58                 result.push_back((*i)->widget);
59         return result;
60 }
61
62 Widget *Container::get_child_at(int x, int y)
63 {
64         for(list<Child *>::iterator i=children.end(); i!=children.begin();)
65                 if((*--i)->widget->is_visible() && (*i)->widget->get_geometry().is_inside(x, y))
66                         return (*i)->widget;
67
68         return 0;
69 }
70
71 Widget *Container::get_descendant_at(int x, int y)
72 {
73         Widget *wdg = get_child_at(x, y);
74         if(Container *cont = dynamic_cast<Container *>(wdg))
75         {
76                 const Geometry &cgeom = wdg->get_geometry();
77                 Widget *wdg2 = cont->get_descendant_at(x-cgeom.x, y-cgeom.y);
78                 if(wdg2)
79                         return wdg2;
80         }
81         return wdg;
82 }
83
84 void Container::raise(Widget &wdg)
85 {
86         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
87                 if((*i)->widget==&wdg)
88                 {
89                         children.splice(children.end(), children, i);
90                         return;
91                 }
92
93         throw hierarchy_error("widget not in container");
94 }
95
96 void Container::set_pointer_focus(Widget *wdg)
97 {
98         if(wdg!=pointer_focus)
99         {
100                 if(pointer_focus)
101                         pointer_focus->pointer_leave();
102
103                 pointer_focus = wdg;
104
105                 if(pointer_focus)
106                         pointer_focus->pointer_enter();
107         }
108 }
109
110 void Container::set_input_focus(Widget *wdg)
111 {
112         if(wdg!=input_focus)
113         {
114                 if(input_focus)
115                         input_focus->focus_out();
116
117                 input_focus = wdg;
118
119                 if(input_focus)
120                 {
121                         raise(*wdg);
122                         input_focus->focus_in();
123                 }
124         }
125 }
126
127 Widget *Container::get_final_input_focus() const
128 {
129         if(Container *container = dynamic_cast<Container *>(input_focus))
130                 if(Widget *focus = container->get_final_input_focus())
131                         return focus;
132
133         return input_focus;
134 }
135
136 void Container::button_press(int x, int y, unsigned btn)
137 {
138         if(pointer_grabbed)
139         {
140                 const Geometry &cgeom = pointer_focus->get_geometry();
141                 pointer_focus->button_press(x-cgeom.x, y-cgeom.y, btn);
142         }
143         else
144         {
145                 if(Widget *wdg = get_child_at(x, y))
146                 {
147                         set_pointer_focus(wdg);
148                         if(wdg->is_focusable())
149                                 set_input_focus(wdg);
150                 }
151                 if(click_focus)
152                 {
153                         const Geometry &cgeom = click_focus->get_geometry();
154                         click_focus->button_press(x-cgeom.x, y-cgeom.y, btn);
155                 }
156                 else
157                 {
158                         if(Widget *wdg = get_child_at(x, y))
159                         {
160                                 click_focus = wdg;
161                                 click_button = btn;
162
163                                 const Geometry &cgeom = wdg->get_geometry();
164                                 wdg->button_press(x-cgeom.x, y-cgeom.y, btn);
165                         }
166                 }
167         }
168 }
169
170 void Container::button_release(int x, int y, unsigned btn)
171 {
172         if(pointer_grabbed)
173         {
174                 const Geometry &cgeom = pointer_focus->get_geometry();
175                 pointer_focus->button_release(x-cgeom.x, y-cgeom.y, btn);
176         }
177         else if(click_focus)
178         {
179                 Widget *wdg = click_focus;
180
181                 if(btn==click_button)
182                         click_focus = 0;
183
184                 const Geometry &cgeom = wdg->get_geometry();
185                 wdg->button_release(x-cgeom.x, y-cgeom.y, btn);
186         }
187         else
188         {
189                 if(Widget *wdg = get_child_at(x, y))
190                 {
191                         const Geometry &cgeom = wdg->get_geometry();
192                         wdg->button_release(x-cgeom.x, y-cgeom.y, btn);
193                 }
194         }
195 }
196
197 void Container::pointer_motion(int x, int y)
198 {
199         if(pointer_grabbed)
200         {
201                 const Geometry &cgeom = pointer_focus->get_geometry();
202                 pointer_focus->pointer_motion(x-cgeom.x, y-cgeom.y);
203         }
204         else
205         {
206                 set_pointer_focus(get_child_at(x, y));
207                 if(click_focus)
208                 {
209                         const Geometry &cgeom = click_focus->get_geometry();
210                         click_focus->pointer_motion(x-cgeom.x, y-cgeom.y);
211                 }
212                 else
213                 {
214                         Widget *wdg = get_child_at(x, y);
215                         if(wdg)
216                         {
217                                 const Geometry &cgeom = wdg->get_geometry();
218                                 wdg->pointer_motion(x-cgeom.x, y-cgeom.y);
219                         }
220                 }
221         }
222 }
223
224 void Container::pointer_leave()
225 {
226         Widget::pointer_leave();
227         click_focus = 0;
228         set_pointer_focus(0);
229 }
230
231 void Container::key_press(unsigned key, unsigned mod)
232 {
233         if(input_focus)
234                 input_focus->key_press(key, mod);
235 }
236
237 void Container::key_release(unsigned key, unsigned mod)
238 {
239         if(input_focus)
240                 input_focus->key_release(key, mod);
241 }
242
243 void Container::character(wchar_t ch)
244 {
245         if(input_focus)
246                 input_focus->character(ch);
247 }
248
249 void Container::focus_out()
250 {
251         set_input_focus(0);
252         Widget::focus_out();
253 }
254
255 void Container::on_reparent()
256 {
257         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
258         {
259                 if(Container *c = dynamic_cast<Container *>((*i)->widget))
260                         c->on_reparent();
261                 (*i)->widget->update_style();
262         }
263 }
264
265
266 Container::Child::Child(Container &c, Widget *w):
267         container(c),
268         widget(w)
269 {
270         widget->signal_visibility_changed.connect(sigc::mem_fun(this, &Child::visibility_changed));
271         widget->signal_request_focus.connect(sigc::mem_fun(this, &Child::request_focus));
272         widget->signal_grab_pointer.connect(sigc::mem_fun(this, &Child::grab_pointer));
273         widget->signal_ungrab_pointer.connect(sigc::mem_fun(this, &Child::ungrab_pointer));
274 }
275
276 Container::Child::~Child()
277 {
278         visibility_changed(false);
279 }
280
281 void Container::Child::visibility_changed(bool v)
282 {
283         if(!v)
284         {
285                 if(widget==container.click_focus)
286                         container.click_focus = 0;
287                 if(widget==container.pointer_focus)
288                         container.set_pointer_focus(0);
289                 if(widget==container.input_focus)
290                         container.set_input_focus(0);
291         }
292 }
293
294 void Container::Child::request_focus()
295 {
296         container.set_input_focus(widget);
297         if(container.parent && container.visible)
298                 container.set_focus();
299 }
300
301 void Container::Child::grab_pointer()
302 {
303         if(!container.pointer_grabbed)
304         {
305                 container.set_pointer_focus(widget);
306                 container.pointer_grabbed = true;
307                 container.signal_grab_pointer.emit();
308         }
309 }
310
311 void Container::Child::ungrab_pointer()
312 {
313         if(container.pointer_grabbed && container.pointer_focus==widget)
314         {
315                 // XXX Should set to the widget under pointer
316                 container.set_pointer_focus(0);
317                 container.pointer_grabbed = false;
318                 container.signal_ungrab_pointer.emit();
319         }
320 }
321
322 } // namespace GLtk
323 } // namespace Msp