Move c/h files implementing/defining standard library stuff into a new libc directory...
[kugel-rb.git] / apps / gui / bitmap / list.c
blob0da67c7c1e76f98c80a46a3781d01e3a4b523ddc
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 by Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 /* This file contains the code to draw the list widget on BITMAP LCDs. */
24 #include "config.h"
25 #include "lcd.h"
26 #include "font.h"
27 #include "button.h"
28 #include "string.h"
29 #include "settings.h"
30 #include "kernel.h"
31 #include "system.h"
32 #include "file.h"
34 #include "action.h"
35 #include "screen_access.h"
36 #include "list.h"
37 #include "scrollbar.h"
38 #include "lang.h"
39 #include "sound.h"
40 #include "misc.h"
41 #include "viewport.h"
42 #include "statusbar-skinned.h"
44 #define ICON_PADDING 1
46 /* these are static to make scrolling work */
47 static struct viewport list_text[NB_SCREENS], title_text[NB_SCREENS];
49 int gui_list_get_item_offset(struct gui_synclist * gui_list, int item_width,
50 int text_pos, struct screen * display,
51 struct viewport *vp);
52 bool list_display_title(struct gui_synclist *list, enum screen_type screen);
54 void gui_synclist_scroll_stop(struct gui_synclist *lists)
56 int i;
57 FOR_NB_SCREENS(i)
59 screens[i].scroll_stop(&list_text[i]);
60 screens[i].scroll_stop(&title_text[i]);
61 screens[i].scroll_stop(lists->parent[i]);
65 /* Draw the list...
66 internal screen layout:
67 -----------------
68 |TI| title | TI is title icon
69 -----------------
70 | | | |
71 |S|I| | S - scrollbar
72 | | | items | I - icons
73 | | | |
74 ------------------
76 Note: This image is flipped horizontally when the language is a
77 right-to-left one (Hebrew, Arabic)
79 static bool draw_title(struct screen *display, struct gui_synclist *list)
81 const int screen = display->screen_type;
82 int style = STYLE_DEFAULT;
83 struct viewport *title_text_vp = &title_text[screen];
85 if (sb_set_title_text(list->title, list->title_icon, screen))
86 return false; /* the sbs is handling the title */
87 display->scroll_stop(title_text_vp);
88 if (!list_display_title(list, screen))
89 return false;
90 *title_text_vp = *(list->parent[screen]);
91 title_text_vp->height = font_get(title_text_vp->font)->height;
93 if (list->title_icon != Icon_NOICON && global_settings.show_icons)
95 struct viewport title_icon = *title_text_vp;
97 title_icon.width = get_icon_width(screen) + ICON_PADDING * 2;
98 if (VP_IS_RTL(&title_icon))
100 title_icon.x += title_text_vp->width - title_icon.width;
102 else
104 title_text_vp->x += title_icon.width;
106 title_text_vp->width -= title_icon.width;
108 display->set_viewport(&title_icon);
109 screen_put_icon(display, 0, 0, list->title_icon);
111 #ifdef HAVE_LCD_COLOR
112 if (list->title_color >= 0)
114 style |= (STYLE_COLORED|list->title_color);
116 #endif
117 display->set_viewport(title_text_vp);
118 display->puts_scroll_style(0, 0, list->title, style);
119 return true;
122 void list_draw(struct screen *display, struct gui_synclist *list)
124 struct viewport list_icons;
125 int start, end, line_height, style, i;
126 const int screen = display->screen_type;
127 const int list_start_item = list->start_item[screen];
128 const int icon_width = get_icon_width(screen) + ICON_PADDING;
129 const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT);
130 const bool show_cursor = !global_settings.cursor_style &&
131 list->show_selection_marker;
132 struct viewport *parent = (list->parent[screen]);
133 #ifdef HAVE_LCD_COLOR
134 unsigned char cur_line = 0;
135 #endif
136 int item_offset;
137 bool show_title;
138 struct viewport *list_text_vp = &list_text[screen];
140 line_height = font_get(parent->font)->height;
141 display->set_viewport(parent);
142 display->clear_viewport();
143 display->scroll_stop(list_text_vp);
144 *list_text_vp = *parent;
145 if ((show_title = draw_title(display, list)))
147 list_text_vp->y += line_height;
148 list_text_vp->height -= line_height;
151 start = list_start_item;
152 end = start + viewport_get_nb_lines(list_text_vp);
154 /* draw the scrollbar if its needed */
155 if (global_settings.scrollbar &&
156 viewport_get_nb_lines(list_text_vp) < list->nb_items)
158 struct viewport vp;
159 vp = *list_text_vp;
160 vp.width = SCROLLBAR_WIDTH;
161 vp.height = line_height * viewport_get_nb_lines(list_text_vp);
162 vp.x = parent->x;
163 list_text_vp->width -= SCROLLBAR_WIDTH;
164 if (scrollbar_in_left)
165 list_text_vp->x += SCROLLBAR_WIDTH;
166 else
167 vp.x += list_text_vp->width;
168 display->set_viewport(&vp);
169 gui_scrollbar_draw(display,
170 (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height,
171 list->nb_items, list_start_item, list_start_item + end-start,
172 VERTICAL);
174 else if (show_title)
176 /* shift everything a bit in relation to the title... */
177 if (!VP_IS_RTL(list_text_vp) && scrollbar_in_left)
179 list_text_vp->width -= SCROLLBAR_WIDTH;
180 list_text_vp->x += SCROLLBAR_WIDTH;
182 else if (VP_IS_RTL(list_text_vp) && !scrollbar_in_left)
184 list_text_vp->width -= SCROLLBAR_WIDTH;
188 /* setup icon placement */
189 list_icons = *list_text_vp;
190 int icon_count = (list->callback_get_item_icon != NULL) ? 1 : 0;
191 if (show_cursor)
192 icon_count++;
193 if (icon_count)
195 list_icons.width = icon_width * icon_count;
196 list_text_vp->width -= list_icons.width + ICON_PADDING;
197 if (VP_IS_RTL(&list_icons))
198 list_icons.x += list_text_vp->width + ICON_PADDING;
199 else
200 list_text_vp->x += list_icons.width + ICON_PADDING;
203 for (i=start; i<end && i<list->nb_items; i++)
205 /* do the text */
206 unsigned const char *s;
207 char entry_buffer[MAX_PATH];
208 unsigned char *entry_name;
209 int text_pos = 0;
210 s = list->callback_get_item_name(i, list->data, entry_buffer,
211 sizeof(entry_buffer));
212 entry_name = P2STR(s);
213 display->set_viewport(list_text_vp);
214 style = STYLE_DEFAULT;
215 /* position the string at the correct offset place */
216 int item_width,h;
217 display->getstringsize(entry_name, &item_width, &h);
218 item_offset = gui_list_get_item_offset(list, item_width, text_pos,
219 display, list_text_vp);
221 #ifdef HAVE_LCD_COLOR
222 /* if the list has a color callback */
223 if (list->callback_get_item_color)
225 int color = list->callback_get_item_color(i, list->data);
226 /* if color selected */
227 if (color >= 0)
229 style |= STYLE_COLORED|color;
232 #endif
233 if(i >= list->selected_item && i < list->selected_item
234 + list->selected_size && list->show_selection_marker)
235 {/* The selected item must be displayed scrolling */
236 if (global_settings.cursor_style == 1
237 #ifdef HAVE_REMOTE_LCD
238 /* the global_settings.cursor_style check is here to make
239 * sure if they want the cursor instead of bar it will work
241 || (display->depth < 16 && global_settings.cursor_style)
242 #endif
245 /* Display inverted-line-style */
246 style = STYLE_INVERT;
248 #ifdef HAVE_LCD_COLOR
249 else if (global_settings.cursor_style == 2)
251 /* Display colour line selector */
252 style = STYLE_COLORBAR;
254 else if (global_settings.cursor_style == 3)
256 /* Display gradient line selector */
257 style = STYLE_GRADIENT;
259 /* Make the lcd driver know how many lines the gradient should
260 cover and current line number */
261 /* number of selected lines */
262 style |= NUMLN_PACK(list->selected_size);
263 /* current line number, zero based */
264 style |= CURLN_PACK(cur_line);
265 cur_line++;
267 #endif
268 /* if the text is smaller than the viewport size */
269 if (item_offset> item_width - (list_text_vp->width - text_pos))
271 /* don't scroll */
272 display->puts_style_offset(0, i-start, entry_name,
273 style, item_offset);
275 else
277 display->puts_scroll_style_offset(0, i-start, entry_name,
278 style, item_offset);
281 else
283 if (list->scroll_all)
284 display->puts_scroll_style_offset(0, i-start, entry_name,
285 style, item_offset);
286 else
287 display->puts_style_offset(0, i-start, entry_name,
288 style, item_offset);
290 /* do the icon */
291 display->set_viewport(&list_icons);
292 if (list->callback_get_item_icon && global_settings.show_icons)
294 screen_put_icon_with_offset(display, show_cursor?1:0,
295 (i-start),show_cursor?ICON_PADDING:0,0,
296 list->callback_get_item_icon(i, list->data));
298 if (show_cursor && i >= list->selected_item &&
299 i < list->selected_item + list->selected_size)
301 screen_put_icon(display, 0, i-start, Icon_Cursor);
304 display->set_viewport(parent);
305 display->update_viewport();
306 display->set_viewport(NULL);
309 #if defined(HAVE_TOUCHSCREEN)
310 /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
311 static bool scrolling=false;
313 static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
314 int y)
316 const int screen = screens[SCREEN_MAIN].screen_type;
317 const int nb_lines = viewport_get_nb_lines(&list_text[screen]);
319 if (nb_lines < gui_list->nb_items)
321 scrolling = true;
323 int scrollbar_size = nb_lines*
324 font_get(gui_list->parent[screen]->font)->height;
325 int actual_y = y - list_text[screen].y;
327 int new_selection = (actual_y * gui_list->nb_items)
328 / scrollbar_size;
330 int start_item = new_selection - nb_lines/2;
331 if(start_item < 0)
332 start_item = 0;
333 else if(start_item > gui_list->nb_items - nb_lines)
334 start_item = gui_list->nb_items - nb_lines;
336 gui_list->start_item[screen] = start_item;
337 gui_synclist_select_item(gui_list, new_selection);
339 return ACTION_REDRAW;
342 return ACTION_NONE;
345 unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
347 short x, y;
348 const int button = action_get_touchscreen_press(&x, &y);
349 int line;
350 const struct screen *display = &screens[SCREEN_MAIN];
351 const int screen = display->screen_type;
352 const int list_start_item = gui_list->start_item[screen];
353 const struct viewport *list_text_vp = &list_text[screen];
355 if (button == BUTTON_NONE)
356 return ACTION_NONE;
358 if (x > list_text_vp->x + list_text_vp->width)
360 if (global_settings.scrollbar == SCROLLBAR_RIGHT &&
361 x > list_text_vp->x + list_text_vp->width + SCROLLBAR_WIDTH)
363 /* wider than the list's viewport, ignore it */
364 return ACTION_NONE;
368 if (x < list_text_vp->x)
370 /* Top left corner is GO_TO_ROOT */
371 if (y<list_text[SCREEN_MAIN].y)
373 if (button == BUTTON_REL)
374 return ACTION_STD_MENU;
375 else if (button == (BUTTON_REPEAT|BUTTON_REL))
376 return ACTION_STD_CONTEXT;
377 else
378 return ACTION_NONE;
380 /* Scroll bar */
381 else if(global_settings.scrollbar == SCROLLBAR_LEFT)
382 return gui_synclist_touchscreen_scrollbar(gui_list, y);
384 else
386 if (x > list_text_vp->x + list_text_vp->width &&
387 global_settings.scrollbar == SCROLLBAR_RIGHT)
388 return gui_synclist_touchscreen_scrollbar(gui_list, y);
390 /* |--------------------------------------------------------|
391 * | Description of the touchscreen list interface: |
392 * |--------------------------------------------------------|
393 * | Pressing an item will select it and "enter" it. |
394 * | |
395 * | Pressing and holding your pen down will scroll through |
396 * | the list of items. |
397 * | |
398 * | Pressing and holding your pen down on a single item |
399 * | will bring up the context menu of it. |
400 * |--------------------------------------------------------|
402 if (y > list_text_vp->y || button & BUTTON_REPEAT)
404 int line_height, actual_y;
406 actual_y = y - list_text_vp->y;
407 line_height = font_get(gui_list->parent[screen]->font)->height;
408 line = actual_y / line_height;
410 /* Pressed below the list*/
411 if (list_start_item + line >= gui_list->nb_items)
412 return ACTION_NONE;
414 /* Pressed a border */
415 if(UNLIKELY(actual_y % line_height == 0))
416 return ACTION_NONE;
418 if (line != (gui_list->selected_item - list_start_item)
419 && button ^ BUTTON_REL)
421 if(button & BUTTON_REPEAT)
422 scrolling = true;
424 gui_synclist_select_item(gui_list, list_start_item + line);
426 return ACTION_REDRAW;
429 /* This has the same effect as the icons do when the scrollbar
430 is on the left (ie eliminate the chances an user enters/starts
431 an item when he wanted to use the scrollbar, due to touchscreen
432 dead zones)
434 if(global_settings.scrollbar == SCROLLBAR_RIGHT &&
435 x > list_text_vp->x + list_text_vp->width -
436 get_icon_width(SCREEN_MAIN))
437 return ACTION_NONE;
439 if (button == (BUTTON_REPEAT|BUTTON_REL))
441 if(!scrolling)
443 /* Pen was hold on the same line as the
444 * previously selected one
445 * => simulate long button press
447 return ACTION_STD_CONTEXT;
449 else
451 /* Pen was moved across several lines and then released on
452 * this one
453 * => do nothing
455 scrolling = false;
456 return ACTION_NONE;
459 else if(button == BUTTON_REL &&
460 line == gui_list->selected_item - list_start_item)
462 /* Pen was released on either the same line as the previously
463 * selected one or an other one
464 * => simulate short press
466 return ACTION_STD_OK;
468 else
469 return ACTION_NONE;
471 /* Everything above the items is cancel */
472 else if (y < list_text_vp->y && button == BUTTON_REL)
473 return ACTION_STD_CANCEL;
475 return ACTION_NONE;
477 #endif