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