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