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