raster pushed a commit to branch master.

http://git.enlightenment.org/apps/rage.git/commit/?id=b4b380ce1f282ddb0c0f6c5279b5cb0f58e8d528

commit b4b380ce1f282ddb0c0f6c5279b5cb0f58e8d528
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
Date:   Sat Oct 10 16:51:49 2015 +0900

    rage - add "browser" when run with no args - index ~/Videos
    
    this adds a video browser that indexes everything in ~/Videos
    flattening subdirs into categories. It also will handle music and
    fetch album art. browser can be mouse or key controlled. makes rage
    almost media-centerey. not intended as a replacement tho, but just
    that it's handy to throw in.
---
 data/themes/default.edc | 208 ++++++++++++
 src/bin/Makefile.mk     |   4 +
 src/bin/albumart.c      |  33 +-
 src/bin/albumart.h      |   4 +-
 src/bin/browser.c       | 828 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/bin/browser.h       |   9 +
 src/bin/key.c           |   5 +-
 src/bin/main.c          |  23 +-
 src/bin/thumb.c         | 158 ++++++---
 src/bin/video.c         |   4 +-
 src/bin/videothumb.c    | 299 +++++++++++++----
 src/bin/videothumb.h    |   1 +
 src/bin/win.c           |  41 ++-
 src/bin/win.h           |   1 +
 src/bin/winvid.c        |  18 +-
 15 files changed, 1487 insertions(+), 149 deletions(-)

diff --git a/data/themes/default.edc b/data/themes/default.edc
index 227ab8b..8ba5c1c 100644
--- a/data/themes/default.edc
+++ b/data/themes/default.edc
@@ -497,6 +497,41 @@ collections {
             }
          }
 
+         part { name: "rage.browser"; type: SWALLOW;
+            description { state: "default" 0.0;
+               rel1.relative: 1.0 0.0;
+               rel1.offset: 40 0;
+               rel2.relative: 2.0 0.0;
+               rel2.offset: -41 -1;
+               rel2.to_y: "controlbar";
+               fixed: 1 1;
+               visible: 0;
+            }
+            description { state: "visible" 0.0;
+               inherit: "default" 0.0;
+               rel1.relative: 0.0 0.0;
+               rel2.relative: 1.0 0.0;
+               color: 255 255 255 255;
+               visible: 1;
+            }
+         }
+         program {
+            signal: "browser,state,visible"; source: "rage";
+            action: STATE_SET "visible" 0.0;
+            transition: DECELERATE 0.3;
+            target: "rage.browser";
+         }
+         program {
+            signal: "browser,state,hidden"; source: "rage";
+            action: STATE_SET "default" 0.0;
+            transition: ACCELERATE 0.5;
+            target: "rage.browser";
+            after: "browser_hidden";
+         }
+         program { name: "browser_hidden";
+            action: SIGNAL_EMIT "browser,state,hidden,finished" "rage";
+         }
+
          part { name: "rage.list"; type: SWALLOW;
             description { state: "default" 0.0;
                align: 0.0 0.5;
@@ -2073,4 +2108,177 @@ collections {
          }
       }
    }
+
+
+
+
+
+
+
+   group { name: "rage/browser/entry";
+      parts {
+         part { name: "rage.title"; type: TEXT; mouse_events: 0;
+            scale: 1;
+            description { state: "default" 0.0;
+               fixed: 0 1;
+               align: 0.0 0.0;
+               rel1.offset: 0 40;
+               rel2.offset: -1 40;
+               rel2.relative: 1.0 0.0;
+               color: 255 255 255 255;
+               text { font: "Sans:style=Bold"; size: 20;
+                  align: 0.0 0.0;
+                  min: 0 1;
+                  elipsis: 0;
+               }
+            }
+         }
+         part { name: "rage.content"; type: SWALLOW;
+            description { state: "default" 0.0;
+               rel1.to_y: "rage.title";
+               rel1.relative: 0.0 1.0;
+               rel1.offset: 0 40;
+               rel2.offset: -1 -41;
+            }
+         }
+      }
+   }
+
+   group { name: "rage/browser/item";
+      images.image: "win_shadow.png" COMP;
+      images.image: "win_glow.png" COMP;
+      images.image: "bg_shine.png" COMP;
+      images.image: "bg_glint.png" COMP;
+
+      parts {
+         part { name: "shadow"; mouse_events: 0;
+            description { state: "default" 0.0;
+               fixed: 1 1;
+               rel1.to: "clip";
+               rel2.to: "clip";
+               image.normal: "win_shadow.png";
+               image.border: 14 14 14 14;
+               image.middle: 0;
+               rel1.offset: -7  -3;
+               rel2.offset: 6 11;
+               fill.smooth: 0;
+            }
+         }
+         part { name: "base"; type: RECT; mouse_events: 0;
+            description { state: "default" 0.0;
+               color: 0 0 0 255;
+               rel1.to: "clip";
+               rel2.to: "clip";
+            }
+         }
+         part { name: "glow"; mouse_events: 0;
+            description { state: "default" 0.0;
+               image.normal: "win_glow.png";
+               image.border: 9 9 9 9;
+               image.middle: 0;
+               rel1.to: "clip";
+               rel1.offset: -5  -5;
+               rel2.to: "clip";
+               rel2.offset: 4 4;
+               fill.smooth: 0;
+               color: 255 255 255 0;
+               visible: 0;
+            }
+            description { state: "selected" 0.0;
+               inherit: "default" 0.0;
+               color: 255 255 255 255;
+               visible: 1;
+            }
+         }
+         program {
+            signal: "rage,state,selected"; source: "rage";
+            action: STATE_SET "selected" 0.0;
+            transition: SINUSOIDAL 0.1;
+            target: "glow";
+         }
+         program {
+            signal: "rage,state,unselected"; source: "rage";
+            action: STATE_SET "default" 0.0;
+            transition: SINUSOIDAL 0.3;
+            target: "glow";
+         }
+
+         part { name: "clip"; type: RECT;
+            description { state: "default" 0.0;
+               rel1.offset: 4 4;
+               rel2.offset: -5 -5;
+            }
+         }
+         part { name: "glintclip"; type: RECT;
+            description { state: "default" 0.0;
+               rel1.to: "clip";
+               rel2.to: "clip";
+               rel1.offset: 0 -10;
+            }
+         }
+         part { name: "rage.content"; type: SWALLOW;
+            clip_to: "clip";
+            description { state: "default" 0.0;
+               rel1.to: "clip";
+               rel2.to: "clip";
+            }
+         }
+         part { name: "shine"; mouse_events: 0;
+            clip_to: "clip";
+            description { state: "default" 0.0;
+               image.normal: "bg_shine.png";
+               fill.smooth: 0;
+               rel1.to: "clip";
+               rel2.to: "clip";
+               align: 0.5 0.0;
+               aspect: (255/120) (255/120);
+               aspect_preference: HORIZONTAL;
+            }
+         }
+         part { name: "glint"; mouse_events: 0;
+            clip_to: "glintclip";
+            description { state: "default" 0.0;
+               fixed: 1 1;
+               min: 79 5;
+               max: 79 5;
+               rel1 {
+                 relative: 0.0 0.0;
+                 offset: 0 0;
+                 to: "clip";
+               }
+               rel2 {
+                 relative: 1.0 0.0;
+                 offset: -1 0;
+                 to: "clip";
+               }
+               image.normal: "bg_glint.png";
+            }
+         }
+         part { name: "rage.title"; type: TEXT; mouse_events: 0;
+            scale: 1;
+            description { state: "default" 0.0;
+               fixed: 1 1;
+               align: 0.0 1.0;
+               rel1.offset: 4 4;
+               rel2.offset: -5 -5;
+               color: 255 255 255 255;
+               text { font: "Sans"; size: 12;
+                  align: 0.0 1.0;
+                  min: 0 1;
+                  elipsis: 0;
+               }
+            }
+         }
+         part { name: "event"; type: RECT;
+            ignore_flags: ON_HOLD;
+            description { state: "default" 0.0;
+               color: 0 0 0 0;
+            }
+         }
+         program {
+            signal: "mouse,clicked,1"; source: "event";
+            action: SIGNAL_EMIT "rage,selected" "rage";
+         }
+      }
+   }
 }
diff --git a/src/bin/Makefile.mk b/src/bin/Makefile.mk
index 6e39b02..e16a942 100644
--- a/src/bin/Makefile.mk
+++ b/src/bin/Makefile.mk
@@ -9,6 +9,8 @@ src_bin_rage_LDADD = @RAGE_LIBS@
 src_bin_rage_SOURCES = \
 src/bin/albumart.c \
 src/bin/albumart.h \
+src/bin/browser.c \
+src/bin/browser.h \
 src/bin/config.c \
 src/bin/config.h \
 src/bin/controls.c \
@@ -39,6 +41,8 @@ internal_bin_PROGRAMS = src/bin/rage_thumb
 
 src_bin_rage_thumb_SOURCES = \
 src/bin/thumb.c \
+src/bin/albumart.c \
+src/bin/albumart.h \
 src/bin/sha1.c \
 src/bin/sha1.h
 
diff --git a/src/bin/albumart.c b/src/bin/albumart.c
index 1e635ae..d32a532 100644
--- a/src/bin/albumart.c
+++ b/src/bin/albumart.c
@@ -17,7 +17,9 @@ static Eina_Strbuf *sb_result = NULL;
 static Eina_Bool fetch_image = EINA_FALSE;
 static char *fetchfile = NULL;
 static FILE *fout = NULL;
-static Evas_Object *fetchwin = NULL;
+static void (*_fetch_done) (void *data) = NULL;
+static void *_fetch_data = NULL;
+
 
 static char *
 _inpath(const char *file)
