added degree symbol
[Rockbox.git] / apps / menu.c
blob6ab57ad061888a1db40f0d143f433fb7d71cdeeb
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Robert E. Hak
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 2005 Kevin Ferrare :
21 - Multi screen support
22 - Rewrote/removed a lot of code now useless with the new gui API
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include "config.h"
27 #include "system.h"
29 #include "lcd.h"
30 #include "font.h"
31 #include "backlight.h"
32 #include "menu.h"
33 #include "button.h"
34 #include "kernel.h"
35 #include "debug.h"
36 #include "usb.h"
37 #include "panic.h"
38 #include "settings.h"
39 #include "settings_list.h"
40 #include "option_select.h"
41 #include "status.h"
42 #include "screens.h"
43 #include "talk.h"
44 #include "lang.h"
45 #include "misc.h"
46 #include "action.h"
47 #include "menus/exported_menus.h"
48 #include "string.h"
49 #include "root_menu.h"
50 #include "bookmark.h"
51 #include "gwps-common.h" /* for fade() */
52 #include "audio.h"
54 #ifdef HAVE_LCD_BITMAP
55 #include "icons.h"
56 #endif
58 /* gui api */
59 #include "list.h"
60 #include "statusbar.h"
61 #include "buttonbar.h"
63 #define MAX_MENUS 8
64 /* used to allow for dynamic menus */
65 #define MAX_MENU_SUBITEMS 64
66 static struct menu_item_ex *current_submenus_menu;
67 static int current_subitems[MAX_MENU_SUBITEMS];
68 static int current_subitems_count = 0;
69 static int talk_menu_item(int selected_item, void *data);
71 static void get_menu_callback(const struct menu_item_ex *m,
72 menu_callback_type *menu_callback)
74 if (m->flags&(MENU_HAS_DESC|MENU_DYNAMIC_DESC))
75 *menu_callback= m->callback_and_desc->menu_callback;
76 else
77 *menu_callback = m->menu_callback;
80 static int get_menu_selection(int selected_item, const struct menu_item_ex *menu)
82 int type = (menu->flags&MENU_TYPE_MASK);
83 if (type == MT_MENU && (selected_item<current_subitems_count))
84 return current_subitems[selected_item];
85 return selected_item;
87 static int find_menu_selection(int selected)
89 int i;
90 for (i=0; i< current_subitems_count; i++)
91 if (current_subitems[i] == selected)
92 return i;
93 return 0;
95 static char * get_menu_item_name(int selected_item,void * data, char *buffer)
97 const struct menu_item_ex *menu = (const struct menu_item_ex *)data;
98 int type = (menu->flags&MENU_TYPE_MASK);
99 selected_item = get_menu_selection(selected_item, menu);
101 (void)buffer;
102 /* only MT_MENU or MT_RETURN_ID is allowed in here */
103 if (type == MT_RETURN_ID)
105 if (menu->flags&MENU_DYNAMIC_DESC)
106 return menu->menu_get_name_and_icon->list_get_name(selected_item,
107 menu->menu_get_name_and_icon->list_get_name_data, buffer);
108 return (char*)menu->strings[selected_item];
111 menu = menu->submenus[selected_item];
113 if ((menu->flags&MENU_DYNAMIC_DESC) && (type != MT_SETTING_W_TEXT))
114 return menu->menu_get_name_and_icon->list_get_name(selected_item,
115 menu->menu_get_name_and_icon->list_get_name_data, buffer);
117 type = (menu->flags&MENU_TYPE_MASK);
118 if ((type == MT_SETTING) || (type == MT_SETTING_W_TEXT))
120 const struct settings_list *v
121 = find_setting(menu->variable, NULL);
122 if (v)
123 return str(v->lang_id);
124 else return "Not Done yet!";
126 return P2STR(menu->callback_and_desc->desc);
128 #ifdef HAVE_LCD_BITMAP
129 static int menu_get_icon(int selected_item, void * data)
131 const struct menu_item_ex *menu = (const struct menu_item_ex *)data;
132 int menu_icon = Icon_NOICON;
133 selected_item = get_menu_selection(selected_item, menu);
135 if ((menu->flags&MENU_TYPE_MASK) == MT_RETURN_ID)
137 return Icon_Menu_functioncall;
139 menu = menu->submenus[selected_item];
140 if (menu->flags&MENU_HAS_DESC)
141 menu_icon = menu->callback_and_desc->icon_id;
142 else if (menu->flags&MENU_DYNAMIC_DESC)
143 menu_icon = menu->menu_get_name_and_icon->icon_id;
145 if (menu_icon == Icon_NOICON)
147 switch (menu->flags&MENU_TYPE_MASK)
149 case MT_SETTING:
150 case MT_SETTING_W_TEXT:
151 menu_icon = Icon_Menu_setting;
152 break;
153 case MT_MENU:
154 menu_icon = Icon_Submenu;
155 break;
156 case MT_FUNCTION_CALL:
157 case MT_RETURN_VALUE:
158 menu_icon = Icon_Menu_functioncall;
159 break;
162 return menu_icon;
164 #endif
166 static void init_menu_lists(const struct menu_item_ex *menu,
167 struct gui_synclist *lists, int selected, bool callback)
169 int i, count = MENU_GET_COUNT(menu->flags);
170 int type = (menu->flags&MENU_TYPE_MASK);
171 menu_callback_type menu_callback = NULL;
172 int icon;
173 current_subitems_count = 0;
175 if (type == MT_RETURN_ID)
176 get_menu_callback(menu, &menu_callback);
178 for (i=0; i<count; i++)
180 if (type != MT_RETURN_ID)
181 get_menu_callback(menu->submenus[i],&menu_callback);
182 if (menu_callback)
184 if (menu_callback(ACTION_REQUEST_MENUITEM,
185 type==MT_RETURN_ID ? (void*)(intptr_t)i: menu->submenus[i])
186 != ACTION_EXIT_MENUITEM)
188 current_subitems[current_subitems_count] = i;
189 current_subitems_count++;
192 else
194 current_subitems[current_subitems_count] = i;
195 current_subitems_count++;
198 current_submenus_menu = (struct menu_item_ex *)menu;
200 gui_synclist_init(lists,get_menu_item_name,(void*)menu,false,1);
201 #ifdef HAVE_LCD_BITMAP
202 if (menu->callback_and_desc->icon_id == Icon_NOICON)
203 icon = Icon_Submenu_Entered;
204 else
205 icon = menu->callback_and_desc->icon_id;
206 gui_synclist_set_title(lists, P2STR(menu->callback_and_desc->desc), icon);
207 gui_synclist_set_icon_callback(lists, menu_get_icon);
208 #else
209 (void)icon;
210 gui_synclist_set_icon_callback(lists, NULL);
211 #endif
212 if(global_settings.talk_menu)
213 gui_synclist_set_voice_callback(lists, talk_menu_item);
214 gui_synclist_set_nb_items(lists,current_subitems_count);
215 gui_synclist_limit_scroll(lists,true);
216 gui_synclist_select_item(lists, find_menu_selection(selected));
218 get_menu_callback(menu,&menu_callback);
219 if (callback && menu_callback)
220 menu_callback(ACTION_ENTER_MENUITEM,menu);
221 gui_synclist_draw(lists);
222 gui_synclist_speak_item(lists);
225 static int talk_menu_item(int selected_item, void *data)
227 const struct menu_item_ex *menu = (const struct menu_item_ex *)data;
228 int id = -1;
229 int type;
230 unsigned char *str;
231 int sel = get_menu_selection(selected_item, menu);
233 if ((menu->flags&MENU_TYPE_MASK) == MT_MENU)
235 type = menu->submenus[sel]->flags&MENU_TYPE_MASK;
236 if ((type == MT_SETTING) || (type == MT_SETTING_W_TEXT))
237 talk_setting(menu->submenus[sel]->variable);
238 else
240 if (menu->submenus[sel]->flags&(MENU_DYNAMIC_DESC))
242 int (*list_speak_item)(int selected_item, void * data)
243 = menu->submenus[sel]->menu_get_name_and_icon->
244 list_speak_item;
245 if(list_speak_item)
246 list_speak_item(sel, menu->submenus[sel]->
247 menu_get_name_and_icon->
248 list_get_name_data);
249 else
251 char buffer[80];
252 str = menu->submenus[sel]->menu_get_name_and_icon->
253 list_get_name(sel, menu->submenus[sel]->
254 menu_get_name_and_icon->
255 list_get_name_data, buffer);
256 id = P2ID(str);
259 else
260 id = P2ID(menu->submenus[sel]->callback_and_desc->desc);
261 if (id != -1)
262 talk_id(id,false);
265 else if(((menu->flags&MENU_TYPE_MASK) == MT_RETURN_ID))
267 if ((menu->flags&MENU_DYNAMIC_DESC) == 0)
269 unsigned char *s = (unsigned char *)menu->strings[sel];
270 /* string list, try to talk it if ID2P was used */
271 id = P2ID(s);
272 if (id != -1)
273 talk_id(id,false);
276 return 0;
278 #define MAX_OPTIONS 32
279 bool do_setting_from_menu(const struct menu_item_ex *temp)
281 int setting_id;
282 const struct settings_list *setting = find_setting(
283 temp->variable,
284 &setting_id);
285 char *title;
286 char padded_title[MAX_PATH];
287 if ((temp->flags&MENU_TYPE_MASK) == MT_SETTING_W_TEXT)
288 title = temp->callback_and_desc->desc;
289 else
290 title = ID2P(setting->lang_id);
292 /* Pad the title string by repeating it. This is needed
293 so the scroll settings title can actually be used to
294 test the setting */
295 if (setting->flags&F_PADTITLE)
297 int i = 0, len;
298 if (setting->lang_id == -1)
299 title = (char*)setting->cfg_vals;
300 else
301 title = P2STR((unsigned char*)title);
302 len = strlen(title);
303 while (i < MAX_PATH-1)
305 int padlen = MIN(len, MAX_PATH-1-i);
306 strncpy(&padded_title[i], title, padlen);
307 i += padlen;
308 if (i<MAX_PATH-1)
309 padded_title[i++] = ' ';
311 padded_title[i] = '\0';
312 title = padded_title;
315 option_screen((struct settings_list *)setting,
316 setting->flags&F_TEMPVAR, title);
317 return false;
320 /* display a menu */
321 int do_menu(const struct menu_item_ex *start_menu, int *start_selected)
323 int selected = start_selected? *start_selected : 0;
324 int action;
325 struct gui_synclist lists;
326 const struct menu_item_ex *temp, *menu;
327 int ret = 0;
328 bool redraw_lists;
329 #ifdef HAS_BUTTONBAR
330 struct gui_buttonbar buttonbar;
331 #endif
333 const struct menu_item_ex *menu_stack[MAX_MENUS];
334 int menu_stack_selected_item[MAX_MENUS];
335 int stack_top = 0;
336 bool in_stringlist, done = false;
337 menu_callback_type menu_callback = NULL;
338 if (start_menu == NULL)
339 menu = &main_menu_;
340 else menu = start_menu;
341 #ifdef HAS_BUTTONBAR
342 gui_buttonbar_init(&buttonbar);
343 gui_buttonbar_set_display(&buttonbar, &(screens[SCREEN_MAIN]) );
344 gui_buttonbar_set(&buttonbar, "<<<", "", "");
345 gui_buttonbar_draw(&buttonbar);
346 #endif
347 init_menu_lists(menu,&lists,selected,true);
348 in_stringlist = ((menu->flags&MENU_TYPE_MASK) == MT_RETURN_ID);
350 /* load the callback, and only reload it if menu changes */
351 get_menu_callback(menu, &menu_callback);
353 while (!done)
355 redraw_lists = false;
356 gui_syncstatusbar_draw(&statusbars, true);
357 action = get_action(CONTEXT_MAINMENU,
358 list_do_action_timeout(&lists, HZ));
359 /* HZ so the status bar redraws corectly */
361 if (action != ACTION_NONE && menu_callback)
363 int old_action = action;
364 action = menu_callback(action, menu);
365 if (action == ACTION_EXIT_AFTER_THIS_MENUITEM)
367 action = old_action;
368 ret = MENU_SELECTED_EXIT; /* will exit after returning
369 from selection */
371 else if (action == ACTION_REDRAW)
373 action = old_action;
374 redraw_lists = true;
378 if (gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD))
379 continue;
380 if (action == ACTION_NONE)
381 continue;
383 #ifdef HAVE_RECORDING
384 if (action == ACTION_STD_REC)
386 ret = GO_TO_RECSCREEN;
387 done = true;
389 else
390 #endif
391 if (action == ACTION_TREE_WPS)
393 ret = GO_TO_PREVIOUS_MUSIC;
394 done = true;
396 else if (action == ACTION_TREE_STOP)
398 redraw_lists = list_stop_handler();
400 else if (action == ACTION_STD_CONTEXT &&
401 menu == &root_menu_)
403 ret = GO_TO_ROOTITEM_CONTEXT;
404 done = true;
406 else if (action == ACTION_STD_MENU)
408 if (menu != &root_menu_)
409 ret = GO_TO_ROOT;
410 else
411 ret = GO_TO_PREVIOUS;
412 done = true;
414 else if (action == ACTION_STD_CANCEL)
416 bool exiting_menu = false;
417 in_stringlist = false;
418 if (menu_callback)
419 menu_callback(ACTION_EXIT_MENUITEM, menu);
421 if (menu->flags&MENU_EXITAFTERTHISMENU)
422 done = true;
423 else if ((menu->flags&MENU_TYPE_MASK) == MT_MENU)
424 exiting_menu = true;
425 if (stack_top > 0)
427 stack_top--;
428 menu = menu_stack[stack_top];
429 if (!exiting_menu && (menu->flags&MENU_EXITAFTERTHISMENU))
430 done = true;
431 else
432 init_menu_lists(menu, &lists,
433 menu_stack_selected_item[stack_top], false);
434 /* new menu, so reload the callback */
435 get_menu_callback(menu, &menu_callback);
437 else if (menu != &root_menu_)
439 ret = GO_TO_PREVIOUS;
440 done = true;
443 else if (action == ACTION_STD_OK)
445 int type;
446 #ifdef HAS_BUTTONBAR
447 gui_buttonbar_unset(&buttonbar);
448 gui_buttonbar_draw(&buttonbar);
449 #endif
450 selected = get_menu_selection(gui_synclist_get_sel_pos(&lists), menu);
451 temp = menu->submenus[selected];
452 redraw_lists = true;
453 if (in_stringlist)
454 type = (menu->flags&MENU_TYPE_MASK);
455 else
457 type = (temp->flags&MENU_TYPE_MASK);
458 get_menu_callback(temp, &menu_callback);
459 if (menu_callback)
461 action = menu_callback(ACTION_ENTER_MENUITEM,temp);
462 if (action == ACTION_EXIT_MENUITEM)
463 break;
466 switch (type)
468 case MT_MENU:
469 if (stack_top < MAX_MENUS)
471 menu_stack[stack_top] = menu;
472 menu_stack_selected_item[stack_top] = selected;
473 stack_top++;
474 init_menu_lists(temp, &lists, 0, true);
475 redraw_lists = false; /* above does the redraw */
476 menu = temp;
478 break;
479 case MT_FUNCTION_CALL:
481 int return_value;
482 if (temp->flags&MENU_FUNC_USEPARAM)
483 return_value = temp->function->function_w_param(
484 temp->function->param);
485 else
486 return_value = temp->function->function();
488 /* check if a new lang was loaded. IF this isnt done then
489 the title for "General Settings" will be fudged untill
490 the settings menu is exited and reentered */
491 if (temp->flags&MENU_HAS_DESC &&
492 temp->callback_and_desc->desc == ID2P(LANG_LANGUAGE))
494 init_menu_lists(menu, &lists, selected, true);
497 if (temp->flags&MENU_FUNC_CHECK_RETVAL)
499 if (return_value == 1)
501 done = true;
502 ret = return_value;
505 break;
507 case MT_SETTING:
508 case MT_SETTING_W_TEXT:
510 if (do_setting_from_menu(temp))
512 init_menu_lists(menu, &lists, selected, true);
513 redraw_lists = false; /* above does the redraw */
515 break;
517 case MT_RETURN_ID:
518 if (in_stringlist)
520 done = true;
521 ret = selected;
523 else if (stack_top < MAX_MENUS)
525 menu_stack[stack_top] = menu;
526 menu_stack_selected_item[stack_top] = selected;
527 stack_top++;
528 menu = temp;
529 init_menu_lists(menu,&lists,0,false);
530 redraw_lists = false; /* above does the redraw */
531 in_stringlist = true;
533 break;
534 case MT_RETURN_VALUE:
535 ret = temp->value;
536 done = true;
537 break;
539 if (type != MT_MENU)
541 if (menu_callback)
542 menu_callback(ACTION_EXIT_MENUITEM,temp);
544 if (current_submenus_menu != menu)
545 init_menu_lists(menu,&lists,selected,true);
546 /* callback was changed, so reload the menu's callback */
547 get_menu_callback(menu, &menu_callback);
548 if ((menu->flags&MENU_EXITAFTERTHISMENU) &&
549 !(temp->flags&MENU_EXITAFTERTHISMENU))
551 done = true;
552 break;
554 #ifdef HAS_BUTTONBAR
555 gui_buttonbar_set(&buttonbar, "<<<", "", "");
556 gui_buttonbar_draw(&buttonbar);
557 #endif
559 else if(default_event_handler(action) == SYS_USB_CONNECTED)
561 ret = MENU_ATTACHED_USB;
562 done = true;
565 if (redraw_lists && !done)
567 gui_synclist_draw(&lists);
568 gui_synclist_speak_item(&lists);
571 if (start_selected)
573 /* make sure the start_selected variable is set to
574 the selected item from the menu do_menu() was called from */
575 if (stack_top > 0)
577 menu = menu_stack[0];
578 init_menu_lists(menu,&lists,menu_stack_selected_item[0],true);
580 *start_selected = get_menu_selection(
581 gui_synclist_get_sel_pos(&lists), menu);
583 return ret;