diff options
author | Björn Stenberg <bjorn@haxx.se> | 2005-01-17 11:39:46 +0000 |
---|---|---|
committer | Björn Stenberg <bjorn@haxx.se> | 2005-01-17 11:39:46 +0000 |
commit | 8a5de5fec96171e739442b0601047d93079e3179 (patch) | |
tree | 5d0a66bea94f7296aaaacc6d42ceecb84e736242 /apps/filetree.c | |
parent | fc53fd708fc12e1a67217c102ea8180b2bde6a6f (diff) |
Added ID3 database support. Still very early.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@5575 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/filetree.c')
-rw-r--r-- | apps/filetree.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/apps/filetree.c b/apps/filetree.c new file mode 100644 index 0000000000..98d59b9e9a --- /dev/null +++ b/apps/filetree.c @@ -0,0 +1,509 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Björn Stenberg + * + * 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 <stdlib.h> +#include <file.h> +#include <dir.h> +#include <string.h> +#include <kernel.h> +#include <lcd.h> +#include <debug.h> +#include <font.h> +#include "bookmark.h" +#include "tree.h" +#include "settings.h" +#include "filetypes.h" +#include "talk.h" +#include "playlist.h" +#include "wps-display.h" +#include "lang.h" +#include "language.h" +#include "screens.h" +#include "plugin.h" +#include "rolo.h" + +static int boot_size = 0; +static int boot_cluster; +extern bool boot_changed; + +int ft_build_playlist(struct tree_context* c, int start_index) +{ + int i; + int start=start_index; + + struct entry *dircache = c->dircache; + + for(i = 0;i < c->filesindir;i++) + { + if((dircache[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA) + { + DEBUGF("Adding %s\n", dircache[i].name); + if (playlist_add(dircache[i].name) < 0) + break; + } + else + { + /* Adjust the start index when se skip non-MP3 entries */ + if(i < start) + start_index--; + } + } + + return start_index; +} + +/* walk a directory and check all dircache entries if a .talk file exists */ +static void check_file_thumbnails(struct tree_context* c) +{ + int i; + struct dirent *entry; + struct entry* dircache = c->dircache; + DIR *dir; + + dir = opendir(c->currdir); + if(!dir) + return; + + for (i=0; i < c->filesindir; i++) /* mark all files as non talking, except the .talk ones */ + { + if (dircache[i].attr & ATTR_DIRECTORY) + continue; /* we're not touching directories */ + + if (strcasecmp(file_thumbnail_ext, + &dircache[i].name[strlen(dircache[i].name) + - strlen(file_thumbnail_ext)])) + { /* no .talk file */ + dircache[i].attr &= ~TREE_ATTR_THUMBNAIL; /* clear */ + } + else + { /* .talk file, we later let them speak themselves */ + dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set */ + } + } + + while((entry = readdir(dir)) != 0) /* walk directory */ + { + int ext_pos; + + ext_pos = strlen(entry->d_name) - strlen(file_thumbnail_ext); + if (ext_pos <= 0 /* too short to carry ".talk" */ + || (entry->attribute & ATTR_DIRECTORY) /* no file */ + || strcasecmp(&entry->d_name[ext_pos], file_thumbnail_ext)) + { /* or doesn't end with ".talk", no candidate */ + continue; + } + + /* terminate the (disposable) name in dir buffer, + this truncates off the ".talk" without needing an extra buffer */ + entry->d_name[ext_pos] = '\0'; + + /* search corresponding file in dir cache */ + for (i=0; i < c->filesindir; i++) + { + if (!strcasecmp(dircache[i].name, entry->d_name)) + { /* match */ + dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */ + break; /* exit search loop, because we found it */ + } + } + } + closedir(dir); +} + +/* support function for qsort() */ +static int compare(const void* p1, const void* p2) +{ + struct entry* e1 = (struct entry*)p1; + struct entry* e2 = (struct entry*)p2; + int criteria; + + if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY) + { /* two directories */ + criteria = global_settings.sort_dir; + } + else if (!(e1->attr & ATTR_DIRECTORY) && !(e2->attr & ATTR_DIRECTORY)) + { /* two files */ + criteria = global_settings.sort_file; + } + else /* dir and file, dir goes first */ + return ( e2->attr & ATTR_DIRECTORY ) - ( e1->attr & ATTR_DIRECTORY ); + + switch(criteria) + { + case 3: /* sort type */ + { + int t1 = e1->attr & TREE_ATTR_MASK; + int t2 = e2->attr & TREE_ATTR_MASK; + + if (!t1) /* unknown type */ + t1 = 0x7FFFFFFF; /* gets a high number, to sort after known */ + if (!t2) /* unknown type */ + t2 = 0x7FFFFFFF; /* gets a high number, to sort after known */ + + if (t1 - t2) /* if different */ + return t1 - t2; + /* else fall through to alphabetical sorting */ + } + case 0: /* sort alphabetically */ + if (global_settings.sort_case) + return strncmp(e1->name, e2->name, MAX_PATH); + else + return strncasecmp(e1->name, e2->name, MAX_PATH); + + case 1: /* sort date */ + return e1->time_write - e2->time_write; + + case 2: /* sort date, newest first */ + return e2->time_write - e1->time_write; + } + return 0; /* never reached */ +} + +/* load and sort directory into dircache. returns NULL on failure. */ +int ft_load(struct tree_context* c, bool *buffer_full) +{ + extern char lastdir[]; /* from tree.c */ + int i; + DIR *dir = opendir(c->currdir); + if(!dir) + return -1; /* not a directory */ + + int name_buffer_used = 0; + c->dirsindir = 0; + if (buffer_full) + *buffer_full = false; + + for ( i=0; i < global_settings.max_files_in_dir; i++ ) { + int len; + struct dirent *entry = readdir(dir); + struct entry* dptr = (struct entry*)(c->dircache + i * sizeof(struct entry)); + if (!entry) + break; + + len = strlen(entry->d_name); + + /* skip directories . and .. */ + if ((entry->attribute & ATTR_DIRECTORY) && + (((len == 1) && + (!strncmp(entry->d_name, ".", 1))) || + ((len == 2) && + (!strncmp(entry->d_name, "..", 2))))) { + i--; + continue; + } + + /* Skip FAT volume ID */ + if (entry->attribute & ATTR_VOLUME_ID) { + i--; + continue; + } + + /* filter out dotfiles and hidden files */ + if (*c->dirfilter != SHOW_ALL && + ((entry->d_name[0]=='.') || + (entry->attribute & ATTR_HIDDEN))) { + i--; + continue; + } + + dptr->attr = entry->attribute; + + /* check for known file types */ + if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) ) + dptr->attr |= filetype_get_attr(entry->d_name); + + /* memorize/compare details about the boot file */ + if ((c->currdir[1] == 0) && !strcasecmp(entry->d_name, BOOTFILE)) { + if (boot_size) { + if ((entry->size != boot_size) || + (entry->startcluster != boot_cluster)) + boot_changed = true; + } + boot_size = entry->size; + boot_cluster = entry->startcluster; + } + + /* filter out non-visible files */ + if (!(dptr->attr & ATTR_DIRECTORY) && ( + (*c->dirfilter == SHOW_PLAYLIST && + (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) || + ((*c->dirfilter == SHOW_MUSIC && + (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MPA) && + (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) || + (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)) || + (*c->dirfilter == SHOW_WPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_WPS) || + (*c->dirfilter == SHOW_CFG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_CFG) || + (*c->dirfilter == SHOW_LNG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_LNG) || + (*c->dirfilter == SHOW_MOD && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MOD) || + (*c->dirfilter == SHOW_FONT && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_FONT) || + (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_ROCK))) + { + i--; + continue; + } + + if (len > c->name_buffer_size - name_buffer_used - 1) { + /* Tell the world that we ran out of buffer space */ + if (buffer_full) + *buffer_full = true; + break; + } + dptr->name = &c->name_buffer[name_buffer_used]; + dptr->time_write = entry->wrtdate<<16 | entry->wrttime; /* in one # */ + strcpy(dptr->name,entry->d_name); + name_buffer_used += len + 1; + + if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */ + c->dirsindir++; + } + c->filesindir = i; + closedir(dir); + + strcpy(lastdir, c->currdir); + + qsort(c->dircache,i,sizeof(struct entry),compare); + + /* If thumbnail talking is enabled, make an extra run to mark files with + associated thumbnails, so we don't do unsuccessful spinups later. */ + if (global_settings.talk_file == 3) + check_file_thumbnails(c); /* map .talk to ours */ + + return 0; +} + +int ft_enter(struct tree_context* c) +{ + int rc = 0; + char buf[MAX_PATH]; + struct entry *dircache = c->dircache; + struct entry* file = &dircache[c->dircursor + c->dirstart]; + bool reload_root = false; + bool reload_dir = false; + bool start_wps = false; + bool exit_func = false; + + if (c->currdir[1]) + snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name); + else + snprintf(buf,sizeof(buf),"/%s",file->name); + + if (file->attr & ATTR_DIRECTORY) { + memcpy(c->currdir, buf, sizeof(c->currdir)); + if ( c->dirlevel < MAX_DIR_LEVELS ) { + c->dirpos[c->dirlevel] = c->dirstart; + c->cursorpos[c->dirlevel] = c->dircursor; + } + c->dirlevel++; + c->dircursor=0; + c->dirstart=0; + } + else { + int seed = current_tick; + bool play = false; + int start_index=0; + + lcd_stop_scroll(); + switch ( file->attr & TREE_ATTR_MASK ) { + case TREE_ATTR_M3U: + if (bookmark_autoload(buf)) + break; + + if (playlist_create(c->currdir, file->name) != -1) + { + if (global_settings.playlist_shuffle) + playlist_shuffle(seed, -1); + start_index = 0; + playlist_start(start_index,0); + play = true; + } + break; + + case TREE_ATTR_MPA: + if (bookmark_autoload(c->currdir)) + break; + + if (playlist_create(c->currdir, NULL) != -1) + { + start_index = + ft_build_playlist(c, c->dircursor + c->dirstart); + if (global_settings.playlist_shuffle) + { + start_index = playlist_shuffle(seed, start_index); + + /* when shuffling dir.: play all files + even if the file selected by user is + not the first one */ + if (!global_settings.play_selected) + start_index = 0; + } + + playlist_start(start_index, 0); + play = true; + } + break; + + /* wps config file */ + case TREE_ATTR_WPS: + wps_load(buf,true); + set_file(buf, global_settings.wps_file, + MAX_FILENAME); + break; + + case TREE_ATTR_CFG: + if (!settings_load_config(buf)) + break; + lcd_clear_display(); + lcd_puts(0,0,str(LANG_SETTINGS_LOADED1)); + lcd_puts(0,1,str(LANG_SETTINGS_LOADED2)); +#ifdef HAVE_LCD_BITMAP + lcd_update(); +#endif + sleep(HZ/2); + break; + + case TREE_ATTR_BMARK: + bookmark_load(buf, false); + reload_dir = true; + break; + + case TREE_ATTR_LNG: + if(!lang_load(buf)) { + extern bool language_changed; /* from settings_menu.c */ + + set_file(buf, global_settings.lang_file, + MAX_FILENAME); + talk_init(); /* use voice of same language */ + splash(HZ, true, str(LANG_LANGUAGE_LOADED)); + language_changed = true; + } + break; + +#ifdef HAVE_LCD_BITMAP + case TREE_ATTR_FONT: + font_load(buf); + set_file(buf, global_settings.font_file, MAX_FILENAME); + break; +#endif + +#ifndef SIMULATOR + /* firmware file */ + case TREE_ATTR_MOD: + rolo_load(buf); + break; +#endif + + /* plugin file */ + case TREE_ATTR_ROCK: + if (plugin_load(buf,NULL) == PLUGIN_USB_CONNECTED) + { + if(*c->dirfilter > NUM_FILTER_MODES) + /* leave sub-browsers after usb, doing + otherwise might be confusing to the user */ + exit_func = true; + else + reload_root = true; + } + break; + + default: + { + char* plugin = filetype_get_plugin(file); + if (plugin) + { + if (plugin_load(plugin,buf) == PLUGIN_USB_CONNECTED) + reload_root = true; + } + break; + } + } + + if ( play ) { + if ( global_settings.resume ) { + /* the resume_index must always be the index in the + shuffled list in case shuffle is enabled */ + global_settings.resume_index = start_index; + global_settings.resume_offset = 0; + settings_save(); + } + + start_wps = true; + } + else { + if (*c->dirfilter > NUM_FILTER_MODES && + *c->dirfilter != SHOW_FONT && + *c->dirfilter != SHOW_PLUGINS) + { + exit_func = true; + } + } + } + + if (reload_dir) + rc = 1; + if (reload_root) + rc = 2; + if (start_wps) + rc = 3; + if (exit_func) + rc = 4; + + return rc; +} + +int ft_exit(struct tree_context* c) +{ + extern char lastfile[]; /* from tree.c */ + char buf[MAX_PATH]; + int rc = 0; + bool exit_func = false; + + int i = strlen(c->currdir); + if (i>1) { + while (c->currdir[i-1]!='/') + i--; + strcpy(buf,&c->currdir[i]); + if (i==1) + c->currdir[i]=0; + else + c->currdir[i-1]=0; + + if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1) + exit_func = true; + + c->dirlevel--; + if ( c->dirlevel < MAX_DIR_LEVELS ) { + c->dirstart = c->dirpos[c->dirlevel]; + c->dircursor = c->cursorpos[c->dirlevel]; + } + else + c->dirstart = c->dircursor = 0; + + if (c->dirstart == -1) + strcpy(lastfile, buf); + } + else + { + if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1) + exit_func = true; + } + + if (exit_func) + rc = 4; + + return rc; +} |