@@ -107,17 +109,10 @@ _cb_http_complete(void *data EINA_UNUSED, int type 
EINA_UNUSED, void *event)
    if (ev->url_con != fetch) return EINA_TRUE;
    if (fetch_image)
      {
-        char *path;
-
         fetch_image = EINA_FALSE;
         fclose(fout);
         fout = NULL;
-        path = _thumbpath(fetchfile);
-        if (path)
-          {
-             win_art(fetchwin, path);
-             free(path);
-          }
+        if (_fetch_done) _fetch_done(_fetch_data);
      }
    else
      {
@@ -190,7 +185,10 @@ _cb_http_complete(void *data EINA_UNUSED, int type 
EINA_UNUSED, void *event)
              free(fetchfile);
              fetchfile = NULL;
           }
+        if (_fetch_done) _fetch_done(_fetch_data);
      }
+   _fetch_done = NULL;
+   _fetch_data = NULL;
    return EINA_FALSE;
 }
 
@@ -225,9 +223,10 @@ _search_append(Eina_Strbuf *sb, const char *str, Eina_Bool 
hadword)
 }
 
 void
-albumart_find(Evas_Object *win, Evas_Object *vid)
+albumart_find(const char *file,
+              const char *artist, const char *album, const char *title,
+              void (*fetch_done) (void *data), void *fetch_data)
 {
-   const char *file, *album, *artist, *title;
    Eina_Strbuf *sb;
    char *path;
 
@@ -252,7 +251,7 @@ albumart_find(Evas_Object *win, Evas_Object *vid)
         sb_result = NULL;
      }
    fetch_image = EINA_FALSE;
-   if (!vid) return;
+   if (!file) return;
 
    if (!handle_data)
      handle_data = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA,
@@ -261,16 +260,18 @@ albumart_find(Evas_Object *win, Evas_Object *vid)
      handle_complete = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE,
                                                _cb_http_complete, NULL);
 
-   file = video_file_get(vid);
    if (!file) return;
    fetchfile = _inpath(file);
 
+   _fetch_done = fetch_done;
+   _fetch_data = _fetch_data;
+
    path = _thumbpath(fetchfile);
    if (path)
      {
         if (ecore_file_exists(path))
           {
-             win_art(win, path);
+             if (fetch_done) fetch_done(fetch_data);
              free(path);
              free(fetchfile);
              fetchfile = NULL;
@@ -278,14 +279,10 @@ albumart_find(Evas_Object *win, Evas_Object *vid)
           }
         else free(path);
      }
-   fetchwin = win;
 
    sb = eina_strbuf_new();
    eina_strbuf_append(sb, Q_START);
 
