Index: apps/SOURCES
===================================================================
RCS file: /cvsroot/rockbox/apps/SOURCES,v
retrieving revision 1.45
diff -u -r1.45 SOURCES
--- apps/SOURCES	18 Jul 2006 13:54:12 -0000	1.45
+++ apps/SOURCES	19 Jul 2006 05:44:57 -0000
@@ -5,6 +5,7 @@
 abrepeat.c
 bookmark.c
 debug_menu.c
+dirwalker.c
 filetypes.c
 language.c
 main.c
Index: apps/playlist.c
===================================================================
RCS file: /cvsroot/rockbox/apps/playlist.c,v
retrieving revision 1.164
diff -u -r1.164 playlist.c
--- apps/playlist.c	18 Jul 2006 13:54:12 -0000	1.164
+++ apps/playlist.c	19 Jul 2006 05:45:20 -0000
@@ -89,6 +89,7 @@
 #include "dircache.h"
 #include "thread.h"
 #include "usb.h"
+#include "dirwalker.h"
 #ifdef HAVE_LCD_BITMAP
 #include "icons.h"
 #include "widgets.h"
@@ -158,7 +159,8 @@
 static int add_track_to_playlist(struct playlist_info* playlist,
                                  const char *filename, int position,
                                  bool queue, int seek_pos);
-static int directory_search_callback(char* filename, void* context);
+static int directory_search_callback(const char* folder,const char* file,
+                                const int file_attr,void *param);
 static int remove_track_from_playlist(struct playlist_info* playlist,
                                       int position, bool write);
 static int randomise_playlist(struct playlist_info* playlist,
@@ -685,21 +687,32 @@
     return insert_position;
 }
 
+
 /*
  * Callback for playlist_directory_tracksearch to insert track into
  * playlist.
  */
