summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Gordon <rockbox@jdgordon.info>2011-11-15 13:22:02 +0000
committerJonathan Gordon <rockbox@jdgordon.info>2011-11-15 13:22:02 +0000
commit101693fd3047fb64e766580e80635a424fa25c4d (patch)
tree80f5664710a6e84b73f33e22c1b8632c13c5a727
parente7e4b131d06f748400b5299d4d1ebfb38f9f08bf (diff)
downloadrockbox-101693fd3047fb64e766580e80635a424fa25c4d.zip
rockbox-101693fd3047fb64e766580e80635a424fa25c4d.tar.gz
rockbox-101693fd3047fb64e766580e80635a424fa25c4d.tar.bz2
rockbox-101693fd3047fb64e766580e80635a424fa25c4d.tar.xz
FS#12251 - User shortcuts in the main menu.
Custom shortcuts which give the user fast access to regularly used files/folders/settings/whatever. Thanks to Alexander Levin for the manual part of the patch git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30990 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/SOURCES1
-rw-r--r--apps/debug_menu.c31
-rw-r--r--apps/debug_menu.h1
-rw-r--r--apps/lang/english.lang14
-rw-r--r--apps/main.c3
-rw-r--r--apps/menu.c38
-rw-r--r--apps/menu.h3
-rw-r--r--apps/misc.h3
-rw-r--r--apps/onplay.c19
-rw-r--r--apps/onplay.h2
-rw-r--r--apps/root_menu.c6
-rw-r--r--apps/root_menu.h1
-rw-r--r--apps/shortcuts.c421
-rw-r--r--apps/shortcuts.h43
-rw-r--r--apps/tree.c5
-rw-r--r--apps/tree.h2
-rw-r--r--manual/main_menu/main.tex57
17 files changed, 628 insertions, 22 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 0734d34..7ef81a1 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -52,6 +52,7 @@ root_menu.c
screens.c
settings.c
settings_list.c
+shortcuts.c
status.c
cuesheet.c
talk.c
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 442bc8b..809644b 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include <string.h>
#include "lcd.h"
+#include "lang.h"
#include "menu.h"
#include "debug_menu.h"
#include "kernel.h"
@@ -45,6 +46,7 @@
#include "screens.h"
#include "misc.h"
#include "splash.h"
+#include "shortcuts.h"
#include "dircache.h"
#include "viewport.h"
#ifdef HAVE_TAGCACHE
@@ -2120,15 +2122,23 @@ static const struct the_menu_item menuitems[] = {
};
static int menu_action_callback(int btn, struct gui_synclist *lists)
{
+ int selection = gui_synclist_get_sel_pos(lists);
if (btn == ACTION_STD_OK)
{
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, false, NULL);
- menuitems[gui_synclist_get_sel_pos(lists)].function();
+ menuitems[selection].function();
btn = ACTION_REDRAW;
FOR_NB_SCREENS(i)
viewportmanager_theme_undo(i, false);
}
+ else if (btn == ACTION_STD_CONTEXT)
+ {
+ MENUITEM_STRINGLIST(menu_items, "Debug Menu", NULL, ID2P(LANG_ADD_TO_FAVES));
+ if (do_menu(&menu_items, NULL, NULL, false) == 0)
+ shortcuts_add(SHORTCUT_DEBUGITEM, menuitems[selection].desc);
+ return ACTION_STD_CANCEL;
+ }
return btn;
}
@@ -2148,3 +2158,22 @@ bool debug_menu(void)
info.get_name = dbg_menu_getname;
return simplelist_show_list(&info);
}
+
+bool run_debug_screen(char* screen)
+{
+ unsigned i;
+ for (i=0; i<ARRAYLEN(menuitems); i++)
+ {
+ if (!strcmp(screen, menuitems[i].desc))
+ {
+ FOR_NB_SCREENS(j)
+ viewportmanager_theme_enable(j, false, NULL);
+ menuitems[i].function();
+ FOR_NB_SCREENS(j)
+ viewportmanager_theme_undo(j, false);
+ return true;
+ }
+ }
+ return false;
+}
+
diff --git a/apps/debug_menu.h b/apps/debug_menu.h
index ecb50c4..17a9c2e 100644
--- a/apps/debug_menu.h
+++ b/apps/debug_menu.h
@@ -22,6 +22,7 @@
#define _DEBUG_MENU_H
bool debug_menu(void);
+bool run_debug_screen(char* screen);
#ifndef SIMULATOR
extern bool dbg_ports(void);
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 5a59473..741f120 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12903,3 +12903,17 @@
*: "Cancel Sleep Timer"
</voice>
</phrase>
+<phrase>
+ id: LANG_SHORTCUTS
+ desc: Title in the shortcuts menu
+ user: core
+ <source>
+ *: "Shortcuts"
+ </source>
+ <dest>
+ *: "Shortcuts"
+ </dest>
+ <voice>
+ *: "Shortcuts"
+ </voice>
+</phrase>
diff --git a/apps/main.c b/apps/main.c
index c2dfc1a..38de780 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -79,6 +79,7 @@
#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
#include "notification.h"
#endif
+#include "shortcuts.h"
#ifdef IPOD_ACCESSORY_PROTOCOL
#include "iap.h"
@@ -387,6 +388,7 @@ static void init(void)
filetype_init();
playlist_init();
theme_init_buffer();
+ shortcuts_init();
#if CONFIG_CODEC != SWCODEC
mp3_init( global_settings.volume,
@@ -667,6 +669,7 @@ static void init(void)
filetype_init();
scrobbler_init();
theme_init_buffer();
+ shortcuts_init();
#if CONFIG_CODEC != SWCODEC
/* No buffer allocation (see buffer.c) may take place after the call to
diff --git a/apps/menu.c b/apps/menu.c
index b4be83f..a1e32f4 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -52,6 +52,7 @@
#include "audio.h"
#include "viewport.h"
#include "quickscreen.h"
+#include "shortcuts.h"
#ifdef HAVE_LCD_BITMAP
#include "icons.h"
@@ -280,19 +281,10 @@ static int talk_menu_item(int selected_item, void *data)
return 0;
}
-void do_setting_from_menu(const struct menu_item_ex *temp,
- struct viewport parent[NB_SCREENS])
+void do_setting_screen(const struct settings_list *setting, const char * title,
+ struct viewport parent[NB_SCREENS])
{
- int setting_id;
- const struct settings_list *setting =
- find_setting(temp->variable, &setting_id);
- char *title;
char padded_title[MAX_PATH];
- if ((temp->flags&MENU_TYPE_MASK) == MT_SETTING_W_TEXT)
- title = temp->callback_and_desc->desc;
- else
- title = ID2P(setting->lang_id);
-
/* Pad the title string by repeating it. This is needed
so the scroll settings title can actually be used to
test the setting */
@@ -317,7 +309,22 @@ void do_setting_from_menu(const struct menu_item_ex *temp,
}
option_screen((struct settings_list *)setting, parent,
- setting->flags&F_TEMPVAR, title);
+ setting->flags&F_TEMPVAR, (char*)title);
+}
+
+
+void do_setting_from_menu(const struct menu_item_ex *temp,
+ struct viewport parent[NB_SCREENS])
+{
+ char *title;
+ int setting_id;
+ const struct settings_list *setting =
+ find_setting(temp->variable, &setting_id);
+ if (temp && ((temp->flags&MENU_TYPE_MASK) == MT_SETTING_W_TEXT))
+ title = temp->callback_and_desc->desc;
+ else
+ title = ID2P(setting->lang_id);
+ do_setting_screen(setting, title, parent);
}
/* display a menu */
@@ -451,7 +458,8 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
ID2P(LANG_TOP_QS_ITEM),
ID2P(LANG_LEFT_QS_ITEM),
ID2P(LANG_BOTTOM_QS_ITEM),
- ID2P(LANG_RIGHT_QS_ITEM));
+ ID2P(LANG_RIGHT_QS_ITEM),
+ ID2P(LANG_ADD_TO_FAVES));
#endif
MENUITEM_STRINGLIST(notquickscreen_able_option,
ID2P(LANG_ONPLAY_MENU_TITLE), NULL,
@@ -486,6 +494,10 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
case 4: /* set as right QS item */
set_as_qs_item(setting, QUICKSCREEN_RIGHT);
break;
+ case 5: /* Add to faves. Same limitation on which can be
+ added to the shortcuts menu as the quickscreen */
+ shortcuts_add(SHORTCUT_SETTING, (void*)setting);
+ break;
#endif
} /* swicth(do_menu()) */
redraw_lists = true;
diff --git a/apps/menu.h b/apps/menu.h
index 2251de2..ee2d9e7 100644
--- a/apps/menu.h
+++ b/apps/menu.h
@@ -26,6 +26,7 @@
#include "icon.h"
#include "icons.h"
#include "root_menu.h" /* needed for MENU_* return codes */
+#include "settings_list.h"
enum menu_item_type {
@@ -103,6 +104,8 @@ typedef int (*menu_callback_type)(int action,
const struct menu_item_ex *this_item);
void do_setting_from_menu(const struct menu_item_ex *temp,
struct viewport parent[NB_SCREENS]);
+void do_setting_screen(const struct settings_list *setting, const char * title,
+ struct viewport parent[NB_SCREENS]);
/*
int do_menu(const struct menu_item_ex *menu, int *start_selected)
diff --git a/apps/misc.h b/apps/misc.h
index bfe6e5b..4647898 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -119,7 +119,8 @@ enum current_activity {
ACTIVITY_CONTEXTMENU,
ACTIVITY_SYSTEMSCREEN,
ACTIVITY_TIMEDATESCREEN,
- ACTIVITY_BOOKMARKSLIST
+ ACTIVITY_BOOKMARKSLIST,
+ ACTIVITY_SHORTCUTSMENU
};
#if CONFIG_CODEC == SWCODEC
diff --git a/apps/onplay.c b/apps/onplay.c
index aab023c..629de93 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -63,6 +63,7 @@
#include "pitchscreen.h"
#include "viewport.h"
#include "filefuncs.h"
+#include "shortcuts.h"
static int context;
static char* selected_file = NULL;
@@ -382,10 +383,13 @@ static int treeplaylist_callback(int action,
return action;
}
-void onplay_show_playlist_menu(char* track_name)
+void onplay_show_playlist_menu(char* path)
{
- selected_file = track_name;
- selected_file_attr = FILE_ATTR_AUDIO;
+ selected_file = path;
+ if (dir_exists(path))
+ selected_file_attr = ATTR_DIRECTORY;
+ else
+ selected_file_attr = filetype_get_attr(path);
do_menu(&tree_playlist_menu, NULL, NULL, false);
}
@@ -1032,8 +1036,13 @@ MENUITEM_FUNCTION(list_viewers_item, 0, ID2P(LANG_ONPLAY_OPEN_WITH),
MENUITEM_FUNCTION(properties_item, MENU_FUNC_USEPARAM, ID2P(LANG_PROPERTIES),
onplay_load_plugin, (void *)"properties",
clipboard_callback, Icon_NOICON);
-MENUITEM_FUNCTION(add_to_faves_item, MENU_FUNC_USEPARAM, ID2P(LANG_ADD_TO_FAVES),
- onplay_load_plugin, (void *)"shortcuts_append",
+static bool onplay_add_to_shortcuts(void)
+{
+ shortcuts_add(SHORTCUT_BROWSER, selected_file);
+ return false;
+}
+MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES),
+ onplay_add_to_shortcuts, NULL,
clipboard_callback, Icon_NOICON);
#if LCD_DEPTH > 1
diff --git a/apps/onplay.h b/apps/onplay.h
index 921303c..d24bf56 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -50,6 +50,6 @@ enum hotkey_action {
/* needed for the playlist viewer.. eventually clean this up */
void onplay_show_playlist_cat_menu(char* track_name);
-void onplay_show_playlist_menu(char* track_name);
+void onplay_show_playlist_menu(char* path);
#endif
diff --git a/apps/root_menu.c b/apps/root_menu.c
index 1e9924f..e78c027 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -37,6 +37,7 @@
#include "power.h"
#include "talk.h"
#include "audio.h"
+#include "shortcuts.h"
#ifdef HAVE_HOTSWAP
#include "storage.h"
@@ -415,12 +416,16 @@ static const struct root_items items[] = {
&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) ;
+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
@@ -492,6 +497,7 @@ MAKE_MENU(root_menu_, ID2P(LANG_ROCKBOX_TITLE),
#if CONFIG_KEYPAD == PLAYER_PAD
,&do_shutdown_item
#endif
+ ,&shortcut_menu
);
static int item_callback(int action, const struct menu_item_ex *this_item)
diff --git a/apps/root_menu.h b/apps/root_menu.h
index 2ffdced..8d11d9b 100644
--- a/apps/root_menu.h
+++ b/apps/root_menu.h
@@ -58,6 +58,7 @@ enum {
GO_TO_PLAYLISTS_SCREEN,
GO_TO_PLAYLIST_VIEWER,
GO_TO_SYSTEM_SCREEN,
+ GO_TO_SHORTCUTMENU
};
extern const struct menu_item_ex root_menu_;
diff --git a/apps/shortcuts.c b/apps/shortcuts.c
new file mode 100644
index 0000000..3992068
--- /dev/null
+++ b/apps/shortcuts.c
@@ -0,0 +1,421 @@
+/***************************************************************************
+ *
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2011 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 <stdbool.h>
+#include <stdlib.h>
+#include "config.h"
+#include "system.h"
+#include "action.h"
+#include "ata_idle_notify.h"
+#include "debug_menu.h"
+#include "core_alloc.h"
+#include "list.h"
+#include "settings.h"
+#include "settings_list.h"
+#include "lang.h"
+#include "menu.h"
+#include "misc.h"
+#include "tree.h"
+#include "splash.h"
+#include "filefuncs.h"
+#include "filetypes.h"
+#include "shortcuts.h"
+#include "onplay.h"
+
+
+
+#define MAX_SHORTCUT_NAME 32
+#define SHORTCUTS_FILENAME ROCKBOX_DIR "/shortcuts.txt"
+char *type_strings[SHORTCUT_TYPE_COUNT] = {
+ [SHORTCUT_SETTING] = "setting",
+ [SHORTCUT_FILE] = "file",
+ [SHORTCUT_DEBUGITEM] = "debug",
+ [SHORTCUT_BROWSER] = "browse",
+ [SHORTCUT_PLAYLISTMENU] = "playlist menu",
+ [SHORTCUT_SEPARATOR] = "separator",
+};
+
+
+struct shortcut {
+ enum shortcut_type type;
+ char name[MAX_SHORTCUT_NAME];
+ int icon;
+ union {
+ char path[MAX_PATH];
+ const struct settings_list *setting;
+ } u;
+};
+#define SHORTCUTS_PER_HANDLE 32
+struct shortcut_handle {
+ struct shortcut shortcuts[SHORTCUTS_PER_HANDLE];
+ int next_handle;
+};
+static int first_handle = 0;
+static int shortcut_count = 0;
+
+static void reset_shortcuts(void)
+{
+ int current_handle = first_handle;
+ struct shortcut_handle *h = NULL;
+ while (current_handle > 0)
+ {
+ int next;
+ h = core_get_data(current_handle);
+ next = h->next_handle;
+ core_free(current_handle);
+ current_handle = next;
+ }
+ first_handle = 0;
+ shortcut_count = 0;
+}
+
+static struct shortcut* get_shortcut(int index)
+{
+ int handle_count, handle_index;
+ int current_handle = first_handle;
+ struct shortcut_handle *h = NULL;
+
+ if (first_handle == 0)
+ {
+ first_handle = core_alloc("shortcuts_head", sizeof(struct shortcut_handle));
+ if (first_handle <= 0)
+ return NULL;
+ h = core_get_data(first_handle);
+ h->next_handle = 0;
+ current_handle = first_handle;
+ }
+
+ handle_count = index/SHORTCUTS_PER_HANDLE + 1;
+ handle_index = index%SHORTCUTS_PER_HANDLE;
+ do {
+ h = core_get_data(current_handle);
+ current_handle = h->next_handle;
+ handle_count--;
+ } while (handle_count > 0 && current_handle > 0);
+ if (handle_count > 0 && handle_index == 0)
+ {
+ char buf[32];
+ snprintf(buf, sizeof buf, "shortcuts_%d", index/SHORTCUTS_PER_HANDLE);
+ h->next_handle = core_alloc(buf, sizeof(struct shortcut_handle));
+ if (h->next_handle <= 0)
+ return NULL;
+ h = core_get_data(h->next_handle);
+ h->next_handle = 0;
+ }
+ return &h->shortcuts[handle_index];
+}
+
+bool verify_shortcut(struct shortcut* sc)
+{
+ switch (sc->type)
+ {
+ case SHORTCUT_UNDEFINED:
+ return false;
+ case SHORTCUT_BROWSER:
+ case SHORTCUT_FILE:
+ case SHORTCUT_PLAYLISTMENU:
+ if (sc->u.path[0] == '\0')
+ return false;
+ break;
+ case SHORTCUT_SETTING:
+ return sc->u.setting != NULL;
+ case SHORTCUT_DEBUGITEM:
+ case SHORTCUT_SEPARATOR:
+ default:
+ break;
+ }
+ return true;
+}
+
+static void init_shortcut(struct shortcut* sc)
+{
+ sc->type = SHORTCUT_UNDEFINED;
+ sc->name[0] = '\0';
+ sc->u.path[0] = '\0';
+ sc->icon = Icon_NOICON;
+}
+static int first_idx_to_writeback = -1;
+void shortcuts_ata_idle_callback(void* data)
+{
+ (void)data;
+ int fd;
+ char buf[MAX_PATH];
+ int current_idx = first_idx_to_writeback;
+ if (first_idx_to_writeback < 0)
+ return;
+ fd = open(SHORTCUTS_FILENAME, O_APPEND|O_RDWR|O_CREAT, 0644);
+ if (fd < 0)
+ return;
+ while (current_idx < shortcut_count)
+ {
+ struct shortcut* sc = get_shortcut(current_idx++);
+ char *type;
+ int len;
+ if (!sc)
+ break;
+ type = type_strings[sc->type];
+ len = snprintf(buf, MAX_PATH, "[shortcut]\ntype: %s\ndata: ", type);
+ write(fd, buf, len);
+ if (sc->type == SHORTCUT_SETTING)
+ write(fd, sc->u.setting->cfg_name, strlen(sc->u.setting->cfg_name));
+ else
+ write(fd, sc->u.path, strlen(sc->u.path));
+ write(fd, "\n\n", 2);
+ }
+ close(fd);
+ if (first_idx_to_writeback == 0)
+ {
+ /* reload all shortcuts because we appended to the shortcuts file which
+ * has not been read yet.
+ */
+ reset_shortcuts();
+ shortcuts_init();
+ }
+ first_idx_to_writeback = -1;
+}
+void shortcuts_add(enum shortcut_type type, char* value)
+{
+ struct shortcut* sc = get_shortcut(shortcut_count++);
+ if (!sc)
+ return;
+ init_shortcut(sc);
+ sc->type = type;
+ if (type == SHORTCUT_SETTING)
+ sc->u.setting = (void*)value;
+ else
+ strlcpy(sc->u.path, value, MAX_PATH);
+ if (first_idx_to_writeback < 0)
+ first_idx_to_writeback = shortcut_count - 1;
+ register_storage_idle_func(shortcuts_ata_idle_callback);
+}
+
+
+int readline_cb(int n, char *buf, void *parameters)
+{
+ (void)n;
+ (void)parameters;
+ struct shortcut **param = (struct shortcut**)parameters;
+ struct shortcut* sc = *param;
+ char *name, *value;
+
+ if (!strcasecmp(skip_whitespace(buf), "[shortcut]"))
+ {
+ if (sc && verify_shortcut(sc))
+ shortcut_count++;
+ sc = get_shortcut(shortcut_count);
+ if (!sc)
+ return 1;
+ init_shortcut(sc);
+ *param = sc;
+ }
+ else if (sc && settings_parseline(buf, &name, &value))
+ {
+ if (!strcmp(name, "type"))
+ {
+ int t = 0;
+ for (t=0; t<SHORTCUT_TYPE_COUNT && sc->type == SHORTCUT_UNDEFINED; t++)
+ if (!strcmp(value, type_strings[t]))
+ sc->type = t;
+ }
+ else if (!strcmp(name, "name"))
+ {
+ strlcpy(sc->name, value, MAX_SHORTCUT_NAME);
+ }
+ else if (!strcmp(name, "data"))
+ {
+ switch (sc->type)
+ {
+ case SHORTCUT_UNDEFINED:
+ case SHORTCUT_TYPE_COUNT:
+ *param = NULL;
+ break;
+ case SHORTCUT_BROWSER:
+ case SHORTCUT_FILE:
+ case SHORTCUT_DEBUGITEM:
+ case SHORTCUT_PLAYLISTMENU:
+ strlcpy(sc->u.path, value, MAX_PATH);
+ break;
+ case SHORTCUT_SETTING:
+ sc->u.setting = find_setting_by_cfgname(value, NULL);
+ break;
+ case SHORTCUT_SEPARATOR:
+ break;
+ }
+ }
+ else if (!strcmp(name, "icon"))
+ {
+ if (!strcmp(value, "filetype") && sc->type != SHORTCUT_SETTING && sc->u.path[0])
+ {
+ sc->icon = filetype_get_icon(filetype_get_attr(sc->u.path));
+ }
+ else
+ {
+ sc->icon = atoi(value);
+ }
+ }
+ }
+ return 0;
+}
+void shortcuts_init(void)
+{
+ int fd;
+ char buf[512];
+ struct shortcut *param = NULL;
+ struct shortcut_handle *h;
+ shortcut_count = 0;
+ fd = open_utf8(SHORTCUTS_FILENAME, O_RDONLY);
+ if (fd < 0)
+ return;
+ first_handle = core_alloc("shortcuts_head", sizeof(struct shortcut_handle));
+ if (first_handle <= 0)
+ return;
+ h = core_get_data(first_handle);
+ h->next_handle = 0;
+ fast_readline(fd, buf, sizeof buf, &param, readline_cb);
+ close(fd);
+ if (param && verify_shortcut(param))
+ shortcut_count++;
+}
+
+const char * shortcut_menu_get_name(int selected_item, void * data,
+ char * buffer, size_t buffer_len)
+{
+ (void)data;
+ (void)buffer;
+ (void)buffer_len;
+ struct shortcut *sc = get_shortcut(selected_item);
+ if (!sc)
+ return "";
+ if (sc->type == SHORTCUT_SETTING)
+ return sc->name[0] ? sc->name : P2STR(ID2P(sc->u.setting->lang_id));
+ else if (sc->type == SHORTCUT_SEPARATOR)
+ return sc->name;
+ return sc->name[0] ? sc->name : sc->u.path;
+}
+
+int shortcut_menu_get_action(int action, struct gui_synclist *lists)
+{
+ (void)lists;
+ if (action == ACTION_STD_OK)
+ return ACTION_STD_CANCEL;
+ return action;
+}
+enum themable_icons shortcut_menu_get_icon(int selected_item, void * data)
+{
+ (void)data;
+ struct shortcut *sc = get_shortcut(selected_item);
+ if (!sc)
+ return Icon_NOICON;
+ if (sc->icon == Icon_NOICON)
+ {
+ switch (sc->type)
+ {
+ case SHORTCUT_FILE:
+ return filetype_get_icon(filetype_get_attr(sc->u.path));
+ case SHORTCUT_BROWSER:
+ return Icon_Folder;
+ case SHORTCUT_SETTING:
+ return Icon_Menu_setting;
+ case SHORTCUT_DEBUGITEM:
+ return Icon_Menu_functioncall;
+ case SHORTCUT_PLAYLISTMENU:
+ return Icon_Playlist;
+ default:
+ break;
+ }
+ }
+ return sc->icon;
+}
+
+int do_shortcut_menu(void *ignored)
+{
+ (void)ignored;
+ struct simplelist_info list;
+ struct shortcut *sc;
+ int done = GO_TO_PREVIOUS;
+ if (first_handle == 0)
+ shortcuts_init();
+ simplelist_info_init(&list, P2STR(ID2P(LANG_SHORTCUTS)), shortcut_count, NULL);
+ list.get_name = shortcut_menu_get_name;
+ list.action_callback = shortcut_menu_get_action;
+ list.get_icon = shortcut_menu_get_icon;
+ list.title_icon = Icon_Bookmark;
+
+ push_current_activity(ACTIVITY_SHORTCUTSMENU);
+
+ while (done == GO_TO_PREVIOUS)
+ {
+ if (simplelist_show_list(&list))
+ break; /* some error happened?! */
+ if (list.selection == -1)
+ break;
+ else
+ {
+ sc = get_shortcut(list.selection);
+ if (!sc)
+ continue;
+ switch (sc->type)
+ {
+ case SHORTCUT_PLAYLISTMENU:
+ if (!file_exists(sc->u.path))
+ {
+ splash(HZ, ID2P(LANG_NO_FILES));
+ break;
+ }
+ else
+ {
+ onplay_show_playlist_menu(sc->u.path);
+ }
+ break;
+ case SHORTCUT_FILE:
+ if (!file_exists(sc->u.path))
+ {
+ splash(HZ, ID2P(LANG_NO_FILES));
+ break;
+ }
+ /* else fall through */
+ case SHORTCUT_BROWSER:
+ {
+ struct browse_context browse;
+ browse_context_init(&browse, global_settings.dirfilter, 0,
+ NULL, NOICON, sc->u.path, NULL);
+ if (sc->type == SHORTCUT_FILE)
+ browse.flags |= BROWSE_RUNFILE;
+ done = rockbox_browse(&browse);
+ }
+ break;
+ case SHORTCUT_SETTING:
+ do_setting_screen(sc->u.setting,
+ sc->name[0] ? sc->name : P2STR(ID2P(sc->u.setting->lang_id)),NULL);
+ break;
+ case SHORTCUT_DEBUGITEM:
+ run_debug_screen(sc->u.path);
+ break;
+ case SHORTCUT_UNDEFINED:
+ default:
+ break;
+ }
+ }
+ }
+ pop_current_activity();
+ return done;
+}
diff --git a/apps/shortcuts.h b/apps/shortcuts.h
new file mode 100644
index 0000000..b794a3a
--- /dev/null
+++ b/apps/shortcuts.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ *
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2011 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.
+ *
+ ****************************************************************************/
+#ifndef __SHORTCUTS_H__
+#define __SHORTCUTS_H__
+#include <stdbool.h>
+#include <stdlib.h>
+
+enum shortcut_type {
+ SHORTCUT_UNDEFINED = -1,
+ SHORTCUT_SETTING = 0,
+ SHORTCUT_FILE,
+ SHORTCUT_DEBUGITEM,
+ SHORTCUT_BROWSER,
+ SHORTCUT_PLAYLISTMENU,
+ SHORTCUT_SEPARATOR,
+
+ SHORTCUT_TYPE_COUNT
+};
+
+void shortcuts_add(enum shortcut_type type, char* value);
+void shortcuts_init(void);
+int do_shortcut_menu(void*ignored);
+
+#endif
diff --git a/apps/tree.c b/apps/tree.c
index eb7783d..3ba54bd 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -1000,7 +1000,10 @@ int rockbox_browse(struct browse_context *browse)
tc.browse = browse;
strcpy(current, browse->root);
set_current_file(current);
- ret_val = dirbrowse();
+ if (browse->flags&BROWSE_RUNFILE)
+ ret_val = ft_enter(&tc);
+ else
+ ret_val = dirbrowse();
}
backup_count--;
if (backup_count >= 0)
diff --git a/apps/tree.h b/apps/tree.h
index 2b29605..a12045c 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -36,8 +36,10 @@ struct entry {
#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */
#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */
+#define BROWSE_RUNFILE 0x0004 /* do ft_open() on the file instead of browsing */
#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */
+
struct tree_context;
struct tree_cache {
diff --git a/manual/main_menu/main.tex b/manual/main_menu/main.tex
index 139d85f..3d5a2be 100644
--- a/manual/main_menu/main.tex
+++ b/manual/main_menu/main.tex
@@ -265,3 +265,60 @@ pages of information.}
quickscreen, then pressing up and down will cycle through this setting in
opposite directions.
}
+
+\section{\label{ref:MainMenuShortcuts}Shortcuts}
+
+This menu item is a container for user defined shortcuts to files, folders or
+settings. The following are valid shortcuts:
+\begin{itemize}
+ \item A file can be ``run'' (i.e. a music file played, plugin started or
+ a \fname{.cfg} loaded)
+ \item The file browser can be opened with the cursor positioned at
+ the specified file or folder
+ \item A file's or folder's ``Current Playlist'' context menu item can
+ be displayed
+ \item Most settings can be configured (any which can be added to the
+ \setting{Quick Screen})
+ \item Any debug menu item (useful for developers mostly)
+\end{itemize}
+
+\note{Shortcuts into the database are not possible}
+
+Shortcuts are loaded from the file \fname{/.rockbox/shortcuts.txt} which lists
+each item to be displayed. Each shortcut looks like the following:
+
+\begin{example}
+ [shortcut]
+ type: <specify the shortcut type/action>
+ data: <what the shortcut actually links to>
+ name: <what you want the shortcut to be displayed as>
+ icon: <number of the theme icon to use (see http://www.rockbox.org/wiki/CustomIcons)>
+\end{example}
+
+Only ``type'' and ``data'' are required (except if type is ``separator'' in which case
+``data'' is also not required).
+
+Available types are:
+\begin{description}
+\item[file] \config{data} is the filename to run
+\item[browse] \config{data} is the file or the folder to open the file browser at
+\item[playlist menu] \config{data} is the file or the folder to open the
+ ``Current Playlist'' context menu item on
+\item[setting] \config{data} is the config name of the setting you want to change
+\item[debug] \config{data} is the name of the debug menu item to display
+\item[separator] \config{data} is ignored; name can be used to display text,
+ or left blank to make the list more accessible with visual gaps
+\end{description}
+
+
+If the name/icon items are not specified a sensible default will be used.
+
+\note{For the ``browse'' type, if you want the file browser to start \emph{inside}
+a folder, make sure the data has the trailing slash (i.e \fname{/Music/} instead of
+\fname {/Music}). Without the trailing slash, it will cause the file broser to open
+with \fname{/Music} selected instead.}
+
+The file \fname{shortcuts.txt} can be edited with any text editor. Most items can
+also be added to it through their context menu item ``Add to shortcuts''.
+A reboot is needed for manual changes to \fname{shortcuts.txt} to be applied.
+