rockbox/apps/root_menu.c
Teruaki Kawashima 08af5d8404 FS#11777: enhancement for rockbox_browse()
* Add struct browse_context to be passed to rockbox_browse.
* Show proper title when selecting e.g. .wps file or .sbs file from the settings menu.
* Add select only mode to rockbox_browse(). when a file is selected, it's path is stored to buffer and the browser exits without 'playing' the file.
this will allow to use the browser in more places to select file including plugins.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28831 a1c6a512-1295-4272-9138-f99709370657
2010-12-14 13:37:58 +00:00

711 lines
22 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 Jonathan Gordon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "string-extra.h"
#include "config.h"
#include "appevents.h"
#include "menu.h"
#include "root_menu.h"
#include "lang.h"
#include "settings.h"
#include "screens.h"
#include "kernel.h"
#include "debug.h"
#include "misc.h"
#include "rolo.h"
#include "powermgmt.h"
#include "power.h"
#include "talk.h"
#include "audio.h"
#ifdef HAVE_HOTSWAP
#include "storage.h"
#include "dir.h"
#endif
/* gui api */
#include "list.h"
#include "splash.h"
#include "action.h"
#include "yesno.h"
#include "viewport.h"
#include "tree.h"
#if CONFIG_TUNER
#include "radio.h"
#endif
#ifdef HAVE_RECORDING
#include "recording.h"
#endif
#include "wps.h"
#include "bookmark.h"
#include "playlist.h"
#include "playlist_viewer.h"
#include "menus/exported_menus.h"
#ifdef HAVE_RTC_ALARM
#include "rtc.h"
#endif
#ifdef HAVE_TAGCACHE
#include "tagcache.h"
#endif
#include "language.h"
#include "plugin.h"
struct root_items {
int (*function)(void* param);
void* param;
const struct menu_item_ex *context_menu;
};
static int next_screen = GO_TO_ROOT; /* holding info about the upcoming screen
* which is the current screen for the
* rest of the code after load_screen
* is called */
static int last_screen = GO_TO_ROOT; /* unfortunatly needed so we can resume
or goto current track based on previous
screen */
static char current_track_path[MAX_PATH];
static void rootmenu_track_changed_callback(void* param)
{
struct mp3entry *id3 = (struct mp3entry *)param;
strlcpy(current_track_path, id3->path, MAX_PATH);
}
static int browser(void* param)
{
int ret_val;
#ifdef HAVE_TAGCACHE
struct tree_context* tc = tree_get_context();
#endif
struct browse_context browse;
int filter = SHOW_SUPPORTED;
char folder[MAX_PATH] = "/";
/* stuff needed to remember position in file browser */
static char last_folder[MAX_PATH] = "/";
/* and stuff for the database browser */
#ifdef HAVE_TAGCACHE
static int last_db_dirlevel = 0, last_db_selection = 0;
#endif
switch ((intptr_t)param)
{
case GO_TO_FILEBROWSER:
filter = global_settings.dirfilter;
if (global_settings.browse_current &&
last_screen == GO_TO_WPS &&
current_track_path[0])
{
strcpy(folder, current_track_path);
}
else if (!strcmp(last_folder, "/"))
{
strcpy(folder, global_settings.start_directory);
}
else
{
#ifdef HAVE_HOTSWAP
bool in_hotswap = false;
/* handle entering an ejected drive */
int i;
for (i = 0; i < NUM_VOLUMES; i++)
{
char vol_string[VOL_ENUM_POS + 8];
if (!storage_removable(i))
continue;
/* VOL_NAMES contains a %d */
snprintf(vol_string, sizeof(vol_string), "/"VOL_NAMES, i);
/* test whether we would browse the external card */
if (!storage_present(i) &&
(strstr(last_folder, vol_string)
#ifdef HAVE_HOTSWAP_STORAGE_AS_MAIN
|| (i == 0)
#endif
))
{ /* leave folder as "/" to avoid crash when trying
* to access an ejected drive */
strcpy(folder, "/");
in_hotswap = true;
break;
}
}
if (!in_hotswap)
#endif
strcpy(folder, last_folder);
}
break;
#ifdef HAVE_TAGCACHE
case GO_TO_DBBROWSER:
if (!tagcache_is_usable())
{
bool reinit_attempted = false;
/* Now display progress until it's ready or the user exits */
while(!tagcache_is_usable())
{
struct tagcache_stat *stat = tagcache_get_stat();
/* Allow user to exit */
if (action_userabort(HZ/2))
break;
/* Maybe just needs to reboot due to delayed commit */
if (stat->commit_delayed)
{
splash(HZ*2, ID2P(LANG_PLEASE_REBOOT));
break;
}
/* Check if ready status is known */
if (!stat->readyvalid)
{
splash(0, str(LANG_TAGCACHE_BUSY));
continue;
}
/* Re-init if required */
if (!reinit_attempted && !stat->ready &&
stat->processed_entries == 0 && stat->commit_step == 0)
{
/* Prompt the user */
reinit_attempted = true;
static const char *lines[]={
ID2P(LANG_TAGCACHE_BUSY), ID2P(LANG_TAGCACHE_FORCE_UPDATE)};
static const struct text_message message={lines, 2};
if(gui_syncyesno_run(&message, NULL, NULL) == YESNO_NO)
break;
int i;
FOR_NB_SCREENS(i)
screens[i].clear_display();
/* Start initialisation */
tagcache_rebuild();
}
/* Display building progress */
static long talked_tick = 0;
if(global_settings.talk_menu &&
(talked_tick == 0
|| TIME_AFTER(current_tick, talked_tick+7*HZ)))
{
talked_tick = current_tick;
if (stat->commit_step > 0)
{
talk_id(LANG_TAGCACHE_INIT, false);
talk_number(stat->commit_step, true);
talk_id(VOICE_OF, true);
talk_number(tagcache_get_max_commit_step(), true);
} else if(stat->processed_entries)
{
talk_number(stat->processed_entries, false);
talk_id(LANG_BUILDING_DATABASE, true);
}
}
if (stat->commit_step > 0)
{
if (lang_is_rtl())
{
splashf(0, "[%d/%d] %s", stat->commit_step,
tagcache_get_max_commit_step(),
str(LANG_TAGCACHE_INIT));
}
else
{
splashf(0, "%s [%d/%d]", str(LANG_TAGCACHE_INIT),
stat->commit_step,
tagcache_get_max_commit_step());
}
}
else
{
splashf(0, str(LANG_BUILDING_DATABASE),
stat->processed_entries);
}
}
}
if (!tagcache_is_usable())
return GO_TO_PREVIOUS;
filter = SHOW_ID3DB;
tc->dirlevel = last_db_dirlevel;
tc->selected_item = last_db_selection;
break;
#endif
}
browse_context_init(&browse, filter, 0, NULL, NOICON, folder, NULL);
ret_val = rockbox_browse(&browse);
switch ((intptr_t)param)
{
case GO_TO_FILEBROWSER:
if (!get_current_file(last_folder, MAX_PATH) ||
(!strchr(&last_folder[1], '/') &&
global_settings.start_directory[1] != '\0'))
{
last_folder[0] = '/';
last_folder[1] = '\0';
}
break;
#ifdef HAVE_TAGCACHE
case GO_TO_DBBROWSER:
last_db_dirlevel = tc->dirlevel;
last_db_selection = tc->selected_item;
break;
#endif
}
return ret_val;
}
static int menu(void* param)
{
(void)param;
return do_menu(NULL, 0, NULL, false);
}
#ifdef HAVE_RECORDING
static int recscrn(void* param)
{
(void)param;
recording_screen(false);
return GO_TO_ROOT;
}
#endif
static int wpsscrn(void* param)
{
int ret_val = GO_TO_PREVIOUS;
(void)param;
if (audio_status())
{
talk_shutup();
ret_val = gui_wps_show();
}
else if ( global_status.resume_index != -1 )
{
DEBUGF("Resume index %X offset %lX\n",
global_status.resume_index,
(unsigned long)global_status.resume_offset);
if (playlist_resume() != -1)
{
playlist_start(global_status.resume_index,
global_status.resume_offset);
ret_val = gui_wps_show();
}
}
else
{
splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
}
return ret_val;
}
#if CONFIG_TUNER
static int radio(void* param)
{
(void)param;
radio_screen();
return GO_TO_ROOT;
}
#endif
static int playlist_view(void * param)
{
(void)param;
switch (playlist_viewer())
{
case PLAYLIST_VIEWER_MAINMENU:
case PLAYLIST_VIEWER_USB:
return GO_TO_ROOT;
case PLAYLIST_VIEWER_OK:
return GO_TO_PREVIOUS;
}
return GO_TO_PREVIOUS;
}
static int load_bmarks(void* param)
{
(void)param;
if(bookmark_mrb_load())
return GO_TO_WPS;
return GO_TO_PREVIOUS;
}
static int plugins_menu(void* param)
{
(void)param;
MENUITEM_STRINGLIST(plugins_menu_items, ID2P(LANG_PLUGINS), NULL,
ID2P(LANG_PLUGIN_GAMES),
ID2P(LANG_PLUGIN_APPS), ID2P(LANG_PLUGIN_DEMOS));
const char *folder;
struct browse_context browse;
char *title;
int retval = GO_TO_PREVIOUS;
int selection = 0, current = 0;
while (retval == GO_TO_PREVIOUS)
{
selection = do_menu(&plugins_menu_items, &current, NULL, false);
switch (selection)
{
case 0:
folder = PLUGIN_GAMES_DIR;
title = str(LANG_PLUGIN_GAMES);
break;
case 1:
folder = PLUGIN_APPS_DIR;
title = str(LANG_PLUGIN_APPS);
break;
case 2:
folder = PLUGIN_DEMOS_DIR;
title = str(LANG_PLUGIN_DEMOS);
break;
default:
return selection;
}
browse_context_init(&browse, SHOW_PLUGINS, 0,
title, Icon_Plugin, folder, NULL);
retval = rockbox_browse(&browse);
}
return retval;
}
int time_screen(void* ignored);
/* These are all static const'd from apps/menus/ *.c
so little hack so we can use them */
extern struct menu_item_ex
file_menu,
#ifdef HAVE_TAGCACHE
tagcache_menu,
#endif
manage_settings,
recording_settings_menu,
radio_settings_menu,
bookmark_settings_menu,
system_menu;
static const struct root_items items[] = {
[GO_TO_FILEBROWSER] = { browser, (void*)GO_TO_FILEBROWSER, &file_menu},
#ifdef HAVE_TAGCACHE
[GO_TO_DBBROWSER] = { browser, (void*)GO_TO_DBBROWSER, &tagcache_menu },
#endif
[GO_TO_WPS] = { wpsscrn, NULL, &playback_settings },
[GO_TO_MAINMENU] = { menu, NULL, &manage_settings },
#ifdef HAVE_RECORDING
[GO_TO_RECSCREEN] = { recscrn, NULL, &recording_settings_menu },
#endif
#if CONFIG_TUNER
[GO_TO_FM] = { radio, NULL, &radio_settings_menu },
#endif
[GO_TO_RECENTBMARKS] = { load_bmarks, NULL, &bookmark_settings_menu },
[GO_TO_BROWSEPLUGINS] = { plugins_menu, NULL, NULL },
[GO_TO_PLAYLIST_VIEWER] = { playlist_view, NULL, NULL },
};
static const int nb_items = sizeof(items)/sizeof(*items);
static int item_callback(int action, const struct menu_item_ex *this_item) ;
MENUITEM_RETURNVALUE(file_browser, ID2P(LANG_DIR_BROWSER), GO_TO_FILEBROWSER,
NULL, Icon_file_view_menu);
#ifdef HAVE_TAGCACHE
MENUITEM_RETURNVALUE(db_browser, ID2P(LANG_TAGCACHE), GO_TO_DBBROWSER,
NULL, Icon_Audio);
#endif
MENUITEM_RETURNVALUE(rocks_browser, ID2P(LANG_PLUGINS), GO_TO_BROWSEPLUGINS,
NULL, Icon_Plugin);
static char *get_wps_item_name(int selected_item, void * data, char *buffer)
{
(void)selected_item; (void)data; (void)buffer;
if (audio_status())
return ID2P(LANG_NOW_PLAYING);
return ID2P(LANG_RESUME_PLAYBACK);
}
MENUITEM_RETURNVALUE_DYNTEXT(wps_item, GO_TO_WPS, NULL, get_wps_item_name,
NULL, NULL, Icon_Playback_menu);
#ifdef HAVE_RECORDING
MENUITEM_RETURNVALUE(rec, ID2P(LANG_RECORDING), GO_TO_RECSCREEN,
NULL, Icon_Recording);
#endif
#if CONFIG_TUNER
MENUITEM_RETURNVALUE(fm, ID2P(LANG_FM_RADIO), GO_TO_FM,
item_callback, Icon_Radio_screen);
#endif
MENUITEM_RETURNVALUE(menu_, ID2P(LANG_SETTINGS), GO_TO_MAINMENU,
NULL, Icon_Submenu_Entered);
MENUITEM_RETURNVALUE(bookmarks, ID2P(LANG_BOOKMARK_MENU_RECENT_BOOKMARKS),
GO_TO_RECENTBMARKS, item_callback,
Icon_Bookmark);
#ifdef HAVE_LCD_CHARCELLS
static int do_shutdown(void)
{
#if CONFIG_CHARGING
if (charger_inserted())
charging_splash();
else
#endif
sys_poweroff();
return 0;
}
MENUITEM_FUNCTION(do_shutdown_item, 0, ID2P(LANG_SHUTDOWN),
do_shutdown, NULL, NULL, Icon_NOICON);
#endif
MAKE_MENU(root_menu_, ID2P(LANG_ROCKBOX_TITLE),
item_callback, Icon_Rockbox,
&bookmarks, &file_browser,
#ifdef HAVE_TAGCACHE
&db_browser,
#endif
&wps_item, &menu_,
#ifdef HAVE_RECORDING
&rec,
#endif
#if CONFIG_TUNER
&fm,
#endif
&playlist_options, &rocks_browser, &info_menu
#ifdef HAVE_LCD_CHARCELLS
,&do_shutdown_item
#endif
);
static int item_callback(int action, const struct menu_item_ex *this_item)
{
switch (action)
{
case ACTION_TREE_STOP:
return ACTION_REDRAW;
case ACTION_REQUEST_MENUITEM:
#if CONFIG_TUNER
if (this_item == &fm)
{
if (radio_hardware_present() == 0)
return ACTION_EXIT_MENUITEM;
}
else
#endif
if (this_item == &bookmarks)
{
if (global_settings.usemrb == 0)
return ACTION_EXIT_MENUITEM;
}
break;
}
return action;
}
static int get_selection(int last_screen)
{
int i;
int len = ARRAYLEN(root_menu__);
for(i=0; i < len; i++)
{
if (((root_menu__[i]->flags&MENU_TYPE_MASK) == MT_RETURN_VALUE) &&
(root_menu__[i]->value == last_screen))
{
return i;
}
}
return 0;
}
static inline int load_screen(int screen)
{
/* set the global_status.last_screen before entering,
if we dont we will always return to the wrong screen on boot */
int old_previous = last_screen;
int ret_val;
if (screen <= GO_TO_ROOT)
return screen;
if (screen == old_previous)
old_previous = GO_TO_ROOT;
global_status.last_screen = (char)screen;
status_save();
ret_val = items[screen].function(items[screen].param);
last_screen = screen;
if (ret_val == GO_TO_PREVIOUS)
last_screen = old_previous;
return ret_val;
}
static int load_context_screen(int selection)
{
const struct menu_item_ex *context_menu = NULL;
if ((root_menu__[selection]->flags&MENU_TYPE_MASK) == MT_RETURN_VALUE)
{
int item = root_menu__[selection]->value;
context_menu = items[item].context_menu;
}
/* special cases */
else if (root_menu__[selection] == &info_menu)
{
context_menu = &system_menu;
}
if (context_menu)
return do_menu(context_menu, NULL, NULL, false);
else
return GO_TO_PREVIOUS;
}
#ifdef HAVE_PICTUREFLOW_INTEGRATION
static int load_plugin_screen(char *plug_path)
{
int ret_val;
int old_previous = last_screen;
last_screen = next_screen;
global_status.last_screen = (char)next_screen;
status_save();
switch (plugin_load(plug_path, NULL))
{
case PLUGIN_GOTO_WPS:
ret_val = GO_TO_WPS;
break;
case PLUGIN_OK:
ret_val = audio_status() ? GO_TO_PREVIOUS : GO_TO_ROOT;
break;
default:
ret_val = GO_TO_PREVIOUS;
break;
}
if (ret_val == GO_TO_PREVIOUS)
last_screen = (old_previous == next_screen) ? GO_TO_ROOT : old_previous;
return ret_val;
}
#endif
static int previous_music = GO_TO_WPS;
void previous_music_is_wps(void)
{
previous_music = GO_TO_WPS;
}
int current_screen(void)
{
return next_screen;
}
void root_menu(void)
{
int previous_browser = GO_TO_FILEBROWSER;
int selected = 0;
if (global_settings.start_in_screen == 0)
next_screen = (int)global_status.last_screen;
else next_screen = global_settings.start_in_screen - 2;
add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, rootmenu_track_changed_callback);
#ifdef HAVE_RTC_ALARM
if ( rtc_check_alarm_started(true) )
{
rtc_enable_alarm(false);
next_screen = GO_TO_WPS;
#if CONFIG_TUNER
if (global_settings.alarm_wake_up_screen == ALARM_START_FM)
next_screen = GO_TO_FM;
#endif
#ifdef HAVE_RECORDING
if (global_settings.alarm_wake_up_screen == ALARM_START_REC)
{
recording_start_automatic = true;
next_screen = GO_TO_RECSCREEN;
}
#endif
}
#endif /* HAVE_RTC_ALARM */
#ifdef HAVE_HEADPHONE_DETECTION
if (next_screen == GO_TO_WPS &&
(global_settings.unplug_autoresume && !headphones_inserted() ))
next_screen = GO_TO_ROOT;
#endif
while (true)
{
switch (next_screen)
{
case MENU_ATTACHED_USB:
case MENU_SELECTED_EXIT:
/* fall through */
case GO_TO_ROOT:
if (last_screen != GO_TO_ROOT)
selected = get_selection(last_screen);
#if (CONFIG_PLATFORM&PLATFORM_ANDROID)
/* When we are in the main menu we want the hardware BACK
* button to be handled by Android instead of rockbox */
android_ignore_back_button(true);
#endif
next_screen = do_menu(&root_menu_, &selected, NULL, false);
#if (CONFIG_PLATFORM&PLATFORM_ANDROID)
android_ignore_back_button(false);
#endif
if (next_screen != GO_TO_PREVIOUS)
last_screen = GO_TO_ROOT;
break;
case GO_TO_PREVIOUS:
next_screen = last_screen;
break;
case GO_TO_PREVIOUS_BROWSER:
next_screen = previous_browser;
break;
case GO_TO_PREVIOUS_MUSIC:
next_screen = previous_music;
break;
case GO_TO_ROOTITEM_CONTEXT:
next_screen = load_context_screen(selected);
break;
#ifdef HAVE_PICTUREFLOW_INTEGRATION
case GO_TO_PICTUREFLOW:
while ( !tagcache_is_usable() )
{
splash(0, str(LANG_TAGCACHE_BUSY));
if ( action_userabort(HZ/5) )
break;
}
{
char pf_path[MAX_PATH];
snprintf(pf_path, sizeof(pf_path),
"%s/pictureflow.rock",
PLUGIN_DEMOS_DIR);
next_screen = load_plugin_screen(pf_path);
}
previous_browser = GO_TO_PICTUREFLOW;
break;
#endif
default:
if (next_screen == GO_TO_FILEBROWSER
#ifdef HAVE_TAGCACHE
|| next_screen == GO_TO_DBBROWSER
#endif
)
previous_browser = next_screen;
if (next_screen == GO_TO_WPS
#if CONFIG_TUNER
|| next_screen == GO_TO_FM
#endif
)
previous_music = next_screen;
next_screen = load_screen(next_screen);
break;
} /* switch() */
}
}