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