raster pushed a commit to branch master.

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

commit 706e4948d7d607d11f13f63c655a26d8c57dff73
Author: Carsten Haitzler (Rasterman) <ras...@rasterman.com>
Date:   Mon Aug 4 17:15:30 2014 +0900

    add video thumbnails on timeline/dragable/seeker at the bottom
    
    these thumbnails are generated in the bg by rage_thumb and will appear
    once generated (or already found cached). it'll generate new thumbs if
    modified dates are equal or newer on tthe src vs the thumbnail cache.
    the caches store 160xN thumbs (keep aspect) with 70% lossy jpeg
    quality in an eet file with one thumb per 10 second interval. so it
    has limited accuracy.
---
 TODO                    |   3 +-
 data/themes/default.edc | 211 ++++++++++++++++++++++++++
 src/bin/Makefile.am     |   3 +-
 src/bin/key.c           |   7 +-
 src/bin/main.c          |  11 +-
 src/bin/thumb.c         |   9 +-
 src/bin/video.c         |   8 +
 src/bin/video.h         |   1 +
 src/bin/videothumb.c    | 382 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/bin/videothumb.h    |   8 +
 src/bin/win.c           |  28 ++--
 src/bin/win.h           |   2 +-
 src/bin/winvid.c        |  84 +++++++++--
 13 files changed, 712 insertions(+), 45 deletions(-)

diff --git a/TODO b/TODO
index 5b1fa2f..944721a 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,5 @@
 * volume status display when changed (slider/image/percentage)
 * emotion engine selection options (gui)
-* timeline thumbnails on position slider
 * right click control panel (ala terminology - need elm config ui code)
 * about display/popup (from panel?)
 * mouse wheel for forward/rewind and/or prev/next and/or volume up/down
@@ -13,4 +12,4 @@
 * show busy anim until opened cb or failure
 * add button/control top-left next to audio to do fullscreen/normal toggle
 * detect letterboxing and auto-crop
