1 #include <Elementary.h>
5 * @defgroup Thumb Thumb
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
12 * Signals that you can add callbacks for are:
14 * clicked - This is called when a user has clicked the thumb without dragging
17 * clicked,double - This is called when a user has double-clicked the thumb.
19 * press - This is called when a user has pressed down the thumb.
21 * generate,start - The thumbnail generation started.
23 * generate,stop - The generation process stopped.
25 * generate,error - The generation failed.
27 * load,error - The thumbnail image loading failed.
30 typedef struct _Widget_Data Widget_Data;
49 Ecore_Event_Handler *eeh;
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;
58 static const char *widtype = NULL;
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[] = {
69 {SIG_CLICKED_DOUBLE, ""},
70 {SIG_GENERATE_ERROR, ""},
71 {SIG_GENERATE_START, ""},
72 {SIG_GENERATE_STOP, ""},
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";
85 #ifdef HAVE_ELEMENTARY_ETHUMB
86 Ethumb_Client *_elm_ethumb_client = NULL;
88 Eina_Bool _elm_ethumb_connected = EINA_FALSE;
90 EAPI int ELM_ECORE_EVENT_ETHUMB_CONNECT = 0;
93 _del_hook(Evas_Object *obj)
95 Widget_Data *wd = elm_widget_data_get(obj);
96 eina_stringshare_del(wd->file);
97 eina_stringshare_del(wd->key);
99 ecore_event_handler_del(wd->eeh);
104 _theme_hook(Evas_Object *obj)
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));
110 #ifdef HAVE_ELEMENTARY_ETHUMB
112 _mouse_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
114 Widget_Data *wd = data;
115 Evas_Event_Mouse_Down *ev = event_info;
118 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
119 wd->on_hold = EINA_TRUE;
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);
125 evas_object_smart_callback_call(data, SIG_PRESS, NULL);
129 _mouse_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
132 Widget_Data *wd = data;
133 Evas_Event_Mouse_Up *ev = event_info;
136 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
137 wd->on_hold = EINA_TRUE;
139 wd->on_hold = EINA_FALSE;
141 evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
142 wd->on_hold = EINA_FALSE;
146 _finished_thumb(Widget_Data *wd, int id, const char *thumb_path, const char *thumb_key)
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;
158 ethumb_client_format_get(_elm_ethumb_client) == ETHUMB_THUMB_EET)
160 wd->children.view = edje_object_add(evas);
161 if (!wd->children.view)
163 ERR("could not create edje object");
166 if (!edje_object_file_set(wd->children.view, thumb_path, "movie/thumb"))
168 ERR("could not set file=%s key=%s for %s", thumb_path, thumb_key,
175 wd->children.view = evas_object_image_filled_add(evas);
176 if (!wd->children.view)
178 ERR("could not create image object");
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)
185 ERR("%s: %s", thumb_path, evas_load_error_str(r));
190 elm_widget_sub_object_add(wd->self, wd->children.view);
191 edje_object_part_swallow(wd->children.frm, "elm.swallow.content",
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);
200 evas_object_del(wd->children.view);
201 wd->children.view = NULL;
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);
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)
210 Widget_Data *wd = data;
212 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_PULSE_STOP, "elm");
213 wd->is_generating = EINA_FALSE;
217 _finished_thumb(wd, id, thumb_path, thumb_key);
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);
227 _thumb_apply(Widget_Data *wd)
229 ethumb_client_file_set(_elm_ethumb_client, wd->file, wd->key);
230 if (ethumb_client_thumb_exists(_elm_ethumb_client))
232 const char *thumb_path, *thumb_key;
233 ethumb_client_thumb_path_get(_elm_ethumb_client, &thumb_path,
235 _finished_thumb(wd, 0, thumb_path, thumb_key);
238 else if (ethumb_client_generate(_elm_ethumb_client, _finished_thumb_cb, wd,
241 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_PULSE_START,
243 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_START,
245 evas_object_smart_callback_call(wd->self, SIG_GENERATE_START, NULL);
249 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_ERROR,
251 evas_object_smart_callback_call(wd->self, SIG_GENERATE_ERROR, NULL);
253 wd->is_generating = EINA_FALSE;
257 _thumb_apply_cb(void *data, int type __UNUSED__, void *ev __UNUSED__)
260 return ECORE_CALLBACK_RENEW;
264 _thumb_show(Widget_Data *wd)
266 evas_object_show(wd->children.frm);
268 if (elm_thumb_ethumb_client_connected())
275 wd->eeh = ecore_event_handler_add(ELM_ECORE_EVENT_ETHUMB_CONNECT,
276 _thumb_apply_cb, wd);
280 _thumb_show_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
286 _cancel_cb(void *data, Eina_Bool success)
288 Widget_Data *wd = data;
292 wd->is_generating = EINA_FALSE;
293 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_STOP,
295 evas_object_smart_callback_call(wd->self, SIG_GENERATE_STOP, NULL);
300 _thumb_hide_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
302 Widget_Data *wd = data;
304 evas_object_hide(wd->children.frm);
306 if (wd->is_generating)
307 ethumb_client_generate_cancel(_elm_ethumb_client, wd->id, _cancel_cb, wd, NULL);
310 ecore_event_handler_del(wd->eeh);
318 static Eina_Bool _elm_need_ethumb = EINA_FALSE;
320 static void _on_die_cb(void *, Ethumb_Client *);
323 _connect_cb(void *data __UNUSED__, Ethumb_Client *c, Eina_Bool success)
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);
332 _elm_ethumb_client = NULL;
336 _on_die_cb(void *data __UNUSED__, Ethumb_Client *c __UNUSED__)
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);
346 _elm_unneed_ethumb(void)
349 if (_elm_need_ethumb)
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;
361 * This must be called before any other function that handle with
362 * elm_thumb objects or ethumb_client instances.
367 elm_need_ethumb(void)
370 if (_elm_need_ethumb)
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);
380 * Add a new thumb object to the parent.
382 * @param parent The parent object.
383 * @return The new object or NULL if it cannot be created.
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()
394 elm_thumb_add(Evas_Object *parent)
399 Evas_Coord minw, minh;
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);
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);
415 edje_object_size_min_calc(obj, &minw, &minh);
416 evas_object_size_hint_min_set(obj, minw, minh);
419 wd->children.view = 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;
430 #ifdef HAVE_ELEMENTARY_ETHUMB
431 evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN,
433 evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP,
435 evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW,
437 evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE,
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);
448 * Set the file that will be used as thumbnail.
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().
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.
458 * @see elm_thumb_file_get()
459 * @see elm_thumb_animate()
464 elm_thumb_file_set(Evas_Object *obj, const char *file, const char *key)
466 ELM_CHECK_WIDTYPE(obj, widtype);
467 Eina_Bool file_replaced, key_replaced;
468 Widget_Data *wd = elm_widget_data_get(obj);
470 file_replaced = eina_stringshare_replace(&(wd->file), file);
471 key_replaced = eina_stringshare_replace(&(wd->key), key);
476 const char **ext, *ptr;
477 static const char *extensions[] = { ".avi", ".mp4", ".ogv", ".mov",
478 ".mpg", ".wmv", NULL };
480 prefix_size = eina_stringshare_strlen(wd->file) - 4;
481 if (prefix_size >= 0)
483 ptr = wd->file + prefix_size;
484 wd->is_video = EINA_FALSE;
485 for (ext = extensions; *ext; ext++)
486 if (!strcasecmp(ptr, *ext))
488 wd->is_video = EINA_TRUE;
494 eina_stringshare_replace(&(wd->thumb.file), NULL);
495 eina_stringshare_replace(&(wd->thumb.key), NULL);
497 #ifdef HAVE_ELEMENTARY_ETHUMB
498 if ((file_replaced || key_replaced) && evas_object_visible_get(obj))
504 * Get the image or video path and key used to generate the thumbnail.
506 * @param obj The thumb object.
507 * @param file Pointer to filename.
508 * @param key Pointer to key.
510 * @see elm_thumb_file_set()
511 * @see elm_thumb_path_get()
512 * @see elm_thumb_animate()
517 elm_thumb_file_get(const Evas_Object *obj, const char **file, const char **key)
519 ELM_CHECK_WIDTYPE(obj, widtype);
520 Widget_Data *wd = elm_widget_data_get(obj);
528 * Get the path and key to the image or video generated by ethumb.
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.
534 * @param obj The thumb object.
535 * @param file Pointer to thumb path.
536 * @param key Pointer to thumb key.
538 * @see elm_thumb_file_get()
543 elm_thumb_path_get(const Evas_Object *obj, const char **file, const char **key)
545 ELM_CHECK_WIDTYPE(obj, widtype);
546 Widget_Data *wd = elm_widget_data_get(obj);
548 *file = wd->thumb.file;
550 *key = wd->thumb.key;
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
558 * @param obj The thumb object.
559 * @param setting The animation setting.
561 * @see elm_thumb_file_set()
566 elm_thumb_animate_set(Evas_Object *obj, Elm_Thumb_Animation_Setting setting)
568 ELM_CHECK_WIDTYPE(obj, widtype);
569 Widget_Data *wd = elm_widget_data_get(obj);
571 if (setting < ELM_THUMB_ANIMATION_START ||
572 setting >= ELM_THUMB_ANIMATION_LAST)
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", "");
587 * Get the animation state for the thumb object.
589 * @param obj The thumb object.
590 * @return getting The animation setting or @c ELM_THUMB_ANIMATION_LAST,
593 * @see elm_thumb_file_get()
597 EAPI Elm_Thumb_Animation_Setting
598 elm_thumb_animate_get(const Evas_Object *obj)
600 ELM_CHECK_WIDTYPE(obj, widtype) ELM_THUMB_ANIMATION_LAST;
601 Widget_Data *wd = elm_widget_data_get(obj);
603 return wd->anim_setting;
607 * Set whether the thumbnail exhibition should keep the image or video aspect.
609 * For positioning the image/video within the object use the function
610 * elm_thumb_align_set().
612 * @param obj The thumb object.
613 * @param setting Whether keep or not the aspect.
615 * @see elm_thumb_file_set()
616 * @see elm_thumb_align_set()
621 elm_thumb_keep_aspect_set(Evas_Object *obj, Eina_Bool setting)
623 ELM_CHECK_WIDTYPE(obj, widtype);
624 Widget_Data *wd = elm_widget_data_get(obj);
625 wd->keep_aspect = setting;
629 * Get back the aspect info set with @c elm_thumb_keep_aspect_set().
631 * @param obj The thumb object.
632 * @return Whether the thumb object keeps or not the aspect for its content.
634 * @see elm_thumb_file_get()
635 * @see elm_thumb_align_get()
640 elm_thumb_keep_aspect_get(const Evas_Object *obj)
642 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
643 Widget_Data *wd = elm_widget_data_get(obj);
644 return wd->keep_aspect;
648 * Set image/video's alignment within the thumbnail.
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).
654 * @see elm_thumb_keep_aspect_set()
655 * @see elm_thumb_align_get()
660 elm_thumb_align_set(Evas_Object *obj, float x_align, float y_align)
662 ELM_CHECK_WIDTYPE(obj, widtype);
663 Widget_Data *wd = elm_widget_data_get(obj);
667 else if (x_align < 0.0)
669 wd->children.align.x = x_align;
673 else if (y_align < 0.0)
675 wd->children.align.y = y_align;
679 * Get the alignenment set for the thumb object.
681 * @param obj The thumb object.
682 * @param x Pointer to x alignenment.
683 * @param y Pointer to y alignenment.
685 * @see elm_thumb_align_set()
690 elm_thumb_align_get(const Evas_Object *obj, float *x, float *y)
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;
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.
703 * @return Ethumb_Client instance or NULL.
708 * #include <Elementary.h>
709 * #ifndef ELM_LIB_QUICKLAUNCH
711 * elm_main(int argc, char **argv)
713 * Ethumb_Client *client;
719 * client = elm_thumb_ethumb_client_get();
722 * ERR("could not get ethumb_client");
725 * ethumb_client_size_set(client, 100, 100);
726 * ethumb_client_crop_align_set(client, 0.5, 0.5);
729 * // Create elm_thumb objects here
743 elm_thumb_ethumb_client_get(void)
745 return _elm_ethumb_client;
749 elm_thumb_ethumb_client_get(void)
756 * Get the ethumb_client connection state.
758 * @return EINA_TRUE if the client is connected to the server or
759 * EINA_FALSE otherwise.
762 elm_thumb_ethumb_client_connected(void)
764 return _elm_ethumb_connected;