diff --git a/apps/filetypes.c b/apps/filetypes.c index 0aceb74910..573e640da1 100644 --- a/apps/filetypes.c +++ b/apps/filetypes.c @@ -56,7 +56,7 @@ #endif /* string buffer length */ -#define STRING_BUFFER_SIZE 512 +#define STRING_BUFFER_SIZE 548 /* number of bytes for the binary icon */ #define ICON_LENGTH 6 diff --git a/apps/lang/english.lang b/apps/lang/english.lang index 83543d3725..9dee957717 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -10358,3 +10358,17 @@ *: "AGC maximum gain" + + id: LANG_PROPERTIES + desc: browser file/dir properties + user: + + *: "Properties" + + + *: "Properties" + + + *: "Properties" + + diff --git a/apps/onplay.c b/apps/onplay.c index 59fa8caa85..90117eaa19 100644 --- a/apps/onplay.c +++ b/apps/onplay.c @@ -560,6 +560,14 @@ bool create_dir(void) return true; } +static bool properties(void) +{ + if(PLUGIN_USB_CONNECTED == filetype_load_plugin("properties", + selected_file)) + onplay_result = ONPLAY_RELOAD_DIR; + return false; +} + /* Store the current selection in the clipboard */ static bool clipboard_clip(bool copy) { @@ -966,7 +974,12 @@ int onplay(char* file, int attr, int from) items[i].desc = ID2P(LANG_CREATE_DIR); items[i].function = create_dir; i++; + + items[i].desc = ID2P(LANG_PROPERTIES); + items[i].function = properties; + i++; } + if (context == CONTEXT_WPS) { #ifdef HAVE_PITCHSCREEN diff --git a/apps/plugin.c b/apps/plugin.c index e74e4a7d42..268fee5fd8 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -192,6 +192,7 @@ static const struct plugin_api rockbox_api = { gui_synclist_scroll_left, #endif gui_synclist_do_button, + gui_synclist_set_title, /* button */ button_get, @@ -229,6 +230,13 @@ static const struct plugin_api rockbox_api = { PREFIX(readdir), PREFIX(mkdir), PREFIX(rmdir), + + /* dir, cached */ +#ifdef HAVE_DIRCACHE + opendir_cached, + readdir_cached, + closedir_cached, +#endif /* kernel/ system */ PREFIX(sleep), diff --git a/apps/plugin.h b/apps/plugin.h index ba0fdf0c29..22f6b97fce 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -37,6 +37,9 @@ #include "config.h" #include "system.h" #include "dir.h" +#ifndef SIMULATOR +#include "dircache.h" +#endif #include "kernel.h" #include "thread.h" #include "button.h" @@ -107,12 +110,12 @@ #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 37 +#define PLUGIN_API_VERSION 38 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 37 +#define PLUGIN_MIN_API_VERSION 38 /* plugin return codes */ enum plugin_status { @@ -281,6 +284,7 @@ struct plugin_api { #endif unsigned (*gui_synclist_do_button)(struct gui_synclist * lists, unsigned button,enum list_wrap wrap); + void (*gui_synclist_set_title)(struct gui_synclist *lists, char* title, ICON icon); /* button */ long (*button_get)(bool block); @@ -318,6 +322,12 @@ struct plugin_api { struct dirent* (*PREFIX(readdir))(DIR* dir); int (*PREFIX(mkdir))(const char *name, int mode); int (*PREFIX(rmdir))(const char *name); + /* dir, cached */ +#ifdef HAVE_DIRCACHE + DIRCACHED* (*opendir_cached)(const char* name); + struct dircache_entry* (*readdir_cached)(DIRCACHED* dir); + int (*closedir_cached)(DIRCACHED* dir); +#endif /* kernel/ system */ void (*PREFIX(sleep))(int ticks); diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index 63cfa8a52f..cf34127f73 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -7,6 +7,7 @@ dict.c firmware_flash.c logo.c mosaique.c +properties.c random_folder_advance_config.c #if (LCD_WIDTH != 240) && ((LCD_WIDTH != 128) || (LCD_HEIGHT != 64)) && !defined(SANSA_E200) rockblox.c diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c new file mode 100644 index 0000000000..458ee22cd6 --- /dev/null +++ b/apps/plugins/properties.c @@ -0,0 +1,359 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Peter D'Hoye + * + * 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; + +bool its_a_dir = false; + +char str_filename[MAX_PATH]; +char str_dirname[MAX_PATH]; +char str_size[64]; +char str_dircount[64]; +char str_filecount[64]; +char str_date[64]; +char str_time[64]; + +static char* filesize2string(long long size, char* pstr, int len) +{ + /* margin set at 10K boundary: 10239 B +1 => 10 KB + routine below is 200 bytes smaller than cascaded if/else :) + not using build-in function because of huge values (long long) */ + const char* kgb[4] = { "B", "KB", "MB", "GB" }; + int i = 0; + while(true) + { + if((size < 10240) || (i > 2)) + { + /* depends on the order in the above array */ + rb->snprintf(pstr, len, "%ld %s", (long)size, kgb[i]); + break; + } + size >>= 10; /* div by 1024 */ + i++; + } + return pstr; +} + +static bool file_properties(char* selected_file) +{ + bool found = false; + char tstr[MAX_PATH]; +#ifdef HAVE_DIRCACHE + DIRCACHED* dir; + struct dircache_entry* entry; +#else + DIR* dir; + struct dirent* entry; +#endif + char* ptr = rb->strrchr(selected_file, '/') + 1; + int dirlen = (ptr - selected_file); + rb->strncpy(tstr, selected_file, dirlen); + tstr[dirlen] = 0; + +#ifdef HAVE_DIRCACHE + dir = rb->opendir_cached(tstr); +#else + dir = rb->opendir(tstr); +#endif + if (dir) + { +#ifdef HAVE_DIRCACHE + while(0 != (entry = rb->readdir_cached(dir))) +#else + while(0 != (entry = rb->readdir(dir))) +#endif + { + if(!rb->strcmp(entry->d_name, selected_file+dirlen)) + { + rb->snprintf(str_dirname, sizeof str_dirname, "Path: %s", + tstr); + rb->snprintf(str_filename, sizeof str_filename, "Name: %s", + selected_file+dirlen); + rb->snprintf(str_size, sizeof str_size, "Size: %s", + filesize2string(entry->size, tstr, sizeof tstr)); + rb->snprintf(str_date, sizeof str_date, "Date: %04d/%02d/%02d", + ((entry->wrtdate >> 9 ) & 0x7F) + 1980, /* year */ + ((entry->wrtdate >> 5 ) & 0x0F), /* month */ + ((entry->wrtdate ) & 0x1F)); /* day */ + rb->snprintf(str_time, sizeof str_time, "Time: %02d:%02d", + ((entry->wrttime >> 11) & 0x1F), /* hour */ + ((entry->wrttime >> 5 ) & 0x3F)); /* minutes */ + found = true; + break; + } + } +#ifdef HAVE_DIRCACHE + rb->closedir_cached(dir); +#else + rb->closedir(dir); +#endif + } + return found; +} + +typedef struct { + char dirname[MAX_PATH]; + int len; + unsigned int dc; + unsigned int fc; + long long bc; + char tstr[64]; + char tstr2[64]; +} DPS; + +static bool _dir_properties(DPS* dps) +{ + /* recursively scan directories in search of files + and informs the user of the progress */ + bool result; + int dirlen; +#ifdef HAVE_DIRCACHE + DIRCACHED* dir; + struct dircache_entry* entry; +#else + DIR* dir; + struct dirent* entry; +#endif + + result = 0; + dirlen = rb->strlen(dps->dirname); +#ifdef HAVE_DIRCACHE + dir = rb->opendir_cached(dps->dirname); +#else + dir = rb->opendir(dps->dirname); +#endif + if (!dir) + return false; /* open error */ + + /* walk through the directory content */ +#ifdef HAVE_DIRCACHE + while((!result) && (0 != (entry = rb->readdir_cached(dir)))) +#else + while((!result) && (0 != (entry = rb->readdir(dir)))) +#endif + { + /* append name to current directory */ + rb->snprintf(dps->dirname+dirlen, dps->len-dirlen, "/%s", + entry->d_name); + + if (entry->attribute & ATTR_DIRECTORY) + { + if (!rb->strcmp((char *)entry->d_name, ".") || + !rb->strcmp((char *)entry->d_name, "..")) + continue; /* skip these */ + + dps->dc++; /* new directory */ + rb->lcd_puts(0,0,"SCANNING..."); + rb->lcd_clear_display(); + rb->lcd_puts(0,1,dps->dirname); + rb->lcd_puts(0,2,entry->d_name); + rb->snprintf(dps->tstr, 64, "Directories: %d", dps->dc); + rb->lcd_puts(0,3,dps->tstr); + rb->snprintf(dps->tstr, 64, "Files: %d", dps->fc); + rb->lcd_puts(0,4,dps->tstr); + rb->snprintf(dps->tstr, 64, "Size: %s", + filesize2string(dps->bc, dps->tstr2, 64)); + rb->lcd_puts(0,5,dps->tstr); + rb->lcd_update(); + + /* recursion */ + result = _dir_properties(dps); + if(rb->get_action(CONTEXT_TREE,TIMEOUT_NOBLOCK)) + result = false; + } + else + { + dps->fc++; /* new file */ + dps->bc += entry->size; + } + rb->yield(); + } +#ifdef HAVE_DIRCACHE + rb->closedir_cached(dir); +#else + rb->closedir(dir); +#endif + + if(rb->action_userabort(0)) result = false; + + return result; +} + +static bool dir_properties(char* selected_file) +{ + DPS dps; + char tstr[64]; + rb->strncpy(dps.dirname, selected_file, MAX_PATH); + dps.len = MAX_PATH; + dps.dc = 0; + dps.fc = 0; + dps.bc = 0; + if(_dir_properties(&dps)) + return true; + + rb->snprintf(str_dirname, MAX_PATH, selected_file); + rb->snprintf(str_dircount, sizeof str_dircount, "Subdirs: %d", dps.dc); + rb->snprintf(str_filecount, sizeof str_filecount, "Files: %d", dps.fc); + rb->snprintf(str_size, sizeof str_size, "Size: %s", + filesize2string(dps.bc, tstr, sizeof tstr)); + return true; +} + +char * get_props(int selected_item, void* data, char *buffer) +{ + (void)data; + + switch(selected_item) + { + case 0: + rb->strcpy(buffer, str_dirname); + break; + case 1: + rb->strcpy(buffer, its_a_dir ? str_dircount : str_filename); + break; + case 2: + rb->strcpy(buffer, its_a_dir ? str_filecount : str_size); + break; + case 3: + rb->strcpy(buffer, its_a_dir ? str_size : str_date); + break; + case 4: + rb->strcpy(buffer, its_a_dir ? "" : str_time); + break; + default: + rb->strcpy(buffer, "ERROR"); + break; + } + return buffer; +} + +enum plugin_status plugin_start(struct plugin_api* api, void* file) +{ + rb = api; + + struct gui_synclist properties_lists; + int button; + bool prev_show_statusbar; + bool quit = false; + + /* determine if it's a file or a directory */ + bool found = false; +#ifdef HAVE_DIRCACHE + DIRCACHED* dir; + struct dircache_entry* entry; +#else + DIR* dir; + struct dirent* entry; +#endif + char* ptr = rb->strrchr((char*)file, '/') + 1; + int dirlen = (ptr - (char*)file); + rb->strncpy(str_dirname, (char*)file, dirlen); + str_dirname[dirlen] = 0; + +#ifdef HAVE_DIRCACHE + dir = rb->opendir_cached(str_dirname); +#else + dir = rb->opendir(str_dirname); +#endif + if (dir) + { +#ifdef HAVE_DIRCACHE + while(0 != (entry = rb->readdir_cached(dir))) +#else + while(0 != (entry = rb->readdir(dir))) +#endif + { + if(!rb->strcmp(entry->d_name, file+dirlen)) + { + its_a_dir = entry->attribute & ATTR_DIRECTORY ? true : false; + found = true; + break; + } + } +#ifdef HAVE_DIRCACHE + rb->closedir_cached(dir); +#else + rb->closedir(dir); +#endif + } + /* now we know if it's a file or a dir or maybe something failed */ + + if(!found) + { + /* weird: we couldn't find the entry. This Should Never Happen (TM) */ + rb->lcd_clear_display(); + rb->lcd_puts(0,0,"File/Dir not found:"); + rb->lcd_puts(0,1,(char*)file); + rb->lcd_update(); + rb->action_userabort(TIMEOUT_BLOCK); + return PLUGIN_OK; + } + + /* get the info */ + if(its_a_dir) + { + if(!dir_properties((char*)file)) + return PLUGIN_OK; + } + else + { + if(!file_properties((char*)file)) + return PLUGIN_OK; + } + + /* prepare the list for the user */ + prev_show_statusbar = rb->global_settings->statusbar; + rb->global_settings->statusbar = false; + + rb->gui_synclist_init(&properties_lists, &get_props, file, false, 1); + rb->gui_synclist_set_title(&properties_lists, its_a_dir ? + "Directory properties" : + "File properties", NOICON); + rb->gui_synclist_set_icon_callback(&properties_lists, NULL); + rb->gui_synclist_set_nb_items(&properties_lists, its_a_dir ? 4 : 5); + rb->gui_synclist_limit_scroll(&properties_lists, true); + rb->gui_synclist_select_item(&properties_lists, 0); + rb->gui_synclist_draw(&properties_lists); + + while(!quit) + { + button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK); + if (rb->gui_synclist_do_button(&properties_lists,button,LIST_WRAP_ON)) + continue; + switch(button) + { + case ACTION_STD_CANCEL: + quit = true; + break; + default: + if (rb->default_event_handler(button) == SYS_USB_CONNECTED) + { + rb->global_settings->statusbar = prev_show_statusbar; + return PLUGIN_USB_CONNECTED; + } + } + } + rb->global_settings->statusbar = prev_show_statusbar; + rb->action_signalscreenchange(); + + return PLUGIN_OK; +} diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config index d9ecd6bbc8..35ad7088e4 100644 --- a/apps/plugins/viewers.config +++ b/apps/plugins/viewers.config @@ -27,3 +27,4 @@ tap,viewers/zxbox,66 52 4A 66 52 4A sna,viewers/zxbox,66 52 4A 66 52 4A tzx,viewers/zxbox,66 52 4A 66 52 4A z80,viewers/zxbox,66 52 4A 66 52 4A +zzz,viewers/properties,00 00 00 00 00 00 diff --git a/docs/CREDITS b/docs/CREDITS index b87ac32463..b673e44a3f 100644 --- a/docs/CREDITS +++ b/docs/CREDITS @@ -250,3 +250,4 @@ Travis Hyyppa Ian Webber Pavel Gnelitsa Lutz Böhne +Will Robertson