summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/SOURCES2
-rw-r--r--apps/dbtree.c343
-rw-r--r--apps/dbtree.h39
-rw-r--r--apps/filetree.c509
-rw-r--r--apps/filetree.h31
-rw-r--r--apps/playlist.c24
-rw-r--r--apps/tree.c1221
-rw-r--r--apps/tree.h33
8 files changed, 1388 insertions, 814 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index c834839474..edb3bb3dd2 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -24,6 +24,8 @@ sound_menu.c
24status.c 24status.c
25talk.c 25talk.c
26tree.c 26tree.c
27dbtree.c
28filetree.c
27wps-display.c 29wps-display.c
28wps.c 30wps.c
29#ifdef HAVE_LCD_CHARCELLS 31#ifdef HAVE_LCD_CHARCELLS
diff --git a/apps/dbtree.c b/apps/dbtree.c
new file mode 100644
index 0000000000..f9bd950057
--- /dev/null
+++ b/apps/dbtree.c
@@ -0,0 +1,343 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Björn Stenberg
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdio.h>
20#include <string.h>
21#include "file.h"
22#include "screens.h"
23#include "kernel.h"
24#include "tree.h"
25#include "lcd.h"
26#include "font.h"
27#include "settings.h"
28#include "icons.h"
29#include "status.h"
30#include "debug.h"
31#include "button.h"
32#include "menu.h"
33#include "main_menu.h"
34#include "mpeg.h"
35#include "misc.h"
36#include "ata.h"
37#include "wps.h"
38#include "filetypes.h"
39#include "applimits.h"
40#include "dbtree.h"
41#include "icons.h"
42
43#ifdef LITTLE_ENDIAN
44#include <netinet/in.h>
45#define BE32(_x_) htonl(_x_)
46#else
47#define BE32(_x_) _x_
48#endif
49
50static int fd;
51
52static int
53 songstart, albumstart, artiststart,
54 songcount, albumcount, artistcount,
55 songlen, songarraylen,
56 albumlen, albumarraylen,
57 artistlen;
58
59int db_init(void)
60{
61 unsigned int version;
62 unsigned int buf[12];
63
64 fd = open(ROCKBOX_DIR "/rockbox.id3db", O_RDONLY);
65 if (fd < 0) {
66 DEBUGF("Failed opening database\n");
67 return -1;
68 }
69 read(fd, buf, 48);
70
71 version = BE32(buf[0]) & 0xff;
72 DEBUGF("Version: RDB%d\n", version);
73
74 songstart = BE32(buf[1]);
75 songcount = BE32(buf[2]);
76 songlen = BE32(buf[3]);
77 DEBUGF("Number of songs: %d\n", songcount);
78 DEBUGF("Songstart: %x\n", songstart);
79 DEBUGF("Songlen: %d\n", songlen);
80
81 albumstart = BE32(buf[4]);
82 albumcount = BE32(buf[5]);
83 albumlen = BE32(buf[6]);
84 songarraylen = BE32(buf[7]);
85 DEBUGF("Number of albums: %d\n", albumcount);
86 DEBUGF("Albumstart: %x\n", albumstart);
87 DEBUGF("Albumlen: %d\n", albumlen);
88
89 artiststart = BE32(buf[8]);
90 artistcount = BE32(buf[9]);
91 artistlen = BE32(buf[10]);
92 albumarraylen = BE32(buf[11]);
93 DEBUGF("Number of artists: %d\n", artistcount);
94 DEBUGF("Artiststart: %x\n", artiststart);
95 DEBUGF("Artistlen: %d\n", artistlen);
96
97 return 0;
98}
99
100int db_load(struct tree_context* c, bool* dir_buffer_full)
101{
102 int i, offset, len, rc;
103 int dcachesize = global_settings.max_files_in_dir * sizeof(struct entry);
104 int max_items, itemcount, stringlen;
105 unsigned int* nptr = (void*) c->name_buffer;
106 unsigned int* dptr = c->dircache;
107 unsigned int* safeplace = NULL;
108
109 int table = c->currtable;
110 int extra = c->currextra;
111 c->dentry_size = 2 * sizeof(int);
112
113 DEBUGF("db_load(%d, %x)\n", table, extra);
114
115 if (!table) {
116 table = allartists;
117 c->currtable = table;
118 }
119
120 switch (table) {
121 case allsongs:
122 offset = songstart;
123 itemcount = songcount;
124 stringlen = songlen;
125 break;
126
127 case allalbums:
128 offset = albumstart;
129 itemcount = albumcount;
130 stringlen = albumlen;
131 break;
132
133 case allartists:
134 offset = artiststart;
135 itemcount = artistcount;
136 stringlen = artistlen;
137 break;
138
139 case albums:
140 /* 'extra' is offset to the artist */
141 len = albumarraylen * 4;
142 safeplace = (void*)(c->name_buffer + c->name_buffer_size - len);
143 //DEBUGF("Seeking to %x\n", extra + artistlen);
144 lseek(fd, extra + artistlen, SEEK_SET);
145 rc = read(fd, safeplace, len);
146 if (rc < len)
147 return -1;
148
149#ifdef LITTLE_ENDIAN
150 for (i=0; i<albumarraylen; i++)
151 safeplace[i] = BE32(safeplace[i]);
152#endif
153
154 offset = safeplace[0];
155 itemcount = albumarraylen;
156 stringlen = albumlen;
157 break;
158
159 case songs:
160 /* 'extra' is offset to the album */
161 len = songarraylen * 4;
162 safeplace = (void*)(c->name_buffer + c->name_buffer_size - len);
163 //DEBUGF("Seeking to %x\n", extra + albumlen + 4);
164 lseek(fd, extra + albumlen + 4, SEEK_SET);
165 rc = read(fd, safeplace, len);
166 if (rc < len)
167 return -1;
168
169#ifdef LITTLE_ENDIAN
170 for (i=0; i<songarraylen; i++)
171 safeplace[i] = BE32(safeplace[i]);
172#endif
173 offset = safeplace[0];
174 itemcount = songarraylen;
175 stringlen = songlen;
176 break;
177
178 default:
179 DEBUGF("Unsupported table %d\n", table);
180 return -1;
181 }
182 max_items = dcachesize / c->dentry_size;
183
184 if (!safeplace) {
185 //DEBUGF("Seeking to %x\n", offset);
186 lseek(fd, offset, SEEK_SET);
187 }
188
189 /* name_buffer (nptr) contains only names, null terminated.
190 the first word of dcache (dptr) is a pointer to the name,
191 the rest is table specific. see below. */
192
193 if (itemcount > max_items)
194 if (dir_buffer_full)
195 *dir_buffer_full = true;
196
197 if (max_items > itemcount) {
198 max_items = itemcount;
199 }
200
201 for ( i=0; i < max_items; i++ ) {
202 int rc, skip=0;
203
204 if (safeplace) {
205 if (!safeplace[i])
206 break;
207 //DEBUGF("Seeking to %x\n", safeplace[i]);
208 lseek(fd, safeplace[i], SEEK_SET);
209 offset = safeplace[i];
210 }
211
212 /* read name */
213 rc = read(fd, nptr, stringlen);
214 if (rc < stringlen)
215 {
216 DEBUGF("%d read(%d) returned %d\n", i, stringlen, rc);
217 return -1;
218 }
219
220 /* store name pointer in dir cache */
221 dptr[0] = (unsigned int)nptr;
222
223 switch (table) {
224 case songs:
225 case allsongs:
226 /* save offset of this song */
227 skip = 12;
228 dptr[1] = offset;
229 break;
230
231 case allalbums:
232 case albums:
233 /* save offset of this album */
234 skip = songarraylen * 4 + 4;
235 dptr[1] = offset;
236 break;
237
238 case allartists:
239 /* save offset of this artist */
240 skip = albumarraylen * 4;
241 dptr[1] = offset;
242 break;
243 }
244
245 //DEBUGF("%x: %s\n", dptr[1], dptr[0]);
246
247 if (skip)
248 lseek(fd, skip, SEEK_CUR);
249
250 /* next name is stored immediately after this */
251 nptr = (void*)nptr + strlen((char*)nptr) + 1;
252 if ((void*)nptr > (void*)c->name_buffer + c->name_buffer_size) {
253 DEBUGF("Name buffer overflow (%d)\n",i);
254 break;
255 }
256 dptr = (void*)dptr + c->dentry_size;
257
258 if (!safeplace)
259 offset += stringlen + skip;
260 }
261
262 c->filesindir = i;
263
264 return i;
265}
266
267void db_enter(struct tree_context* c)
268{
269 switch (c->currtable) {
270 case allartists:
271 case albums:
272 c->dirpos[c->dirlevel] = c->dirstart;
273 c->cursorpos[c->dirlevel] = c->dircursor;
274 c->table_history[c->dirlevel] = c->currtable;
275 c->extra_history[c->dirlevel] = c->currextra;
276 c->dirlevel++;
277 break;
278
279 default:
280 break;
281 }
282
283 switch (c->currtable) {
284 case allartists:
285 c->currtable = albums;
286 c->currextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1];
287 break;
288
289 case albums:
290 c->currtable = songs;
291 c->currextra = ((int*)c->dircache)[(c->dircursor + c->dirstart)*2 + 1];
292 break;
293
294 case songs:
295 splash(HZ,true,"No playing implemented yet");
296#if 0
297 /* find filenames, build playlist, play */
298 playlist_create(NULL,NULL);
299#endif
300 break;
301
302 default:
303 break;
304 }
305
306 c->dirstart = c->dircursor = 0;
307}
308
309void db_exit(struct tree_context* c)
310{
311 c->dirlevel--;
312 c->dirstart = c->dirpos[c->dirlevel];
313 c->dircursor = c->cursorpos[c->dirlevel];
314 c->currtable = c->table_history[c->dirlevel];
315 c->currextra = c->extra_history[c->dirlevel];
316}
317
318#ifdef HAVE_LCD_BITMAP
319const char* db_get_icon(struct tree_context* c)
320{
321 int icon;
322
323 switch (c->currtable)
324 {
325 case allsongs:
326 case songs:
327 icon = File;
328 break;
329
330 default:
331 icon = Folder;
332 break;
333 }
334
335 return bitmap_icons_6x8[icon];
336}
337#else
338int db_get_icon(struct tree_context* c)
339{
340 (void)c;
341 return Folder;
342}
343#endif
diff --git a/apps/dbtree.h b/apps/dbtree.h
new file mode 100644
index 0000000000..7664715775
--- /dev/null
+++ b/apps/dbtree.h
@@ -0,0 +1,39 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Björn Stenberg
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef DBTREE_H
20#define DBTREE_H
21
22#include "tree.h"
23
24enum table { invalid, allsongs, allalbums, allartists, albums, songs };
25
26int db_init(void);
27void db_enter(struct tree_context* c);
28void db_exit(struct tree_context* c);
29int db_load(struct tree_context* c,
30 bool* dir_buffer_full);
31#ifdef HAVE_LCD_BITMAP
32const char* db_get_icon(struct tree_context* c);
33#else
34int db_get_icon(struct tree_context* c);
35#endif
36
37
38#endif
39
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Björn Stenberg
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include <stdlib.h>
20#include <file.h>
21#include <dir.h>
22#include <string.h>
23#include <kernel.h>
24#include <lcd.h>
25#include <debug.h>
26#include <font.h>
27#include "bookmark.h"
28#include "tree.h"
29#include "settings.h"
30#include "filetypes.h"
31#include "talk.h"
32#include "playlist.h"
33#include "wps-display.h"
34#include "lang.h"
35#include "language.h"
36#include "screens.h"
37#include "plugin.h"
38#include "rolo.h"
39
40static int boot_size = 0;
41static int boot_cluster;
42extern bool boot_changed;
43
44int ft_build_playlist(struct tree_context* c, int start_index)
45{
46 int i;
47 int start=start_index;
48
49 struct entry *dircache = c->dircache;
50
51 for(i = 0;i < c->filesindir;i++)
52 {
53 if((dircache[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
54 {
55 DEBUGF("Adding %s\n", dircache[i].name);
56 if (playlist_add(dircache[i].name) < 0)
57 break;
58 }
59 else
60 {
61 /* Adjust the start index when se skip non-MP3 entries */
62 if(i < start)
63 start_index--;
64 }
65 }
66
67 return start_index;
68}
69
70/* walk a directory and check all dircache entries if a .talk file exists */
71static void check_file_thumbnails(struct tree_context* c)
72{
73 int i;
74 struct dirent *entry;
75 struct entry* dircache = c->dircache;
76 DIR *dir;
77
78 dir = opendir(c->currdir);
79 if(!dir)
80 return;
81
82 for (i=0; i < c->filesindir; i++) /* mark all files as non talking, except the .talk ones */
83 {
84 if (dircache[i].attr & ATTR_DIRECTORY)
85 continue; /* we're not touching directories */
86
87 if (strcasecmp(file_thumbnail_ext,
88 &dircache[i].name[strlen(dircache[i].name)
89 - strlen(file_thumbnail_ext)]))
90 { /* no .talk file */
91 dircache[i].attr &= ~TREE_ATTR_THUMBNAIL; /* clear */
92 }
93 else
94 { /* .talk file, we later let them speak themselves */
95 dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set */
96 }
97 }
98
99 while((entry = readdir(dir)) != 0) /* walk directory */
100 {
101 int ext_pos;
102
103 ext_pos = strlen(entry->d_name) - strlen(file_thumbnail_ext);
104 if (ext_pos <= 0 /* too short to carry ".talk" */
105 || (entry->attribute & ATTR_DIRECTORY) /* no file */
106 || strcasecmp(&entry->d_name[ext_pos], file_thumbnail_ext))
107 { /* or doesn't end with ".talk", no candidate */
108 continue;
109 }
110
111 /* terminate the (disposable) name in dir buffer,
112 this truncates off the ".talk" without needing an extra buffer */
113 entry->d_name[ext_pos] = '\0';
114
115 /* search corresponding file in dir cache */
116 for (i=0; i < c->filesindir; i++)
117 {
118 if (!strcasecmp(dircache[i].name, entry->d_name))
119 { /* match */
120 dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */
121 break; /* exit search loop, because we found it */
122 }
123 }
124 }
125 closedir(dir);
126}
127
128/* support function for qsort() */
129static int compare(const void* p1, const void* p2)
130{
131 struct entry* e1 = (struct entry*)p1;
132 struct entry* e2 = (struct entry*)p2;
133 int criteria;
134
135 if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY)
136 { /* two directories */
137 criteria = global_settings.sort_dir;
138 }
139 else if (!(e1->attr & ATTR_DIRECTORY) && !(e2->attr & ATTR_DIRECTORY))
140 { /* two files */
141 criteria = global_settings.sort_file;
142 }
143 else /* dir and file, dir goes first */
144 return ( e2->attr & ATTR_DIRECTORY ) - ( e1->attr & ATTR_DIRECTORY );
145
146 switch(criteria)
147 {
148 case 3: /* sort type */
149 {
150 int t1 = e1->attr & TREE_ATTR_MASK;
151 int t2 = e2->attr & TREE_ATTR_MASK;
152
153 if (!t1) /* unknown type */
154 t1 = 0x7FFFFFFF; /* gets a high number, to sort after known */
155 if (!t2) /* unknown type */
156 t2 = 0x7FFFFFFF; /* gets a high number, to sort after known */
157
158 if (t1 - t2) /* if different */
159 return t1 - t2;
160 /* else fall through to alphabetical sorting */
161 }
162 case 0: /* sort alphabetically */
163 if (global_settings.sort_case)
164 return strncmp(e1->name, e2->name, MAX_PATH);
165 else
166 return strncasecmp(e1->name, e2->name, MAX_PATH);
167
168 case 1: /* sort date */
169 return e1->time_write - e2->time_write;
170
171 case 2: /* sort date, newest first */
172 return e2->time_write - e1->time_write;
173 }
174 return 0; /* never reached */
175}
176
177/* load and sort directory into dircache. returns NULL on failure. */
178int ft_load(struct tree_context* c, bool *buffer_full)
179{
180 extern char lastdir[]; /* from tree.c */
181 int i;
182 DIR *dir = opendir(c->currdir);
183 if(!dir)
184 return -1; /* not a directory */
185
186 int name_buffer_used = 0;
187 c->dirsindir = 0;
188 if (buffer_full)
189 *buffer_full = false;
190
191 for ( i=0; i < global_settings.max_files_in_dir; i++ ) {
192 int len;
193 struct dirent *entry = readdir(dir);
194 struct entry* dptr = (struct entry*)(c->dircache + i * sizeof(struct entry));
195 if (!entry)
196 break;
197
198 len = strlen(entry->d_name);
199
200 /* skip directories . and .. */
201 if ((entry->attribute & ATTR_DIRECTORY) &&
202 (((len == 1) &&
203 (!strncmp(entry->d_name, ".", 1))) ||
204 ((len == 2) &&
205 (!strncmp(entry->d_name, "..", 2))))) {
206 i--;
207 continue;
208 }
209
210 /* Skip FAT volume ID */
211 if (entry->attribute & ATTR_VOLUME_ID) {
212 i--;
213 continue;
214 }
215
216 /* filter out dotfiles and hidden files */
217 if (*c->dirfilter != SHOW_ALL &&
218 ((entry->d_name[0]=='.') ||
219 (entry->attribute & ATTR_HIDDEN))) {
220 i--;
221 continue;
222 }
223
224 dptr->attr = entry->attribute;
225
226 /* check for known file types */
227 if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) )
228 dptr->attr |= filetype_get_attr(entry->d_name);
229
230 /* memorize/compare details about the boot file */
231 if ((c->currdir[1] == 0) && !strcasecmp(entry->d_name, BOOTFILE)) {
232 if (boot_size) {
233 if ((entry->size != boot_size) ||
234 (entry->startcluster != boot_cluster))
235 boot_changed = true;
236 }
237 boot_size = entry->size;
238 boot_cluster = entry->startcluster;
239 }
240
241 /* filter out non-visible files */
242 if (!(dptr->attr & ATTR_DIRECTORY) && (
243 (*c->dirfilter == SHOW_PLAYLIST &&
244 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) ||
245 ((*c->dirfilter == SHOW_MUSIC &&
246 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MPA) &&
247 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) ||
248 (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)) ||
249 (*c->dirfilter == SHOW_WPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_WPS) ||
250 (*c->dirfilter == SHOW_CFG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_CFG) ||
251 (*c->dirfilter == SHOW_LNG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_LNG) ||
252 (*c->dirfilter == SHOW_MOD && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MOD) ||
253 (*c->dirfilter == SHOW_FONT && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_FONT) ||
254 (*c->dirfilter == SHOW_PLUGINS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_ROCK)))
255 {
256 i--;
257 continue;
258 }
259
260 if (len > c->name_buffer_size - name_buffer_used - 1) {
261 /* Tell the world that we ran out of buffer space */
262 if (buffer_full)
263 *buffer_full = true;
264 break;
265 }
266 dptr->name = &c->name_buffer[name_buffer_used];
267 dptr->time_write = entry->wrtdate<<16 | entry->wrttime; /* in one # */
268 strcpy(dptr->name,entry->d_name);
269 name_buffer_used += len + 1;
270
271 if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */
272 c->dirsindir++;
273 }
274 c->filesindir = i;
275 closedir(dir);
276
277 strcpy(lastdir, c->currdir);
278
279 qsort(c->dircache,i,sizeof(struct entry),compare);
280
281 /* If thumbnail talking is enabled, make an extra run to mark files with
282 associated thumbnails, so we don't do unsuccessful spinups later. */
283 if (global_settings.talk_file == 3)
284 check_file_thumbnails(c); /* map .talk to ours */
285
286 return 0;
287}
288
289int ft_enter(struct tree_context* c)
290{
291 int rc = 0;
292 char buf[MAX_PATH];
293 struct entry *dircache = c->dircache;
294 struct entry* file = &dircache[c->dircursor + c->dirstart];
295 bool reload_root = false;
296 bool reload_dir = false;
297 bool start_wps = false;
298 bool exit_func = false;
299
300 if (c->currdir[1])
301 snprintf(buf,sizeof(buf),"%s/%s",c->currdir, file->name);
302 else
303 snprintf(buf,sizeof(buf),"/%s",file->name);
304
305 if (file->attr & ATTR_DIRECTORY) {
306 memcpy(c->currdir, buf, sizeof(c->currdir));
307 if ( c->dirlevel < MAX_DIR_LEVELS ) {
308 c->dirpos[c->dirlevel] = c->dirstart;
309 c->cursorpos[c->dirlevel] = c->dircursor;
310 }
311 c->dirlevel++;
312 c->dircursor=0;
313 c->dirstart=0;
314 }
315 else {
316 int seed = current_tick;
317 bool play = false;
318 int start_index=0;
319
320 lcd_stop_scroll();
321 switch ( file->attr & TREE_ATTR_MASK ) {
322 case TREE_ATTR_M3U:
323 if (bookmark_autoload(buf))
324 break;
325
326 if (playlist_create(c->currdir, file->name) != -1)
327 {
328 if (global_settings.playlist_shuffle)
329 playlist_shuffle(seed, -1);
330 start_index = 0;
331 playlist_start(start_index,0);
332 play = true;
333 }
334 break;
335
336 case TREE_ATTR_MPA:
337 if (bookmark_autoload(c->currdir))
338 break;
339
340 if (playlist_create(c->currdir, NULL) != -1)
341 {
342 start_index =
343 ft_build_playlist(c, c->dircursor + c->dirstart);
344 if (global_settings.playlist_shuffle)
345 {
346 start_index = playlist_shuffle(seed, start_index);
347
348 /* when shuffling dir.: play all files
349 even if the file selected by user is
350 not the first one */
351 if (!global_settings.play_selected)
352 start_index = 0;
353 }
354
355 playlist_start(start_index, 0);
356 play = true;
357 }
358 break;
359
360 /* wps config file */
361 case TREE_ATTR_WPS:
362 wps_load(buf,true);
363 set_file(buf, global_settings.wps_file,
364 MAX_FILENAME);
365 break;
366
367 case TREE_ATTR_CFG:
368 if (!settings_load_config(buf))
369 break;
370 lcd_clear_display();
371 lcd_puts(0,0,str(LANG_SETTINGS_LOADED1));
372 lcd_puts(0,1,str(LANG_SETTINGS_LOADED2));
373#ifdef HAVE_LCD_BITMAP
374 lcd_update();
375#endif
376 sleep(HZ/2);
377 break;
378
379 case TREE_ATTR_BMARK:
380 bookmark_load(buf, false);
381 reload_dir = true;
382 break;
383
384 case TREE_ATTR_LNG:
385 if(!lang_load(buf)) {
386 extern bool language_changed; /* from settings_menu.c */
387
388 set_file(buf, global_settings.lang_file,
389 MAX_FILENAME);
390 talk_init(); /* use voice of same language */
391 splash(HZ, true, str(LANG_LANGUAGE_LOADED));
392 language_changed = true;
393 }
394 break;
395
396#ifdef HAVE_LCD_BITMAP
397 case TREE_ATTR_FONT:
398 font_load(buf);
399 set_file(buf, global_settings.font_file, MAX_FILENAME);
400 break;
401#endif
402
403#ifndef SIMULATOR
404 /* firmware file */
405 case TREE_ATTR_MOD:
406 rolo_load(buf);
407 break;
408#endif
409
410 /* plugin file */
411 case TREE_ATTR_ROCK:
412 if (plugin_load(buf,NULL) == PLUGIN_USB_CONNECTED)
413 {
414 if(*c->dirfilter > NUM_FILTER_MODES)
415 /* leave sub-browsers after usb, doing
416 otherwise might be confusing to the user */
417 exit_func = true;
418 else
419 reload_root = true;
420 }
421 break;
422
423 default:
424 {
425 char* plugin = filetype_get_plugin(file);
426 if (plugin)
427 {
428 if (plugin_load(plugin,buf) == PLUGIN_USB_CONNECTED)
429 reload_root = true;
430 }
431 break;
432 }
433 }
434
435 if ( play ) {
436 if ( global_settings.resume ) {
437 /* the resume_index must always be the index in the
438 shuffled list in case shuffle is enabled */
439 global_settings.resume_index = start_index;
440 global_settings.resume_offset = 0;
441 settings_save();
442 }
443
444 start_wps = true;
445 }
446 else {
447 if (*c->dirfilter > NUM_FILTER_MODES &&
448 *c->dirfilter != SHOW_FONT &&
449 *c->dirfilter != SHOW_PLUGINS)
450 {
451 exit_func = true;
452 }
453 }
454 }
455
456 if (reload_dir)
457 rc = 1;
458 if (reload_root)
459 rc = 2;
460 if (start_wps)
461 rc = 3;
462 if (exit_func)
463 rc = 4;
464
465 return rc;
466}
467
468int ft_exit(struct tree_context* c)
469{
470 extern char lastfile[]; /* from tree.c */
471 char buf[MAX_PATH];
472 int rc = 0;
473 bool exit_func = false;
474
475 int i = strlen(c->currdir);
476 if (i>1) {
477 while (c->currdir[i-1]!='/')
478 i--;
479 strcpy(buf,&c->currdir[i]);
480 if (i==1)
481 c->currdir[i]=0;
482 else
483 c->currdir[i-1]=0;
484
485 if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1)
486 exit_func = true;
487
488 c->dirlevel--;
489 if ( c->dirlevel < MAX_DIR_LEVELS ) {
490 c->dirstart = c->dirpos[c->dirlevel];
491 c->dircursor = c->cursorpos[c->dirlevel];
492 }
493 else
494 c->dirstart = c->dircursor = 0;
495
496 if (c->dirstart == -1)
497 strcpy(lastfile, buf);
498 }
499 else
500 {
501 if (*c->dirfilter > NUM_FILTER_MODES && c->dirlevel < 1)
502 exit_func = true;
503 }
504
505 if (exit_func)
506 rc = 4;
507
508 return rc;
509}
diff --git a/apps/filetree.h b/apps/filetree.h
new file mode 100644
index 0000000000..c1ba5681a8
--- /dev/null
+++ b/apps/filetree.h
@@ -0,0 +1,31 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 by Björn Stenberg
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef FILETREE_H
20#define FILETREE_H
21#include "tree.h"
22
23int ft_load(struct tree_context* c, bool *buffer_full);
24int ft_play_filenumber(int pos, int attr);
25int ft_play_dirname(int start_index);
26void ft_play_filename(char *dir, char *file);
27int ft_enter(struct tree_context* c);
28int ft_exit(struct tree_context* c);
29int ft_build_playlist(struct tree_context* c, int start_index);
30
31#endif
diff --git a/apps/playlist.c b/apps/playlist.c
index 3b96225b17..1a68a052ba 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -84,7 +84,7 @@
84#include "atoi.h" 84#include "atoi.h"
85#include "misc.h" 85#include "misc.h"
86#include "button.h" 86#include "button.h"
87#include "tree.h" 87#include "filetree.h"
88#ifdef HAVE_LCD_BITMAP 88#ifdef HAVE_LCD_BITMAP
89#include "icons.h" 89#include "icons.h"
90#include "widgets.h" 90#include "widgets.h"
@@ -537,16 +537,18 @@ static int add_directory_to_playlist(struct playlist_info* playlist,
537 char *count_str; 537 char *count_str;
538 int result = 0; 538 int result = 0;
539 int num_files = 0; 539 int num_files = 0;
540 bool buffer_full = false;
541 int i; 540 int i;
542 int dirfilter = SHOW_ALL; 541 int dirfilter = global_settings.dirfilter;
543 struct entry *files; 542 struct entry *files;
544 543
545 /* use the tree browser dircache to load files */ 544 /* use the tree browser dircache to load files */
546 files = load_and_sort_directory(dirname, &dirfilter, &num_files, 545 global_settings.dirfilter = SHOW_ALL;
547 &buffer_full); 546 struct tree_context* tc = tree_get_context();
547 strncpy(tc->currdir, dirname, sizeof(tc->currdir));
548 num_files = ft_load(tc, NULL);
549 files = (struct entry*) tc->dircache;
548 550
549 if(!files) 551 if(!num_files)
550 { 552 {
551 splash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR)); 553 splash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
552 return 0; 554 return 0;
@@ -582,9 +584,10 @@ static int add_directory_to_playlist(struct playlist_info* playlist,
582 break; 584 break;
583 585
584 /* we now need to reload our current directory */ 586 /* we now need to reload our current directory */
585 files = load_and_sort_directory(dirname, &dirfilter, &num_files, 587 strncpy(tc->currdir, dirname, sizeof(tc->currdir));
586 &buffer_full); 588 num_files = ft_load(tc, NULL);
587 if (!files) 589 files = (struct entry*) tc->dircache;
590 if (!num_files)
588 { 591 {
589 result = -1; 592 result = -1;
590 break; 593 break;
@@ -627,6 +630,9 @@ static int add_directory_to_playlist(struct playlist_info* playlist,
627 } 630 }
628 } 631 }
629 632
633 /* restore dirfilter */
634 global_settings.dirfilter = dirfilter;
635
630 return result; 636 return result;
631} 637}
632 638
diff --git a/apps/tree.c b/apps/tree.c
index 9b4f6898de..eb1f7c3b74 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -57,18 +57,13 @@
57#include "talk.h" 57#include "talk.h"
58#include "filetypes.h" 58#include "filetypes.h"
59#include "misc.h" 59#include "misc.h"
60#include "filetree.h"
61#include "dbtree.h"
60 62
61#ifdef HAVE_LCD_BITMAP 63#ifdef HAVE_LCD_BITMAP
62#include "widgets.h" 64#include "widgets.h"
63#define BOOTFILE "ajbrec.ajz"
64#else
65#define BOOTFILE "archos.mod"
66#endif 65#endif
67 66
68/* This flag is set by dirbrowse() if a new language is loaded.
69 It is defined in settings_menu.c */
70extern bool language_changed;
71
72/* a table for the know file types */ 67/* a table for the know file types */
73const struct filetype filetypes[] = { 68const struct filetype filetypes[] = {
74 { ".mp3", TREE_ATTR_MPA, File, VOICE_EXT_MPA }, 69 { ".mp3", TREE_ATTR_MPA, File, VOICE_EXT_MPA },
@@ -92,33 +87,17 @@ const struct filetype filetypes[] = {
92#endif /* #ifndef SIMULATOR */ 87#endif /* #ifndef SIMULATOR */
93}; 88};
94 89
95/* Boot value of global_settings.max_files_in_dir */ 90static struct tree_context tc;
96static int max_files_in_dir; 91
97 92bool boot_changed = false;
98static char *name_buffer; 93
99static int name_buffer_size; /* Size of allocated buffer */ 94char lastfile[MAX_PATH];
100static int name_buffer_length; /* Currently used amount */ 95char lastdir[MAX_PATH];
101 96
102static struct entry *dircache;
103
104static int dircursor;
105static int dirstart;
106static int dirlevel;
107static int filesindir;
108static int dirsindir; /* how many of the dircache entries are directories */
109static int dirpos[MAX_DIR_LEVELS];
110static int cursorpos[MAX_DIR_LEVELS];
111static char lastdir[MAX_PATH];
112static char lastfile[MAX_PATH];
113static char currdir[MAX_PATH];
114static char currdir_save[MAX_PATH];
115static bool reload_dir = false; 97static bool reload_dir = false;
116static int boot_size = 0;
117static int boot_cluster;
118static bool boot_changed = false;
119 98
120static bool start_wps = false; 99static bool start_wps = false;
121static bool dirbrowse(const char *root, const int *dirfilter); 100static bool dirbrowse(void);
122 101
123bool check_rockboxdir(void) 102bool check_rockboxdir(void)
124{ 103{
@@ -140,11 +119,13 @@ void browse_root(void)
140 filetype_init(); 119 filetype_init();
141 check_rockboxdir(); 120 check_rockboxdir();
142 121
122 strcpy(tc.currdir, "/");
123
143#ifndef SIMULATOR 124#ifndef SIMULATOR
144 dirbrowse("/", &global_settings.dirfilter); 125 dirbrowse();
145 126
146#else 127#else
147 if (!dirbrowse("/", &global_settings.dirfilter)) { 128 if (!dirbrowse()) {
148 DEBUGF("No filesystem found. Have you forgotten to create it?\n"); 129 DEBUGF("No filesystem found. Have you forgotten to create it?\n");
149 } 130 }
150#endif 131#endif
@@ -156,11 +137,16 @@ void tree_get_filetypes(const struct filetype** types, int* count)
156 *count = sizeof(filetypes) / sizeof(*filetypes); 137 *count = sizeof(filetypes) / sizeof(*filetypes);
157} 138}
158 139
140struct tree_context* tree_get_context(void)
141{
142 return &tc;
143}
144
159#ifdef HAVE_LCD_BITMAP 145#ifdef HAVE_LCD_BITMAP
160 146
161/* pixel margins */ 147/* pixel margins */
162#define MARGIN_X (global_settings.scrollbar && \ 148#define MARGIN_X (global_settings.scrollbar && \
163 filesindir > tree_max_on_screen ? SCROLLBAR_WIDTH : 0) + \ 149 tc.filesindir > tree_max_on_screen ? SCROLLBAR_WIDTH : 0) + \
164 CURSOR_WIDTH + (global_settings.show_icons && ICON_WIDTH > 0 ? ICON_WIDTH :0) 150 CURSOR_WIDTH + (global_settings.show_icons && ICON_WIDTH > 0 ? ICON_WIDTH :0)
165#define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0) 151#define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
166 152
@@ -169,7 +155,7 @@ void tree_get_filetypes(const struct filetype** types, int* count)
169#define LINE_Y (global_settings.statusbar ? 1 : 0) 155#define LINE_Y (global_settings.statusbar ? 1 : 0)
170 156
171#define CURSOR_X (global_settings.scrollbar && \ 157#define CURSOR_X (global_settings.scrollbar && \
172 filesindir > tree_max_on_screen ? 1 : 0) 158 tc.filesindir > tree_max_on_screen ? 1 : 0)
173#define CURSOR_Y 0 /* the cursor is not positioned in regard to 159#define CURSOR_Y 0 /* the cursor is not positioned in regard to
174 the margins, so this is the amount of lines 160 the margins, so this is the amount of lines
175 we add to the cursor Y position to position 161 we add to the cursor Y position to position
@@ -197,158 +183,8 @@ void tree_get_filetypes(const struct filetype** types, int* count)
197/* talkbox hovering delay, to avoid immediate disk activity */ 183/* talkbox hovering delay, to avoid immediate disk activity */
198#define HOVER_DELAY (HZ/2) 184#define HOVER_DELAY (HZ/2)
199 185
200static int build_playlist(int start_index) 186static void showfileline(int line, char* name, int attr, bool scroll)
201{
202 int i;
203 int start=start_index;
204
205 for(i = 0;i < filesindir;i++)
206 {
207 if((dircache[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
208 {
209 DEBUGF("Adding %s\n", dircache[i].name);
210 if (playlist_add(dircache[i].name) < 0)
211 break;
212 }
213 else
214 {
215 /* Adjust the start index when se skip non-MP3 entries */
216 if(i < start)
217 start_index--;
218 }
219 }
220
221 return start_index;
222}
223
224static int play_filenumber(int pos, int attr)
225{
226 /* try to find a voice ID for the extension, if known */
227 unsigned int j;
228 int ext_id = -1; /* default to none */
229 for (j=0; j<sizeof(filetypes)/sizeof(*filetypes); j++)
230 {
231 if (attr == filetypes[j].tree_attr)
232 {
233 ext_id = filetypes[j].voiceclip;
234 break;
235 }
236 }
237
238 talk_id(VOICE_FILE, false);
239 talk_number(pos, true);
240 talk_id(ext_id, true);
241 return 1;
242}
243
244static int play_dirname(int start_index)
245{ 187{
246 int fd;
247 char dirname_mp3_filename[MAX_PATH+1];
248
249 if (mpeg_status() & MPEG_STATUS_PLAY)
250 return 0;
251
252 snprintf(dirname_mp3_filename, sizeof(dirname_mp3_filename), "%s/%s/%s",
253 currdir, dircache[start_index].name, dir_thumbnail_name);
254
255 DEBUGF("Checking for %s\n", dirname_mp3_filename);
256
257 fd = open(dirname_mp3_filename, O_RDONLY);
258 if (fd < 0)
259 {
260 DEBUGF("Failed to find: %s\n", dirname_mp3_filename);
261 return -1;
262 }
263
264 close(fd);
265
266 DEBUGF("Found: %s\n", dirname_mp3_filename);
267
268 talk_file(dirname_mp3_filename, false);
269 return 1;
270}
271
272static void play_filename(char *dir, char *file)
273{
274 char name_mp3_filename[MAX_PATH+1];
275
276 if (mpeg_status() & MPEG_STATUS_PLAY)
277 return;
278
279 if (strcasecmp(&file[strlen(file) - strlen(file_thumbnail_ext)],
280 file_thumbnail_ext))
281 { /* file has no .talk extension */
282 snprintf(name_mp3_filename, sizeof(name_mp3_filename),
283 "%s/%s%s", dir, file, file_thumbnail_ext);
284
285 talk_file(name_mp3_filename, false);
286 }
287 else
288 { /* it already is a .talk file, play this directly */
289 snprintf(name_mp3_filename, sizeof(name_mp3_filename),
290 "%s/%s", dir, file);
291 talk_id(LANG_VOICE_DIR_HOVER, false); /* prefix it */
292 talk_file(name_mp3_filename, true);
293 }
294}
295
296static int compare(const void* p1, const void* p2)
297{
298 struct entry* e1 = (struct entry*)p1;
299 struct entry* e2 = (struct entry*)p2;
300 int criteria;
301
302 if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY)
303 { /* two directories */
304 criteria = global_settings.sort_dir;
305 if (e1->attr & ATTR_VOLUME || e2->attr & ATTR_VOLUME)
306 { /* a volume identifier is involved */
307 if (e1->attr & ATTR_VOLUME && e2->attr & ATTR_VOLUME)
308 criteria = 0; /* two volumes: sort alphabetically */
309 else /* only one is a volume: volume first */
310 return (e2->attr & ATTR_VOLUME) - (e1->attr & ATTR_VOLUME);
311 }
312 }
313 else if (!(e1->attr & ATTR_DIRECTORY) && !(e2->attr & ATTR_DIRECTORY))
314 { /* two files */
315 criteria = global_settings.sort_file;
316 }
317 else /* dir and file, dir goes first */
318 return ( e2->attr & ATTR_DIRECTORY ) - ( e1->attr & ATTR_DIRECTORY );
319
320 switch(criteria)
321 {
322 case 3: /* sort type */
323 {
324 int t1 = e1->attr & TREE_ATTR_MASK;
325 int t2 = e2->attr & TREE_ATTR_MASK;
326
327 if (!t1) /* unknown type */
328 t1 = 0x7FFFFFFF; /* gets a high number, to sort after known */
329 if (!t2) /* unknown type */
330 t2 = 0x7FFFFFFF; /* gets a high number, to sort after known */
331
332 if (t1 - t2) /* if different */
333 return t1 - t2;
334 /* else fall through to alphabetical sorting */
335 }
336 case 0: /* sort alphabetically */
337 if (global_settings.sort_case)
338 return strncmp(e1->name, e2->name, MAX_PATH);
339 else
340 return strncasecmp(e1->name, e2->name, MAX_PATH);
341 case 1: /* sort date */
342 return e1->time_write - e2->time_write;
343 case 2: /* sort date, newest first */
344 return e2->time_write - e1->time_write;
345 }
346 return 0; /* never reached */
347}
348
349static void showfileline(int line, int direntry, bool scroll, const int *dirfilter)
350{
351 char* name = dircache[direntry].name;
352 int xpos = LINE_X; 188 int xpos = LINE_X;
353 char* dotpos = NULL; 189 char* dotpos = NULL;
354 190
@@ -358,8 +194,9 @@ static void showfileline(int line, int direntry, bool scroll, const int *dirfilt
358#endif 194#endif
359 195
360 /* if any file filter is on, strip the extension */ 196 /* if any file filter is on, strip the extension */
361 if (*dirfilter != SHOW_ALL && 197 if (*tc.dirfilter != SHOW_ID3DB &&
362 !(dircache[direntry].attr & ATTR_DIRECTORY)) 198 *tc.dirfilter != SHOW_ALL &&
199 !(attr & ATTR_DIRECTORY))
363 { 200 {
364 dotpos = strrchr(name, '.'); 201 dotpos = strrchr(name, '.');
365 if (dotpos) { 202 if (dotpos) {
@@ -383,207 +220,6 @@ static void showfileline(int line, int direntry, bool scroll, const int *dirfilt
383 *dotpos = '.'; 220 *dotpos = '.';
384} 221}
385 222
386/* walk a directory and check all dircache entries if a .talk file exists */
387void check_file_thumbnails(const char *dirname, int num_files)
388{
389 int i;
390 struct dirent *entry;
391 DIR *dir;
392
393 dir = opendir(dirname);
394 if(!dir)
395 return;
396
397 for (i=0; i<num_files; i++) /* mark all files as non talking, except the .talk ones */
398 {
399 if (dircache[i].attr & ATTR_DIRECTORY)
400 continue; /* we're not touching directories */
401
402 if (strcasecmp(file_thumbnail_ext,
403 &dircache[i].name[strlen(dircache[i].name)
404 - strlen(file_thumbnail_ext)]))
405 { /* no .talk file */
406 dircache[i].attr &= ~TREE_ATTR_THUMBNAIL; /* clear */
407 }
408 else
409 { /* .talk file, we later let them speak themselves */
410 dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set */
411 }
412 }
413
414 while((entry = readdir(dir)) != 0) /* walk directory */
415 {
416 int ext_pos;
417
418 ext_pos = strlen(entry->d_name) - strlen(file_thumbnail_ext);
419 if (ext_pos <= 0 /* too short to carry ".talk" */
420 || (entry->attribute & ATTR_DIRECTORY) /* no file */
421 || strcasecmp(&entry->d_name[ext_pos], file_thumbnail_ext))
422 { /* or doesn't end with ".talk", no candidate */
423 continue;
424 }
425
426 /* terminate the (disposable) name in dir buffer,
427 this truncates off the ".talk" without needing an extra buffer */
428 entry->d_name[ext_pos] = '\0';
429
430 /* search corresponding file in dir cache */
431 for (i=0; i<num_files; i++)
432 {
433 if (!strcasecmp(dircache[i].name, entry->d_name))
434 { /* match */
435 dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */
436 break; /* exit search loop, because we found it */
437 }
438 }
439 }
440 closedir(dir);
441}
442
443/* check all dircache directories if they contain a "_dirname.talk" file */
444#if 0 /* not practical, this is too slow */
445void check_dir_thumbnails(const char *dirname, int num_files)
446{
447 int i;
448 int fd;
449 char clipfile[MAX_PATH];
450
451 for (i=0; i<num_files; i++)
452 {
453 if (!(dircache[i].attr & ATTR_DIRECTORY))
454 continue; /* only directories are interesting */
455
456 /* compose pathname of directory name clip file */
457 snprintf(clipfile, sizeof(clipfile), "%s%s/%s",
458 dirname, dircache[i].name, dir_thumbnail_name);
459
460 fd = open(clipfile, O_RDONLY); /* check if exists */
461 if (fd >= 0)
462 { /* there is one */
463 dircache[i].attr |= TREE_ATTR_THUMBNAIL; /* set the flag */
464 close(fd);
465 }
466 else
467 { /* none found, clear the flag */
468 dircache[i].attr &= ~TREE_ATTR_THUMBNAIL;
469 }
470 }
471}
472#endif /* #if 0 */
473
474/* load sorted directory into dircache. returns NULL on failure. */
475struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter,
476 int *num_files, bool *buffer_full)
477{
478 int i;
479
480 DIR *dir = opendir(dirname);
481 if(!dir)
482 return NULL; /* not a directory */
483
484 name_buffer_length = 0;
485 dirsindir = 0;
486 *buffer_full = false;
487
488 for ( i=0; i < max_files_in_dir; i++ ) {
489 int len;
490 struct dirent *entry = readdir(dir);
491 struct entry* dptr = &dircache[i];
492 if (!entry)
493 break;
494
495 len = strlen(entry->d_name);
496
497 /* skip directories . and .. */
498 if ((entry->attribute & ATTR_DIRECTORY) &&
499 (((len == 1) &&
500 (!strncmp(entry->d_name, ".", 1))) ||
501 ((len == 2) &&
502 (!strncmp(entry->d_name, "..", 2))))) {
503 i--;
504 continue;
505 }
506
507 /* Skip FAT volume ID */
508 if (entry->attribute & ATTR_VOLUME_ID) {
509 i--;
510 continue;
511 }
512
513 /* filter out dotfiles and hidden files */
514 if (*dirfilter != SHOW_ALL &&
515 ((entry->d_name[0]=='.') ||
516 (entry->attribute & ATTR_HIDDEN))) {
517 i--;
518 continue;
519 }
520
521 dptr->attr = entry->attribute;
522
523 /* check for known file types */
524 if ( !(dptr->attr & ATTR_DIRECTORY) && (len > 4) )
525 dptr->attr |= filetype_get_attr(entry->d_name);
526
527 /* memorize/compare details about the boot file */
528 if ((currdir[1] == 0) && !strcasecmp(entry->d_name, BOOTFILE)) {
529 if (boot_size) {
530 if ((entry->size != boot_size) ||
531 (entry->startcluster != boot_cluster))
532 boot_changed = true;
533 }
534 boot_size = entry->size;
535 boot_cluster = entry->startcluster;
536 }
537
538 /* filter out non-visible files */
539 if (!(dptr->attr & ATTR_DIRECTORY) && (
540 (*dirfilter == SHOW_PLAYLIST &&
541 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) ||
542 ((*dirfilter == SHOW_MUSIC &&
543 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MPA) &&
544 (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_M3U) ||
545 (*dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)) ||
546 (*dirfilter == SHOW_WPS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_WPS) ||
547 (*dirfilter == SHOW_CFG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_CFG) ||
548 (*dirfilter == SHOW_LNG && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_LNG) ||
549 (*dirfilter == SHOW_MOD && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_MOD) ||
550 (*dirfilter == SHOW_FONT && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_FONT) ||
551 (*dirfilter == SHOW_PLUGINS && (dptr->attr & TREE_ATTR_MASK) != TREE_ATTR_ROCK)))
552 {
553 i--;
554 continue;
555 }
556
557 if (len > name_buffer_size - name_buffer_length - 1) {
558 /* Tell the world that we ran out of buffer space */
559 *buffer_full = true;
560 break;
561 }
562 dptr->name = &name_buffer[name_buffer_length];
563 dptr->time_write = entry->wrtdate<<16 | entry->wrttime; /* in one # */
564 strcpy(dptr->name,entry->d_name);
565 name_buffer_length += len + 1;
566
567 if (dptr->attr & ATTR_DIRECTORY) /* count the remaining dirs */
568 dirsindir++;
569 }
570 *num_files = i;
571 closedir(dir);
572 strncpy(lastdir,dirname,sizeof(lastdir));
573 lastdir[sizeof(lastdir)-1] = 0;
574 qsort(dircache,i,sizeof(struct entry),compare);
575
576 /* If thumbnail talking is enabled, make an extra run to mark files with
577 associated thumbnails, so we don't do unsuccessful spinups later. */
578 if (global_settings.talk_file == 3)
579 check_file_thumbnails(dirname, i); /* map .talk to ours */
580#if 0 /* not practical, this is too slow */
581 if (global_settings.talk_dir == 3)
582 check_dir_thumbnails(dirname, i); /* try in the directories */
583#endif /* #if 0 */
584 return dircache;
585}
586
587#ifdef HAVE_LCD_BITMAP 223#ifdef HAVE_LCD_BITMAP
588static int recalc_screen_height(void) 224static int recalc_screen_height(void)
589{ 225{
@@ -604,11 +240,12 @@ static int recalc_screen_height(void)
604} 240}
605#endif 241#endif
606 242
607static int showdir(const char *path, int start, const int *dirfilter) 243static int showdir(void)
608{ 244{
245 struct entry *dircache = tc.dircache;
609 int i; 246 int i;
610 int tree_max_on_screen; 247 int tree_max_on_screen;
611 bool dir_buffer_full; 248 bool dir_buffer_full = false;
612 249
613#ifdef HAVE_LCD_BITMAP 250#ifdef HAVE_LCD_BITMAP
614 const char* icon; 251 const char* icon;
@@ -622,24 +259,31 @@ static int showdir(const char *path, int start, const int *dirfilter)
622 int icon; 259 int icon;
623 tree_max_on_screen = TREE_MAX_ON_SCREEN; 260 tree_max_on_screen = TREE_MAX_ON_SCREEN;
624#endif 261#endif
262 int start = tc.dirstart;
263 bool id3db = global_settings.dirfilter == SHOW_ID3DB;
625 264
626 /* new dir? cache it */ 265 /* new file dir? load it */
627 if (strncmp(path,lastdir,sizeof(lastdir)) || reload_dir) { 266 if (id3db) {
628 if (!load_and_sort_directory(path, dirfilter, 267 if (db_load(&tc, &dir_buffer_full) < 0)
629 &filesindir, &dir_buffer_full))
630 return -1; 268 return -1;
269 }
270 else {
271 if (strncmp(tc.currdir, lastdir, sizeof(lastdir)) || reload_dir) {
272 if (ft_load(&tc, &dir_buffer_full) < 0)
273 return -1;
274 }
275 }
631 276
632 if ( dir_buffer_full || filesindir == max_files_in_dir ) { 277 if ( dir_buffer_full || tc.filesindir == global_settings.max_files_in_dir ) {
633#ifdef HAVE_LCD_CHARCELLS 278#ifdef HAVE_LCD_CHARCELLS
634 lcd_double_height(false); 279 lcd_double_height(false);
635#endif 280#endif
636 lcd_clear_display(); 281 lcd_clear_display();
637 lcd_puts(0,0,str(LANG_SHOWDIR_ERROR_BUFFER)); 282 lcd_puts(0,0,str(LANG_SHOWDIR_ERROR_BUFFER));
638 lcd_puts(0,1,str(LANG_SHOWDIR_ERROR_FULL)); 283 lcd_puts(0,1,str(LANG_SHOWDIR_ERROR_FULL));
639 lcd_update(); 284 lcd_update();
640 sleep(HZ*2); 285 sleep(HZ*2);
641 lcd_clear_display(); 286 lcd_clear_display();
642 }
643 } 287 }
644 288
645 if (start == -1) 289 if (start == -1)
@@ -649,8 +293,10 @@ static int showdir(const char *path, int start, const int *dirfilter)
649 /* use lastfile to determine start (default=0) */ 293 /* use lastfile to determine start (default=0) */
650 start = 0; 294 start = 0;
651 295
652 for (i=0; i<filesindir; i++) 296 for (i=0; i < tc.filesindir; i++)
653 { 297 {
298 struct entry *dircache = tc.dircache;
299
654 if (!strcasecmp(dircache[i].name, lastfile)) 300 if (!strcasecmp(dircache[i].name, lastfile))
655 { 301 {
656 start = i; 302 start = i;
@@ -658,7 +304,7 @@ static int showdir(const char *path, int start, const int *dirfilter)
658 } 304 }
659 } 305 }
660 306
661 diff_files = filesindir - start; 307 diff_files = tc.filesindir - start;
662 if (diff_files < tree_max_on_screen) 308 if (diff_files < tree_max_on_screen)
663 { 309 {
664 int oldstart = start; 310 int oldstart = start;
@@ -667,25 +313,25 @@ static int showdir(const char *path, int start, const int *dirfilter)
667 if (start < 0) 313 if (start < 0)
668 start = 0; 314 start = 0;
669 315
670 dircursor = oldstart - start; 316 tc.dircursor = oldstart - start;
671 } 317 }
672 318
673 dirstart = start; 319 tc.dirstart = start;
674 } 320 }
675 321
676 /* The cursor might point to an invalid line, for example if someone 322 /* The cursor might point to an invalid line, for example if someone
677 deleted the last file in the dir */ 323 deleted the last file in the dir */
678 if(filesindir) 324 if (tc.filesindir)
679 { 325 {
680 while(start + dircursor >= filesindir) 326 while (start + tc.dircursor >= tc.filesindir)
681 { 327 {
682 if(start) 328 if (start)
683 start--; 329 start--;
684 else 330 else
685 if(dircursor) 331 if (tc.dircursor)
686 dircursor--; 332 tc.dircursor--;
687 } 333 }
688 dirstart = start; 334 tc.dirstart = start;
689 } 335 }
690 336
691#ifdef HAVE_LCD_CHARCELLS 337#ifdef HAVE_LCD_CHARCELLS
@@ -698,11 +344,24 @@ static int showdir(const char *path, int start, const int *dirfilter)
698 lcd_setfont(FONT_UI); 344 lcd_setfont(FONT_UI);
699#endif 345#endif
700 346
701 for ( i=start; i < start+tree_max_on_screen; i++ ) {
702 if ( i >= filesindir )
703 break;
704 347
705 icon = filetype_get_icon(dircache[i].attr); 348 for ( i=start; i < start+tree_max_on_screen && i < tc.filesindir; i++ ) {
349 int line = i - start;
350 char* name;
351 int attr = 0;
352
353 if (id3db) {
354 name = ((char**)tc.dircache)[i * 2];
355 icon = db_get_icon(&tc);
356 }
357 else {
358 struct entry* dc = tc.dircache;
359 struct entry* e = &dc[i];
360 name = e->name;
361 attr = e->attr;
362 icon = filetype_get_icon(dircache[i].attr);
363 }
364
706 365
707 if (icon && global_settings.show_icons) { 366 if (icon && global_settings.show_icons) {
708#ifdef HAVE_LCD_BITMAP 367#ifdef HAVE_LCD_BITMAP
@@ -720,18 +379,18 @@ static int showdir(const char *path, int start, const int *dirfilter)
720#endif 379#endif
721 } 380 }
722 381
723 showfileline(i-start, i, false, dirfilter); /* no scroll */ 382 showfileline(line, name, attr, false); /* no scroll */
724 } 383 }
725 384
726#ifdef HAVE_LCD_BITMAP 385#ifdef HAVE_LCD_BITMAP
727 if (global_settings.scrollbar && (filesindir > tree_max_on_screen)) 386 if (global_settings.scrollbar && (tc.filesindir > tree_max_on_screen))
728 scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1, 387 scrollbar(SCROLLBAR_X, SCROLLBAR_Y, SCROLLBAR_WIDTH - 1,
729 tree_max_on_screen * line_height, filesindir, start, 388 tree_max_on_screen * line_height, tc.filesindir, start,
730 start + tree_max_on_screen, VERTICAL); 389 start + tree_max_on_screen, VERTICAL);
731 390
732#if CONFIG_KEYPAD == RECORDER_PAD 391#if CONFIG_KEYPAD == RECORDER_PAD
733 if(global_settings.buttonbar) { 392 if(global_settings.buttonbar) {
734 buttonbar_set(*dirfilter < NUM_FILTER_MODES ? 393 buttonbar_set(*tc.dirfilter < NUM_FILTER_MODES ?
735 str(LANG_DIRBROWSE_F1) : (unsigned char *) "", 394 str(LANG_DIRBROWSE_F1) : (unsigned char *) "",
736 str(LANG_DIRBROWSE_F2), 395 str(LANG_DIRBROWSE_F2),
737 str(LANG_DIRBROWSE_F3)); 396 str(LANG_DIRBROWSE_F3));
@@ -741,7 +400,7 @@ static int showdir(const char *path, int start, const int *dirfilter)
741#endif 400#endif
742 status_draw(true); 401 status_draw(true);
743 402
744 return filesindir; 403 return tc.filesindir;
745} 404}
746 405
747static bool ask_resume(bool ask_once) 406static bool ask_resume(bool ask_once)
@@ -826,12 +485,12 @@ void resume_directory(const char *dir)
826{ 485{
827 bool buffer_full; 486 bool buffer_full;
828 487
829 if (!load_and_sort_directory(dir, &global_settings.dirfilter, &filesindir, 488 strcpy(tc.currdir, dir);
830 &buffer_full)) 489 if (!ft_load(&tc, &buffer_full))
831 return; 490 return;
832 lastdir[0] = 0; 491 lastdir[0] = 0;
833 492
834 build_playlist(0); 493 ft_build_playlist(&tc, 0);
835} 494}
836 495
837/* Returns the current working directory and also writes cwd to buf if 496/* Returns the current working directory and also writes cwd to buf if
@@ -839,10 +498,10 @@ void resume_directory(const char *dir)
839char *getcwd(char *buf, int size) 498char *getcwd(char *buf, int size)
840{ 499{
841 if (!buf) 500 if (!buf)
842 return currdir; 501 return tc.currdir;
843 else if (size > 0) 502 else if (size > 0)
844 { 503 {
845 strncpy(buf, currdir, size); 504 strncpy(buf, tc.currdir, size);
846 return buf; 505 return buf;
847 } 506 }
848 else 507 else
@@ -888,41 +547,54 @@ void set_current_file(char *path)
888 if (name) 547 if (name)
889 { 548 {
890 *name = 0; 549 *name = 0;
891 strcpy(currdir, path); 550 strcpy(tc.currdir, path);
892 *name = '/'; 551 *name = '/';
893 name++; 552 name++;
894 } 553 }
895 else 554 else
896 { 555 {
897 strcpy(currdir, "/"); 556 strcpy(tc.currdir, "/");
898 name = path+1; 557 name = path+1;
899 } 558 }
900 559
901 strcpy(lastfile, name); 560 strcpy(lastfile, name);
902 561
903 dircursor = 0; 562 tc.dircursor = 0;
904 dirstart = -1; 563 tc.dirstart = -1;
905 564
906 if (strncmp(currdir,lastdir,sizeof(lastdir))) 565 if (strncmp(tc.currdir,lastdir,sizeof(lastdir)))
907 { 566 {
908 dirlevel = 0; 567 tc.dirlevel = 0;
909 dirpos[dirlevel] = -1; 568 tc.dirpos[tc.dirlevel] = -1;
910 cursorpos[dirlevel] = 0; 569 tc.cursorpos[tc.dirlevel] = 0;
911 570
912 /* use '/' to calculate dirlevel */ 571 /* use '/' to calculate dirlevel */
913 for (i=1; i<strlen(path)+1; i++) 572 for (i=1; i<strlen(path)+1; i++)
914 { 573 {
915 if (path[i] == '/') 574 if (path[i] == '/')
916 { 575 {
917 dirlevel++; 576 tc.dirlevel++;
918 dirpos[dirlevel] = -1; 577 tc.dirpos[tc.dirlevel] = -1;
919 cursorpos[dirlevel] = 0; 578 tc.cursorpos[tc.dirlevel] = 0;
920 } 579 }
921 } 580 }
922 } 581 }
923} 582}
924 583
925static bool dirbrowse(const char *root, const int *dirfilter) 584static bool check_changed_id3mode(bool currmode)
585{
586 if (currmode != (global_settings.dirfilter == SHOW_ID3DB)) {
587 currmode = global_settings.dirfilter == SHOW_ID3DB;
588 if (currmode) {
589 db_load(&tc, NULL);
590 }
591 else
592 ft_load(&tc, NULL);
593 }
594 return currmode;
595}
596
597static bool dirbrowse(void)
926{ 598{
927 int numentries=0; 599 int numentries=0;
928 char buf[MAX_PATH]; 600 char buf[MAX_PATH];
@@ -931,7 +603,7 @@ static bool dirbrowse(const char *root, const int *dirfilter)
931 int button; 603 int button;
932 int tree_max_on_screen; 604 int tree_max_on_screen;
933 bool reload_root = false; 605 bool reload_root = false;
934 int lastfilter = *dirfilter; 606 int lastfilter = *tc.dirfilter;
935 bool lastsortcase = global_settings.sort_case; 607 bool lastsortcase = global_settings.sort_case;
936 int lastdircursor=-1; 608 int lastdircursor=-1;
937 bool need_update = true; 609 bool need_update = true;
@@ -940,6 +612,8 @@ static bool dirbrowse(const char *root, const int *dirfilter)
940 bool update_all = false; /* set this to true when the whole file list 612 bool update_all = false; /* set this to true when the whole file list
941 has been refreshed on screen */ 613 has been refreshed on screen */
942 int lastbutton = 0; 614 int lastbutton = 0;
615 char* currdir = tc.currdir; /* just a shortcut */
616 bool id3db = global_settings.dirfilter == SHOW_ID3DB;
943 617
944#ifdef HAVE_LCD_BITMAP 618#ifdef HAVE_LCD_BITMAP
945 tree_max_on_screen = recalc_screen_height(); 619 tree_max_on_screen = recalc_screen_height();
@@ -947,31 +621,28 @@ static bool dirbrowse(const char *root, const int *dirfilter)
947 tree_max_on_screen = TREE_MAX_ON_SCREEN; 621 tree_max_on_screen = TREE_MAX_ON_SCREEN;
948#endif 622#endif
949 623
950 dircursor=0; 624 tc.dircursor=0;
951 dirstart=0; 625 tc.dirstart=0;
952 dirlevel=0; 626 tc.dirlevel=0;
953 627
954 memcpy(currdir,root,sizeof(currdir)); 628 if (*tc.dirfilter < NUM_FILTER_MODES)
955
956 if (*dirfilter < NUM_FILTER_MODES)
957 start_resume(true); 629 start_resume(true);
958 630
959 numentries = showdir(currdir, dirstart, dirfilter); 631 numentries = showdir();
960 if (numentries == -1) 632 if (numentries == -1)
961 return false; /* currdir is not a directory */ 633 return false; /* currdir is not a directory */
962 634
963 if (*dirfilter > NUM_FILTER_MODES && numentries==0) 635 if (*tc.dirfilter > NUM_FILTER_MODES && numentries==0)
964 { 636 {
965 splash(HZ*2, true, str(LANG_NO_FILES)); 637 splash(HZ*2, true, str(LANG_NO_FILES));
966 return false; /* No files found for rockbox_browser() */ 638 return false; /* No files found for rockbox_browser() */
967 } 639 }
968
969 update_all = true; 640 update_all = true;
970 641
971 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 642 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
972 643
973 while(1) { 644 while(1) {
974 struct entry* file = &dircache[dircursor+dirstart]; 645 struct entry *dircache = tc.dircache;
975 646
976 bool restore = false; 647 bool restore = false;
977 648
@@ -1003,7 +674,7 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1003 (button & BUTTON_REL)) 674 (button & BUTTON_REL))
1004 stop = true; 675 stop = true;
1005 break; 676 break;
1006 } 677 }
1007 } 678 }
1008 679
1009 restore = true; 680 restore = true;
@@ -1012,48 +683,71 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1012#endif 683#endif
1013 684
1014 switch ( button ) { 685 switch ( button ) {
1015 case TREE_EXIT: 686#ifdef TREE_ENTER
1016 case TREE_EXIT | BUTTON_REPEAT: 687 case TREE_ENTER:
1017#ifdef TREE_RC_EXIT 688 case TREE_ENTER | BUTTON_REPEAT:
1018 case TREE_RC_EXIT:
1019#endif 689#endif
1020 i=strlen(currdir); 690#ifdef TREE_RC_ENTER
1021 if (i>1) { 691 case TREE_RC_ENTER:
1022 while (currdir[i-1]!='/') 692#endif
1023 i--; 693 case TREE_RUN:
1024 strcpy(buf,&currdir[i]); 694#ifdef TREE_RUN_PRE
1025 if (i==1) 695 if ((button == TREE_RUN) &&
1026 currdir[i]=0; 696 (lastbutton != TREE_RUN_PRE))
1027 else 697 break;
1028 currdir[i-1]=0; 698#endif
1029 699 if ( !numentries )
1030 if (*dirfilter > NUM_FILTER_MODES && dirlevel < 1) 700 break;
1031 exit_func = true;
1032 701
1033 dirlevel--; 702 if (id3db)
1034 if ( dirlevel < MAX_DIR_LEVELS ) { 703 db_enter(&tc);
1035 dirstart = dirpos[dirlevel]; 704 else {
1036 dircursor = cursorpos[dirlevel]; 705 switch (ft_enter(&tc))
706 {
707 case 1: reload_dir = true; break;
708 case 2: reload_root = true; break;
709 case 3: start_wps = true; break;
710 case 4: exit_func = true; break;
711 default: break;
1037 } 712 }
1038 else
1039 dirstart = dircursor = 0;
1040 713
1041 if (dirstart == -1) 714#ifdef HAVE_LCD_BITMAP
1042 strcpy(lastfile, buf); 715 /* maybe we have a new font */
716 tree_max_on_screen = recalc_screen_height();
717#endif
718 }
1043 719
1044 restore = true; 720 /* make sure cursor is on screen */
721 while ( tc.dircursor > tree_max_on_screen )
722 {
723 tc.dircursor--;
724 tc.dirstart++;
1045 } 725 }
726
727 restore = true;
728 break;
729
730 case TREE_EXIT:
731 case TREE_EXIT | BUTTON_REPEAT:
732#ifdef TREE_RC_EXIT
733 case TREE_RC_EXIT:
734#endif
735 if (!tc.dirlevel)
736 break;
737
738 if (id3db)
739 db_exit(&tc);
1046 else 740 else
1047 { 741 if (ft_exit(&tc) == 4)
1048 if (*dirfilter > NUM_FILTER_MODES && dirlevel < 1)
1049 exit_func = true; 742 exit_func = true;
1050 } 743
744 restore = true;
1051 break; 745 break;
1052 746
1053#ifdef TREE_OFF 747#ifdef TREE_OFF
1054#ifndef HAVE_SW_POWEROFF 748#ifndef HAVE_SW_POWEROFF
1055 case TREE_OFF: 749 case TREE_OFF:
1056 if (*dirfilter < NUM_FILTER_MODES) 750 if (*tc.dirfilter < NUM_FILTER_MODES)
1057 { 751 {
1058 /* Stop the music if it is playing, else show the shutdown 752 /* Stop the music if it is playing, else show the shutdown
1059 screen */ 753 screen */
@@ -1078,238 +772,37 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1078 break; 772 break;
1079#endif 773#endif
1080 774
1081#ifdef TREE_ENTER
1082 case TREE_ENTER:
1083 case TREE_ENTER | BUTTON_REPEAT:
1084#endif
1085#ifdef TREE_RC_ENTER
1086 case TREE_RC_ENTER:
1087#endif
1088 case TREE_RUN:
1089#ifdef TREE_RUN_PRE
1090 if ((button == TREE_RUN) &&
1091 (lastbutton != TREE_RUN_PRE))
1092 break;
1093#endif
1094 if ( !numentries )
1095 break;
1096 if (currdir[1])
1097 snprintf(buf,sizeof(buf),"%s/%s",currdir, file->name);
1098 else
1099 snprintf(buf,sizeof(buf),"/%s",file->name);
1100
1101 if (file->attr & ATTR_DIRECTORY) {
1102 memcpy(currdir,buf,sizeof(currdir));
1103 if ( dirlevel < MAX_DIR_LEVELS ) {
1104 dirpos[dirlevel] = dirstart;
1105 cursorpos[dirlevel] = dircursor;
1106 }
1107 dirlevel++;
1108 dircursor=0;
1109 dirstart=0;
1110 }
1111 else {
1112 int seed = current_tick;
1113 bool play = false;
1114 int start_index=0;
1115
1116 lcd_stop_scroll();
1117 switch ( file->attr & TREE_ATTR_MASK ) {
1118 case TREE_ATTR_M3U:
1119 if (bookmark_autoload(buf))
1120 {
1121 restore = true;
1122 break;
1123 }
1124
1125 if (playlist_create(currdir, file->name) != -1)
1126 {
1127 if (global_settings.playlist_shuffle)
1128 playlist_shuffle(seed, -1);
1129 start_index = 0;
1130 playlist_start(start_index,0);
1131 play = true;
1132 }
1133 break;
1134
1135 case TREE_ATTR_MPA:
1136 if (bookmark_autoload(currdir))
1137 {
1138 restore = true;
1139 break;
1140 }
1141
1142 if (playlist_create(currdir, NULL) != -1)
1143 {
1144 start_index =
1145 build_playlist(dircursor+dirstart);
1146 if (global_settings.playlist_shuffle)
1147 {
1148 start_index =
1149 playlist_shuffle(seed,start_index);
1150
1151 /* when shuffling dir.: play all files
1152 even if the file selected by user is
1153 not the first one */
1154 if (!global_settings.play_selected)
1155 start_index = 0;
1156 }
1157
1158 playlist_start(start_index, 0);
1159 play = true;
1160 }
1161 break;
1162
1163 /* wps config file */
1164 case TREE_ATTR_WPS:
1165 wps_load(buf,true);
1166 set_file(buf, global_settings.wps_file,
1167 MAX_FILENAME);
1168 restore = true;
1169 break;
1170
1171 case TREE_ATTR_CFG:
1172 if (!settings_load_config(buf))
1173 break;
1174 lcd_clear_display();
1175 lcd_puts(0,0,str(LANG_SETTINGS_LOADED1));
1176 lcd_puts(0,1,str(LANG_SETTINGS_LOADED2));
1177#ifdef HAVE_LCD_BITMAP
1178 lcd_update();
1179
1180 /* maybe we have a new font */
1181 tree_max_on_screen = recalc_screen_height();
1182 /* make sure cursor is on screen */
1183 while ( dircursor > tree_max_on_screen )
1184 {
1185 dircursor--;
1186 dirstart++;
1187 }
1188#endif
1189 sleep(HZ/2);
1190 restore = true;
1191 break;
1192
1193 case TREE_ATTR_BMARK:
1194 bookmark_load(buf, false);
1195 restore = true;
1196 reload_dir = true;
1197 break;
1198
1199 case TREE_ATTR_LNG:
1200 if(!lang_load(buf)) {
1201 set_file(buf, global_settings.lang_file,
1202 MAX_FILENAME);
1203 talk_init(); /* use voice of same language */
1204 splash(HZ, true, str(LANG_LANGUAGE_LOADED));
1205 restore = true;
1206
1207 language_changed = true;
1208 }
1209 break;
1210
1211#ifdef HAVE_LCD_BITMAP
1212 case TREE_ATTR_FONT:
1213 font_load(buf);
1214 set_file(buf, global_settings.font_file,
1215 MAX_FILENAME);
1216
1217 tree_max_on_screen = recalc_screen_height();
1218 /* make sure cursor is on screen */
1219 while ( dircursor > tree_max_on_screen ) {
1220 dircursor--;
1221 dirstart++;
1222 }
1223 restore = true;
1224 break;
1225#endif
1226
1227#ifndef SIMULATOR
1228 /* firmware file */
1229 case TREE_ATTR_MOD:
1230 rolo_load(buf);
1231 break;
1232#endif
1233
1234 /* plugin file */
1235 case TREE_ATTR_ROCK:
1236 if (plugin_load(buf,NULL) == PLUGIN_USB_CONNECTED)
1237 {
1238 if(*dirfilter > NUM_FILTER_MODES)
1239 /* leave sub-browsers after usb, doing
1240 otherwise might be confusing to the user */
1241 exit_func = true;
1242 else
1243 reload_root = true;
1244 }
1245 else
1246 restore = true;
1247 break;
1248
1249 default:
1250 {
1251 char* plugin = filetype_get_plugin(file);
1252 if (plugin)
1253 {
1254 if (plugin_load(plugin,buf) == PLUGIN_USB_CONNECTED)
1255 reload_root = true;
1256 else
1257 restore = true;
1258 }
1259 break;
1260 }
1261 }
1262
1263 if ( play ) {
1264 if ( global_settings.resume ) {
1265 /* the resume_index must always be the index in the
1266 shuffled list in case shuffle is enabled */
1267 global_settings.resume_index = start_index;
1268 global_settings.resume_offset = 0;
1269 settings_save();
1270 }
1271
1272 start_wps = true;
1273 }
1274 else if (*dirfilter > NUM_FILTER_MODES &&
1275 *dirfilter != SHOW_FONT &&
1276 *dirfilter != SHOW_PLUGINS)
1277 exit_func = true;
1278 }
1279 restore = true;
1280 break;
1281
1282 case TREE_PREV: 775 case TREE_PREV:
1283 case TREE_PREV | BUTTON_REPEAT: 776 case TREE_PREV | BUTTON_REPEAT:
1284#ifdef TREE_RC_PREV 777#ifdef TREE_RC_PREV
1285 case TREE_RC_PREV: 778 case TREE_RC_PREV:
1286 case TREE_RC_PREV | BUTTON_REPEAT: 779 case TREE_RC_PREV | BUTTON_REPEAT:
1287#endif 780#endif
1288 if(filesindir) { 781 if(tc.filesindir) {
1289 if(dircursor) { 782 if(tc.dircursor) {
1290 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false); 783 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false);
1291 dircursor--; 784 tc.dircursor--;
1292 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 785 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
1293 } 786 }
1294 else { 787 else {
1295 if (dirstart) { 788 if (tc.dirstart) {
1296 dirstart--; 789 tc.dirstart--;
1297 numentries = showdir(currdir, dirstart, dirfilter); 790 numentries = showdir();
1298 update_all=true; 791 update_all=true;
1299 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 792 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
1300 } 793 }
1301 else { 794 else {
1302 if (numentries < tree_max_on_screen) { 795 if (numentries < tree_max_on_screen) {
1303 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, 796 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor,
1304 false); 797 false);
1305 dircursor = numentries - 1; 798 tc.dircursor = numentries - 1;
1306 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, 799 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor,
1307 true); 800 true);
1308 } 801 }
1309 else { 802 else {
1310 dirstart = numentries - tree_max_on_screen; 803 tc.dirstart = numentries - tree_max_on_screen;
1311 dircursor = tree_max_on_screen - 1; 804 tc.dircursor = tree_max_on_screen - 1;
1312 numentries = showdir(currdir, dirstart, dirfilter); 805 numentries = showdir();
1313 update_all = true; 806 update_all = true;
1314 put_cursorxy(CURSOR_X, CURSOR_Y + 807 put_cursorxy(CURSOR_X, CURSOR_Y +
1315 tree_max_on_screen - 1, true); 808 tree_max_on_screen - 1, true);
@@ -1326,32 +819,32 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1326 case TREE_RC_NEXT: 819 case TREE_RC_NEXT:
1327 case TREE_RC_NEXT | BUTTON_REPEAT: 820 case TREE_RC_NEXT | BUTTON_REPEAT:
1328#endif 821#endif
1329 if(filesindir) 822 if(tc.filesindir)
1330 { 823 {
1331 if (dircursor + dirstart + 1 < numentries ) { 824 if (tc.dircursor + tc.dirstart + 1 < numentries ) {
1332 if(dircursor+1 < tree_max_on_screen) { 825 if(tc.dircursor+1 < tree_max_on_screen) {
1333 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false); 826 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false);
1334 dircursor++; 827 tc.dircursor++;
1335 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 828 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
1336 } 829 }
1337 else { 830 else {
1338 dirstart++; 831 tc.dirstart++;
1339 numentries = showdir(currdir, dirstart, dirfilter); 832 numentries = showdir();
1340 update_all = true; 833 update_all = true;
1341 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 834 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
1342 } 835 }
1343 } 836 }
1344 else { 837 else {
1345 if(numentries < tree_max_on_screen) { 838 if(numentries < tree_max_on_screen) {
1346 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, false); 839 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, false);
1347 dirstart = dircursor = 0; 840 tc.dirstart = tc.dircursor = 0;
1348 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 841 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
1349 } 842 }
1350 else { 843 else {
1351 dirstart = dircursor = 0; 844 tc.dirstart = tc.dircursor = 0;
1352 numentries = showdir(currdir, dirstart, dirfilter); 845 numentries = showdir();
1353 update_all=true; 846 update_all=true;
1354 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 847 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
1355 } 848 }
1356 } 849 }
1357 need_update = true; 850 need_update = true;
@@ -1361,26 +854,26 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1361#ifdef TREE_PGUP 854#ifdef TREE_PGUP
1362 case TREE_PGUP: 855 case TREE_PGUP:
1363 case TREE_PGUP | BUTTON_REPEAT: 856 case TREE_PGUP | BUTTON_REPEAT:
1364 if ( dirstart ) { 857 if ( tc.dirstart ) {
1365 dirstart -= tree_max_on_screen; 858 tc.dirstart -= tree_max_on_screen;
1366 if ( dirstart < 0 ) 859 if ( tc.dirstart < 0 )
1367 dirstart = 0; 860 tc.dirstart = 0;
1368 } 861 }
1369 else 862 else
1370 dircursor = 0; 863 tc.dircursor = 0;
1371 restore = true; 864 restore = true;
1372 break; 865 break;
1373 866
1374 case TREE_PGDN: 867 case TREE_PGDN:
1375 case TREE_PGDN | BUTTON_REPEAT: 868 case TREE_PGDN | BUTTON_REPEAT:
1376 if ( dirstart < numentries - tree_max_on_screen ) { 869 if ( tc.dirstart < numentries - tree_max_on_screen ) {
1377 dirstart += tree_max_on_screen; 870 tc.dirstart += tree_max_on_screen;
1378 if ( dirstart > 871 if ( tc.dirstart >
1379 numentries - tree_max_on_screen ) 872 numentries - tree_max_on_screen )
1380 dirstart = numentries - tree_max_on_screen; 873 tc.dirstart = numentries - tree_max_on_screen;
1381 } 874 }
1382 else 875 else
1383 dircursor = numentries - dirstart - 1; 876 tc.dircursor = numentries - tc.dirstart - 1;
1384 restore = true; 877 restore = true;
1385 break; 878 break;
1386#endif 879#endif
@@ -1390,12 +883,15 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1390 if (lastbutton != TREE_MENU_PRE) 883 if (lastbutton != TREE_MENU_PRE)
1391 break; 884 break;
1392#endif 885#endif
1393 if (*dirfilter < NUM_FILTER_MODES) 886 /* don't enter menu from plugin browser */
887 if (*tc.dirfilter < NUM_FILTER_MODES)
1394 { 888 {
1395 lcd_stop_scroll(); 889 lcd_stop_scroll();
1396 if (main_menu()) 890 if (main_menu())
1397 reload_root = true; 891 reload_root = true;
1398 restore = true; 892 restore = true;
893
894 id3db = check_changed_id3mode(id3db);
1399 } 895 }
1400 break; 896 break;
1401 897
@@ -1405,7 +901,7 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1405 break; 901 break;
1406#endif 902#endif
1407 /* don't enter wps from plugin browser etc */ 903 /* don't enter wps from plugin browser etc */
1408 if (*dirfilter < NUM_FILTER_MODES) 904 if (*tc.dirfilter < NUM_FILTER_MODES)
1409 { 905 {
1410 if (mpeg_status() & MPEG_STATUS_PLAY) 906 if (mpeg_status() & MPEG_STATUS_PLAY)
1411 { 907 {
@@ -1421,16 +917,20 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1421 917
1422#ifdef BUTTON_F2 918#ifdef BUTTON_F2
1423 case BUTTON_F2: 919 case BUTTON_F2:
1424 if (*dirfilter < NUM_FILTER_MODES) 920 /* don't enter f2 from plugin browser */
921 if (*tc.dirfilter < NUM_FILTER_MODES)
1425 { 922 {
1426 if (quick_screen(CONTEXT_TREE, BUTTON_F2)) 923 if (quick_screen(CONTEXT_TREE, BUTTON_F2))
1427 reload_root = true; 924 reload_root = true;
1428 restore = true; 925 restore = true;
926
927 id3db = check_changed_id3mode(id3db);
1429 break; 928 break;
1430 } 929 }
1431 930
1432 case BUTTON_F3: 931 case BUTTON_F3:
1433 if (*dirfilter < NUM_FILTER_MODES) 932 /* don't enter f3 from plugin browser */
933 if (*tc.dirfilter < NUM_FILTER_MODES)
1434 { 934 {
1435 if (quick_screen(CONTEXT_TREE, BUTTON_F3)) 935 if (quick_screen(CONTEXT_TREE, BUTTON_F3))
1436 reload_root = true; 936 reload_root = true;
@@ -1452,12 +952,12 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1452 else { 952 else {
1453 if (currdir[1]) 953 if (currdir[1])
1454 snprintf(buf, sizeof buf, "%s/%s", 954 snprintf(buf, sizeof buf, "%s/%s",
1455 currdir, dircache[dircursor+dirstart].name); 955 currdir, dircache[tc.dircursor+tc.dirstart].name);
1456 else 956 else
1457 snprintf(buf, sizeof buf, "/%s", 957 snprintf(buf, sizeof buf, "/%s",
1458 dircache[dircursor+dirstart].name); 958 dircache[tc.dircursor+tc.dirstart].name);
1459 onplay_result = onplay(buf, 959 onplay_result = onplay(buf,
1460 dircache[dircursor+dirstart].attr); 960 dircache[tc.dircursor+tc.dirstart].attr);
1461 } 961 }
1462 962
1463 switch (onplay_result) 963 switch (onplay_result)
@@ -1485,7 +985,7 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1485 if (dircache[lasti].attr & ATTR_DIRECTORY) 985 if (dircache[lasti].attr & ATTR_DIRECTORY)
1486 { 986 {
1487 DEBUGF("Playing directory thumbnail: %s", currdir); 987 DEBUGF("Playing directory thumbnail: %s", currdir);
1488 res = play_dirname(lasti); 988 res = ft_play_dirname(lasti);
1489 if (res < 0) /* failed, not existing */ 989 if (res < 0) /* failed, not existing */
1490 { /* say the number instead, as a fallback */ 990 { /* say the number instead, as a fallback */
1491 talk_id(VOICE_DIR, false); 991 talk_id(VOICE_DIR, false);
@@ -1495,10 +995,10 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1495 else 995 else
1496 { 996 {
1497 DEBUGF("Playing file thumbnail: %s/%s%s\n", 997 DEBUGF("Playing file thumbnail: %s/%s%s\n",
1498 currdir, dircache[lasti].name, file_thumbnail_ext); 998 currdir, dircache[lasti].name, file_thumbnail_ext);
1499 /* no fallback necessary, we knew in advance 999 /* no fallback necessary, we knew in advance
1500 that the file exists */ 1000 that the file exists */
1501 play_filename(currdir, dircache[lasti].name); 1001 ft_play_filename(currdir, dircache[lasti].name);
1502 } 1002 }
1503 thumbnail_time = -1; /* job done */ 1003 thumbnail_time = -1; /* job done */
1504 } 1004 }
@@ -1508,7 +1008,7 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1508 default: 1008 default:
1509 if(default_event_handler(button) == SYS_USB_CONNECTED) 1009 if(default_event_handler(button) == SYS_USB_CONNECTED)
1510 { 1010 {
1511 if(*dirfilter > NUM_FILTER_MODES) 1011 if(*tc.dirfilter > NUM_FILTER_MODES)
1512 /* leave sub-browsers after usb, doing otherwise 1012 /* leave sub-browsers after usb, doing otherwise
1513 might be confusing to the user */ 1013 might be confusing to the user */
1514 exit_func = true; 1014 exit_func = true;
@@ -1538,22 +1038,22 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1538 1038
1539 /* do we need to rescan dir? */ 1039 /* do we need to rescan dir? */
1540 if (reload_dir || reload_root || 1040 if (reload_dir || reload_root ||
1541 lastfilter != *dirfilter || 1041 lastfilter != *tc.dirfilter ||
1542 lastsortcase != global_settings.sort_case) 1042 lastsortcase != global_settings.sort_case)
1543 { 1043 {
1544 if ( reload_root ) { 1044 if ( reload_root ) {
1545 strcpy(currdir, "/"); 1045 strcpy(currdir, "/");
1546 dirlevel = 0; 1046 tc.dirlevel = 0;
1547 reload_root = false; 1047 reload_root = false;
1548 } 1048 }
1549 if (! reload_dir ) 1049 if (! reload_dir )
1550 { 1050 {
1551 dircursor = 0; 1051 tc.dircursor = 0;
1552 dirstart = 0; 1052 tc.dirstart = 0;
1553 lastdir[0] = 0; 1053 lastdir[0] = 0;
1554 } 1054 }
1555 1055
1556 lastfilter = *dirfilter; 1056 lastfilter = *tc.dirfilter;
1557 lastsortcase = global_settings.sort_case; 1057 lastsortcase = global_settings.sort_case;
1558 restore = true; 1058 restore = true;
1559 while (button_get(false)); /* clear button queue */ 1059 while (button_get(false)); /* clear button queue */
@@ -1571,9 +1071,9 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1571 1071
1572 /* We need to adjust if the number of lines on screen have 1072 /* We need to adjust if the number of lines on screen have
1573 changed because of a status bar change */ 1073 changed because of a status bar change */
1574 if(CURSOR_Y+LINE_Y+dircursor>tree_max_on_screen) { 1074 if(CURSOR_Y+LINE_Y+tc.dircursor>tree_max_on_screen) {
1575 dirstart++; 1075 tc.dirstart++;
1576 dircursor--; 1076 tc.dircursor--;
1577 } 1077 }
1578#ifdef HAVE_LCD_BITMAP 1078#ifdef HAVE_LCD_BITMAP
1579 /* the sub-screen might've ruined the margins */ 1079 /* the sub-screen might've ruined the margins */
@@ -1581,72 +1081,98 @@ static bool dirbrowse(const char *root, const int *dirfilter)
1581 icon */ 1081 icon */
1582 lcd_setfont(FONT_UI); 1082 lcd_setfont(FONT_UI);
1583#endif 1083#endif
1584 numentries = showdir(currdir, dirstart, dirfilter); 1084 numentries = showdir();
1585 update_all = true; 1085 update_all = true;
1586 put_cursorxy(CURSOR_X, CURSOR_Y + dircursor, true); 1086 put_cursorxy(CURSOR_X, CURSOR_Y + tc.dircursor, true);
1587 1087
1588 need_update = true; 1088 need_update = true;
1589 reload_dir = false; 1089 reload_dir = false;
1590 } 1090 }
1591 1091
1592 if ( numentries && need_update) { 1092 if ( numentries && need_update) {
1593 i = dirstart+dircursor; 1093 i = tc.dirstart+tc.dircursor;
1594 1094
1595 /* if MP3 filter is on, cut off the extension */ 1095 /* if MP3 filter is on, cut off the extension */
1596 if(lasti!=i || restore) { 1096 if(lasti!=i || restore) {
1097 char* name;
1098 int attr = 0;
1099
1100 if (id3db)
1101 name = ((char**)tc.dircache)[lasti * 2];
1102 else {
1103 struct entry* dc = tc.dircache;
1104 struct entry* e = &dc[lasti];
1105 name = e->name;
1106 attr = e->attr;
1107 }
1108
1597 lcd_stop_scroll(); 1109 lcd_stop_scroll();
1598 1110
1599 /* So if lastdircursor and dircursor differ, and then full 1111 /* So if lastdircursor and dircursor differ, and then full
1600 screen was not refreshed, restore the previous line */ 1112 screen was not refreshed, restore the previous line */
1601 if ((lastdircursor != dircursor) && !update_all ) { 1113 if ((lastdircursor != tc.dircursor) && !update_all ) {
1602 showfileline(lastdircursor, lasti, false, dirfilter); /* no scroll */ 1114 showfileline(lastdircursor, name, attr, false); /* no scroll */
1603 } 1115 }
1604 lasti=i; 1116 lasti=i;
1605 lastdircursor=dircursor; 1117 lastdircursor=tc.dircursor;
1606 thumbnail_time = -1; /* cancel whatever we were about to say */ 1118 thumbnail_time = -1; /* cancel whatever we were about to say */
1607 1119
1608 showfileline(dircursor, i, true, dirfilter); /* scroll please */ 1120 if (id3db)
1121 name = ((char**)tc.dircache)[lasti * 2];
1122 else {
1123 struct entry* dc = tc.dircache;
1124 struct entry* e = &dc[lasti];
1125 name = e->name;
1126 attr = e->attr;
1127 }
1128 showfileline(tc.dircursor, name, attr, true); /* scroll please */
1609 need_update = true; 1129 need_update = true;
1610 1130
1611 if (dircache[i].attr & ATTR_DIRECTORY) /* directory? */ 1131 if (dircache[i].attr & ATTR_DIRECTORY) /* directory? */
1612 { 1132 {
1613 /* play directory thumbnail */ 1133 /* play directory thumbnail */
1614 if (global_settings.talk_dir == 3) /* hover */ 1134 switch (global_settings.talk_dir) {
1615 { /* "schedule" a thumbnail, to have a little dalay */ 1135 case 1: /* dirs as numbers */
1616 thumbnail_time = current_tick + HOVER_DELAY; 1136 talk_id(VOICE_DIR, false);
1617 } 1137 talk_number(i+1, true);
1618 else if (global_settings.talk_dir == 1) /* dirs as numbers */ 1138 break;
1619 { 1139
1620 talk_id(VOICE_DIR, false); 1140 case 2: /* dirs spelled */
1621 talk_number(i+1, true); 1141 talk_spell(dircache[i].name, false);
1622 } 1142 break;
1623 else if (global_settings.talk_dir == 2) /* dirs spelled */ 1143
1624 { 1144 case 3: /* thumbnail clip */
1625 talk_spell(dircache[i].name, false); 1145 /* "schedule" a thumbnail, to have a little dalay */
1146 thumbnail_time = current_tick + HOVER_DELAY;
1147 break;
1148
1149 default:
1150 break;
1626 } 1151 }
1627 } 1152 }
1628 else /* file */ 1153 else /* file */
1629 { 1154 {
1630 int voicemethod = global_settings.talk_file; 1155 switch (global_settings.talk_file) {
1631 if (voicemethod == 3) /* thumbnail clip */ 1156 case 1: /* files as numbers */
1632 { /* "schedule" a thumbnail, to have a little delay */ 1157 ft_play_filenumber(i-tc.dirsindir+1,
1633 if (dircache[i].attr & TREE_ATTR_THUMBNAIL) 1158 dircache[i].attr & TREE_ATTR_MASK);
1634 { 1159 break;
1635 thumbnail_time = current_tick + HOVER_DELAY; 1160
1636 } 1161 case 2: /* files spelled */
1637 else 1162 talk_spell(dircache[i].name, false);
1638 { /* say the number as fallback */ 1163 break;
1639 voicemethod = 1; 1164
1640 } 1165 case 3: /* thumbnail clip */
1641 } 1166 /* "schedule" a thumbnail, to have a little delay */
1642 if (voicemethod == 1) /* files as numbers */ 1167 if (dircache[i].attr & TREE_ATTR_THUMBNAIL)
1643 { 1168 thumbnail_time = current_tick + HOVER_DELAY;
1644 play_filenumber(i-dirsindir+1, 1169 else
1645 dircache[i].attr & TREE_ATTR_MASK); 1170 /* spell the number as fallback */
1646 } 1171 talk_spell(dircache[i].name, false);
1647 else if (voicemethod == 2) /* files spelled */ 1172 break;
1648 { 1173
1649 talk_spell(dircache[i].name, false); 1174 default:
1175 break;
1650 } 1176 }
1651 } 1177 }
1652 } 1178 }
@@ -1752,7 +1278,7 @@ bool create_playlist(void)
1752 char filename[MAX_PATH]; 1278 char filename[MAX_PATH];
1753 1279
1754 snprintf(filename, sizeof filename, "%s.m3u", 1280 snprintf(filename, sizeof filename, "%s.m3u",
1755 currdir[1] ? currdir : "/root"); 1281 tc.currdir[1] ? tc.currdir : "/root");
1756 1282
1757 lcd_clear_display(); 1283 lcd_clear_display();
1758 lcd_puts(0,0,str(LANG_CREATING)); 1284 lcd_puts(0,0,str(LANG_CREATING));
@@ -1763,7 +1289,8 @@ bool create_playlist(void)
1763 if (fd < 0) 1289 if (fd < 0)
1764 return false; 1290 return false;
1765 1291
1766 snprintf(filename, sizeof(filename), "%s", currdir[1] ? currdir : "/"); 1292 snprintf(filename, sizeof(filename), "%s",
1293 tc.currdir[1] ? tc.currdir : "/");
1767 plsize = 0; 1294 plsize = 0;
1768 add_dir(filename, sizeof(filename), fd); 1295 add_dir(filename, sizeof(filename), fd);
1769 close(fd); 1296 close(fd);
@@ -1775,37 +1302,51 @@ bool create_playlist(void)
1775bool rockbox_browse(const char *root, int dirfilter) 1302bool rockbox_browse(const char *root, int dirfilter)
1776{ 1303{
1777 bool rc; 1304 bool rc;
1778 int dircursor_save = dircursor; 1305 int dircursor_save = tc.dircursor;
1779 int dirstart_save = dirstart; 1306 int dirstart_save = tc.dirstart;
1780 int dirlevel_save = dirlevel; 1307 int dirlevel_save = tc.dirlevel;
1781 int dirpos_save = dirpos[0]; 1308 int dirpos_save = tc.dirpos[0];
1782 int cursorpos_save = cursorpos[0]; 1309 int cursorpos_save = tc.cursorpos[0];
1783 1310 int* dirfilter_save = tc.dirfilter;
1784 memcpy(currdir_save, currdir, sizeof(currdir)); 1311 static char currdir_save[MAX_PATH];
1312
1313 memcpy(currdir_save, tc.currdir, sizeof(tc.currdir));
1785 reload_dir = true; 1314 reload_dir = true;
1315 memcpy(tc.currdir, root, sizeof(tc.currdir));
1786 start_wps = false; 1316 start_wps = false;
1787 rc = dirbrowse(root, &dirfilter); 1317 tc.dirfilter = &dirfilter;
1788 memcpy(currdir, currdir_save, sizeof(currdir)); 1318
1319 rc = dirbrowse();
1789 1320
1321 memcpy(tc.currdir, currdir_save, sizeof(tc.currdir));
1790 reload_dir = true; 1322 reload_dir = true;
1791 dirstart = dirstart_save; 1323 tc.dirstart = dirstart_save;
1792 cursorpos[0] = cursorpos_save; 1324 tc.cursorpos[0] = cursorpos_save;
1793 dirlevel = dirlevel_save; 1325 tc.dirlevel = dirlevel_save;
1794 dircursor = dircursor_save; 1326 tc.dircursor = dircursor_save;
1795 dirpos[0] = dirpos_save; 1327 tc.dirpos[0] = dirpos_save;
1328 tc.dirfilter = dirfilter_save;
1796 1329
1797 return false; 1330 return false;
1798} 1331}
1799 1332
1800void tree_init(void) 1333void tree_init(void)
1801{ 1334{
1335 /* initialize tree context struct */
1336 memset(&tc, 0, sizeof(tc));
1337 tc.dirfilter = &global_settings.dirfilter;
1338
1339 db_init();
1340
1802 /* We copy the settings value in case it is changed by the user. We can't 1341 /* We copy the settings value in case it is changed by the user. We can't
1803 use it until the next reboot. */ 1342 use it until the next reboot. */
1804 max_files_in_dir = global_settings.max_files_in_dir; 1343 int max_files = global_settings.max_files_in_dir;
1805 name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files_in_dir;
1806 1344
1807 name_buffer = buffer_alloc(name_buffer_size); 1345 tc.name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files;
1808 dircache = buffer_alloc(max_files_in_dir * sizeof(struct entry)); 1346 tc.name_buffer = buffer_alloc(tc.name_buffer_size);
1347
1348 tc.dircache_size = max_files * sizeof(struct entry);
1349 tc.dircache = buffer_alloc(tc.dircache_size);
1809} 1350}
1810 1351
1811void bookmark_play(char *resume_file, int index, int offset, int seed, 1352void bookmark_play(char *resume_file, int index, int offset, int seed,
@@ -1875,3 +1416,77 @@ void bookmark_play(char *resume_file, int index, int offset, int seed,
1875 1416
1876 start_wps=true; 1417 start_wps=true;
1877} 1418}
1419
1420int ft_play_filenumber(int pos, int attr)
1421{
1422 /* try to find a voice ID for the extension, if known */
1423 unsigned int j;
1424 int ext_id = -1; /* default to none */
1425 for (j=0; j<sizeof(filetypes)/sizeof(*filetypes); j++)
1426 {
1427 if (attr == filetypes[j].tree_attr)
1428 {
1429 ext_id = filetypes[j].voiceclip;
1430 break;
1431 }
1432 }
1433
1434 talk_id(VOICE_FILE, false);
1435 talk_number(pos, true);
1436 talk_id(ext_id, true);
1437 return 1;
1438}
1439
1440int ft_play_dirname(int start_index)
1441{
1442 int fd;
1443 char dirname_mp3_filename[MAX_PATH+1];
1444 struct entry *dircache = tc.dircache;
1445
1446 if (mpeg_status() & MPEG_STATUS_PLAY)
1447 return 0;
1448
1449 snprintf(dirname_mp3_filename, sizeof(dirname_mp3_filename), "%s/%s/%s",
1450 tc.currdir, dircache[start_index].name, dir_thumbnail_name);
1451
1452 DEBUGF("Checking for %s\n", dirname_mp3_filename);
1453
1454 fd = open(dirname_mp3_filename, O_RDONLY);
1455 if (fd < 0)
1456 {
1457 DEBUGF("Failed to find: %s\n", dirname_mp3_filename);
1458 return -1;
1459 }
1460
1461 close(fd);
1462
1463 DEBUGF("Found: %s\n", dirname_mp3_filename);
1464
1465 talk_file(dirname_mp3_filename, false);
1466 return 1;
1467}
1468
1469void ft_play_filename(char *dir, char *file)
1470{
1471 char name_mp3_filename[MAX_PATH+1];
1472
1473 if (mpeg_status() & MPEG_STATUS_PLAY)
1474 return;
1475
1476 if (strcasecmp(&file[strlen(file) - strlen(file_thumbnail_ext)],
1477 file_thumbnail_ext))
1478 { /* file has no .talk extension */
1479 snprintf(name_mp3_filename, sizeof(name_mp3_filename),
1480 "%s/%s%s", dir, file, file_thumbnail_ext);
1481
1482 talk_file(name_mp3_filename, false);
1483 }
1484 else
1485 { /* it already is a .talk file, play this directly */
1486 snprintf(name_mp3_filename, sizeof(name_mp3_filename),
1487 "%s/%s", dir, file);
1488 talk_id(LANG_VOICE_DIR_HOVER, false); /* prefix it */
1489 talk_file(name_mp3_filename, true);
1490 }
1491}
1492
diff --git a/apps/tree.h b/apps/tree.h
index f76df90463..87e0361e50 100644
--- a/apps/tree.h
+++ b/apps/tree.h
@@ -20,6 +20,8 @@
20#define _TREE_H_ 20#define _TREE_H_
21 21
22#include <stdbool.h> 22#include <stdbool.h>
23#include <applimits.h>
24#include <file.h>
23 25
24#if CONFIG_KEYPAD == IRIVER_H100_PAD 26#if CONFIG_KEYPAD == IRIVER_H100_PAD
25#define TREE_NEXT BUTTON_DOWN 27#define TREE_NEXT BUTTON_DOWN
@@ -95,6 +97,12 @@
95 97
96#endif 98#endif
97 99
100#ifdef HAVE_LCD_BITMAP
101#define BOOTFILE "ajbrec.ajz"
102#else
103#define BOOTFILE "archos.mod"
104#endif
105
98struct entry { 106struct entry {
99 short attr; /* FAT attributes + file type flags */ 107 short attr; /* FAT attributes + file type flags */
100 unsigned long time_write; /* Last write time */ 108 unsigned long time_write; /* Last write time */
@@ -108,6 +116,28 @@ struct filetype {
108 int voiceclip; 116 int voiceclip;
109}; 117};
110 118
119/* browser context for file or db */
120struct tree_context {
121 int dirlevel;
122 int dircursor;
123 int dirstart;
124 int dirpos[MAX_DIR_LEVELS];
125 int cursorpos[MAX_DIR_LEVELS];
126 char currdir[MAX_PATH]; /* file use */
127 int *dirfilter; /* file use */
128 int filesindir;
129 int dirsindir; /* file use */
130 int table_history[MAX_DIR_LEVELS]; /* db use */
131 int extra_history[MAX_DIR_LEVELS]; /* db use */
132 int currtable; /* db use */
133 int currextra; /* db use */
134
135 void* dircache;
136 int dircache_size;
137 char* name_buffer;
138 int name_buffer_size;
139 int dentry_size;
140};
111 141
112/* using attribute bits not used by FAT (FAT uses lower 7) */ 142/* using attribute bits not used by FAT (FAT uses lower 7) */
113 143
@@ -134,8 +164,7 @@ bool create_playlist(void);
134void resume_directory(const char *dir); 164void resume_directory(const char *dir);
135char *getcwd(char *buf, int size); 165char *getcwd(char *buf, int size);
136void reload_directory(void); 166void reload_directory(void);
137struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter,
138 int *num_files, bool *buffer_full);
139bool check_rockboxdir(void); 167bool check_rockboxdir(void);
168struct tree_context* tree_get_context(void);
140 169
141#endif 170#endif