-static int directory_search_callback(char* filename, void* context)
+static int directory_search_callback(const char* folder,const char* file,
+                        const int file_attr,void *param)
 {
     struct directory_search_context* c =
-        (struct directory_search_context*) context;
+        (struct directory_search_context*) param;
     int insert_pos;
+    static char filename[MAX_PATH];
 
+    if ((file_attr & TREE_ATTR_MASK) != TREE_ATTR_MPA)
+        return DIRWALKER_OK;
+    /* user abort */
+    if (button_get(false) == SETTINGS_CANCEL)
+    {
+            return DIRWALKER_QUIT;
+    }
+    snprintf(filename,MAX_PATH,"%s/%s", folder, file);
     insert_pos = add_track_to_playlist(c->playlist, filename, c->position,
         c->queue, -1);
 
     if (insert_pos < 0)
-        return -1;
+        return DIRWALKER_ERROR;
     
     (c->count)++;
     
@@ -1287,194 +1300,124 @@
  * search through all the directories (starting with the current) to find
  * one that has tracks to play
  */
-static int get_next_dir(char *dir, bool is_forward, bool recursion)
-{
-    struct playlist_info* playlist = &current_playlist;
-    int result = -1;
-    int sort_dir = global_settings.sort_dir;
-    char *start_dir = NULL;
-    bool exit = false;
-    struct tree_context* tc = tree_get_context();
-    int dirfilter = *(tc->dirfilter);
-
-    if (recursion){
-       /* start with root */
-       dir[0] = '\0';
-    }
-    else{
-        /* start with current directory */
-        strncpy(dir, playlist->filename, playlist->dirlen-1);
-        dir[playlist->dirlen-1] = '\0';
-    }
-
-    /* use the tree browser dircache to load files */
-    *(tc->dirfilter) = SHOW_ALL;
-
-    /* sort in another direction if previous dir is requested */
-    if(!is_forward){
-       if ((global_settings.sort_dir == 0) || (global_settings.sort_dir == 3))
-           global_settings.sort_dir = 4;
-       else if (global_settings.sort_dir == 1)
-           global_settings.sort_dir = 2;
-       else if (global_settings.sort_dir == 2)
-           global_settings.sort_dir = 1;
-       else if (global_settings.sort_dir == 4)
-           global_settings.sort_dir = 0;
-    }
-
-    while (!exit)
-    {
-        struct entry *files;
-        int num_files = 0;
-        int i;
 
-        if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
+static int get_next_dir_folder_callback(const char* dir, void *param)
+{
+    char *start_dir = (char*)param;
+    /* user abort */
+    if (button_get(false) == SETTINGS_CANCEL)
+    {
+        return DIRWALKER_QUIT;
+    }
+    
+    if (!start_dir[0]) /* seen the start_dir already, so quit */
+    {
+        if (check_subdir_for_music((char*)dir,NULL) >= 0) /* only if we have music tho */
         {
-            gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
-            exit = true;
-            result = -1;
-            break;
+            strcpy(start_dir,dir);
+            return DIRWALKER_QUIT;
         }
-        
-        files = (struct entry*) tc->dircache;
-        num_files = tc->filesindir;
+    }
+    else if (!strcmp(start_dir,dir)) /* this is the start_dir */
+        start_dir[0] = '\0';
+    
+    return DIRWALKER_OK;
+}
 
-        for (i=0; i<num_files; i++)
+static int get_next_dir(char *dir, bool is_forward, bool recursion)
+{
+    int result;
+    struct playlist_info* playlist = &current_playlist;
+    char start_dir[MAX_PATH], *s;
+    
+    strncpy(start_dir, playlist->filename, playlist->dirlen-1);
+    start_dir[playlist->dirlen-1] = '\0';
+    s = strrchr(start_dir,'/');
+    strcpy(dir,start_dir);
+    if (s && s != start_dir)
+        *s = '\0'; /* start 1 directory up */
+    
+    result = dirwalker(start_dir,true,is_forward,get_next_dir_folder_callback,dir,NULL,0);                    
+    if (result != DIRWALKER_RETURN_ERROR)
+    {
+        if (result == DIRWALKER_RETURN_FORCED &&
+            strcmp(dir,start_dir))
         {
-            /* user abort */
-            if (button_get(false) == SETTINGS_CANCEL)
-            {
-                result = -1;
-                exit = true;
-                break;
-            }
-            
-            if (files[i].attr & ATTR_DIRECTORY)
-            {
-                if (!start_dir)
-                {
-                    result = check_subdir_for_music(dir, files[i].name);
-                    if (result != -1)
-                    {
-                        exit = true;
-                        break;
-                    }
-                }
-                else if (!strcmp(start_dir, files[i].name))
-                    start_dir = NULL;
-            }
+            return 0;
         }
-
-        if (!exit)
+        else if (recursion)
         {
-            /* move down to parent directory.  current directory name is
-               stored as the starting point for the search in parent */
-            start_dir = strrchr(dir, '/');
-            if (start_dir)
+            dir[0] = '\0';
+            result = dirwalker("/",true,is_forward,get_next_dir_folder_callback,dir,NULL,0);
+            if (result == DIRWALKER_RETURN_FORCED)
             {
-                *start_dir = '\0';
-                start_dir++;
+                return 0;
             }
-            else
-                break;
         }
     }
-
-    /* we've overwritten the dircache so tree browser will need to be
-       reloaded */
-    reload_directory();
-
-    /* restore dirfilter & sort_dir */
-    *(tc->dirfilter) = dirfilter;
-    global_settings.sort_dir = sort_dir;
-
-    /* special case if nothing found: try start searching again from root */
-    if (result == -1 && !recursion){
-        result = get_next_dir(dir,is_forward, true);
-    }
-
-    return result;
+    return -1;
 }
 
 /*
  * Checks if there are any music files in the dir or any of its
  * subdirectories.  May be called recursively.
  */
-static int check_subdir_for_music(char *dir, char *subdir)
+static int check_dir_folder_callback(const char* dir, void *param)
 {
-    int result = -1;
-    int dirlen = strlen(dir);
-    int num_files = 0;
-    int i;
-    struct entry *files;
-    bool has_music = false;
-    bool has_subdir = false;
-    struct tree_context* tc = tree_get_context();
-
-    snprintf(dir+dirlen, MAX_PATH-dirlen, "/%s", subdir);
-    
-    if (ft_load(tc, dir) < 0)
-    {
-        gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
-        return -2;
-    }
-    
-    files = (struct entry*) tc->dircache;
-    num_files = tc->filesindir;
-    
-    for (i=0; i<num_files; i++)
+    (void)dir;
+    char *info = (char*)param;
+    *info |= 0x1; /* has subdir */
+    /* user abort */
+    if (button_get(false) == SETTINGS_CANCEL)
     {
-        if (files[i].attr & ATTR_DIRECTORY)
-            has_subdir = true;
-        else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
-        {
-            has_music = true;
-            break;
-        }
+        return DIRWALKER_QUIT;
     }
+    return DIRWALKER_OK;
+}
 
-    if (has_music)
-        return 0;
-
-    if (has_subdir)
+static int check_dir_file_callback(const char* dir,const char* file,
+                                 const int file_attr, void *param)
+{
+    char *info = (char*)param;
+    (void)dir;(void)file;
+    /* user abort */
+    if (button_get(false) == SETTINGS_CANCEL)
     {
-        for (i=0; i<num_files; i++)
-        {
-            if (button_get(false) == SETTINGS_CANCEL)
-            {
-                result = -2;
-                break;
-            }
-
-            if (files[i].attr & ATTR_DIRECTORY)
-            {
-                result = check_subdir_for_music(dir, files[i].name);
-                if (!result)
-                    break;
-            }
-        }
+        return DIRWALKER_QUIT;
     }
-
-    if (result < 0)
+    
+    if ((file_attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
     {
-        if (dirlen)
-        {
-            dir[dirlen] = '\0';
-        }
-        else
-        {
-            strcpy(dir, "/");
+        *info |= 0x2;
+        return DIRWALKER_QUIT; /* an quit now */
+    }
+    return DIRWALKER_OK;
+}
+static int check_subdir_for_music(char *dir, char *subdir)
+{
+    char info = 0; /* 0x1 if has_subdir, 0x2 if has music */
+    int result;
+    static char dirname[MAX_PATH];
+    
+    if (subdir)
+        snprintf(dirname, MAX_PATH, "%s/%s", dir,subdir);
+    else strcpy(dirname,dir);
+    result = dirwalker(dirname,false,1,NULL,0,
+                       check_dir_file_callback,&info);                    
+    if (result != DIRWALKER_RETURN_ERROR)
+    {
+        if (info&0x2) /* has music */
+            return 0;
+        else if (info&0x1) /* has subdir */
+        {
+            result = dirwalker(dirname,true,1,check_dir_folder_callback,0,
+                               check_dir_file_callback,(void*)&info);
+            if ((result != DIRWALKER_RETURN_ERROR) && (info&0x2))
+                return 0;
         }
-
-        /* we now need to reload our current directory */
-        if(ft_load(tc, dir) < 0)
-            gui_syncsplash(HZ*2, true,
-                str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));        
     }
-
-    return result;
+    return -1;
 }
-
 /*
  * Returns absolute path of track
  */
@@ -2760,29 +2703,31 @@
         count_str = str(LANG_PLAYLIST_INSERT_COUNT);
 
     display_playlist_count(0, count_str);
-
     context.playlist = playlist;
     context.position = position;
     context.queue = queue;
     context.count = 0;
-
     cpu_boost(true);
-
-    result = playlist_directory_tracksearch(dirname, recurse,
-        directory_search_callback, &context);
-
-    sync_control(playlist, false);
+    
+    result = dirwalker(dirname,recurse,true,dirwalker_recurse_unless_userabort,
+                       0,directory_search_callback,(void*)&context);
 
     cpu_boost(false);
+    if (result != DIRWALKER_RETURN_ERROR)
+    {
 
-    display_playlist_count(context.count, count_str);
-
-    if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
-        audio_flush_and_reload_tracks();
-
+        sync_control(playlist, false);
+    
+    
+        display_playlist_count(context.count, count_str);
+    
+        if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
+            audio_flush_and_reload_tracks();
+    
 #ifdef HAVE_DIRCACHE
-    queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
+        queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
 #endif
+    }
 
     return result;
 }
@@ -3338,98 +3283,3 @@
 
     return result;
 }
-
-/*
- * Search specified directory for tracks and notify via callback.  May be
- * called recursively.
- */
-int playlist_directory_tracksearch(const char* dirname, bool recurse,
-                                   int (*callback)(char*, void*),
-                                   void* context)
-{
-    char buf[MAX_PATH+1];
-    int result = 0;
-    int num_files = 0;
-    int i;
-    struct entry *files;
-    struct tree_context* tc = tree_get_context();
-    int old_dirfilter = *(tc->dirfilter);
-
-    if (!callback)
-        return -1;
-
-    /* use the tree browser dircache to load files */
-    *(tc->dirfilter) = SHOW_ALL;
-
-    if (ft_load(tc, dirname) < 0)
-    {
-        gui_syncsplash(HZ*2, true, str(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
-        *(tc->dirfilter) = old_dirfilter;
-        return -1;
-    }
-
-    files = (struct entry*) tc->dircache;
-    num_files = tc->filesindir;
-
-    /* we've overwritten the dircache so tree browser will need to be
-       reloaded */
-    reload_directory();
-
-    for (i=0; i<num_files; i++)
-    {
-        /* user abort */
-        if (button_get(false) == SETTINGS_CANCEL)
-        {
-            result = -1;
-            break;
-        }
-
-        if (files[i].attr & ATTR_DIRECTORY)
-        {
-            if (recurse)
-            {
-                /* recursively add directories */
-                snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
-                result = playlist_directory_tracksearch(buf, recurse,
-                    callback, context);
-                if (result < 0)
-                    break;
-
-                /* we now need to reload our current directory */
-                if(ft_load(tc, dirname) < 0)
-                {
-                    result = -1;
-                    break;
-                }
-                    
-                files = (struct entry*) tc->dircache;
-                num_files = tc->filesindir;
-                if (!num_files)
-                {
-                    result = -1;
-                    break;
-                }
-            }
-            else
-                continue;
-        }
-        else if ((files[i].attr & TREE_ATTR_MASK) == TREE_ATTR_MPA)
-        {
-            snprintf(buf, sizeof(buf), "%s/%s", dirname, files[i].name);
-
-            if (callback(buf, context) != 0)
-            {
-                result = -1;
-                break;
-            }
-
-            /* let the other threads work */
-            yield();
-        }
-    }
-
-    /* restore dirfilter */
-    *(tc->dirfilter) = old_dirfilter;
-
-    return result;
-}
Index: apps/playlist_catalog.c
===================================================================
RCS file: /cvsroot/rockbox/apps/playlist_catalog.c,v
retrieving revision 1.1
diff -u -r1.1 playlist_catalog.c
--- apps/playlist_catalog.c	18 Jul 2006 13:54:12 -0000	1.1
+++ apps/playlist_catalog.c	19 Jul 2006 05:45:21 -0000
@@ -37,6 +37,7 @@
 #include "sprintf.h"
 #include "tree.h"
 #include "yesno.h"
+#include "dirwalker.h"
 
 #define PLAYLIST_CATALOG_CFG ROCKBOX_DIR "/playlist_catalog.config"
 #define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists"
@@ -355,9 +356,20 @@
 }
 
 /* Add specified track into playlist.  Callback from directory insert */
-static int add_track_to_playlist(char* filename, void* context)
+static int add_track_to_playlist(const char* folder,const char* file,
+                                 const int file_attr,void *param)
 {
-    struct add_track_context* c = (struct add_track_context*) context;
+    static char filename[MAX_PATH];
+    struct add_track_context* c = (struct add_track_context*) param;
+    
+    if ((file_attr & TREE_ATTR_MASK) != TREE_ATTR_MPA)
+        return DIRWALKER_OK;
+    /* user abort */
+    if (button_get(false) == SETTINGS_CANCEL)
+    {
+        return DIRWALKER_QUIT;
+    }
+    snprintf(filename,MAX_PATH,"%s/%s", folder, file);
 
     if (fdprintf(c->fd, "%s\n", filename) <= 0)
         return -1;
@@ -448,8 +460,9 @@
 
         display_insert_count(0);
 
-        result = playlist_directory_tracksearch(sel, recurse,
-            add_track_to_playlist, &context);
+        result = dirwalker(sel,recurse,true,
+                           dirwalker_recurse_unless_userabort,0,
+                           add_track_to_playlist,(void*)&context);
 
         display_insert_count(context.count);
     }
Index: apps/plugin.c
===================================================================
RCS file: /cvsroot/rockbox/apps/plugin.c,v
retrieving revision 1.173
diff -u -r1.173 plugin.c
--- apps/plugin.c	1 Jul 2006 10:14:26 -0000	1.173
+++ apps/plugin.c	19 Jul 2006 05:45:22 -0000
@@ -448,6 +448,7 @@
 #else
     {&screens[SCREEN_MAIN]},
 #endif
+    dirwalker
 };
 
 int plugin_load(const char* plugin, void* parameter)
Index: apps/plugin.h
===================================================================
RCS file: /cvsroot/rockbox/apps/plugin.h,v
retrieving revision 1.181
diff -u -r1.181 plugin.h
--- apps/plugin.h	2 Jul 2006 12:28:27 -0000	1.181
+++ apps/plugin.h	19 Jul 2006 05:45:23 -0000
@@ -36,6 +36,7 @@
 #include <sys/types.h>
 #include "config.h"
 #include "dir.h"
+#include "dirwalker.h"
 #include "kernel.h"
 #include "button.h"
 #include "usb.h"
@@ -524,6 +525,11 @@
     void (*reload_directory)(void);
     bool (*set_bool)(const char* string, bool* variable );
     struct screen* screens[NB_SCREENS];
+    
+    int (*dirwalker)(const char *start_dir,bool recurse, bool is_forward,
+           int (*folder_callback)(const char* dir, void *param),void* folder_param,
+           int (*file_callback)(const char* folder,const char* file,
+           const int file_attr,void *param),void* file_param);
 };
 
 /* plugin header */
Index: apps/plugins/stats.c
===================================================================
RCS file: /cvsroot/rockbox/apps/plugins/stats.c,v
retrieving revision 1.10
diff -u -r1.10 stats.c
--- apps/plugins/stats.c	2 Apr 2006 20:47:42 -0000	1.10
+++ apps/plugins/stats.c	19 Jul 2006 05:45:39 -0000
@@ -46,10 +46,8 @@
 #define STATS_STOP_REMOTE BUTTON_RC_STOP
 #endif
 
-/* TODO: Better get the exts from the filetypes var in tree.c */
-const char *music_exts[] = {"mp3","mp2","mp1","mpa","ogg",
-        "wav","flac","ac3","a52","mpc","wv","m4a","mp4","shn",
-        "aif","aiff"};
+/* stolen from tree.h */
+#define TREE_ATTR_MPA   0x0300 /* mpeg audio file */
 
 void prn(const char *str, int y)
 {
@@ -65,7 +63,7 @@
 
 void update_screen(void)
 {
-    char buf[15];
+    char buf[64];
 #ifdef HAVE_LCD_BITMAP
     rb->lcd_clear_display();
 #ifdef HAVE_REMOTE_LCD
@@ -91,61 +89,44 @@
     prn(buf,1);
 #endif
 }
-
-void traversedir(char* location, char* name)
+bool check_and_update(void)
 {
     int button;
-    struct dirent *entry;
-    DIR* dir;
-    char fullpath[MAX_PATH];
-
-    rb->snprintf(fullpath, sizeof(fullpath), "%s/%s", location, name);
-    dir = rb->opendir(fullpath);
-    if (dir) {
-        entry = rb->readdir(dir);
-        while (entry) {
-            if (abort == true)
-                break;
-            /* Skip .. and . */
-            if (rb->strcmp(entry->d_name, ".") && rb->strcmp(entry->d_name, ".."))
-            {
-                if (entry->attribute & ATTR_DIRECTORY) {
-                    traversedir(fullpath, entry->d_name);
-                    dirs++;
-                }
-                else {
-                    char *ptr = rb->strrchr(entry->d_name,'.'); 
-                    files++;
-                    /* Might want to only count .mp3, .ogg etc. */
-                    if(ptr){
-                        unsigned i;
-                        ptr++;
-                        for(i=0;i<sizeof(music_exts)/sizeof(char*);i++)
-                            if(!rb->strcasecmp(ptr,music_exts[i])){
-                                musicfiles++; break;
-                            }
-                        
-                    }
-                }
-            }
-            if (*rb->current_tick - lasttick > (HZ/2)) {
-                update_screen();
-                lasttick = *rb->current_tick;
-                button = rb->button_get(false);
-                if (button == STATS_STOP
-#ifdef HAVE_REMOTE_LCD
-                    || button == STATS_STOP_REMOTE
-#endif
-                    ) {
-                    abort = true;
-                    break;
-                }
-            }
-
-            entry = rb->readdir(dir);
-        }
-        rb->closedir(dir);
+    if (*rb->current_tick - lasttick > (HZ/2)) {
+        update_screen();
+        lasttick = *rb->current_tick;
+        button = rb->button_get(false);
+        if (button == STATS_STOP
+#ifdef HAVE_REMOTE_LCD
+            || button == STATS_STOP_REMOTE
+#endif
+           ) {
+            return true;
+           }
+           update_screen();
     }
+    return false;
+}
+
+int folder_callback(const char* dir, void *param)
+{
+    (void)dir;(void)param;
+    dirs++;
+    if (check_and_update())
+        return DIRWALKER_QUIT;
+    return DIRWALKER_OK;
+}
+
+int file_callback(const char* folder,const char* file,
+                     const int file_attr,void *param)
+{
+    (void)folder;(void)file;(void)param;
+    if (file_attr & TREE_ATTR_MPA)
+        musicfiles++;
+    files++;
+    if (check_and_update())
+        return DIRWALKER_QUIT;
+    return DIRWALKER_OK;
 }
 
 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
@@ -158,6 +139,7 @@
     files = 0;
     dirs = 0;
     musicfiles = 0;
+
     abort = false;
 
 #ifdef HAVE_LCD_BITMAP
@@ -167,8 +149,9 @@
     update_screen();
     lasttick = *rb->current_tick;
 
-    traversedir("", "");
-    if (abort == true) {
+    if (rb->dirwalker("/",true,folder_callback,0,file_callback,0) 
+        != DIRWALKER_RETURN_SUCCESS)
+    {
         rb->splash(HZ, true, "Aborted");
         return PLUGIN_OK;
     }
