Make skinned lists handle the "0 items" case without crashing
[maemo-rb.git] / apps / gui / bitmap / list-skinned.c
blob672a1e90e3dc86e2629b88b633ce37ee13c48363
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2011 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 #include "config.h"
23 #include "lcd.h"
24 #include "font.h"
25 #include "button.h"
26 #include "string.h"
27 #include "settings.h"
28 #include "kernel.h"
29 #include "system.h"
30 #include "file.h"
32 #include "action.h"
33 #include "screen_access.h"
34 #include "list.h"
35 #include "scrollbar.h"
36 #include "lang.h"
37 #include "sound.h"
38 #include "misc.h"
39 #include "viewport.h"
40 #include "statusbar-skinned.h"
41 #include "skin_engine/skin_engine.h"
42 #include "skin_engine/skin_display.h"
43 #include "appevents.h"
45 static struct listitem_viewport_cfg *listcfg[NB_SCREENS] = {NULL};
46 static struct gui_synclist *current_list;
48 void skinlist_set_cfg(enum screen_type screen,
49 struct listitem_viewport_cfg *cfg)
51 if (listcfg[screen] != cfg)
53 if (listcfg[screen])
54 screens[screen].scroll_stop(&listcfg[screen]->selected_item_vp.vp);
55 listcfg[screen] = cfg;
56 current_list = NULL;
60 static bool skinlist_is_configured(enum screen_type screen,
61 struct gui_synclist *list)
63 return (listcfg[screen] != NULL) &&
64 (!list || (list && list->selected_size == 1));
66 static int current_drawing_line;
67 static int offset_to_item(int offset, bool wrap)
69 int item = current_drawing_line + offset;
70 if (!current_list || current_list->nb_items == 0)
71 return -1;
72 if (item < 0)
74 if (!wrap)
75 return -1;
76 else
77 item = (item + current_list->nb_items) % current_list->nb_items;
79 else if (item >= current_list->nb_items && !wrap)
80 return -1;
81 else
82 item = item % current_list->nb_items;
83 return item;
86 int skinlist_get_item_number()
88 return current_drawing_line;
91 const char* skinlist_get_item_text(int offset, bool wrap, char* buf, size_t buf_size)
93 int item = offset_to_item(offset, wrap);
94 if (item < 0 || !current_list)
95 return NULL;
96 const char* ret = current_list->callback_get_item_name(
97 item, current_list->data, buf, buf_size);
98 return P2STR((unsigned char*)ret);
101 enum themable_icons skinlist_get_item_icon(int offset, bool wrap)
103 int item = offset_to_item(offset, wrap);
104 if (item < 0 || !current_list || current_list->callback_get_item_icon == NULL)
105 return Icon_NOICON;
106 return current_list->callback_get_item_icon(item, current_list->data);
109 static bool is_selected = false;
110 bool skinlist_is_selected_item(void)
112 return is_selected;
115 int skinlist_get_line_count(enum screen_type screen, struct gui_synclist *list)
117 struct viewport *parent = (list->parent[screen]);
118 if (!skinlist_is_configured(screen, list))
119 return -1;
120 if (listcfg[screen]->tile == true)
122 int rows = (parent->height / listcfg[screen]->height);
123 int cols = (parent->width / listcfg[screen]->width);
124 return rows*cols;
126 else
127 return (parent->height / listcfg[screen]->height);
130 static int current_item;
131 static int current_nbitems;
132 static bool needs_scrollbar[NB_SCREENS];
133 bool skinlist_needs_scrollbar(enum screen_type screen)
135 return needs_scrollbar[screen];
138 void skinlist_get_scrollbar(int* nb_item, int* first_shown, int* last_shown)
140 if (!skinlist_is_configured(0, NULL))
142 *nb_item = 0;
143 *first_shown = 0;
144 *last_shown = 0;
146 else
148 *nb_item = current_item;
149 *first_shown = 0;
150 *last_shown = current_nbitems;
154 bool skinlist_draw(struct screen *display, struct gui_synclist *list)
156 int cur_line, display_lines;
157 const int screen = display->screen_type;
158 struct viewport *parent = (list->parent[screen]);
159 char* label = NULL;
160 const int list_start_item = list->start_item[screen];
161 struct gui_wps wps;
162 if (!skinlist_is_configured(screen, list))
163 return false;
164 current_list = list;
165 wps.display = display;
166 wps.data = listcfg[screen]->data;
167 display_lines = skinlist_get_line_count(screen, list);
168 label = listcfg[screen]->label;
169 display->set_viewport(parent);
170 display->clear_viewport();
171 current_item = list->selected_item;
172 current_nbitems = list->nb_items;
173 needs_scrollbar[screen] = list->nb_items > display_lines;
175 for (cur_line = 0; cur_line < display_lines; cur_line++)
177 struct skin_element* viewport;
178 struct skin_viewport* skin_viewport = NULL;
179 if (list_start_item+cur_line+1 > list->nb_items)
180 break;
181 current_drawing_line = list_start_item+cur_line;
182 is_selected = list->show_selection_marker &&
183 list_start_item+cur_line == list->selected_item;
185 for (viewport = SKINOFFSETTOPTR(get_skin_buffer(wps.data), listcfg[screen]->data->tree);
186 viewport;
187 viewport = SKINOFFSETTOPTR(get_skin_buffer(wps.data), viewport->next))
189 int origional_x, origional_y;
190 int origional_w, origional_h;
191 skin_viewport = SKINOFFSETTOPTR(get_skin_buffer(wps.data), viewport->data);
192 char *viewport_label = SKINOFFSETTOPTR(get_skin_buffer(wps.data), skin_viewport->label);
193 if (viewport->children == 0 || !viewport_label ||
194 (skin_viewport->label && strcmp(label, viewport_label))
196 continue;
197 if (is_selected)
199 memcpy(&listcfg[screen]->selected_item_vp, skin_viewport, sizeof(struct skin_viewport));
200 skin_viewport = &listcfg[screen]->selected_item_vp;
202 origional_x = skin_viewport->vp.x;
203 origional_y = skin_viewport->vp.y;
204 origional_w = skin_viewport->vp.width;
205 origional_h = skin_viewport->vp.height;
206 if (listcfg[screen]->tile)
208 int cols = (parent->width / listcfg[screen]->width);
209 int col = (cur_line)%cols;
210 int row = (cur_line)/cols;
212 skin_viewport->vp.x = parent->x + listcfg[screen]->width*col + origional_x;
213 skin_viewport->vp.y = parent->y + listcfg[screen]->height*row + origional_y;
215 else
217 skin_viewport->vp.x = parent->x + origional_x;
218 skin_viewport->vp.y = parent->y + origional_y +
219 (listcfg[screen]->height*cur_line);
221 display->set_viewport(&skin_viewport->vp);
222 #ifdef HAVE_LCD_BITMAP
223 /* Set images to not to be displayed */
224 struct skin_token_list *imglist = SKINOFFSETTOPTR(get_skin_buffer(wps.data), wps.data->images);
225 while (imglist)
227 struct wps_token *token = SKINOFFSETTOPTR(get_skin_buffer(wps.data), imglist->token);
228 struct gui_img *img = SKINOFFSETTOPTR(get_skin_buffer(wps.data), token->value.data);
229 img->display = -1;
230 imglist = SKINOFFSETTOPTR(get_skin_buffer(wps.data), imglist->next);
232 #endif
233 struct skin_element** children = SKINOFFSETTOPTR(get_skin_buffer(wps.data), viewport->children);
234 skin_render_viewport(SKINOFFSETTOPTR(get_skin_buffer(wps.data), (intptr_t)children[0]),
235 &wps, skin_viewport, SKIN_REFRESH_ALL);
236 #ifdef HAVE_LCD_BITMAP
237 wps_display_images(&wps, &skin_viewport->vp);
238 #endif
239 /* force disableing scroll because it breaks later */
240 if (!is_selected)
242 display->scroll_stop(&skin_viewport->vp);
243 skin_viewport->vp.x = origional_x;
244 skin_viewport->vp.y = origional_y;
245 skin_viewport->vp.width = origional_w;
246 skin_viewport->vp.height = origional_h;
250 display->set_viewport(parent);
251 display->update_viewport();
252 current_drawing_line = list->selected_item;
253 #if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
254 /* Abuse the callback to force the sbs to update */
255 send_event(LCD_EVENT_ACTIVATION, NULL);
256 #endif
257 return true;