elementary: reduce possibility of reading thumbnail in the middle of its generation.
[framework/uifw/elementary.git] / src / lib / elm_thumb.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Thumb Thumb
6  *
7  * A thumb object is used for displaying the thumbnail of an image or video.
8  * You must have compiled Elementary with Ethumb_Client support and the DBus
9  * service must be present and auto-activated in order to have thumbnails to
10  * be generated.
11  *
12  * Signals that you can add callbacks for are:
13  *
14  * "clicked" - This is called when a user has clicked the thumb without dragging
15  *             around.
16  * "clicked,double" - This is called when a user has double-clicked the thumb.
17  * "press" - This is called when a user has pressed down the thumb.
18  * "generate,start" - The thumbnail generation started.
19  * "generate,stop" - The generation process stopped.
20  * "generate,error" - The generation failed.
21  * "load,error" - The thumbnail image loading failed.
22  */
23
24 typedef struct _Widget_Data Widget_Data;
25
26 struct _Widget_Data
27 {
28    Evas_Object *self;
29    Evas_Object *frame;
30    Evas_Object *view;
31    const char *file;
32    const char *key;
33    struct
34      {
35         int id;
36         const char *file;
37         const char *key;
38 #ifdef HAVE_ELEMENTARY_ETHUMB
39         const char *thumb_path;
40         const char *thumb_key;
41         Ethumb_Exists *exists;
42         Ecore_Idle_Enterer *idler;
43
44         Ethumb_Thumb_Format format;
45 #endif
46      } thumb;
47    Ecore_Event_Handler *eeh;
48    Elm_Thumb_Animation_Setting anim_setting;
49    Eina_Bool on_hold : 1;
50    Eina_Bool is_video : 1;
51    Eina_Bool was_video : 1;
52    Eina_Bool edit : 1;
53 };
54
55 static const char *widtype = NULL;
56
57 static const char SIG_CLICKED[] = "clicked";
58 static const char SIG_CLICKED_DOUBLE[] = "clicked,double";
59 static const char SIG_GENERATE_ERROR[] = "generate,error";
60 static const char SIG_GENERATE_START[] = "generate,start";
61 static const char SIG_GENERATE_STOP[] = "generate,stop";
62 static const char SIG_LOAD_ERROR[] = "load,error";
63 static const char SIG_PRESS[] = "press";
64
65 static const Evas_Smart_Cb_Description _signals[] =
66 {
67      {SIG_CLICKED, ""},
68      {SIG_CLICKED_DOUBLE, ""},
69      {SIG_GENERATE_ERROR, ""},
70      {SIG_GENERATE_START, ""},
71      {SIG_GENERATE_STOP, ""},
72      {SIG_LOAD_ERROR, ""},
73      {SIG_PRESS, ""},
74      {NULL, NULL}
75 };
76
77 #define EDJE_SIGNAL_GENERATE_START "elm,thumb,generate,start"
78 #define EDJE_SIGNAL_GENERATE_STOP "elm,thumb,generate,stop"
79 #define EDJE_SIGNAL_GENERATE_ERROR "elm,thumb,generate,error"
80 #define EDJE_SIGNAL_LOAD_ERROR "elm,thumb,load,error"
81 #define EDJE_SIGNAL_PULSE_START "elm,state,pulse,start"
82 #define EDJE_SIGNAL_PULSE_STOP "elm,state,pulse,stop"
83
84 struct _Ethumb_Client *_elm_ethumb_client = NULL;
85 Eina_Bool _elm_ethumb_connected = EINA_FALSE;
86
87 EAPI int ELM_ECORE_EVENT_ETHUMB_CONNECT = 0;
88
89 static void
90 _del_hook(Evas_Object *obj)
91 {
92    Widget_Data *wd = elm_widget_data_get(obj);
93
94 #ifdef HAVE_ELEMENTARY_ETHUMB
95    if (wd->thumb.id >= 0)
96      {
97         ethumb_client_generate_cancel(_elm_ethumb_client, wd->thumb.id,
98                                       NULL, NULL, NULL);
99         wd->thumb.id = -1;
100      }
101    if (wd->thumb.exists)
102      {
103         ethumb_client_thumb_exists_cancel(wd->thumb.exists);
104         wd->thumb.exists = NULL;
105      }
106    if (wd->thumb.idler)
107      {
108         ecore_idle_enterer_del(wd->thumb.idler);
109         wd->thumb.idler = NULL;
110      }
111
112    eina_stringshare_del(wd->thumb.thumb_path);
113    eina_stringshare_del(wd->thumb.thumb_key);
114 #endif
115
116    eina_stringshare_del(wd->file);
117    eina_stringshare_del(wd->key);
118    if (wd->eeh) ecore_event_handler_del(wd->eeh);
119    free(wd);
120 }
121
122 static void
123 _theme_hook(Evas_Object *obj)
124 {
125    Widget_Data *wd = elm_widget_data_get(obj);
126    _elm_theme_object_set(obj, wd->frame, "thumb", "base",
127                          elm_widget_style_get(obj));
128 }
129
130 #ifdef HAVE_ELEMENTARY_ETHUMB
131 static void
132 _mouse_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
133 {
134    Widget_Data *wd = data;
135    Evas_Event_Mouse_Down *ev = event_info;
136
137    if (ev->button != 1)
138      return;
139    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
140      wd->on_hold = EINA_TRUE;
141    else
142      wd->on_hold = EINA_FALSE;
143    if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
144      evas_object_smart_callback_call(wd->self, SIG_CLICKED_DOUBLE, NULL);
145    else
146      evas_object_smart_callback_call(wd->self, SIG_PRESS, NULL);
147 }
148
149 static void
150 _mouse_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
151 {
152    Widget_Data *wd = data;
153    Evas_Event_Mouse_Up *ev = event_info;
154
155    if (ev->button != 1)
156      return;
157    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
158      wd->on_hold = EINA_TRUE;
159    else
160      wd->on_hold = EINA_FALSE;
161    if (!wd->on_hold)
162      evas_object_smart_callback_call(wd->self, SIG_CLICKED, NULL);
163    wd->on_hold = EINA_FALSE;
164 }
165
166 /* As we do use stat to check if a thumbnail is available, it's possible
167    that we end up accessing before the file is completly written on disk.
168    By using an idler enterer, I hope to wait enought before trying accessing
169    it again.
170 */
171 static Eina_Bool
172 _retry_thumb(void *data)
173 {
174    Widget_Data *wd = data;
175    Evas_Coord mw, mh;
176    int r;
177
178    wd->thumb.idler = NULL;
179
180    if ((wd->is_video) && (wd->thumb.format == ETHUMB_THUMB_EET))
181      {
182         if (!edje_object_file_set(wd->view,
183                                   wd->thumb.thumb_path,
184                                   "movie/thumb"))
185           {
186              ERR("could not set file=%s key=%s for %s",
187                  wd->thumb.thumb_path,
188                  wd->thumb.thumb_key,
189                  wd->file);
190              goto view_err;
191           }
192      }
193    else
194      {
195         evas_object_image_file_set(wd->view,
196                                    wd->thumb.thumb_path,
197                                    wd->thumb.thumb_key);
198         r = evas_object_image_load_error_get(wd->view);
199         if (r != EVAS_LOAD_ERROR_NONE)
200           {
201              ERR("%s: %s", wd->thumb.thumb_path, evas_load_error_str(r));
202              goto view_err;
203           }
204      }
205
206    edje_object_part_swallow(wd->frame, "elm.swallow.content", wd->view);
207    edje_object_size_min_get(wd->frame, &mw, &mh);
208    edje_object_size_min_restricted_calc(wd->frame, &mw, &mh, mw, mh);
209    evas_object_size_hint_min_set(wd->self, mw, mh);
210    eina_stringshare_replace(&(wd->thumb.file), wd->thumb.thumb_path);
211    eina_stringshare_replace(&(wd->thumb.key), wd->thumb.thumb_key);
212    edje_object_signal_emit(wd->frame, EDJE_SIGNAL_GENERATE_STOP, "elm");
213    evas_object_smart_callback_call(wd->self, SIG_GENERATE_STOP, NULL);
214
215    eina_stringshare_del(wd->thumb.thumb_path);
216    wd->thumb.thumb_path = NULL;
217
218    eina_stringshare_del(wd->thumb.thumb_key);
219    wd->thumb.thumb_key = NULL;
220
221    return ECORE_CALLBACK_CANCEL;
222
223  view_err:
224    eina_stringshare_del(wd->thumb.thumb_path);
225    wd->thumb.thumb_path = NULL;
226
227    eina_stringshare_del(wd->thumb.thumb_key);
228    wd->thumb.thumb_key = NULL;
229
230    evas_object_del(wd->view);
231    wd->view = NULL;
232
233    edje_object_signal_emit(wd->frame, EDJE_SIGNAL_LOAD_ERROR, "elm");
234    evas_object_smart_callback_call(wd->self, SIG_LOAD_ERROR, NULL);
235
236    return ECORE_CALLBACK_CANCEL;
237 }
238
239 static void
240 _finished_thumb(Widget_Data *wd,
241                 const char *thumb_path,
242                 const char *thumb_key)
243 {
244    Evas *evas;
245    Evas_Coord mw, mh;
246    int r;
247
248    evas = evas_object_evas_get(wd->self);
249    if ((wd->view) && (wd->is_video ^ wd->was_video))
250      {
251         evas_object_del(wd->view);
252         wd->view = NULL;
253      }
254    wd->was_video = wd->is_video;
255
256    if ((wd->is_video) &&
257        (ethumb_client_format_get(_elm_ethumb_client) == ETHUMB_THUMB_EET))
258      {
259         if (!wd->view)
260           {
261              wd->view = edje_object_add(evas);
262              if (!wd->view)
263                {
264                   ERR("could not create edje object");
265                   goto err;
266                }
267              elm_widget_sub_object_add(wd->self, wd->view);
268           }
269
270         if (!edje_object_file_set(wd->view, thumb_path, "movie/thumb"))
271           {
272              wd->thumb.thumb_path = eina_stringshare_ref(thumb_path);
273              wd->thumb.thumb_key = eina_stringshare_ref(thumb_key);
274              wd->thumb.format = ethumb_client_format_get(_elm_ethumb_client);
275              wd->thumb.idler = ecore_idle_enterer_add(_retry_thumb, wd);
276              return ;
277           }
278      }
279    else
280      {
281         if (!wd->view)
282           {
283              wd->view = evas_object_image_filled_add(evas);
284              if (!wd->view)
285                {
286                   ERR("could not create image object");
287                   goto err;
288                }
289              elm_widget_sub_object_add(wd->self, wd->view);
290           }
291
292         evas_object_image_file_set(wd->view, thumb_path, thumb_key);
293         r = evas_object_image_load_error_get(wd->view);
294         if (r != EVAS_LOAD_ERROR_NONE)
295           {
296              WRN("%s: %s", thumb_path, evas_load_error_str(r));
297              wd->thumb.thumb_path = eina_stringshare_ref(thumb_path);
298              wd->thumb.thumb_key = eina_stringshare_ref(thumb_key);
299              wd->thumb.format = ethumb_client_format_get(_elm_ethumb_client);
300              wd->thumb.idler = ecore_idle_enterer_add(_retry_thumb, wd);
301              return ;
302           }
303      }
304
305    edje_object_part_swallow(wd->frame, "elm.swallow.content", wd->view);
306    edje_object_size_min_get(wd->frame, &mw, &mh);
307    edje_object_size_min_restricted_calc(wd->frame, &mw, &mh, mw, mh);
308    evas_object_size_hint_min_set(wd->self, mw, mh);
309    eina_stringshare_replace(&(wd->thumb.file), thumb_path);
310    eina_stringshare_replace(&(wd->thumb.key), thumb_key);
311    edje_object_signal_emit(wd->frame, EDJE_SIGNAL_GENERATE_STOP, "elm");
312    evas_object_smart_callback_call(wd->self, SIG_GENERATE_STOP, NULL);
313    return;
314
315 err:
316    edje_object_signal_emit(wd->frame, EDJE_SIGNAL_LOAD_ERROR, "elm");
317    evas_object_smart_callback_call(wd->self, SIG_LOAD_ERROR, NULL);
318 }
319
320 static void
321 _finished_thumb_cb(void *data, Ethumb_Client *c __UNUSED__, int id, const char *file, const char *key, const char *thumb_path, const char *thumb_key, Eina_Bool success)
322 {
323    Widget_Data *wd = data;
324
325    EINA_SAFETY_ON_FALSE_RETURN(wd->thumb.id == id);
326    wd->thumb.id = -1;
327
328    edje_object_signal_emit(wd->frame, EDJE_SIGNAL_PULSE_STOP, "elm");
329
330    if (success)
331      {
332         _finished_thumb(wd, thumb_path, thumb_key);
333         return;
334      }
335
336    ERR("could not generate thumbnail for %s (key: %s)", file, key ? key : "");
337    edje_object_signal_emit(wd->frame, EDJE_SIGNAL_GENERATE_ERROR, "elm");
338    evas_object_smart_callback_call(wd->self, SIG_GENERATE_ERROR, NULL);
339 }
340
341 static void
342 _thumb_exists(Ethumb_Client *client __UNUSED__, Ethumb_Exists *thread,
343               Eina_Bool exists, void *data)
344 {
345    Widget_Data *wd = data;
346
347    if (ethumb_client_thumb_exists_check(thread))
348      return ;
349
350    wd->thumb.exists = NULL;
351
352    if (exists)
353      {
354         const char *thumb_path, *thumb_key;
355
356         wd->thumb.id = -1;
357         ethumb_client_thumb_path_get(_elm_ethumb_client, &thumb_path,
358                                      &thumb_key);
359         _finished_thumb(wd, thumb_path, thumb_key);
360         return;
361      }
362    else if ((wd->thumb.id = ethumb_client_generate
363              (_elm_ethumb_client, _finished_thumb_cb, wd, NULL)) != -1)
364      {
365         edje_object_signal_emit(wd->frame, EDJE_SIGNAL_PULSE_START, "elm");
366         edje_object_signal_emit(wd->frame, EDJE_SIGNAL_GENERATE_START, "elm");
367         evas_object_smart_callback_call(wd->self, SIG_GENERATE_START, NULL);
368      }
369    else
370      {
371         wd->thumb.id = -1;
372         edje_object_signal_emit(wd->frame, EDJE_SIGNAL_GENERATE_ERROR, "elm");
373         evas_object_smart_callback_call(wd->self, SIG_GENERATE_ERROR, NULL);
374      }
375
376 }
377
378 static void
379 _thumb_apply(Widget_Data *wd)
380 {
381    if (wd->thumb.id > 0)
382      {
383         ethumb_client_generate_cancel
384            (_elm_ethumb_client, wd->thumb.id, NULL, NULL, NULL);
385         wd->thumb.id = -1;
386      }
387
388    if (wd->thumb.exists)
389      {
390         ethumb_client_thumb_exists_cancel(wd->thumb.exists);
391         wd->thumb.exists = NULL;
392      }
393
394    if (wd->thumb.idler)
395      {
396         ecore_idle_enterer_del(wd->thumb.idler);
397         wd->thumb.idler = NULL;
398      }
399
400    if (!wd->file) return;
401
402    ethumb_client_file_set(_elm_ethumb_client, wd->file, wd->key);
403    wd->thumb.exists = ethumb_client_thumb_exists(_elm_ethumb_client,
404                                                  _thumb_exists,
405                                                  wd);
406 }
407
408 static Eina_Bool
409 _thumb_apply_cb(void *data, int type __UNUSED__, void *ev __UNUSED__)
410 {
411    _thumb_apply(data);
412    return ECORE_CALLBACK_RENEW;
413 }
414
415 static void
416 _thumb_show(Widget_Data *wd)
417 {
418    evas_object_show(wd->frame);
419
420    if (elm_thumb_ethumb_client_connected())
421      {
422         _thumb_apply(wd);
423         return;
424      }
425
426    if (!wd->eeh)
427      wd->eeh = ecore_event_handler_add(ELM_ECORE_EVENT_ETHUMB_CONNECT,
428                                        _thumb_apply_cb, wd);
429 }
430
431 static void
432 _thumb_show_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
433 {
434    _thumb_show(data);
435 }
436
437 static void
438 _thumb_hide_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
439 {
440    Widget_Data *wd = data;
441
442    evas_object_hide(wd->frame);
443
444    if (wd->thumb.id >= 0)
445      {
446         ethumb_client_generate_cancel
447            (_elm_ethumb_client, wd->thumb.id, NULL, NULL, NULL);
448         wd->thumb.id = -1;
449
450         edje_object_signal_emit(wd->frame, EDJE_SIGNAL_GENERATE_STOP, "elm");
451         evas_object_smart_callback_call(wd->self, SIG_GENERATE_STOP, NULL);
452      }
453
454    if (wd->thumb.exists)
455      {
456         ethumb_client_thumb_exists_cancel(wd->thumb.exists);
457         wd->thumb.exists = NULL;
458      }
459
460    if (wd->thumb.idler)
461      {
462         ecore_idle_enterer_del(wd->thumb.idler);
463         wd->thumb.idler = NULL;
464      }
465
466    if (wd->eeh)
467      {
468         ecore_event_handler_del(wd->eeh);
469         wd->eeh = NULL;
470      }
471 }
472
473 #endif
474
475 #ifdef ELM_ETHUMB
476 static int _elm_need_ethumb = 0;
477
478 static void _on_die_cb(void *, Ethumb_Client *);
479
480 static void
481 _connect_cb(void *data __UNUSED__, Ethumb_Client *c, Eina_Bool success)
482 {
483    if (success)
484      {
485         ethumb_client_on_server_die_callback_set(c, _on_die_cb, NULL, NULL);
486         _elm_ethumb_connected = EINA_TRUE;
487         ecore_event_add(ELM_ECORE_EVENT_ETHUMB_CONNECT, NULL, NULL, NULL);
488      }
489    else
490      _elm_ethumb_client = NULL;
491 }
492
493 static void
494 _on_die_cb(void *data __UNUSED__, Ethumb_Client *c __UNUSED__)
495 {
496    ethumb_client_disconnect(_elm_ethumb_client);
497    _elm_ethumb_client = NULL;
498    _elm_ethumb_connected = EINA_FALSE;
499    _elm_ethumb_client = ethumb_client_connect(_connect_cb, NULL, NULL);
500 }
501 #endif
502
503 void
504 _elm_unneed_ethumb(void)
505 {
506 #ifdef ELM_ETHUMB
507    if (--_elm_need_ethumb) return;
508
509    ethumb_client_disconnect(_elm_ethumb_client);
510    _elm_ethumb_client = NULL;
511    ethumb_client_shutdown();
512    ELM_ECORE_EVENT_ETHUMB_CONNECT = 0;
513 #endif
514 }
515
516 static Eina_Bool
517 _elm_thumb_dropcb(void *data __UNUSED__, Evas_Object *o, Elm_Selection_Data *drop)
518 {
519    if ((!o) || (!drop) || (!drop->data)) return EINA_FALSE;
520    elm_thumb_file_set(o, drop->data, NULL);
521    return EINA_TRUE;
522 }
523
524 /**
525  * This must be called before any other function that handle with
526  * elm_thumb objects or ethumb_client instances.
527  *
528  * @ingroup Thumb
529  */
530 EAPI Eina_Bool
531 elm_need_ethumb(void)
532 {
533 #ifdef ELM_ETHUMB
534    if (_elm_need_ethumb++) return EINA_TRUE;
535    ELM_ECORE_EVENT_ETHUMB_CONNECT = ecore_event_type_new();
536    ethumb_client_init();
537    _elm_ethumb_client = ethumb_client_connect(_connect_cb, NULL, NULL);
538    return EINA_TRUE;
539 #else
540    return EINA_FALSE;
541 #endif
542 }
543
544 /**
545  * Add a new thumb object to the parent.
546  *
547  * @param parent The parent object.
548  * @return The new object or NULL if it cannot be created.
549  *
550  * @see elm_thumb_file_set()
551  * @see elm_thumb_ethumb_client_get()
552  *
553  * @ingroup Thumb
554  */
555 EAPI Evas_Object *
556 elm_thumb_add(Evas_Object *parent)
557 {
558    Evas *e;
559    Widget_Data *wd;
560    Evas_Object *obj;
561    Evas_Coord minw, minh;
562
563    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
564
565    ELM_SET_WIDTYPE(widtype, "thumb");
566    elm_widget_type_set(obj, "thumb");
567    elm_widget_sub_object_add(parent, obj);
568    elm_widget_data_set(obj, wd);
569    elm_widget_del_hook_set(obj, _del_hook);
570    elm_widget_theme_hook_set(obj, _theme_hook);
571    elm_widget_can_focus_set(obj, EINA_FALSE);
572
573    wd->frame = edje_object_add(e);
574    _elm_theme_object_set(obj, wd->frame, "thumb", "base", "default");
575    elm_widget_resize_object_set(obj, wd->frame);
576
577    edje_object_size_min_calc(obj, &minw, &minh);
578    evas_object_size_hint_min_set(obj, minw, minh);
579
580    wd->self = obj;
581    wd->view = NULL;
582    wd->file = NULL;
583    wd->key = NULL;
584    wd->eeh = NULL;
585    wd->thumb.id = -1;
586    wd->on_hold = EINA_FALSE;
587    wd->is_video = EINA_FALSE;
588    wd->was_video = EINA_FALSE;
589
590 #ifdef HAVE_ELEMENTARY_ETHUMB
591    wd->thumb.thumb_path = NULL;
592    wd->thumb.thumb_key = NULL;
593    wd->thumb.exists = NULL;
594    wd->thumb.idler = NULL;
595    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN,
596                                   _mouse_down_cb, wd);
597    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP,
598                                   _mouse_up_cb, wd);
599    evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW,
600                                   _thumb_show_cb, wd);
601    evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE,
602                                   _thumb_hide_cb, wd);
603 #endif
604
605    // TODO: convert Elementary to subclassing of Evas_Smart_Class
606    // TODO: and save some bytes, making descriptions per-class and not instance!
607    evas_object_smart_callbacks_descriptions_set(obj, _signals);
608    return obj;
609 }
610
611 /**
612  * Reload thumbnail if it was generated before.
613  *
614  * This is useful if the ethumb client configuration changed, like its
615  * size, aspect or any other property one set in the handle returned
616  * by elm_thumb_ethumb_client_get().
617  *
618  * @param obj The thumb object to reload
619  *
620  * @see elm_thumb_file_set()
621  *
622  * @ingroup Thumb
623  */
624 EAPI void
625 elm_thumb_reload(Evas_Object *obj)
626 {
627    ELM_CHECK_WIDTYPE(obj, widtype);
628    Widget_Data *wd = elm_widget_data_get(obj);
629
630    eina_stringshare_replace(&(wd->thumb.file), NULL);
631    eina_stringshare_replace(&(wd->thumb.key), NULL);
632
633 #ifdef HAVE_ELEMENTARY_ETHUMB
634    if (evas_object_visible_get(obj))
635      _thumb_show(wd);
636 #endif
637 }
638
639 /**
640  * Set the file that will be used as thumbnail.
641  *
642  * The file can be an image or a video (in that case, acceptable extensions are:
643  * avi, mp4, ogv, mov, mpg and wmv). To start the video animation, use the
644  * function elm_thumb_animate().
645  *
646  * @param obj The thumb object.
647  * @param file The path to file that will be used as thumb.
648  * @param key The key used in case of an EET file.
649  *
650  * @see elm_thumb_file_get()
651  * @see elm_thumb_reload()
652  * @see elm_thumb_animate()
653  *
654  * @ingroup Thumb
655  */
656 EAPI void
657 elm_thumb_file_set(Evas_Object *obj, const char *file, const char *key)
658 {
659    ELM_CHECK_WIDTYPE(obj, widtype);
660    Eina_Bool file_replaced, key_replaced;
661    Widget_Data *wd = elm_widget_data_get(obj);
662
663    file_replaced = eina_stringshare_replace(&(wd->file), file);
664    key_replaced = eina_stringshare_replace(&(wd->key), key);
665
666    if (file_replaced)
667      {
668         int prefix_size;
669         const char **ext, *ptr;
670         static const char *extensions[] =
671           {
672              ".avi", ".mp4", ".ogv", ".mov", ".mpg", ".wmv", NULL
673           };
674
675         prefix_size = eina_stringshare_strlen(wd->file) - 4;
676         if (prefix_size >= 0)
677           {
678              ptr = wd->file + prefix_size;
679              wd->is_video = EINA_FALSE;
680              for (ext = extensions; *ext; ext++)
681                if (!strcasecmp(ptr, *ext))
682                  {
683                     wd->is_video = EINA_TRUE;
684                     break;
685                  }
686           }
687      }
688
689    eina_stringshare_replace(&(wd->thumb.file), NULL);
690    eina_stringshare_replace(&(wd->thumb.key), NULL);
691
692 #ifdef HAVE_ELEMENTARY_ETHUMB
693    if (((file_replaced) || (key_replaced)) && (evas_object_visible_get(obj)))
694      _thumb_show(wd);
695 #endif
696 }
697
698 /**
699  * Get the image or video path and key used to generate the thumbnail.
700  *
701  * @param obj The thumb object.
702  * @param file Pointer to filename.
703  * @param key Pointer to key.
704  *
705  * @see elm_thumb_file_set()
706  * @see elm_thumb_path_get()
707  * @see elm_thumb_animate()
708  *
709  * @ingroup Thumb
710  */
711 EAPI void
712 elm_thumb_file_get(const Evas_Object *obj, const char **file, const char **key)
713 {
714    ELM_CHECK_WIDTYPE(obj, widtype);
715    Widget_Data *wd = elm_widget_data_get(obj);
716
717    if (file)
718      *file = wd->file;
719    if (key)
720      *key = wd->key;
721 }
722
723 /**
724  * Get the path and key to the image or video generated by ethumb.
725  *
726  * One just need to make sure that the thumbnail was generated before getting
727  * its path; otherwise, the path will be NULL. One way to do that is by asking
728  * for the path when/after the "generate,stop" smart callback is called.
729  *
730  * @param obj The thumb object.
731  * @param file Pointer to thumb path.
732  * @param key Pointer to thumb key.
733  *
734  * @see elm_thumb_file_get()
735  *
736  * @ingroup Thumb
737  */
738 EAPI void
739 elm_thumb_path_get(const Evas_Object *obj, const char **file, const char **key)
740 {
741    ELM_CHECK_WIDTYPE(obj, widtype);
742    Widget_Data *wd = elm_widget_data_get(obj);
743
744    if (file)
745      *file = wd->thumb.file;
746    if (key)
747      *key = wd->thumb.key;
748 }
749
750 /**
751  * Set the animation state for the thumb object. If its content is an animated
752  * video, you may start/stop the animation or tell it to play continuously and
753  * looping.
754  *
755  * @param obj The thumb object.
756  * @param setting The animation setting.
757  *
758  * @see elm_thumb_file_set()
759  *
760  * @ingroup Thumb
761  */
762 EAPI void
763 elm_thumb_animate_set(Evas_Object *obj, Elm_Thumb_Animation_Setting setting)
764 {
765    ELM_CHECK_WIDTYPE(obj, widtype);
766    Widget_Data *wd = elm_widget_data_get(obj);
767
768    EINA_SAFETY_ON_TRUE_RETURN(setting >= ELM_THUMB_ANIMATION_LAST);
769
770    wd->anim_setting = setting;
771    if (setting == ELM_THUMB_ANIMATION_LOOP)
772      edje_object_signal_emit(wd->view, "animate_loop", "");
773    else if (setting == ELM_THUMB_ANIMATION_START)
774      edje_object_signal_emit(wd->view, "animate", "");
775    else if (setting == ELM_THUMB_ANIMATION_STOP)
776      edje_object_signal_emit(wd->view, "animate_stop", "");
777 }
778
779 /**
780  * Get the animation state for the thumb object.
781  *
782  * @param obj The thumb object.
783  * @return getting The animation setting or @c ELM_THUMB_ANIMATION_LAST,
784  * on errors.
785  *
786  * @see elm_thumb_file_get()
787  *
788  * @ingroup Thumb
789  */
790 EAPI Elm_Thumb_Animation_Setting
791 elm_thumb_animate_get(const Evas_Object *obj)
792 {
793    ELM_CHECK_WIDTYPE(obj, widtype) ELM_THUMB_ANIMATION_LAST;
794    Widget_Data *wd = elm_widget_data_get(obj);
795
796    return wd->anim_setting;
797 }
798
799 /**
800  * Get the ethumb_client handle so custom configuration can be made.
801  * This must be called before the objects are created to be sure no object is
802  * visible and no generation started.
803  *
804  * @return Ethumb_Client instance or NULL.
805  *
806  * Example of usage:
807  *
808  * @code
809  * #include <Elementary.h>
810  * #ifndef ELM_LIB_QUICKLAUNCH
811  * EAPI int
812  * elm_main(int argc, char **argv)
813  * {
814  *    Ethumb_Client *client;
815  *
816  *    elm_need_ethumb();
817  *
818  *    // ... your code
819  *
820  *    client = elm_thumb_ethumb_client_get();
821  *    if (!client)
822  *      {
823  *         ERR("could not get ethumb_client");
824  *         return 1;
825  *      }
826  *    ethumb_client_size_set(client, 100, 100);
827  *    ethumb_client_crop_align_set(client, 0.5, 0.5);
828  *    // ... your code
829  *
830  *    // Create elm_thumb objects here
831  *
832  *    elm_run();
833  *    elm_shutdown();
834  *    return 0;
835  * }
836  * #endif
837  * ELM_MAIN()
838  * @endcode
839  *
840  * @ingroup Thumb
841  */
842 EAPI void *
843 elm_thumb_ethumb_client_get(void)
844 {
845    return _elm_ethumb_client;
846 }
847
848 /**
849  * Get the ethumb_client connection state.
850  *
851  * @return EINA_TRUE if the client is connected to the server or
852  *         EINA_FALSE otherwise.
853  */
854 EAPI Eina_Bool
855 elm_thumb_ethumb_client_connected(void)
856 {
857    return _elm_ethumb_connected;
858 }
859
860 EAPI Eina_Bool
861 elm_thumb_editable_set(Evas_Object *obj, Eina_Bool edit)
862 {
863    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
864    Widget_Data *wd = elm_widget_data_get(obj);
865
866    if (!wd) return EINA_FALSE;
867    edit = !!edit;
868    if (wd->edit == edit) return EINA_TRUE;
869
870    wd->edit = edit;
871    if (wd->edit)
872      elm_drop_target_add(obj, ELM_SEL_FORMAT_IMAGE,
873                          _elm_thumb_dropcb, obj);
874    else
875      elm_drop_target_del(obj);
876
877    return EINA_TRUE;
878 }
879
880 EAPI Eina_Bool
881 elm_thumb_editable_get(const Evas_Object *obj)
882 {
883    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
884    Widget_Data *wd = elm_widget_data_get(obj);
885
886    if (!wd) return EINA_FALSE;
887    return wd->edit;
888 }
889
890 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/