-
+* thumbnail picker show all thumbs for video in a grid to select position
diff --git a/data/themes/default.edc b/data/themes/default.edc
index f3ea3e6..5aaa1dc 100644
--- a/data/themes/default.edc
+++ b/data/themes/default.edc
@@ -35,6 +35,10 @@ collections {
       images.image: "horizontal_separated_bar_small_glow.png" COMP;
       images.image: "vgrad_shadow_bi.png" COMP;
       
+      images.image: "win_shadow.png" COMP;
+      images.image: "bg_shine.png" COMP;
+      images.image: "bg_glint.png" COMP;
+
       set { name: "pos_indicator";
          image { image: "pos_indicator_big.png" COMP; size: 32 28 99999 99999; 
}
          image { image: "pos_indicator.png" COMP; size: 0 0 31 27; }
@@ -61,6 +65,7 @@ collections {
       script {
          public pos_visible;
          public pos_timer;
+         public drag_is_down;
          
          public pos_timeout(val) {
             set_int(pos_visible, 0);
@@ -1261,6 +1266,212 @@ collections {
             }
          }
          
+         part { name: "dragover_master"; type: RECT;
+            clip_to: "posclip";
+            description { state: "default" 0.0;
+               visible: 1;
+            }
+            description { state: "novideo" 0.0;
+               visible: 0;
+            }
+         }
+         program {
+            signal: "state,novideo"; source: "rage";
+            action: STATE_SET "novideo" 0.0;
+            target: "dragover_master";
+         }
+         program {
+            signal: "state,video"; source: "rage";
+            action: STATE_SET "default" 0.0;
+            target: "dragover_master";
+         }
+         part { name: "dragover_active"; type: RECT;
+            clip_to: "dragover_master";
+            description { state: "default" 0.0;
+               color: 255 255 255 0;
+               visible: 0;
+            }
+            description { state: "visible" 0.0;
+               visible: 1;
+               color: 255 255 255 255;
+            }
+         }
+         part { name: "dragover_event"; type: RECT; repeat_events: 1;
+            clip_to: "posclip";
+            description { state: "default" 0.0;
+               rel1.to_x: "bar_bg";
+               rel1.to_y: "posevent";
+               rel2.to_x: "bar_bg";
+               color: 0 0 0 0;
+               fixed: 1 1;
+            }
+         }
+         program {
+            signal: "mouse,in"; source: "dragover_event";
+            action: STATE_SET "visible" 0.0;
+            transition: SINUSOIDAL 0.5;
+            target: "dragover_active";
+         }
+         program {
+            signal: "mouse,out"; source: "dragover_event";
+            action: STATE_SET "default" 0.0;
+            transition: SINUSOIDAL 1.0;
+            target: "dragover_active";
+         }
+         part { name: "dragover"; type: SPACER;
+            description { state: "default" 0.0;
+               rel1.to: "dragover_event";
+               rel1.relative: 0.5 0.0;
+               rel2.to: "dragover_event";
+               rel2.relative: 0.5 0.0;
+               fixed: 1 1;
+            }
+         }
+         part { name: "dragshadow"; mouse_events: 0;
+            clip_to: "dragover_active";
+            description { state: "default" 0.0;
+               fixed: 1 1;
+               rel1.to: "rage.dragable.content";
+               rel2.to: "rage.dragable.content";
+               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: "dragover_fill"; mouse_events: 0;
+            clip_to: "dragover_active";
+            description { state: "default" 0.0;
+               fixed: 1 1;
+               image.normal: "pm_fill.png";
+               fill {
+                  size.relative: 0.0 0.0;
+                  size.offset: 64 64;
+               }
+               rel1.to: "rage.dragable.content";
+               rel2.to: "rage.dragable.content";
+            }
+         }
+         part { name: "rage.dragable.content"; type: SWALLOW;
+            clip_to: "dragover_active";
+            description { state: "default" 0.0;
+               rel1.to: "dragover";
+               rel2.to: "dragover";
+               align: 0.5 1.0;
+               fixed: 1 1;
+            }
+         }
+         part { name: "dragover_clip"; type: RECT;
+            clip_to: "dragover_active";
+            description { state: "default" 0.0;
+               rel1.to: "rage.dragable.content";
+               rel2.to: "rage.dragable.content";
+               fixed: 1 1;
+            }
+         }
+         part { name: "dragover_glintclip"; type: RECT;
+            clip_to: "dragover_active";
+            description { state: "default" 0.0;
+               rel1.to: "dragover_clip";
+               rel2.to: "dragover_clip";
+               rel1.offset: 0 -10;
+            }
+         }
+         part { name: "shine"; mouse_events: 0;
+            clip_to: "dragover_clip";
+            description { state: "default" 0.0;
+               image.normal: "bg_shine.png";
+               fill.smooth: 0;
+               rel1.to: "dragover_clip";
+               rel2.to: "dragover_clip";
+               align: 0.5 0.0;
+               aspect: (255/120) (255/120);
+               aspect_preference: HORIZONTAL;
+            }
+         }
+         part { name: "glint"; mouse_events: 0;
+            clip_to: "dragover_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: "dragover_clip";
+               }
+               rel2 {
+                 relative: 1.0 0.0;
+                 offset: -1 0;
+                 to: "dragover_clip";
+               }
+               image.normal: "bg_glint.png";
+            }
+         }
+         part { name: "dragover_bevel2";
+            clip_to: "dragover_active";
+            description { state: "default" 0.0;
+               image.normal: "bg_bevel.png";
+               image.border: 1 1 1 1;
+               image.middle: 0;
+               fill.smooth: 0;
+               rel1.to: "rage.dragable.content";
+               rel2.to: "rage.dragable.content";
+            }
+         }
+         program { name: "dragmove";
+            signal: "mouse,move"; source: "dragover_event";
+            script {
+               new mx, my, x, y, w, h;
+               new Float:pos, Float:px, Float:pw;
+
+               get_mouse(mx, my);
+               get_geometry(PART:"dragover_event", x, y, w, h);
+               x = mx - x;
+               y = my - y;
+               px = x;
+               pw = w;
+               if (w > 0) pos = px / pw;
+               else pos = 0.5;
+               custom_state(PART:"dragover", "default", 0.0);
+               set_state_val(PART:"dragover", STATE_REL1, pos, 0.0);
+               set_state_val(PART:"dragover", STATE_REL2, pos, 0.0);
+               set_state(PART:"dragover", "custom", 0.0);
+               send_message(MSG_FLOAT, 13, pos);
+               if (get_int(drag_is_down) == 1)
+                 send_message(MSG_FLOAT, 10, pos);
+            }
+         }
+         program { name: "dragdown";
+            signal: "mouse,down,1"; source: "dragover_event";
+            script {
+               new mx, my, x, y, w, h;
+               new Float:pos, Float:px, Float:pw;
+
+               set_int(drag_is_down, 1);
+               get_mouse(mx, my);
+               get_geometry(PART:"dragover_event", x, y, w, h);
+               x = mx - x;
+               y = my - y;
+               px = x;
+               pw = w;
+               if (w > 0) pos = px / pw;
+               else pos = 0.5;
+               custom_state(PART:"dragover", "default", 0.0);
+               set_state_val(PART:"dragover", STATE_REL1, pos, 0.0);
+               set_state_val(PART:"dragover", STATE_REL2, pos, 0.0);
+               set_state(PART:"dragover", "custom", 0.0);
+               send_message(MSG_FLOAT, 10, pos);
+            }
+         }
+         program { name: "dragup";
+            signal: "mouse,up,1"; source: "dragover_event";
+            script {
+               set_int(drag_is_down, 0);
+            }
+         }
          part { name: "event"; type: RECT; repeat_events: 1;
             description { state: "default" 0.0;
                color: 0 0 0 0;
diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am
index 479d5ab..bc92fe4 100644
--- a/src/bin/Makefile.am
+++ b/src/bin/Makefile.am
@@ -20,7 +20,8 @@ winvid.c winvid.h \
 win.c win.h \
 winlist.c winlist.h \
 config.c config.h \
-sha1.c sha1.h
+sha1.c sha1.h \
+videothumb.c videothumb.h
 
 internal_bindir = $(libdir)/rage/utils
 internal_bin_PROGRAMS = rage_thumb
diff --git a/src/bin/key.c b/src/bin/key.c
index 8d24ee3..a33ac67 100644
--- a/src/bin/key.c
+++ b/src/bin/key.c
@@ -9,8 +9,7 @@ void
 key_handle(Evas_Object *win, Evas_Event_Key_Down *ev)
 {
    Inf *inf = evas_object_data_get(win, "inf");
-   
-   printf("%s | %s\n", ev->key, ev->keyname);
+
    if ((!strcmp(ev->key, "Left")) ||
        (!strcmp(ev->key, "bracketleft")))
      {
@@ -20,7 +19,7 @@ key_handle(Evas_Object *win, Evas_Event_Key_Down *ev)
         else
           {
              double pos = video_position_get(inf->vid);
-             
+
              if (!inf->last_action_rwind)
                {
                   double t = ecore_time_get();
@@ -161,7 +160,7 @@ key_handle(Evas_Object *win, Evas_Event_Key_Down *ev)
    else if (!strcmp(ev->keyname, "n"))
      {
         int w, h;
-        
+
         video_ratio_size_get(inf->vid, &w, &h);
         if ((w > 1) && (h > 1)) evas_object_resize(win, w, h);
      }
diff --git a/src/bin/main.c b/src/bin/main.c
index 58d01e5..7f6ae4f 100644
--- a/src/bin/main.c
+++ b/src/bin/main.c
@@ -32,7 +32,8 @@ elm_main(int argc, char **argv)
    int i;
    Inf *inf;
    Config *config;
-   
+
+   elm_need_efreet();
    config_init();
    config = config_get();
    for (i = 1; i < argc; i++)
@@ -62,7 +63,7 @@ elm_main(int argc, char **argv)
         else
           list = eina_list_append(list, eina_stringshare_add(argv[i]));
      }
-   
+
    elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
    elm_app_compile_bin_dir_set(PACKAGE_BIN_DIR);
    elm_app_compile_data_dir_set(PACKAGE_DATA_DIR);
@@ -78,11 +79,11 @@ elm_main(int argc, char **argv)
    win = win_add();
    evas_object_event_callback_add(win, EVAS_CALLBACK_RESIZE, _cb_resize, NULL);
    evas_object_resize(win, 320, 200);
-   
+
    win_video_init(win);
    win_video_file_list_set(win, list);
    EINA_LIST_FREE(list, f) eina_stringshare_del(f);
-   
+
    inf = evas_object_data_get(win, "inf");
    if (argc <= 1)
      {
@@ -94,7 +95,7 @@ elm_main(int argc, char **argv)
      {
         inf->show_timeout = ecore_timer_add(10.0, _cb_show_timeout, win);
      }
-                        
+
    elm_run();
 
    config_shutdown();
diff --git a/src/bin/thumb.c b/src/bin/thumb.c
index 397ac9f..11a7aa1 100644
--- a/src/bin/thumb.c
+++ b/src/bin/thumb.c
@@ -63,7 +63,6 @@ elm_main(int argc, char **argv)
         char key[128];
 
         snprintf(key, sizeof(key), "%i", pos);
-        pos += incr;
         evas_object_image_file_set(vidimage, argv[1], key);
         evas_object_image_size_get(vidimage, &iw, &ih);
         if ((iw <= 0) || (ih <= 0)) break;
@@ -75,10 +74,10 @@ elm_main(int argc, char **argv)
         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);
-          }
+          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);
diff --git a/src/bin/video.c b/src/bin/video.c
index 500e6a5..3ac4f0f 100644
--- a/src/bin/video.c
+++ b/src/bin/video.c
@@ -472,6 +472,14 @@ video_file_set(Evas_Object *obj, const char *file)
    video_position_set(obj, 0.0);
 }
 
+const char *
+video_file_get(Evas_Object *obj)
+{
+   Video *sd = evas_object_smart_data_get(obj);
+   if (!sd) return NULL;
+   return sd->file;
+}
+
 void
 video_mute_set(Evas_Object *obj, Eina_Bool mute)
 {
diff --git a/src/bin/video.h b/src/bin/video.h
index be644f0..8257520 100644
--- a/src/bin/video.h
+++ b/src/bin/video.h
@@ -5,6 +5,7 @@
 
 Evas_Object *video_add(Evas_Object *parent);
 void video_file_set(Evas_Object *obj, const char *file);
+const char *video_file_get(Evas_Object *obj);
 void video_mute_set(Evas_Object *obj, Eina_Bool mute);
 Eina_Bool video_mute_get(Evas_Object *obj);
 void video_play_set(Evas_Object *obj, Eina_Bool play);
diff --git a/src/bin/videothumb.c b/src/bin/videothumb.c
new file mode 100644
index 0000000..8d41f5b
--- /dev/null
+++ b/src/bin/videothumb.c
@@ -0,0 +1,382 @@
+#include <Elementary.h>
+#include "videothumb.h"
+#include "sha1.h"
+
+typedef struct _Videothumb Videothumb;
+
+struct _Videothumb
+{
+   Evas_Object_Smart_Clipped_Data __clipped_data;
+   Evas_Object *o_img;
+   Ecore_Exe *thumb_exe;
+   Ecore_Event_Handler *exe_handler;
+   const char *file;
+   const char *realfile;
+   double pos;
+   unsigned int realpos;
+   int iw, ih;
+   Evas_Coord w, h;
+   Eina_Bool seen : 1;
+};
+
+static Evas_Smart *_smart = NULL;
+static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
+
+static Eina_List *busy_thumbs = NULL;
+static Eina_List *vidthumbs = NULL;
+
+static Eina_Bool
+_busy_add(const char *file)
+{
+   Eina_List *l;
+   const char *s;
+
+   EINA_LIST_FOREACH(busy_thumbs, l, s)
+     {
+        if (!strcmp(file, s)) return EINA_FALSE;
+     }
+   busy_thumbs = eina_list_append(busy_thumbs, eina_stringshare_add(file));
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_busy_del(const char *file)
+{
+   Eina_List *l;
+   const char *s;
+
+   EINA_LIST_FOREACH(busy_thumbs, l, s)
+     {
+        if (!strcmp(file, s))
+          {
+             eina_stringshare_del(s);
+             busy_thumbs = eina_list_remove_list(busy_thumbs, l);
+             return EINA_TRUE;
+          }
+     }
+   return EINA_FALSE;
+}
+
+static void
+_thumb_update(Evas_Object *obj)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   char buf[PATH_MAX];
+
+   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));
+   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);
+     }
+   else
+     {
+        evas_object_image_preload(sd->o_img, EINA_FALSE);
+        evas_object_smart_callback_call(obj, "loaded", NULL);
+     }
+}
+
+static void
+_thumb_match_update(Evas_Object *obj, const char *file)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+
+   if (!sd) return;
+   if (!strcmp(sd->file, file)) _thumb_update(obj);
+}
+
+static Eina_Bool
+_cb_thumb_exe(void *data, int type EINA_UNUSED, void *event)
+{
+   Ecore_Exe_Event_Del *ev;
+   Videothumb *sd = evas_object_smart_data_get(data);
+
+   if (!sd) return EINA_TRUE;
+   ev = event;
+   if (ev->exe == sd->thumb_exe)
+     {
+        Eina_List *l;
+        Evas_Object *o;
+
+        _busy_del(sd->file);
+        EINA_LIST_FOREACH(vidthumbs, l, o) _thumb_match_update(o, sd->file);
+        if ((sd->iw <= 0) || (sd->ih <= 0))
+          {
+             if (sd->exe_handler)
+               {
+                  ecore_event_handler_del(sd->exe_handler);
+                  sd->exe_handler = NULL;
+               }
+          }
+        sd->thumb_exe = NULL;
+        return EINA_FALSE;
+     }
+   return EINA_TRUE;
+}
+
+static void
+_cb_preload(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, 
void *info EINA_UNUSED)
+{
+   Videothumb *sd = evas_object_smart_data_get(data);
+
+   if (!sd) return;
+   evas_object_show(sd->o_img);
+   evas_object_smart_callback_call(data, "data", NULL);
+}
+
+static void
+_videothumb_image_load(Evas_Object *obj)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   char buf_base[PATH_MAX];
+   char buf_file[PATH_MAX];
+   char buf[PATH_MAX];
+   char *s;
+   const char *libdir;
+   unsigned char sum[20];
+
+   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->file, strlen(sd->file), 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,
+                                  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));
+   if (sd->iw > 0)
+     {
+        Eina_Bool ok = EINA_FALSE;
+        struct stat st1, st2;
+
+        if (stat(sd->file, &st1) == 0)
+          {
+             if (stat(sd->realfile, &st2) == 0)
+               {
+                  if (st1.st_mtime < st2.st_mtime) ok = EINA_TRUE;
+               }
+          }
+        if (ok)
+          {
+             evas_object_image_preload(sd->o_img, EINA_FALSE);
+             return;
+          }
+     }
+   if (!_busy_add(sd->file)) 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->file);
+   if (s)
+     {
+        libdir = elm_app_lib_dir_get();
+        if (libdir)
+          {
+             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);
+          }
+     }
+}
+
+static void
+_videothumb_eval(Evas_Object *obj, Eina_Bool force)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   Evas_Coord ox, oy, ow, oh, vw, vh;
+   Eina_Bool seen = EINA_FALSE;
+
+   if (!sd) return;
+   evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
+   evas_output_viewport_get(evas_object_evas_get(obj), NULL, NULL, &vw, &vh);
+   // XXX: not listening to canvas resizes! :(
+   if (ELM_RECTS_INTERSECT(ox, oy, ow, oh, 0, 0, vw, vh)) seen = EINA_TRUE;
+   // XXX: fix - wrokaround and always visible
+   seen = EINA_TRUE;
+   if (force)
+     {
+        sd->seen = seen;
+        if (sd->o_img)
+          {
+             evas_object_del(sd->o_img);
+             sd->o_img = NULL;
+          }
+        _videothumb_image_load(obj);
+        evas_object_move(sd->o_img, ox, oy);
+        evas_object_resize(sd->o_img, ow, oh);
+     }
+   else
+     {
+        if (seen != sd->seen)
+          {
+             sd->seen = seen;
+             if (sd->seen)
+               {
+                  if (sd->o_img)
+                    {
+                       evas_object_del(sd->o_img);
+                       sd->o_img = NULL;
+                    }
+                  _videothumb_image_load(obj);
+                  evas_object_move(sd->o_img, ox, oy);
+                  evas_object_resize(sd->o_img, ow, oh);
+               }
+             else
+               {
+                  if (sd->o_img)
+                    {
+                       evas_object_del(sd->o_img);
+                       sd->o_img = NULL;
+                    }
+               }
+          }
+     }
+}
+
+static void
+_smart_add(Evas_Object *obj)
+{
+   Videothumb *sd;
+
+   sd = calloc(1, sizeof(Videothumb));
+   EINA_SAFETY_ON_NULL_RETURN(sd);
+   evas_object_smart_data_set(obj, sd);
+   _parent_sc.add(obj);
+}
+
+static void
+_smart_del(Evas_Object *obj)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   if (!sd) return;
+   vidthumbs = eina_list_remove(vidthumbs, obj);
+   if (sd->file) eina_stringshare_del(sd->file);
+   if (sd->realfile) eina_stringshare_del(sd->realfile);
+   if (sd->o_img) evas_object_del(sd->o_img);
+   if (sd->thumb_exe) ecore_exe_free(sd->thumb_exe);
+   if (sd->exe_handler) ecore_event_handler_del(sd->exe_handler);
+   _parent_sc.del(obj);
+}
+
+static void
+_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   Evas_Coord ox, oy, ow, oh;
+   if (!sd) return;
+   evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
+   if ((ow == w) && (oh == h)) return;
+   evas_object_smart_changed(obj);
+}
+
+static void
+_smart_calculate(Evas_Object *obj)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   Evas_Coord ox, oy, ow, oh;
+
+   if (!sd) return;
+   evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
+   sd->w = ow;
+   sd->h = oh;
+   _videothumb_eval(obj, EINA_FALSE);
+   if (sd->o_img)
+     {
+        evas_object_move(sd->o_img, ox, oy);
+        evas_object_resize(sd->o_img, ow, oh);
+     }
+}
+
+static void
+_smart_move(Evas_Object *obj, Evas_Coord x EINA_UNUSED, Evas_Coord y 
EINA_UNUSED)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   if (!sd) return;
+   evas_object_smart_changed(obj);
+}
+
+static void
+_smart_init(void)
+{
+   static Evas_Smart_Class sc;
+
+   evas_object_smart_clipped_smart_set(&_parent_sc);
+   sc           = _parent_sc;
+   sc.name      = "videothumb";
+   sc.version   = EVAS_SMART_CLASS_VERSION;
+   sc.add       = _smart_add;
+   sc.del       = _smart_del;
+   sc.resize    = _smart_resize;
+   sc.move      = _smart_move;
+   sc.calculate = _smart_calculate;
+   _smart = evas_smart_class_new(&sc);
+}
+
+Evas_Object *
+videothumb_add(Evas_Object *parent)
+{
+   Evas *e;
+   Evas_Object *obj;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
+   e = evas_object_evas_get(parent);
+   if (!e) return NULL;
+   if (!_smart) _smart_init();
+   obj = evas_object_smart_add(e, _smart);
+   vidthumbs = eina_list_prepend(vidthumbs, obj);
+   return obj;
+}
+
+void
+videothumb_file_set(Evas_Object *obj, const char *file, double pos)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   if (!sd) return;
+   if ((sd->file == file) && (sd->pos == pos)) return;
+   if (sd->file) eina_stringshare_del(sd->file);
+   sd->file = eina_stringshare_add(file);
+   sd->pos = pos;
+   _videothumb_eval(obj, EINA_TRUE);
+}
+
+void
+videothumb_size_get(Evas_Object *obj, int *w, int *h)
+{
+   Videothumb *sd = evas_object_smart_data_get(obj);
+   *w = 1;
+   *h = 1;
+   if (!sd) return;
+   *w = sd->iw;
+   *h = sd->ih;
+}
diff --git a/src/bin/videothumb.h b/src/bin/videothumb.h
new file mode 100644
index 0000000..ce0029a
--- /dev/null
+++ b/src/bin/videothumb.h
@@ -0,0 +1,8 @@
+#ifndef _VIDEOTHUMB_H__
+#define _VIDEOTHUMB_H__ 1
+
+Evas_Object *videothumb_add(Evas_Object *parent);
+void videothumb_file_set(Evas_Object *obj, const char *file, double pos);
+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 988ace1..d356099 100644
--- a/src/bin/win.c
+++ b/src/bin/win.c
@@ -138,7 +138,7 @@ win_do_next(Evas_Object *win)
                {
                   double pos = video_position_get(inf->vid);
                   double len = video_length_get(inf->vid);
-                  
+
                   if ((len - pos) > 5.0)
                     {
                        video_position_set(inf->vid, len - 5.0);
@@ -198,7 +198,7 @@ win_video_prev(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
    Eina_List *l;
-   
+
    if (!inf->file_list) return;
    if (!inf->file_cur) l = eina_list_last(inf->file_list);
    else l = inf->file_cur->prev;
@@ -213,7 +213,7 @@ win_video_first(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
    Eina_List *l;
-   
+
    if (!inf->file_list) return;
    l = inf->file_list;
    inf->file_cur = l;
@@ -226,7 +226,7 @@ win_video_last(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
    Eina_List *l;
-   
+
    if (!inf->file_list) return;
    l = eina_list_last(inf->file_list);
    if (!l) return;
@@ -240,7 +240,7 @@ win_video_have_next(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
    Eina_List *l;
-   
+
    if (!inf->file_list) return EINA_FALSE;
    if (!inf->file_cur) return EINA_FALSE;
    else l = inf->file_cur->next;
@@ -253,7 +253,7 @@ win_video_have_prev(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
    Eina_List *l;
-   
+
    if (!inf->file_list) return EINA_FALSE;
    if (!inf->file_cur) return EINA_FALSE;
    else l = inf->file_cur->prev;
@@ -269,19 +269,19 @@ win_add(void)
    Inf *inf = calloc(1, sizeof(Inf));
 
    if (!inf) return NULL;
-   
+
    win = elm_win_add(NULL, "Rage", ELM_WIN_BASIC);
    if (!win) return NULL;
-   
+
    elm_win_title_set(win, "Rage");
    elm_win_autodel_set(win, EINA_TRUE);
-   
+
    evas_object_data_set(win, "inf", inf);
    evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, _cb_win_del, NULL);
    evas_object_smart_callback_add(win, "fullscreen", _cb_fullscreen, NULL);
    evas_object_smart_callback_add(win, "unfullscreen", _cb_unfullscreen, NULL);
    evas_object_smart_callback_add(win, "normal", _cb_unfullscreen, NULL);
-   
+
    o = evas_object_image_add(evas_object_evas_get(win));
    snprintf(buf, sizeof(buf), "%s/images/rage.png", elm_app_data_dir_get());
    evas_object_image_file_set(o, buf, NULL);
@@ -295,7 +295,7 @@ win_add(void)
    evas_object_show(o);
    inf->lay = o;
    controls_init(win, o);
-   
+
    o = evas_object_rectangle_add(evas_object_evas_get(win));
    evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
    elm_win_resize_object_add(win, o);
@@ -334,7 +334,7 @@ win_title_update(Evas_Object *win)
    Inf *inf = evas_object_data_get(win, "inf");
    const char *file, *s;
    char buf[4096];
-   
+
    if (!inf->file_cur)
      {
         elm_win_title_set(win, "Rage");
@@ -393,7 +393,7 @@ win_aspect_adjust(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
    int w = 1, h = 1;
-   
+
    video_ratio_size_get(inf->vid, &w, &h);
    if (inf->zoom_mode == 1)
      evas_object_size_hint_aspect_set(inf->vid, EVAS_ASPECT_CONTROL_NEITHER,
@@ -415,6 +415,6 @@ void
 win_frame_decode(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
-   
+
    controls_update(inf->lay, inf->vid);
 }
diff --git a/src/bin/win.h b/src/bin/win.h
index 43ec07a..6967f78 100644
--- a/src/bin/win.h
+++ b/src/bin/win.h
@@ -5,7 +5,7 @@ typedef struct _Inf Inf;
 
 struct _Inf
 {
-   Evas_Object *vid, *lay, *event, *event2, *glayer;
+   Evas_Object *vid, *lay, *event, *event2, *glayer, *vidthumb;
    Eina_List *file_list, *file_cur;
    Ecore_Job *next_job;
    Ecore_Timer *show_timeout;
diff --git a/src/bin/winvid.c b/src/bin/winvid.c
index 4f55e61..e93d53c 100644
--- a/src/bin/winvid.c
+++ b/src/bin/winvid.c
@@ -4,6 +4,7 @@
 #include "video.h"
 #include "winlist.h"
 #include "winvid.h"
+#include "videothumb.h"
 
 static void
 _cb_resize(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
@@ -30,7 +31,6 @@ static void
 _cb_stop(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
 {
    Inf *inf = evas_object_data_get(data, "inf");
-   printf("stop\n");
    if (win_video_have_next(data))
      {
         if (inf->next_job) ecore_job_del(inf->next_job);
@@ -42,7 +42,6 @@ _cb_stop(void *data, Evas_Object *obj EINA_UNUSED, void 
*event EINA_UNUSED)
 static void
 _cb_opened(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
 {
-   printf("opened\n");
    win_aspect_adjust(data);
    win_frame_decode(data);
    win_title_update(data);
@@ -51,7 +50,6 @@ _cb_opened(void *data, Evas_Object *obj EINA_UNUSED, void 
*event EINA_UNUSED)
 static void
 _cb_length(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
 {
-   printf("len\n");
    win_frame_decode(data);
    win_title_update(data);
 }
@@ -59,21 +57,18 @@ _cb_length(void *data, Evas_Object *obj EINA_UNUSED, void 
*event EINA_UNUSED)
 static void
 _cb_title(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
 {
-   printf("title\n");
    win_title_update(data);
 }
 
 static void
 _cb_audio(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
 {
-   printf("audio\n");
    win_title_update(data);
 }
 
 static void
 _cb_channels(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
 {
-   printf("channels\n");
    win_title_update(data);
 }
 
@@ -81,7 +76,6 @@ static void
 _cb_play_start(void *data, Evas_Object *obj EINA_UNUSED, void *event 
EINA_UNUSED)
 {
    Inf *inf = evas_object_data_get(data, "inf");
-   printf("play start\n");
    win_aspect_adjust(data);
    win_frame_decode(data);
    win_title_update(data);
@@ -92,7 +86,6 @@ static void
 _cb_play_finish(void *data, Evas_Object *obj EINA_UNUSED, void *event 
EINA_UNUSED)
 {
    Inf *inf = evas_object_data_get(data, "inf");
-   printf("play finish\n");
    if (!inf->playing) win_show(data, 160, 160);
    inf->playing = EINA_FALSE;
 }
@@ -100,23 +93,75 @@ _cb_play_finish(void *data, Evas_Object *obj EINA_UNUSED, 
void *event EINA_UNUSE
 static void
 _cb_button_num(void *data, Evas_Object *obj EINA_UNUSED, void *event 
EINA_UNUSED)
 {
-   printf("button num\n");
    win_title_update(data);
 }
 
 static void
 _cb_button(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
 {
-   printf("button\n");
    win_title_update(data);
 }
 
+static void
+_cb_vidthumb_loaded(void *data, Evas_Object *obj EINA_UNUSED, void *event 
EINA_UNUSED)
+{
+   Inf *inf = evas_object_data_get(data, "inf");
+
+   if (!inf) return;
+}
+
+static void
+_cb_vidthumb_failed(void *data, Evas_Object *obj EINA_UNUSED, void *event 
EINA_UNUSED)
+{
+   Inf *inf = evas_object_data_get(data, "inf");
+
+   if (!inf) return;
+}
+
+static void
+_cb_vidthumb_data(void *data, Evas_Object *obj EINA_UNUSED, void *event 
EINA_UNUSED)
+{
+   int w, h;
+   Inf *inf = evas_object_data_get(data, "inf");
+
+   if (!inf) return;
+   videothumb_size_get(inf->vidthumb, &w, &h);
+   w = ((double)w * elm_config_scale_get()) / 2.0;
+   h = ((double)h * elm_config_scale_get()) / 2.0;
+   evas_object_size_hint_min_set(inf->vidthumb, w, h);
+}
+
+static void
+_cb_layout_message(void *data, Evas_Object *obj EINA_UNUSED, Edje_Message_Type 
type, int id, void *msg)
+{
+   Inf *inf = evas_object_data_get(data, "inf");
+
+   if (!inf) return;
+   if (type == EDJE_MESSAGE_FLOAT)
+     {
+        if (id == 10)
+          {
+             Edje_Message_Float *m = msg;
+             double len = video_length_get(inf->vid);
+             video_position_set(inf->vid, len * m->val);
+          }
+        else if (id == 13)
+          {
+             Edje_Message_Float *m = msg;
+             double len = video_length_get(inf->vid);
+             videothumb_file_set(inf->vidthumb, video_file_get(inf->vid),
+                                 len * m->val);
+          }
+     }
+}
+
 void
 win_video_init(Evas_Object *win)
 {
    Inf *inf = evas_object_data_get(win, "inf");
    Evas_Object *o;
-   
+   Evas_Coord sz;
+
    o = video_add(win);
    video_fill_set(o, EINA_TRUE);
    inf->vid = o;
@@ -134,6 +179,19 @@ win_video_init(Evas_Object *win)
    evas_object_smart_callback_add(o, "button", _cb_button, win);
    elm_object_part_content_set(inf->lay, "rage.content", o);
    evas_object_show(o);
+
+   o = videothumb_add(win);
+   inf->vidthumb = o;
+   evas_object_smart_callback_add(o, "loaded", _cb_vidthumb_loaded, win);
+   evas_object_smart_callback_add(o, "failed", _cb_vidthumb_failed, win);
+   evas_object_smart_callback_add(o, "data", _cb_vidthumb_data, win);
+   sz = 80;
+   sz = (double)sz * elm_config_scale_get();
+   evas_object_size_hint_min_set(o, sz, sz);
+   elm_object_part_content_set(inf->lay, "rage.dragable.content", o);
+   evas_object_show(o);
+   edje_object_message_handler_set(elm_layout_edje_get(inf->lay),
+                                   _cb_layout_message, win);
 }
 
 void
@@ -142,7 +200,7 @@ win_video_file_list_set(Evas_Object *win, Eina_List *list)
    Inf *inf = evas_object_data_get(win, "inf");
    Eina_List *l, *list2 = NULL;
    const char *f;
-   
+
    EINA_LIST_FOREACH(list, l, f)
      {
         list2 = eina_list_append(list2, eina_stringshare_add(f));
@@ -155,7 +213,7 @@ void
 win_video_insert(Evas_Object *win, const char *file)
 {
    Inf *inf = evas_object_data_get(win, "inf");
-   
+
    inf->file_list = eina_list_append_relative_list
      (inf->file_list, eina_stringshare_add(file), inf->file_cur);
    evas_object_data_set(win, "file_list", inf->file_list);

-- 


Reply via email to