]> git.tdb.fi Git - libs/gltk.git/blob - source/list.cpp
Strip copyright messages and id tags from individual files
[libs/gltk.git] / source / list.cpp
1 #include <msp/gl/immediate.h>
2 #include <msp/gl/matrix.h>
3 #include <msp/gl/transform.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 List::List():
17         sel_index(-1),
18         first(0),
19         n_visible(1),
20         row_height(1),
21         items_part(0)
22 {
23         add(slider);
24         slider.set_step(1);
25         slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
26 }
27
28 void List::autosize()
29 {
30         autosize_rows(5);
31 }
32
33 void List::autosize_rows(unsigned n)
34 {
35         if(!style)
36                 return;
37
38         Widget::autosize();
39
40         if(items_part)
41         {
42                 const Sides &margin = items_part->get_margin();
43                 float font_size = style->get_font()->get_default_size();
44
45                 unsigned max_w = 0;
46                 for(vector<string>::iterator i=items.begin(); i!=items.end(); ++i)
47                 {
48                         unsigned w = static_cast<unsigned>(style->get_font()->get_string_width(*i)*font_size);
49                         max_w = max(max_w, w);
50                 }
51
52                 geom.w = max(geom.w, max_w+margin.left+margin.right);
53                 geom.h = max(geom.h, n*row_height+margin.top+margin.bottom);
54         }
55
56         if(const Part *slider_part = style->get_part("slider"))
57         {
58                 Geometry sgeom = slider_part->get_geometry();
59                 if(!sgeom.w || !sgeom.h)
60                 {
61                         slider.autosize();
62                         if(!sgeom.w)
63                                 sgeom.w = slider.get_geometry().w;
64                         if(!sgeom.h)
65                                 sgeom.h = slider.get_geometry().h;
66                 }
67
68                 const Sides &margin = slider_part->get_margin();
69                 geom.w = max(geom.w, sgeom.w+margin.left+margin.right);
70                 geom.h = max(geom.h, sgeom.h+margin.top+margin.bottom);
71
72                 reposition_slider();
73         }
74
75         check_view_range();
76 }
77
78 void List::autosize_all()
79 {
80         autosize_rows(items.size());
81 }
82
83 void List::append(const string &v)
84 {
85         items.push_back(v);
86         check_view_range();
87         signal_autosize_changed.emit();
88 }
89
90 void List::insert(unsigned i, const string &v)
91 {
92         if(i>items.size())
93                 throw InvalidParameterValue("Index out of range");
94
95         items.insert(items.begin()+i, v);
96         check_view_range();
97         signal_autosize_changed.emit();
98 }
99
100 void List::remove(unsigned i)
101 {
102         if(i>items.size())
103                 throw InvalidParameterValue("Index out of range");
104
105         items.erase(items.begin()+i);
106         if(sel_index>static_cast<int>(i))
107                 --sel_index;
108         else if(sel_index==static_cast<int>(i))
109                 sel_index = -1;
110
111         check_view_range();
112         signal_autosize_changed.emit();
113 }
114
115 void List::clear()
116 {
117         items.clear();
118         sel_index = -1;
119
120         check_view_range();
121         signal_autosize_changed.emit();
122 }
123
124 void List::set_selected_index(int i)
125 {
126         if(i<0)
127                 sel_index = -1;
128         else if(i<static_cast<int>(items.size()))
129         {
130                 sel_index = i;
131                 signal_item_selected.emit(sel_index, items[sel_index]);
132         }
133         else
134                 throw InvalidParameterValue("Index out of range");
135 }
136
137 const string &List::get_selected() const
138 {
139         if(sel_index<0)
140                 throw InvalidState("No selection");
141
142         return items[sel_index];
143 }
144
145 void List::render_special(const Part &part) const
146 {
147         if(part.get_name()=="items")
148         {
149                 const Sides &margin = part.get_margin();
150                 Geometry pgeom = geom;
151                 pgeom.h = row_height+margin.top+margin.bottom;
152
153                 GL::PushMatrix push_mtx;
154                 GL::translate(0, geom.h-pgeom.h, 0);
155
156                 for(unsigned i=0; (i<n_visible && first+i<items.size()); ++i)
157                 {
158                         if(i!=0)
159                                 GL::translate(0, -static_cast<int>(row_height), 0);
160                         Text(*style, items[first+i]).render(part, pgeom);
161                 }
162         }
163         else if(part.get_name()=="selection")
164         {
165                 if(sel_index>=static_cast<int>(first) && sel_index<static_cast<int>(first+n_visible))
166                 {
167                         const Sides &margin = part.get_margin();
168
169                         Geometry pgeom = geom;
170                         pgeom.h = row_height;
171                         pgeom.w -= margin.left+margin.right;
172
173                         Geometry rgeom = part.get_geometry();
174                         rgeom.y += geom.h-margin.top-row_height*(sel_index-first+1);
175                         rgeom.x += margin.left;
176                         part.get_alignment().apply(rgeom, pgeom);
177
178                         GL::push_matrix();
179                         GL::translate(rgeom.x, rgeom.y, 0);
180                         part.get_graphic(state)->render(rgeom.w, rgeom.h);
181                         GL::pop_matrix();
182                 }
183         }
184         else if(part.get_name()=="slider")
185                 slider.render();
186 }
187
188 void List::button_press(int x, int y, unsigned btn)
189 {
190         Container::button_press(x, y, btn);
191         if(!click_focus && btn==1)
192         {
193                 if(items_part)
194                         y += items_part->get_margin().top;
195
196                 unsigned i = (geom.h-1-y)/row_height;
197                 if(i<n_visible && first+i<items.size())
198                 {
199                         sel_index = first+i;
200
201                         signal_item_selected.emit(sel_index, items[sel_index]);
202                 }
203         }
204 }
205
206 void List::on_geometry_change()
207 {
208         reposition_slider();
209
210         check_view_range();
211 }
212
213 void List::on_style_change()
214 {
215         if(!style)
216         {
217                 items_part = 0;
218                 return;
219         }
220
221         reposition_slider();
222
223         items_part = style->get_part("items");
224
225         const GL::Font &font = *style->get_font();
226         row_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font.get_default_size());
227
228         check_view_range();
229 }
230
231 void List::reposition_slider()
232 {
233         if(!style)
234                 return;
235
236         if(const Part *slider_part = style->get_part("slider"))
237         {
238                 Geometry sgeom = slider_part->get_geometry();
239                 slider_part->get_alignment().apply(sgeom, geom, slider_part->get_margin());
240                 slider.set_geometry(sgeom);
241         }
242 }
243
244 void List::check_view_range()
245 {
246         unsigned h = geom.h;
247         if(items_part)
248         {
249                 const Sides &margin = items_part->get_margin();
250                 h -= margin.top+margin.bottom;
251         }
252
253         n_visible = h/row_height;
254
255         if(first+n_visible>items.size())
256         {
257                 if(items.size()>n_visible)
258                         first = items.size()-n_visible;
259                 else
260                         first = 0;
261         }
262
263         if(items.size()>n_visible)
264         {
265                 slider.set_range(0, items.size()-n_visible);
266                 slider.set_value(items.size()-n_visible-first);
267         }
268         else
269         {
270                 slider.set_range(0, 0);
271                 slider.set_value(0);
272         }
273 }
274
275 void List::slider_value_changed(double value)
276 {
277         if(items.size()>n_visible)
278                 first = items.size()-n_visible-static_cast<unsigned>(value);
279 }
280
281
282 List::Loader::Loader(List &l):
283         Widget::Loader(l)
284 {
285         add("item", &Loader::item);
286 }
287
288 void List::Loader::item(const string &v)
289 {
290         dynamic_cast<List &>(obj).append(v);
291 }
292
293 } // namespace GLtk
294 } // namespace Msp