-   title = video_meta_title_get(vid);
-   artist = video_meta_artist_get(vid);
-   album = video_meta_album_get(vid);
    if ((title) || (album) || (artist))
      {
         Eina_Bool had = EINA_FALSE;
diff --git a/src/bin/albumart.h b/src/bin/albumart.h
index 3a803c3..d19bb73 100644
--- a/src/bin/albumart.h
+++ b/src/bin/albumart.h
@@ -1,7 +1,9 @@
 #ifndef _ALBUMART_H__
 #define _ALBUMART_H__ 1
 
-void albumart_find(Evas_Object *win, Evas_Object *vid);
+void albumart_find(const char *file,
+                   const char *artist, const char *album, const char *title,
+                   void (*fetch_done) (void *data), void *fetch_data);
 char *albumart_file_get(const char *file);
 
 #endif
diff --git a/src/bin/browser.c b/src/bin/browser.c
new file mode 100644
index 0000000..de771fe
--- /dev/null
+++ b/src/bin/browser.c
@@ -0,0 +1,828 @@
+#include <Elementary.h>
+#include "main.h"
+#include "win.h"
+#include "winvid.h"
+#include "browser.h"
+#include "videothumb.h"
+#include "key.h"
+
+typedef struct _Message Message;
+typedef struct _Entry Entry;
+
+typedef enum _Type
+{
+   TYPE_NEW,
+   TYPE_UPDATE,
+   TYPE_FINISH
+} Type;
+
+struct _Message
+{
+   Type type;
+   Entry *entry;
+};
+
+struct _Entry
+{
+   Eina_Lock lock;
+   Entry *parent; // what is above
+   Eina_Stringshare *path; // full path
+   Eina_List *dirs; // entries fo subdir entires
+   Eina_List *files; // strings of just filenames in path dir
+   Eina_List *sels;
+   Evas_Object *base;
+   Evas_Object *box;
+   Evas_Object *table;
+   Evas_Object *sizer;
+   Evas_Coord iw, ih;
+   int cols, rows;
+   int sel_x, sel_y;
+   Eina_Bool sel : 1;
+};
+
+static Evas_Object *bx = NULL;
+static Evas_Object *sc, *bt;
+static Ecore_Thread *fill_thread = NULL;
+static Entry *dir_entry = NULL;
+
+static void
+_item_size_get(Evas_Object *win, Evas_Coord *w, Evas_Coord *h)
+{
+   Evas_Coord sz = 0;
+
+   elm_coords_finger_size_adjust(1, &sz, 1, &sz);
+   evas_object_geometry_get(win, NULL, NULL, w, h);
+   *w = *w / 5;
+   *h = *h / 5;
+   if (*w < sz) *w = sz;
+   if (*h < sz) *h = sz;
+}
+
+static Eina_Bool
+_video_ok(const char *path)
+{
+   const char *exts[] =
+     {
+        ".asf", ".avi", ".bdm", ".bdmv", ".clpi", ".cpi", ".dv", ".fla",
+        ".flv", ".m1v", ".m2t", ".m2v", ".m4v", ".mkv", ".mov", ".mp2",
+        ".mp2ts", ".mp4", ".mpe", ".mpeg", ".mpg", ".mpl", ".mpls", ".mts",
+        ".mxf", ".nut", ".nuv", ".ogg", ".ogm", ".ogv", ".qt", ".rm", ".rmj",
+        ".rmm", ".rms", ".rmvb", ".rmx", ".rv", ".swf", ".ts", ".weba",
+        ".webm", ".wmv", ".3g2", ".3gp", ".3gp2", ".3gpp", ".3gpp2", ".3p2",
+        ".264",
+        NULL
+     };
+   int i;
+   const char *ext = strrchr(path, '.');
+   if (!ext) return EINA_FALSE;
+   for (i = 0; exts[i]; i++)
+     {
+        if (!strcasecmp(ext, exts[i])) return EINA_TRUE;
+     }
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_audio_ok(const char *path)
+{
+   const char *exts[] =
+     {
+        ".mp3", ".m4a", ".oga", ".aac", ".flac", ".wav",
+        NULL
+     };
+   int i;
+   const char *ext = strrchr(path, '.');
+   if (!ext) return EINA_FALSE;
+   for (i = 0; exts[i]; i++)
+     {
+        if (!strcasecmp(ext, exts[i])) return EINA_TRUE;
+     }
+   return EINA_FALSE;
+}
+
+static void
+_fill_message(Ecore_Thread *th, Type type, Entry *entry)
+{
+   Message *message = calloc(1, sizeof(Message));
+   if (!message) return;
+   message->type = type;
+   message->entry = entry;
+   ecore_thread_feedback(th, message);
+}
+
+static Entry *
+_fill_scan(Ecore_Thread *th, Entry *parent, const char *dir)
+{
+   Eina_List *files;
+   char *file;
+   Entry *entry = NULL;
+
+   entry = calloc(1, sizeof(Entry));
+   if (!entry) return NULL;
+   files = ecore_file_ls(dir);
+   if (!files)
+     {
+        free(entry);
+        return NULL;
+     }
+   eina_lock_new(&(entry->lock));
+   entry->parent = parent;
+   entry->path = eina_stringshare_add(dir);
+   if (parent)
+     {
+        eina_lock_take(&(parent->lock));
+        parent->dirs = eina_list_append(parent->dirs, entry);
+        eina_lock_release(&(parent->lock));
+     }
+   _fill_message(th, TYPE_NEW, entry);
+   EINA_LIST_FREE(files, file)
+     {
+        if (!ecore_thread_check(th))
+          {
+             if (file[0] != '.')
+               {
+                  char buf[PATH_MAX];
+
+                  snprintf(buf, sizeof(buf), "%s/%s", dir, file);
+                  if (ecore_file_is_dir(buf))
+                    {
+                       _fill_scan(th, entry, buf);
+                       _fill_message(th, TYPE_UPDATE, entry);
+                    }
+                  else
+                    {
+                       if (_video_ok(file) || _audio_ok(file))
+                         {
+                            eina_lock_take(&(entry->lock));
+                            entry->files = eina_list_append
+                              (entry->files, eina_stringshare_add(file));
+                            eina_lock_release(&(entry->lock));
+                            _fill_message(th, TYPE_UPDATE, entry);
+                         }
+                    }
+               }
+          }
+        free(file);
+     }
+   _fill_message(th, TYPE_FINISH, entry);
+   return entry;
+}
+
+static void
+_fill_thread(void *data EINA_UNUSED, Ecore_Thread *th)
+{
+   char buf[PATH_MAX];
+
+   snprintf(buf, sizeof(buf), "%s/Videos", eina_environment_home_get());
+   _fill_scan(th, NULL, buf);
+}
+
+static void
+_cb_vidthumb_data(void *data EINA_UNUSED, Evas_Object *obj, void *event 
EINA_UNUSED)
+{
+   int w, h;
+
+   videothumb_size_get(obj, &w, &h);
+   evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_BOTH, w, h);
+}
+
+static void
+_activate(Evas_Object *win, Entry *entry, const char *file)
+{
+   Eina_List *list = NULL;
+   Winvid_Entry *vid;
+   char buf[PATH_MAX];
+
+   vid = calloc(1, sizeof(Winvid_Entry));
+   if (vid)
+     {
+        snprintf(buf, sizeof(buf), "%s/%s", entry->path, file);
+        vid->file = eina_stringshare_add(buf);
+        list = eina_list_append(list, vid);
+     }
+   win_video_file_list_set(win, list);
+   EINA_LIST_FREE(list, vid)
+     {
+        if (vid->file) eina_stringshare_del(vid->file);
+        if (vid->sub) eina_stringshare_del(vid->sub);
+        if (vid->uri) efreet_uri_free(vid->uri);
+        free(vid);
+     }
+   browser_hide(win);
+}
+
+static void
+_cb_file_selected(void *data, Evas_Object *obj, const char *sig EINA_UNUSED, 
const char *src EINA_UNUSED)
+{
+   Evas_Object *win = data;
+   Entry *entry = evas_object_data_get(obj, "entry");
+   const char *file = evas_object_data_get(obj, "file");
+
+   elm_layout_signal_emit(obj, "rage,state,selected", "rage");
+   _activate(win, entry, file);
+}
+
+static void
+_entry_files_pop(Evas_Object *win, Entry *entry)
+{
+   Evas_Object *o, *base;
+   int i = 0, j = 0;
+   Eina_List *l;
+   const char *file;
+   char buf[PATH_MAX], *p;
+
+   if (evas_object_data_get(entry->table, "populated")) return;
+   evas_object_data_set(entry->table, "populated", entry->table);
+
+   EINA_LIST_FOREACH(entry->files, l, file)
+     {
+        base = o = elm_layout_add(win);
+        entry->sels = eina_list_append(entry->sels, o);
+        evas_object_data_set(o, "entry", entry);
+        evas_object_data_set(o, "file", file);
+        elm_object_focus_allow_set(o, EINA_FALSE);
+        snprintf(buf, sizeof(buf), "%s/themes/default.edj", 
elm_app_data_dir_get());
+        elm_layout_file_set(o, buf, "rage/browser/item");
+        snprintf(buf, sizeof(buf), "%s", file);
+        p = strrchr(buf, '.');
+        if (p) *p = 0;
+        elm_object_part_text_set(o, "rage.title", buf);
+        evas_object_size_hint_weight_set(o, 0.0, 0.0);
+        evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
+        elm_table_pack(entry->table, o, i, j, 1, 1);
+        evas_object_show(o);
+
+        elm_layout_signal_callback_add(o, "rage,selected", "rage", 
_cb_file_selected, win);
+
+        o = videothumb_add(win);
+        evas_object_smart_callback_add(o, "data", _cb_vidthumb_data, base);
+        evas_object_data_set(o, "entry", entry);
+        evas_object_data_set(o, "file", file);
+        snprintf(buf, sizeof(buf), "%s/%s", entry->path, file);
+        videothumb_file_set(o, buf, 0.0);
+        videothumb_autocycle_set(o, EINA_TRUE);
+        elm_object_part_content_set(base, "rage.content", o);
+        evas_object_show(o);
+
+        if ((entry->sel) && (entry->sel_x == i) && (entry->sel_y == j))
+          elm_layout_signal_emit(base, "rage,state,selected", "rage");
+
+        i++;
+        if (i == entry->cols)
+          {
+             i = 0;
+             j++;
+          }
+     }
+  if ((entry->cols > 0) && (entry->rows > 0))
+     elm_table_pack(entry->table, entry->sizer, 0, 0, entry->cols, 
entry->rows);
+   else
+     elm_table_pack(entry->table, entry->sizer, 0, 0, 1, 1);
+}
+
+static void
+_entry_files_unpop(Evas_Object *win EINA_UNUSED, Entry *entry)
+{
+   evas_object_size_hint_min_set(entry->sizer,
+                                 entry->cols * entry->iw,
+                                 entry->rows * entry->ih);
+   if (!evas_object_data_get(entry->table, "populated")) return;
+   entry->sels = eina_list_free(entry->sels);
+   evas_object_data_del(entry->table, "populated");
+   elm_table_unpack(entry->table, entry->sizer);
+   elm_table_clear(entry->table, EINA_TRUE);
+  if ((entry->cols > 0) && (entry->rows > 0))
+     elm_table_pack(entry->table, entry->sizer, 0, 0, entry->cols, 
entry->rows);
+   else
+     elm_table_pack(entry->table, entry->sizer, 0, 0, 1, 1);
+}
+
+static void
+_entry_files_redo(Evas_Object *win, Entry *entry)
+{
+   Evas_Coord x, y,w, h, iw = 1, ih = 1, ww, wh;
+   int num, cols, rows;
+   Eina_Rectangle r1, r2;
+
+   eina_lock_take(&(entry->lock));
+
+   num = eina_list_count(entry->files);
+   evas_object_geometry_get(win, NULL, NULL, &ww, &wh);
+   evas_object_geometry_get(entry->table, &x, &y, &w, &h);
+   if (w < 40) goto done;
+
+   if (w > (ww - 20)) w = (ww - 20);
+
+   _item_size_get(win, &iw, &ih);
+   cols = w / iw;
+   if (cols < 1) cols = 1;
+   if (cols > 0) rows = (num + (cols - 1)) / cols;
+   else rows = 0;
+
+   entry->iw = iw;
+   entry->ih = ih;
+   entry->cols = cols;
+   entry->rows = rows;
+
+   r1.x = 0; r1.y = 0; r1.w = ww; r1.h = wh;
+   r2.x = x; r2.y = y; r2.w = w; r2.h = h;
+
+   _entry_files_unpop(win, entry);
+   if (eina_rectangles_intersect(&r1, &r2)) _entry_files_pop(win, entry);
+
+done:
+   eina_lock_release(&(entry->lock));
+}
+
+static void
+_cb_entry_table_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*info EINA_UNUSED)
+{
+   Entry *entry = data;
+   Evas_Object *win = evas_object_data_get(obj, "win");
+   Evas_Coord x, y,w, h, ww, wh;
+   Eina_Rectangle r1, r2;
+
+   eina_lock_take(&(entry->lock));
+
+   evas_object_geometry_get(win, NULL, NULL, &ww, &wh);
+   evas_object_geometry_get(entry->table, &x, &y, &w, &h);
+   if (w < 40) goto done;
+
+   if (w > (ww - 20)) w = (ww - 20);
+
+   r1.x = 0; r1.y = 0; r1.w = ww; r1.h = wh;
+   r2.x = x; r2.y = y; r2.w = w; r2.h = h;
+
+   if (eina_rectangles_intersect(&r1, &r2)) _entry_files_pop(win, entry);
+   else _entry_files_unpop(win, entry);
+
+done:
+   eina_lock_release(&(entry->lock));
+}
+
+static void
+_cb_entry_table_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*info EINA_UNUSED)
+{
+   Entry *entry = data;
+   Evas_Object *win = evas_object_data_get(obj, "win");
+   _entry_files_redo(win, entry);
+}
+
+static void
+_fill_feedback(void *data, Ecore_Thread *th, void *msg)
+{
+   Evas_Object *win = data;
+   Message *message = msg;
+   Evas_Object *o;
+   char buf[PATH_MAX];
+
+   if ((th == fill_thread) && (bx))
+     {
+        Entry *entry = message->entry;
+
+        if ((message->type == TYPE_NEW) && (!dir_entry)) dir_entry = entry;
+
+        if (message->type == TYPE_NEW)
+          {
+             eina_lock_take(&(entry->lock));
+             if ((entry->dirs) || (entry->files))
+               {
+                  if (!entry->base)
+                    {
+                       const char *file;
+
+                       entry->base = o = elm_layout_add(win);
+                       elm_object_focus_allow_set(o, EINA_FALSE);
+                       snprintf(buf, sizeof(buf), "%s/themes/default.edj", 
elm_app_data_dir_get());
+                       elm_layout_file_set(o, buf, "rage/browser/entry");
+                       evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 
0.0);
+                       evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
+
+                       if (entry->parent)
+                         {
+                            file = entry->path + strlen(dir_entry->path) + 1;
+                            elm_object_part_text_set(o, "rage.title", file);
+                         }
+
+                       entry->box = o = elm_box_add(win);
+                       evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 
EVAS_HINT_EXPAND);
+                       evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 
EVAS_HINT_FILL);
+                       elm_object_part_content_set(entry->base, 
"rage.content", o);
+                       evas_object_show(o);
+
+                       entry->table = o = elm_table_add(win);
+                       elm_table_homogeneous_set(o, EINA_TRUE);
+                       evas_object_data_set(o, "win", win);
+                       evas_object_event_callback_add(o, EVAS_CALLBACK_MOVE, 
_cb_entry_table_move, entry);
+                       evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE, 
_cb_entry_table_resize, entry);
+                       evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 
0.0);
+                       evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
+                       elm_box_pack_end(entry->box, o);
+                       evas_object_show(o);
+
+                       entry->sizer = o = 
evas_object_rectangle_add(evas_object_evas_get(win));
+                       evas_object_color_set(o, 0, 0, 0, 0);
+                       evas_object_size_hint_min_set(o, 10, 10);
+                       elm_table_pack(entry->table, o, 0, 0, 1, 1);
+                       evas_object_show(o);
+
+                       if (!entry->parent)
+                         elm_box_pack_end(bx, entry->base);
+                       else
+                         elm_box_pack_end(entry->parent->box, entry->base);
+                       evas_object_show(entry->base);
+                    }
+               }
+             eina_lock_release(&(entry->lock));
+          }
+        else if ((message->type == TYPE_FINISH) && (entry->parent))
+          {
+             _entry_files_redo(win, entry);
+          }
+     }
+   free(msg);
+}
+
+static void
+_fill_end(void *data EINA_UNUSED, Ecore_Thread *th)
+{
+   if (th == fill_thread) fill_thread = NULL;
+}
+
+static void
+_fill_cancel(void *data EINA_UNUSED, Ecore_Thread *th)
+{
+   if (th == fill_thread) fill_thread = NULL;
+}
+
+static void
+_entry_free(Entry *entry)
+{
+   Entry *subentry;
+   Eina_Stringshare *str;
+   if (!entry) return;
+   entry->sels = eina_list_free(entry->sels);
+   if (entry->base) evas_object_del(entry->base);
+   EINA_LIST_FREE(entry->files, str) eina_stringshare_del(str);
+   EINA_LIST_FREE(entry->dirs, subentry) _entry_free(subentry);
+   eina_stringshare_del(entry->path);
+   eina_lock_free(&(entry->lock));
+   free(entry);
+}
+
+static void
+_fill(Evas_Object *win)
+{
+   if (fill_thread)
+     {
+        ecore_thread_cancel(fill_thread);
+        ecore_thread_wait(fill_thread, 10.0);
+     }
+   _entry_free(dir_entry);
+   dir_entry = NULL;
+   fill_thread = ecore_thread_feedback_run(_fill_thread, _fill_feedback,
+                                           _fill_end, _fill_cancel,
+                                           win, EINA_TRUE);
+}
+
+static Entry *
+_sel_find(Entry *entry)
+{
+   Eina_List *l;
+   Entry *subentry, *tmpentry;
+
+   eina_lock_take(&(entry->lock));
+   if (entry->sel)
+     {
+        eina_lock_release(&(entry->lock));
+        return entry;
+     }
+   EINA_LIST_FOREACH(entry->dirs, l, subentry)
+     {
+        tmpentry = _sel_find(subentry);
+        if (tmpentry)
+          {
+             eina_lock_release(&(entry->lock));
+             return tmpentry;
+          }
+     }
+   eina_lock_release(&(entry->lock));
+   return NULL;
+}
+
+static Evas_Object *
+_sel_object_find(Entry *entry)
+{
+   int num = (entry->sel_y * entry->cols) + entry->sel_x;
+   Evas_Object *o = eina_list_nth(entry->sels, num);
+   return o;
+}
+
+static const char *
+_sel_file_find(Entry *entry)
+{
+   int num = (entry->sel_y * entry->cols) + entry->sel_x;
+   const char *file = eina_list_nth(entry->files, num);
+   return file;
+}
+
+static Eina_List *
+_entry_list_flatten(Eina_List *list, Entry *entry)
+{
+   Eina_List *l;
+   Entry *subentry;
+
+   eina_lock_take(&(entry->lock));
+   if (entry->files) list = eina_list_append(list, entry);
+   EINA_LIST_FOREACH(entry->dirs, l, subentry)
+     {
+        list = _entry_list_flatten(list, subentry);
+     }
+   eina_lock_release(&(entry->lock));
+   return list;
+}
+
+static void
+_sel_go(Evas_Object *win EINA_UNUSED, Entry *base_entry, int x, int y)
+{
+   Evas_Object *o;
+   Evas_Coord bxx, bxy, tbx, tby;
+
+   if (!base_entry) return;
+   evas_object_geometry_get(bx, &bxx, &bxy, NULL, NULL);
+   Entry *entry = _sel_find(base_entry);
+   if (!entry)
+     {
+        Eina_List *flatlist = _entry_list_flatten(NULL, base_entry);
+
+        if (flatlist)
+          {
+             entry = flatlist->data;
+             eina_lock_take(&(entry->lock));
+             entry->sel = EINA_TRUE;
+             entry->sel_x = 0;
+             entry->sel_y = 0;
+             o = _sel_object_find(entry);
+             elm_layout_signal_emit(o, "rage,state,selected", "rage");
+             eina_lock_release(&(entry->lock));
+             eina_list_free(flatlist);
+          }
+     }
+   else
+     {
+        int sel_x, sel_y, num;
+        Entry *subentry;
+        Eina_List *l;
+        Eina_List *flatlist = _entry_list_flatten(NULL, base_entry);
+
+        eina_lock_take(&(entry->lock));
+        sel_x = entry->sel_x + x;
+        sel_y = entry->sel_y + y;
+        if (sel_x >= entry->cols)
+          {
+             sel_y++;
+             sel_x = 0;
+          }
+        else if (sel_x < 0)
+          {
+             sel_y--;
+             sel_x = entry->cols - 1;
+          }
+        num = (sel_y * entry->cols) + sel_x;
+        if (num < 0)
+          {
+             EINA_LIST_FOREACH(flatlist, l, subentry)
+               {
+                  if (subentry == entry)
+                    {
+                       if (l->prev)
+                         {
+                            subentry = l->prev->data;
+                            entry->sel = EINA_FALSE;
+                            subentry->sel = EINA_TRUE;
+                            if (sel_x < subentry->cols)
+                              subentry->sel_x = sel_x;
+                            else
+                              subentry->sel_x = subentry->cols - 1;
+                            subentry->sel_y = subentry->rows - 1;
+                            num = eina_list_count(subentry->files) - 1 -
+                              ((subentry->sel_y * subentry->cols) +
+                               subentry->sel_x);
+                            if (num < 0) subentry->sel_x += num;
+                            evas_object_geometry_get(subentry->table, &tbx, 
&tby, NULL, NULL);
+                            o = _sel_object_find(entry);
+                            if (o) elm_layout_signal_emit(o, 
"rage,state,unselected", "rage");
+                            o = _sel_object_find(subentry);
+                            if (o) elm_layout_signal_emit(o, 
"rage,state,selected", "rage");
+                            elm_scroller_region_bring_in
+                              (sc,
+                               (tbx - bxx) + (subentry->sel_x * subentry->iw),
+                               (tby - bxy) + (subentry->sel_y * subentry->ih),
+                               entry->iw, entry->ih);
+                         }
+                       break;
+                    }
+               }
+          }
+        else if (num >= (int)eina_list_count(entry->files))
+          {
+             EINA_LIST_FOREACH(flatlist, l, subentry)
+               {
+                  if (subentry == entry)
+                    {
+                       if (l->next)
+                         {
+                            subentry = l->next->data;
+                            entry->sel = EINA_FALSE;
+                            subentry->sel = EINA_TRUE;
+                            if (sel_x < subentry->cols)
+                              subentry->sel_x = sel_x;
+                            else
+                              subentry->sel_x = subentry->cols - 1;
+                            subentry->sel_y = 0;
+                            num = eina_list_count(subentry->files) - 1 -
+                              ((subentry->sel_y * subentry->cols) +
+                               subentry->sel_x);
+                            if (num < 0) subentry->sel_x += num;
+                            evas_object_geometry_get(subentry->table, &tbx, 
&tby, NULL, NULL);
+                            o = _sel_object_find(entry);
+                            if (o) elm_layout_signal_emit(o, 
"rage,state,unselected", "rage");
+                            o = _sel_object_find(subentry);
+                            if (o) elm_layout_signal_emit(o, 
"rage,state,selected", "rage");
+                            elm_scroller_region_bring_in
+                              (sc,
+                               (tbx - bxx) + (subentry->sel_x * subentry->iw),
+                               (tby - bxy) + (subentry->sel_y * subentry->ih),
+                               entry->iw, entry->ih);
+                         }
+                       break;
+                    }
+               }
+          }
+        else
+          {
+             o = _sel_object_find(entry);
+             if (o) elm_layout_signal_emit(o, "rage,state,unselected", "rage");
+             entry->sel_x = sel_x;
+             entry->sel_y = sel_y;
+             evas_object_geometry_get(entry->table, &tbx, &tby, NULL, NULL);
+             o = _sel_object_find(entry);
+             if (o) elm_layout_signal_emit(o, "rage,state,selected", "rage");
+             elm_scroller_region_bring_in
+               (sc,
+                (tbx - bxx) + (entry->sel_x * entry->iw),
+                (tby - bxy) + (entry->sel_y * entry->ih),
+                entry->iw, entry->ih);
+          }
+        eina_lock_release(&(entry->lock));
+        eina_list_free(flatlist);
+     }
+}
+
+static void
+_sel_do(Evas_Object *win, Entry *base_entry)
+{
+   Entry *entry;
+
+   if (!base_entry) return;
+   entry = _sel_find(base_entry);
+   if (entry)
+     {
+        eina_lock_take(&(entry->lock));
+        Evas_Object *o = _sel_object_find(entry);
+        const char *file = _sel_file_find(entry);
+        if (file)
+          {
+             elm_layout_signal_emit(o, "rage,state,selected", "rage");
+             _activate(win, entry, file);
+          }
+        eina_lock_release(&(entry->lock));
+     }
+}
+
+static void
+_cb_key_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, 
void *event_info)
+{
+   Evas_Event_Key_Down *ev = event_info;
+   Evas_Object *win = data;
+   if ((!strcmp(ev->key, "Left")) ||
+       (!strcmp(ev->key, "bracketleft")))
+     {
+        _sel_go(win, dir_entry, -1, 0);
+     }
+   else if ((!strcmp(ev->key, "Right")) ||
+            (!strcmp(ev->key, "bracketright")))
+     {
+        _sel_go(win, dir_entry, 1, 0);
+     }
+   else if ((!strcmp(ev->key, "Up")) ||
+            (!strcmp(ev->key, "Prior")) ||
+            (!strcmp(ev->key, "XF86AudioPrev")))
+     {
+        _sel_go(win, dir_entry, 0, -1);
+     }
+   else if ((!strcmp(ev->key, "Down")) ||
+            (!strcmp(ev->key, "Next")) ||
+            (!strcmp(ev->key, "XF86AudioNext")))
+     {
+        _sel_go(win, dir_entry, 0, 1);
+     }
+   else if ((!strcmp(ev->key, "space")) ||
+            (!strcmp(ev->key, "Pause")) ||
+            (!strcmp(ev->keyname, "p")) ||
+            (!strcmp(ev->key, "XF86AudioPlay")) ||
+            (!strcmp(ev->key, "Return")) ||
+            (!strcmp(ev->key, "KP_Enter")))
+     {
+        _sel_do(win, dir_entry);
+     }
+   else if ((!strcmp(ev->keyname, "q")) ||
+            (!strcmp(ev->key, "Escape")) ||
+            (!strcmp(ev->key, "e")))
+     {
+        browser_hide(win);
+     }
+   else key_handle(win, ev);
+}
+
+void
+browser_show(Evas_Object *win)
+{
+   Inf *inf = evas_object_data_get(win, "inf");
+
+   if (!bx)
+     {
+        bx = elm_box_add(win);
+
+        sc = elm_scroller_add(win);
+        elm_object_style_set(sc, "noclip");
+        elm_object_focus_allow_set(sc, EINA_FALSE);
+        evas_object_size_hint_weight_set(sc, EVAS_HINT_EXPAND, 
EVAS_HINT_EXPAND);
+        evas_object_size_hint_align_set(sc, EVAS_HINT_FILL, EVAS_HINT_FILL);
+        elm_scroller_content_min_limit(sc, EINA_TRUE, EINA_FALSE);
+
+        bx = elm_box_add(win);
+        elm_object_focus_allow_set(bx, EINA_FALSE);
+        evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, 0.0);
+        evas_object_size_hint_align_set(bx, EVAS_HINT_FILL, 0.5);
+        elm_object_content_set(sc, bx);
+        evas_object_show(bx);
+
+        elm_object_part_content_set(inf->lay, "rage.browser", sc);
+        evas_object_show(sc);
+
+        _fill(win);
+
+        // a dummy button to collect key events and have focus
+        bt = elm_button_add(win);
+        elm_object_focus_highlight_style_set(bt, "blank");
+        evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 
EVAS_HINT_EXPAND);
+        elm_win_resize_object_add(win, bt);
+        evas_object_lower(bt);
+        evas_object_show(bt);
+        elm_object_focus_set(bt, EINA_TRUE);
+        evas_object_event_callback_add(bt, EVAS_CALLBACK_KEY_DOWN,
+                                       _cb_key_down, win);
+     }
+   elm_layout_signal_emit(inf->lay, "browser,state,visible", "rage");
+}
+
+static void
+_cb_hidden(void *data, Evas_Object *obj, const char *sig EINA_UNUSED, const 
char *src EINA_UNUSED)
+{
+   elm_layout_signal_callback_del(obj, "browser,state,hidden,finished", "rage",
+                                  _cb_hidden);
+   if (fill_thread) ecore_thread_cancel(fill_thread);
+   evas_object_del(bx);
+   bx = NULL;
+   evas_object_del(bt);
+   bt = NULL;
+   evas_object_del(sc);
+   sc = NULL;
+   elm_object_focus_next(data, ELM_FOCUS_PREVIOUS);
+}
+
+void
+browser_hide(Evas_Object *win)
+{
+   Inf *inf = evas_object_data_get(win, "inf");
+
+   if (!bx) return;
+   elm_layout_signal_callback_add(inf->lay, "browser,state,hidden,finished", 
"rage",
+                                  _cb_hidden, win);
+   elm_layout_signal_emit(inf->lay, "browser,state,hidden", "rage");
+}
+
+void
+browser_toggle(Evas_Object *win)
+{
+   if (bx) browser_hide(win);
+   else browser_show(win);
+}
+
+void
+browser_size_update(Evas_Object *win EINA_UNUSED)
+{
+   if (!bx) return;
+   // XXX: unpop + repop
+}
diff --git a/src/bin/browser.h b/src/bin/browser.h
new file mode 100644
index 0000000..287bab2
--- /dev/null
+++ b/src/bin/browser.h
@@ -0,0 +1,9 @@
+#ifndef _BROWSER_H__
+#define _BROWSER_H__ 1
+
+void browser_show(Evas_Object *win);
+void browser_hide(Evas_Object *win);
+void browser_toggle(Evas_Object *win);
+void browser_size_update(Evas_Object *win);
+
+#endif
diff --git a/src/bin/key.c b/src/bin/key.c
index 7fab4c2..69de77d 100644
--- a/src/bin/key.c
+++ b/src/bin/key.c
@@ -5,6 +5,7 @@
 #include "video.h"
 #include "key.h"
 #include "winlist.h"
