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