]> git.tdb.fi Git - libs/gltk.git/blob - source/list.cpp
Store the Resources reference only in Root widget
[libs/gltk.git] / source / list.cpp
1 /* $Id$
2
3 This file is part of libmspgltk
4 Copyright © 2007-2009  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <msp/gl/immediate.h>
9 #include <msp/gl/matrix.h>
10 #include <msp/gl/transform.h>
11 #include "graphic.h"
12 #include "list.h"
13 #include "part.h"
14 #include "style.h"
15 #include "text.h"
16 #include "vslider.h"
17
18 using namespace std;
19
20 namespace Msp {
21 namespace GLtk {
22
23 List::List():
24         sel_index(-1),
25         first(0),
26         n_visible(1),
27         row_height(1),
28         items_part(0)
29 {
30         add(slider);
31         slider.set_step(1);
32         slider.signal_value_changed.connect(sigc::mem_fun(this, &List::slider_value_changed));
33 }
34
35 void List::autosize()
36 {
37         if(!style)
38                 return;
39
40         float font_size = style->get_font()->get_default_size();
41
42         geom.w = 0;
43         for(vector<string>::iterator i=items.begin(); i!=items.end(); ++i)
44         {
45                 unsigned w = static_cast<unsigned>(style->get_font()->get_string_width(*i)*font_size);
46                 geom.w = max(geom.w, w);
47         }
48
49         geom.h = items.size()*row_height;
50
51         if(items_part)
52         {
53                 const Sides &margin = items_part->get_margin();
54                 geom.w += margin.left+margin.right;
55                 geom.h += margin.top+margin.bottom;
56         }
57 }
58
59 void List::append(const string &v)
60 {
61         items.push_back(v);
62         check_view_range();
63 }
64
65 void List::insert(unsigned i, const string &v)
66 {
67         if(i>items.size())
68                 throw InvalidParameterValue("Index out of range");
69
70         items.insert(items.begin()+i, v);
71         check_view_range();
72 }
73
74 void List::remove(unsigned i)
75 {
76         if(i>items.size())
77                 throw InvalidParameterValue("Index out of range");
78
79         items.erase(items.begin()+i);
80         if(sel_index>static_cast<int>(i))
81                 --sel_index;
82         else if(sel_index==static_cast<int>(i))
83                 sel_index = -1;
84
85         check_view_range();
86 }
87
88 void List::clear()
89 {
90         items.clear();
91         sel_index = -1;
92
93         check_view_range();
94 }
95
96 void List::set_selected_index(int i)
97 {
98         if(i<0)
99                 sel_index = -1;
100         else if(i<static_cast<int>(items.size()))
101         {
102                 sel_index = i;
103                 signal_item_selected.emit(sel_index, items[sel_index]);
104         }
105         else
106                 throw InvalidParameterValue("Index out of range");
107 }
108
109 const string &List::get_selected() const
110 {
111         if(sel_index<0)
112                 throw InvalidState("No selection");
113
114         return items[sel_index];
115 }
116
117 void List::button_press(int x, int y, unsigned btn)
118 {
119         Container::button_press(x, y, btn);
120         if(!click_focus && btn==1)
121         {
122                 if(items_part)
123                         y += items_part->get_margin().top;
124
125                 unsigned i = (geom.h-1-y)/row_height;
126                 if(i<n_visible && first+i<items.size())
127                 {
128                         sel_index = first+i;
129
130                         signal_item_selected.emit(sel_index, items[sel_index]);
131                 }
132         }
133 }
134
135 void List::render_special(const Part &part) const
136 {
137         if(part.get_name()=="items")
138         {
139                 const Sides &margin = part.get_margin();
140                 Geometry pgeom = geom;
141                 pgeom.h = row_height+margin.top+margin.bottom;
142
143                 GL::PushMatrix push_mtx;
144                 GL::translate(0, geom.h-pgeom.h, 0);
145
146                 for(unsigned i=0; (i<n_visible && first+i<items.size()); ++i)
147                 {
148                         if(i!=0)
149                                 GL::translate(0, -static_cast<int>(row_height), 0);
150                         Text(*style, items[first+i]).render(part, pgeom);
151                 }
152         }
153         else if(part.get_name()=="selection")
154         {
155                 if(sel_index>=static_cast<int>(first) && sel_index<static_cast<int>(first+n_visible))
156                 {
157                         const Sides &margin = part.get_margin();
158
159                         Geometry pgeom = geom;
160                         pgeom.h = row_height;
161                         pgeom.w -= margin.left+margin.right;
162
163                         Geometry rgeom = part.get_geometry();
164                         rgeom.y += geom.h-margin.top-row_height*(sel_index-first+1);
165                         rgeom.x += margin.left;
166                         part.get_alignment().apply(rgeom, pgeom);
167
168                         GL::push_matrix();
169                         GL::translate(rgeom.x, rgeom.y, 0);
170                         part.get_graphic(state)->render(rgeom.w, rgeom.h);
171                         GL::pop_matrix();
172                 }
173         }
174         else if(part.get_name()=="slider")
175                 slider.render();
176 }
177
178 void List::on_geometry_change()
179 {
180         reposition_slider();
181
182         check_view_range();
183 }
184
185 void List::on_style_change()
186 {
187         if(!style)
188         {
189                 items_part = 0;
190                 return;
191         }
192
193         reposition_slider();
194
195         items_part = style->get_part("items");
196
197         const GL::Font &font = *style->get_font();
198         row_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font.get_default_size());
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::check_view_range()
217 {
218         unsigned h = geom.h;
219         if(items_part)
220         {
221                 const Sides &margin = items_part->get_margin();
222                 h -= margin.top+margin.bottom;
223         }
224
225         n_visible = h/row_height;
226
227         if(first+n_visible>items.size())
228         {
229                 if(items.size()>n_visible)
230                         first = items.size()-n_visible;
231                 else
232                         first = 0;
233         }
234
235         if(items.size()>n_visible)
236         {
237                 slider.set_range(0, items.size()-n_visible);
238                 slider.set_value(items.size()-n_visible-first);
239         }
240         else
241         {
242                 slider.set_range(0, 0);
243                 slider.set_value(0);
244         }
245 }
246
247 void List::slider_value_changed(double value)
248 {
249         if(items.size()>n_visible)
250                 first = items.size()-n_visible-static_cast<unsigned>(value);
251 }
252
253
254 List::Loader::Loader(List &l):
255         Widget::Loader(l)
256 {
257         add("item", &Loader::item);
258 }
259
260 void List::Loader::item(const string &v)
261 {
262         dynamic_cast<List &>(wdg).append(v);
263 }
264
265 } // namespace GLtk
266 } // namespace Msp