+#include "browser.h"
 
 void
 key_handle(Evas_Object *win, Evas_Event_Key_Down *ev)
@@ -110,6 +111,7 @@ key_handle(Evas_Object *win, Evas_Event_Key_Down *ev)
      {
         video_stop(inf->vid);
         elm_layout_signal_emit(inf->lay, "action,stop", "rage");
+        if (inf->browse_mode) browser_show(win);
      }
    else if (!strcmp(ev->keyname, "c"))
      {
@@ -178,7 +180,8 @@ key_handle(Evas_Object *win, Evas_Event_Key_Down *ev)
      }
    else if (!strcmp(ev->keyname, "backslash"))
      {
-        win_list_toggle(win);
+        if (inf->browse_mode) browser_toggle(win);
+        else win_list_toggle(win);
      }
    else if (!strcmp(ev->keyname, "y"))
      {
diff --git a/src/bin/main.c b/src/bin/main.c
index 31cf605..3746431 100644
--- a/src/bin/main.c
+++ b/src/bin/main.c
@@ -3,6 +3,7 @@
 #include "win.h"
 #include "winvid.h"
 #include "winlist.h"
+#include "browser.h"
 #include "config.h"
 
 static Eina_Bool
@@ -20,6 +21,7 @@ static void
 _cb_resize(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void 
*event_info EINA_UNUSED)
 {
    win_list_size_update(obj);
+   browser_size_update(obj);
 }
 
 EAPI_MAIN int
@@ -32,6 +34,8 @@ elm_main(int argc, char **argv)
    Inf *inf;
    Config *config;
    Winvid_Entry *vid = NULL;
+   Eina_Bool fullscreen = EINA_FALSE;
+   int file_num = 0;
 
    elm_need_efreet();
    config_init();
@@ -47,6 +51,8 @@ elm_main(int argc, char **argv)
                     "    -h | -help | --help\n"
                     "      This help\n"
                     "\n"
+                    "    -f\n"
+                    "      Enable fullscreen mode at start\n"
                     "    -e ENGINE\n"
                     "      ENGINE is one of gstreamer1, xine or vlc\n"
                     "      The default is gstreamer1\n"
@@ -68,6 +74,10 @@ elm_main(int argc, char **argv)
                   config->emotion_engine = eina_stringshare_add(argv[i]);
                }
           }
