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