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