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