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