add needed functions
[vuplus_dvbapp] / lib / gui / elistbox.cpp
1 #include <lib/gui/elistbox.h>
2 #include <lib/gui/elistboxcontent.h>
3 #include <lib/gui/eslider.h>
4 #include <lib/actions/action.h>
5
6 eListbox::eListbox(eWidget *parent)
7         :eWidget(parent), m_prev_scrollbar_page(-1), m_content_changed(false)
8         , m_scrollbar_mode(showNever), m_scrollbar(NULL)
9 {
10         setContent(new eListboxStringContent());
11
12         ePtr<eActionMap> ptr;
13         eActionMap::getInstance(ptr);
14         
15         m_itemheight = 25;
16         m_selection_enabled = 1;
17         
18         ptr->bindAction("ListboxActions", 0, 0, this);
19 }
20
21 eListbox::~eListbox()
22 {
23         if (m_scrollbar)
24                 delete m_scrollbar;
25         
26         ePtr<eActionMap> ptr;
27         eActionMap::getInstance(ptr);
28         ptr->unbindAction(this, 0);
29 }
30
31 void eListbox::setScrollbarMode(int mode)
32 {
33         m_scrollbar_mode = mode;
34         if ( m_scrollbar )
35         {
36                 if ( m_scrollbar_mode == showNever )
37                 {
38                         delete m_scrollbar;
39                         m_scrollbar=0;
40                 }
41         }
42         else
43         {
44                 m_scrollbar = new eSlider(this);
45                 m_scrollbar->hide();
46                 m_scrollbar->setBorderWidth(1);
47                 m_scrollbar->setOrientation(eSlider::orVertical);
48                 m_scrollbar->setRange(0,100);
49         }
50 }
51
52 void eListbox::setContent(iListboxContent *content)
53 {
54         m_content = content;
55         if (content)
56                 m_content->setListbox(this);
57         entryReset();
58 }
59
60 void eListbox::moveSelection(int dir)
61 {
62                 /* refuse to do anything without a valid list. */
63         if (!m_content)
64                 return;
65         
66                 /* if our list does not have one entry, don't do anything. */
67         if (!m_items_per_page)
68                 return;
69                 
70                 /* we need the old top/sel to see what we have to redraw */
71         int oldtop = m_top;
72         int oldsel = m_selected;
73         
74                 /* first, move cursor */
75         switch (dir)
76         {
77         case moveUp:
78                 m_content->cursorMove(-1);
79                 break;
80         case moveDown:
81                 m_content->cursorMove(1);
82                         /* ok - we could have reached the end. we just go one back then. */
83                 if (!m_content->cursorValid())
84                         m_content->cursorMove(-1);
85                 break;
86         case pageUp:
87                 if (m_content->cursorGet() >= m_items_per_page)
88                 {
89                         m_content->cursorMove(-m_items_per_page);
90                         m_top -= m_items_per_page;
91                         if (m_top < 0)
92                                 m_top = 0;
93                 } else
94                 {
95                         m_top = 0;
96                         m_content->cursorHome();
97                 }
98                 break;
99         case moveTop:
100                 m_content->cursorHome();
101                 m_top = 0; /* align with top, speeds up process */
102                 break;
103
104         case pageDown:
105                 m_content->cursorMove(m_items_per_page);
106                 if (m_content->cursorValid())
107                         break;
108                                 /* fall through */
109         case moveEnd:
110                         /* move to last existing one ("end" is already invalid) */
111                 m_content->cursorEnd(); m_content->cursorMove(-1); 
112                         /* current selection invisible? */
113                 if (m_top + m_items_per_page <= m_content->cursorGet())
114                 {
115                         int rest = m_content->size() % m_items_per_page;
116                         if ( rest )
117                                 m_top = m_content->cursorGet() - rest + 1;
118                         else
119                                 m_top = m_content->cursorGet() - m_items_per_page + 1;
120                         if (m_top < 0)
121                                 m_top = 0;
122                 }
123                 break;
124         case justCheck:
125                 break;
126         }
127         
128                 /* note that we could be on an invalid cursor position, but we don't
129                    care. this only happens on empty lists, and should have almost no
130                    side effects. */
131         
132                 /* now, look wether the current selection is out of screen */
133         m_selected = m_content->cursorGet();
134
135         while (m_selected < m_top)
136         {
137                 m_top -= m_items_per_page;
138                 if (m_top < 0)
139                         m_top = 0;
140         }
141         while (m_selected >= m_top + m_items_per_page)
142                 /* m_top should be always valid here as it's selected */
143                 m_top += m_items_per_page;
144
145         updateScrollBar();
146
147         if (m_top != oldtop)
148                 invalidate();
149         else if (m_selected != oldsel)
150         {
151                 
152                         /* redraw the old and newly selected */
153                 gRegion inv = eRect(0, m_itemheight * (m_selected-m_top), size().width(), m_itemheight);
154                 inv |= eRect(0, m_itemheight * (oldsel-m_top), size().width(), m_itemheight);
155                 
156                 invalidate(inv);
157         }
158 }
159
160 void eListbox::moveSelectionTo(int index)
161 {
162         if ( m_content )
163         {
164                 m_content->cursorHome();
165                 m_content->cursorMove(index);
166                 moveSelection(justCheck);
167         }
168 }
169
170 int eListbox::getCurrentIndex()
171 {
172         if ( m_content && m_content->cursorValid() )
173                 return m_content->cursorGet();
174         return 0;
175 }
176
177 void eListbox::updateScrollBar()
178 {
179         if (!m_content || m_scrollbar_mode == showNever )
180                 return;
181         int entries = m_content->size();
182         if ( m_content_changed )
183         {
184                 int width = size().width();
185                 int height = size().height();
186                 m_content_changed = false;
187                 if ( entries > m_items_per_page || m_scrollbar_mode == showAlways )
188                 {
189                         int sbarwidth=width/16;
190                         if ( sbarwidth < 18 )
191                                 sbarwidth=18;
192                         if ( sbarwidth > 22 )
193                                 sbarwidth=22;
194                         m_scrollbar->move(ePoint(width-sbarwidth, 0));
195                         m_scrollbar->resize(eSize(sbarwidth, height));
196                         m_content->setSize(eSize(width-sbarwidth-5, m_itemheight));
197                         m_scrollbar->show();
198                 }
199                 else
200                 {
201                         m_content->setSize(eSize(width, m_itemheight));
202                         m_scrollbar->hide();
203                 }
204         }
205         if ( m_items_per_page && entries )
206         {
207                 int curVisiblePage = m_top / m_items_per_page;
208                 if (m_prev_scrollbar_page != curVisiblePage)
209                 {
210                         m_prev_scrollbar_page = curVisiblePage;
211                         int pages = entries / m_items_per_page;
212                         if ( (pages*m_items_per_page) < entries )
213                                 ++pages;
214                         int start=(m_top*100)/(pages*m_items_per_page);
215                         int vis=(m_items_per_page*100)/(pages*m_items_per_page);
216                         if (vis < 3)
217                                 vis=3;
218                         m_scrollbar->setStartEnd(start,start+vis);
219                 }
220         }
221 }
222
223 int eListbox::event(int event, void *data, void *data2)
224 {
225         switch (event)
226         {
227         case evtPaint:
228         {
229                 ePtr<eWindowStyle> style;
230                 
231                 if (!m_content)
232                         return eWidget::event(event, data, data2);
233                 assert(m_content);
234                 
235                 getStyle(style);
236                 
237                 if (!m_content)
238                         return 0;
239                 
240                 gPainter &painter = *(gPainter*)data2;
241                 
242                 m_content->cursorSave();
243                 m_content->cursorMove(m_top - m_selected);
244                 
245                 for (int y = 0, i = 0; i <= m_items_per_page; y += m_itemheight, ++i)
246                 {
247                         m_content->paint(painter, *style, ePoint(0, y), m_selected == m_content->cursorGet() && m_content->size() && m_selection_enabled);
248                         m_content->cursorMove(+1);
249                 }
250
251                 if ( m_scrollbar && m_scrollbar->isVisible() )
252                 {
253                         painter.clip(eRect(m_scrollbar->position() - ePoint(5,0), eSize(5,m_scrollbar->size().height())));
254                         painter.clear();
255                         painter.clippop();
256                 }
257
258                 m_content->cursorRestore();
259
260                 return 0;
261         }
262         case evtChangedSize:
263                 recalcSize();
264                 return eWidget::event(event, data, data2);
265                 
266         case evtAction:
267                 if (isVisible())
268                 {
269                         moveSelection((int)data2);
270                         return 1;
271                 }
272                 return 0;
273         default:
274                 return eWidget::event(event, data, data2);
275         }
276 }
277
278 void eListbox::recalcSize()
279 {
280         m_content_changed=true;
281         m_prev_scrollbar_page=-1;
282         m_content->setSize(eSize(size().width(), m_itemheight));
283         m_items_per_page = size().height() / m_itemheight;
284         moveSelection(justCheck);
285 }
286
287 void eListbox::setItemHeight(int h)
288 {
289         if (h)
290                 m_itemheight = h;
291         else
292                 m_itemheight = 20;
293         recalcSize();
294 }
295
296 void eListbox::setSelectionEnable(int en)
297 {
298         if (m_selection_enabled == en)
299                 return;
300         m_selection_enabled = en;
301         entryChanged(m_selected); /* redraw current entry */
302 }
303
304 void eListbox::entryAdded(int index)
305 {
306                 /* manage our local pointers. when the entry was added before the current position, we have to advance. */
307                 
308                 /* we need to check <= - when the new entry has the (old) index of the cursor, the cursor was just moved down. */
309         if (index <= m_selected)
310                 ++m_selected;
311         if (index <= m_top)
312                 ++m_top;
313                 
314                 /* we have to check wether our current cursor is gone out of the screen. */
315                 /* moveSelection will check for this case */
316         moveSelection(justCheck);
317         
318                 /* now, check if the new index is visible. */
319         if ((m_top <= index) && (index < (m_top + m_items_per_page)))
320         {
321                         /* todo, calc exact invalidation... */
322                 invalidate();
323         }
324 }
325
326 void eListbox::entryRemoved(int index)
327 {
328         if (index == m_selected)
329                 m_selected = m_content->cursorGet();
330
331         moveSelection(justCheck);
332
333         if ((m_top <= index) && (index < (m_top + m_items_per_page)))
334         {
335                         /* todo, calc exact invalidation... */
336                 invalidate();
337         }
338 }
339
340 void eListbox::entryChanged(int index)
341 {
342         if ((m_top <= index) && (index < (m_top + m_items_per_page)))
343         {
344                 gRegion inv = eRect(0, m_itemheight * (index-m_top), size().width(), m_itemheight);
345                 invalidate(inv);
346         }
347 }
348
349 void eListbox::entryReset(bool cursorHome)
350 {
351         m_content_changed=true;
352         m_prev_scrollbar_page=-1;
353         if ( cursorHome )
354         {
355                 if (m_content)
356                         m_content->cursorHome();
357                 m_top = 0;
358                 m_selected = 0;
359         }
360         moveSelection(justCheck);
361         invalidate();
362 }