diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/SOURCES | 2 | ||||
-rw-r--r-- | apps/dbtree.c | 343 | ||||
-rw-r--r-- | apps/dbtree.h | 39 | ||||
-rw-r--r-- | apps/filetree.c | 509 | ||||
-rw-r--r-- | apps/filetree.h | 31 | ||||
-rw-r--r-- | apps/playlist.c | 24 | ||||
-rw-r--r-- | apps/tree.c | 1221 | ||||
-rw-r--r-- | apps/tree.h | 33 |
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 | |||
24 | status.c | 24 | status.c |
25 | talk.c | 25 | talk.c |
26 | tree.c | 26 | tree.c |
27 | dbtree.c | ||
28 | filetree.c | ||
27 | wps-display.c | 29 | wps-display.c |
28 | wps.c | 30 | wps.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 | |||
50 | static int fd; | ||
51 | |||
52 | static int | ||
53 | songstart, albumstart, artiststart, | ||
54 | songcount, albumcount, artistcount, | ||
55 | songlen, songarraylen, | ||
56 | albumlen, albumarraylen, | ||
57 | artistlen; | ||
58 | |||
59 | int 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 | |||
100 | int 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 | |||
267 | void 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 | |||
309 | void 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 | ||
319 | const 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 | ||
338 | int 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 | |||
24 | enum table { invalid, allsongs, allalbums, allartists, albums, songs }; | ||
25 | |||
26 | int db_init(void); | ||
27 | void db_enter(struct tree_context* c); | ||
28 | void db_exit(struct tree_context* c); | ||
29 | int db_load(struct tree_context* c, | ||
30 | bool* dir_buffer_full); | ||
31 | #ifdef HAVE_LCD_BITMAP | ||
32 | const char* db_get_icon(struct tree_context* c); | ||
33 | #else | ||
34 | int 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 | |||
40 | static int boot_size = 0; | ||
41 | static int boot_cluster; | ||
42 | extern bool boot_changed; | ||
43 | |||
44 | int 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 */ | ||
71 | static 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() */ | ||
129 | static 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. */ | ||
178 | int 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 | |||
289 | int 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 | |||
468 | int 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 | |||
23 | int ft_load(struct tree_context* c, bool *buffer_full); | ||
24 | int ft_play_filenumber(int pos, int attr); | ||
25 | int ft_play_dirname(int start_index); | ||
26 | void ft_play_filename(char *dir, char *file); | ||
27 | int ft_enter(struct tree_context* c); | ||
28 | int ft_exit(struct tree_context* c); | ||
29 | int 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 */ | ||
70 | extern bool language_changed; | ||
71 | |||
72 | /* a table for the know file types */ | 67 | /* a table for the know file types */ |
73 | const struct filetype filetypes[] = { | 68 | const 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 */ | 90 | static struct tree_context tc; |
96 | static int max_files_in_dir; | 91 | |
97 | 92 | bool boot_changed = false; | |
98 | static char *name_buffer; | 93 | |
99 | static int name_buffer_size; /* Size of allocated buffer */ | 94 | char lastfile[MAX_PATH]; |
100 | static int name_buffer_length; /* Currently used amount */ | 95 | char lastdir[MAX_PATH]; |
101 | 96 | ||
102 | static struct entry *dircache; | ||
103 | |||
104 | static int dircursor; | ||
105 | static int dirstart; | ||
106 | static int dirlevel; | ||
107 | static int filesindir; | ||
108 | static int dirsindir; /* how many of the dircache entries are directories */ | ||
109 | static int dirpos[MAX_DIR_LEVELS]; | ||
110 | static int cursorpos[MAX_DIR_LEVELS]; | ||
111 | static char lastdir[MAX_PATH]; | ||
112 | static char lastfile[MAX_PATH]; | ||
113 | static char currdir[MAX_PATH]; | ||
114 | static char currdir_save[MAX_PATH]; | ||
115 | static bool reload_dir = false; | 97 | static bool reload_dir = false; |
116 | static int boot_size = 0; | ||
117 | static int boot_cluster; | ||
118 | static bool boot_changed = false; | ||
119 | 98 | ||
120 | static bool start_wps = false; | 99 | static bool start_wps = false; |
121 | static bool dirbrowse(const char *root, const int *dirfilter); | 100 | static bool dirbrowse(void); |
122 | 101 | ||
123 | bool check_rockboxdir(void) | 102 | bool 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 | ||
140 | struct 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 | ||
200 | static int build_playlist(int start_index) | 186 | static 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 | |||
224 | static 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 | |||
244 | static 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 | |||
272 | static 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 | |||
296 | static 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 | |||
349 | static 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 */ | ||
387 | void 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 */ | ||
445 | void 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. */ | ||
475 | struct 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 |
588 | static int recalc_screen_height(void) | 224 | static 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 | ||
607 | static int showdir(const char *path, int start, const int *dirfilter) | 243 | static 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 | ||
747 | static bool ask_resume(bool ask_once) | 406 | static 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) | |||
839 | char *getcwd(char *buf, int size) | 498 | char *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 | ||
925 | static bool dirbrowse(const char *root, const int *dirfilter) | 584 | static 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 | |||
597 | static 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) | |||
1775 | bool rockbox_browse(const char *root, int dirfilter) | 1302 | bool 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 | ||
1800 | void tree_init(void) | 1333 | void 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 | ||
1811 | void bookmark_play(char *resume_file, int index, int offset, int seed, | 1352 | void 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 | |||
1420 | int 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 | |||
1440 | int 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 | |||
1469 | void 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 | |||
98 | struct entry { | 106 | struct 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 */ | ||
120 | struct 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); | |||
134 | void resume_directory(const char *dir); | 164 | void resume_directory(const char *dir); |
135 | char *getcwd(char *buf, int size); | 165 | char *getcwd(char *buf, int size); |
136 | void reload_directory(void); | 166 | void reload_directory(void); |
137 | struct entry* load_and_sort_directory(const char *dirname, const int *dirfilter, | ||
138 | int *num_files, bool *buffer_full); | ||
139 | bool check_rockboxdir(void); | 167 | bool check_rockboxdir(void); |
168 | struct tree_context* tree_get_context(void); | ||
140 | 169 | ||
141 | #endif | 170 | #endif |