1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002 Daniel Stenberg
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
24 #include "applimits.h"
29 #include "backlight.h"
34 #include "main_menu.h"
40 #include "wps-display.h"
58 #include "filetypes.h"
61 #ifdef HAVE_LCD_BITMAP
63 #define BOOTFILE "ajbrec.ajz"
65 #define BOOTFILE "archos.mod"
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
;
72 /* a table for the know file types */
73 const struct filetype filetypes
[] = {
74 { ".mp3", TREE_ATTR_MPA
, File
, VOICE_EXT_MPA
},
75 { ".mp2", TREE_ATTR_MPA
, File
, VOICE_EXT_MPA
},
76 { ".mpa", TREE_ATTR_MPA
, File
, VOICE_EXT_MPA
},
77 { ".m3u", TREE_ATTR_M3U
, Playlist
, LANG_PLAYINDICES_PLAYLIST
},
78 { ".cfg", TREE_ATTR_CFG
, Config
, VOICE_EXT_CFG
},
79 { ".wps", TREE_ATTR_WPS
, Wps
, VOICE_EXT_WPS
},
80 { ".lng", TREE_ATTR_LNG
, Language
, LANG_LANGUAGE
},
81 { ".rock",TREE_ATTR_ROCK
,Plugin
, VOICE_EXT_ROCK
},
82 #ifdef HAVE_LCD_BITMAP
83 { ".fnt", TREE_ATTR_FONT
,Font
, VOICE_EXT_FONT
},
85 { ".bmark",TREE_ATTR_BMARK
, Bookmark
, VOICE_EXT_BMARK
},
87 #ifdef HAVE_LCD_BITMAP
88 { ".ajz", TREE_ATTR_MOD
, Mod_Ajz
, VOICE_EXT_AJZ
},
90 { ".mod", TREE_ATTR_MOD
, Mod_Ajz
, VOICE_EXT_AJZ
},
92 #endif /* #ifndef SIMULATOR */
95 /* Boot value of global_settings.max_files_in_dir */
96 static int max_files_in_dir
;
98 static char *name_buffer
;
99 static int name_buffer_size
; /* Size of allocated buffer */
100 static int name_buffer_length
; /* Currently used amount */
102 static struct entry
*dircache
;
104 static int dircursor
;
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;
116 static int boot_size
= 0;
117 static int boot_cluster
;
118 static bool boot_changed
= false;
120 static bool start_wps
= false;
121 static bool dirbrowse(const char *root
, const int *dirfilter
);
123 bool check_rockboxdir(void)
125 DIR *dir
= opendir(ROCKBOX_DIR
);
129 splash(HZ
*2, true, str(LANG_NO_ROCKBOX_DIR
));
131 splash(HZ
*2, true, str(LANG_INSTALLATION_INCOMPLETE
));
138 void browse_root(void)
144 dirbrowse("/", &global_settings
.dirfilter
);
147 if (!dirbrowse("/", &global_settings
.dirfilter
)) {
148 DEBUGF("No filesystem found. Have you forgotten to create it?\n");
153 void tree_get_filetypes(const struct filetype
** types
, int* count
)
156 *count
= sizeof(filetypes
) / sizeof(*filetypes
);
159 #ifdef HAVE_LCD_BITMAP
162 #define MARGIN_X (global_settings.scrollbar && \
163 filesindir > tree_max_on_screen ? SCROLLBAR_WIDTH : 0) + \
164 CURSOR_WIDTH + (global_settings.show_icons && ICON_WIDTH > 0 ? ICON_WIDTH :0)
165 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
167 /* position the entry-list starts at */
169 #define LINE_Y (global_settings.statusbar ? 1 : 0)
171 #define CURSOR_X (global_settings.scrollbar && \
172 filesindir > tree_max_on_screen ? 1 : 0)
173 #define CURSOR_Y 0 /* the cursor is not positioned in regard to
174 the margins, so this is the amount of lines
175 we add to the cursor Y position to position
177 #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
181 #define SCROLLBAR_X 0
182 #define SCROLLBAR_Y lcd_getymargin()
183 #define SCROLLBAR_WIDTH 6
185 #else /* HAVE_LCD_BITMAP */
187 #define TREE_MAX_ON_SCREEN 2
188 #define TREE_MAX_LEN_DISPLAY 11 /* max length that fits on screen */
189 #define LINE_X 2 /* X position the entry-list starts at */
190 #define LINE_Y 0 /* Y position the entry-list starts at */
193 #define CURSOR_Y 0 /* not really used for players */
195 #endif /* HAVE_LCD_BITMAP */
197 /* talkbox hovering delay, to avoid immediate disk activity */
198 #define HOVER_DELAY (HZ/2)
200 static int build_playlist(int start_index
)
203 int start
=start_index
;
205 for(i
= 0;i
< filesindir
;i
++)
207 if((dircache
[i
].attr
& TREE_ATTR_MASK
) == TREE_ATTR_MPA
)
209 DEBUGF("Adding %s\n", dircache
[i
].name
);
210 if (playlist_add(dircache
[i
].name
) < 0)
215 /* Adjust the start index when se skip non-MP3 entries */
224 static int play_filenumber(int pos
, int attr
)
226 /* try to find a voice ID for the extension, if known */
228 int ext_id
= -1; /* default to none */
229 for (j
=0; j
<sizeof(filetypes
)/sizeof(*filetypes
); j
++)
231 if (attr
== filetypes
[j
].tree_attr
)
233 ext_id
= filetypes
[j
].voiceclip
;
238 talk_id(VOICE_FILE
, false);
239 talk_number(pos
, true);
240 talk_id(ext_id
, true);
244 static int play_dirname(int start_index
)
247 char dirname_mp3_filename
[MAX_PATH
+1];
249 if (mpeg_status() & MPEG_STATUS_PLAY
)
252 snprintf(dirname_mp3_filename
, sizeof(dirname_mp3_filename
), "%s/%s/%s",
253 currdir
, dircache
[start_index
].name
, dir_thumbnail_name
);
255 DEBUGF("Checking for %s\n", dirname_mp3_filename
);
257 fd
= open(dirname_mp3_filename
, O_RDONLY
);
260 DEBUGF("Failed to find: %s\n", dirname_mp3_filename
);
266 DEBUGF("Found: %s\n", dirname_mp3_filename
);
268 talk_file(dirname_mp3_filename
, false);
272 static int play_filename(char *dir
, char *file
)
275 char name_mp3_filename
[MAX_PATH
+1];
277 if (mpeg_status() & MPEG_STATUS_PLAY
)
280 if (strcasecmp(&file
[strlen(file
) - strlen(TALK_EXT
)], TALK_EXT
))
281 { /* file has no .talk extension */
282 snprintf(name_mp3_filename
, sizeof(name_mp3_filename
),
283 "%s/%s" TALK_EXT
, dir
, file
);
285 /* check if a corresponding .talk file exists */
286 DEBUGF("Checking for Filename Thumb %s\n", name_mp3_filename
);
287 fd
= open(name_mp3_filename
, O_RDONLY
);
290 DEBUGF("Failed to find: %s\n", name_mp3_filename
);
293 DEBUGF("Found: %s\n", name_mp3_filename
);
295 talk_file(name_mp3_filename
, false);
298 { /* it already is a .talk file, play this directly */
299 snprintf(name_mp3_filename
, sizeof(name_mp3_filename
),
301 talk_id(LANG_VOICE_DIR_HOVER
, false); /* prefix it */
302 talk_file(name_mp3_filename
, true);
308 static int compare(const void* p1
, const void* p2
)
310 struct entry
* e1
= (struct entry
*)p1
;
311 struct entry
* e2
= (struct entry
*)p2
;
314 if (e1
->attr
& ATTR_DIRECTORY
&& e2
->attr
& ATTR_DIRECTORY
)
315 { /* two directories */
316 criteria
= global_settings
.sort_dir
;
318 else if (!(e1
->attr
& ATTR_DIRECTORY
) && !(e2
->attr
& ATTR_DIRECTORY
))
320 criteria
= global_settings
.sort_file
;
322 else /* dir and file, dir goes first */
323 return ( e2
->attr
& ATTR_DIRECTORY
) - ( e1
->attr
& ATTR_DIRECTORY
);
327 case 3: /* sort type */
329 int t1
= e1
->attr
& TREE_ATTR_MASK
;
330 int t2
= e2
->attr
& TREE_ATTR_MASK
;
332 if (!t1
) /* unknown type */
333 t1
= 0x7FFFFFFF; /* gets a high number, to sort after known */
334 if (!t2
) /* unknown type */
335 t2
= 0x7FFFFFFF; /* gets a high number, to sort after known */
337 if (t1
- t2
) /* if different */
339 /* else fall through to alphabetical sorting */
341 case 0: /* sort alphabetically */
342 if (global_settings
.sort_case
)
343 return strncmp(e1
->name
, e2
->name
, MAX_PATH
);
345 return strncasecmp(e1
->name
, e2
->name
, MAX_PATH
);
346 case 1: /* sort date */
347 return e1
->time_write
- e2
->time_write
;
348 case 2: /* sort date, newest first */
349 return e2
->time_write
- e1
->time_write
;
351 return 0; /* never reached */
354 static void showfileline(int line
, int direntry
, bool scroll
, const int *dirfilter
)
356 char* name
= dircache
[direntry
].name
;
360 #ifdef HAVE_LCD_CHARCELLS
361 if (!global_settings
.show_icons
)
365 /* if any file filter is on, strip the extension */
366 if (*dirfilter
!= SHOW_ALL
&&
367 !(dircache
[direntry
].attr
& ATTR_DIRECTORY
))
369 dotpos
= strrchr(name
, '.');
376 #ifdef HAVE_LCD_BITMAP
377 lcd_setfont(FONT_UI
);
378 if (global_settings
.invert_cursor
)
379 lcd_puts_scroll_style(xpos
, line
, name
, STYLE_INVERT
);
382 lcd_puts_scroll(xpos
, line
, name
);
384 lcd_puts(xpos
, line
, name
);
386 /* Restore the dot before the extension if it was removed */
391 /* load sorted directory into dircache. returns NULL on failure. */
392 struct entry
* load_and_sort_directory(const char *dirname
, const int *dirfilter
,
393 int *num_files
, bool *buffer_full
)
397 DIR *dir
= opendir(dirname
);
399 return NULL
; /* not a directory */
401 name_buffer_length
= 0;
403 *buffer_full
= false;
405 for ( i
=0; i
< max_files_in_dir
; i
++ ) {
407 struct dirent
*entry
= readdir(dir
);
408 struct entry
* dptr
= &dircache
[i
];
412 len
= strlen(entry
->d_name
);
414 /* skip directories . and .. */
415 if ((entry
->attribute
& ATTR_DIRECTORY
) &&
417 (!strncmp(entry
->d_name
, ".", 1))) ||
419 (!strncmp(entry
->d_name
, "..", 2))))) {
424 /* Skip FAT volume ID */
425 if (entry
->attribute
& ATTR_VOLUME_ID
) {
430 /* filter out dotfiles and hidden files */
431 if (*dirfilter
!= SHOW_ALL
&&
432 ((entry
->d_name
[0]=='.') ||
433 (entry
->attribute
& ATTR_HIDDEN
))) {
438 dptr
->attr
= entry
->attribute
;
440 /* check for known file types */
441 if ( !(dptr
->attr
& ATTR_DIRECTORY
) && (len
> 4) )
442 dptr
->attr
|= filetype_get_attr(entry
->d_name
);
444 /* memorize/compare details about the boot file */
445 if ((currdir
[1] == 0) && !strcasecmp(entry
->d_name
, BOOTFILE
)) {
447 if ((entry
->size
!= boot_size
) ||
448 (entry
->startcluster
!= boot_cluster
))
451 boot_size
= entry
->size
;
452 boot_cluster
= entry
->startcluster
;
455 /* filter out non-visible files */
456 if (!(dptr
->attr
& ATTR_DIRECTORY
) && (
457 (*dirfilter
== SHOW_PLAYLIST
&&
458 (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_M3U
) ||
459 ((*dirfilter
== SHOW_MUSIC
&&
460 (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_MPA
) &&
461 (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_M3U
) ||
462 (*dirfilter
== SHOW_SUPPORTED
&& !filetype_supported(dptr
->attr
)) ||
463 (*dirfilter
== SHOW_WPS
&& (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_WPS
) ||
464 (*dirfilter
== SHOW_CFG
&& (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_CFG
) ||
465 (*dirfilter
== SHOW_LNG
&& (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_LNG
) ||
466 (*dirfilter
== SHOW_MOD
&& (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_MOD
) ||
467 (*dirfilter
== SHOW_FONT
&& (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_FONT
) ||
468 (*dirfilter
== SHOW_PLUGINS
&& (dptr
->attr
& TREE_ATTR_MASK
) != TREE_ATTR_ROCK
)))
474 if (len
> name_buffer_size
- name_buffer_length
- 1) {
475 /* Tell the world that we ran out of buffer space */
479 dptr
->name
= &name_buffer
[name_buffer_length
];
480 dptr
->time_write
= entry
->wrtdate
<<16 | entry
->wrttime
; /* in one # */
481 strcpy(dptr
->name
,entry
->d_name
);
482 name_buffer_length
+= len
+ 1;
484 if (dptr
->attr
& ATTR_DIRECTORY
) /* count the remaining dirs */
489 strncpy(lastdir
,dirname
,sizeof(lastdir
));
490 lastdir
[sizeof(lastdir
)-1] = 0;
491 qsort(dircache
,i
,sizeof(struct entry
),compare
);
496 #ifdef HAVE_LCD_BITMAP
497 static int recalc_screen_height(void)
500 int height
= LCD_HEIGHT
;
502 lcd_setfont(FONT_UI
);
503 lcd_getstringsize("A", &fw
, &fh
);
504 if(global_settings
.statusbar
)
505 height
-= STATUSBAR_HEIGHT
;
507 if(global_settings
.buttonbar
)
508 height
-= BUTTONBAR_HEIGHT
;
514 static int showdir(const char *path
, int start
, const int *dirfilter
)
517 int tree_max_on_screen
;
518 bool dir_buffer_full
;
520 #ifdef HAVE_LCD_BITMAP
524 lcd_setfont(FONT_UI
);
525 lcd_getstringsize("A", &fw
, &fh
);
526 tree_max_on_screen
= recalc_screen_height();
530 tree_max_on_screen
= TREE_MAX_ON_SCREEN
;
533 /* new dir? cache it */
534 if (strncmp(path
,lastdir
,sizeof(lastdir
)) || reload_dir
) {
535 if (!load_and_sort_directory(path
, dirfilter
,
536 &filesindir
, &dir_buffer_full
))
539 if ( dir_buffer_full
|| filesindir
== max_files_in_dir
) {
540 #ifdef HAVE_LCD_CHARCELLS
541 lcd_double_height(false);
544 lcd_puts(0,0,str(LANG_SHOWDIR_ERROR_BUFFER
));
545 lcd_puts(0,1,str(LANG_SHOWDIR_ERROR_FULL
));
556 /* use lastfile to determine start (default=0) */
559 for (i
=0; i
<filesindir
; i
++)
561 if (!strcasecmp(dircache
[i
].name
, lastfile
))
568 diff_files
= filesindir
- start
;
569 if (diff_files
< tree_max_on_screen
)
571 int oldstart
= start
;
573 start
-= (tree_max_on_screen
- diff_files
);
577 dircursor
= oldstart
- start
;
583 /* The cursor might point to an invalid line, for example if someone
584 deleted the last file in the dir */
587 while(start
+ dircursor
>= filesindir
)
598 #ifdef HAVE_LCD_CHARCELLS
600 lcd_double_height(false);
603 #ifdef HAVE_LCD_BITMAP
604 lcd_setmargins(MARGIN_X
,MARGIN_Y
); /* leave room for cursor and icon */
605 lcd_setfont(FONT_UI
);
608 for ( i
=start
; i
< start
+tree_max_on_screen
; i
++ ) {
609 if ( i
>= filesindir
)
612 icon
= filetype_get_icon(dircache
[i
].attr
);
614 if (icon
&& global_settings
.show_icons
) {
615 #ifdef HAVE_LCD_BITMAP
617 if ( line_height
> 8 )
618 offset
= (line_height
- 8) / 2;
620 CURSOR_X
* 6 + CURSOR_WIDTH
,
621 MARGIN_Y
+(i
-start
)*line_height
+ offset
,
626 lcd_putc(LINE_X
-1, i
-start
, icon
);
630 showfileline(i
-start
, i
, false, dirfilter
); /* no scroll */
633 #ifdef HAVE_LCD_BITMAP
634 if (global_settings
.scrollbar
&& (filesindir
> tree_max_on_screen
))
635 scrollbar(SCROLLBAR_X
, SCROLLBAR_Y
, SCROLLBAR_WIDTH
- 1,
636 tree_max_on_screen
* line_height
, filesindir
, start
,
637 start
+ tree_max_on_screen
, VERTICAL
);
639 #if CONFIG_KEYPAD == RECORDER_PAD
640 if(global_settings
.buttonbar
) {
641 buttonbar_set(*dirfilter
< NUM_FILTER_MODES
?
642 str(LANG_DIRBROWSE_F1
) : (unsigned char *) "",
643 str(LANG_DIRBROWSE_F2
),
644 str(LANG_DIRBROWSE_F3
));
654 static bool ask_resume(bool ask_once
)
659 #ifdef HAVE_LCD_CHARCELLS
660 lcd_double_height(false);
664 default_event_handler(SYS_USB_CONNECTED
);
669 if ( global_settings
.resume
== RESUME_ON
)
673 lcd_puts(0,0,str(LANG_RESUME_ASK
));
674 #ifdef HAVE_LCD_CHARCELLS
676 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER
));
678 lcd_puts(0,1,str(LANG_CONFIRM_WITH_PLAY_RECORDER
));
679 lcd_puts(0,2,str(LANG_CANCEL_WITH_ANY_RECORDER
));
684 button
= button_get(true);
693 /* ignore the ON button, since it might
694 still be pressed since booting */
696 case BUTTON_ON
| BUTTON_REL
:
697 case BUTTON_ON
| BUTTON_REPEAT
:
701 if(default_event_handler(button
) || (button
& BUTTON_REL
))
707 if ( global_settings
.resume
== RESUME_ASK_ONCE
&& ask_once
) {
708 global_settings
.resume_index
= -1;
715 /* load tracks from specified directory to resume play */
716 void resume_directory(const char *dir
)
720 if (!load_and_sort_directory(dir
, &global_settings
.dirfilter
, &filesindir
,
728 /* Returns the current working directory and also writes cwd to buf if
729 non-NULL. In case of error, returns NULL. */
730 char *getcwd(char *buf
, int size
)
736 strncpy(buf
, currdir
, size
);
743 /* Force a reload of the directory next time directory browser is called */
744 void reload_directory(void)
749 static void start_resume(bool ask_once
)
751 if ( global_settings
.resume
&&
752 global_settings
.resume_index
!= -1 ) {
753 DEBUGF("Resume index %X offset %X\n",
754 global_settings
.resume_index
,
755 global_settings
.resume_offset
);
757 if (!ask_resume(ask_once
))
760 if (playlist_resume() != -1)
762 playlist_start(global_settings
.resume_index
,
763 global_settings
.resume_offset
);
772 void set_current_file(char *path
)
777 /* separate directory from filename */
778 name
= strrchr(path
+1,'/');
782 strcpy(currdir
, path
);
788 strcpy(currdir
, "/");
792 strcpy(lastfile
, name
);
797 if (strncmp(currdir
,lastdir
,sizeof(lastdir
)))
800 dirpos
[dirlevel
] = -1;
801 cursorpos
[dirlevel
] = 0;
803 /* use '/' to calculate dirlevel */
804 for (i
=1; i
<strlen(path
)+1; i
++)
809 dirpos
[dirlevel
] = -1;
810 cursorpos
[dirlevel
] = 0;
816 static bool dirbrowse(const char *root
, const int *dirfilter
)
823 int tree_max_on_screen
;
824 bool reload_root
= false;
825 int lastfilter
= *dirfilter
;
826 bool lastsortcase
= global_settings
.sort_case
;
827 int lastdircursor
=-1;
828 bool need_update
= true;
829 bool exit_func
= false;
830 long thumbnail_time
= -1; /* for delaying a thumbnail */
831 bool update_all
= false; /* set this to true when the whole file list
832 has been refreshed on screen */
835 #ifdef HAVE_LCD_BITMAP
836 tree_max_on_screen
= recalc_screen_height();
838 tree_max_on_screen
= TREE_MAX_ON_SCREEN
;
845 memcpy(currdir
,root
,sizeof(currdir
));
847 if (*dirfilter
< NUM_FILTER_MODES
)
850 numentries
= showdir(currdir
, dirstart
, dirfilter
);
851 if (numentries
== -1)
852 return false; /* currdir is not a directory */
854 if (*dirfilter
> NUM_FILTER_MODES
&& numentries
==0)
856 splash(HZ
*2, true, str(LANG_NO_FILES
));
857 return false; /* No files found for rockbox_browser() */
862 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
865 struct entry
* file
= &dircache
[dircursor
+dirstart
];
867 bool restore
= false;
869 button
= button_get_w_tmo(HZ
/5);
871 /* ignore leftover release event */
872 if (!lastbutton
&& (button
& BUTTON_REL
))
881 lcd_puts(0,0,str(LANG_BOOT_CHANGED
));
882 lcd_puts(0,1,str(LANG_REBOOT_NOW
));
883 #ifdef HAVE_LCD_BITMAP
884 lcd_puts(0,3,str(LANG_CONFIRM_WITH_PLAY_RECORDER
));
885 lcd_puts(0,4,str(LANG_CANCEL_WITH_ANY_RECORDER
));
889 button
= button_get(true);
892 rolo_load("/" BOOTFILE
);
897 if(default_event_handler(button
) ||
898 (button
& BUTTON_REL
))
905 boot_changed
= false;
911 case TREE_EXIT
| BUTTON_REPEAT
:
917 while (currdir
[i
-1]!='/')
919 strcpy(buf
,&currdir
[i
]);
925 if (*dirfilter
> NUM_FILTER_MODES
&& dirlevel
< 1)
929 if ( dirlevel
< MAX_DIR_LEVELS
) {
930 dirstart
= dirpos
[dirlevel
];
931 dircursor
= cursorpos
[dirlevel
];
934 dirstart
= dircursor
= 0;
937 strcpy(lastfile
, buf
);
943 if (*dirfilter
> NUM_FILTER_MODES
&& dirlevel
< 1)
950 /* Stop the music if it is playing, else show the shutdown
955 if (!charger_inserted()) {
964 case TREE_OFF
| BUTTON_REPEAT
:
965 if (charger_inserted()) {
974 case TREE_ENTER
| BUTTON_REPEAT
:
983 snprintf(buf
,sizeof(buf
),"%s/%s",currdir
, file
->name
);
985 snprintf(buf
,sizeof(buf
),"/%s",file
->name
);
987 if (file
->attr
& ATTR_DIRECTORY
) {
988 memcpy(currdir
,buf
,sizeof(currdir
));
989 if ( dirlevel
< MAX_DIR_LEVELS
) {
990 dirpos
[dirlevel
] = dirstart
;
991 cursorpos
[dirlevel
] = dircursor
;
998 int seed
= current_tick
;
1003 switch ( file
->attr
& TREE_ATTR_MASK
) {
1005 if (bookmark_autoload(buf
))
1011 if (playlist_create(currdir
, file
->name
) != -1)
1013 if (global_settings
.playlist_shuffle
)
1014 playlist_shuffle(seed
, -1);
1016 playlist_start(start_index
,0);
1022 if (bookmark_autoload(currdir
))
1028 if (playlist_create(currdir
, NULL
) != -1)
1031 build_playlist(dircursor
+dirstart
);
1032 if (global_settings
.playlist_shuffle
)
1035 playlist_shuffle(seed
,start_index
);
1037 /* when shuffling dir.: play all files
1038 even if the file selected by user is
1039 not the first one */
1040 if (!global_settings
.play_selected
)
1044 playlist_start(start_index
, 0);
1049 /* wps config file */
1052 set_file(buf
, global_settings
.wps_file
,
1058 if (!settings_load_config(buf
))
1060 lcd_clear_display();
1061 lcd_puts(0,0,str(LANG_SETTINGS_LOADED1
));
1062 lcd_puts(0,1,str(LANG_SETTINGS_LOADED2
));
1063 #ifdef HAVE_LCD_BITMAP
1066 /* maybe we have a new font */
1067 tree_max_on_screen
= recalc_screen_height();
1068 /* make sure cursor is on screen */
1069 while ( dircursor
> tree_max_on_screen
)
1079 case TREE_ATTR_BMARK
:
1080 bookmark_load(buf
, false);
1086 if(!lang_load(buf
)) {
1087 set_file(buf
, global_settings
.lang_file
,
1089 talk_init(); /* use voice of same language */
1090 splash(HZ
, true, str(LANG_LANGUAGE_LOADED
));
1093 language_changed
= true;
1097 #ifdef HAVE_LCD_BITMAP
1098 case TREE_ATTR_FONT
:
1100 set_file(buf
, global_settings
.font_file
,
1103 tree_max_on_screen
= recalc_screen_height();
1104 /* make sure cursor is on screen */
1105 while ( dircursor
> tree_max_on_screen
) {
1121 case TREE_ATTR_ROCK
:
1122 if (plugin_load(buf
,NULL
) == PLUGIN_USB_CONNECTED
)
1130 char* plugin
= filetype_get_plugin(file
);
1133 if (plugin_load(plugin
,buf
) == PLUGIN_USB_CONNECTED
)
1143 if ( global_settings
.resume
) {
1144 /* the resume_index must always be the index in the
1145 shuffled list in case shuffle is enabled */
1146 global_settings
.resume_index
= start_index
;
1147 global_settings
.resume_offset
= 0;
1153 else if (*dirfilter
> NUM_FILTER_MODES
&&
1154 *dirfilter
!= SHOW_FONT
&&
1155 *dirfilter
!= SHOW_PLUGINS
)
1162 case TREE_PREV
| BUTTON_REPEAT
:
1165 case TREE_RC_PREV
| BUTTON_REPEAT
:
1169 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, false);
1171 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
1176 numentries
= showdir(currdir
, dirstart
, dirfilter
);
1178 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
1181 if (numentries
< tree_max_on_screen
) {
1182 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
,
1184 dircursor
= numentries
- 1;
1185 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
,
1189 dirstart
= numentries
- tree_max_on_screen
;
1190 dircursor
= tree_max_on_screen
- 1;
1191 numentries
= showdir(currdir
, dirstart
, dirfilter
);
1193 put_cursorxy(CURSOR_X
, CURSOR_Y
+
1194 tree_max_on_screen
- 1, true);
1203 case TREE_NEXT
| BUTTON_REPEAT
:
1206 case TREE_RC_NEXT
| BUTTON_REPEAT
:
1210 if (dircursor
+ dirstart
+ 1 < numentries
) {
1211 if(dircursor
+1 < tree_max_on_screen
) {
1212 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, false);
1214 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
1218 numentries
= showdir(currdir
, dirstart
, dirfilter
);
1220 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
1224 if(numentries
< tree_max_on_screen
) {
1225 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, false);
1226 dirstart
= dircursor
= 0;
1227 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
1230 dirstart
= dircursor
= 0;
1231 numentries
= showdir(currdir
, dirstart
, dirfilter
);
1233 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
1242 case TREE_PGUP
| BUTTON_REPEAT
:
1244 dirstart
-= tree_max_on_screen
;
1254 case TREE_PGDN
| BUTTON_REPEAT
:
1255 if ( dirstart
< numentries
- tree_max_on_screen
) {
1256 dirstart
+= tree_max_on_screen
;
1258 numentries
- tree_max_on_screen
)
1259 dirstart
= numentries
- tree_max_on_screen
;
1262 dircursor
= numentries
- dirstart
- 1;
1268 if (*dirfilter
< NUM_FILTER_MODES
)
1279 if (lastbutton
!= TREE_WPS_PRE
)
1282 /* don't enter wps from plugin browser etc */
1283 if (*dirfilter
< NUM_FILTER_MODES
)
1285 if (mpeg_status() & MPEG_STATUS_PLAY
)
1291 start_resume(false);
1299 if (*dirfilter
< NUM_FILTER_MODES
)
1301 if (quick_screen(CONTEXT_TREE
, BUTTON_F2
))
1308 if (*dirfilter
< NUM_FILTER_MODES
)
1310 if (quick_screen(CONTEXT_TREE
, BUTTON_F3
))
1312 tree_max_on_screen
= recalc_screen_height();
1319 #ifdef TREE_CONTEXT2
1326 onplay_result
= onplay(NULL
, 0);
1329 snprintf(buf
, sizeof buf
, "%s/%s",
1330 currdir
, dircache
[dircursor
+dirstart
].name
);
1332 snprintf(buf
, sizeof buf
, "/%s",
1333 dircache
[dircursor
+dirstart
].name
);
1334 onplay_result
= onplay(buf
,
1335 dircache
[dircursor
+dirstart
].attr
);
1338 switch (onplay_result
)
1344 case ONPLAY_RELOAD_DIR
:
1348 case ONPLAY_START_PLAY
:
1356 if (thumbnail_time
!= -1 &&
1357 TIME_AFTER(current_tick
, thumbnail_time
))
1358 { /* a delayed hovering thumbnail is due now */
1360 if (dircache
[lasti
].attr
& ATTR_DIRECTORY
)
1362 DEBUGF("Playing directory thumbnail: %s", currdir
);
1363 res
= play_dirname(lasti
);
1364 if (res
< 0) /* failed, not existing */
1365 { /* say the number instead, as a fallback */
1366 talk_id(VOICE_DIR
, false);
1367 talk_number(lasti
+1, true);
1372 DEBUGF("Playing file thumbnail: %s/%s%s\n",
1373 currdir
, dircache
[lasti
].name
, TALK_EXT
);
1374 res
= play_filename(currdir
, dircache
[lasti
].name
);
1375 if (res
< 0) /* failed, not existing */
1376 { /* say the number instead, as a fallback */
1377 play_filenumber(lasti
-dirsindir
+1,
1378 dircache
[lasti
].attr
);
1381 thumbnail_time
= -1; /* job done */
1387 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
1395 lastbutton
= button
;
1401 if (wps_show() == SYS_USB_CONNECTED
)
1403 #ifdef HAVE_LCD_BITMAP
1404 tree_max_on_screen
= recalc_screen_height();
1410 /* do we need to rescan dir? */
1411 if (reload_dir
|| reload_root
||
1412 lastfilter
!= *dirfilter
||
1413 lastsortcase
!= global_settings
.sort_case
)
1415 if ( reload_root
) {
1416 strcpy(currdir
, "/");
1418 reload_root
= false;
1427 lastfilter
= *dirfilter
;
1428 lastsortcase
= global_settings
.sort_case
;
1430 while (button_get(false)); /* clear button queue */
1436 if (restore
|| reload_dir
) {
1437 /* restore display */
1439 #ifdef HAVE_LCD_BITMAP
1440 tree_max_on_screen
= recalc_screen_height();
1443 /* We need to adjust if the number of lines on screen have
1444 changed because of a status bar change */
1445 if(CURSOR_Y
+LINE_Y
+dircursor
>tree_max_on_screen
) {
1449 #ifdef HAVE_LCD_BITMAP
1450 /* the sub-screen might've ruined the margins */
1451 lcd_setmargins(MARGIN_X
,MARGIN_Y
); /* leave room for cursor and
1453 lcd_setfont(FONT_UI
);
1455 numentries
= showdir(currdir
, dirstart
, dirfilter
);
1457 put_cursorxy(CURSOR_X
, CURSOR_Y
+ dircursor
, true);
1463 if ( numentries
&& need_update
) {
1464 i
= dirstart
+dircursor
;
1466 /* if MP3 filter is on, cut off the extension */
1467 if(lasti
!=i
|| restore
) {
1470 /* So if lastdircursor and dircursor differ, and then full
1471 screen was not refreshed, restore the previous line */
1472 if ((lastdircursor
!= dircursor
) && !update_all
) {
1473 showfileline(lastdircursor
, lasti
, false, dirfilter
); /* no scroll */
1476 lastdircursor
=dircursor
;
1477 thumbnail_time
= -1; /* cancel whatever we were about to say */
1479 showfileline(dircursor
, i
, true, dirfilter
); /* scroll please */
1482 if (dircache
[i
].attr
& ATTR_DIRECTORY
) /* directory? */
1484 /* play directory thumbnail */
1485 if (global_settings
.talk_dir
== 3) /* hover */
1486 { /* "schedule" a thumbnail, to have a little dalay */
1487 thumbnail_time
= current_tick
+ HOVER_DELAY
;
1489 else if (global_settings
.talk_dir
== 1) /* dirs as numbers */
1491 talk_id(VOICE_DIR
, false);
1492 talk_number(i
+1, true);
1494 else if (global_settings
.talk_dir
== 2) /* dirs spelled */
1496 talk_spell(dircache
[i
].name
, false);
1499 else if (global_settings
.talk_file
== 1) /* files as numbers */
1501 play_filenumber(i
-dirsindir
+1,
1502 dircache
[i
].attr
& TREE_ATTR_MASK
);
1504 else if (global_settings
.talk_file
== 2) /* files spelled */
1506 talk_spell(dircache
[i
].name
, false);
1508 else if (global_settings
.talk_file
== 3) /* hover */
1509 { /* "schedule" a thumbnail, to have a little dalay */
1510 thumbnail_time
= current_tick
+ HOVER_DELAY
;
1519 need_update
= false;
1527 static int plsize
= 0;
1528 static bool add_dir(char* dirname
, int len
, int fd
)
1533 /* check for user abort */
1535 if (button_get(false) == BUTTON_STOP
)
1537 if (button_get(false) == BUTTON_OFF
)
1541 dir
= opendir(dirname
);
1546 struct dirent
*entry
;
1548 entry
= readdir(dir
);
1551 if (entry
->attribute
& ATTR_DIRECTORY
) {
1552 int dirlen
= strlen(dirname
);
1555 if (!strcmp(entry
->d_name
, ".") ||
1556 !strcmp(entry
->d_name
, ".."))
1560 snprintf(dirname
+dirlen
, len
-dirlen
, "/%s", entry
->d_name
);
1562 snprintf(dirname
, len
, "/%s", entry
->d_name
);
1564 result
= add_dir(dirname
, len
, fd
);
1565 dirname
[dirlen
] = '\0';
1572 int x
= strlen(entry
->d_name
);
1573 if ((!strcasecmp(&entry
->d_name
[x
-4], ".mp3")) ||
1574 (!strcasecmp(&entry
->d_name
[x
-4], ".mp2")) ||
1575 (!strcasecmp(&entry
->d_name
[x
-4], ".mpa")))
1578 write(fd
, dirname
, strlen(dirname
));
1580 write(fd
, entry
->d_name
, x
);
1584 snprintf(buf
, sizeof buf
, "%d", plsize
);
1585 #ifdef HAVE_LCD_BITMAP
1610 bool create_playlist(void)
1613 char filename
[MAX_PATH
];
1615 snprintf(filename
, sizeof filename
, "%s.m3u",
1616 currdir
[1] ? currdir
: "/root");
1618 lcd_clear_display();
1619 lcd_puts(0,0,str(LANG_CREATING
));
1620 lcd_puts_scroll(0,1,filename
);
1623 fd
= creat(filename
,0);
1627 snprintf(filename
, sizeof(filename
), "%s", currdir
[1] ? currdir
: "/");
1629 add_dir(filename
, sizeof(filename
), fd
);
1636 bool rockbox_browse(const char *root
, int dirfilter
)
1639 int dircursor_save
= dircursor
;
1640 int dirstart_save
= dirstart
;
1641 int dirlevel_save
= dirlevel
;
1642 int dirpos_save
= dirpos
[0];
1643 int cursorpos_save
= cursorpos
[0];
1645 memcpy(currdir_save
, currdir
, sizeof(currdir
));
1648 rc
= dirbrowse(root
, &dirfilter
);
1649 memcpy(currdir
, currdir_save
, sizeof(currdir
));
1652 dirstart
= dirstart_save
;
1653 cursorpos
[0] = cursorpos_save
;
1654 dirlevel
= dirlevel_save
;
1655 dircursor
= dircursor_save
;
1656 dirpos
[0] = dirpos_save
;
1661 void tree_init(void)
1663 /* We copy the settings value in case it is changed by the user. We can't
1664 use it until the next reboot. */
1665 max_files_in_dir
= global_settings
.max_files_in_dir
;
1666 name_buffer_size
= AVERAGE_FILENAME_LENGTH
* max_files_in_dir
;
1668 name_buffer
= buffer_alloc(name_buffer_size
);
1669 dircache
= buffer_alloc(max_files_in_dir
* sizeof(struct entry
));
1672 void bookmark_play(char *resume_file
, int index
, int offset
, int seed
)
1674 int len
=strlen(resume_file
);
1676 if (!strcasecmp(&resume_file
[len
-4], ".m3u"))
1679 // check that the file exists
1680 int fd
= open(resume_file
, O_RDONLY
);
1685 slash
= strrchr(resume_file
,'/');
1695 if (playlist_create(cp
, slash
+1) != -1)
1697 if (global_settings
.playlist_shuffle
)
1698 playlist_shuffle(seed
, -1);
1699 playlist_start(index
,offset
);
1707 if (playlist_create(resume_file
, NULL
) != -1)
1709 resume_directory(resume_file
);
1710 if (global_settings
.playlist_shuffle
)
1711 playlist_shuffle(seed
, -1);
1712 playlist_start(index
,offset
);