From 101693fd3047fb64e766580e80635a424fa25c4d Mon Sep 17 00:00:00 2001 From: Jonathan Gordon Date: Tue, 15 Nov 2011 13:22:02 +0000 Subject: 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 --- apps/shortcuts.c | 421 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 421 insertions(+) create mode 100644 apps/shortcuts.c (limited to 'apps/shortcuts.c') 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 @@ +/*************************************************************************** + * + * __________ __ ___. + * 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 +#include +#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; ttype == 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, ¶m, 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; +} -- cgit v1.2.3