+        else if (!strcmp(argv[i], "-f"))
+          {
+             fullscreen = EINA_TRUE;
+          }
         else if (!strcmp(argv[i], "-sub"))
           {
              if (i < (argc - 1))
@@ -81,6 +91,7 @@ elm_main(int argc, char **argv)
              vid = calloc(1, sizeof(Winvid_Entry));
              if (vid)
                {
+                  file_num++;
                   vid->file = eina_stringshare_add(argv[i]);
                   list = eina_list_append(list, vid);
                }
@@ -112,8 +123,8 @@ elm_main(int argc, char **argv)
 
    evas_object_event_callback_add(win, EVAS_CALLBACK_RESIZE, _cb_resize, NULL);
    evas_object_resize(win,
-                      320 * elm_config_scale_get(),
-                      200 * elm_config_scale_get());
+                      600 * elm_config_scale_get(),
+                      360 * elm_config_scale_get());
 
    win_video_init(win);
    win_video_file_list_set(win, list);
@@ -125,10 +136,14 @@ elm_main(int argc, char **argv)
         free(vid);
      }
 
+   if (fullscreen) elm_win_fullscreen_set(win, EINA_TRUE);
+
    inf = evas_object_data_get(win, "inf");
-   if (argc <= 1)
+   if (file_num <= 0)
      {
-        elm_layout_signal_emit(inf->lay, "about,show", "rage");
+        inf->browse_mode = EINA_TRUE;
+        browser_show(win);
+//        elm_layout_signal_emit(inf->lay, "about,show", "rage");
         evas_object_show(win);
      }
    else
diff --git a/src/bin/thumb.c b/src/bin/thumb.c
index 8996b57..4c07a64 100644
--- a/src/bin/thumb.c
+++ b/src/bin/thumb.c
@@ -1,18 +1,46 @@
 #include <Elementary.h>
+#include <Emotion.h>
 #include "sha1.h"
+#include "albumart.h"
 
 static Evas_Object *win = NULL, *subwin = NULL, *image = NULL;
 static Evas_Object *vidimage = NULL;
 static int iw, ih;
 static unsigned char sum[20];
