summaryrefslogtreecommitdiff
path: root/apps/gui/list.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/list.c')
-rw-r--r--apps/gui/list.c499
1 files changed, 499 insertions, 0 deletions
diff --git a/apps/gui/list.c b/apps/gui/list.c
new file mode 100644
index 0000000000..bb3eb7caaa
--- /dev/null
+++ b/apps/gui/list.c
@@ -0,0 +1,499 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Kévin FERRARE
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "config.h"
21#include "lcd.h"
22#include "font.h"
23#include "button.h"
24#include "sprintf.h"
25#include "settings.h"
26#include "kernel.h"
27
28#include "screen_access.h"
29#include "list.h"
30#include "scrollbar.h"
31#include "statusbar.h"
32
33#ifdef HAVE_LCD_CHARCELLS
34#define SCROLL_LIMIT 1
35#else
36#define SCROLL_LIMIT 2
37#endif
38
39
40
41void gui_list_init(struct gui_list * gui_list,
42 void (*callback_get_item_icon)(int selected_item, ICON * icon),
43 char * (*callback_get_item_name)(int selected_item, char *buffer))
44{
45 gui_list->callback_get_item_icon = callback_get_item_icon;
46 gui_list->callback_get_item_name = callback_get_item_name;
47 gui_list->display = NULL;
48 gui_list_set_nb_items(gui_list, 0);
49 gui_list->selected_item = 0;
50 gui_list->start_item = 0;
51}
52
53void gui_list_set_nb_items(struct gui_list * gui_list, int nb_items)
54{
55 gui_list->nb_items = nb_items;
56}
57
58void gui_list_set_display(struct gui_list * gui_list, struct screen * display)
59{
60 if(gui_list->display != 0) /* we switched from a previous display */
61 gui_list->display->stop_scroll();
62 gui_list->display = display;
63#ifdef HAVE_LCD_CHARCELLS
64 display->double_height(false);
65#endif
66 gui_list_put_selection_in_screen(gui_list, false);
67}
68
69void gui_list_put_selection_in_screen(struct gui_list * gui_list,
70 bool put_from_end)
71{
72 struct screen * display = gui_list->display;
73 if(put_from_end)
74 {
75 int list_end = gui_list->selected_item + SCROLL_LIMIT - 1;
76 if(list_end > gui_list->nb_items)
77 list_end = gui_list->nb_items;
78 gui_list->start_item = list_end - display->nb_lines;
79 }
80 else
81 {
82 int list_start = gui_list->selected_item - SCROLL_LIMIT + 1;
83 if(list_start + display->nb_lines > gui_list->nb_items)
84 list_start = gui_list->nb_items - display->nb_lines;
85 gui_list->start_item = list_start;
86 }
87 if(gui_list->start_item < 0)
88 gui_list->start_item = 0;
89}
90
91void gui_list_get_selected_item_name(struct gui_list * gui_list, char *buffer)
92{
93 gui_list->callback_get_item_name(gui_list->selected_item, buffer);
94}
95
96int gui_list_get_selected_item_position(struct gui_list * gui_list)
97{
98 return gui_list->selected_item;
99}
100
101void gui_list_draw(struct gui_list * gui_list)
102{
103 struct screen * display=gui_list->display;
104 int cursor_pos = 0;
105 int icon_pos = 1;
106 int text_pos;
107 bool draw_icons = (gui_list->callback_get_item_icon != NULL &&
108 global_settings.show_icons) ;
109 bool draw_cursor;
110 int i;
111
112 /* Adjust the position of icon, cursor, text */
113#ifdef HAVE_LCD_BITMAP
114 bool draw_scrollbar = (global_settings.scrollbar &&
115 display->nb_lines < gui_list->nb_items);
116
117 int list_y_start = screen_get_text_y_start(gui_list->display);
118 int list_y_end = screen_get_text_y_end(gui_list->display);
119
120 draw_cursor = !global_settings.invert_cursor;
121 text_pos = 0; /* here it's in pixels */
122 if(draw_scrollbar)
123 {
124 ++cursor_pos;
125 ++icon_pos;
126 text_pos += SCROLLBAR_WIDTH;
127 }
128 if(!draw_cursor)
129 {
130 --icon_pos;
131 }
132 else
133 text_pos += CURSOR_WIDTH;
134
135 if(draw_icons)
136 text_pos += 8;
137#else
138 draw_cursor = true;
139 if(draw_icons)
140 text_pos = 2; /* here it's in chars */
141 else
142 text_pos = 1;
143#endif
144 /* The drawing part */
145#ifdef HAVE_LCD_BITMAP
146 /* clear the drawing area */
147 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
148 display->fillrect(0, list_y_start,
149 display->width, list_y_end - list_y_start);
150 display->set_drawmode(DRMODE_SOLID);
151
152 /* FIXME: should not be handled here, but rather in the
153 * code that changes fonts */
154 screen_update_nblines(display);
155
156 display->stop_scroll();
157 display->setmargins(text_pos, list_y_start);
158#else
159 display->clear_display();
160#endif
161
162 for(i = 0;i < display->nb_lines;++i)
163 {
164 char entry_buffer[MAX_PATH];
165 char * entry_name;
166 int current_item = gui_list->start_item + i;
167
168 /* When there are less items to display than the
169 * current available space on the screen, we stop*/
170 if(current_item >= gui_list->nb_items)
171 break;
172 entry_name = gui_list->callback_get_item_name(current_item,
173 entry_buffer);
174 if(current_item == gui_list->selected_item)
175 {
176 /* The selected item must be displayed scrolling */
177#ifdef HAVE_LCD_BITMAP
178 if (global_settings.invert_cursor)/* Display inverted-line-style*/
179 display->puts_scroll_style(0, i, entry_name, STYLE_INVERT);
180 else
181 display->puts_scroll(0, i, entry_name);
182#else
183 display->puts_scroll(text_pos, i, entry_name);
184#endif
185
186 if(draw_cursor)
187 screen_put_cursorxy(display, cursor_pos, i);
188 }
189 else
190 {/* normal item */
191#ifdef HAVE_LCD_BITMAP
192 display->puts(0, i, entry_name);
193#else
194 display->puts(text_pos, i, entry_name);
195#endif
196 }
197 /* Icons display */
198 if(draw_icons)
199 {
200 ICON icon;
201 gui_list->callback_get_item_icon(current_item, &icon);
202 screen_put_iconxy(display, icon_pos, i, icon);
203 }
204 }
205#ifdef HAVE_LCD_BITMAP
206 /* Draw the scrollbar if needed*/
207 if(draw_scrollbar)
208 {
209 int scrollbar_y_end = display->char_height *
210 display->nb_lines + list_y_start;
211 gui_scrollbar_draw(display, 0, list_y_start, SCROLLBAR_WIDTH-1,
212 scrollbar_y_end - list_y_start, gui_list->nb_items,
213 gui_list->start_item,
214 gui_list->start_item + display->nb_lines, VERTICAL);
215 }
216 display->update_rect(0, list_y_start, display->width,
217 list_y_end - list_y_start);
218#else
219#ifdef SIMULATOR
220 display->update();
221#endif
222#endif
223}
224
225void gui_list_select_item(struct gui_list * gui_list, int item_number)
226{
227 if( item_number > gui_list->nb_items-1 || item_number < 0 )
228 return;
229 gui_list->selected_item = item_number;
230 gui_list_put_selection_in_screen(gui_list, false);
231}
232
233void gui_list_select_next(struct gui_list * gui_list)
234{
235 int item_pos;
236 int end_item;
237 int nb_lines = gui_list->display->nb_lines;
238
239 ++gui_list->selected_item;
240
241 if( gui_list->selected_item >= gui_list->nb_items )
242 {
243 /* we have already reached the bottom of the list */
244 gui_list->selected_item = 0;
245 gui_list->start_item = 0;
246 }
247 else
248 {
249 item_pos = gui_list->selected_item - gui_list->start_item;
250 end_item = gui_list->start_item + nb_lines;
251 /* we start scrolling vertically when reaching the line
252 * (nb_lines-SCROLL_LIMIT)
253 * and when we are not in the last part of the list*/
254 if( item_pos > nb_lines-SCROLL_LIMIT && end_item < gui_list->nb_items )
255 ++gui_list->start_item;
256 }
257}
258
259void gui_list_select_previous(struct gui_list * gui_list)
260{
261 int item_pos;
262 int nb_lines = gui_list->display->nb_lines;
263
264 --gui_list->selected_item;
265 if( gui_list->selected_item < 0 )
266 {
267 /* we have aleady reached the top of the list */
268 int start;
269 gui_list->selected_item = gui_list->nb_items-1;
270 start = gui_list->nb_items-nb_lines;
271 if( start < 0 )
272 gui_list->start_item = 0;
273 else
274 gui_list->start_item = start;
275 }
276 else
277 {
278 item_pos = gui_list->selected_item - gui_list->start_item;
279 if( item_pos < SCROLL_LIMIT-1 && gui_list->start_item > 0 )
280 --gui_list->start_item;
281 }
282}
283
284void gui_list_select_next_page(struct gui_list * gui_list, int nb_lines)
285{
286 if(gui_list->selected_item == gui_list->nb_items-1)
287 gui_list->selected_item = 0;
288 else
289 {
290 gui_list->selected_item += nb_lines;
291 if(gui_list->selected_item > gui_list->nb_items-1)
292 gui_list->selected_item = gui_list->nb_items-1;
293 }
294 gui_list_put_selection_in_screen(gui_list, true);
295}
296
297void gui_list_select_previous_page(struct gui_list * gui_list, int nb_lines)
298{
299 if(gui_list->selected_item == 0)
300 gui_list->selected_item = gui_list->nb_items - 1;
301 else
302 {
303 gui_list->selected_item -= nb_lines;
304 if(gui_list->selected_item < 0)
305 gui_list->selected_item = 0;
306 }
307 gui_list_put_selection_in_screen(gui_list, false);
308}
309
310void gui_list_add_item(struct gui_list * gui_list)
311{
312 ++gui_list->nb_items;
313 /* if only one item in the list, select it */
314 if(gui_list->nb_items == 1)
315 gui_list->selected_item = 0;
316}
317
318void gui_list_del_item(struct gui_list * gui_list)
319{
320 int nb_lines = gui_list->display->nb_lines;
321
322 if(gui_list->nb_items > 0)
323 {
324 int dist_selected_from_end = gui_list->nb_items
325 - gui_list->selected_item - 1;
326 int dist_start_from_end = gui_list->nb_items
327 - gui_list->start_item - 1;
328 if(dist_selected_from_end == 0)
329 {
330 /* Oops we are removing the selected item,
331 select the previous one */
332 --gui_list->selected_item;
333 }
334 --gui_list->nb_items;
335
336 /* scroll the list if needed */
337 if( (dist_start_from_end < nb_lines) && (gui_list->start_item != 0) )
338 --gui_list->start_item;
339 }
340}
341
342/*
343 * Synchronized lists stuffs
344 */
345void gui_synclist_init(
346 struct gui_synclist * lists,
347 void (*callback_get_item_icon)(int selected_item, ICON * icon),
348 char * (*callback_get_item_name)(int selected_item, char *buffer)
349 )
350{
351 int i;
352 for(i = 0;i < NB_SCREENS;i++)
353 {
354 gui_list_init(&(lists->gui_list[i]), callback_get_item_icon,
355 callback_get_item_name);
356 gui_list_set_display(&(lists->gui_list[i]), &(screens[i]));
357 }
358}
359
360void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items)
361{
362 int i;
363 for(i = 0;i < NB_SCREENS;i++)
364 {
365 gui_list_set_nb_items(&(lists->gui_list[i]), nb_items);
366 }
367}
368
369void gui_synclist_get_selected_item_name(struct gui_synclist * lists,
370 char *buffer)
371{
372 gui_list_get_selected_item_name(&(lists->gui_list[0]), buffer);
373}
374
375int gui_synclist_get_selected_item_position(struct gui_synclist * lists)
376{
377 return gui_list_get_selected_item_position(&(lists->gui_list[0]));
378}
379
380void gui_synclist_draw(struct gui_synclist * lists)
381{
382 int i;
383 for(i = 0;i < NB_SCREENS;i++)
384 gui_list_draw(&(lists->gui_list[i]));
385}
386
387void gui_synclist_select_item(struct gui_synclist * lists, int item_number)
388{
389 int i;
390 for(i = 0;i < NB_SCREENS;i++)
391 gui_list_select_item(&(lists->gui_list[i]), item_number);
392}
393
394void gui_synclist_select_next(struct gui_synclist * lists)
395{
396 int i;
397 for(i = 0;i < NB_SCREENS;i++)
398 gui_list_select_next(&(lists->gui_list[i]));
399}
400
401void gui_synclist_select_previous(struct gui_synclist * lists)
402{
403 int i;
404 for(i = 0;i < NB_SCREENS;i++)
405 gui_list_select_previous(&(lists->gui_list[i]));
406}
407
408void gui_synclist_select_next_page(struct gui_synclist * lists,
409 enum screen_type screen)
410{
411 int i;
412 for(i = 0;i < NB_SCREENS;i++)
413 gui_list_select_next_page(&(lists->gui_list[i]),
414 screens[screen].nb_lines);
415}
416
417void gui_synclist_select_previous_page(struct gui_synclist * lists,
418 enum screen_type screen)
419{
420 int i;
421 for(i = 0;i < NB_SCREENS;i++)
422 gui_list_select_previous_page(&(lists->gui_list[i]),
423 screens[screen].nb_lines);
424}
425
426void gui_synclist_add_item(struct gui_synclist * lists)
427{
428 int i;
429 for(i = 0;i < NB_SCREENS;i++)
430 gui_list_add_item(&(lists->gui_list[i]));
431}
432
433void gui_synclist_del_item(struct gui_synclist * lists)
434{
435 int i;
436 for(i = 0;i < NB_SCREENS;i++)
437 gui_list_del_item(&(lists->gui_list[i]));
438}
439
440bool gui_synclist_do_button(struct gui_synclist * lists, unsigned button)
441{
442 switch(button)
443 {
444 case LIST_PREV:
445 case LIST_PREV | BUTTON_REPEAT:
446#ifdef LIST_RC_PREV
447 case LIST_RC_PREV:
448 case LIST_RC_PREV | BUTTON_REPEAT:
449#endif
450 gui_synclist_select_previous(lists);
451 gui_synclist_draw(lists);
452 return true;
453
454 case LIST_NEXT:
455 case LIST_NEXT | BUTTON_REPEAT:
456#ifdef LIST_RC_NEXT
457 case LIST_RC_NEXT:
458 case LIST_RC_NEXT | BUTTON_REPEAT:
459#endif
460 gui_synclist_select_next(lists);
461 gui_synclist_draw(lists);
462 return true;
463/* for pgup / pgdown, we are obliged to have a different behaviour depending on the screen
464 * for which the user pressed the key since for example, remote and main screen doesn't
465 * have the same number of lines*/
466#ifdef LIST_PGUP
467 case LIST_PGUP:
468 case LIST_PGUP | BUTTON_REPEAT:
469 gui_synclist_select_previous_page(lists, SCREEN_MAIN);
470 gui_synclist_draw(lists);
471 return true;
472#endif
473
474#ifdef LIST_RC_PGUP
475 case LIST_RC_PGUP:
476 case LIST_RC_PGUP | BUTTON_REPEAT:
477 gui_synclist_select_previous_page(lists, SCREEN_REMOTE);
478 gui_synclist_draw(lists);
479 return true;
480#endif
481
482#ifdef LIST_PGDN
483 case LIST_PGDN:
484 case LIST_PGDN | BUTTON_REPEAT:
485 gui_synclist_select_next_page(lists, SCREEN_MAIN);
486 gui_synclist_draw(lists);
487 return true;
488#endif
489
490#ifdef LIST_RC_PGDN
491 case LIST_RC_PGDN:
492 case LIST_RC_PGDN | BUTTON_REPEAT:
493 gui_synclist_select_next_page(lists, SCREEN_REMOTE);
494 gui_synclist_draw(lists);
495 return true;
496#endif
497 }
498 return false;
499}