]> git.tdb.fi Git - libs/gltk.git/blob - source/container.cpp
e8b666d67344e854cedf0d948fe6f38d2874eab8
[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(Widget *child = get_pointer_target(x, y))
139         {
140                 if(!click_focus)
141                 {
142                         set_pointer_focus(child);
143                         if(child->is_focusable())
144                                 set_input_focus(child);
145
146                         click_focus = child;
147                         click_button = btn;
148                 }
149
150                 const Geometry &cgeom = child->get_geometry();
151                 child->button_press(x-cgeom.x, y-cgeom.y, btn);
152         }
153 }
154
155 void Container::button_release(int x, int y, unsigned btn)
156 {
157         if(Widget *child = get_pointer_target(x, y))
158         {
159                 if(child==click_focus && btn==click_button)
160                 {
161                         click_focus = 0;
162                         if(!pointer_focus)
163                                 set_pointer_focus(get_child_at(x, y));
164                 }
165
166                 const Geometry &cgeom = child->get_geometry();
167                 child->button_release(x-cgeom.x, y-cgeom.y, btn);
168         }
169 }
170
171 void Container::pointer_motion(int x, int y)
172 {
173         Widget *child = get_pointer_target(x, y);
174         if(!pointer_grabbed)
175                 set_pointer_focus((!click_focus || child->get_geometry().is_inside(x, y)) ? child : 0);
176
177         if(child)
178         {
179                 const Geometry &cgeom = child->get_geometry();
180                 child->pointer_motion(x-cgeom.x, y-cgeom.y);
181         }
182 }
183
184 Widget *Container::get_pointer_target(int x, int y)
185 {
186         if(pointer_grabbed)
187                 return pointer_focus;
188         else if(click_focus)
189                 return click_focus;
190         else
191                 return get_child_at(x, y);
192 }
193
194 void Container::pointer_leave()
195 {
196         Widget::pointer_leave();
197         set_pointer_focus(0);
198 }
199
200 void Container::key_press(unsigned key, unsigned mod)
201 {
202         if(input_focus)
203                 input_focus->key_press(key, mod);
204 }
205
206 void Container::key_release(unsigned key, unsigned mod)
207 {
208         if(input_focus)
209                 input_focus->key_release(key, mod);
210 }
211
212 void Container::character(wchar_t ch)
213 {
214         if(input_focus)
215                 input_focus->character(ch);
216 }
217
218 void Container::focus_out()
219 {
220         set_input_focus(0);
221         Widget::focus_out();
222 }
223
224 void Container::on_reparent()
225 {
226         for(list<Child *>::iterator i=children.begin(); i!=children.end(); ++i)
227         {
228                 if(Container *c = dynamic_cast<Container *>((*i)->widget))
229                         c->on_reparent();
230                 (*i)->widget->update_style();
231         }
232 }
233
234
235 Container::Child::Child(Container &c, Widget *w):
236         container(c),
237         widget(w)
238 {
239         widget->signal_visibility_changed.connect(sigc::mem_fun(this, &Child::visibility_changed));
240         widget->signal_request_focus.connect(sigc::mem_fun(this, &Child::request_focus));
241         widget->signal_grab_pointer.connect(sigc::mem_fun(this, &Child::grab_pointer));
242         widget->signal_ungrab_pointer.connect(sigc::mem_fun(this, &Child::ungrab_pointer));
243 }
244
245 Container::Child::~Child()
246 {
247         visibility_changed(false);
248 }
249
250 void Container::Child::visibility_changed(bool v)
251 {
252         if(!v)
253         {
254                 if(widget==container.click_focus)
255                         container.click_focus = 0;
256                 if(widget==container.pointer_focus)
257                         container.set_pointer_focus(0);
258                 if(widget==container.input_focus)
259                         container.set_input_focus(0);
260         }
261 }
262
263 void Container::Child::request_focus()
264 {
265         container.set_input_focus(widget);
266         if(container.parent && container.visible)
267                 container.set_focus();
268 }
269
270 void Container::Child::grab_pointer()
271 {
272         if(!container.pointer_grabbed)
273         {
274                 container.set_pointer_focus(widget);
275                 container.pointer_grabbed = true;
276                 container.signal_grab_pointer.emit();
277         }
278 }
279
280 void Container::Child::ungrab_pointer()
281 {
282         if(container.pointer_grabbed && container.pointer_focus==widget)
283         {
284                 // XXX Should set to the widget under pointer
285                 container.set_pointer_focus(0);
286                 container.pointer_grabbed = false;
287                 container.signal_ungrab_pointer.emit();
288         }
289 }
290
291 } // namespace GLtk
292 } // namespace Msp