-static Eet_File *ef;
+static Eet_File *ef = NULL;
+static Ecore_Timer *vid_timeout = NULL;
+
+static void
+_cb_fetched(void *data EINA_UNUSED)
+{
+   elm_exit();
+}
+
+static void
+_cb_loaded(void *data, Evas_Object *obj, void *info EINA_UNUSED)
+{
+   const char *file, *title, *artist, *album;
+
+   file = data;
+   title = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_TITLE);
+   artist = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_ARTIST);
+   album = emotion_object_meta_info_get(obj, EMOTION_META_INFO_TRACK_ALBUM);
+
+   albumart_find(file, title, artist, album, _cb_fetched, (void *)file);
+}
+
+static Eina_Bool
+_cb_timeout(void *data EINA_UNUSED)
+{
+   vid_timeout = NULL;
+   elm_exit();
+   return EINA_FALSE;
+}
 
 EAPI_MAIN int
 elm_main(int argc, char **argv)
 {
-   char buf_base[PATH_MAX];
-   char buf_file[PATH_MAX];
-   unsigned int pos, incr;
+   Eina_Bool is_audio = EINA_FALSE;
 
    if (argc < 3) exit(1);
    elm_need_efreet();
@@ -33,54 +61,88 @@ elm_main(int argc, char **argv)
    elm_win_norender_push(subwin);
    elm_win_norender_push(win);
 
-   vidimage = evas_object_image_filled_add(evas_object_evas_get(subwin));
-   evas_object_show(vidimage);
-
-   evas_object_image_file_set(vidimage, argv[1], NULL);
-   evas_object_image_size_get(vidimage, &iw, &ih);
-   if (!sha1((unsigned char *)argv[1], strlen(argv[1]), sum)) exit(2);
-   if (!efreet_cache_home_get()) exit(3);
-   snprintf(buf_base, sizeof(buf_base), "%s/rage/thumb/%02x",
-            efreet_cache_home_get(), sum[0]);
-   snprintf(buf_file, sizeof(buf_base),
-            "%s/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
-            "%02x%02x%02x%02x%02x%02x%02x%02x.eet",
-            buf_base,
-            sum[1], sum[2], sum[3],
-            sum[4], sum[5], sum[6], sum[7],
-            sum[8], sum[9], sum[10], sum[11],
-            sum[12], sum[13], sum[14], sum[15],
-            sum[16], sum[17], sum[18], sum[19]);
-   if (!ecore_file_mkpath(buf_base)) exit(4);
-   ef = eet_open(buf_file, EET_FILE_MODE_WRITE);
-   if (!ef) exit(5);
-   pos = 0;
-   incr = atoi(argv[2]);
-   for (pos = 0; ; pos += incr)
+   const char *extn = strchr(argv[1], '.');
+   if (extn)
      {
-        int w, h;
-        int *pixels;
-        char key[128];
+        if ((!strcasecmp(extn, ".mp3")) ||
+            (!strcasecmp(extn, ".m4a")) ||
+            (!strcasecmp(extn, ".oga")) ||
+            (!strcasecmp(extn, ".aac")) ||
+            (!strcasecmp(extn, ".flac")) ||
+            (!strcasecmp(extn, ".wav")))
+          {
+             is_audio = EINA_TRUE;
+          }
+     }
+   if (is_audio)
+     {
+        Evas_Object *vid;
 
-        snprintf(key, sizeof(key), "%i", pos);
-        evas_object_image_file_set(vidimage, argv[1], key);
+        vid = emotion_object_add(evas_object_evas_get(win));
+        if (emotion_object_init(vid, NULL))
+          {
+             evas_object_smart_callback_add(vid, "open_done",
+                                            _cb_loaded, argv[1]);
+             emotion_object_file_set(vid, argv[1]);
+             vid_timeout = ecore_timer_add(2.0, _cb_timeout, NULL);
+             elm_run();
+          }
+     }
+   else
+     {
+        char buf_base[PATH_MAX];
+        char buf_file[PATH_MAX];
+        unsigned int pos, incr;
+
+        vidimage = evas_object_image_filled_add(evas_object_evas_get(subwin));
+        evas_object_show(vidimage);
+
+        evas_object_image_file_set(vidimage, argv[1], NULL);
         evas_object_image_size_get(vidimage, &iw, &ih);
-        if ((iw <= 0) || (ih <= 0)) break;
-        w = 160;
-        h = (ih * 160) / iw;
-        if (h < 1) h = 1;
-        evas_object_resize(vidimage, w, h);
-        evas_object_resize(subwin, w, h);
-        elm_win_render(subwin);
-        pixels = evas_object_image_data_get(image, EINA_FALSE);
-        if (pixels)
-          eet_data_image_write(ef, key, pixels, w, h,
-                               0, 0, 70, EET_IMAGE_JPEG);
-        else
-          exit(6);
-        evas_object_image_data_set(image, pixels);
+        if (!sha1((unsigned char *)argv[1], strlen(argv[1]), sum)) exit(2);
+        if (!efreet_cache_home_get()) exit(3);
+        snprintf(buf_base, sizeof(buf_base), "%s/rage/thumb/%02x",
+                 efreet_cache_home_get(), sum[0]);
+        snprintf(buf_file, sizeof(buf_base),
+                 "%s/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+                 "%02x%02x%02x%02x%02x%02x%02x%02x.eet",
+                 buf_base,
+                 sum[1], sum[2], sum[3],
+                 sum[4], sum[5], sum[6], sum[7],
+                 sum[8], sum[9], sum[10], sum[11],
+                 sum[12], sum[13], sum[14], sum[15],
+                 sum[16], sum[17], sum[18], sum[19]);
+        if (!ecore_file_mkpath(buf_base)) exit(4);
+        ef = eet_open(buf_file, EET_FILE_MODE_WRITE);
+        if (!ef) exit(5);
+        pos = 0;
+        incr = atoi(argv[2]);
+        for (pos = 0; ; pos += incr)
+          {
+             int w, h;
+             int *pixels;
+             char key[128];
+
+             snprintf(key, sizeof(key), "%i", pos);
+             evas_object_image_file_set(vidimage, argv[1], key);
+             evas_object_image_size_get(vidimage, &iw, &ih);
+             if ((iw <= 0) || (ih <= 0)) break;
+             w = 160;
+             h = (ih * 160) / iw;
+             if (h < 1) h = 1;
+             evas_object_resize(vidimage, w, h);
+             evas_object_resize(subwin, w, h);
+             elm_win_render(subwin);
+             pixels = evas_object_image_data_get(image, EINA_FALSE);
+             if (pixels)
+               eet_data_image_write(ef, key, pixels, w, h,
+                                    0, 0, 70, EET_IMAGE_JPEG);
+             else
+               exit(6);
+             evas_object_image_data_set(image, pixels);
+          }
+        eet_close(ef);
      }
-   eet_close(ef);
    elm_shutdown();
    return 0;
 }
