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"
63 #ifdef HAVE_LCD_BITMAP
67 /* a table for the know file types */
68 const struct filetype filetypes
[] = {
69 { ".mp3", TREE_ATTR_MPA
, File
, VOICE_EXT_MPA
},
70 { ".mp2", TREE_ATTR_MPA
, File
, VOICE_EXT_MPA
},
71 { ".mpa", TREE_ATTR_MPA
, File
, VOICE_EXT_MPA
},
72 { ".m3u", TREE_ATTR_M3U
, Playlist
, LANG_PLAYINDICES_PLAYLIST
},
73 { ".cfg", TREE_ATTR_CFG
, Config
, VOICE_EXT_CFG
},
74 { ".wps", TREE_ATTR_WPS
, Wps
, VOICE_EXT_WPS
},
75 { ".lng", TREE_ATTR_LNG
, Language
, LANG_LANGUAGE
},
76 { ".rock",TREE_ATTR_ROCK
,Plugin
, VOICE_EXT_ROCK
},
77 #ifdef HAVE_LCD_BITMAP
78 { ".fnt", TREE_ATTR_FONT
,Font
, VOICE_EXT_FONT
},
80 { ".bmark",TREE_ATTR_BMARK
, Bookmark
, VOICE_EXT_BMARK
},
82 #ifdef HAVE_LCD_BITMAP
83 { ".ajz", TREE_ATTR_MOD
, Mod_Ajz
, VOICE_EXT_AJZ
},
85 { ".mod", TREE_ATTR_MOD
, Mod_Ajz
, VOICE_EXT_AJZ
},
87 #endif /* #ifndef SIMULATOR */
90 static struct tree_context tc
;
92 bool boot_changed
= false;
94 char lastfile
[MAX_PATH
];
95 static char lastdir
[MAX_PATH
];
96 static int lasttable
, lastextra
, lastfirstpos
;
97 static int max_files
= 0;
99 static bool reload_dir
= false;
101 static bool start_wps
= false;
102 static bool dirbrowse(void);
104 bool check_rockboxdir(void)
106 DIR *dir
= opendir(ROCKBOX_DIR
);
110 splash(HZ
*2, true, str(LANG_NO_ROCKBOX_DIR
));
112 splash(HZ
*2, true, str(LANG_INSTALLATION_INCOMPLETE
));
119 void browse_root(void)
124 strcpy(tc
.currdir
, "/");
131 DEBUGF("No filesystem found. Have you forgotten to create it?\n");
136 void tree_get_filetypes(const struct filetype
** types
, int* count
)
139 *count
= sizeof(filetypes
) / sizeof(*filetypes
);
142 struct tree_context
* tree_get_context(void)
147 #ifdef HAVE_LCD_BITMAP
150 #define MARGIN_X (global_settings.scrollbar && \
151 tc.filesindir > tree_max_on_screen ? SCROLLBAR_WIDTH : 0) + \
152 CURSOR_WIDTH + (global_settings.show_icons && ICON_WIDTH > 0 ? ICON_WIDTH :0)
153 #define MARGIN_Y (global_settings.statusbar ? STATUSBAR_HEIGHT : 0)
155 /* position the entry-list starts at */
157 #define LINE_Y (global_settings.statusbar ? 1 : 0)
159 #define CURSOR_X (global_settings.scrollbar && \
160 tc.filesindir > tree_max_on_screen ? 1 : 0)
161 #define CURSOR_Y 0 /* the cursor is not positioned in regard to
162 the margins, so this is the amount of lines
163 we add to the cursor Y position to position
165 #define CURSOR_WIDTH (global_settings.invert_cursor ? 0 : 4)
169 #define SCROLLBAR_X 0
170 #define SCROLLBAR_Y lcd_getymargin()
171 #define SCROLLBAR_WIDTH 6
173 #else /* HAVE_LCD_BITMAP */
175 #define TREE_MAX_ON_SCREEN 2
176 #define TREE_MAX_LEN_DISPLAY 11 /* max length that fits on screen */
177 #define LINE_X 2 /* X position the entry-list starts at */
178 #define LINE_Y 0 /* Y position the entry-list starts at */
181 #define CURSOR_Y 0 /* not really used for players */
183 #endif /* HAVE_LCD_BITMAP */
185 /* talkbox hovering delay, to avoid immediate disk activity */
186 #define HOVER_DELAY (HZ/2)
188 static void showfileline(int line
, char* name
, int attr
, bool scroll
)
193 #ifdef HAVE_LCD_CHARCELLS
194 if (!global_settings
.show_icons
)
198 /* if any file filter is on, strip the extension */
199 if (*tc
.dirfilter
!= SHOW_ID3DB
&&
200 *tc
.dirfilter
!= SHOW_ALL
&&
201 !(attr
& ATTR_DIRECTORY
))
203 dotpos
= strrchr(name
, '.');
210 #ifdef HAVE_LCD_BITMAP
211 lcd_setfont(FONT_UI
);
212 if (global_settings
.invert_cursor
)
213 lcd_puts_scroll_style(xpos
, line
, name
, STYLE_INVERT
);
216 lcd_puts_scroll(xpos
, line
, name
);
218 lcd_puts(xpos
, line
, name
);
220 /* Restore the dot before the extension if it was removed */
225 #ifdef HAVE_LCD_BITMAP
226 static int recalc_screen_height(void)
229 int height
= LCD_HEIGHT
;
231 lcd_setfont(FONT_UI
);
232 lcd_getstringsize("A", &fw
, &fh
);
233 if(global_settings
.statusbar
)
234 height
-= STATUSBAR_HEIGHT
;
236 #if CONFIG_KEYPAD == RECORDER_PAD
237 if(global_settings
.buttonbar
)
238 height
-= BUTTONBAR_HEIGHT
;
245 static int showdir(void)
247 struct entry
*dircache
= tc
.dircache
;
249 int tree_max_on_screen
;
250 int start
= tc
.dirstart
;
251 bool id3db
= *tc
.dirfilter
== SHOW_ID3DB
;
253 #ifdef HAVE_LCD_BITMAP
257 lcd_setfont(FONT_UI
);
258 lcd_getstringsize("A", &fw
, &fh
);
259 tree_max_on_screen
= recalc_screen_height();
263 tree_max_on_screen
= TREE_MAX_ON_SCREEN
;
266 /* new file dir? load it */
268 if (tc
.currtable
!= lasttable
||
269 tc
.currextra
!= lastextra
||
270 tc
.firstpos
!= lastfirstpos
)
272 if (db_load(&tc
) < 0)
274 lasttable
= tc
.currtable
;
275 lastextra
= tc
.currextra
;
276 lastfirstpos
= tc
.firstpos
;
281 if (strncmp(tc
.currdir
, lastdir
, sizeof(lastdir
)) || reload_dir
) {
282 if (ft_load(&tc
, NULL
) < 0)
284 strcpy(lastdir
, tc
.currdir
);
289 if (newdir
&& !id3db
&&
290 (tc
.dirfull
|| tc
.filesindir
== global_settings
.max_files_in_dir
) )
292 #ifdef HAVE_LCD_CHARCELLS
293 lcd_double_height(false);
296 lcd_puts(0,0,str(LANG_SHOWDIR_ERROR_BUFFER
));
297 lcd_puts(0,1,str(LANG_SHOWDIR_ERROR_FULL
));
307 /* use lastfile to determine start (default=0) */
310 for (i
=0; i
< tc
.filesindir
; i
++)
312 struct entry
*dircache
= tc
.dircache
;
314 if (!strcasecmp(dircache
[i
].name
, lastfile
))
321 diff_files
= tc
.filesindir
- start
;
322 if (diff_files
< tree_max_on_screen
)
324 int oldstart
= start
;
326 start
-= (tree_max_on_screen
- diff_files
);
330 tc
.dircursor
= oldstart
- start
;
336 /* The cursor might point to an invalid line, for example if someone
337 deleted the last file in the dir */
340 while (start
+ tc
.dircursor
>= tc
.filesindir
)
351 #ifdef HAVE_LCD_CHARCELLS
353 lcd_double_height(false);
356 #ifdef HAVE_LCD_BITMAP
357 lcd_setmargins(MARGIN_X
,MARGIN_Y
); /* leave room for cursor and icon */
358 lcd_setfont(FONT_UI
);
362 for ( i
=start
; i
< start
+tree_max_on_screen
&& i
< tc
.filesindir
; i
++ ) {
363 int line
= i
- start
;
368 name
= ((char**)tc
.dircache
)[i
* tc
.dentry_size
];
369 icon
= db_get_icon(&tc
);
372 struct entry
* dc
= tc
.dircache
;
373 struct entry
* e
= &dc
[i
];
376 icon
= filetype_get_icon(dircache
[i
].attr
);
380 if (icon
&& global_settings
.show_icons
) {
381 #ifdef HAVE_LCD_BITMAP
383 if ( line_height
> 8 )
384 offset
= (line_height
- 8) / 2;
386 CURSOR_X
* 6 + CURSOR_WIDTH
,
387 MARGIN_Y
+(i
-start
)*line_height
+ offset
,
392 lcd_putc(LINE_X
-1, i
-start
, icon
);
396 showfileline(line
, name
, attr
, false); /* no scroll */
399 #ifdef HAVE_LCD_BITMAP
400 if (global_settings
.scrollbar
&& (tc
.dirlength
> tree_max_on_screen
))
401 scrollbar(SCROLLBAR_X
, SCROLLBAR_Y
, SCROLLBAR_WIDTH
- 1,
402 tree_max_on_screen
* line_height
, tc
.dirlength
,
404 start
+ tc
.firstpos
+ tree_max_on_screen
, VERTICAL
);
406 #if CONFIG_KEYPAD == RECORDER_PAD
407 if(global_settings
.buttonbar
) {
408 buttonbar_set(*tc
.dirfilter
< NUM_FILTER_MODES
?
409 str(LANG_DIRBROWSE_F1
) : (unsigned char *) "",
410 str(LANG_DIRBROWSE_F2
),
411 str(LANG_DIRBROWSE_F3
));
418 return tc
.filesindir
;
421 static bool ask_resume(bool ask_once
)
425 static bool ignore_power
= true;
427 #ifdef HAVE_LCD_CHARCELLS
428 lcd_double_height(false);
432 default_event_handler(SYS_USB_CONNECTED
);
437 if ( global_settings
.resume
== RESUME_ON
)
441 lcd_puts(0,0,str(LANG_RESUME_ASK
));
442 #ifdef HAVE_LCD_CHARCELLS
444 lcd_puts(0,1,str(LANG_RESUME_CONFIRM_PLAYER
));
446 lcd_puts(0,1,str(LANG_CONFIRM_WITH_PLAY_RECORDER
));
447 lcd_puts(0,2,str(LANG_CANCEL_WITH_ANY_RECORDER
));
452 button
= button_get(true);
455 case TREE_RUN_PRE
: /* catch the press, not the release */
462 ignore_power
= false;
463 /* Don't ignore the power button for subsequent calls */
466 #ifdef TREE_POWER_BTN
467 /* Initially ignore the button which powers on the box. It
468 might still be pressed since booting. */
470 case TREE_POWER_BTN
| BUTTON_REPEAT
:
475 /* No longer ignore the power button after it was released */
476 case TREE_POWER_BTN
| BUTTON_REL
:
477 ignore_power
= false;
480 /* Handle sys events, ignore button releases */
482 if(default_event_handler(button
) || !(button
& BUTTON_REL
))
488 if ( global_settings
.resume
== RESUME_ASK_ONCE
&& ask_once
) {
489 global_settings
.resume_index
= -1;
493 ignore_power
= false;
494 /* Don't ignore the power button for subsequent calls */
498 /* load tracks from specified directory to resume play */
499 void resume_directory(const char *dir
)
501 if (ft_load(&tc
, dir
) < 0)
505 ft_build_playlist(&tc
, 0);
508 /* Returns the current working directory and also writes cwd to buf if
509 non-NULL. In case of error, returns NULL. */
510 char *getcwd(char *buf
, int size
)
516 strncpy(buf
, tc
.currdir
, size
);
523 /* Force a reload of the directory next time directory browser is called */
524 void reload_directory(void)
529 static void start_resume(bool ask_once
)
531 if ( global_settings
.resume
&&
532 global_settings
.resume_index
!= -1 ) {
533 DEBUGF("Resume index %X offset %X\n",
534 global_settings
.resume_index
,
535 global_settings
.resume_offset
);
537 if (!ask_resume(ask_once
))
540 if (playlist_resume() != -1)
542 playlist_start(global_settings
.resume_index
,
543 global_settings
.resume_offset
);
552 void set_current_file(char *path
)
557 /* separate directory from filename */
558 name
= strrchr(path
+1,'/');
562 strcpy(tc
.currdir
, path
);
568 strcpy(tc
.currdir
, "/");
572 strcpy(lastfile
, name
);
577 if (strncmp(tc
.currdir
,lastdir
,sizeof(lastdir
)))
580 tc
.dirpos
[tc
.dirlevel
] = -1;
581 tc
.cursorpos
[tc
.dirlevel
] = 0;
583 /* use '/' to calculate dirlevel */
584 for (i
=1; i
<strlen(path
)+1; i
++)
589 tc
.dirpos
[tc
.dirlevel
] = -1;
590 tc
.cursorpos
[tc
.dirlevel
] = 0;
596 static bool check_changed_id3mode(bool currmode
)
598 if (currmode
!= (global_settings
.dirfilter
== SHOW_ID3DB
)) {
599 currmode
= global_settings
.dirfilter
== SHOW_ID3DB
;
609 static bool dirbrowse(void)
616 int tree_max_on_screen
;
617 bool reload_root
= false;
618 int lastfilter
= *tc
.dirfilter
;
619 bool lastsortcase
= global_settings
.sort_case
;
620 int lastdircursor
=-1;
621 bool need_update
= true;
622 bool exit_func
= false;
623 long thumbnail_time
= -1; /* for delaying a thumbnail */
624 bool update_all
= false; /* set this to true when the whole file list
625 has been refreshed on screen */
627 char* currdir
= tc
.currdir
; /* just a shortcut */
628 bool id3db
= *tc
.dirfilter
== SHOW_ID3DB
;
630 #ifdef HAVE_LCD_BITMAP
631 tree_max_on_screen
= recalc_screen_height();
633 tree_max_on_screen
= TREE_MAX_ON_SCREEN
;
644 if (*tc
.dirfilter
< NUM_FILTER_MODES
)
648 numentries
= showdir();
649 if (numentries
== -1)
650 return false; /* currdir is not a directory */
652 if (*tc
.dirfilter
> NUM_FILTER_MODES
&& numentries
==0)
654 splash(HZ
*2, true, str(LANG_NO_FILES
));
655 return false; /* No files found for rockbox_browser() */
659 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, true);
663 struct entry
*dircache
= tc
.dircache
;
665 bool restore
= false;
667 button
= button_get_w_tmo(HZ
/5);
675 lcd_puts(0,0,str(LANG_BOOT_CHANGED
));
676 lcd_puts(0,1,str(LANG_REBOOT_NOW
));
677 #ifdef HAVE_LCD_BITMAP
678 lcd_puts(0,3,str(LANG_CONFIRM_WITH_PLAY_RECORDER
));
679 lcd_puts(0,4,str(LANG_CANCEL_WITH_ANY_RECORDER
));
683 button
= button_get(true);
686 rolo_load("/" BOOTFILE
);
691 if(default_event_handler(button
) ||
692 (button
& BUTTON_REL
))
699 boot_changed
= false;
706 case TREE_ENTER
| BUTTON_REPEAT
:
713 if ((button
== TREE_RUN
) &&
714 (lastbutton
!= TREE_RUN_PRE
))
727 case 1: reload_dir
= true; break;
728 case 2: reload_root
= true; break;
729 case 3: start_wps
= true; break;
730 case 4: exit_func
= true; break;
734 #ifdef HAVE_LCD_BITMAP
735 /* maybe we have a new font */
736 tree_max_on_screen
= recalc_screen_height();
738 /* make sure cursor is on screen */
739 while ( tc
.dircursor
> tree_max_on_screen
)
749 case TREE_EXIT
| BUTTON_REPEAT
:
753 if (*tc
.dirfilter
> NUM_FILTER_MODES
&& tc
.dirlevel
< 1) {
764 if (ft_exit(&tc
) == 4)
771 #ifndef HAVE_SW_POWEROFF
773 if (*tc
.dirfilter
< NUM_FILTER_MODES
)
775 /* Stop the music if it is playing, else show the shutdown
780 if (!charger_inserted()) {
790 case TREE_OFF
| BUTTON_REPEAT
:
791 if (charger_inserted()) {
799 case TREE_PREV
| BUTTON_REPEAT
:
802 case TREE_RC_PREV
| BUTTON_REPEAT
:
808 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, false);
810 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, true);
813 if (tc
.dirstart
|| tc
.firstpos
) {
817 if (tc
.firstpos
> max_files
/2) {
818 tc
.firstpos
-= max_files
/2;
819 tc
.dirstart
+= max_files
/2;
823 tc
.dirstart
= tc
.firstpos
- 1;
830 if (numentries
< tree_max_on_screen
) {
831 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
,
833 tc
.dircursor
= numentries
- 1;
834 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
,
837 else if (id3db
&& tc
.dirfull
) {
838 /* load last dir segment */
839 /* use max_files/2 in case names are longer than
840 AVERAGE_FILE_LENGTH */
841 tc
.firstpos
= tc
.dirlength
- max_files
/2;
842 tc
.dirstart
= tc
.firstpos
;
843 tc
.dircursor
= tree_max_on_screen
- 1;
844 numentries
= showdir();
846 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
,
850 tc
.dirstart
= numentries
- tree_max_on_screen
;
851 tc
.dircursor
= tree_max_on_screen
- 1;
860 case TREE_NEXT
| BUTTON_REPEAT
:
863 case TREE_RC_NEXT
| BUTTON_REPEAT
:
868 if (tc
.dircursor
+ tc
.dirstart
+ 1 < numentries
) {
869 if(tc
.dircursor
+1 < tree_max_on_screen
) {
870 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, false);
872 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, true);
879 else if (id3db
&& (tc
.firstpos
|| tc
.dirfull
)) {
880 if (tc
.dircursor
+ tc
.dirstart
+ tc
.firstpos
+ 1 >= tc
.dirlength
) {
881 /* wrap and load first dir segment */
882 tc
.firstpos
= tc
.dirstart
= tc
.dircursor
= 0;
885 /* load next dir segment */
886 tc
.firstpos
+= tc
.dirstart
;
892 if(numentries
< tree_max_on_screen
) {
893 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, false);
894 tc
.dirstart
= tc
.dircursor
= 0;
895 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, true);
898 tc
.dirstart
= tc
.dircursor
= 0;
899 numentries
= showdir();
901 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, true);
909 case TREE_PGUP
| BUTTON_REPEAT
:
911 tc
.dirstart
-= tree_max_on_screen
;
912 if ( tc
.dirstart
< 0 )
915 else if (tc
.firstpos
) {
916 if (tc
.firstpos
> max_files
/2) {
917 tc
.firstpos
-= max_files
/2;
918 tc
.dirstart
+= max_files
/2;
919 tc
.dirstart
-= tree_max_on_screen
;
922 tc
.dirstart
= tc
.firstpos
- tree_max_on_screen
;
932 case TREE_PGDN
| BUTTON_REPEAT
:
933 if ( tc
.dirstart
< numentries
- tree_max_on_screen
) {
934 tc
.dirstart
+= tree_max_on_screen
;
935 if ( tc
.dirstart
> numentries
- tree_max_on_screen
)
936 tc
.dirstart
= numentries
- tree_max_on_screen
;
938 else if (id3db
&& tc
.dirfull
) {
939 /* load next dir segment */
940 tc
.firstpos
+= tc
.dirstart
;
944 tc
.dircursor
= numentries
- tc
.dirstart
- 1;
951 if (lastbutton
!= TREE_MENU_PRE
)
954 /* don't enter menu from plugin browser */
955 if (*tc
.dirfilter
< NUM_FILTER_MODES
)
962 id3db
= check_changed_id3mode(id3db
);
968 if (lastbutton
!= TREE_WPS_PRE
)
971 /* don't enter wps from plugin browser etc */
972 if (*tc
.dirfilter
< NUM_FILTER_MODES
)
974 if (mpeg_status() & MPEG_STATUS_PLAY
)
988 /* don't enter f2 from plugin browser */
989 if (*tc
.dirfilter
< NUM_FILTER_MODES
)
991 if (quick_screen(CONTEXT_TREE
, BUTTON_F2
))
995 id3db
= check_changed_id3mode(id3db
);
1000 /* don't enter f3 from plugin browser */
1001 if (*tc
.dirfilter
< NUM_FILTER_MODES
)
1003 if (quick_screen(CONTEXT_TREE
, BUTTON_F3
))
1005 tree_max_on_screen
= recalc_screen_height();
1012 #ifdef TREE_CONTEXT2
1020 onplay_result
= onplay(NULL
, 0);
1023 snprintf(buf
, sizeof buf
, "%s/%s",
1024 currdir
, dircache
[tc
.dircursor
+tc
.dirstart
].name
);
1026 snprintf(buf
, sizeof buf
, "/%s",
1027 dircache
[tc
.dircursor
+tc
.dirstart
].name
);
1029 attr
= dircache
[tc
.dircursor
+tc
.dirstart
].attr
;
1030 onplay_result
= onplay(buf
, attr
);
1033 switch (onplay_result
)
1039 case ONPLAY_RELOAD_DIR
:
1043 case ONPLAY_START_PLAY
:
1051 if (thumbnail_time
!= -1 &&
1052 TIME_AFTER(current_tick
, thumbnail_time
))
1053 { /* a delayed hovering thumbnail is due now */
1055 if (dircache
[lasti
].attr
& ATTR_DIRECTORY
)
1057 DEBUGF("Playing directory thumbnail: %s", currdir
);
1058 res
= ft_play_dirname(lasti
);
1059 if (res
< 0) /* failed, not existing */
1060 { /* say the number instead, as a fallback */
1061 talk_id(VOICE_DIR
, false);
1062 talk_number(lasti
+1, true);
1067 DEBUGF("Playing file thumbnail: %s/%s%s\n",
1068 currdir
, dircache
[lasti
].name
, file_thumbnail_ext
);
1069 /* no fallback necessary, we knew in advance
1070 that the file exists */
1071 ft_play_filename(currdir
, dircache
[lasti
].name
);
1073 thumbnail_time
= -1; /* job done */
1079 if(default_event_handler(button
) == SYS_USB_CONNECTED
)
1081 if(*tc
.dirfilter
> NUM_FILTER_MODES
)
1082 /* leave sub-browsers after usb, doing otherwise
1083 might be confusing to the user */
1094 lastbutton
= button
;
1100 if (wps_show() == SYS_USB_CONNECTED
)
1102 #ifdef HAVE_LCD_BITMAP
1103 tree_max_on_screen
= recalc_screen_height();
1105 id3db
= check_changed_id3mode(id3db
);
1110 /* do we need to rescan dir? */
1111 if (reload_dir
|| reload_root
||
1112 lastfilter
!= *tc
.dirfilter
||
1113 lastsortcase
!= global_settings
.sort_case
)
1115 if ( reload_root
) {
1116 strcpy(currdir
, "/");
1118 reload_root
= false;
1127 lastfilter
= *tc
.dirfilter
;
1128 lastsortcase
= global_settings
.sort_case
;
1130 while (button_get(false)); /* clear button queue */
1136 if (restore
|| reload_dir
) {
1137 /* restore display */
1139 #ifdef HAVE_LCD_BITMAP
1140 tree_max_on_screen
= recalc_screen_height();
1143 /* We need to adjust if the number of lines on screen have
1144 changed because of a status bar change */
1145 if(CURSOR_Y
+LINE_Y
+tc
.dircursor
>tree_max_on_screen
) {
1149 #ifdef HAVE_LCD_BITMAP
1150 /* the sub-screen might've ruined the margins */
1151 lcd_setmargins(MARGIN_X
,MARGIN_Y
); /* leave room for cursor and
1153 lcd_setfont(FONT_UI
);
1155 numentries
= showdir();
1157 put_cursorxy(CURSOR_X
, CURSOR_Y
+ tc
.dircursor
, true);
1163 if ( numentries
&& need_update
) {
1164 i
= tc
.dirstart
+tc
.dircursor
;
1166 /* if MP3 filter is on, cut off the extension */
1167 if(lasti
!=i
|| restore
) {
1172 name
= ((char**)tc
.dircache
)[lasti
* tc
.dentry_size
];
1174 struct entry
* dc
= tc
.dircache
;
1175 struct entry
* e
= &dc
[lasti
];
1182 /* So if lastdircursor and dircursor differ, and then full
1183 screen was not refreshed, restore the previous line */
1184 if ((lastdircursor
!= tc
.dircursor
) && !update_all
) {
1185 showfileline(lastdircursor
, name
, attr
, false); /* no scroll */
1188 lastdircursor
=tc
.dircursor
;
1189 thumbnail_time
= -1; /* cancel whatever we were about to say */
1192 name
= ((char**)tc
.dircache
)[lasti
* tc
.dentry_size
];
1194 struct entry
* dc
= tc
.dircache
;
1195 struct entry
* e
= &dc
[lasti
];
1199 showfileline(tc
.dircursor
, name
, attr
, true); /* scroll please */
1202 if (dircache
[i
].attr
& ATTR_DIRECTORY
) /* directory? */
1204 /* play directory thumbnail */
1205 switch (global_settings
.talk_dir
) {
1206 case 1: /* dirs as numbers */
1207 talk_id(VOICE_DIR
, false);
1208 talk_number(i
+1, true);
1211 case 2: /* dirs spelled */
1212 talk_spell(dircache
[i
].name
, false);
1215 case 3: /* thumbnail clip */
1216 /* "schedule" a thumbnail, to have a little dalay */
1217 thumbnail_time
= current_tick
+ HOVER_DELAY
;
1226 switch (global_settings
.talk_file
) {
1227 case 1: /* files as numbers */
1228 ft_play_filenumber(i
-tc
.dirsindir
+1,
1229 dircache
[i
].attr
& TREE_ATTR_MASK
);
1232 case 2: /* files spelled */
1233 talk_spell(dircache
[i
].name
, false);
1236 case 3: /* thumbnail clip */
1237 /* "schedule" a thumbnail, to have a little delay */
1238 if (dircache
[i
].attr
& TREE_ATTR_THUMBNAIL
)
1239 thumbnail_time
= current_tick
+ HOVER_DELAY
;
1241 /* spell the number as fallback */
1242 talk_spell(dircache
[i
].name
, false);
1255 need_update
= false;
1263 static int plsize
= 0;
1264 static bool add_dir(char* dirname
, int len
, int fd
)
1269 /* check for user abort */
1271 if (button_get(false) == BUTTON_STOP
)
1273 if (button_get(false) == BUTTON_OFF
)
1277 dir
= opendir(dirname
);
1282 struct dirent
*entry
;
1284 entry
= readdir(dir
);
1287 if (entry
->attribute
& ATTR_DIRECTORY
) {
1288 int dirlen
= strlen(dirname
);
1291 if (!strcmp(entry
->d_name
, ".") ||
1292 !strcmp(entry
->d_name
, ".."))
1296 snprintf(dirname
+dirlen
, len
-dirlen
, "/%s", entry
->d_name
);
1298 snprintf(dirname
, len
, "/%s", entry
->d_name
);
1300 result
= add_dir(dirname
, len
, fd
);
1301 dirname
[dirlen
] = '\0';
1308 int x
= strlen(entry
->d_name
);
1309 if ((!strcasecmp(&entry
->d_name
[x
-4], ".mp3")) ||
1310 (!strcasecmp(&entry
->d_name
[x
-4], ".mp2")) ||
1311 (!strcasecmp(&entry
->d_name
[x
-4], ".mpa")))
1314 write(fd
, dirname
, strlen(dirname
));
1316 write(fd
, entry
->d_name
, x
);
1320 snprintf(buf
, sizeof buf
, "%d", plsize
);
1321 #ifdef HAVE_LCD_BITMAP
1346 bool create_playlist(void)
1349 char filename
[MAX_PATH
];
1351 snprintf(filename
, sizeof filename
, "%s.m3u",
1352 tc
.currdir
[1] ? tc
.currdir
: "/root");
1354 lcd_clear_display();
1355 lcd_puts(0,0,str(LANG_CREATING
));
1356 lcd_puts_scroll(0,1,filename
);
1359 fd
= creat(filename
,0);
1363 snprintf(filename
, sizeof(filename
), "%s",
1364 tc
.currdir
[1] ? tc
.currdir
: "/");
1366 add_dir(filename
, sizeof(filename
), fd
);
1373 bool rockbox_browse(const char *root
, int dirfilter
)
1375 static struct tree_context backup
;
1379 memcpy(tc
.currdir
, root
, sizeof(tc
.currdir
));
1381 tc
.dirfilter
= &dirfilter
;
1391 void tree_init(void)
1393 /* We copy the settings value in case it is changed by the user. We can't
1394 use it until the next reboot. */
1395 max_files
= global_settings
.max_files_in_dir
;
1397 /* initialize tree context struct */
1398 memset(&tc
, 0, sizeof(tc
));
1399 tc
.dirfilter
= &global_settings
.dirfilter
;
1403 tc
.name_buffer_size
= AVERAGE_FILENAME_LENGTH
* max_files
;
1404 tc
.name_buffer
= buffer_alloc(tc
.name_buffer_size
);
1406 tc
.dircache_size
= max_files
* sizeof(struct entry
);
1407 tc
.dircache
= buffer_alloc(tc
.dircache_size
);
1410 void bookmark_play(char *resume_file
, int index
, int offset
, int seed
,
1414 int len
=strlen(resume_file
);
1416 if (!strcasecmp(&resume_file
[len
-4], ".m3u"))
1418 /* Playlist playback */
1420 // check that the file exists
1421 int fd
= open(resume_file
, O_RDONLY
);
1426 slash
= strrchr(resume_file
,'/');
1436 if (playlist_create(cp
, slash
+1) != -1)
1438 if (global_settings
.playlist_shuffle
)
1439 playlist_shuffle(seed
, -1);
1440 playlist_start(index
,offset
);
1447 /* Directory playback */
1449 if (playlist_create(resume_file
, NULL
) != -1)
1451 resume_directory(resume_file
);
1452 if (global_settings
.playlist_shuffle
)
1453 playlist_shuffle(seed
, -1);
1455 /* Check if the file is at the same spot in the directory,
1456 else search for it */
1457 if ((strcmp(strrchr(playlist_peek(index
) + 1,'/') + 1,
1460 for ( i
=0; i
< playlist_amount(); i
++ )
1462 if ((strcmp(strrchr(playlist_peek(i
) + 1,'/') + 1,
1466 if (i
< playlist_amount())
1471 playlist_start(index
,offset
);
1478 int ft_play_filenumber(int pos
, int attr
)
1480 /* try to find a voice ID for the extension, if known */
1482 int ext_id
= -1; /* default to none */
1483 for (j
=0; j
<sizeof(filetypes
)/sizeof(*filetypes
); j
++)
1485 if (attr
== filetypes
[j
].tree_attr
)
1487 ext_id
= filetypes
[j
].voiceclip
;
1492 talk_id(VOICE_FILE
, false);
1493 talk_number(pos
, true);
1494 talk_id(ext_id
, true);
1498 int ft_play_dirname(int start_index
)
1501 char dirname_mp3_filename
[MAX_PATH
+1];
1502 struct entry
*dircache
= tc
.dircache
;
1504 if (mpeg_status() & MPEG_STATUS_PLAY
)
1507 snprintf(dirname_mp3_filename
, sizeof(dirname_mp3_filename
), "%s/%s/%s",
1508 tc
.currdir
, dircache
[start_index
].name
, dir_thumbnail_name
);
1510 DEBUGF("Checking for %s\n", dirname_mp3_filename
);
1512 fd
= open(dirname_mp3_filename
, O_RDONLY
);
1515 DEBUGF("Failed to find: %s\n", dirname_mp3_filename
);
1521 DEBUGF("Found: %s\n", dirname_mp3_filename
);
1523 talk_file(dirname_mp3_filename
, false);
1527 void ft_play_filename(char *dir
, char *file
)
1529 char name_mp3_filename
[MAX_PATH
+1];
1531 if (mpeg_status() & MPEG_STATUS_PLAY
)
1534 if (strcasecmp(&file
[strlen(file
) - strlen(file_thumbnail_ext
)],
1535 file_thumbnail_ext
))
1536 { /* file has no .talk extension */
1537 snprintf(name_mp3_filename
, sizeof(name_mp3_filename
),
1538 "%s/%s%s", dir
, file
, file_thumbnail_ext
);
1540 talk_file(name_mp3_filename
, false);
1543 { /* it already is a .talk file, play this directly */
1544 snprintf(name_mp3_filename
, sizeof(name_mp3_filename
),
1545 "%s/%s", dir
, file
);
1546 talk_id(LANG_VOICE_DIR_HOVER
, false); /* prefix it */
1547 talk_file(name_mp3_filename
, true);