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