diff --git a/src/bin/video.c b/src/bin/video.c
index 292c49d..1bc6c53 100644
--- a/src/bin/video.c
+++ b/src/bin/video.c
@@ -552,7 +552,6 @@ void
 video_file_set(Evas_Object *obj, const char *file)
 {
    Video *sd = evas_object_smart_data_get(obj);
-   const char *extn;
    if (!sd) return;
    evas_object_hide(sd->o_img);
    evas_object_hide(sd->o_vid);
@@ -562,10 +561,11 @@ video_file_set(Evas_Object *obj, const char *file)
    video_position_set(obj, 0.0);
    if ((sd->file) && (sd->doart))
      {
-        extn = strchr(sd->file, '.');
+        const char *extn = strchr(sd->file, '.');
         if (extn)
           {
              if ((!strcasecmp(extn, ".mp3")) ||
+                 (!strcasecmp(extn, ".m4a")) ||
                  (!strcasecmp(extn, ".oga")) ||
                  (!strcasecmp(extn, ".aac")) ||
                  (!strcasecmp(extn, ".flac")) ||
diff --git a/src/bin/videothumb.c b/src/bin/videothumb.c
index 0dc3c90..222fde6 100644
--- a/src/bin/videothumb.c
+++ b/src/bin/videothumb.c
@@ -1,15 +1,18 @@
 #include <Elementary.h>
 #include "videothumb.h"
 #include "sha1.h"
+#include "albumart.h"
 
 typedef struct _Videothumb Videothumb;
 
 struct _Videothumb
 {
    Evas_Object_Smart_Clipped_Data __clipped_data;
-   Evas_Object *o_img;
+   Evas_Object *o_img, *o_img2;
    Ecore_Exe *thumb_exe;
    Ecore_Event_Handler *exe_handler;
+   Ecore_Timer *cycle_timer;
+   Ecore_Timer *launch_timer;
    const char *file;
    const char *realfile;
    char *realpath;
@@ -26,6 +29,12 @@ static Evas_Smart_Class _parent_sc = 
EVAS_SMART_CLASS_INIT_NULL;
 static Eina_List *busy_thumbs = NULL;
 static Eina_List *vidthumbs = NULL;
 
+static int        _thumb_running = 0;
+
+static Eina_Bool _cb_thumb_exe(void *data, int type EINA_UNUSED, void *event);
+static void _videothumb_eval(Evas_Object *obj, Eina_Bool force);
+static void _smart_calculate(Evas_Object *obj);
+
 static Eina_Bool
 _busy_add(const char *file)
 {
@@ -66,14 +75,25 @@ _thumb_update(Evas_Object *obj)
 
    if (!sd) return;
    snprintf(buf, sizeof(buf), "%u", sd->realpos);
-   evas_object_image_file_set(sd->o_img, NULL, NULL);
-   evas_object_image_file_set(sd->o_img, sd->realfile, buf);
-   evas_object_image_size_get(sd->o_img, &(sd->iw), &(sd->ih));
+   evas_object_image_file_set(sd->o_img2, NULL, NULL);
+   evas_object_image_file_set(sd->o_img2, sd->realfile, buf);
+   evas_object_image_size_get(sd->o_img2, &(sd->iw), &(sd->ih));
    if ((sd->iw <= 0) || (sd->ih <= 0))
      {
-        evas_object_del(sd->o_img);
-        sd->o_img = NULL;
-        evas_object_smart_callback_call(obj, "failed", NULL);
+        if (sd->cycle_timer)
+          {
+             sd->pos = 0.0;
+             if (!sd->thumb_exe)
+               {
+                  _videothumb_eval(obj, EINA_TRUE);
+               }
+          }
+        else
+          {
+             evas_object_del(sd->o_img2);
+             sd->o_img2 = NULL;
+             evas_object_smart_callback_call(obj, "failed", NULL);
+          }
      }
    else
      {
@@ -88,9 +108,79 @@ _thumb_match_update(Evas_Object *obj, const char *file)
    Videothumb *sd = evas_object_smart_data_get(obj);
 
    if (!sd) return;
+   if (!sd->realpath) return;
    if (!strcmp(sd->realpath, file)) _thumb_update(obj);
 }
 
+static void
+_videothumb_launch_do(Evas_Object *obj)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   char buf[PATH_MAX];
+   const char *libdir;
+   char *s;
+
+   if (!sd) return;
+   ecore_exe_run_priority_set(10);
+   if (sd->thumb_exe)
+     {
+        _busy_del(sd->realpath);
+        ecore_exe_kill(sd->thumb_exe);
+        ecore_exe_free(sd->thumb_exe);
+        sd->thumb_exe = NULL;
+        _thumb_running--;
+     }
+   s = ecore_file_escape_name(sd->realpath);
+   if (s)
+     {
+        libdir = elm_app_lib_dir_get();
+        if (libdir)
+          {
+             if (_busy_add(sd->realpath))
+               {
+                  if (!sd->exe_handler)
+                    sd->exe_handler = 
ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
+                                                              _cb_thumb_exe, 
obj);
+                  snprintf(buf, sizeof(buf),
+                           "%s/rage/utils/rage_thumb %s 10000 >& /dev/null",
+                           libdir, s);
+                  sd->thumb_exe = ecore_exe_pipe_run(buf,
+                                                     
ECORE_EXE_TERM_WITH_PARENT |
+                                                     ECORE_EXE_NOT_LEADER,
+                                                     obj);
+                  _thumb_running++;
+               }
+             else return;
+          }
+        free(s);
+     }
+}
+
+static Eina_Bool
+_cb_videothumb_delay(void *data)
+{
+   Evas_Object *obj = data;
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   if (!sd) return EINA_FALSE;
+   if (_thumb_running < (eina_cpu_count() + 1))
+     {
+        sd->launch_timer = NULL;
+        _videothumb_launch_do(obj);
+        return EINA_FALSE;
+     }
+   return EINA_TRUE;
+}
+
+static void
+_videothumb_launch(Evas_Object *obj)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+
+   if (!sd) return;
+   if (sd->launch_timer) return;
+   sd->launch_timer = ecore_timer_add(1.0, _cb_videothumb_delay, obj);
+}
+
 static Eina_Bool
 _cb_thumb_exe(void *data, int type EINA_UNUSED, void *event)
 {
@@ -105,6 +195,8 @@ _cb_thumb_exe(void *data, int type EINA_UNUSED, void *event)
         Evas_Object *o;
 
         _busy_del(sd->realpath);
+        sd->thumb_exe = NULL;
+        _thumb_running--;
         EINA_LIST_FOREACH(vidthumbs, l, o)
           {
              _thumb_match_update(o, sd->realpath);
@@ -117,7 +209,6 @@ _cb_thumb_exe(void *data, int type EINA_UNUSED, void *event)
                   sd->exe_handler = NULL;
                }
           }
-        sd->thumb_exe = NULL;
         return EINA_FALSE;
      }
    return EINA_TRUE;
@@ -129,6 +220,9 @@ _cb_preload(void *data, Evas *e EINA_UNUSED, Evas_Object 
*obj EINA_UNUSED, void
    Videothumb *sd = evas_object_smart_data_get(data);
 
    if (!sd) return;
+   if (sd->o_img) evas_object_del(sd->o_img);
+   sd->o_img = sd->o_img2;
+   sd->o_img2 = NULL;
    evas_object_show(sd->o_img);
    evas_object_smart_callback_call(data, "data", NULL);
 }
@@ -140,35 +234,61 @@ _videothumb_image_load(Evas_Object *obj)
    char buf_base[PATH_MAX];
    char buf_file[PATH_MAX];
    char buf[PATH_MAX];
-   char *s;
-   const char *libdir;
    unsigned char sum[20];
+   Eina_Bool is_audio = EINA_FALSE;
 
    if (!sd) return;
    if (!sd->file) return;
-   sd->o_img = evas_object_image_filled_add(evas_object_evas_get(obj));
-   evas_object_smart_member_add(sd->o_img, obj);
-   if (!sha1((unsigned char *)sd->realpath, strlen(sd->realpath), sum)) return;
-   snprintf(buf_base, sizeof(buf_base), "%s/rage/thumb/%02x",
-            efreet_cache_home_get(), sum[0]);
-   snprintf(buf_file, sizeof(buf_base),
-            "%s/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
-            "%02x%02x%02x%02x%02x%02x%02x%02x.eet",
-            buf_base,
-            sum[1], sum[2], sum[3],
-            sum[4], sum[5], sum[6], sum[7],
-            sum[8], sum[9], sum[10], sum[11],
-            sum[12], sum[13], sum[14], sum[15],
-            sum[16], sum[17], sum[18], sum[19]);
-   if (sd->realfile) eina_stringshare_del(sd->realfile);
-   sd->realfile = eina_stringshare_add(buf_file);
-   sd->realpos = (((unsigned int)(sd->pos * 1000.0)) / 10000) * 10000;
-   snprintf(buf, sizeof(buf), "%u", sd->realpos);
-   evas_object_event_callback_add(sd->o_img,
+   sd->o_img2 = evas_object_image_filled_add(evas_object_evas_get(obj));
+   evas_object_smart_member_add(sd->o_img2, obj);
+   const char *extn = strchr(sd->realpath, '.');
+   if (extn)
+     {
+        if ((!strcasecmp(extn, ".mp3")) ||
+            (!strcasecmp(extn, ".m4a")) ||
+            (!strcasecmp(extn, ".oga")) ||
+            (!strcasecmp(extn, ".aac")) ||
+            (!strcasecmp(extn, ".flac")) ||
+            (!strcasecmp(extn, ".wav")))
+          {
+             is_audio = EINA_TRUE;
+          }
+     }
+   if (is_audio)
+     {
+        char *artfile = albumart_file_get(sd->realpath);
+        if (artfile)
+          {
+             sd->realfile = eina_stringshare_add(artfile);
+             free(artfile);
+          }
+     }
+   else
+     {
+        if (!sha1((unsigned char *)sd->realpath, strlen(sd->realpath), sum))
+          return;
+        snprintf(buf_base, sizeof(buf_base), "%s/rage/thumb/%02x",
+                 efreet_cache_home_get(), sum[0]);
+        snprintf(buf_file, sizeof(buf_base),
+                 "%s/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+                 "%02x%02x%02x%02x%02x%02x%02x%02x.eet",
+                 buf_base,
+                 sum[1], sum[2], sum[3],
+                 sum[4], sum[5], sum[6], sum[7],
+                 sum[8], sum[9], sum[10], sum[11],
+                 sum[12], sum[13], sum[14], sum[15],
+                 sum[16], sum[17], sum[18], sum[19]);
+        if (sd->realfile) eina_stringshare_del(sd->realfile);
+        sd->realfile = eina_stringshare_add(buf_file);
+        sd->realpos = (((unsigned int)(sd->pos * 1000.0)) / 10000) * 10000;
+        snprintf(buf, sizeof(buf), "%u", sd->realpos);
+     }
+   evas_object_event_callback_add(sd->o_img2,
                                   EVAS_CALLBACK_IMAGE_PRELOADED,
                                   _cb_preload, obj);
-   evas_object_image_file_set(sd->o_img, sd->realfile, buf);
-   evas_object_image_size_get(sd->o_img, &(sd->iw), &(sd->ih));
+   evas_object_image_file_set(sd->o_img2, sd->realfile, buf);
+   evas_object_image_size_get(sd->o_img2, &(sd->iw), &(sd->ih));
+   _smart_calculate(obj);
    if (sd->iw > 0)
      {
         Eina_Bool ok = EINA_FALSE;
@@ -183,33 +303,28 @@ _videothumb_image_load(Evas_Object *obj)
           }
         if (ok)
           {
-             evas_object_image_preload(sd->o_img, EINA_FALSE);
+             evas_object_image_preload(sd->o_img2, EINA_FALSE);
              return;
           }
      }
-   if (!_busy_add(sd->realpath)) return;
-   ecore_exe_run_priority_set(10);
-   if (sd->thumb_exe)
-     {
-        ecore_exe_free(sd->thumb_exe);
-        sd->thumb_exe = NULL;
-     }
-   s = ecore_file_escape_name(sd->realpath);
-   if (s)
+   if (sd->iw <= 0)
      {
-        libdir = elm_app_lib_dir_get();
-        if (libdir)
+        Eina_Bool ok = EINA_FALSE;
+        struct stat st1, st2;
+
+        if (stat(sd->realpath, &st1) == 0)
           {
-             if (!sd->exe_handler)
-               sd->exe_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
-                                                         _cb_thumb_exe, obj);
-             snprintf(buf, sizeof(buf),
-                      "%s/rage/utils/rage_thumb %s 10000 >& /dev/null",
-                      libdir, s);
-             sd->thumb_exe = ecore_exe_pipe_run(buf,
-                                                ECORE_EXE_TERM_WITH_PARENT |
-                                                ECORE_EXE_NOT_LEADER,
-                                                obj);
+             if (stat(sd->realfile, &st2) == 0)
+               {
+                  if (st1.st_mtime < st2.st_mtime)
+                    {
+                       ok = EINA_TRUE;
+                    }
+               }
+          }
+        if (!ok)
+          {
+             _videothumb_launch(obj);
           }
      }
 }
