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