diff options
Diffstat (limited to 'apps/gui/list.c')
-rw-r--r-- | apps/gui/list.c | 499 |
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 | |||
41 | void 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 | |||
53 | void gui_list_set_nb_items(struct gui_list * gui_list, int nb_items) | ||
54 | { | ||
55 | gui_list->nb_items = nb_items; | ||
56 | } | ||
57 | |||
58 | void 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 | |||
69 | void 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 | |||
91 | void 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 | |||
96 | int gui_list_get_selected_item_position(struct gui_list * gui_list) | ||
97 | { | ||
98 | return gui_list->selected_item; | ||
99 | } | ||
100 | |||
101 | void 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 | |||
225 | void 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 | |||
233 | void 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 | |||
259 | void 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 | |||
284 | void 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 | |||
297 | void 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 | |||
310 | void 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 | |||
318 | void 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 | */ | ||
345 | void 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 | |||
360 | void 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 | |||
369 | void 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 | |||
375 | int 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 | |||
380 | void 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 | |||
387 | void 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 | |||
394 | void 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 | |||
401 | void 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 | |||
408 | void 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 | |||
417 | void 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 | |||
426 | void 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 | |||
433 | void 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 | |||
440 | bool 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 | } | ||