Attempt to fix yellow on sims (can't reproduce on my system).
[kugel-rb.git] / apps / tree.c
blob482c5b7c1b296a819b35a83a12eac53bb4fd051f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Daniel Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdbool.h>
26 #include "applimits.h"
27 #include "dir.h"
28 #include "file.h"
29 #include "lcd.h"
30 #include "font.h"
31 #include "button.h"
32 #include "kernel.h"
33 #include "usb.h"
34 #include "tree.h"
35 #include "sprintf.h"
36 #include "audio.h"
37 #include "playlist.h"
38 #include "menu.h"
39 #include "gwps.h"
40 #include "settings.h"
41 #include "debug.h"
42 #include "storage.h"
43 #include "rolo.h"
44 #include "icons.h"
45 #include "lang.h"
46 #include "screens.h"
47 #include "keyboard.h"
48 #include "bookmark.h"
49 #include "onplay.h"
50 #include "buffer.h"
51 #include "power.h"
52 #include "action.h"
53 #include "talk.h"
54 #include "filetypes.h"
55 #include "misc.h"
56 #include "filetree.h"
57 #include "tagtree.h"
58 #ifdef HAVE_RECORDING
59 #include "recorder/recording.h"
60 #endif
61 #include "rtc.h"
62 #include "dircache.h"
63 #ifdef HAVE_TAGCACHE
64 #include "tagcache.h"
65 #endif
66 #include "yesno.h"
67 #include "gwps-common.h"
68 #include "eeprom_settings.h"
69 #include "playlist_catalog.h"
71 /* gui api */
72 #include "list.h"
73 #include "splash.h"
74 #include "buttonbar.h"
75 #include "action.h"
76 #include "quickscreen.h"
78 #include "root_menu.h"
80 static const struct filetype *filetypes;
81 static int filetypes_count;
83 struct gui_synclist tree_lists;
85 /* I put it here because other files doesn't use it yet,
86 * but should be elsewhere since it will be used mostly everywhere */
87 #ifdef HAVE_BUTTONBAR
88 struct gui_buttonbar tree_buttonbar;
89 #endif
90 static struct tree_context tc;
92 bool boot_changed = false;
94 char lastfile[MAX_PATH];
95 static char lastdir[MAX_PATH];
96 #ifdef HAVE_TAGCACHE
97 static int lasttable, lastextra, lastfirstpos;
98 #endif
99 static int max_files = 0;
101 static bool reload_dir = false;
103 static bool start_wps = false;
104 static int curr_context = false;/* id3db or tree*/
106 static int dirbrowse(void);
107 static int ft_play_dirname(char* name);
108 static void ft_play_filename(char *dir, char *file);
109 static void say_filetype(int attr);
111 static char * tree_get_filename(int selected_item, void *data,
112 char *buffer, size_t buffer_len)
114 struct tree_context * local_tc=(struct tree_context *)data;
115 char *name;
116 int attr=0;
117 bool stripit = false;
118 #ifdef HAVE_TAGCACHE
119 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
121 if (id3db)
123 return tagtree_get_entry(&tc, selected_item)->name;
125 else
126 #endif
128 struct entry* dc = local_tc->dircache;
129 struct entry* e = &dc[selected_item];
130 name = e->name;
131 attr = e->attr;
134 if(!(attr & ATTR_DIRECTORY))
136 switch(global_settings.show_filename_ext)
138 case 0:
139 /* show file extension: off */
140 stripit = true;
141 break;
142 case 1:
143 /* show file extension: on */
144 break;
145 case 2:
146 /* show file extension: only unknown types */
147 stripit = filetype_supported(attr);
148 break;
149 case 3:
150 default:
151 /* show file extension: only when viewing all */
152 stripit = (*(local_tc->dirfilter) != SHOW_ID3DB) &&
153 (*(local_tc->dirfilter) != SHOW_ALL);
154 break;
158 if(stripit)
160 return(strip_extension(buffer, buffer_len, name));
162 return(name);
165 #ifdef HAVE_LCD_COLOR
166 static int tree_get_filecolor(int selected_item, void * data)
168 if (*tc.dirfilter == SHOW_ID3DB)
169 return -1;
170 struct tree_context * local_tc=(struct tree_context *)data;
171 struct entry* dc = local_tc->dircache;
172 struct entry* e = &dc[selected_item];
173 return filetype_get_color(e->name, e->attr);
175 #endif
177 static int tree_get_fileicon(int selected_item, void * data)
179 struct tree_context * local_tc=(struct tree_context *)data;
180 #ifdef HAVE_TAGCACHE
181 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
182 if (id3db) {
183 return tagtree_get_icon(&tc);
185 else
186 #endif
188 struct entry* dc = local_tc->dircache;
189 struct entry* e = &dc[selected_item];
190 return filetype_get_icon(e->attr);
194 static int tree_voice_cb(int selected_item, void * data)
196 struct tree_context * local_tc=(struct tree_context *)data;
197 char *name;
198 int attr=0;
199 #ifdef HAVE_TAGCACHE
200 bool id3db = *(local_tc->dirfilter) == SHOW_ID3DB;
202 if (id3db)
204 attr = tagtree_get_attr(local_tc);
205 name = tagtree_get_entry(local_tc, selected_item)->name;
207 else
208 #endif
210 struct entry* dc = local_tc->dircache;
211 struct entry* e = &dc[selected_item];
212 name = e->name;
213 attr = e->attr;
215 bool is_dir = (attr & ATTR_DIRECTORY);
216 bool did_clip = false;
217 /* First the .talk clip case */
218 if(is_dir)
220 if(global_settings.talk_dir_clip)
222 did_clip = true;
223 if(ft_play_dirname(name) <0)
224 /* failed, not existing */
225 did_clip = false;
227 } else { /* it's a file */
228 if (global_settings.talk_file_clip && (attr & FILE_ATTR_THUMBNAIL))
230 did_clip = true;
231 ft_play_filename(local_tc->currdir, name);
234 if(!did_clip)
236 /* say the number or spell if required or as a fallback */
237 switch (is_dir ? global_settings.talk_dir : global_settings.talk_file)
239 case 1: /* as numbers */
240 talk_id(is_dir ? VOICE_DIR : VOICE_FILE, false);
241 talk_number(selected_item+1 - (is_dir ? 0 : local_tc->dirsindir),
242 true);
243 if(global_settings.talk_filetype
244 && !is_dir && *local_tc->dirfilter < NUM_FILTER_MODES)
245 say_filetype(attr);
246 break;
247 case 2: /* spelled */
248 talk_shutup();
249 if(global_settings.talk_filetype)
251 if(is_dir)
252 talk_id(VOICE_DIR, true);
253 else if(*local_tc->dirfilter < NUM_FILTER_MODES)
254 say_filetype(attr);
256 talk_spell(name, true);
257 break;
260 return 0;
263 bool check_rockboxdir(void)
265 if(!dir_exists(ROCKBOX_DIR))
266 { /* No need to localise this message.
267 If .rockbox is missing, it wouldn't work anyway */
268 int i;
269 FOR_NB_SCREENS(i)
270 screens[i].clear_display();
271 splash(HZ*2, "No .rockbox directory");
272 FOR_NB_SCREENS(i)
273 screens[i].clear_display();
274 splash(HZ*2, "Installation incomplete");
275 return false;
277 return true;
280 /* do this really late in the init sequence */
281 void tree_gui_init(void)
283 check_rockboxdir();
285 strcpy(tc.currdir, "/");
287 #ifdef HAVE_LCD_CHARCELLS
288 int i;
289 FOR_NB_SCREENS(i)
290 screens[i].double_height(false);
291 #endif
292 #ifdef HAVE_BUTTONBAR
293 gui_buttonbar_init(&tree_buttonbar);
294 /* since archos only have one screen, no need to create more than that */
295 gui_buttonbar_set_display(&tree_buttonbar, &(screens[SCREEN_MAIN]) );
296 #endif
297 gui_synclist_init(&tree_lists, &tree_get_filename, &tc, false, 1, NULL);
298 gui_synclist_set_voice_callback(&tree_lists, tree_voice_cb);
299 gui_synclist_set_icon_callback(&tree_lists, &tree_get_fileicon);
300 #ifdef HAVE_LCD_COLOR
301 gui_synclist_set_color_callback(&tree_lists, &tree_get_filecolor);
302 #endif
307 struct tree_context* tree_get_context(void)
309 return &tc;
313 * Returns the position of a given file in the current directory
314 * returns -1 if not found
316 static int tree_get_file_position(char * filename)
318 int i;
320 /* use lastfile to determine the selected item (default=0) */
321 for (i=0; i < tc.filesindir; i++)
323 struct entry* dc = tc.dircache;
324 struct entry* e = &dc[i];
325 if (!strcasecmp(e->name, filename))
326 return(i);
328 return(-1);/* no file can match, returns undefined */
332 * Called when a new dir is loaded (for example when returning from other apps ...)
333 * also completely redraws the tree
335 static int update_dir(void)
337 bool changed = false;
338 #ifdef HAVE_TAGCACHE
339 bool id3db = *tc.dirfilter == SHOW_ID3DB;
340 /* Checks for changes */
341 if (id3db) {
342 if (tc.currtable != lasttable ||
343 tc.currextra != lastextra ||
344 tc.firstpos != lastfirstpos ||
345 reload_dir)
347 if (tagtree_load(&tc) < 0)
348 return -1;
350 lasttable = tc.currtable;
351 lastextra = tc.currextra;
352 lastfirstpos = tc.firstpos;
353 changed = true;
356 else
357 #endif
359 /* if the tc.currdir has been changed, reload it ...*/
360 if (strncmp(tc.currdir, lastdir, sizeof(lastdir)) || reload_dir)
362 if (ft_load(&tc, NULL) < 0)
363 return -1;
364 strcpy(lastdir, tc.currdir);
365 changed = true;
368 /* if selected item is undefined */
369 if (tc.selected_item == -1)
371 /* use lastfile to determine the selected item */
372 tc.selected_item = tree_get_file_position(lastfile);
374 /* If the file doesn't exists, select the first one (default) */
375 if(tc.selected_item < 0)
376 tc.selected_item = 0;
377 changed = true;
379 if (changed)
382 #ifdef HAVE_TAGCACHE
383 !id3db &&
384 #endif
385 (tc.dirfull || tc.filesindir == global_settings.max_files_in_dir) )
387 splash(HZ, ID2P(LANG_SHOWDIR_BUFFER_FULL));
390 #ifdef HAVE_TAGCACHE
391 if (id3db)
393 #ifdef HAVE_LCD_BITMAP
394 if (global_settings.show_path_in_browser == SHOW_PATH_FULL
395 || global_settings.show_path_in_browser == SHOW_PATH_CURRENT)
397 gui_synclist_set_title(&tree_lists, tagtree_get_title(&tc),
398 filetype_get_icon(ATTR_DIRECTORY));
400 else
402 /* Must clear the title as the list is reused */
403 gui_synclist_set_title(&tree_lists, NULL, NOICON);
405 #endif
407 else
408 #endif
410 #ifdef HAVE_LCD_BITMAP
411 if (global_settings.show_path_in_browser &&
412 *(tc.dirfilter) == SHOW_PLUGINS)
414 char *title;
415 if (!strcmp(tc.currdir, PLUGIN_GAMES_DIR))
416 title = str(LANG_PLUGIN_GAMES);
417 else if (!strcmp(tc.currdir, PLUGIN_APPS_DIR))
418 title = str(LANG_PLUGIN_APPS);
419 else if (!strcmp(tc.currdir, PLUGIN_DEMOS_DIR))
420 title = str(LANG_PLUGIN_DEMOS);
421 else title = str(LANG_PLUGINS);
422 gui_synclist_set_title(&tree_lists, title, Icon_Plugin);
424 else if (global_settings.show_path_in_browser == SHOW_PATH_FULL)
426 gui_synclist_set_title(&tree_lists, tc.currdir,
427 filetype_get_icon(ATTR_DIRECTORY));
429 else if (global_settings.show_path_in_browser == SHOW_PATH_CURRENT)
431 char *title = strrchr(tc.currdir, '/') + 1;
432 if (*title == '\0')
434 /* Display "Files" for the root dir */
435 gui_synclist_set_title(&tree_lists, str(LANG_DIR_BROWSER),
436 filetype_get_icon(ATTR_DIRECTORY));
438 else
439 gui_synclist_set_title(&tree_lists, title,
440 filetype_get_icon(ATTR_DIRECTORY));
442 else
444 /* Must clear the title as the list is reused */
445 gui_synclist_set_title(&tree_lists, NULL, NOICON);
447 #endif
450 gui_synclist_set_nb_items(&tree_lists, tc.filesindir);
451 gui_synclist_set_icon_callback(&tree_lists, tree_get_fileicon);
452 if( tc.selected_item >= tc.filesindir)
453 tc.selected_item=tc.filesindir-1;
455 gui_synclist_select_item(&tree_lists, tc.selected_item);
456 #ifdef HAVE_BUTTONBAR
457 if (global_settings.buttonbar) {
458 if (*tc.dirfilter < NUM_FILTER_MODES)
459 gui_buttonbar_set(&tree_buttonbar, str(LANG_SYSFONT_DIRBROWSE_F1),
460 str(LANG_SYSFONT_DIRBROWSE_F2),
461 str(LANG_SYSFONT_DIRBROWSE_F3));
462 else
463 gui_buttonbar_set(&tree_buttonbar, "<<<", "", "");
464 gui_buttonbar_draw(&tree_buttonbar);
466 #endif
467 gui_synclist_draw(&tree_lists);
468 gui_synclist_speak_item(&tree_lists);
469 return tc.filesindir;
472 /* load tracks from specified directory to resume play */
473 void resume_directory(const char *dir)
475 int dirfilter = *tc.dirfilter;
476 int ret;
477 #ifdef HAVE_TAGCACHE
478 bool id3db = *tc.dirfilter == SHOW_ID3DB;
479 #endif
480 /* make sure the dirfilter is sane. The only time it should be possible
481 * thats its not is when resume playlist is called from a plugin
483 #ifdef HAVE_TAGCACHE
484 if (!id3db)
485 #endif
486 *tc.dirfilter = global_settings.dirfilter;
487 ret = ft_load(&tc, dir);
488 *tc.dirfilter = dirfilter;
489 if (ret < 0)
490 return;
491 lastdir[0] = 0;
493 ft_build_playlist(&tc, 0);
495 #ifdef HAVE_TAGCACHE
496 if (id3db)
497 tagtree_load(&tc);
498 #endif
501 /* Returns the current working directory and also writes cwd to buf if
502 non-NULL. In case of error, returns NULL. */
503 char *getcwd(char *buf, int size)
505 if (!buf)
506 return tc.currdir;
507 else if (size > 0)
509 strncpy(buf, tc.currdir, size);
510 return buf;
512 else
513 return NULL;
516 /* Force a reload of the directory next time directory browser is called */
517 void reload_directory(void)
519 reload_dir = true;
522 void get_current_file(char* buffer, int buffer_len)
524 #ifdef HAVE_TAGCACHE
525 /* in ID3DB mode it is a bad idea to call this function */
526 /* (only happens with `follow playlist') */
527 if( *tc.dirfilter == SHOW_ID3DB )
528 return;
529 #endif
531 struct entry* dc = tc.dircache;
532 struct entry* e = &dc[tc.selected_item];
533 snprintf(buffer, buffer_len, "%s/%s", getcwd(NULL,0),
534 tc.dirlength ? e->name : "");
537 /* Allow apps to change our dirfilter directly (required for sub browsers)
538 if they're suddenly going to become a file browser for example */
539 void set_dirfilter(int l_dirfilter)
541 *tc.dirfilter = l_dirfilter;
544 /* Selects a file and update tree context properly */
545 void set_current_file(char *path)
547 char *name;
548 int i;
550 #ifdef HAVE_TAGCACHE
551 /* in ID3DB mode it is a bad idea to call this function */
552 /* (only happens with `follow playlist') */
553 if( *tc.dirfilter == SHOW_ID3DB )
554 return;
555 #endif
557 /* separate directory from filename */
558 /* gets the directory's name and put it into tc.currdir */
559 name = strrchr(path+1,'/');
560 if (name)
562 *name = 0;
563 strcpy(tc.currdir, path);
564 *name = '/';
565 name++;
567 else
569 strcpy(tc.currdir, "/");
570 name = path+1;
573 strcpy(lastfile, name);
576 /* If we changed dir we must recalculate the dirlevel
577 and adjust the selected history properly */
578 if (strncmp(tc.currdir,lastdir,sizeof(lastdir)))
580 tc.dirlevel = 0;
581 tc.selected_item_history[tc.dirlevel] = -1;
583 /* use '/' to calculate dirlevel */
584 for (i = 1; path[i] != '\0'; i++)
586 if (path[i] == '/')
588 tc.dirlevel++;
589 tc.selected_item_history[tc.dirlevel] = -1;
593 if (ft_load(&tc, NULL) >= 0)
595 tc.selected_item = tree_get_file_position(lastfile);
600 /* main loop, handles key events */
601 static int dirbrowse()
603 int numentries=0;
604 char buf[MAX_PATH];
605 int button, oldbutton;
606 bool reload_root = false;
607 int lastfilter = *tc.dirfilter;
608 bool lastsortcase = global_settings.sort_case;
609 bool exit_func = false;
611 char* currdir = tc.currdir; /* just a shortcut */
612 #ifdef HAVE_TAGCACHE
613 bool id3db = *tc.dirfilter == SHOW_ID3DB;
615 if (id3db)
616 curr_context=CONTEXT_ID3DB;
617 else
618 #endif
619 curr_context=CONTEXT_TREE;
620 if (tc.selected_item < 0)
621 tc.selected_item = 0;
622 #ifdef HAVE_TAGCACHE
623 tc.firstpos = 0;
624 lasttable = -1;
625 lastextra = -1;
626 lastfirstpos = 0;
627 #endif
629 start_wps = false;
630 numentries = update_dir();
631 reload_dir = false;
632 if (numentries == -1)
633 return GO_TO_PREVIOUS; /* currdir is not a directory */
635 if (*tc.dirfilter > NUM_FILTER_MODES && numentries==0)
637 splash(HZ*2, ID2P(LANG_NO_FILES));
638 return GO_TO_PREVIOUS; /* No files found for rockbox_browser() */
641 gui_synclist_draw(&tree_lists);
642 while(1) {
643 struct entry *dircache = tc.dircache;
644 bool restore = false;
645 if (tc.dirlevel < 0)
646 tc.dirlevel = 0; /* shouldnt be needed.. this code needs work! */
647 #ifdef BOOTFILE
648 if (boot_changed) {
649 static const char *lines[]={ID2P(LANG_BOOT_CHANGED), ID2P(LANG_REBOOT_NOW)};
650 static const struct text_message message={lines, 2};
651 if(gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
652 rolo_load("/" BOOTFILE);
653 restore = true;
654 boot_changed = false;
656 #endif
657 button = get_action(CONTEXT_TREE,
658 list_do_action_timeout(&tree_lists, HZ/2));
659 oldbutton = button;
660 gui_synclist_do_button(&tree_lists, &button,LIST_WRAP_UNLESS_HELD);
661 tc.selected_item = gui_synclist_get_sel_pos(&tree_lists);
662 switch ( button ) {
663 case ACTION_STD_OK:
664 /* nothing to do if no files to display */
665 if ( numentries == 0 )
666 break;
668 #ifdef HAVE_TAGCACHE
669 switch (id3db?tagtree_enter(&tc):ft_enter(&tc))
670 #else
671 switch (ft_enter(&tc))
672 #endif
674 case 1: reload_dir = true; break;
675 case 2: start_wps = true; break;
676 case 3: exit_func = true; break;
677 default: break;
679 restore = true;
680 break;
682 case ACTION_STD_CANCEL:
683 if (*tc.dirfilter > NUM_FILTER_MODES && tc.dirlevel < 1) {
684 exit_func = true;
685 break;
687 if ((*tc.dirfilter == SHOW_ID3DB && tc.dirlevel == 0) ||
688 ((*tc.dirfilter != SHOW_ID3DB && !strcmp(currdir,"/"))))
690 #ifdef HAVE_LCD_BITMAP /* charcell doesnt have ACTION_TREE_PGLEFT so this isnt needed */
691 if (oldbutton == ACTION_TREE_PGLEFT)
692 break;
693 else
694 #endif
695 return GO_TO_ROOT;
698 #ifdef HAVE_TAGCACHE
699 if (id3db)
700 tagtree_exit(&tc);
701 else
702 #endif
703 if (ft_exit(&tc) == 3)
704 exit_func = true;
706 restore = true;
707 break;
709 case ACTION_TREE_STOP:
710 if (list_stop_handler())
711 restore = true;
712 break;
714 case ACTION_STD_MENU:
715 return GO_TO_ROOT;
716 break;
718 #ifdef HAVE_RECORDING
719 case ACTION_STD_REC:
720 return GO_TO_RECSCREEN;
721 #endif
723 case ACTION_TREE_WPS:
724 return GO_TO_PREVIOUS_MUSIC;
725 break;
726 #ifdef HAVE_QUICKSCREEN
727 case ACTION_STD_QUICKSCREEN:
728 /* don't enter f2 from plugin browser */
729 if (*tc.dirfilter < NUM_FILTER_MODES)
731 if (quick_screen_quick(button))
732 reload_dir = true;
733 restore = true;
735 break;
736 #endif
737 #ifdef BUTTON_F3
738 case ACTION_F3:
739 /* don't enter f3 from plugin browser */
740 if (*tc.dirfilter < NUM_FILTER_MODES)
742 if (quick_screen_f3(ACTION_F3))
743 reload_dir = true;
744 restore = true;
746 break;
747 #endif
749 case ACTION_STD_CONTEXT:
751 int onplay_result;
752 int attr = 0;
754 if(!numentries)
755 onplay_result = onplay(NULL, 0, curr_context);
756 else {
757 #ifdef HAVE_TAGCACHE
758 if (id3db)
760 if (tagtree_get_attr(&tc) == FILE_ATTR_AUDIO)
762 attr = FILE_ATTR_AUDIO;
763 tagtree_get_filename(&tc, buf, sizeof(buf));
765 else
766 attr = ATTR_DIRECTORY;
768 else
769 #endif
771 attr = dircache[tc.selected_item].attr;
773 if (currdir[1]) /* Not in / */
774 snprintf(buf, sizeof buf, "%s/%s",
775 currdir,
776 dircache[tc.selected_item].name);
777 else /* In / */
778 snprintf(buf, sizeof buf, "/%s",
779 dircache[tc.selected_item].name);
781 onplay_result = onplay(buf, attr, curr_context);
783 switch (onplay_result)
785 case ONPLAY_MAINMENU:
786 return GO_TO_ROOT;
788 case ONPLAY_OK:
789 restore = true;
790 break;
792 case ONPLAY_RELOAD_DIR:
793 reload_dir = true;
794 break;
796 case ONPLAY_START_PLAY:
797 return GO_TO_WPS;
798 break;
800 break;
803 #ifdef HAVE_HOTSWAP
804 case SYS_FS_CHANGED:
805 #ifdef HAVE_TAGCACHE
806 if (!id3db)
807 #endif
808 reload_dir = true;
809 /* The 'dir no longer valid' situation will be caught later
810 * by checking the showdir() result. */
811 break;
812 #endif
814 default:
815 if (default_event_handler(button) == SYS_USB_CONNECTED)
817 if(*tc.dirfilter > NUM_FILTER_MODES)
818 /* leave sub-browsers after usb, doing otherwise
819 might be confusing to the user */
820 exit_func = true;
821 else
822 reload_dir = true;
824 break;
826 if (start_wps)
827 return GO_TO_WPS;
828 if (button && !IS_SYSEVENT(button))
830 storage_spin();
834 check_rescan:
835 /* do we need to rescan dir? */
836 if (reload_dir || reload_root ||
837 lastfilter != *tc.dirfilter ||
838 lastsortcase != global_settings.sort_case)
840 if (reload_root) {
841 strcpy(currdir, "/");
842 tc.dirlevel = 0;
843 #ifdef HAVE_TAGCACHE
844 tc.currtable = 0;
845 tc.currextra = 0;
846 lasttable = -1;
847 lastextra = -1;
848 #endif
849 reload_root = false;
852 if (!reload_dir)
854 gui_synclist_select_item(&tree_lists, 0);
855 gui_synclist_draw(&tree_lists);
856 tc.selected_item = 0;
857 lastdir[0] = 0;
860 lastfilter = *tc.dirfilter;
861 lastsortcase = global_settings.sort_case;
862 restore = true;
865 if (exit_func)
866 return GO_TO_PREVIOUS;
868 if (restore || reload_dir) {
869 /* restore display */
870 numentries = update_dir();
871 reload_dir = false;
872 if (currdir[1] && (numentries < 0))
873 { /* not in root and reload failed */
874 reload_root = true; /* try root */
875 goto check_rescan;
879 return true;
882 bool create_playlist(void)
884 char filename[MAX_PATH];
886 snprintf(filename, sizeof filename, "%s.m3u8",
887 tc.currdir[1] ? tc.currdir : "/root");
888 splashf(0, "%s %s", str(LANG_CREATING), filename);
890 trigger_cpu_boost();
891 catalog_add_to_a_playlist(tc.currdir, ATTR_DIRECTORY, true, filename);
892 cancel_cpu_boost();
894 return true;
897 int rockbox_browse(const char *root, int dirfilter)
899 int ret_val = 0;
900 int *last_filter = tc.dirfilter;
901 tc.dirfilter = &dirfilter;
902 tc.sort_dir = global_settings.sort_dir;
904 reload_dir = true;
905 if (dirfilter >= NUM_FILTER_MODES)
907 static struct tree_context backup;
908 int last_context;
910 backup = tc;
911 tc.selected_item = 0;
912 tc.dirlevel = 0;
913 memcpy(tc.currdir, root, sizeof(tc.currdir));
914 start_wps = false;
915 last_context = curr_context;
917 ret_val = dirbrowse();
918 tc = backup;
919 curr_context = last_context;
921 else
923 static char buf[MAX_PATH];
924 if (dirfilter != SHOW_ID3DB)
925 tc.dirfilter = &global_settings.dirfilter;
926 strcpy(buf,root);
927 set_current_file(buf);
928 ret_val = dirbrowse();
930 tc.dirfilter = last_filter;
931 return ret_val;
934 void tree_mem_init(void)
936 /* We copy the settings value in case it is changed by the user. We can't
937 use it until the next reboot. */
938 max_files = global_settings.max_files_in_dir;
940 /* initialize tree context struct */
941 memset(&tc, 0, sizeof(tc));
942 tc.dirfilter = &global_settings.dirfilter;
943 tc.sort_dir = global_settings.sort_dir;
945 tc.name_buffer_size = AVERAGE_FILENAME_LENGTH * max_files;
946 tc.name_buffer = buffer_alloc(tc.name_buffer_size);
948 tc.dircache_size = max_files * sizeof(struct entry);
949 tc.dircache = buffer_alloc(tc.dircache_size);
950 tree_get_filetypes(&filetypes, &filetypes_count);
953 bool bookmark_play(char *resume_file, int index, int offset, int seed,
954 char *filename)
956 int i;
957 char* suffix = strrchr(resume_file, '.');
958 bool started = false;
960 if (suffix != NULL &&
961 (!strcasecmp(suffix, ".m3u") || !strcasecmp(suffix, ".m3u8")))
963 /* Playlist playback */
964 char* slash;
965 /* check that the file exists */
966 if(!file_exists(resume_file))
967 return false;
969 slash = strrchr(resume_file,'/');
970 if (slash)
972 char* cp;
973 *slash=0;
975 cp=resume_file;
976 if (!cp[0])
977 cp="/";
979 if (playlist_create(cp, slash+1) != -1)
981 if (global_settings.playlist_shuffle)
982 playlist_shuffle(seed, -1);
983 playlist_start(index,offset);
984 started = true;
986 *slash='/';
989 else
991 /* Directory playback */
992 lastdir[0]='\0';
993 if (playlist_create(resume_file, NULL) != -1)
995 char* peek_filename;
996 resume_directory(resume_file);
997 if (global_settings.playlist_shuffle)
998 playlist_shuffle(seed, -1);
1000 /* Check if the file is at the same spot in the directory,
1001 else search for it */
1002 peek_filename = playlist_peek(index);
1004 if (peek_filename == NULL)
1005 return false;
1007 if (strcmp(strrchr(peek_filename, '/') + 1, filename))
1009 for ( i=0; i < playlist_amount(); i++ )
1011 peek_filename = playlist_peek(i);
1013 if (peek_filename == NULL)
1014 return false;
1016 if (!strcmp(strrchr(peek_filename, '/') + 1, filename))
1017 break;
1019 if (i < playlist_amount())
1020 index = i;
1021 else
1022 return false;
1024 playlist_start(index,offset);
1025 started = true;
1029 if (started)
1030 start_wps = true;
1031 return started;
1034 static void say_filetype(int attr)
1036 /* try to find a voice ID for the extension, if known */
1037 int j;
1038 attr &= FILE_ATTR_MASK; /* file type */
1039 for (j=0; j<filetypes_count; j++)
1040 if (attr == filetypes[j].tree_attr)
1042 talk_id(filetypes[j].voiceclip, true);
1043 return;
1047 static int ft_play_dirname(char* name)
1049 #if CONFIG_CODEC != SWCODEC
1050 if (audio_status() & AUDIO_STATUS_PLAY)
1051 return 0;
1052 #endif
1054 if(talk_file(tc.currdir, name, dir_thumbnail_name, NULL,
1055 NULL, false))
1057 if(global_settings.talk_filetype)
1058 talk_id(VOICE_DIR, true);
1059 return 1;
1061 else
1062 return -1;
1065 static void ft_play_filename(char *dir, char *file)
1067 #if CONFIG_CODEC != SWCODEC
1068 if (audio_status() & AUDIO_STATUS_PLAY)
1069 return;
1070 #endif
1072 if (strlen(file) >= strlen(file_thumbnail_ext)
1073 && strcasecmp(&file[strlen(file) - strlen(file_thumbnail_ext)],
1074 file_thumbnail_ext))
1075 /* file has no .talk extension */
1076 talk_file(dir, NULL, file, file_thumbnail_ext,
1077 NULL, false);
1078 else
1079 /* it already is a .talk file, play this directly, but prefix it. */
1080 talk_file(dir, NULL, file, NULL,
1081 TALK_IDARRAY(LANG_VOICE_DIR_HOVER), false);
1084 /* These two functions are called by the USB and shutdown handlers */
1085 void tree_flush(void)
1087 #ifdef HAVE_TAGCACHE
1088 tagcache_shutdown();
1089 #endif
1091 #ifdef HAVE_TC_RAMCACHE
1092 tagcache_unload_ramcache();
1093 #endif
1095 #ifdef HAVE_DIRCACHE
1097 int old_val = global_status.dircache_size;
1098 if (global_settings.dircache)
1100 if (!dircache_is_initializing())
1101 global_status.dircache_size = dircache_get_cache_size();
1102 # ifdef HAVE_EEPROM_SETTINGS
1103 if (firmware_settings.initialized)
1104 dircache_save();
1105 # endif
1106 dircache_disable();
1108 else
1110 global_status.dircache_size = 0;
1112 if (old_val != global_status.dircache_size)
1113 status_save();
1115 #endif
1118 void tree_restore(void)
1120 #ifdef HAVE_EEPROM_SETTINGS
1121 firmware_settings.disk_clean = false;
1122 #endif
1124 #ifdef HAVE_TC_RAMCACHE
1125 remove(TAGCACHE_STATEFILE);
1126 #endif
1128 #ifdef HAVE_DIRCACHE
1129 remove(DIRCACHE_FILE);
1130 if (global_settings.dircache)
1132 /* Print "Scanning disk..." to the display. */
1133 splash(0, str(LANG_SCANNING_DISK));
1135 dircache_build(global_status.dircache_size);
1137 #endif
1138 #ifdef HAVE_TAGCACHE
1139 tagcache_start_scan();
1140 #endif