fix text scrolling handling in do_menu, set_time_screen and time_screen.
[kugel-rb.git] / apps / gui / bitmap / list.c
blob9e222c1973ffad7b8522ab72f7a8dd15498aeb9f
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 "sprintf.h"
29 #include "string.h"
30 #include "settings.h"
31 #include "kernel.h"
32 #include "system.h"
33 #include "file.h"
35 #include "action.h"
36 #include "screen_access.h"
37 #include "list.h"
38 #include "scrollbar.h"
39 #include "lang.h"
40 #include "sound.h"
41 #include "misc.h"
42 #include "viewport.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 display->scroll_stop(title_text_vp);
86 if (!list_display_title(list, screen))
87 return false;
88 *title_text_vp = *(list->parent[screen]);
89 title_text_vp->height = font_get(title_text_vp->font)->height;
91 if (list->title_icon != Icon_NOICON && global_settings.show_icons)
93 struct viewport title_icon = *title_text_vp;
95 title_icon.width = get_icon_width(screen) + ICON_PADDING * 2;
96 if (VP_IS_RTL(&title_icon))
98 title_icon.x += title_text_vp->width - title_icon.width;
100 else
102 title_text_vp->x += title_icon.width;
104 title_text_vp->width -= title_icon.width;
106 display->set_viewport(&title_icon);
107 screen_put_icon(display, 0, 0, list->title_icon);
109 #ifdef HAVE_LCD_COLOR
110 if (list->title_color >= 0)
112 style |= (STYLE_COLORED|list->title_color);
114 #endif
115 display->set_viewport(title_text_vp);
116 display->puts_scroll_style(0, 0, list->title, style);
117 return true;
120 void list_draw(struct screen *display, struct gui_synclist *list)
122 struct viewport list_icons;
123 int start, end, line_height, style, i;
124 const int screen = display->screen_type;
125 const int list_start_item = list->start_item[screen];
126 const int icon_width = get_icon_width(screen) + ICON_PADDING;
127 const bool scrollbar_in_left = (global_settings.scrollbar == SCROLLBAR_LEFT);
128 const bool show_cursor = !global_settings.cursor_style &&
129 list->show_selection_marker;
130 struct viewport *parent = (list->parent[screen]);
131 #ifdef HAVE_LCD_COLOR
132 unsigned char cur_line = 0;
133 #endif
134 int item_offset;
135 bool show_title;
136 struct viewport *list_text_vp = &list_text[screen];
138 line_height = font_get(parent->font)->height;
139 display->set_viewport(parent);
140 display->clear_viewport();
141 display->scroll_stop(list_text_vp);
142 *list_text_vp = *parent;
143 if ((show_title = draw_title(display, list)))
145 list_text_vp->y += line_height;
146 list_text_vp->height -= line_height;
149 start = list_start_item;
150 end = start + viewport_get_nb_lines(list_text_vp);
152 /* draw the scrollbar if its needed */
153 if (global_settings.scrollbar &&
154 viewport_get_nb_lines(list_text_vp) < list->nb_items)
156 struct viewport vp;
157 vp = *list_text_vp;
158 vp.width = SCROLLBAR_WIDTH;
159 vp.height = line_height * viewport_get_nb_lines(list_text_vp);
160 vp.x = parent->x;
161 list_text_vp->width -= SCROLLBAR_WIDTH;
162 if (scrollbar_in_left)
163 list_text_vp->x += SCROLLBAR_WIDTH;
164 else
165 vp.x += list_text_vp->width;
166 display->set_viewport(&vp);
167 gui_scrollbar_draw(display,
168 (scrollbar_in_left? 0: 1), 0, SCROLLBAR_WIDTH-1, vp.height,
169 list->nb_items, list_start_item, list_start_item + end-start,
170 VERTICAL);
172 else if (show_title)
174 /* shift everything a bit in relation to the title... */
175 if (!VP_IS_RTL(list_text_vp) && scrollbar_in_left)
177 list_text_vp->width -= SCROLLBAR_WIDTH;
178 list_text_vp->x += SCROLLBAR_WIDTH;
180 else if (VP_IS_RTL(list_text_vp) && !scrollbar_in_left)
182 list_text_vp->width -= SCROLLBAR_WIDTH;
186 /* setup icon placement */
187 list_icons = *list_text_vp;
188 int icon_count = (list->callback_get_item_icon != NULL) ? 1 : 0;
189 if (show_cursor)
190 icon_count++;
191 if (icon_count)
193 list_icons.width = icon_width * icon_count;
194 list_text_vp->width -= list_icons.width + ICON_PADDING;
195 if (VP_IS_RTL(&list_icons))
196 list_icons.x += list_text_vp->width + ICON_PADDING;
197 else
198 list_text_vp->x += list_icons.width + ICON_PADDING;
201 for (i=start; i<end && i<list->nb_items; i++)
203 /* do the text */
204 unsigned const char *s;
205 char entry_buffer[MAX_PATH];
206 unsigned char *entry_name;
207 int text_pos = 0;
208 s = list->callback_get_item_name(i, list->data, entry_buffer,
209 sizeof(entry_buffer));
210 entry_name = P2STR(s);
211 display->set_viewport(list_text_vp);
212 style = STYLE_DEFAULT;
213 /* position the string at the correct offset place */
214 int item_width,h;
215 display->getstringsize(entry_name, &item_width, &h);
216 item_offset = gui_list_get_item_offset(list, item_width, text_pos,
217 display, list_text_vp);
219 #ifdef HAVE_LCD_COLOR
220 /* if the list has a color callback */
221 if (list->callback_get_item_color)
223 int color = list->callback_get_item_color(i, list->data);
224 /* if color selected */
225 if (color >= 0)
227 style |= STYLE_COLORED|color;
230 #endif
231 if(i >= list->selected_item && i < list->selected_item
232 + list->selected_size && list->show_selection_marker)
233 {/* The selected item must be displayed scrolling */
234 if (global_settings.cursor_style == 1
235 #ifdef HAVE_REMOTE_LCD
236 /* the global_settings.cursor_style check is here to make
237 * sure if they want the cursor instead of bar it will work
239 || (display->depth < 16 && global_settings.cursor_style)
240 #endif
243 /* Display inverted-line-style */
244 style = STYLE_INVERT;
246 #ifdef HAVE_LCD_COLOR
247 else if (global_settings.cursor_style == 2)
249 /* Display colour line selector */
250 style = STYLE_COLORBAR;
252 else if (global_settings.cursor_style == 3)
254 /* Display gradient line selector */
255 style = STYLE_GRADIENT;
257 /* Make the lcd driver know how many lines the gradient should
258 cover and current line number */
259 /* number of selected lines */
260 style |= NUMLN_PACK(list->selected_size);
261 /* current line number, zero based */
262 style |= CURLN_PACK(cur_line);
263 cur_line++;
265 #endif
266 /* if the text is smaller than the viewport size */
267 if (item_offset> item_width - (list_text_vp->width - text_pos))
269 /* don't scroll */
270 display->puts_style_offset(0, i-start, entry_name,
271 style, item_offset);
273 else
275 display->puts_scroll_style_offset(0, i-start, entry_name,
276 style, item_offset);
279 else
281 if (list->scroll_all)
282 display->puts_scroll_style_offset(0, i-start, entry_name,
283 style, item_offset);
284 else
285 display->puts_style_offset(0, i-start, entry_name,
286 style, item_offset);
288 /* do the icon */
289 display->set_viewport(&list_icons);
290 if (list->callback_get_item_icon && global_settings.show_icons)
292 screen_put_icon_with_offset(display, show_cursor?1:0,
293 (i-start),show_cursor?ICON_PADDING:0,0,
294 list->callback_get_item_icon(i, list->data));
296 if (show_cursor && i >= list->selected_item &&
297 i < list->selected_item + list->selected_size)
299 screen_put_icon(display, 0, i-start, Icon_Cursor);
302 display->set_viewport(parent);
303 display->update_viewport();
304 display->set_viewport(NULL);
307 #if defined(HAVE_TOUCHSCREEN)
308 /* This needs to be fixed if we ever get more than 1 touchscreen on a target. */
309 static bool scrolling=false;
311 static int gui_synclist_touchscreen_scrollbar(struct gui_synclist * gui_list,
312 int y)
314 const int screen = screens[SCREEN_MAIN].screen_type;
315 const int nb_lines = viewport_get_nb_lines(&list_text[screen]);
317 if (nb_lines < gui_list->nb_items)
319 scrolling = true;
321 int scrollbar_size = nb_lines*
322 font_get(gui_list->parent[screen]->font)->height;
323 int actual_y = y - list_text[screen].y;
325 int new_selection = (actual_y * gui_list->nb_items)
326 / scrollbar_size;
328 int start_item = new_selection - nb_lines/2;
329 if(start_item < 0)
330 start_item = 0;
331 else if(start_item > gui_list->nb_items - nb_lines)
332 start_item = gui_list->nb_items - nb_lines;
334 gui_list->start_item[screen] = start_item;
335 gui_synclist_select_item(gui_list, new_selection);
337 return ACTION_REDRAW;
340 return ACTION_NONE;
343 unsigned gui_synclist_do_touchscreen(struct gui_synclist * gui_list)
345 short x, y;
346 const int button = action_get_touchscreen_press(&x, &y);
347 int line;
348 const struct screen *display = &screens[SCREEN_MAIN];
349 const int screen = display->screen_type;
350 const int list_start_item = gui_list->start_item[screen];
351 const struct viewport *list_text_vp = &list_text[screen];
353 if (button == BUTTON_NONE)
354 return ACTION_NONE;
355 if (x > list_text_vp->x + list_text_vp->width)
357 /* wider than the list's viewport, ignore it */
358 return ACTION_NONE;
360 else if (x < list_text_vp->x)
362 /* Top left corner is GO_TO_ROOT */
363 if (y<list_text[SCREEN_MAIN].y)
365 if (button == BUTTON_REL)
366 return ACTION_STD_MENU;
367 else if (button == (BUTTON_REPEAT|BUTTON_REL))
368 return ACTION_STD_CONTEXT;
369 else
370 return ACTION_NONE;
372 /* Scroll bar */
373 else if(global_settings.scrollbar == SCROLLBAR_LEFT)
374 return gui_synclist_touchscreen_scrollbar(gui_list, y);
376 else
378 if (x > list_text_vp->x + list_text_vp->width &&
379 global_settings.scrollbar == SCROLLBAR_RIGHT)
380 return gui_synclist_touchscreen_scrollbar(gui_list, y);
382 /* |--------------------------------------------------------|
383 * | Description of the touchscreen list interface: |
384 * |--------------------------------------------------------|
385 * | Pressing an item will select it and "enter" it. |
386 * | |
387 * | Pressing and holding your pen down will scroll through |
388 * | the list of items. |
389 * | |
390 * | Pressing and holding your pen down on a single item |
391 * | will bring up the context menu of it. |
392 * |--------------------------------------------------------|
394 if (y > list_text_vp->y || button & BUTTON_REPEAT)
396 int line_height, actual_y;
398 actual_y = y - list_text_vp->y;
399 line_height = font_get(gui_list->parent[screen]->font)->height;
400 line = actual_y / line_height;
402 /* Pressed below the list*/
403 if (list_start_item + line >= gui_list->nb_items)
404 return ACTION_NONE;
406 /* Pressed a border */
407 if(UNLIKELY(actual_y % line_height == 0))
408 return ACTION_NONE;
410 if (line != (gui_list->selected_item - list_start_item)
411 && button ^ BUTTON_REL)
413 if(button & BUTTON_REPEAT)
414 scrolling = true;
416 gui_synclist_select_item(gui_list, list_start_item + line);
418 return ACTION_REDRAW;
421 /* This has the same effect as the icons do when the scrollbar
422 is on the left (ie eliminate the chances an user enters/starts
423 an item when he wanted to use the scrollbar, due to touchscreen
424 dead zones)
426 if(global_settings.scrollbar == SCROLLBAR_RIGHT &&
427 x > list_text_vp->x + list_text_vp->width -
428 get_icon_width(SCREEN_MAIN))
429 return ACTION_NONE;
431 if (button == (BUTTON_REPEAT|BUTTON_REL))
433 if(!scrolling)
435 /* Pen was hold on the same line as the
436 * previously selected one
437 * => simulate long button press
439 return ACTION_STD_CONTEXT;
441 else
443 /* Pen was moved across several lines and then released on
444 * this one
445 * => do nothing
447 scrolling = false;
448 return ACTION_NONE;
451 else if(button == BUTTON_REL &&
452 line == gui_list->selected_item - list_start_item)
454 /* Pen was released on either the same line as the previously
455 * selected one or an other one
456 * => simulate short press
458 return ACTION_STD_OK;
460 else
461 return ACTION_NONE;
463 /* Everything above the items is cancel */
464 else if (y < list_text_vp->y && button == BUTTON_REL)
465 return ACTION_STD_CANCEL;
467 return ACTION_NONE;
469 #endif