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