1 #include <Elementary.h>
5 * @defgroup Thumb Thumb
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
13 * Signals that you can add callbacks for are:
15 * clicked - This is called when a user has clicked the thumb without dragging
18 * clicked,double - This is called when a user has double-clicked the thumb.
20 * press - This is called when a user has pressed down the thumb.
22 * generate,start - The thumbnail generation started.
24 * generate,stop - The generation process stopped.
26 * generate,error - The generation failed.
28 * load,error - The thumbnail image loading failed.
31 typedef struct _Widget_Data Widget_Data;
50 Ecore_Event_Handler *eeh;
52 Elm_Thumb_Animation_Setting anim_setting;
53 Eina_Bool on_hold : 1;
54 Eina_Bool is_video : 1;
55 Eina_Bool is_generating : 1;
56 Eina_Bool keep_aspect : 1;
59 static const char *widtype = NULL;
61 static const char SIG_CLICKED[] = "clicked";
62 static const char SIG_CLICKED_DOUBLE[] = "clicked,double";
63 static const char SIG_GENERATE_ERROR[] = "generate,error";
64 static const char SIG_GENERATE_START[] = "generate,start";
65 static const char SIG_GENERATE_STOP[] = "generate,stop";
66 static const char SIG_LOAD_ERROR[] = "load,error";
67 static const char SIG_PRESS[]= "press";
68 static const Evas_Smart_Cb_Description _signals[] = {
70 {SIG_CLICKED_DOUBLE, ""},
71 {SIG_GENERATE_ERROR, ""},
72 {SIG_GENERATE_START, ""},
73 {SIG_GENERATE_STOP, ""},
79 static const char EDJE_SIGNAL_GENERATE_START[] = "elm,thumb,generate,start";
80 static const char EDJE_SIGNAL_GENERATE_STOP[] = "elm,thumb,generate,stop";
81 static const char EDJE_SIGNAL_GENERATE_ERROR[] = "elm,thumb,generate,error";
82 static const char EDJE_SIGNAL_LOAD_ERROR[] = "elm,thumb,load,error";
83 static const char EDJE_SIGNAL_PULSE_START[] = "elm,state,pulse,start";
84 static const char EDJE_SIGNAL_PULSE_STOP[] = "elm,state,pulse,stop";
86 #ifdef HAVE_ELEMENTARY_ETHUMB
87 Ethumb_Client *_elm_ethumb_client = NULL;
89 Eina_Bool _elm_ethumb_connected = EINA_FALSE;
91 EAPI int ELM_ECORE_EVENT_ETHUMB_CONNECT = 0;
94 _del_hook(Evas_Object *obj)
96 Widget_Data *wd = elm_widget_data_get(obj);
97 eina_stringshare_del(wd->file);
98 eina_stringshare_del(wd->key);
100 ecore_event_handler_del(wd->eeh);
105 _theme_hook(Evas_Object *obj)
107 Widget_Data *wd = elm_widget_data_get(obj);
108 _elm_theme_object_set(obj, wd->children.frm, "thumb", "base", elm_widget_style_get(obj));
111 #ifdef HAVE_ELEMENTARY_ETHUMB
113 _mouse_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
115 Widget_Data *wd = data;
116 Evas_Event_Mouse_Down *ev = event_info;
119 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
120 wd->on_hold = EINA_TRUE;
122 wd->on_hold = EINA_FALSE;
123 if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
124 evas_object_smart_callback_call(data, SIG_CLICKED_DOUBLE, NULL);
126 evas_object_smart_callback_call(data, SIG_PRESS, NULL);
130 _mouse_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
133 Widget_Data *wd = data;
134 Evas_Event_Mouse_Up *ev = event_info;
137 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
138 wd->on_hold = EINA_TRUE;
140 wd->on_hold = EINA_FALSE;
142 evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
143 wd->on_hold = EINA_FALSE;
147 _finished_thumb(Widget_Data *wd, int id, const char *thumb_path, const char *thumb_key)
152 evas = evas_object_evas_get(wd->self);
153 if (wd->children.view)
154 evas_object_del(wd->children.view);
155 wd->children.view = NULL;
159 ethumb_client_format_get(_elm_ethumb_client) == ETHUMB_THUMB_EET)
161 wd->children.view = edje_object_add(evas);
162 if (!wd->children.view)
164 ERR("could not create edje object");
167 if (!edje_object_file_set(wd->children.view, thumb_path, "movie/thumb"))
169 ERR("could not set file=%s key=%s for %s", thumb_path, thumb_key,
176 wd->children.view = evas_object_image_filled_add(evas);
177 if (!wd->children.view)
179 ERR("could not create image object");
182 evas_object_image_file_set(wd->children.view, thumb_path, thumb_key);
183 r = evas_object_image_load_error_get(wd->children.view);
184 if (r != EVAS_LOAD_ERROR_NONE)
186 ERR("%s: %s", thumb_path, evas_load_error_str(r));
191 elm_widget_sub_object_add(wd->self, wd->children.view);
192 edje_object_part_swallow(wd->children.frm, "elm.swallow.content",
194 eina_stringshare_replace(&(wd->thumb.file), thumb_path);
195 eina_stringshare_replace(&(wd->thumb.key), thumb_key);
196 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_STOP, "elm");
197 evas_object_smart_callback_call(wd->self, SIG_GENERATE_STOP, NULL);
201 evas_object_del(wd->children.view);
202 wd->children.view = NULL;
204 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_LOAD_ERROR, "elm");
205 evas_object_smart_callback_call(wd->self, SIG_LOAD_ERROR, NULL);
209 _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)
211 Widget_Data *wd = data;
213 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_PULSE_STOP, "elm");
214 wd->is_generating = EINA_FALSE;
218 _finished_thumb(wd, id, thumb_path, thumb_key);
222 ERR("could not generate thumbnail for %s (key: %s)", file, key ? key : "");
223 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_ERROR, "elm");
224 evas_object_smart_callback_call(wd->self, SIG_GENERATE_ERROR, NULL);
228 _thumb_apply(Widget_Data *wd)
230 ethumb_client_file_set(_elm_ethumb_client, wd->file, wd->key);
231 if (ethumb_client_thumb_exists(_elm_ethumb_client))
233 const char *thumb_path, *thumb_key;
234 ethumb_client_thumb_path_get(_elm_ethumb_client, &thumb_path,
236 _finished_thumb(wd, 0, thumb_path, thumb_key);
239 else if (ethumb_client_generate(_elm_ethumb_client, _finished_thumb_cb, wd,
242 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_PULSE_START,
244 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_START,
246 evas_object_smart_callback_call(wd->self, SIG_GENERATE_START, NULL);
250 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_ERROR,
252 evas_object_smart_callback_call(wd->self, SIG_GENERATE_ERROR, NULL);
254 wd->is_generating = EINA_FALSE;
258 _thumb_apply_cb(void *data, int type __UNUSED__, void *ev __UNUSED__)
261 return ECORE_CALLBACK_RENEW;
265 _thumb_show(Widget_Data *wd)
267 evas_object_show(wd->children.frm);
269 if (elm_thumb_ethumb_client_connected())
276 wd->eeh = ecore_event_handler_add(ELM_ECORE_EVENT_ETHUMB_CONNECT,
277 _thumb_apply_cb, wd);
281 _thumb_show_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
287 _cancel_cb(void *data, Eina_Bool success)
289 Widget_Data *wd = data;
293 wd->is_generating = EINA_FALSE;
294 edje_object_signal_emit(wd->children.frm, EDJE_SIGNAL_GENERATE_STOP,
296 evas_object_smart_callback_call(wd->self, SIG_GENERATE_STOP, NULL);
301 _thumb_hide_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
303 Widget_Data *wd = data;
305 evas_object_hide(wd->children.frm);
307 if (wd->is_generating)
308 ethumb_client_generate_cancel(_elm_ethumb_client, wd->id, _cancel_cb, wd, NULL);
311 ecore_event_handler_del(wd->eeh);
319 static Eina_Bool _elm_need_ethumb = EINA_FALSE;
321 static void _on_die_cb(void *, Ethumb_Client *);
324 _connect_cb(void *data __UNUSED__, Ethumb_Client *c, Eina_Bool success)
328 ethumb_client_on_server_die_callback_set(c, _on_die_cb, NULL, NULL);
329 _elm_ethumb_connected = EINA_TRUE;
330 ecore_event_add(ELM_ECORE_EVENT_ETHUMB_CONNECT, NULL, NULL, NULL);
333 _elm_ethumb_client = NULL;
337 _on_die_cb(void *data __UNUSED__, Ethumb_Client *c __UNUSED__)
339 ethumb_client_disconnect(_elm_ethumb_client);
340 _elm_ethumb_client = NULL;
341 _elm_ethumb_connected = EINA_FALSE;
342 _elm_ethumb_client = ethumb_client_connect(_connect_cb, NULL, NULL);
347 _elm_unneed_ethumb(void)
350 if (_elm_need_ethumb)
352 _elm_need_ethumb = 0;
353 ethumb_client_disconnect(_elm_ethumb_client);
354 _elm_ethumb_client = NULL;
355 ethumb_client_shutdown();
356 ELM_ECORE_EVENT_ETHUMB_CONNECT = 0;
362 * This must be called before any other function that handle with
363 * elm_thumb objects or ethumb_client instances.
368 elm_need_ethumb(void)
371 if (_elm_need_ethumb)
373 ELM_ECORE_EVENT_ETHUMB_CONNECT = ecore_event_type_new();
374 _elm_need_ethumb = 1;
375 ethumb_client_init();
376 _elm_ethumb_client = ethumb_client_connect(_connect_cb, NULL, NULL);
381 * Add a new thumb object to the parent.
383 * @param parent The parent object.
384 * @return The new object or NULL if it cannot be created.
386 * @see elm_thumb_file_set()
387 * @see elm_thumb_ethumb_client_get()
388 * @see elm_thumb_keep_aspect_set()
389 * @see elm_thumb_align_set()
390 * @see elm_thumb_align_get()
395 elm_thumb_add(Evas_Object *parent)
400 Evas_Coord minw, minh;
402 wd = ELM_NEW(Widget_Data);
403 evas = evas_object_evas_get(parent);
404 obj = elm_widget_add(evas);
405 ELM_SET_WIDTYPE(widtype, "thumb");
406 elm_widget_type_set(obj, "thumb");
407 elm_widget_sub_object_add(parent, obj);
408 elm_widget_data_set(obj, wd);
409 elm_widget_del_hook_set(obj, _del_hook);
410 elm_widget_theme_hook_set(obj, _theme_hook);
412 wd->children.frm = edje_object_add(evas);
413 _elm_theme_object_set(obj, wd->children.frm, "thumb", "base", "default");
414 elm_widget_resize_object_set(obj, wd->children.frm);
416 edje_object_size_min_calc(obj, &minw, &minh);
417 evas_object_size_hint_min_set(obj, minw, minh);
420 wd->children.view = NULL;
424 wd->children.align.x = 0.5;
425 wd->children.align.y = 0.5;
426 wd->on_hold = EINA_FALSE;
427 wd->is_video = EINA_FALSE;
428 wd->is_generating = EINA_FALSE;
429 wd->keep_aspect = EINA_FALSE;
431 #ifdef HAVE_ELEMENTARY_ETHUMB
432 evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN,
434 evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP,
436 evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW,
438 evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE,
442 // TODO: convert Elementary to subclassing of Evas_Smart_Class
443 // TODO: and save some bytes, making descriptions per-class and not instance!
444 evas_object_smart_callbacks_descriptions_set(obj, _signals);
449 * Set the file that will be used as thumbnail.
451 * The file can be an image or a video (in that case, acceptable extensions are:
452 * avi, mp4, ogv, mov, mpg and wmv). To start the video animation, use the
453 * function elm_thumb_animate().
455 * @param obj The thumb object.
456 * @param file The path to file that will be used as thumb.
457 * @param key The key used in case of an EET file.
459 * @see elm_thumb_file_get()
460 * @see elm_thumb_animate()
465 elm_thumb_file_set(Evas_Object *obj, const char *file, const char *key)
467 ELM_CHECK_WIDTYPE(obj, widtype);
468 Eina_Bool file_replaced, key_replaced;
469 Widget_Data *wd = elm_widget_data_get(obj);
471 file_replaced = eina_stringshare_replace(&(wd->file), file);
472 key_replaced = eina_stringshare_replace(&(wd->key), key);
477 const char **ext, *ptr;
478 static const char *extensions[] = { ".avi", ".mp4", ".ogv", ".mov",
479 ".mpg", ".wmv", NULL };
481 prefix_size = eina_stringshare_strlen(wd->file) - 4;
482 if (prefix_size >= 0)
484 ptr = wd->file + prefix_size;
485 wd->is_video = EINA_FALSE;
486 for (ext = extensions; *ext; ext++)
487 if (!strcasecmp(ptr, *ext))
489 wd->is_video = EINA_TRUE;
495 eina_stringshare_replace(&(wd->thumb.file), NULL);
496 eina_stringshare_replace(&(wd->thumb.key), NULL);
498 #ifdef HAVE_ELEMENTARY_ETHUMB
499 if ((file_replaced || key_replaced) && evas_object_visible_get(obj))
505 * Get the image or video path and key used to generate the thumbnail.
507 * @param obj The thumb object.
508 * @param file Pointer to filename.
509 * @param key Pointer to key.
511 * @see elm_thumb_file_set()
512 * @see elm_thumb_path_get()
513 * @see elm_thumb_animate()
518 elm_thumb_file_get(const Evas_Object *obj, const char **file, const char **key)
520 ELM_CHECK_WIDTYPE(obj, widtype);
521 Widget_Data *wd = elm_widget_data_get(obj);
529 * Get the path and key to the image or video generated by ethumb.
531 * One just need to make sure that the thumbnail was generated before getting
532 * its path; otherwise, the path will be NULL. One way to do that is by asking
533 * for the path when/after the "generate,stop" smart callback is called.
535 * @param obj The thumb object.
536 * @param file Pointer to thumb path.
537 * @param key Pointer to thumb key.
539 * @see elm_thumb_file_get()
544 elm_thumb_path_get(const Evas_Object *obj, const char **file, const char **key)
546 ELM_CHECK_WIDTYPE(obj, widtype);
547 Widget_Data *wd = elm_widget_data_get(obj);
549 *file = wd->thumb.file;
551 *key = wd->thumb.key;
555 * Set the animation state for the thumb object. If its content is an animated
556 * video, you may start/stop the animation or tell it to play continuously and
559 * @param obj The thumb object.
560 * @param setting The animation setting.
562 * @see elm_thumb_file_set()
567 elm_thumb_animate_set(Evas_Object *obj, Elm_Thumb_Animation_Setting setting)
569 ELM_CHECK_WIDTYPE(obj, widtype);
570 Widget_Data *wd = elm_widget_data_get(obj);
572 if (setting < ELM_THUMB_ANIMATION_START ||
573 setting >= ELM_THUMB_ANIMATION_LAST)
578 wd->anim_setting = setting;
579 if (setting == ELM_THUMB_ANIMATION_LOOP)
580 edje_object_signal_emit(wd->children.view, "animate_loop", "");
581 else if (setting == ELM_THUMB_ANIMATION_START)
582 edje_object_signal_emit(wd->children.view, "animate", "");
583 else if (setting == ELM_THUMB_ANIMATION_STOP)
584 edje_object_signal_emit(wd->children.view, "animate_stop", "");
588 * Get the animation state for the thumb object.
590 * @param obj The thumb object.
591 * @return getting The animation setting or @c ELM_THUMB_ANIMATION_LAST,
594 * @see elm_thumb_file_get()
598 EAPI Elm_Thumb_Animation_Setting
599 elm_thumb_animate_get(const Evas_Object *obj)
601 ELM_CHECK_WIDTYPE(obj, widtype) ELM_THUMB_ANIMATION_LAST;
602 Widget_Data *wd = elm_widget_data_get(obj);
604 return wd->anim_setting;
608 * Set whether the thumbnail exhibition should keep the image or video aspect.
610 * For positioning the image/video within the object use the function
611 * elm_thumb_align_set().
613 * @param obj The thumb object.
614 * @param setting Whether keep or not the aspect.
616 * @see elm_thumb_file_set()
617 * @see elm_thumb_align_set()
622 elm_thumb_keep_aspect_set(Evas_Object *obj, Eina_Bool setting)
624 ELM_CHECK_WIDTYPE(obj, widtype);
625 Widget_Data *wd = elm_widget_data_get(obj);
626 wd->keep_aspect = setting;
630 * Get back the aspect info set with @c elm_thumb_keep_aspect_set().
632 * @param obj The thumb object.
633 * @return Whether the thumb object keeps or not the aspect for its content.
635 * @see elm_thumb_file_get()
636 * @see elm_thumb_align_get()
641 elm_thumb_keep_aspect_get(const Evas_Object *obj)
643 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
644 Widget_Data *wd = elm_widget_data_get(obj);
645 return wd->keep_aspect;
649 * Set image/video's alignment within the thumbnail.
651 * @param obj The thumb object.
652 * @param x_align The x alignment (0 <= x <= 1).
653 * @param y_align The y alignment (0 <= y <= 1).
655 * @see elm_thumb_keep_aspect_set()
656 * @see elm_thumb_align_get()
661 elm_thumb_align_set(Evas_Object *obj, float x_align, float y_align)
663 ELM_CHECK_WIDTYPE(obj, widtype);
664 Widget_Data *wd = elm_widget_data_get(obj);
668 else if (x_align < 0.0)
670 wd->children.align.x = x_align;
674 else if (y_align < 0.0)
676 wd->children.align.y = y_align;
680 * Get the alignenment set for the thumb object.
682 * @param obj The thumb object.
683 * @param x Pointer to x alignenment.
684 * @param y Pointer to y alignenment.
686 * @see elm_thumb_align_set()
691 elm_thumb_align_get(const Evas_Object *obj, float *x, float *y)
693 ELM_CHECK_WIDTYPE(obj, widtype);
694 Widget_Data *wd = elm_widget_data_get(obj);
695 if (x) *x = wd->children.align.x;
696 if (y) *y = wd->children.align.y;
700 * Get the ethumb_client handle so custom configuration can be made.
701 * This must be called before the objects are created to be sure no object is
702 * visible and no generation started.
704 * @return Ethumb_Client instance or NULL.
709 * #include <Elementary.h>
710 * #ifndef ELM_LIB_QUICKLAUNCH
712 * elm_main(int argc, char **argv)
714 * Ethumb_Client *client;
720 * client = elm_thumb_ethumb_client_get();
723 * ERR("could not get ethumb_client");
726 * ethumb_client_size_set(client, 100, 100);
727 * ethumb_client_crop_align_set(client, 0.5, 0.5);
730 * // Create elm_thumb objects here
744 elm_thumb_ethumb_client_get(void)
746 return _elm_ethumb_client;
750 elm_thumb_ethumb_client_get(void)
757 * Get the ethumb_client connection state.
759 * @return EINA_TRUE if the client is connected to the server or
760 * EINA_FALSE otherwise.
763 elm_thumb_ethumb_client_connected(void)
765 return _elm_ethumb_connected;