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.tar.gz
rockbox-101693fd3047fb64e766580e80635a424fa25c4d.zip
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 0734d3475a..7ef81a10ee 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -52,6 +52,7 @@ root_menu.c
52screens.c 52screens.c
53settings.c 53settings.c
54settings_list.c 54settings_list.c
55shortcuts.c
55status.c 56status.c
56cuesheet.c 57cuesheet.c
57talk.c 58talk.c
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 442bc8b162..809644b3d2 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -25,6 +25,7 @@
25#include <stdbool.h> 25#include <stdbool.h>
26#include <string.h> 26#include <string.h>
27#include "lcd.h" 27#include "lcd.h"
28#include "lang.h"
28#include "menu.h" 29#include "menu.h"
29#include "debug_menu.h" 30#include "debug_menu.h"
30#include "kernel.h" 31#include "kernel.h"
@@ -45,6 +46,7 @@
45#include "screens.h" 46#include "screens.h"
46#include "misc.h" 47#include "misc.h"
47#include "splash.h" 48#include "splash.h"
49#include "shortcuts.h"
48#include "dircache.h" 50#include "dircache.h"
49#include "viewport.h" 51#include "viewport.h"
50#ifdef HAVE_TAGCACHE 52#ifdef HAVE_TAGCACHE
@@ -2120,15 +2122,23 @@ static const struct the_menu_item menuitems[] = {
2120 }; 2122 };
2121static int menu_action_callback(int btn, struct gui_synclist *lists) 2123static int menu_action_callback(int btn, struct gui_synclist *lists)
2122{ 2124{
2125 int selection = gui_synclist_get_sel_pos(lists);
2123 if (btn == ACTION_STD_OK) 2126 if (btn == ACTION_STD_OK)
2124 { 2127 {
2125 FOR_NB_SCREENS(i) 2128 FOR_NB_SCREENS(i)
2126 viewportmanager_theme_enable(i, false, NULL); 2129 viewportmanager_theme_enable(i, false, NULL);
2127 menuitems[gui_synclist_get_sel_pos(lists)].function(); 2130 menuitems[selection].function();
2128 btn = ACTION_REDRAW; 2131 btn = ACTION_REDRAW;
2129 FOR_NB_SCREENS(i) 2132 FOR_NB_SCREENS(i)
2130 viewportmanager_theme_undo(i, false); 2133 viewportmanager_theme_undo(i, false);
2131 } 2134 }
2135 else if (btn == ACTION_STD_CONTEXT)
2136 {
2137 MENUITEM_STRINGLIST(menu_items, "Debug Menu", NULL, ID2P(LANG_ADD_TO_FAVES));
2138 if (do_menu(&menu_items, NULL, NULL, false) == 0)
2139 shortcuts_add(SHORTCUT_DEBUGITEM, menuitems[selection].desc);
2140 return ACTION_STD_CANCEL;
2141 }
2132 return btn; 2142 return btn;
2133} 2143}
2134 2144
@@ -2148,3 +2158,22 @@ bool debug_menu(void)
2148 info.get_name = dbg_menu_getname; 2158 info.get_name = dbg_menu_getname;
2149 return simplelist_show_list(&info); 2159 return simplelist_show_list(&info);
2150} 2160}
2161
2162bool run_debug_screen(char* screen)
2163{
2164 unsigned i;
2165 for (i=0; i<ARRAYLEN(menuitems); i++)
2166 {
2167 if (!strcmp(screen, menuitems[i].desc))
2168 {
2169 FOR_NB_SCREENS(j)
2170 viewportmanager_theme_enable(j, false, NULL);
2171 menuitems[i].function();
2172 FOR_NB_SCREENS(j)
2173 viewportmanager_theme_undo(j, false);
2174 return true;
2175 }
2176 }
2177 return false;
2178}
2179
diff --git a/apps/debug_menu.h b/apps/debug_menu.h
index ecb50c40ae..17a9c2e094 100644
--- a/apps/debug_menu.h
+++ b/apps/debug_menu.h
@@ -22,6 +22,7 @@
22#define _DEBUG_MENU_H 22#define _DEBUG_MENU_H
23 23
24bool debug_menu(void); 24bool debug_menu(void);
25bool run_debug_screen(char* screen);
25 26
26#ifndef SIMULATOR 27#ifndef SIMULATOR
27extern bool dbg_ports(void); 28extern bool dbg_ports(void);
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index 5a59473681..741f120023 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -12903,3 +12903,17 @@
12903 *: "Cancel Sleep Timer" 12903 *: "Cancel Sleep Timer"
12904 </voice> 12904 </voice>
12905</phrase> 12905</phrase>
12906<phrase>
12907 id: LANG_SHORTCUTS
12908 desc: Title in the shortcuts menu
12909 user: core
12910 <source>
12911 *: "Shortcuts"
12912 </source>
12913 <dest>
12914 *: "Shortcuts"
12915 </dest>
12916 <voice>
12917 *: "Shortcuts"
12918 </voice>
12919</phrase>
diff --git a/apps/main.c b/apps/main.c
index c2dfc1a435..38de780c8d 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -79,6 +79,7 @@
79#if (CONFIG_PLATFORM & PLATFORM_ANDROID) 79#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
80#include "notification.h" 80#include "notification.h"
81#endif 81#endif
82#include "shortcuts.h"
82 83
83#ifdef IPOD_ACCESSORY_PROTOCOL 84#ifdef IPOD_ACCESSORY_PROTOCOL
84#include "iap.h" 85#include "iap.h"
@@ -387,6 +388,7 @@ static void init(void)
387 filetype_init(); 388 filetype_init();
388 playlist_init(); 389 playlist_init();
389 theme_init_buffer(); 390 theme_init_buffer();
391 shortcuts_init();
390 392
391#if CONFIG_CODEC != SWCODEC 393#if CONFIG_CODEC != SWCODEC
392 mp3_init( global_settings.volume, 394 mp3_init( global_settings.volume,
@@ -667,6 +669,7 @@ static void init(void)
667 filetype_init(); 669 filetype_init();
668 scrobbler_init(); 670 scrobbler_init();
669 theme_init_buffer(); 671 theme_init_buffer();
672 shortcuts_init();
670 673
671#if CONFIG_CODEC != SWCODEC 674#if CONFIG_CODEC != SWCODEC
672 /* No buffer allocation (see buffer.c) may take place after the call to 675 /* No buffer allocation (see buffer.c) may take place after the call to
diff --git a/apps/menu.c b/apps/menu.c
index b4be83fc26..a1e32f4625 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -52,6 +52,7 @@
52#include "audio.h" 52#include "audio.h"
53#include "viewport.h" 53#include "viewport.h"
54#include "quickscreen.h" 54#include "quickscreen.h"
55#include "shortcuts.h"
55 56
56#ifdef HAVE_LCD_BITMAP 57#ifdef HAVE_LCD_BITMAP
57#include "icons.h" 58#include "icons.h"
@@ -280,19 +281,10 @@ static int talk_menu_item(int selected_item, void *data)
280 return 0; 281 return 0;
281} 282}
282 283
283void do_setting_from_menu(const struct menu_item_ex *temp, 284void do_setting_screen(const struct settings_list *setting, const char * title,
284 struct viewport parent[NB_SCREENS]) 285 struct viewport parent[NB_SCREENS])
285{ 286{
286 int setting_id;
287 const struct settings_list *setting =
288 find_setting(temp->variable, &setting_id);
289 char *title;
290 char padded_title[MAX_PATH]; 287 char padded_title[MAX_PATH];
291 if ((temp->flags&MENU_TYPE_MASK) == MT_SETTING_W_TEXT)
292 title = temp->callback_and_desc->desc;
293 else
294 title = ID2P(setting->lang_id);
295
296 /* Pad the title string by repeating it. This is needed 288 /* Pad the title string by repeating it. This is needed
297 so the scroll settings title can actually be used to 289 so the scroll settings title can actually be used to
298 test the setting */ 290 test the setting */
@@ -317,7 +309,22 @@ void do_setting_from_menu(const struct menu_item_ex *temp,
317 } 309 }
318 310
319 option_screen((struct settings_list *)setting, parent, 311 option_screen((struct settings_list *)setting, parent,
320 setting->flags&F_TEMPVAR, title); 312 setting->flags&F_TEMPVAR, (char*)title);
313}
314
315
316void do_setting_from_menu(const struct menu_item_ex *temp,
317 struct viewport parent[NB_SCREENS])
318{
319 char *title;
320 int setting_id;
321 const struct settings_list *setting =
322 find_setting(temp->variable, &setting_id);
323 if (temp && ((temp->flags&MENU_TYPE_MASK) == MT_SETTING_W_TEXT))
324 title = temp->callback_and_desc->desc;
325 else
326 title = ID2P(setting->lang_id);
327 do_setting_screen(setting, title, parent);
321} 328}
322 329
323/* display a menu */ 330/* display a menu */
@@ -451,7 +458,8 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
451 ID2P(LANG_TOP_QS_ITEM), 458 ID2P(LANG_TOP_QS_ITEM),
452 ID2P(LANG_LEFT_QS_ITEM), 459 ID2P(LANG_LEFT_QS_ITEM),
453 ID2P(LANG_BOTTOM_QS_ITEM), 460 ID2P(LANG_BOTTOM_QS_ITEM),
454 ID2P(LANG_RIGHT_QS_ITEM)); 461 ID2P(LANG_RIGHT_QS_ITEM),
462 ID2P(LANG_ADD_TO_FAVES));
455#endif 463#endif
456 MENUITEM_STRINGLIST(notquickscreen_able_option, 464 MENUITEM_STRINGLIST(notquickscreen_able_option,
457 ID2P(LANG_ONPLAY_MENU_TITLE), NULL, 465 ID2P(LANG_ONPLAY_MENU_TITLE), NULL,
@@ -486,6 +494,10 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
486 case 4: /* set as right QS item */ 494 case 4: /* set as right QS item */
487 set_as_qs_item(setting, QUICKSCREEN_RIGHT); 495 set_as_qs_item(setting, QUICKSCREEN_RIGHT);
488 break; 496 break;
497 case 5: /* Add to faves. Same limitation on which can be
498 added to the shortcuts menu as the quickscreen */
499 shortcuts_add(SHORTCUT_SETTING, (void*)setting);
500 break;
489#endif 501#endif
490 } /* swicth(do_menu()) */ 502 } /* swicth(do_menu()) */
491 redraw_lists = true; 503 redraw_lists = true;
diff --git a/apps/menu.h b/apps/menu.h
index 2251de243c..ee2d9e7f40 100644
--- a/apps/menu.h
+++ b/apps/menu.h
@@ -26,6 +26,7 @@
26#include "icon.h" 26#include "icon.h"
27#include "icons.h" 27#include "icons.h"
28#include "root_menu.h" /* needed for MENU_* return codes */ 28#include "root_menu.h" /* needed for MENU_* return codes */
29#include "settings_list.h"
29 30
30 31
31enum menu_item_type { 32enum menu_item_type {
@@ -103,6 +104,8 @@ typedef int (*menu_callback_type)(int action,
103 const struct menu_item_ex *this_item); 104 const struct menu_item_ex *this_item);
104void do_setting_from_menu(const struct menu_item_ex *temp, 105void do_setting_from_menu(const struct menu_item_ex *temp,
105 struct viewport parent[NB_SCREENS]); 106 struct viewport parent[NB_SCREENS]);
107void do_setting_screen(const struct settings_list *setting, const char * title,
108 struct viewport parent[NB_SCREENS]);
106 109
107/* 110/*
108 int do_menu(const struct menu_item_ex *menu, int *start_selected) 111 int do_menu(const struct menu_item_ex *menu, int *start_selected)
diff --git a/apps/misc.h b/apps/misc.h
index bfe6e5b449..4647898889 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -119,7 +119,8 @@ enum current_activity {
119 ACTIVITY_CONTEXTMENU, 119 ACTIVITY_CONTEXTMENU,
120 ACTIVITY_SYSTEMSCREEN, 120 ACTIVITY_SYSTEMSCREEN,
121 ACTIVITY_TIMEDATESCREEN, 121 ACTIVITY_TIMEDATESCREEN,
122 ACTIVITY_BOOKMARKSLIST 122 ACTIVITY_BOOKMARKSLIST,
123 ACTIVITY_SHORTCUTSMENU
123}; 124};
124 125
125#if CONFIG_CODEC == SWCODEC 126#if CONFIG_CODEC == SWCODEC
diff --git a/apps/onplay.c b/apps/onplay.c
index aab023c846..629de93886 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -63,6 +63,7 @@
63#include "pitchscreen.h" 63#include "pitchscreen.h"
64#include "viewport.h" 64#include "viewport.h"
65#include "filefuncs.h" 65#include "filefuncs.h"
66#include "shortcuts.h"
66 67
67static int context; 68static int context;
68static char* selected_file = NULL; 69static char* selected_file = NULL;
@@ -382,10 +383,13 @@ static int treeplaylist_callback(int action,
382 return action; 383 return action;
383} 384}
384 385
385void onplay_show_playlist_menu(char* track_name) 386void onplay_show_playlist_menu(char* path)
386{ 387{
387 selected_file = track_name; 388 selected_file = path;
388 selected_file_attr = FILE_ATTR_AUDIO; 389 if (dir_exists(path))
390 selected_file_attr = ATTR_DIRECTORY;
391 else
392 selected_file_attr = filetype_get_attr(path);
389 do_menu(&tree_playlist_menu, NULL, NULL, false); 393 do_menu(&tree_playlist_menu, NULL, NULL, false);
390} 394}
391 395
@@ -1032,8 +1036,13 @@ MENUITEM_FUNCTION(list_viewers_item, 0, ID2P(LANG_ONPLAY_OPEN_WITH),
1032MENUITEM_FUNCTION(properties_item, MENU_FUNC_USEPARAM, ID2P(LANG_PROPERTIES), 1036MENUITEM_FUNCTION(properties_item, MENU_FUNC_USEPARAM, ID2P(LANG_PROPERTIES),
1033 onplay_load_plugin, (void *)"properties", 1037 onplay_load_plugin, (void *)"properties",
1034 clipboard_callback, Icon_NOICON); 1038 clipboard_callback, Icon_NOICON);
1035MENUITEM_FUNCTION(add_to_faves_item, MENU_FUNC_USEPARAM, ID2P(LANG_ADD_TO_FAVES), 1039static bool onplay_add_to_shortcuts(void)
1036 onplay_load_plugin, (void *)"shortcuts_append", 1040{
1041 shortcuts_add(SHORTCUT_BROWSER, selected_file);
1042 return false;
1043}
1044MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES),
1045 onplay_add_to_shortcuts, NULL,
1037 clipboard_callback, Icon_NOICON); 1046 clipboard_callback, Icon_NOICON);
1038 1047
1039#if LCD_DEPTH > 1 1048#if LCD_DEPTH > 1
diff --git a/apps/onplay.h b/apps/onplay.h
index 921303cf57..d24bf567b1 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -50,6 +50,6 @@ enum hotkey_action {
50 50
51/* needed for the playlist viewer.. eventually clean this up */ 51/* needed for the playlist viewer.. eventually clean this up */
52void onplay_show_playlist_cat_menu(char* track_name); 52void onplay_show_playlist_cat_menu(char* track_name);
53void onplay_show_playlist_menu(char* track_name); 53void onplay_show_playlist_menu(char* path);
54 54
55#endif 55#endif
diff --git a/apps/root_menu.c b/apps/root_menu.c
index 1e9924fb44..e78c02759b 100644
--- a/apps/root_menu.c
+++ b/apps/root_menu.c
@@ -37,6 +37,7 @@
37#include "power.h" 37#include "power.h"
38#include "talk.h" 38#include "talk.h"
39#include "audio.h" 39#include "audio.h"
40#include "shortcuts.h"
40 41
41#ifdef HAVE_HOTSWAP 42#ifdef HAVE_HOTSWAP
42#include "storage.h" 43#include "storage.h"
@@ -415,12 +416,16 @@ static const struct root_items items[] = {
415 &playlist_options }, 416 &playlist_options },
416 [GO_TO_PLAYLIST_VIEWER] = { playlist_view, NULL, &playlist_options }, 417 [GO_TO_PLAYLIST_VIEWER] = { playlist_view, NULL, &playlist_options },
417 [GO_TO_SYSTEM_SCREEN] = { miscscrn, &info_menu, &system_menu }, 418 [GO_TO_SYSTEM_SCREEN] = { miscscrn, &info_menu, &system_menu },
419 [GO_TO_SHORTCUTMENU] = { do_shortcut_menu, NULL, NULL },
418 420
419}; 421};
420static const int nb_items = sizeof(items)/sizeof(*items); 422static const int nb_items = sizeof(items)/sizeof(*items);
421 423
422static int item_callback(int action, const struct menu_item_ex *this_item) ; 424static int item_callback(int action, const struct menu_item_ex *this_item) ;
423 425
426MENUITEM_RETURNVALUE(shortcut_menu, ID2P(LANG_SHORTCUTS), GO_TO_SHORTCUTMENU,
427 NULL, Icon_Bookmark);
428
424MENUITEM_RETURNVALUE(file_browser, ID2P(LANG_DIR_BROWSER), GO_TO_FILEBROWSER, 429MENUITEM_RETURNVALUE(file_browser, ID2P(LANG_DIR_BROWSER), GO_TO_FILEBROWSER,
425 NULL, Icon_file_view_menu); 430 NULL, Icon_file_view_menu);
426#ifdef HAVE_TAGCACHE 431#ifdef HAVE_TAGCACHE
@@ -492,6 +497,7 @@ MAKE_MENU(root_menu_, ID2P(LANG_ROCKBOX_TITLE),
492#if CONFIG_KEYPAD == PLAYER_PAD 497#if CONFIG_KEYPAD == PLAYER_PAD
493 ,&do_shutdown_item 498 ,&do_shutdown_item
494#endif 499#endif
500 ,&shortcut_menu
495 ); 501 );
496 502
497static int item_callback(int action, const struct menu_item_ex *this_item) 503static 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 2ffdcedda1..8d11d9b338 100644
--- a/apps/root_menu.h
+++ b/apps/root_menu.h
@@ -58,6 +58,7 @@ enum {
58 GO_TO_PLAYLISTS_SCREEN, 58 GO_TO_PLAYLISTS_SCREEN,
59 GO_TO_PLAYLIST_VIEWER, 59 GO_TO_PLAYLIST_VIEWER,
60 GO_TO_SYSTEM_SCREEN, 60 GO_TO_SYSTEM_SCREEN,
61 GO_TO_SHORTCUTMENU
61}; 62};
62 63
63extern const struct menu_item_ex root_menu_; 64extern const struct menu_item_ex root_menu_;
diff --git a/apps/shortcuts.c b/apps/shortcuts.c
new file mode 100644
index 0000000000..3992068807
--- /dev/null
+++ b/apps/shortcuts.c
@@ -0,0 +1,421 @@
1/***************************************************************************
2 *
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2011 Jonathan Gordon
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22
23#include <stdbool.h>
24#include <stdlib.h>
25#include "config.h"
26#include "system.h"
27#include "action.h"
28#include "ata_idle_notify.h"
29#include "debug_menu.h"
30#include "core_alloc.h"
31#include "list.h"
32#include "settings.h"
33#include "settings_list.h"
34#include "lang.h"
35#include "menu.h"
36#include "misc.h"
37#include "tree.h"
38#include "splash.h"
39#include "filefuncs.h"
40#include "filetypes.h"
41#include "shortcuts.h"
42#include "onplay.h"
43
44
45
46#define MAX_SHORTCUT_NAME 32
47#define SHORTCUTS_FILENAME ROCKBOX_DIR "/shortcuts.txt"
48char *type_strings[SHORTCUT_TYPE_COUNT] = {
49 [SHORTCUT_SETTING] = "setting",
50 [SHORTCUT_FILE] = "file",
51 [SHORTCUT_DEBUGITEM] = "debug",
52 [SHORTCUT_BROWSER] = "browse",
53 [SHORTCUT_PLAYLISTMENU] = "playlist menu",
54 [SHORTCUT_SEPARATOR] = "separator",
55};
56
57
58struct shortcut {
59 enum shortcut_type type;
60 char name[MAX_SHORTCUT_NAME];
61 int icon;
62 union {
63 char path[MAX_PATH];
64 const struct settings_list *setting;
65 } u;
66};
67#define SHORTCUTS_PER_HANDLE 32
68struct shortcut_handle {
69 struct shortcut shortcuts[SHORTCUTS_PER_HANDLE];
70 int next_handle;
71};
72static int first_handle = 0;
73static int shortcut_count = 0;
74
75static void reset_shortcuts(void)
76{
77 int current_handle = first_handle;
78 struct shortcut_handle *h = NULL;
79 while (current_handle > 0)
80 {
81 int next;
82 h = core_get_data(current_handle);
83 next = h->next_handle;
84 core_free(current_handle);
85 current_handle = next;
86 }
87 first_handle = 0;
88 shortcut_count = 0;
89}
90
91static struct shortcut* get_shortcut(int index)
92{
93 int handle_count, handle_index;
94 int current_handle = first_handle;
95 struct shortcut_handle *h = NULL;
96
97 if (first_handle == 0)
98 {
99 first_handle = core_alloc("shortcuts_head", sizeof(struct shortcut_handle));
100 if (first_handle <= 0)
101 return NULL;
102 h = core_get_data(first_handle);
103 h->next_handle = 0;
104 current_handle = first_handle;
105 }
106
107 handle_count = index/SHORTCUTS_PER_HANDLE + 1;
108 handle_index = index%SHORTCUTS_PER_HANDLE;
109 do {
110 h = core_get_data(current_handle);
111 current_handle = h->next_handle;
112 handle_count--;
113 } while (handle_count > 0 && current_handle > 0);
114 if (handle_count > 0 && handle_index == 0)
115 {
116 char buf[32];
117 snprintf(buf, sizeof buf, "shortcuts_%d", index/SHORTCUTS_PER_HANDLE);
118 h->next_handle = core_alloc(buf, sizeof(struct shortcut_handle));
119 if (h->next_handle <= 0)
120 return NULL;
121 h = core_get_data(h->next_handle);
122 h->next_handle = 0;
123 }
124 return &h->shortcuts[handle_index];
125}
126
127bool verify_shortcut(struct shortcut* sc)
128{
129 switch (sc->type)
130 {
131 case SHORTCUT_UNDEFINED:
132 return false;
133 case SHORTCUT_BROWSER:
134 case SHORTCUT_FILE:
135 case SHORTCUT_PLAYLISTMENU:
136 if (sc->u.path[0] == '\0')
137 return false;
138 break;
139 case SHORTCUT_SETTING:
140 return sc->u.setting != NULL;
141 case SHORTCUT_DEBUGITEM:
142 case SHORTCUT_SEPARATOR:
143 default:
144 break;
145 }
146 return true;
147}
148
149static void init_shortcut(struct shortcut* sc)
150{
151 sc->type = SHORTCUT_UNDEFINED;
152 sc->name[0] = '\0';
153 sc->u.path[0] = '\0';
154 sc->icon = Icon_NOICON;
155}
156static int first_idx_to_writeback = -1;
157void shortcuts_ata_idle_callback(void* data)
158{
159 (void)data;
160 int fd;
161 char buf[MAX_PATH];
162 int current_idx = first_idx_to_writeback;
163 if (first_idx_to_writeback < 0)
164 return;
165 fd = open(SHORTCUTS_FILENAME, O_APPEND|O_RDWR|O_CREAT, 0644);
166 if (fd < 0)
167 return;
168 while (current_idx < shortcut_count)
169 {
170 struct shortcut* sc = get_shortcut(current_idx++);
171 char *type;
172 int len;
173 if (!sc)
174 break;
175 type = type_strings[sc->type];
176 len = snprintf(buf, MAX_PATH, "[shortcut]\ntype: %s\ndata: ", type);
177 write(fd, buf, len);
178 if (sc->type == SHORTCUT_SETTING)
179 write(fd, sc->u.setting->cfg_name, strlen(sc->u.setting->cfg_name));
180 else
181 write(fd, sc->u.path, strlen(sc->u.path));
182 write(fd, "\n\n", 2);
183 }
184 close(fd);
185 if (first_idx_to_writeback == 0)
186 {
187 /* reload all shortcuts because we appended to the shortcuts file which
188 * has not been read yet.
189 */
190 reset_shortcuts();
191 shortcuts_init();
192 }
193 first_idx_to_writeback = -1;
194}
195void shortcuts_add(enum shortcut_type type, char* value)
196{
197 struct shortcut* sc = get_shortcut(shortcut_count++);
198 if (!sc)
199 return;
200 init_shortcut(sc);
201 sc->type = type;
202 if (type == SHORTCUT_SETTING)
203 sc->u.setting = (void*)value;
204 else
205 strlcpy(sc->u.path, value, MAX_PATH);
206 if (first_idx_to_writeback < 0)
207 first_idx_to_writeback = shortcut_count - 1;
208 register_storage_idle_func(shortcuts_ata_idle_callback);
209}
210
211
212int readline_cb(int n, char *buf, void *parameters)
213{
214 (void)n;
215 (void)parameters;
216 struct shortcut **param = (struct shortcut**)parameters;
217 struct shortcut* sc = *param;
218 char *name, *value;
219
220 if (!strcasecmp(skip_whitespace(buf), "[shortcut]"))
221 {
222 if (sc && verify_shortcut(sc))
223 shortcut_count++;
224 sc = get_shortcut(shortcut_count);
225 if (!sc)
226 return 1;
227 init_shortcut(sc);
228 *param = sc;
229 }
230 else if (sc && settings_parseline(buf, &name, &value))
231 {
232 if (!strcmp(name, "type"))
233 {
234 int t = 0;
235 for (t=0; t<SHORTCUT_TYPE_COUNT && sc->type == SHORTCUT_UNDEFINED; t++)
236 if (!strcmp(value, type_strings[t]))
237 sc->type = t;
238 }
239 else if (!strcmp(name, "name"))
240 {
241 strlcpy(sc->name, value, MAX_SHORTCUT_NAME);
242 }
243 else if (!strcmp(name, "data"))
244 {
245 switch (sc->type)
246 {
247 case SHORTCUT_UNDEFINED:
248 case SHORTCUT_TYPE_COUNT:
249 *param = NULL;
250 break;
251 case SHORTCUT_BROWSER:
252 case SHORTCUT_FILE:
253 case SHORTCUT_DEBUGITEM:
254 case SHORTCUT_PLAYLISTMENU:
255 strlcpy(sc->u.path, value, MAX_PATH);
256 break;
257 case SHORTCUT_SETTING:
258 sc->u.setting = find_setting_by_cfgname(value, NULL);
259 break;
260 case SHORTCUT_SEPARATOR:
261 break;
262 }
263 }
264 else if (!strcmp(name, "icon"))
265 {
266 if (!strcmp(value, "filetype") && sc->type != SHORTCUT_SETTING && sc->u.path[0])
267 {
268 sc->icon = filetype_get_icon(filetype_get_attr(sc->u.path));
269 }
270 else
271 {
272 sc->icon = atoi(value);
273 }
274 }
275 }
276 return 0;
277}
278void shortcuts_init(void)
279{
280 int fd;
281 char buf[512];
282 struct shortcut *param = NULL;
283 struct shortcut_handle *h;
284 shortcut_count = 0;
285 fd = open_utf8(SHORTCUTS_FILENAME, O_RDONLY);
286 if (fd < 0)
287 return;
288 first_handle = core_alloc("shortcuts_head", sizeof(struct shortcut_handle));
289 if (first_handle <= 0)
290 return;
291 h = core_get_data(first_handle);
292 h->next_handle = 0;
293 fast_readline(fd, buf, sizeof buf, &param, readline_cb);
294 close(fd);
295 if (param && verify_shortcut(param))
296 shortcut_count++;
297}
298
299const char * shortcut_menu_get_name(int selected_item, void * data,
300 char * buffer, size_t buffer_len)
301{
302 (void)data;
303 (void)buffer;
304 (void)buffer_len;
305 struct shortcut *sc = get_shortcut(selected_item);
306 if (!sc)
307 return "";
308 if (sc->type == SHORTCUT_SETTING)
309 return sc->name[0] ? sc->name : P2STR(ID2P(sc->u.setting->lang_id));
310 else if (sc->type == SHORTCUT_SEPARATOR)
311 return sc->name;
312 return sc->name[0] ? sc->name : sc->u.path;
313}
314
315int shortcut_menu_get_action(int action, struct gui_synclist *lists)
316{
317 (void)lists;
318 if (action == ACTION_STD_OK)
319 return ACTION_STD_CANCEL;
320 return action;
321}
322enum themable_icons shortcut_menu_get_icon(int selected_item, void * data)
323{
324 (void)data;
325 struct shortcut *sc = get_shortcut(selected_item);
326 if (!sc)
327 return Icon_NOICON;
328 if (sc->icon == Icon_NOICON)
329 {
330 switch (sc->type)
331 {
332 case SHORTCUT_FILE:
333 return filetype_get_icon(filetype_get_attr(sc->u.path));
334 case SHORTCUT_BROWSER:
335 return Icon_Folder;
336 case SHORTCUT_SETTING:
337 return Icon_Menu_setting;
338 case SHORTCUT_DEBUGITEM:
339 return Icon_Menu_functioncall;
340 case SHORTCUT_PLAYLISTMENU:
341 return Icon_Playlist;
342 default:
343 break;
344 }
345 }
346 return sc->icon;
347}
348
349int do_shortcut_menu(void *ignored)
350{
351 (void)ignored;
352 struct simplelist_info list;
353 struct shortcut *sc;
354 int done = GO_TO_PREVIOUS;
355 if (first_handle == 0)
356 shortcuts_init();
357 simplelist_info_init(&list, P2STR(ID2P(LANG_SHORTCUTS)), shortcut_count, NULL);
358 list.get_name = shortcut_menu_get_name;
359 list.action_callback = shortcut_menu_get_action;
360 list.get_icon = shortcut_menu_get_icon;
361 list.title_icon = Icon_Bookmark;
362
363 push_current_activity(ACTIVITY_SHORTCUTSMENU);
364
365 while (done == GO_TO_PREVIOUS)
366 {
367 if (simplelist_show_list(&list))
368 break; /* some error happened?! */
369 if (list.selection == -1)
370 break;
371 else
372 {
373 sc = get_shortcut(list.selection);
374 if (!sc)
375 continue;
376 switch (sc->type)
377 {
378 case SHORTCUT_PLAYLISTMENU:
379 if (!file_exists(sc->u.path))
380 {
381 splash(HZ, ID2P(LANG_NO_FILES));
382 break;
383 }
384 else
385 {
386 onplay_show_playlist_menu(sc->u.path);
387 }
388 break;
389 case SHORTCUT_FILE:
390 if (!file_exists(sc->u.path))
391 {
392 splash(HZ, ID2P(LANG_NO_FILES));
393 break;
394 }
395 /* else fall through */
396 case SHORTCUT_BROWSER:
397 {
398 struct browse_context browse;
399 browse_context_init(&browse, global_settings.dirfilter, 0,
400 NULL, NOICON, sc->u.path, NULL);
401 if (sc->type == SHORTCUT_FILE)
402 browse.flags |= BROWSE_RUNFILE;
403 done = rockbox_browse(&browse);
404 }
405 break;
406 case SHORTCUT_SETTING:
407 do_setting_screen(sc->u.setting,
408 sc->name[0] ? sc->name : P2STR(ID2P(sc->u.setting->lang_id)),NULL);
409 break;
410 case SHORTCUT_DEBUGITEM:
411 run_debug_screen(sc->u.path);
412 break;
413 case SHORTCUT_UNDEFINED:
414 default:
415 break;
416 }
417 }
418 }
419 pop_current_activity();
420 return done;
421}
diff --git a/apps/shortcuts.h b/apps/shortcuts.h
new file mode 100644
index 0000000000..b794a3a2f4
--- /dev/null
+++ b/apps/shortcuts.h
@@ -0,0 +1,43 @@
1/***************************************************************************
2 *
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
9 * $Id$
10 *
11 * Copyright (C) 2011 Jonathan Gordon
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#ifndef __SHORTCUTS_H__
23#define __SHORTCUTS_H__
24#include <stdbool.h>
25#include <stdlib.h>
26
27enum shortcut_type {
28 SHORTCUT_UNDEFINED = -1,
29 SHORTCUT_SETTING = 0,
30 SHORTCUT_FILE,
31 SHORTCUT_DEBUGITEM,
32 SHORTCUT_BROWSER,
33 SHORTCUT_PLAYLISTMENU,
34 SHORTCUT_SEPARATOR,
35
36 SHORTCUT_TYPE_COUNT
37};
38
39void shortcuts_add(enum shortcut_type type, char* value);
40void shortcuts_init(void);
41int do_shortcut_menu(void*ignored);
42
43#endif
diff --git a/apps/tree.c b/apps/tree.c
index eb7783d4ee..3ba54bdfc6 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -1000,7 +1000,10 @@ int rockbox_browse(struct browse_context *browse)
1000 tc.browse = browse; 1000 tc.browse = browse;
1001 strcpy(current, browse->root); 1001 strcpy(current, browse->root);
1002 set_current_file(current); 1002 set_current_file(current);
1003 ret_val = dirbrowse(); 1003 if (browse->flags&BROWSE_RUNFILE)
1004 ret_val = ft_enter(&tc);
1005 else
1006 ret_val = dirbrowse();
1004 } 1007 }
1005 backup_count--; 1008 backup_count--;
1006 if (backup_count >= 0) 1009 if (backup_count >= 0)
diff --git a/apps/tree.h b/apps/tree.h
index 2b296050d3..a12045cae4 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -36,8 +36,10 @@ struct entry {
36 36
37#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */ 37#define BROWSE_SELECTONLY 0x0001 /* exit on selecting a file */
38#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */ 38#define BROWSE_NO_CONTEXT_MENU 0x0002 /* disable context menu */
39#define BROWSE_RUNFILE 0x0004 /* do ft_open() on the file instead of browsing */
39#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */ 40#define BROWSE_SELECTED 0x0100 /* this bit is set if user selected item */
40 41
42
41struct tree_context; 43struct tree_context;
42 44
43struct tree_cache { 45struct tree_cache {
diff --git a/manual/main_menu/main.tex b/manual/main_menu/main.tex
index 139d85f958..3d5a2be69f 100644
--- a/manual/main_menu/main.tex
+++ b/manual/main_menu/main.tex
@@ -265,3 +265,60 @@ pages of information.}
265 quickscreen, then pressing up and down will cycle through this setting in 265 quickscreen, then pressing up and down will cycle through this setting in
266 opposite directions. 266 opposite directions.
267} 267}
268
269\section{\label{ref:MainMenuShortcuts}Shortcuts}
270
271This menu item is a container for user defined shortcuts to files, folders or
272settings. The following are valid shortcuts:
273\begin{itemize}
274 \item A file can be ``run'' (i.e. a music file played, plugin started or
275 a \fname{.cfg} loaded)
276 \item The file browser can be opened with the cursor positioned at
277 the specified file or folder
278 \item A file's or folder's ``Current Playlist'' context menu item can
279 be displayed
280 \item Most settings can be configured (any which can be added to the
281 \setting{Quick Screen})
282 \item Any debug menu item (useful for developers mostly)
283\end{itemize}
284
285\note{Shortcuts into the database are not possible}
286
287Shortcuts are loaded from the file \fname{/.rockbox/shortcuts.txt} which lists
288each item to be displayed. Each shortcut looks like the following:
289
290\begin{example}
291 [shortcut]
292 type: <specify the shortcut type/action>
293 data: <what the shortcut actually links to>
294 name: <what you want the shortcut to be displayed as>
295 icon: <number of the theme icon to use (see http://www.rockbox.org/wiki/CustomIcons)>
296\end{example}
297
298Only ``type'' and ``data'' are required (except if type is ``separator'' in which case
299``data'' is also not required).
300
301Available types are:
302\begin{description}
303\item[file] \config{data} is the filename to run
304\item[browse] \config{data} is the file or the folder to open the file browser at
305\item[playlist menu] \config{data} is the file or the folder to open the
306 ``Current Playlist'' context menu item on
307\item[setting] \config{data} is the config name of the setting you want to change
308\item[debug] \config{data} is the name of the debug menu item to display
309\item[separator] \config{data} is ignored; name can be used to display text,
310 or left blank to make the list more accessible with visual gaps
311\end{description}
312
313
314If the name/icon items are not specified a sensible default will be used.
315
316\note{For the ``browse'' type, if you want the file browser to start \emph{inside}
317a folder, make sure the data has the trailing slash (i.e \fname{/Music/} instead of
318\fname {/Music}). Without the trailing slash, it will cause the file broser to open
319with \fname{/Music} selected instead.}
320
321The file \fname{shortcuts.txt} can be edited with any text editor. Most items can
322also be added to it through their context menu item ``Add to shortcuts''.
323A reboot is needed for manual changes to \fname{shortcuts.txt} to be applied.
324