From 946a815cd4166f8761fac0c9cba0092b871cf900 Mon Sep 17 00:00:00 2001 From: Peter D'Hoye Date: Mon, 3 Sep 2007 22:24:26 +0000 Subject: Accept FS #7667 by Alexander Levin with minor fixes by me. Splits the shortcuts plugin into two, one for adding and one for viewing. Removes hard-coded file extension and allows to link from one shortcut file to another. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@14599 a1c6a512-1295-4272-9138-f99709370657 --- apps/onplay.c | 2 +- apps/plugins/CATEGORIES | 3 +- apps/plugins/SOURCES | 1 - apps/plugins/SUBDIRS | 1 + apps/plugins/shortcuts.c | 550 ------------------------------ apps/plugins/shortcuts/Makefile | 90 +++++ apps/plugins/shortcuts/shortcuts.h | 90 +++++ apps/plugins/shortcuts/shortcuts_append.c | 109 ++++++ apps/plugins/shortcuts/shortcuts_common.c | 394 +++++++++++++++++++++ apps/plugins/shortcuts/shortcuts_view.c | 238 +++++++++++++ apps/plugins/viewers.config | 3 +- 11 files changed, 927 insertions(+), 554 deletions(-) delete mode 100644 apps/plugins/shortcuts.c create mode 100644 apps/plugins/shortcuts/Makefile create mode 100644 apps/plugins/shortcuts/shortcuts.h create mode 100644 apps/plugins/shortcuts/shortcuts_append.c create mode 100644 apps/plugins/shortcuts/shortcuts_common.c create mode 100644 apps/plugins/shortcuts/shortcuts_view.c diff --git a/apps/onplay.c b/apps/onplay.c index ba21572c3d..0e5169a007 100644 --- a/apps/onplay.c +++ b/apps/onplay.c @@ -1082,7 +1082,7 @@ MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_SET_AS_REC_DIR), #endif static bool add_to_faves(void) { - if(PLUGIN_USB_CONNECTED == filetype_load_plugin("shortcuts", + if(PLUGIN_USB_CONNECTED == filetype_load_plugin("shortcuts_append", selected_file)) onplay_result = ONPLAY_RELOAD_DIR; return false; diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES index a9997e60d4..a821fa0d91 100644 --- a/apps/plugins/CATEGORIES +++ b/apps/plugins/CATEGORIES @@ -58,7 +58,8 @@ rocklife,games rockpaint,apps search,viewers searchengine,viewers -shortcuts,viewers +shortcuts_view,viewers +shortcuts_append,viewers sliding_puzzle,games snake2,games snake,games diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 23d3e6becc..31d94d5811 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -13,7 +13,6 @@ random_folder_advance_config.c rockblox.c rockbox_flash.c search.c -shortcuts.c snow.c sort.c stats.c diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS index 3f0fc9051b..11643a5b2f 100644 --- a/apps/plugins/SUBDIRS +++ b/apps/plugins/SUBDIRS @@ -1,6 +1,7 @@ #ifndef IRIVER_IFP7XX_SERIES /* For all targets */ +shortcuts /* For various targets... */ diff --git a/apps/plugins/shortcuts.c b/apps/plugins/shortcuts.c deleted file mode 100644 index af92bf4634..0000000000 --- a/apps/plugins/shortcuts.c +++ /dev/null @@ -1,550 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2007 Bryan Childs - * - * All files in this archive are subject to the GNU General Public License. - * See the file COPYING in the source tree root for full license agreement. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include "plugin.h" - -PLUGIN_HEADER - -static struct plugin_api* rb; - -#define SHORTCUTS_FILENAME "/shortcuts.link" -#define MAX_SHORTCUTS 50 - -MEM_FUNCTION_WRAPPERS(rb); - -typedef struct sc_file_s -{ - int readsize; - char* filebuf; -} sc_file_t; - -typedef struct sc_entries_s -{ - char shortcut[MAX_PATH+1]; - int sc_len; - struct sc_entries_s* next; -} sc_entries_t; - -enum shortcut_type { - SCTYPE_NONE, - SCTYPE_FILE, - SCTYPE_DIR, -}; - -enum sc_list_action_type { - SCLA_NONE, - SCLA_SELECT, - SCLA_DELETE, -}; - -void sc_alloc_init(void); -void* sc_malloc(unsigned int size); -bool sc_init(void); -enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc); -char* build_sc_list(int selected_item, void* data, char* buffer); -void delete_sc(int sc_num); -bool load_sc_file(void); -bool load_user_sc_file(char* filename); -bool exists(char* filename); -enum plugin_status list_sc(void); -enum plugin_status write_sc_file(char* directory_name,enum shortcut_type st); - -char str_dirname[MAX_PATH]; -ssize_t bufleft; -long mem_ptr; -long bufsize; -unsigned char* mallocbuf; -bool its_a_dir = false; -bool user_file = false; -sc_file_t the_file; -sc_entries_t* shortcuts = 0; -sc_entries_t* lastentry = 0; -int total_entries = 0; -int gselected_item = 0; - -void sc_alloc_init(void){ - mem_ptr=0; - - mallocbuf = rb->plugin_get_buffer(&bufleft); - bufsize = (long)bufleft; - - rb->memset(mallocbuf,0,bufsize); - - return; -} - -void* sc_malloc(unsigned int size) { - void* x; - - if(mem_ptr + (long)size > bufsize) { - rb->splash(HZ*2,"OUT OF MEMORY"); - return NULL; - } - - x=&mallocbuf[mem_ptr]; - mem_ptr+=(size+3)&~3; /* Keep memory 32-bit aligned */ - - return x; -} - -bool exists(char* filename){ - int fd = 0; - /*strip trailing slashes */ - char* ptr = rb->strrchr((char*)filename, '/') + 1; - int dirlen = (ptr - (char*)filename); - rb->strncpy(str_dirname, (char*)filename, dirlen); - str_dirname[dirlen] = 0; - - fd = rb->open(str_dirname,O_RDONLY); - if (!fd) { - return false; - } - rb->close(fd); - return true; -} - -bool sc_init(void) { - return load_sc_file(); -} - -enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc) { - int button; - - rb->gui_synclist_draw(&gui_sc); - - while (true) { - /* draw the statusbar, should be done often */ - rb->gui_syncstatusbar_draw(rb->statusbars, true); - /* user input */ - button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); - if (rb->gui_synclist_do_button(&gui_sc,button, - LIST_WRAP_UNLESS_HELD)) { - /* automatic handling of user input. - * _UNLESS_HELD can be _ON or _OFF also - * selection changed, so redraw */ - continue; - } - switch (button) { /* process the user input */ - case ACTION_STD_OK: - gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); - return SCLA_SELECT; - break; - case ACTION_STD_MENU: - if(!user_file){ - gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); - rb->splash(HZ,"Deleting item"); - return SCLA_DELETE; - } else { - return SCLA_NONE; - } - break; - case ACTION_STD_CANCEL: - return SCLA_NONE; - break; - } - } -} - -char* build_sc_list(int selected_item, void* data, char* buffer) { - int i; - sc_entries_t* temp_node = (sc_entries_t*)data; - char text_buffer[MAX_PATH]; - - for (i=0;inext; - } - if (temp_node == NULL){ - return NULL; - } - rb->snprintf(text_buffer, MAX_PATH, "%s", temp_node->shortcut); - - rb->strcpy(buffer, text_buffer); - return buffer; -} - -void delete_sc(int sc_num){ - /* Note: This function is a nasty hack and I should probably - * be shot for doing it this way.*/ - int i; - sc_entries_t* current = shortcuts; - sc_entries_t* previous = shortcuts; - - if(total_entries==1){ - /* This is the only item in the file - * so just set the whole shortcuts list - * to zero */ - shortcuts=0; - } else { - if(sc_num!=0){ - for (i=0;inext; - } - /* current should now be pointing at the item - * to be deleted, so update the previous item - * to point to whatever current is pointing to - * as next item */ - if(current){ - previous->next = current->next; - }else{ - previous->next = 0; - } - }else{ - shortcuts = shortcuts->next; - } - } - return; -} - -enum plugin_status list_sc(void) { - int selected_item = 0; - char selected_dir[MAX_PATH]; - enum sc_list_action_type action = SCLA_NONE; - struct gui_synclist gui_sc; - - rb->memset(selected_dir,0,MAX_PATH); - - /* Setup the GUI list object, draw it to the screen, - * and then handle the user input to it */ - rb->gui_synclist_init(&gui_sc,&build_sc_list,shortcuts,false,1); - rb->gui_synclist_set_title(&gui_sc,"Shortcuts",NOICON); - rb->gui_synclist_set_nb_items(&gui_sc,total_entries); - rb->gui_synclist_limit_scroll(&gui_sc,false); - rb->gui_synclist_select_item(&gui_sc,0); - - /* Draw the prepared widget to the LCD now */ - action = draw_sc_list(gui_sc); - - /* which item do we action? */ - selected_item = gselected_item; - - /* Find out which option the user selected. - * Handily, the callback function which is used - * to populate the list is equally useful here! */ - build_sc_list(selected_item,(void*)shortcuts,selected_dir); - - /* perform the following actions if the user "selected" - * the item in the list (i.e. they want to go there - * in the filebrowser tree */ - switch(action) { - case SCLA_SELECT: - /* Check to see if the directory referenced still exists */ - if(!exists(selected_dir)){ - rb->splash(HZ*2,"File / Directory no longer exists on disk"); - return PLUGIN_ERROR; - } - - /* Set the browsers dirfilter to the global setting - * This is required in case the plugin was launched - * from the plugins browser, in which case the - * dirfilter is set to only display .rock files */ - rb->set_dirfilter(rb->global_settings->dirfilter); - - /* Change directory to the entry selected by the user */ - rb->set_current_file(selected_dir); - break; - case SCLA_DELETE: - delete_sc(selected_item); - return write_sc_file(0,SCTYPE_NONE); - break; - case SCLA_NONE: - return PLUGIN_OK; - break; - } - return PLUGIN_OK; -} - -bool load_sc_file(void){ - int fd = 0; - int amountread = 0; - char sc_content[MAX_PATH]; - sc_entries_t* entry = 0; - - fd = rb->open(SHORTCUTS_FILENAME,O_RDONLY); - if(fd<0){ - /* The shortcuts.link file didn't exist on disk - * so create an empty one. - */ - fd = rb->creat(SHORTCUTS_FILENAME); - if(fd<0){ - /* For some reason we couldn't create a new shortcuts.link - * file, so return an error message and exit - */ - rb->splash(HZ*2,"Couldn't create the shortcuts file"); - return false; - } - /* File created, but there's nothing in it - * so just exit */ - rb->close(fd); - return true; - } - - /* if we get to here, the file already exists, and has been opened - * successfully, so we can start reading it - */ - while((amountread=rb->read_line(fd,sc_content,MAX_PATH))){ - if(!(entry = (sc_entries_t*)sc_malloc(sizeof(sc_entries_t)))){ - rb->splash(HZ*2,"Couldn't get memory for a new entry"); - rb->close(fd); - return false; - } - if(shortcuts==NULL) { - /* This is the first entry created, so set - * shortcuts to point to it - */ - shortcuts=entry; - } - if(lastentry!=NULL) { - /* This isn't the first item in the list - * so update the previous item in the list - * to point to this new item. - */ - lastentry->next = entry; - } - - total_entries++; - rb->snprintf(entry->shortcut,amountread,"%s",sc_content); - entry->sc_len = amountread-1; - - /* Make sure the 'next' pointer is null */ - entry->next=0; - - /* Now we can make last look at this entry, - * ready for the next one - */ - lastentry = entry; - } - rb->close(fd); - return true; -} - -bool load_user_sc_file(char* filename){ - int fd = 0; - int amountread = 0; - char sc_content[MAX_PATH]; - sc_entries_t* entry = 0; - - /* user has chosen to open a non-default .link file - * so overwrite current memory contents */ - shortcuts = 0; - lastentry = 0; - total_entries = 0; - - fd = rb->open(filename,O_RDONLY); - if(fd<0){ - /* The shortcuts.link file didn't exist on disk - * so create an empty one. - */ - rb->splash(HZ,"Couldn't open %s",filename); - return false; - } - - while((amountread=rb->read_line(fd,sc_content,MAX_PATH))){ - if(!(entry = (sc_entries_t*)sc_malloc(sizeof(sc_entries_t)))){ - rb->splash(HZ*2,"Couldn't get memory for a new entry"); - rb->close(fd); - return false; - } - if(shortcuts==NULL) { - /* This is the first entry created, so set - * shortcuts to point to it - */ - shortcuts=entry; - } - if(lastentry!=NULL) { - /* This isn't the first item in the list - * so update the previous item in the list - * to point to this new item. - */ - lastentry->next = entry; - } - - total_entries++; - rb->snprintf(entry->shortcut,amountread,"%s",sc_content); - entry->sc_len = amountread-1; - - /* Make sure the 'next' pointer is null */ - entry->next=0; - - /* Now we can make last look at this entry, - * ready for the next one - */ - lastentry = entry; - } - rb->close(fd); - return true; -} - -enum plugin_status write_sc_file(char* directory_name, enum shortcut_type st) { - int fd; - int i; - sc_entries_t *temp_node = shortcuts; - char text_buffer[MAX_PATH]; - - if(total_entries>=MAX_SHORTCUTS) { - /* too many entries in the file already - * so don't add this one, and give the - * user an error */ - rb->splash(HZ*2,"Shortcuts file is full"); - return PLUGIN_ERROR; - } - - /* ideally, we should just write a new - * entry to the file, but I'm going to - * be lazy, and just re-write the whole - * thing. */ - fd = rb->open(SHORTCUTS_FILENAME,O_RDWR); - if(fd<0){ - rb->splash(HZ*2,"Error writing to shortcuts file"); - return PLUGIN_ERROR; - } - - /* truncate the current file, since we're writing it - * all over again */ - rb->ftruncate(fd,0); - - /* Check to see that the list is not empty */ - if(temp_node){ - for (i=0;isnprintf(text_buffer,temp_node->sc_len+2, - "%s\n",temp_node->shortcut); - rb->write(fd,text_buffer,temp_node->sc_len+1); - temp_node = temp_node->next; - } - } - /* Reached the end of the existing entries, so check to - * see if we need to add one more for the new entry - */ - if(st!=SCTYPE_NONE){ - if(st==SCTYPE_FILE) { - rb->snprintf(text_buffer,rb->strlen(directory_name)+2, /*+2 is \n and 0x00 */ - "%s\n",directory_name); - rb->write(fd,text_buffer,rb->strlen(directory_name)+1); - } else if(st==SCTYPE_DIR){ - rb->snprintf(text_buffer,rb->strlen(directory_name)+3, /*+3 is /, \n and 0x00 */ - "%s/\n",directory_name); - rb->write(fd,text_buffer,rb->strlen(directory_name)+2); - } - } - rb->close(fd); - - return PLUGIN_OK; -} - -enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { - rb = api; - bool found = false; - - DIR* dir; - struct dirent* entry; - - /* Initialise the plugin buffer */ - sc_alloc_init(); - - if(!sc_init()) - return PLUGIN_ERROR; - - /* Were we passed a parameter at startup? */ - if(parameter) { - /* determine if it's a file or a directory */ - char* ptr = rb->strrchr((char*)parameter, '/') + 1; - int dirlen = (ptr - (char*)parameter); - rb->strncpy(str_dirname, (char*)parameter, dirlen); - str_dirname[dirlen] = 0; - - dir = rb->opendir(str_dirname); - if (dir) { - while(0 != (entry = rb->readdir(dir))) { - if(!rb->strcmp(entry->d_name, parameter+dirlen)) { - its_a_dir = entry->attribute & ATTR_DIRECTORY ? - true : false; - found = true; - break; - } - } - rb->closedir(dir); - } - /* now we know if it's a file or a directory - * (or something went wrong) */ - - if(!found) { - /* Something's gone properly pear shaped - - * we couldn't even find the entry */ - rb->splash(HZ*2,"File / Directory not found : %s", - (char*)parameter); - return PLUGIN_ERROR; - } - - if(!its_a_dir) { - /* Don't add the shortcuts.link file to itself */ - if(rb->strcmp((char*)parameter,SHORTCUTS_FILENAME)==0){ - return list_sc(); - } - /* this section handles user created .link files */ - if(rb->strcasestr((char*)parameter,".link")){ - if(!load_user_sc_file((char*)parameter)){ - return PLUGIN_ERROR; - } - user_file = true; - if(total_entries==1){ - /* if there's only one entry in the user .link file, - * go straight to it without displaying the menu */ - char selected_dir[MAX_PATH]; - /* go to entry immediately */ - build_sc_list(0,(void*)shortcuts,selected_dir); - - /* Check to see if the directory referenced still exists */ - if(!exists(selected_dir)){ - rb->splash(HZ*2,"File / Directory no longer exists on disk"); - return PLUGIN_ERROR; - } - - /* Set the browsers dirfilter to the global setting */ - rb->set_dirfilter(rb->global_settings->dirfilter); - - /* Change directory to the entry selected by the user */ - rb->set_current_file(selected_dir); - return PLUGIN_OK; - }else{ - /* This user created link file has multiple entries in it - * so display a menu to choose between them */ - return list_sc(); - } - } else { - /* This isn't a .link file so add it to the shortcuts.link - * file as a new entry */ - return write_sc_file((char*)parameter,SCTYPE_FILE); - } - }else{ - /* This is a directory and should be added to - * the shortcuts.link file */ - return write_sc_file((char*)parameter,SCTYPE_DIR); - } - } - else { /* We weren't passed a parameter, so open the - * shortcuts file directly */ - return list_sc(); - } - return PLUGIN_OK; -} - diff --git a/apps/plugins/shortcuts/Makefile b/apps/plugins/shortcuts/Makefile new file mode 100644 index 0000000000..93089cc140 --- /dev/null +++ b/apps/plugins/shortcuts/Makefile @@ -0,0 +1,90 @@ +# __________ __ ___. +# Open \______ \ ____ ____ | | _\_ |__ _______ ___ +# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / +# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < +# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ +# \/ \/ \/ \/ \/ +# $$Id: $$ +# + +INCLUDES = -I$(APPSDIR) -I.. -I. $(TARGET_INC) -I$(FIRMDIR)/include -I$(FIRMDIR)/export \ + -I$(FIRMDIR)/common -I$(FIRMDIR)/drivers -I$(OUTDIR) -I$(BUILDDIR) \ + -I$(BUILDDIR)/pluginbitmaps +CFLAGS = $(INCLUDES) $(GCCOPTS) $(TARGET) $(EXTRA_DEFINES) \ + -DTARGET_ID=$(TARGET_ID) -DMEM=${MEMORYSIZE} -DPLUGIN + +ifdef APPEXTRA + INCLUDES += $(patsubst %,-I$(APPSDIR)/%,$(subst :, ,$(APPEXTRA))) +endif + +LINKFILE := $(OBJDIR)/link.lds +DEPFILE = $(OBJDIR)/dep-shortcuts + +SOURCES := shortcuts_common.c shortcuts_view.c shortcuts_append.c +VIEW_OBJS := $(OBJDIR)/shortcuts_common.o $(OBJDIR)/shortcuts_view.o +APPEND_OBJS := $(OBJDIR)/shortcuts_common.o $(OBJDIR)/shortcuts_append.o +DIRS = . + +ifndef SIMVER + LDS := ../plugin.lds +endif + +OUTPUT = $(OUTDIR)/shortcuts_view.rock $(OUTDIR)/shortcuts_append.rock + +all: $(OUTPUT) + +ifndef SIMVER +$(OBJDIR)/shortcuts_view.elf: $(VIEW_OBJS) $(LINKFILE) $(BITMAPLIBS) + $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(VIEW_OBJS) -L$(BUILDDIR) -lplugin -lgcc \ + $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/shortcuts_view.map + +$(OUTDIR)/shortcuts_view.rock: $(OBJDIR)/shortcuts_view.elf + $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ + +$(OBJDIR)/shortcuts_append.elf: $(APPEND_OBJS) $(LINKFILE) $(BITMAPLIBS) + $(call PRINTS,LD $(@F))$(CC) $(GCCOPTS) -O -nostdlib -o $@ $(APPEND_OBJS) -L$(BUILDDIR) -lplugin -lgcc \ + $(LINKBITMAPS) -T$(LINKFILE) -Wl,-Map,$(OBJDIR)/shortcuts_append.map + +$(OUTDIR)/shortcuts_append.rock: $(OBJDIR)/shortcuts_append.elf + $(call PRINTS,OBJCOPY $(@F))$(OC) -O binary $< $@ +else + +################################################### +# This is the SDL simulator version + +$(OUTDIR)/shortcuts_view.rock: $(VIEW_OBJS) + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(VIEW_OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ +ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) +# 'x' must be kept or you'll have "Win32 error 5" +# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 +# #define ERROR_ACCESS_DENIED 5L +else + @chmod -x $@ +endif + +$(OUTDIR)/shortcuts_append.rock: $(APPEND_OBJS) + $(call PRINTS,LD $(@F))$(CC) $(CFLAGS) $(SHARED_FLAG) $(APPEND_OBJS) -L$(BUILDDIR) -lplugin $(LINKBITMAPS) -o $@ +ifeq ($(findstring CYGWIN,$(UNAME)),CYGWIN) +# 'x' must be kept or you'll have "Win32 error 5" +# $ fgrep 5 /usr/include/w32api/winerror.h | head -1 +# #define ERROR_ACCESS_DENIED 5L +else + @chmod -x $@ +endif + +endif # end of simulator section + + +include $(TOOLSDIR)/make.inc + +# MEMORYSIZE should be passed on to this makefile with the chosen memory size +# given in number of MB +$(LINKFILE): $(LDS) + $(call PRINTS,build $(@F))cat $< | $(CC) -DMEMORYSIZE=$(MEMORYSIZE) $(INCLUDES) $(TARGET) \ + $(DEFINES) -E -P - >$@ + +clean: + $(call PRINTS,cleaning shortcuts)rm -rf $(OBJDIR)/shortcuts + $(SILENT)rm -f $(OBJDIR)/shortcuts* $(DEPFILE) + +-include $(DEPFILE) diff --git a/apps/plugins/shortcuts/shortcuts.h b/apps/plugins/shortcuts/shortcuts.h new file mode 100644 index 0000000000..6ccb320a36 --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts.h @@ -0,0 +1,90 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Bryan Childs + * Copyright (c) 2007 Alexander Levin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * 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 "plugin.h" + +#define PATH_SEPARATOR "/" +#define PATH_SEPARATOR_LEN 1 /* strlen(PATH_SEPARATOR) */ + +#if defined(DEBUG) || defined(SIMULATOR) +#define SC_DEBUG +#endif + +#define SHORTCUTS_FILENAME "/shortcuts.link" + +extern struct plugin_api* rb; + +typedef struct sc_entry_s +{ + char path[MAX_PATH+1]; + char disp[MAX_PATH+1]; + bool explicit_disp; +} sc_entry_t; + +typedef struct sc_file_s +{ + sc_entry_t *entries; + int max_entries; /* Max allowed number of entries */ + int entry_cnt; /* Current number of entries */ + int show_last_segments; +} sc_file_t; + + +extern void *memory_buf; +extern long memory_bufsize; + + +extern sc_file_t sc_file; + + +/* Allocates a chunk of memory (as much as possible) */ +void allocate_memory(void **buf, size_t *bufsize); + +/* Initializes the file */ +void init_sc_file(sc_file_t *file, void *buf, size_t bufsize); + +/* Loads shortcuts from the file. Returns true iff succeeded */ +bool load_sc_file(sc_file_t *file, char* filename, bool must_exist, + void *entry_buf, size_t entry_bufsize); + +/* Writes contents to the file. File is overwritten. */ +bool dump_sc_file(sc_file_t *file, char *filename); + +/* Appends the entry to the file. Entry is copied. Returns true iff succeded. */ +bool append_entry(sc_file_t *file, sc_entry_t *entry); + +/* Removes the specified entry (0-based index). Returns true iff succeded. */ +bool remove_entry(sc_file_t *file, int entry_idx); + +/* Checks whether the index is a valid one for the file. */ +bool is_valid_index(sc_file_t *file, int entry_idx); + +bool file_exists(char *filename); /* Does the specified file exist? */ +bool dir_exists(char *path); /* Does the specified dir exist? */ + + +#ifdef SC_DEBUG +void print_file(sc_file_t *file); +#endif + +#endif diff --git a/apps/plugins/shortcuts/shortcuts_append.c b/apps/plugins/shortcuts/shortcuts_append.c new file mode 100644 index 0000000000..bc287234dd --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts_append.c @@ -0,0 +1,109 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Bryan Childs + * Copyright (c) 2007 Alexander Levin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "shortcuts.h" + +PLUGIN_HEADER + + +bool append_entry_to_file(sc_file_t *file, char *path, bool is_dir) +{ + sc_entry_t entry; + + unsigned int required_len = rb->strlen(path); + if (is_dir) { + required_len += PATH_SEPARATOR_LEN; /* Add 1 for the trailing / */ + } + if (required_len >= sizeof(entry.path)) { + /* no attempt to print it: it will also be so too long for the splash */ + rb->splash(HZ*2, "Can't append shortcut, it's too long"); + return false; + } + entry.explicit_disp = false; + rb->strcpy(entry.path, path); + if (is_dir) { + rb->strcat(entry.path, PATH_SEPARATOR); + } + if (!append_entry(file, &entry)) { + rb->splash(HZ*2, "Too many entries!"); + return false; + } + return true; +} + + +enum plugin_status plugin_start(struct plugin_api* api, void* void_parameter) +{ + rb = api; + bool found; + bool its_a_dir; + + /* This is a viewer, so a parameter must have been specified */ + if (void_parameter == NULL) { + rb->splash(HZ*2, "No parameter specified!"); + return PLUGIN_ERROR; + } + char *parameter = (char*)void_parameter; + DEBUGF("Trying to append '%s' to the default link file '%s'...\n", + parameter, SHORTCUTS_FILENAME); + + allocate_memory(&memory_buf, &memory_bufsize); + + /* Determine if it's a file or a directory. First check + * if it's a dir and then file (not vice versa) since + * open() can also open a dir */ + found = true; + if (dir_exists(parameter)) { + its_a_dir = true; + } else if (file_exists(parameter)) { + its_a_dir = false; + } else { + found = false; + } + /* now we know if it's a file or a directory + * (or something went wrong) */ + + if (!found) { + /* Something's gone properly pear shaped - + * we couldn't even find the entry */ + rb->splash(HZ*2, "File/Dir not found: %s", parameter); + return PLUGIN_ERROR; + } + + DEBUGF("'%s' is a %s\n", parameter, (its_a_dir ? "dir" : "file")); + + if (!load_sc_file(&sc_file, SHORTCUTS_FILENAME, false, + memory_buf, memory_bufsize)) { + DEBUGF("Couldn't load '%s'\n", SHORTCUTS_FILENAME); + return PLUGIN_ERROR; + } + + if (!append_entry_to_file(&sc_file, parameter, its_a_dir)) { + DEBUGF("Couldn't append entry (too many entries?)\n"); + return PLUGIN_ERROR; + } + + if (!dump_sc_file(&sc_file, SHORTCUTS_FILENAME)) { + DEBUGF("Couldn't write shortcuts to '%s'\n", SHORTCUTS_FILENAME); + return PLUGIN_ERROR; + } + + return PLUGIN_OK; +} diff --git a/apps/plugins/shortcuts/shortcuts_common.c b/apps/plugins/shortcuts/shortcuts_common.c new file mode 100644 index 0000000000..4a097f0a0d --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts_common.c @@ -0,0 +1,394 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Bryan Childs + * Copyright (c) 2007 Alexander Levin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "shortcuts.h" +MEM_FUNCTION_WRAPPERS(rb); + +#define SHORTCUTS_FILENAME "/shortcuts.link" + +#define PATH_DISP_SEPARATOR "\t" +#define PATH_DISP_SEPARATOR_LEN 1 /* strlen(PATH_DISP_SEPARATOR) */ +#define CONTROL_PREFIX "#" +#define CONTROL_PREFIX_LEN 1 /* strlen(CONTROL_PREFIX) */ +#define NAME_VALUE_SEPARATOR "=" +#define NAME_VALUE_SEPARATOR_LEN 1 /* strlen(NAME_VALUE_SEPARATOR) */ + +#define INSTR_DISPLAY_LAST_SEGMENTS "Display last path segments" + +/* Memory (will be used for entries) */ +void *memory_buf; +long memory_bufsize; /* Size of memory_buf in bytes */ + + +/* The file we're processing */ +sc_file_t sc_file; + +bool parse_entry_content(char *line, sc_entry_t *entry, int last_segm); +char *last_segments(char *path, int nsegm); +bool is_control(char *line, sc_file_t *file); +bool starts_with(char *string, char *prefix); +bool parse_name_value(char *line, char *name, int namesize, + char *value, int valuesize); +void write_entry_to_file(int fd, sc_entry_t *entry); +void write_int_instruction_to_file(int fd, char *instr, int value); + + +void allocate_memory(void **buf, size_t *bufsize) +{ + *buf = rb->plugin_get_buffer(bufsize); + DEBUGF("Got %ld bytes of memory\n", *bufsize); +} + + +void init_sc_file(sc_file_t *file, void *buf, size_t bufsize) +{ + file->entries = (sc_entry_t*)buf; + file->max_entries = bufsize / sizeof(sc_entry_t); + DEBUGF("Buffer capacity: %d entries\n", file->max_entries); + file->entry_cnt = 0; + file->show_last_segments = -1; +} + + +bool load_sc_file(sc_file_t *file, char *filename, bool must_exist, + void *entry_buf, size_t entry_bufsize) +{ + int fd = -1; + bool ret_val = false; /* Assume bad case */ + int amountread = 0; + char sc_content[2*MAX_PATH]; + sc_entry_t entry; + + /* We start to load a new file -> prepare it */ + init_sc_file(&sc_file, entry_buf, entry_bufsize); + + fd = rb->open(filename, O_RDONLY); + if (fd < 0) { + /* The file didn't exist on disk */ + if (!must_exist) { + DEBUGF("Trying to create link file '%s'...\n", filename); + fd = rb->creat(filename); + if (fd < 0){ + /* For some reason we couldn't create the file, + * so return an error message and exit */ + rb->splash(HZ*2, "Couldn't create the shortcuts file %s", + filename); + goto end_of_proc; + } + /* File created, but there's nothing in it, so just exit */ + ret_val = true; + goto end_of_proc; + } else { + rb->splash(HZ, "Couldn't open %s", filename); + goto end_of_proc; + } + } + + while ((amountread=rb->read_line(fd,sc_content, sizeof(sc_content)))) { + if (is_control(sc_content, file)) { + continue; + } + if (file->entry_cnt >= file->max_entries) { + rb->splash(HZ*2, "Too many entries in the file, max allowed: %d", + file->max_entries); + goto end_of_proc; + } + if (!parse_entry_content(sc_content, &entry,file->show_last_segments)) { + /* Could not parse the entry (too long path?) -> ignore */ + DEBUGF("Could not parse '%s' -> ignored\n", sc_content); + continue; + } + DEBUGF("Parsed entry: path=%s, disp=%s\n", entry.path, entry.disp); + append_entry(file, &entry); + } + +#ifdef SC_DEBUG + print_file(file); +#endif + + ret_val = true; /* Everything went ok */ + +end_of_proc: + if (fd >= 0) { + rb->close(fd); + fd = -1; + } + return ret_val; +} + +#ifdef SC_DEBUG +void print_file(sc_file_t *file) +{ + DEBUGF("Number of entries : %d\n", file->entry_cnt); + DEBUGF("Show Last Segments: %d\n", file->show_last_segments); + int i; + sc_entry_t *entry; + for (i=0, entry=file->entries; ientry_cnt; i++,entry++) { + if (entry->explicit_disp) { + DEBUGF("%2d. '%s', show as '%s'\n", i+1, entry->path, entry->disp); + } else { + DEBUGF("%2d. '%s' (%s)\n", i+1, entry->path, entry->disp); + } + } +} +#endif + + +bool append_entry(sc_file_t *file, sc_entry_t *entry) +{ + if (file->entry_cnt >= file->max_entries) { + return false; + } + rb->memcpy(file->entries+file->entry_cnt, entry, sizeof(*entry)); + file->entry_cnt++; + return true; +} + + +bool remove_entry(sc_file_t *file, int entry_idx) +{ + if ((entry_idx<0) || (entry_idx>=file->entry_cnt)) { + return false; + } + sc_entry_t *start = file->entries + entry_idx; + rb->memmove(start, start + 1, + (file->entry_cnt-entry_idx-1) * sizeof(sc_entry_t)); + file->entry_cnt--; + return true; +} + + +bool is_valid_index(sc_file_t *file, int entry_idx) +{ + return (entry_idx >= 0) && (entry_idx < file->entry_cnt); +} + + +bool parse_entry_content(char *line, sc_entry_t *entry, int last_segm) +{ + char *sep; + char *path, *disp; + unsigned int path_len, disp_len; + bool expl; + + sep = rb->strcasestr(line, PATH_DISP_SEPARATOR); + expl = (sep != NULL); + if (expl) { + /* Explicit name for the entry is specified -> use it */ + path = line; + path_len = sep - line; + disp = sep + PATH_DISP_SEPARATOR_LEN; + disp_len = rb->strlen(disp); + } else { + /* No special name to display */ + path = line; + path_len = rb->strlen(line); + if (last_segm <= 0) { + disp = path; + } else { + disp = last_segments(line, last_segm); + } + disp_len = rb->strlen(disp); + } + + if (path_len >= sizeof(entry->path) || disp_len >= sizeof(entry->disp)) { + DEBUGF("Bad entry: pathlen=%d, displen=%d\n", path_len, disp_len); + return false; + } + rb->strncpy(entry->path, path, path_len); + entry->path[path_len] = '\0'; + rb->strcpy(entry->disp, disp); /* Safe since we've checked the length */ + entry->explicit_disp = expl; + return true; +} + + +char *last_segments(char *path, int nsegm) +{ + char *p = rb->strrchr(path, PATH_SEPARATOR[0]); /* Hack */ + int seg_cnt; + if (p == NULL) + return path; /* No separator??? */ + seg_cnt = 0; + while ((p > path) && (seg_cnt < nsegm)) { + p--; + if (!starts_with(p, PATH_SEPARATOR)) { + continue; + } + seg_cnt++; + if (seg_cnt == nsegm && p > path) { + p++; /* Eat the '/' to show that something has been truncated */ + } + } + return p; +} + + +bool is_control(char *line, sc_file_t *file) +{ + char name[MAX_PATH], value[MAX_PATH]; + if (!starts_with(line, CONTROL_PREFIX)) { + return false; + } + line += CONTROL_PREFIX_LEN; + + if (!parse_name_value(line, name, sizeof(name), + value, sizeof(value))) { + DEBUGF("Bad processing instruction: '%s'\n", line); + return true; + } + + /* Process control instruction */ + if (rb->strcasestr(name, INSTR_DISPLAY_LAST_SEGMENTS)) { + file->show_last_segments = rb->atoi(value); + DEBUGF("Set show last segms to %d\n", file->show_last_segments); + } else { + /* Unknown instruction -> ignore */ + DEBUGF("Unknown processing instruction: '%s'\n", name); + } + + return true; +} + + +bool starts_with(char *string, char *prefix) +{ + unsigned int pfx_len = rb->strlen(prefix); + if (rb->strlen(string) < pfx_len) + return false; + return (rb->strncmp(string, prefix, pfx_len) == 0); +} + + +bool parse_name_value(char *line, char *name, int namesize, + char *value, int valuesize) +{ + char *sep; + int name_len, val_len; + name[0] = value[0] = '\0'; + + sep = rb->strcasestr(line, NAME_VALUE_SEPARATOR); + if (sep == NULL) { + /* No separator char -> weird instruction */ + return false; + } + name_len = sep - line; + if (name_len >= namesize) { + /* Too long name */ + return false; + } + rb->strncpy(name, line, name_len); + name[name_len] = '\0'; + + val_len = rb->strlen(line) - name_len - NAME_VALUE_SEPARATOR_LEN; + if (val_len >= valuesize) { + /* Too long value */ + return false; + } + rb->strncpy(value, sep+NAME_VALUE_SEPARATOR_LEN, val_len+1); + return true; +} + + +bool dump_sc_file(sc_file_t *file, char *filename) +{ + DEBUGF("Dumping shortcuts to the file '%s'\n", filename); + int fd; + + /* ideally, we should just write a new + * entry to the file, but I'm going to + * be lazy, and just re-write the whole + * thing. */ + fd = rb->open(filename, O_WRONLY|O_TRUNC); + if (fd < 0) { + rb->splash(HZ*2, "Could not open shortcuts file %s for writing", + filename); + return false; + } + + /* + * Write instructions + */ + /* Always dump the 'display last segms' settings (even it it's + * not active) so that it can be easily changed without having + * to remember the setting name */ + write_int_instruction_to_file(fd, + INSTR_DISPLAY_LAST_SEGMENTS, file->show_last_segments); + + int i; + sc_entry_t *entry; + for (i=0, entry=file->entries; ientry_cnt; i++,entry++) { + write_entry_to_file(fd, entry); + } + + rb->close(fd); + DEBUGF("Dumped %d entries to the file '%s'\n", file->entry_cnt, filename); + + return true; +} + + +void write_int_instruction_to_file(int fd, char *instr, int value) +{ + rb->fdprintf(fd, "%s%s%s%d\n", CONTROL_PREFIX, instr, + NAME_VALUE_SEPARATOR, value); +} + + +void write_entry_to_file(int fd, sc_entry_t *entry) +{ + rb->fdprintf(fd, "%s", entry->path); + if (entry->explicit_disp) { + rb->fdprintf(fd, "%s%s", PATH_DISP_SEPARATOR, entry->disp); + } + rb->fdprintf(fd, "\n"); +} + + +bool file_exists(char *filename) +{ + int fd = rb->open(filename, O_RDONLY); + bool retval; + if (fd >= 0) { + rb->close(fd); + retval = true; + } else { + retval = false; + } + DEBUGF("Checked existence of the file '%s': %s\n", + filename, (retval ? "found" : "NOT FOUND")); + return retval; +} + + +bool dir_exists(char *path) +{ + DIR* d = rb->opendir(path); + bool retval; + if (d != NULL) { + rb->closedir(d); + retval = true; + } else { + retval = false; + } + DEBUGF("Checked existence of the dir '%s': %s\n", + path, (retval ? "found" : "NOT FOUND")); + return retval; +} diff --git a/apps/plugins/shortcuts/shortcuts_view.c b/apps/plugins/shortcuts/shortcuts_view.c new file mode 100644 index 0000000000..3b28b4b34c --- /dev/null +++ b/apps/plugins/shortcuts/shortcuts_view.c @@ -0,0 +1,238 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Bryan Childs + * Copyright (c) 2007 Alexander Levin + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "shortcuts.h" + +PLUGIN_HEADER + +enum sc_list_action_type +{ + SCLA_NONE, + SCLA_SELECT, + SCLA_DELETE, + SCLA_USB, +}; + + +static char *link_filename; +static bool user_file; +static int gselected_item; +static bool usb_connected = false; + +enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc); + +/* Will be passed sc_file* as data */ +char* build_sc_list(int selected_item, void *data, char *buffer); + +/* Returns true iff we should leave the main loop */ +bool list_sc(bool is_editable); + +bool goto_entry(char *file_or_dir); +bool ends_with(char *str, char *suffix); + + +enum sc_list_action_type draw_sc_list(struct gui_synclist gui_sc) +{ + int button; + + rb->gui_synclist_draw(&gui_sc); + + while (true) { + /* draw the statusbar, should be done often */ + rb->gui_syncstatusbar_draw(rb->statusbars, true); + /* user input */ + button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK); + if (rb->gui_synclist_do_button(&gui_sc, button, + LIST_WRAP_UNLESS_HELD)) { + /* automatic handling of user input. + * _UNLESS_HELD can be _ON or _OFF also + * selection changed, so redraw */ + continue; + } + switch (button) { /* process the user input */ + case ACTION_STD_OK: + gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); + return SCLA_SELECT; + case ACTION_STD_MENU: + /* Only allow delete entries in the default file + * since entries can be appended (with a plugin) + * to the default file only. The behaviour is thus + * symmetric in this respect. */ + if (!user_file) { + gselected_item = rb->gui_synclist_get_sel_pos(&gui_sc); + return SCLA_DELETE; + } + break; + case ACTION_STD_CANCEL: + return SCLA_NONE; + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { + return SCLA_USB; + } + } + } +} + + +char* build_sc_list(int selected_item, void *data, char *buffer) +{ + char text_buffer[MAX_PATH]; + sc_file_t *file = (sc_file_t*)data; + + if (!is_valid_index(file, selected_item)) { + return NULL; + } + sc_entry_t *entry = file->entries + selected_item; + rb->snprintf(text_buffer, sizeof(text_buffer), "%s", entry->disp); + rb->strcpy(buffer, text_buffer); + return buffer; +} + + +bool list_sc(bool is_editable) +{ + int selected_item = 0; + char selected_dir[MAX_PATH]; + enum sc_list_action_type action = SCLA_NONE; + struct gui_synclist gui_sc; + + rb->memset(selected_dir, 0, sizeof(selected_dir)); + + /* Setup the GUI list object, draw it to the screen, + * and then handle the user input to it */ + rb->gui_synclist_init(&gui_sc, &build_sc_list, &sc_file, false, 1); + rb->gui_synclist_set_title(&gui_sc, + (is_editable?"Shortcuts (editable)":"Shortcuts (sealed)"), NOICON); + rb->gui_synclist_set_nb_items(&gui_sc, sc_file.entry_cnt); + rb->gui_synclist_limit_scroll(&gui_sc, false); + rb->gui_synclist_select_item(&gui_sc, 0); + + /* Draw the prepared widget to the LCD now */ + action = draw_sc_list(gui_sc); + if (action == SCLA_USB) { + usb_connected = true; + return true; + } + + /* which item do we action? */ + selected_item = gselected_item; + + if (!is_valid_index(&sc_file, selected_item)) { + /* This should never happen */ + rb->splash(HZ*2, "Bad entry selected!"); + return true; + } + + /* perform the following actions if the user "selected" + * the item in the list (i.e. they want to go there + * in the filebrowser tree */ + switch(action) { + case SCLA_SELECT: + return goto_entry(sc_file.entries[selected_item].path); + case SCLA_DELETE: + rb->splash(HZ, "Deleting %s", sc_file.entries[selected_item].disp); + remove_entry(&sc_file, selected_item); + dump_sc_file(&sc_file, link_filename); + return (sc_file.entry_cnt == 0); + default: + return true; + } +} + + +bool goto_entry(char *file_or_dir) +{ + DEBUGF("Trying to go to '%s'...\n", file_or_dir); + + bool is_dir = ends_with(file_or_dir, PATH_SEPARATOR); + bool exists; + char *what; + if (is_dir) { + what = "Directory"; + exists = dir_exists(file_or_dir); + } else { + what = "File"; + exists = file_exists(file_or_dir); + } + + if (!exists) { + rb->splash(HZ*2, "%s %s no longer exists on disk", what, file_or_dir); + return false; + } + /* Set the browsers dirfilter to the global setting + * This is required in case the plugin was launched + * from the plugins browser, in which case the + * dirfilter is set to only display .rock files */ + rb->set_dirfilter(rb->global_settings->dirfilter); + + /* Change directory to the entry selected by the user */ + rb->set_current_file(file_or_dir); + return true; +} + + +bool ends_with(char *string, char *suffix) +{ + unsigned int str_len = rb->strlen(string); + unsigned int sfx_len = rb->strlen(suffix); + if (str_len < sfx_len) + return false; + return (rb->strncmp(string + str_len - sfx_len, suffix, sfx_len) == 0); +} + + +enum plugin_status plugin_start(struct plugin_api* api, void* void_parameter) +{ + rb = api; + bool leave_loop; + + /* This is a viewer, so a parameter must have been specified */ + if (void_parameter == NULL) { + rb->splash(HZ*2, "No parameter specified!"); + return PLUGIN_ERROR; + } + link_filename = (char*)void_parameter; + user_file = (rb->strcmp(link_filename, SHORTCUTS_FILENAME) != 0); + + allocate_memory(&memory_buf, &memory_bufsize); + + if (!load_sc_file(&sc_file, link_filename, true, + memory_buf, memory_bufsize)) { + DEBUGF("Could not load %s\n", link_filename); + return PLUGIN_ERROR; + } + if (sc_file.entry_cnt==0) { + rb->splash(HZ*2, "No shortcuts in the file!"); + return PLUGIN_OK; + } else if ((sc_file.entry_cnt==1) && user_file) { + /* if there's only one entry in the user .link file, + * go straight to it without displaying the menu + * thus allowing 'quick links' */ + goto_entry(sc_file.entries[0].path); + return PLUGIN_OK; + } + + do { + /* Display a menu to choose between the entries */ + leave_loop = list_sc(!user_file); + } while (!leave_loop); + + return usb_connected ? PLUGIN_USB_CONNECTED : PLUGIN_OK; +} diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index 342af3a5e0..27fae18b4c 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config @@ -36,4 +36,5 @@ z80,viewers/zxbox,12 *,viewers/properties,- colours,apps/text_editor,11 ssg,games/superdom,- -link,viewers/shortcuts,- +link,viewers/shortcuts_view,- +*,viewers/shortcuts_append,- -- cgit v1.2.3