6c52fa139c
Prefer to use designated initializers to avoid having to specify unneeded parameters. Non-initialized members are zero-initialized by the compiler. Change-Id: Ia6a03c45cb3ef0b30f458d7d0ae1604a350c737c
1033 lines
33 KiB
C
1033 lines
33 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 "open_plugin.h"
|
|
#include "rolo.h"
|
|
#include "powermgmt.h"
|
|
#include "power.h"
|
|
#include "talk.h"
|
|
#include "audio.h"
|
|
#include "shortcuts.h"
|
|
|
|
#ifdef HAVE_HOTSWAP
|
|
#include "storage.h"
|
|
#include "mv.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 "playlist_catalog.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"
|
|
#include "disk.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 int previous_music = GO_TO_WPS; /* Toggles behavior of the return-to
|
|
* playback-button depending
|
|
* on FM radio */
|
|
|
|
#if (CONFIG_TUNER)
|
|
static void rootmenu_start_playback_callback(unsigned short id, void *param)
|
|
{
|
|
(void) id; (void) param;
|
|
/* Cancel FM radio selection as previous music. For cases where we start
|
|
playback without going to the WPS, such as playlist insert or
|
|
playlist catalog. */
|
|
previous_music = GO_TO_WPS;
|
|
}
|
|
#endif
|
|
|
|
static char current_track_path[MAX_PATH];
|
|
static void rootmenu_track_changed_callback(unsigned short id, void* param)
|
|
{
|
|
(void)id;
|
|
struct mp3entry *id3 = ((struct track_event *)param)->id3;
|
|
strmemccpy(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
|
|
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, last_ft_dirlevel = 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_MAX_LEN + 1];
|
|
if (!volume_removable(i))
|
|
continue;
|
|
get_volume_name(i, vol_string);
|
|
/* test whether we would browse the external card */
|
|
if (!volume_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 /*HAVE_HOTSWAP*/
|
|
strcpy(folder, last_folder);
|
|
}
|
|
push_current_activity(ACTIVITY_FILEBROWSER);
|
|
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, ID2P(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;
|
|
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())
|
|
{
|
|
splash_progress(stat->commit_step,
|
|
tagcache_get_max_commit_step(),
|
|
"[%d/%d] %s", stat->commit_step,
|
|
tagcache_get_max_commit_step(),
|
|
str(LANG_TAGCACHE_INIT));
|
|
}
|
|
else
|
|
{
|
|
splash_progress(stat->commit_step,
|
|
tagcache_get_max_commit_step(),
|
|
"%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;
|
|
last_ft_dirlevel = tc->dirlevel;
|
|
tc->dirlevel = last_db_dirlevel;
|
|
tc->selected_item = last_db_selection;
|
|
push_current_activity(ACTIVITY_DATABASEBROWSER);
|
|
break;
|
|
#endif /*HAVE_TAGCACHE*/
|
|
}
|
|
|
|
struct browse_context browse = {
|
|
.dirfilter = filter,
|
|
.icon = Icon_NOICON,
|
|
.root = folder,
|
|
};
|
|
|
|
ret_val = rockbox_browse(&browse);
|
|
|
|
if (ret_val == GO_TO_WPS
|
|
|| ret_val == GO_TO_PREVIOUS_MUSIC
|
|
|| ret_val == GO_TO_PLUGIN)
|
|
pop_current_activity_without_refresh();
|
|
else
|
|
pop_current_activity();
|
|
|
|
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;
|
|
tc->dirlevel = last_ft_dirlevel;
|
|
break;
|
|
#endif
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
#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;
|
|
push_current_activity(ACTIVITY_WPS);
|
|
if (audio_status())
|
|
{
|
|
talk_shutup();
|
|
ret_val = gui_wps_show();
|
|
}
|
|
else if ( global_status.resume_index != -1 )
|
|
{
|
|
DEBUGF("Resume index %X crc32 %lX offset %lX\n",
|
|
global_status.resume_index,
|
|
(unsigned long)global_status.resume_crc32,
|
|
(unsigned long)global_status.resume_offset);
|
|
if (playlist_resume() != -1)
|
|
{
|
|
playlist_resume_track(global_status.resume_index,
|
|
global_status.resume_crc32,
|
|
global_status.resume_elapsed,
|
|
global_status.resume_offset);
|
|
ret_val = gui_wps_show();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
|
|
}
|
|
|
|
if (ret_val == GO_TO_PLAYLIST_VIEWER
|
|
|| ret_val == GO_TO_PLUGIN
|
|
|| ret_val == GO_TO_WPS
|
|
|| ret_val == GO_TO_PREVIOUS_MUSIC
|
|
|| ret_val == GO_TO_PREVIOUS_BROWSER
|
|
|| (ret_val == GO_TO_PREVIOUS
|
|
&& (last_screen == GO_TO_MAINMENU /* Settings */
|
|
|| last_screen == GO_TO_BROWSEPLUGINS
|
|
|| last_screen == GO_TO_SYSTEM_SCREEN
|
|
|| last_screen == GO_TO_PLAYLISTS_SCREEN)))
|
|
{
|
|
pop_current_activity_without_refresh();
|
|
}
|
|
else
|
|
pop_current_activity();
|
|
|
|
return ret_val;
|
|
}
|
|
#if CONFIG_TUNER
|
|
static int radio(void* param)
|
|
{
|
|
(void)param;
|
|
radio_screen();
|
|
return GO_TO_ROOT;
|
|
}
|
|
#endif
|
|
|
|
static int miscscrn(void * param)
|
|
{
|
|
const struct menu_item_ex *menu = (const struct menu_item_ex*)param;
|
|
int result = do_menu(menu, NULL, NULL, false);
|
|
switch (result)
|
|
{
|
|
case GO_TO_PLUGIN:
|
|
case GO_TO_PLAYLIST_VIEWER:
|
|
case GO_TO_WPS:
|
|
case GO_TO_PREVIOUS_MUSIC:
|
|
return result;
|
|
default:
|
|
return GO_TO_ROOT;
|
|
}
|
|
}
|
|
|
|
|
|
static int playlist_view_catalog(void * param)
|
|
{
|
|
(void)param;
|
|
push_current_activity(ACTIVITY_PLAYLISTBROWSER);
|
|
bool item_was_selected = catalog_view_playlists();
|
|
|
|
if (item_was_selected)
|
|
{
|
|
pop_current_activity_without_refresh();
|
|
return GO_TO_WPS;
|
|
}
|
|
pop_current_activity();
|
|
return GO_TO_ROOT;
|
|
}
|
|
|
|
static int playlist_view(void * param)
|
|
{
|
|
(void)param;
|
|
int val;
|
|
|
|
val = playlist_viewer();
|
|
switch (val)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* 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
|
|
main_menu_,
|
|
manage_settings,
|
|
plugin_menu,
|
|
playlist_options,
|
|
info_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] = { miscscrn, (struct menu_item_ex*)&main_menu_,
|
|
&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] = { miscscrn, &plugin_menu, NULL },
|
|
[GO_TO_PLAYLISTS_SCREEN] = { playlist_view_catalog, NULL,
|
|
&playlist_options },
|
|
[GO_TO_PLAYLIST_VIEWER] = { playlist_view, NULL, &playlist_options },
|
|
[GO_TO_SYSTEM_SCREEN] = { miscscrn, &info_menu, &system_menu },
|
|
[GO_TO_SHORTCUTMENU] = { do_shortcut_menu, NULL, NULL },
|
|
|
|
};
|
|
static const int nb_items = sizeof(items)/sizeof(*items);
|
|
|
|
static int item_callback(int action,
|
|
const struct menu_item_ex *this_item,
|
|
struct gui_synclist *this_list);
|
|
|
|
MENUITEM_RETURNVALUE(shortcut_menu, ID2P(LANG_SHORTCUTS), GO_TO_SHORTCUTMENU,
|
|
NULL, Icon_Bookmark);
|
|
|
|
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);
|
|
|
|
MENUITEM_RETURNVALUE(playlist_browser, ID2P(LANG_CATALOG), GO_TO_PLAYLIST_VIEWER,
|
|
NULL, Icon_Playlist);
|
|
|
|
static char *get_wps_item_name(int selected_item, void * data,
|
|
char *buffer, size_t buffer_len)
|
|
{
|
|
(void)selected_item; (void)data; (void)buffer; (void)buffer_len;
|
|
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);
|
|
MENUITEM_RETURNVALUE(playlists, ID2P(LANG_CATALOG), GO_TO_PLAYLISTS_SCREEN,
|
|
NULL, Icon_Playlist);
|
|
MENUITEM_RETURNVALUE(system_menu_, ID2P(LANG_SYSTEM), GO_TO_SYSTEM_SCREEN,
|
|
NULL, Icon_System_menu);
|
|
|
|
struct menu_item_ex root_menu_;
|
|
static struct menu_callback_with_desc root_menu_desc = {
|
|
item_callback, ID2P(LANG_ROCKBOX_TITLE), Icon_Rockbox };
|
|
|
|
static struct menu_table menu_table[] = {
|
|
/* Order here represents the default ordering */
|
|
{ "bookmarks", &bookmarks },
|
|
{ "files", &file_browser },
|
|
#ifdef HAVE_TAGCACHE
|
|
{ "database", &db_browser },
|
|
#endif
|
|
{ "wps", &wps_item },
|
|
{ "settings", &menu_ },
|
|
#ifdef HAVE_RECORDING
|
|
{ "recording", &rec },
|
|
#endif
|
|
#if CONFIG_TUNER
|
|
{ "radio", &fm },
|
|
#endif
|
|
{ "playlists", &playlists },
|
|
{ "plugins", &rocks_browser },
|
|
{ "system_menu", &system_menu_ },
|
|
{ "shortcuts", &shortcut_menu },
|
|
};
|
|
#define MAX_MENU_ITEMS (sizeof(menu_table) / sizeof(struct menu_table))
|
|
static struct menu_item_ex *root_menu__[MAX_MENU_ITEMS];
|
|
|
|
struct menu_table *root_menu_get_options(int *nb_options)
|
|
{
|
|
*nb_options = MAX_MENU_ITEMS;
|
|
|
|
return menu_table;
|
|
}
|
|
|
|
void root_menu_load_from_cfg(void* setting, char *value)
|
|
{
|
|
char *next = value, *start, *end;
|
|
unsigned int menu_item_count = 0, i;
|
|
bool main_menu_added = false;
|
|
|
|
if (*value == '-')
|
|
{
|
|
root_menu_set_default(setting, NULL);
|
|
return;
|
|
}
|
|
root_menu_.flags = MENU_HAS_DESC | MT_MENU;
|
|
root_menu_.submenus = (const struct menu_item_ex **)&root_menu__;
|
|
root_menu_.callback_and_desc = &root_menu_desc;
|
|
|
|
while (next && menu_item_count < MAX_MENU_ITEMS)
|
|
{
|
|
start = next;
|
|
next = strchr(next, ',');
|
|
if (next)
|
|
{
|
|
*next = '\0';
|
|
next++;
|
|
}
|
|
start = skip_whitespace(start);
|
|
if ((end = strchr(start, ' ')))
|
|
*end = '\0';
|
|
for (i=0; i<MAX_MENU_ITEMS; i++)
|
|
{
|
|
if (*start && !strcmp(start, menu_table[i].string))
|
|
{
|
|
root_menu__[menu_item_count++] = (struct menu_item_ex *)menu_table[i].item;
|
|
if (menu_table[i].item == &menu_)
|
|
main_menu_added = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!main_menu_added)
|
|
root_menu__[menu_item_count++] = (struct menu_item_ex *)&menu_;
|
|
root_menu_.flags |= MENU_ITEM_COUNT(menu_item_count);
|
|
*(bool*)setting = true;
|
|
}
|
|
|
|
char* root_menu_write_to_cfg(void* setting, char*buf, int buf_len)
|
|
{
|
|
(void)setting;
|
|
unsigned i, written, j;
|
|
for (i = 0; i < MENU_GET_COUNT(root_menu_.flags); i++)
|
|
{
|
|
for (j=0; j<MAX_MENU_ITEMS; j++)
|
|
{
|
|
if (menu_table[j].item == root_menu__[i])
|
|
{
|
|
written = snprintf(buf, buf_len, "%s, ", menu_table[j].string);
|
|
buf_len -= written;
|
|
buf += written;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
void root_menu_set_default(void* setting, void* defaultval)
|
|
{
|
|
unsigned i;
|
|
(void)defaultval;
|
|
|
|
root_menu_.flags = MENU_HAS_DESC | MT_MENU;
|
|
root_menu_.submenus = (const struct menu_item_ex **)&root_menu__;
|
|
root_menu_.callback_and_desc = &root_menu_desc;
|
|
|
|
for (i=0; i<MAX_MENU_ITEMS; i++)
|
|
{
|
|
root_menu__[i] = (struct menu_item_ex *)menu_table[i].item;
|
|
}
|
|
root_menu_.flags |= MENU_ITEM_COUNT(MAX_MENU_ITEMS);
|
|
*(bool*)setting = false;
|
|
}
|
|
|
|
bool root_menu_is_changed(void* setting, void* defaultval)
|
|
{
|
|
(void)defaultval;
|
|
return *(bool*)setting;
|
|
}
|
|
|
|
static int item_callback(int action,
|
|
const struct menu_item_ex *this_item,
|
|
struct gui_synclist *this_list)
|
|
{
|
|
(void)this_list;
|
|
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;
|
|
enum current_activity activity = ACTIVITY_UNKNOWN;
|
|
if (screen <= GO_TO_ROOT)
|
|
return screen;
|
|
if (screen == old_previous)
|
|
old_previous = GO_TO_ROOT;
|
|
global_status.last_screen = (char)screen;
|
|
status_save();
|
|
|
|
if (screen == GO_TO_BROWSEPLUGINS)
|
|
activity = ACTIVITY_PLUGINBROWSER;
|
|
else if (screen == GO_TO_MAINMENU)
|
|
activity = ACTIVITY_SETTINGS;
|
|
else if (screen == GO_TO_SYSTEM_SCREEN)
|
|
activity = ACTIVITY_SYSTEMSCREEN;
|
|
|
|
if (activity != ACTIVITY_UNKNOWN)
|
|
push_current_activity(activity);
|
|
|
|
ret_val = items[screen].function(items[screen].param);
|
|
|
|
if (activity != ACTIVITY_UNKNOWN)
|
|
{
|
|
if (ret_val == GO_TO_PLUGIN
|
|
|| ret_val == GO_TO_WPS
|
|
|| ret_val == GO_TO_PREVIOUS_MUSIC
|
|
|| ret_val == GO_TO_PREVIOUS_BROWSER
|
|
|| ret_val == GO_TO_FILEBROWSER)
|
|
{
|
|
pop_current_activity_without_refresh();
|
|
}
|
|
else
|
|
pop_current_activity();
|
|
}
|
|
|
|
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;
|
|
int retval = GO_TO_PREVIOUS;
|
|
push_current_activity(ACTIVITY_CONTEXTMENU);
|
|
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)
|
|
retval = do_menu(context_menu, NULL, NULL, false);
|
|
pop_current_activity();
|
|
return retval;
|
|
}
|
|
|
|
static int load_plugin_screen(char *key)
|
|
{
|
|
int ret_val = PLUGIN_ERROR;
|
|
int loops = 100;
|
|
int old_previous = last_screen;
|
|
int old_global = global_status.last_screen;
|
|
last_screen = next_screen;
|
|
global_status.last_screen = (char)next_screen;
|
|
/*status_save(); //only needed if we crash */
|
|
|
|
while(loops-- > 0) /* just to keep things from getting out of hand */
|
|
{
|
|
|
|
int opret = open_plugin_load_entry(key);
|
|
struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
|
|
char *path = op_entry->path;
|
|
char *param = op_entry->param;
|
|
if (param[0] == '\0')
|
|
param = NULL;
|
|
if (path[0] == '\0' && key)
|
|
path = P2STR((unsigned char *)key);
|
|
|
|
int ret = plugin_load(path, param);
|
|
|
|
if (ret == PLUGIN_USB_CONNECTED || ret == PLUGIN_ERROR)
|
|
ret_val = GO_TO_ROOT;
|
|
else if (ret == PLUGIN_GOTO_WPS)
|
|
ret_val = GO_TO_WPS;
|
|
else if (ret == PLUGIN_GOTO_PLUGIN)
|
|
continue;
|
|
else
|
|
{
|
|
ret_val = GO_TO_PREVIOUS;
|
|
/* Prevents infinite loop with WPS, Plugins, Previous Screen*/
|
|
if (ret == PLUGIN_OK && old_global == GO_TO_WPS && !audio_status())
|
|
ret_val = GO_TO_ROOT;
|
|
last_screen = (old_previous == next_screen || old_global == GO_TO_ROOT)
|
|
? GO_TO_ROOT : old_previous;
|
|
if (last_screen == GO_TO_ROOT)
|
|
global_status.last_screen = GO_TO_ROOT;
|
|
}
|
|
/* ret_val != GO_TO_PLUGIN */
|
|
|
|
if (opret != OPEN_PLUGIN_NEEDS_FLUSHED || last_screen != GO_TO_WPS)
|
|
{
|
|
/* Keep the entry in case of GO_TO_PREVIOUS */
|
|
op_entry->hash = 0; /*remove hash -- prevents flush to disk */
|
|
op_entry->lang_id = LANG_PREVIOUS_SCREEN;
|
|
/*open_plugin_add_path(NULL, NULL, NULL);// clear entry */
|
|
}
|
|
break;
|
|
} /*while */
|
|
return ret_val;
|
|
}
|
|
|
|
static void ignore_back_button_stub(bool ignore)
|
|
{
|
|
#if (CONFIG_PLATFORM&PLATFORM_ANDROID)
|
|
/* BACK button to be handled by Android instead of rockbox */
|
|
android_ignore_back_button(ignore);
|
|
#else
|
|
(void) ignore;
|
|
#endif
|
|
}
|
|
|
|
static int root_menu_setup_screens(void)
|
|
{
|
|
int new_screen = next_screen;
|
|
if (global_settings.start_in_screen == 0)
|
|
new_screen = (int)global_status.last_screen;
|
|
else new_screen = global_settings.start_in_screen - 2;
|
|
if (new_screen == GO_TO_PLUGIN)
|
|
{
|
|
if (global_status.last_screen == GO_TO_SHORTCUTMENU)
|
|
{
|
|
/* Can make this any value other than GO_TO_SHORTCUTMENU
|
|
otherwise it takes over on startup when the user wanted
|
|
the plugin at key - LANG_START_SCREEN */
|
|
global_status.last_screen = GO_TO_PLUGIN;
|
|
}
|
|
if(global_status.last_screen == GO_TO_SHORTCUTMENU ||
|
|
global_status.last_screen == GO_TO_PLUGIN)
|
|
{
|
|
if (global_settings.start_in_screen == 0)
|
|
{ /* Start in: Previous Screen */
|
|
last_screen = GO_TO_PREVIOUS;
|
|
global_status.last_screen = GO_TO_ROOT;
|
|
/* since the plugin has GO_TO_PLUGIN as origin it
|
|
will just return GO_TO_PREVIOUS <=> GO_TO_PLUGIN in a loop
|
|
To allow exit after restart we check for GO_TO_ROOT
|
|
if so exit to ROOT after the plugin exits */
|
|
}
|
|
}
|
|
}
|
|
#if CONFIG_TUNER
|
|
add_event(PLAYBACK_EVENT_START_PLAYBACK, rootmenu_start_playback_callback);
|
|
#endif
|
|
add_event(PLAYBACK_EVENT_TRACK_CHANGE, rootmenu_track_changed_callback);
|
|
#ifdef HAVE_RTC_ALARM
|
|
int alarm_wake_up_screen = 0;
|
|
if ( rtc_check_alarm_started(true) )
|
|
{
|
|
rtc_enable_alarm(false);
|
|
|
|
#if (defined(HAVE_RECORDING) || CONFIG_TUNER)
|
|
alarm_wake_up_screen = global_settings.alarm_wake_up_screen;
|
|
#endif
|
|
switch (alarm_wake_up_screen)
|
|
{
|
|
#if CONFIG_TUNER
|
|
case ALARM_START_FM:
|
|
new_screen = GO_TO_FM;
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_RECORDING
|
|
case ALARM_START_REC:
|
|
recording_start_automatic = true;
|
|
new_screen = GO_TO_RECSCREEN;
|
|
break;
|
|
#endif
|
|
default:
|
|
new_screen = GO_TO_WPS;
|
|
break;
|
|
} /* switch() */
|
|
}
|
|
#endif /* HAVE_RTC_ALARM */
|
|
|
|
#if defined(HAVE_HEADPHONE_DETECTION) || defined(HAVE_LINEOUT_DETECTION)
|
|
if (new_screen == GO_TO_WPS && global_settings.unplug_autoresume)
|
|
{
|
|
new_screen = GO_TO_ROOT;
|
|
#ifdef HAVE_HEADPHONE_DETECTION
|
|
if (headphones_inserted())
|
|
new_screen = GO_TO_WPS;
|
|
#endif
|
|
#ifdef HAVE_LINEOUT_DETECTION
|
|
if (lineout_inserted())
|
|
new_screen = GO_TO_WPS;
|
|
#endif
|
|
}
|
|
#endif /*(HAVE_HEADPHONE_DETECTION) || (HAVE_LINEOUT_DETECTION)*/
|
|
return new_screen;
|
|
}
|
|
|
|
void root_menu(void)
|
|
{
|
|
int previous_browser = GO_TO_FILEBROWSER;
|
|
int selected = 0;
|
|
int shortcut_origin = GO_TO_ROOT;
|
|
|
|
push_current_activity(ACTIVITY_MAINMENU);
|
|
next_screen = root_menu_setup_screens();
|
|
|
|
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);
|
|
global_status.last_screen = GO_TO_ROOT; /* We've returned to ROOT */
|
|
/* When we are in the main menu we want the hardware BACK
|
|
* button to be handled by HOST instead of rockbox */
|
|
ignore_back_button_stub(true);
|
|
|
|
next_screen = do_menu(&root_menu_, &selected, NULL, false);
|
|
|
|
ignore_back_button_stub(false);
|
|
|
|
if (next_screen != GO_TO_PREVIOUS)
|
|
last_screen = GO_TO_ROOT;
|
|
break;
|
|
#ifdef HAVE_TAGCACHE
|
|
case GO_TO_FILEBROWSER:
|
|
case GO_TO_DBBROWSER:
|
|
previous_browser = next_screen;
|
|
goto load_next_screen;
|
|
break;
|
|
#endif /* With !HAVE_TAGCACHE previous_browser is always GO_TO_FILEBROWSER */
|
|
#if CONFIG_TUNER
|
|
case GO_TO_WPS:
|
|
case GO_TO_FM:
|
|
previous_music = next_screen;
|
|
goto load_next_screen;
|
|
break;
|
|
#endif /* With !CONFIG_TUNER previous_music is always GO_TO_WPS */
|
|
|
|
case GO_TO_PREVIOUS:
|
|
{
|
|
next_screen = last_screen;
|
|
if (last_screen == GO_TO_PLUGIN)/* for WPS */
|
|
last_screen = GO_TO_PREVIOUS;
|
|
else if (last_screen == GO_TO_PREVIOUS)
|
|
next_screen = GO_TO_ROOT;
|
|
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;
|
|
case GO_TO_PLUGIN:
|
|
{
|
|
|
|
char *key;
|
|
if (global_status.last_screen == GO_TO_SHORTCUTMENU)
|
|
{
|
|
struct open_plugin_entry_t *op_entry = open_plugin_get_entry();
|
|
if (op_entry->lang_id == LANG_OPEN_PLUGIN)
|
|
op_entry->lang_id = LANG_SHORTCUTS;
|
|
shortcut_origin = last_screen;
|
|
key = ID2P(LANG_SHORTCUTS);
|
|
}
|
|
else
|
|
{
|
|
switch (last_screen)
|
|
{
|
|
case GO_TO_ROOT:
|
|
key = ID2P(LANG_START_SCREEN);
|
|
break;
|
|
case GO_TO_WPS:
|
|
key = ID2P(LANG_OPEN_PLUGIN_SET_WPS_CONTEXT_PLUGIN);
|
|
break;
|
|
case GO_TO_SHORTCUTMENU:
|
|
key = ID2P(LANG_SHORTCUTS);
|
|
break;
|
|
case GO_TO_PREVIOUS:
|
|
key = ID2P(LANG_PREVIOUS_SCREEN);
|
|
break;
|
|
default:
|
|
key = ID2P(LANG_OPEN_PLUGIN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
push_activity_without_refresh(ACTIVITY_UNKNOWN); /* prevent plugin_load */
|
|
next_screen = load_plugin_screen(key); /* from flashing root */
|
|
pop_current_activity_without_refresh(); /* menu activity */
|
|
|
|
if (next_screen == GO_TO_PREVIOUS)
|
|
{
|
|
/* shortcuts may take several trips through the GO_TO_PLUGIN
|
|
case make sure we preserve and restore the origin */
|
|
if (shortcut_origin != GO_TO_ROOT)
|
|
{
|
|
if (shortcut_origin != GO_TO_WPS)
|
|
next_screen = shortcut_origin;
|
|
shortcut_origin = GO_TO_ROOT;
|
|
}
|
|
/* skip GO_TO_PREVIOUS */
|
|
if (last_screen == GO_TO_BROWSEPLUGINS)
|
|
{
|
|
next_screen = last_screen;
|
|
last_screen = GO_TO_PLUGIN;
|
|
}
|
|
}
|
|
previous_browser = (next_screen != GO_TO_WPS) ? GO_TO_FILEBROWSER : GO_TO_PLUGIN;
|
|
break;
|
|
}
|
|
default:
|
|
goto load_next_screen;
|
|
break;
|
|
} /* switch() */
|
|
continue;
|
|
load_next_screen: /* load_screen is inlined */
|
|
next_screen = load_screen(next_screen);
|
|
}
|
|
|
|
}
|