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