forked from len0rd/rockbox
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
This commit is contained in:
parent
9b3be37f84
commit
946a815cd4
11 changed files with 927 additions and 554 deletions
|
|
@ -1082,7 +1082,7 @@ MENUITEM_FUNCTION(set_recdir_item, 0, ID2P(LANG_SET_AS_REC_DIR),
|
||||||
#endif
|
#endif
|
||||||
static bool add_to_faves(void)
|
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))
|
selected_file))
|
||||||
onplay_result = ONPLAY_RELOAD_DIR;
|
onplay_result = ONPLAY_RELOAD_DIR;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@ rocklife,games
|
||||||
rockpaint,apps
|
rockpaint,apps
|
||||||
search,viewers
|
search,viewers
|
||||||
searchengine,viewers
|
searchengine,viewers
|
||||||
shortcuts,viewers
|
shortcuts_view,viewers
|
||||||
|
shortcuts_append,viewers
|
||||||
sliding_puzzle,games
|
sliding_puzzle,games
|
||||||
snake2,games
|
snake2,games
|
||||||
snake,games
|
snake,games
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ random_folder_advance_config.c
|
||||||
rockblox.c
|
rockblox.c
|
||||||
rockbox_flash.c
|
rockbox_flash.c
|
||||||
search.c
|
search.c
|
||||||
shortcuts.c
|
|
||||||
snow.c
|
snow.c
|
||||||
sort.c
|
sort.c
|
||||||
stats.c
|
stats.c
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef IRIVER_IFP7XX_SERIES
|
#ifndef IRIVER_IFP7XX_SERIES
|
||||||
|
|
||||||
/* For all targets */
|
/* For all targets */
|
||||||
|
shortcuts
|
||||||
|
|
||||||
/* For various targets... */
|
/* For various targets... */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;i<selected_item && temp_node != NULL;i++){
|
|
||||||
temp_node = temp_node->next;
|
|
||||||
}
|
|
||||||
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;i<sc_num && current != NULL;i++){
|
|
||||||
/* keep previous pointing at the prior
|
|
||||||
* item in the list */
|
|
||||||
if(previous!=current){
|
|
||||||
previous = current;
|
|
||||||
}
|
|
||||||
current=current->next;
|
|
||||||
}
|
|
||||||
/* 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;i<MAX_SHORTCUTS && temp_node != NULL;i++){
|
|
||||||
rb->snprintf(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
90
apps/plugins/shortcuts/Makefile
Normal file
90
apps/plugins/shortcuts/Makefile
Normal file
|
|
@ -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)
|
||||||
90
apps/plugins/shortcuts/shortcuts.h
Normal file
90
apps/plugins/shortcuts/shortcuts.h
Normal file
|
|
@ -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
|
||||||
109
apps/plugins/shortcuts/shortcuts_append.c
Normal file
109
apps/plugins/shortcuts/shortcuts_append.c
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
394
apps/plugins/shortcuts/shortcuts_common.c
Normal file
394
apps/plugins/shortcuts/shortcuts_common.c
Normal file
|
|
@ -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; i<file->entry_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; i<file->entry_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;
|
||||||
|
}
|
||||||
238
apps/plugins/shortcuts/shortcuts_view.c
Normal file
238
apps/plugins/shortcuts/shortcuts_view.c
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -36,4 +36,5 @@ z80,viewers/zxbox,12
|
||||||
*,viewers/properties,-
|
*,viewers/properties,-
|
||||||
colours,apps/text_editor,11
|
colours,apps/text_editor,11
|
||||||
ssg,games/superdom,-
|
ssg,games/superdom,-
|
||||||
link,viewers/shortcuts,-
|
link,viewers/shortcuts_view,-
|
||||||
|
*,viewers/shortcuts_append,-
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue