]> git.tdb.fi Git - libs/gltk.git/blob - source/list.cpp
Some new datafile keywords
[libs/gltk.git] / source / list.cpp
1 #include <msp/gl/matrix.h>
2 #include <msp/gl/meshbuilder.h>
3 #include "graphic.h"
4 #include "list.h"
5 #include "part.h"
6 #include "style.h"
7 #include "text.h"
8 #include "vslider.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GLtk {
14
15 List::List():
16         data(new BasicListData<string>),
17         own_data(true)
18 {
19         init();
20 }
21
22 List::List(ListData &d):
23         data(&d),
24         own_data(false)
25 {
26         init();
27 }
28
29 void List::init()
30 {
31         sel_index = -1;
32         first = 0;
33         max_scroll = 0;
34         view_size = 5;
35
36         observer = new DataObserver(*this);
37
38         add(slider);
39         slider.set_step(1);
40         slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
41 }
42
43 List::~List()
44 {
45         delete observer;
46         if(own_data)
47                 delete data;
48 }
49
50 void List::autosize_special(const Part &part, Geometry &ageom)
51 {
52         if(part.get_name()=="items")
53         {
54                 const Sides &margin = part.get_margin();
55
56                 unsigned max_w = 0;
57                 unsigned total_h = 0;
58                 for(unsigned i=0; i<items.size(); ++i)
59                 {
60                         items[i]->autosize();
61                         const Geometry &igeom = items[i]->get_geometry();
62                         max_w = max(max_w, igeom.w);
63                         if(view_size==0 || i<view_size)
64                                 total_h += igeom.h;
65                 }
66
67                 if(!items.empty() && items.size()<view_size)
68                         total_h = total_h*view_size/items.size();
69
70                 ageom.w = max(ageom.w, max_w+margin.left+margin.right);
71                 ageom.h = max(ageom.h, total_h+margin.top+margin.bottom);
72         }
73 }
74
75 void List::set_data(ListData &d)
76 {
77         delete observer;
78         if(own_data)
79                 delete data;
80
81         data = &d;
82         own_data = false;
83         observer = new DataObserver(*this);
84
85         items_changed();
86 }
87
88 void List::items_changed()
89 {
90         check_view_range();
91         signal_autosize_changed.emit();
92         reposition_items();
93 }
94
95 List::Item *List::create_item(unsigned index)
96 {
97         return new BasicItem(data->get_string(index));
98 }
99
100 void List::set_view_size(unsigned s)
101 {
102         view_size = s;
103         signal_autosize_changed.emit();
104 }
105
106 void List::set_view_all()
107 {
108         set_view_size(0);
109 }
110
111 void List::set_selected_index(int i)
112 {
113         if(i>static_cast<int>(data->size()))
114                 throw out_of_range("List::set_selected_index");
115
116         if(sel_index>=0)
117                 items[sel_index]->set_active(false);
118         if(i<0)
119                 sel_index = -1;
120         else if(i!=sel_index)
121         {
122                 sel_index = i;
123                 items[sel_index]->set_active(true);
124                 signal_item_selected.emit(sel_index);
125         }
126 }
127
128 void List::render_special(const Part &part, GL::Renderer &renderer) const
129 {
130         if(part.get_name()=="items")
131         {
132                 for(unsigned i=first; (i<items.size() && items[i]->is_visible()); ++i)
133                         items[i]->render(renderer);
134         }
135         else if(part.get_name()=="slider")
136                 slider.render(renderer);
137 }
138
139 void List::button_press(int x, int y, unsigned btn)
140 {
141         Container::button_press(x, y, btn);
142         if(click_focus && btn==1)
143         {
144                 for(unsigned i=first; (i<items.size() && items[i]->is_visible()); ++i)
145                         if(click_focus==items[i])
146                         {
147                                 set_selected_index(i);
148                                 break;
149                         }
150         }
151 }
152
153 void List::on_geometry_change()
154 {
155         reposition_slider();
156         reposition_items();
157
158         check_view_range();
159 }
160
161 void List::on_style_change()
162 {
163         if(!style)
164                 return;
165
166         reposition_slider();
167         reposition_items();
168
169         check_view_range();
170 }
171
172 void List::reposition_slider()
173 {
174         if(!style)
175                 return;
176
177         if(const Part *slider_part = style->get_part("slider"))
178         {
179                 Geometry sgeom = slider_part->get_geometry();
180                 slider_part->get_alignment().apply(sgeom, geom, slider_part->get_margin());
181                 slider.set_geometry(sgeom);
182         }
183 }
184
185 void List::reposition_items()
186 {
187         if(!style)
188                 return;
189
190         if(const Part *items_part = style->get_part("items"))
191         {
192                 const Sides &margin = items_part->get_margin();
193                 unsigned w = geom.w-margin.left-margin.right;
194                 unsigned y = geom.h-margin.top;
195                 for(unsigned i=0; i<items.size(); ++i)
196                 {
197                         if(i<first || !y)
198                                 items[i]->set_visible(false);
199                         else
200                         {
201                                 Geometry igeom = items[i]->get_geometry();
202                                 if(igeom.h+margin.bottom<=y)
203                                 {
204                                         items[i]->set_visible(true);
205                                         y -= igeom.h;
206                                         igeom.x = margin.left;
207                                         igeom.y = y;
208                                         igeom.w = w;
209                                         items[i]->set_geometry(igeom);
210                                 }
211                                 else
212                                 {
213                                         items[i]->set_visible(false);
214                                         y = 0;
215                                 }
216                         }
217                 }
218         }
219 }
220
221 void List::check_view_range()
222 {
223         if(!style)
224                 return;
225
226         unsigned h = geom.h;
227         if(const Part *items_part = style->get_part("items"))
228         {
229                 const Sides &margin = items_part->get_margin();
230                 h -= margin.top+margin.bottom;
231         }
232
233         max_scroll = items.size();
234         for(unsigned i=items.size(); i-->0; )
235         {
236                 unsigned ih = items[i]->get_geometry().h;
237                 if(ih<=h)
238                 {
239                         h -= ih;
240                         --max_scroll;
241                 }
242         }
243
244         if(first>max_scroll)
245                 first = max_scroll;
246
247         slider.set_range(0, max_scroll);
248         slider.set_value(max_scroll-first);
249 }
250
251 void List::slider_value_changed(double value)
252 {
253         if(max_scroll>0)
254         {
255                 first = max_scroll-static_cast<unsigned>(value);
256                 reposition_items();
257         }
258 }
259
260
261 List::DataObserver::DataObserver(List &l):
262         list(l)
263 {
264         list.data->signal_item_added.connect(sigc::mem_fun(this, &DataObserver::item_added));
265         list.data->signal_item_removed.connect(sigc::mem_fun(this, &DataObserver::item_removed));
266         list.data->signal_cleared.connect(sigc::mem_fun(this, &DataObserver::cleared));
267         list.data->signal_refresh_strings.connect(sigc::mem_fun(this, &DataObserver::refresh_strings));
268 }
269
270 void List::DataObserver::item_added(unsigned i)
271 {
272         if(list.sel_index>=static_cast<int>(i))
273                 ++list.sel_index;
274
275         Item *item = list.create_item(i);
276         list.add(*item);
277         list.items.insert(list.items.begin()+i, item);
278         list.items_changed();
279 }
280
281 void List::DataObserver::item_removed(unsigned i)
282 {
283         if(list.sel_index>static_cast<int>(i))
284                 --list.sel_index;
285         else if(list.sel_index==static_cast<int>(i))
286                 list.sel_index = -1;
287
288         delete list.items[i];
289         list.items.erase(list.items.begin()+i);
290         list.items_changed();
291 }
292
293 void List::DataObserver::cleared()
294 {
295         list.sel_index = -1;
296         for(vector<Item *>::iterator i=list.items.begin(); i!=list.items.end(); ++i)
297                 delete *i;
298         list.items.clear();
299         list.items_changed();
300 }
301
302 void List::DataObserver::refresh_strings()
303 {
304         list.items_changed();
305 }
306
307
308 void List::Item::autosize_special(const Part &part, Geometry &ageom)
309 {
310         if(part.get_name()=="children")
311         {
312                 const Sides &margin = part.get_margin();
313                 for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
314                 {
315                         const Geometry &cgeom = (*i)->widget->get_geometry();
316                         ageom.w = max(ageom.w, cgeom.x+cgeom.w+margin.right);
317                         ageom.h = max(ageom.h, cgeom.y+cgeom.h+margin.top);
318                 }
319         }
320 }
321
322 void List::Item::set_active(bool a)
323 {
324         set_state(ACTIVE, (a ? ACTIVE : NORMAL));
325 }
326
327 void List::Item::render_special(const Part &part, GL::Renderer &renderer) const
328 {
329         if(part.get_name()=="children")
330         {
331                 for(list<Child *>::const_iterator i=children.begin(); i!=children.end(); ++i)
332                         (*i)->widget->render(renderer);
333         }
334 }
335
336
337 List::BasicItem::BasicItem(const string &text):
338         label(text)
339 {
340         add(label);
341 }
342
343 void List::BasicItem::on_style_change()
344 {
345         if(!style)
346                 return;
347
348         label.autosize();
349         if(const Part *part = style->get_part("children"))
350         {
351                 const Sides &margin = part->get_margin();
352                 label.set_position(margin.left, margin.bottom);
353         }
354 }
355
356
357 List::Loader::Loader(List &l):
358         DataFile::DerivedObjectLoader<List, Widget::Loader>(l)
359 {
360         add("item", &Loader::item);
361         add("view_size", &List::view_size);
362 }
363
364 void List::Loader::item(const string &v)
365 {
366         dynamic_cast<BasicListData<string> &>(*obj.data).append(v);
367 }
368
369 } // namespace GLtk
370 } // namespace Msp