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