Index: apps/onplay.c =================================================================== --- apps/onplay.c (revision 14518) +++ apps/onplay.c (working copy) @@ -1082,7 +1082,7 @@ #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; Index: apps/plugins/CATEGORIES =================================================================== --- apps/plugins/CATEGORIES (revision 14518) +++ apps/plugins/CATEGORIES (working copy) @@ -58,7 +58,8 @@ rockpaint,apps search,viewers searchengine,viewers -shortcuts,viewers +shortcuts_view,viewers +shortcuts_append,viewers sliding_puzzle,games snake2,games snake,games Index: apps/plugins/shortcuts.c =================================================================== --- apps/plugins/shortcuts.c (revision 14518) +++ apps/plugins/shortcuts.c (working copy) @@ -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; -} - Index: apps/plugins/shortcuts/Makefile =================================================================== --- apps/plugins/shortcuts/Makefile (revision 0) +++ apps/plugins/shortcuts/Makefile (revision 0) @@ -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) Index: apps/plugins/shortcuts/shortcuts.h =================================================================== --- apps/plugins/shortcuts/shortcuts.h (revision 0) +++ apps/plugins/shortcuts/shortcuts.h (revision 0) @@ -0,0 +1,66 @@ +/*************************************************************************** + * __________ __ ___. + * 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; + struct sc_entry_s* next; +} sc_entry_t; + + +void sc_alloc_init(void); +void* sc_malloc(unsigned int size); + + +bool load_sc_file(char* filename, bool must_exist); +enum plugin_status write_sc_file(char *filename); +void append_entry_to_the_list(sc_entry_t *entry); +sc_entry_t *get_entry(int entry_index); /* 0 based index */ +void get_dir_name(char *file_or_dir, char *dir); + + +#ifdef SC_DEBUG +void print_list(sc_entry_t *head); +#endif + +extern sc_entry_t* shortcuts; +extern int total_entries; +extern bool user_file; +extern char *link_filename; + + +#endif Index: apps/plugins/shortcuts/shortcuts_append.c =================================================================== --- apps/plugins/shortcuts/shortcuts_append.c (revision 0) +++ apps/plugins/shortcuts/shortcuts_append.c (revision 0) @@ -0,0 +1,108 @@ +/*************************************************************************** + * __________ __ ___. + * 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 plugin_status append_entry_to_file(char *fname, char *path, bool is_dir); + +static char str_dirname[MAX_PATH+1]; + + +enum plugin_status append_entry_to_file(char *fname, char *path, bool is_dir) { + DEBUGF("Appending '%s' to the file '%s'\n", path, fname); + if (!load_sc_file(fname, false)) { + return PLUGIN_ERROR; + } + sc_entry_t *entry = (sc_entry_t*)sc_malloc(sizeof(sc_entry_t)); + if (entry == NULL) { + return PLUGIN_ERROR; + } + 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)) { + rb->splash(HZ*2, "Can't append %s, it's too long", path); + return PLUGIN_ERROR; + } + entry->explicit_disp = false; + rb->strcpy(entry->path, path); + if (is_dir) { + rb->strcat(entry->path, PATH_SEPARATOR); + } + append_entry_to_the_list(entry); + return write_sc_file(fname); +} + + +enum plugin_status plugin_start(struct plugin_api* api, void* void_parameter) { + rb = api; + bool found; + + DIR* dir; + struct dirent* entry; + 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); + + /* Initialise the plugin buffer */ + sc_alloc_init(); + + /* determine if it's a file or a directory */ + get_dir_name(parameter, str_dirname); + char *fname = parameter + rb->strlen(str_dirname); + DEBUGF("Dir: '%s', file: '%s'\n", str_dirname, fname); + + found = false; + dir = rb->opendir(str_dirname); + if (dir) { + while(0 != (entry = rb->readdir(dir))) { + if(!rb->strcmp(entry->d_name, fname)) { + 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/Dir not found: %s", parameter); + return PLUGIN_ERROR; + } + + DEBUGF("'%s' is a %s\n", parameter, (its_a_dir ? "dir" : "file")); + + return append_entry_to_file(SHORTCUTS_FILENAME, parameter, its_a_dir); +} Index: apps/plugins/shortcuts/shortcuts_common.c =================================================================== --- apps/plugins/shortcuts/shortcuts_common.c (revision 0) +++ apps/plugins/shortcuts/shortcuts_common.c (revision 0) @@ -0,0 +1,380 @@ +/*************************************************************************** + * __________ __ ___. + * 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" + +/* + * State variables + */ +static unsigned char* mallocbuf; +static long bufsize; /* Size of mallocbuf */ +static long mem_ptr; /* Index of the next free byte */ + +sc_entry_t *shortcuts; +int total_entries; +char *link_filename; +bool user_file; +int show_last_segments; + +static sc_entry_t *lastentry; + +bool parse_entry_content(char *line, sc_entry_t *entry); +char *last_segments(char *path, int nsegm); +bool is_control(char *line); +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 sc_alloc_init(void){ + ssize_t bufleft; + mem_ptr = 0; + + mallocbuf = rb->plugin_get_buffer(&bufleft); + bufsize = (long)bufleft; + + rb->memset(mallocbuf, 0, bufsize); +} + +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; +} + +void sc_content_init(void){ + shortcuts = NULL; + lastentry = NULL; + mem_ptr = 0; + total_entries = 0; + show_last_segments = -1; +} + +sc_entry_t *get_entry(int entry_index) { + sc_entry_t *entry = shortcuts; + int i = 0; + while (i < entry_index && entry != NULL) { + entry = entry->next; + i++; + } + return entry; +} + +bool load_sc_file(char *filename, bool must_exist) { + 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, so reset the memory contents */ + sc_content_init(); + + 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)) { + continue; + } + if (!(entry = (sc_entry_t*)sc_malloc(sizeof(sc_entry_t)))){ + rb->splash(HZ*2, "Couldn't get memory for a new entry"); + goto end_of_proc; + } + if (!parse_entry_content(sc_content, entry)) { + /* Could not parse the entry (too long path?) -> ignore */ + continue; + } + DEBUGF("Parsed entry: path=%s, disp=%s\n", entry->path, entry->disp); + append_entry_to_the_list(entry); + } + +#ifdef SC_DEBUG + print_list(shortcuts); +#endif + + user_file = (rb->strcmp(filename, SHORTCUTS_FILENAME) != 0); + link_filename = filename; + 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_list(sc_entry_t *head) { + int i = 0; + sc_entry_t *entry = head; + while (entry != NULL) { + i++; + if (entry->explicit_disp) { + DEBUGF("%2d. '%s', show as '%s'\n", i, entry->path, entry->disp); + } else { + DEBUGF("%2d. '%s' (%s)\n", i, entry->path, entry->disp); + } + entry = entry->next; + } +} +#endif + +void append_entry_to_the_list(sc_entry_t *entry) { + entry->next = NULL; /* This is the last entry */ + if (shortcuts == NULL) { + /* The list is empty, 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++; + + /* Now we can make last look at this entry, + * ready for the next one */ + lastentry = entry; +} + +bool parse_entry_content(char *line, sc_entry_t *entry) { + 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 (show_last_segments <= 0) { + disp = path; + } else { + disp = last_segments(line, show_last_segments); + } + 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) { + 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)) { + show_last_segments = rb->atoi(value); + DEBUGF("Set show last segms to %d\n", 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; +} + +enum plugin_status write_sc_file(char *filename) { + DEBUGF("Dumping shortcuts to the file '%s'\n", filename); + int fd; + sc_entry_t *entry; + int entry_count; + + /* 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_RDWR); + if (fd < 0) { + rb->splash(HZ*2, "Could not open shortcuts file %s for writing", + filename); + return PLUGIN_ERROR; + } + + /* truncate the current file, since we're writing it all over again */ + rb->ftruncate(fd, 0); + + /* + * 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, show_last_segments); + + entry = shortcuts; + entry_count = 0; + while (entry != NULL) { + write_entry_to_file(fd, entry); + entry_count++; + entry = entry->next; + } + + rb->close(fd); + DEBUGF("Dumped %d entries to the file '%s'\n", entry_count, filename); + + return PLUGIN_OK; +} + +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"); +} + +void get_dir_name(char *file_or_dir, char *dir) { + /*strip trailing slashes */ + char* ptr = rb->strrchr(file_or_dir, '/') + 1; + int dirlen = (ptr - file_or_dir); + rb->strncpy(dir, file_or_dir, dirlen); + dir[dirlen] = '\0'; +} Index: apps/plugins/shortcuts/shortcuts_view.c =================================================================== --- apps/plugins/shortcuts/shortcuts_view.c (revision 0) +++ apps/plugins/shortcuts/shortcuts_view.c (revision 0) @@ -0,0 +1,255 @@ +/*************************************************************************** + * __________ __ ___. + * 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, +}; + + +static char str_dirname[MAX_PATH+1]; +static int gselected_item = 0; + + +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 append_entry_to_the_list(sc_entry_t *entry); + +void delete_sc(int sc_num); +enum plugin_status list_sc(bool is_editable); +bool dir_exists(char* dir); +bool goto_dir(char *file_or_dir); + + +bool dir_exists(char *dir) { + DIR* d = rb->opendir(dir); + bool retval; + if (d != NULL) { + rb->closedir(d); + retval = true; + } else { + retval = false; + } + DEBUGF("Checked existence of the dir '%s': %s\n", + dir, (retval ? "found" : "NOT FOUND")); + return retval; +} + +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; + } + } +} + +char* build_sc_list(int selected_item, void* data, char* buffer) { + int i; + sc_entry_t* temp_node = (sc_entry_t*)data; + char text_buffer[MAX_PATH]; + + for (i=0; inext; + } + if (temp_node == NULL){ + return NULL; + } + rb->snprintf(text_buffer, sizeof(text_buffer), "%s", temp_node->disp); + 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_entry_t* current = shortcuts; + sc_entry_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 = NULL; + } 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; + } + } +} + +enum plugin_status 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; + sc_entry_t *selected_entry; + + 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, + (is_editable?"Shortcuts (editable)":"Shortcuts (sealed)"),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: + selected_entry = get_entry(selected_item); + if (selected_entry != NULL) { + if (!goto_dir(selected_entry->path)) { + return PLUGIN_ERROR; + } + return PLUGIN_OK; + } + break; + case SCLA_DELETE: + rb->splash(HZ, "Deleting item"); + delete_sc(selected_item); + return write_sc_file(link_filename); + case SCLA_NONE: + return PLUGIN_OK; + } + return PLUGIN_OK; +} + +bool goto_dir(char *file_or_dir) { + DEBUGF("Trying to go to '%s'...\n", file_or_dir); + + get_dir_name(file_or_dir, str_dirname); + DEBUGF("Dir from '%s': '%s'\n", file_or_dir, str_dirname); + + if (!dir_exists(str_dirname)) { + rb->splash(HZ*2, "File / Directory no longer exists on disk"); + 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(str_dirname); + return true; +} + +enum plugin_status plugin_start(struct plugin_api* api, void* void_parameter) { + rb = api; + + /* 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; + + /* Initialise the plugin buffer */ + sc_alloc_init(); + + if (!load_sc_file(parameter, true)) { + DEBUGF("Could not load %s\n", parameter); + return PLUGIN_ERROR; + } + if (total_entries==0) { + rb->splash(HZ*2, "No shortcuts in the file!"); + return PLUGIN_OK; + } else if ((total_entries==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' */ + if (!goto_dir(shortcuts->path)) { + return PLUGIN_ERROR; + } + return PLUGIN_OK; + } + /* Display a menu to choose between the entries */ + return list_sc(!user_file); +} Index: apps/plugins/SOURCES =================================================================== --- apps/plugins/SOURCES (revision 14518) +++ apps/plugins/SOURCES (working copy) @@ -13,7 +13,6 @@ rockblox.c rockbox_flash.c search.c -shortcuts.c snow.c sort.c stats.c Index: apps/plugins/SUBDIRS =================================================================== --- apps/plugins/SUBDIRS (revision 14518) +++ apps/plugins/SUBDIRS (working copy) @@ -1,6 +1,7 @@ #ifndef IRIVER_IFP7XX_SERIES /* For all targets */ +shortcuts /* For various targets... */ Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (revision 14518) +++ apps/plugins/viewers.config (working copy) @@ -36,4 +36,5 @@ *,viewers/properties,- colours,apps/text_editor,11 ssg,games/superdom,- -link,viewers/shortcuts,- +link,viewers/shortcuts_view,- +*,viewers/shortcuts_append,-