@@ -231,14 +346,14 @@ _videothumb_eval(Evas_Object *obj, Eina_Bool force)
    if (force)
      {
         sd->seen = seen;
-        if (sd->o_img)
+        if (sd->o_img2)
           {
-             evas_object_del(sd->o_img);
-             sd->o_img = NULL;
+             evas_object_del(sd->o_img2);
+             sd->o_img2 = NULL;
           }
         _videothumb_image_load(obj);
-        evas_object_move(sd->o_img, ox, oy);
-        evas_object_resize(sd->o_img, ow, oh);
+        evas_object_move(sd->o_img2, ox, oy);
+        evas_object_resize(sd->o_img2, ow, oh);
      }
    else
      {
@@ -247,21 +362,21 @@ _videothumb_eval(Evas_Object *obj, Eina_Bool force)
              sd->seen = seen;
              if (sd->seen)
                {
-                  if (sd->o_img)
+                  if (sd->o_img2)
                     {
-                       evas_object_del(sd->o_img);
-                       sd->o_img = NULL;
+                       evas_object_del(sd->o_img2);
+                       sd->o_img2 = NULL;
                     }
                   _videothumb_image_load(obj);
-                  evas_object_move(sd->o_img, ox, oy);
-                  evas_object_resize(sd->o_img, ow, oh);
+                  evas_object_move(sd->o_img2, ox, oy);
+                  evas_object_resize(sd->o_img2, ow, oh);
                }
              else
                {
-                  if (sd->o_img)
+                  if (sd->o_img2)
                     {
-                       evas_object_del(sd->o_img);
-                       sd->o_img = NULL;
+                       evas_object_del(sd->o_img2);
+                       sd->o_img2 = NULL;
                     }
                }
           }
@@ -285,12 +400,24 @@ _smart_del(Evas_Object *obj)
    Videothumb *sd = evas_object_smart_data_get(obj);
    if (!sd) return;
    vidthumbs = eina_list_remove(vidthumbs, obj);
+   if (sd->thumb_exe)
+     {
+        if (sd->realpath) _busy_del(sd->realpath);
+        _thumb_running--;
+        ecore_exe_kill(sd->thumb_exe);
+        ecore_exe_free(sd->thumb_exe);
+     }
+   if (sd->launch_timer)
+     {
+        ecore_timer_del(sd->launch_timer);
+     }
    if (sd->file) eina_stringshare_del(sd->file);
    if (sd->realfile) eina_stringshare_del(sd->realfile);
    if (sd->realpath) free(sd->realpath);
    if (sd->o_img) evas_object_del(sd->o_img);
-   if (sd->thumb_exe) ecore_exe_free(sd->thumb_exe);
+   if (sd->o_img2) evas_object_del(sd->o_img2);
    if (sd->exe_handler) ecore_event_handler_del(sd->exe_handler);
+   if (sd->cycle_timer) ecore_timer_del(sd->cycle_timer);
    _parent_sc.del(obj);
 }
 
@@ -321,6 +448,11 @@ _smart_calculate(Evas_Object *obj)
         evas_object_move(sd->o_img, ox, oy);
         evas_object_resize(sd->o_img, ow, oh);
      }
+   if (sd->o_img2)
+     {
+        evas_object_move(sd->o_img2, ox, oy);
+        evas_object_resize(sd->o_img2, ow, oh);
+     }
 }
 
 static void
@@ -389,6 +521,39 @@ videothumb_file_set(Evas_Object *obj, const char *file, 
double pos)
    _videothumb_eval(obj, EINA_TRUE);
 }
 
+static Eina_Bool
+_cb_cycle(void *data)
+{
+   Evas_Object *obj = data;
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   sd->pos += 10.0;
+   if (!sd->thumb_exe)
+     {
+        _videothumb_eval(obj, EINA_TRUE);
+        if (sd->iw <= 0)
+          {
+             sd->pos = 0;
+             _videothumb_eval(obj, EINA_TRUE);
+          }
+     }
+   return EINA_TRUE;
+}
+
+void
+videothumb_autocycle_set(Evas_Object *obj, Eina_Bool enabled)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   if (!sd) return;
+   if (!enabled)
+     {
+        if (sd->cycle_timer) ecore_timer_del(sd->cycle_timer);
+        sd->cycle_timer = NULL;
+        return;
+     }
+   if (sd->cycle_timer) return;
+   sd->cycle_timer = ecore_timer_add(0.5, _cb_cycle, obj);
+}
+
 void
 videothumb_size_get(Evas_Object *obj, int *w, int *h)
 {
diff --git a/src/bin/videothumb.h b/src/bin/videothumb.h
index ce0029a..ab5c703 100644
--- a/src/bin/videothumb.h
+++ b/src/bin/videothumb.h
@@ -3,6 +3,7 @@
 
 Evas_Object *videothumb_add(Evas_Object *parent);
 void videothumb_file_set(Evas_Object *obj, const char *file, double pos);
+void videothumb_autocycle_set(Evas_Object *obj, Eina_Bool enabled);
 void videothumb_size_get(Evas_Object *obj, int *w, int *h);
 
 #endif
diff --git a/src/bin/win.c b/src/bin/win.c
index 4b95e9f..69151ca 100644
--- a/src/bin/win.c
+++ b/src/bin/win.c
@@ -9,6 +9,7 @@
 #include "controls.h"
 #include "gesture.h"
 #include "albumart.h"
+#include "browser.h"
 
 static void
 _cb_fullscreen(void *data EINA_UNUSED, Evas_Object *obj, void *event 
EINA_UNUSED)
@@ -23,9 +24,12 @@ static void
 _cb_unfullscreen(void *data EINA_UNUSED, Evas_Object *obj, void *event 
EINA_UNUSED)
 {
    Inf *inf = evas_object_data_get(obj, "inf");
-   elm_layout_signal_emit(inf->lay, "state,win,normal", "rage");
-   elm_win_noblank_set(obj, EINA_FALSE);
-   evas_object_hide(inf->event2);
+   if (!elm_win_fullscreen_get(obj))
+     {
+        elm_layout_signal_emit(inf->lay, "state,win,normal", "rage");
+        elm_win_noblank_set(obj, EINA_FALSE);
+        evas_object_hide(inf->event2);
+     }
 }
 
 static void
@@ -98,6 +102,23 @@ _cb_mouse_down(void *data, Evas *evas EINA_UNUSED, 
Evas_Object *obj EINA_UNUSED,
      elm_win_fullscreen_set(data, !elm_win_fullscreen_get(data));
 }
 
+static void
+_cb_fetched(void *data)
+{
+   Evas_Object *win = data;
+   Inf *inf = evas_object_data_get(win, "inf");
+   const char *file;
+
+   if (!inf->vid) return;
+   file = video_file_get(inf->vid);
+   if (file)
+     {
+        char *path = albumart_file_get(file);
+        win_art(win, path);
+        free(path);
+     }
+}
+
 static Eina_Bool
 _cb_albumart_delay(void *data)
 {
@@ -109,8 +130,15 @@ _cb_albumart_delay(void *data)
    if (!inf->vid) return EINA_FALSE;
 
    if ((!video_has_video_get(inf->vid)) && (video_has_audio_get(inf->vid)))
-     albumart_find(win, inf->vid);
-   else albumart_find(win, NULL);
+     {
+        const char *file = video_file_get(inf->vid);
+        const char *title = video_meta_title_get(inf->vid);
+        const char *artist = video_meta_artist_get(inf->vid);
+        const char *album = video_meta_album_get(inf->vid);
+
+        albumart_find(file, title, artist, album, _cb_fetched, win);
+     }
+   else albumart_find(NULL, NULL, NULL, NULL, NULL, NULL);
    return EINA_FALSE;
 }
 
@@ -234,7 +262,8 @@ win_video_next(Evas_Object *win)
    else l = inf->file_cur->next;
    if (!l)
      {
-        elm_exit();
+        if (inf->browse_mode) browser_show(win);
+        else elm_exit();
         return;
      }
    inf->file_cur = l;
diff --git a/src/bin/win.h b/src/bin/win.h
index 049a858..16a26b6 100644
--- a/src/bin/win.h
+++ b/src/bin/win.h
@@ -23,6 +23,7 @@ struct _Inf
    Eina_Bool playing : 1;
    Eina_Bool was_playing : 1;
    Eina_Bool dragging : 1;
+   Eina_Bool browse_mode : 1;
 };
 
 // ui high level controls
diff --git a/src/bin/winvid.c b/src/bin/winvid.c
index 1fd7da7..8e98a0d 100644
--- a/src/bin/winvid.c
+++ b/src/bin/winvid.c
@@ -5,6 +5,7 @@
 #include "winlist.h"
 #include "winvid.h"
 #include "videothumb.h"
+#include "browser.h"
 
 static void
 _cb_resize(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
@@ -36,7 +37,11 @@ _cb_stop(void *data, Evas_Object *obj EINA_UNUSED, void 
*event EINA_UNUSED)
         if (inf->next_job) ecore_job_del(inf->next_job);
         inf->next_job = ecore_job_add(_cb_stop_next, data);
      }
-   else elm_exit();
+   else
+     {
+        if (inf->browse_mode) browser_show(data);
+        else elm_exit();
+     }
 }
 
 static void
@@ -210,6 +215,15 @@ win_video_file_list_set(Evas_Object *win, Eina_List *list)
    Eina_List *l, *list2 = NULL;
    Winvid_Entry *vid;
 
+   inf->file_cur = NULL;
+   EINA_LIST_FREE(inf->file_list, vid)
+     {
+        if (vid->file) eina_stringshare_del(vid->file);
+        if (vid->sub) eina_stringshare_del(vid->sub);
+        if (vid->uri) efreet_uri_free(vid->uri);
+        free(vid);
+     }
+
    EINA_LIST_FOREACH(list, l, vid)
      {
         Winvid_Entry *vid2;
@@ -224,7 +238,7 @@ win_video_file_list_set(Evas_Object *win, Eina_List *list)
           }
      }
    inf->file_list = list2;
-   win_video_next(win);
+   win_video_first(win);
 }
 
 void

-- 


Reply via email to