1 #include <Elementary.h>
3 #include "elm_widget_thumb.h"
5 EAPI const char ELM_THUMB_SMART_NAME[] = "elm_thumb";
7 static const char SIG_CLICKED[] = "clicked";
8 static const char SIG_CLICKED_DOUBLE[] = "clicked,double";
9 static const char SIG_GENERATE_ERROR[] = "generate,error";
10 static const char SIG_GENERATE_START[] = "generate,start";
11 static const char SIG_GENERATE_STOP[] = "generate,stop";
12 static const char SIG_LOAD_ERROR[] = "load,error";
13 static const char SIG_PRESS[] = "press";
14 static const Evas_Smart_Cb_Description _smart_callbacks[] =
17 {SIG_CLICKED_DOUBLE, ""},
18 {SIG_GENERATE_ERROR, ""},
19 {SIG_GENERATE_START, ""},
20 {SIG_GENERATE_STOP, ""},
26 #define EDJE_SIGNAL_GENERATE_START "elm,thumb,generate,start"
27 #define EDJE_SIGNAL_GENERATE_STOP "elm,thumb,generate,stop"
28 #define EDJE_SIGNAL_GENERATE_ERROR "elm,thumb,generate,error"
29 #define EDJE_SIGNAL_LOAD_ERROR "elm,thumb,load,error"
30 #define EDJE_SIGNAL_PULSE_START "elm,state,pulse,start"
31 #define EDJE_SIGNAL_PULSE_STOP "elm,state,pulse,stop"
33 static struct _Ethumb_Client *_elm_ethumb_client = NULL;
34 static Eina_Bool _elm_ethumb_connected = EINA_FALSE;
36 #ifdef HAVE_ELEMENTARY_ETHUMB
37 static Eina_List *retry = NULL;
38 static int pending_request = 0;
41 EAPI int ELM_ECORE_EVENT_ETHUMB_CONNECT = 0;
43 EVAS_SMART_SUBCLASS_NEW
44 (ELM_THUMB_SMART_NAME, _elm_thumb, Elm_Thumb_Smart_Class,
45 Elm_Widget_Smart_Class, elm_widget_smart_class_get, _smart_callbacks);
47 #ifdef HAVE_ELEMENTARY_ETHUMB
49 _mouse_down_cb(void *data,
54 Elm_Thumb_Smart_Data *sd = data;
55 Evas_Event_Mouse_Down *ev = event_info;
59 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
60 sd->on_hold = EINA_TRUE;
62 sd->on_hold = EINA_FALSE;
63 if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
64 evas_object_smart_callback_call(obj, SIG_CLICKED_DOUBLE, NULL);
66 evas_object_smart_callback_call(obj, SIG_PRESS, NULL);
70 _mouse_up_cb(void *data,
75 Elm_Thumb_Smart_Data *sd = data;
76 Evas_Event_Mouse_Up *ev = event_info;
80 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
81 sd->on_hold = EINA_TRUE;
83 sd->on_hold = EINA_FALSE;
85 evas_object_smart_callback_call(obj, SIG_CLICKED, NULL);
87 sd->on_hold = EINA_FALSE;
91 _thumb_ready_inform(Elm_Thumb_Smart_Data *sd,
92 const char *thumb_path,
93 const char *thumb_key)
98 evas_object_image_size_get(sd->view, &aw, &ah);
99 evas_object_size_hint_aspect_set
100 (sd->view, EVAS_ASPECT_CONTROL_BOTH, aw, ah);
101 elm_layout_content_set
102 (ELM_WIDGET_DATA(sd)->resize_obj, "elm.swallow.content", sd->view);
103 edje_object_size_min_get(ELM_WIDGET_DATA(sd)->resize_obj, &mw, &mh);
104 edje_object_size_min_restricted_calc
105 (ELM_WIDGET_DATA(sd)->resize_obj, &mw, &mh, mw, mh);
106 evas_object_size_hint_min_set(ELM_WIDGET_DATA(sd)->obj, mw, mh);
107 eina_stringshare_replace(&(sd->thumb.file), thumb_path);
108 eina_stringshare_replace(&(sd->thumb.key), thumb_key);
109 elm_layout_signal_emit
110 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_PULSE_STOP, "elm");
111 elm_layout_signal_emit
112 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_GENERATE_STOP, "elm");
113 evas_object_smart_callback_call
114 (ELM_WIDGET_DATA(sd)->obj, SIG_GENERATE_STOP, NULL);
118 _on_thumb_preloaded(void *data,
120 Evas_Object *obj __UNUSED__,
121 void *event_info __UNUSED__)
123 Elm_Thumb_Smart_Data *sd = data;
124 const char *thumb_path;
125 const char *thumb_key;
127 evas_object_image_file_get(sd->view, &thumb_path, &thumb_key);
129 _thumb_ready_inform(sd, thumb_path, thumb_key);
132 /* As we do use stat to check if a thumbnail is available, it's
133 * possible that we end up accessing it before the file is completly
134 * written on disk. By retrying each time a thumbnail is finished we
135 * should be fine or not.
138 _thumb_retry(Elm_Thumb_Smart_Data *sd)
142 if ((sd->is_video) && (sd->thumb.format == ETHUMB_THUMB_EET))
144 edje_object_file_set(sd->view, NULL, NULL);
145 if (!edje_object_file_set
146 (sd->view, sd->thumb.thumb_path, "movie/thumb"))
148 if (pending_request == 0)
149 ERR("could not set file=%s key=%s for %s",
150 sd->thumb.thumb_path,
155 /* FIXME: Do we want to reload a thumbnail when the file
156 changes? I think not at the moment, as this may increase
157 the presure on the system a lot when using it in a file
162 evas_object_image_file_set(sd->view, NULL, NULL);
163 evas_object_image_file_set
164 (sd->view, sd->thumb.thumb_path, sd->thumb.thumb_key);
165 r = evas_object_image_load_error_get(sd->view);
166 if (r != EVAS_LOAD_ERROR_NONE)
168 if (pending_request == 0)
169 ERR("%s: %s", sd->thumb.thumb_path, evas_load_error_str(r));
173 evas_object_event_callback_add
174 (sd->view, EVAS_CALLBACK_IMAGE_PRELOADED, _on_thumb_preloaded, sd);
175 evas_object_image_preload(sd->view, EINA_TRUE);
180 _thumb_ready_inform(sd, sd->thumb.thumb_path, sd->thumb.thumb_key);
182 eina_stringshare_del(sd->thumb.thumb_path);
183 sd->thumb.thumb_path = NULL;
185 eina_stringshare_del(sd->thumb.thumb_key);
186 sd->thumb.thumb_key = NULL;
195 _thumb_finish(Elm_Thumb_Smart_Data *sd,
196 const char *thumb_path,
197 const char *thumb_key)
203 evas = evas_object_evas_get(ELM_WIDGET_DATA(sd)->obj);
204 if ((sd->view) && (sd->is_video ^ sd->was_video))
206 evas_object_del(sd->view);
209 sd->was_video = sd->is_video;
211 if ((sd->is_video) &&
212 (ethumb_client_format_get(_elm_ethumb_client) == ETHUMB_THUMB_EET))
216 sd->view = edje_object_add(evas);
219 ERR("could not create edje object");
224 if (!edje_object_file_set(sd->view, thumb_path, thumb_key))
226 sd->thumb.thumb_path = eina_stringshare_ref(thumb_path);
227 sd->thumb.thumb_key = eina_stringshare_ref(thumb_key);
228 sd->thumb.format = ethumb_client_format_get(_elm_ethumb_client);
229 sd->thumb.retry = EINA_TRUE;
231 retry = eina_list_append(retry, sd);
239 sd->view = evas_object_image_filled_add(evas);
242 ERR("could not create image object");
245 evas_object_event_callback_add
246 (sd->view, EVAS_CALLBACK_IMAGE_PRELOADED, _on_thumb_preloaded,
248 evas_object_hide(sd->view);
251 evas_object_image_file_set(sd->view, thumb_path, thumb_key);
252 r = evas_object_image_load_error_get(sd->view);
253 if (r != EVAS_LOAD_ERROR_NONE)
255 WRN("%s: %s", thumb_path, evas_load_error_str(r));
256 sd->thumb.thumb_path = eina_stringshare_ref(thumb_path);
257 sd->thumb.thumb_key = eina_stringshare_ref(thumb_key);
258 sd->thumb.format = ethumb_client_format_get(_elm_ethumb_client);
259 sd->thumb.retry = EINA_TRUE;
261 retry = eina_list_append(retry, sd);
265 evas_object_image_preload(sd->view, EINA_FALSE);
269 _thumb_ready_inform(sd, thumb_path, thumb_key);
271 EINA_LIST_FOREACH_SAFE(retry, l, ll, sd)
273 if (_thumb_retry(sd))
274 retry = eina_list_remove_list(retry, l);
277 if (pending_request == 0)
278 EINA_LIST_FREE (retry, sd)
280 eina_stringshare_del(sd->thumb.thumb_path);
281 sd->thumb.thumb_path = NULL;
283 eina_stringshare_del(sd->thumb.thumb_key);
284 sd->thumb.thumb_key = NULL;
286 evas_object_del(sd->view);
289 elm_layout_signal_emit
290 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_LOAD_ERROR, "elm");
291 evas_object_smart_callback_call
292 (ELM_WIDGET_DATA(sd)->obj, SIG_LOAD_ERROR, NULL);
298 elm_layout_signal_emit
299 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_LOAD_ERROR, "elm");
300 evas_object_smart_callback_call
301 (ELM_WIDGET_DATA(sd)->obj, SIG_LOAD_ERROR, NULL);
305 _on_ethumb_thumb_done(Ethumb_Client *client __UNUSED__,
306 const char *thumb_path,
307 const char *thumb_key,
310 Elm_Thumb_Smart_Data *sd = data;
312 if (EINA_UNLIKELY(!sd->thumb.request))
314 ERR("Something odd happened with a thumbnail request");
319 sd->thumb.request = NULL;
321 _thumb_finish(sd, thumb_path, thumb_key);
325 _on_ethumb_thumb_error(Ethumb_Client *client __UNUSED__,
328 Elm_Thumb_Smart_Data *sd = data;
330 if (EINA_UNLIKELY(!sd->thumb.request))
332 ERR("Something odd happened with a thumbnail request");
337 sd->thumb.request = NULL;
339 ERR("could not generate thumbnail for %s (key: %s)",
340 sd->thumb.file, sd->thumb.key ? sd->thumb.key : "");
342 elm_layout_signal_emit
343 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_GENERATE_ERROR, "elm");
344 evas_object_smart_callback_call
345 (ELM_WIDGET_DATA(sd)->obj, SIG_GENERATE_ERROR, NULL);
349 _thumb_start(Elm_Thumb_Smart_Data *sd)
351 if (sd->thumb.request)
353 ethumb_client_thumb_async_cancel(_elm_ethumb_client, sd->thumb.request);
354 sd->thumb.request = NULL;
358 retry = eina_list_remove(retry, sd);
359 sd->thumb.retry = EINA_FALSE;
362 if (!sd->file) return;
364 elm_layout_signal_emit
365 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_PULSE_START, "elm");
366 elm_layout_signal_emit
367 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_GENERATE_START, "elm");
368 evas_object_smart_callback_call
369 (ELM_WIDGET_DATA(sd)->obj, SIG_GENERATE_START, NULL);
372 ethumb_client_file_set(_elm_ethumb_client, sd->file, sd->key);
373 sd->thumb.request = ethumb_client_thumb_async_get
374 (_elm_ethumb_client, _on_ethumb_thumb_done, _on_ethumb_thumb_error, sd);
378 _thumbnailing_available_cb(void *data,
384 return ECORE_CALLBACK_RENEW;
388 _thumb_show(Elm_Thumb_Smart_Data *sd)
390 evas_object_show(ELM_WIDGET_DATA(sd)->resize_obj);
392 if (elm_thumb_ethumb_client_connected_get())
399 sd->eeh = ecore_event_handler_add
400 (ELM_ECORE_EVENT_ETHUMB_CONNECT, _thumbnailing_available_cb, sd);
406 _elm_thumb_smart_show(Evas_Object *obj)
408 ELM_THUMB_DATA_GET(obj, sd);
410 ELM_WIDGET_CLASS(_elm_thumb_parent_sc)->base.show(obj);
420 _elm_thumb_smart_hide(Evas_Object *obj)
422 ELM_THUMB_DATA_GET(obj, sd);
424 ELM_WIDGET_CLASS(_elm_thumb_parent_sc)->base.hide(obj);
426 evas_object_hide(ELM_WIDGET_DATA(sd)->resize_obj);
429 if (sd->thumb.request)
431 ethumb_client_thumb_async_cancel(_elm_ethumb_client, sd->thumb.request);
432 sd->thumb.request = NULL;
434 elm_layout_signal_emit
435 (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_SIGNAL_GENERATE_STOP, "elm");
436 evas_object_smart_callback_call
437 (ELM_WIDGET_DATA(sd)->obj, SIG_GENERATE_STOP, NULL);
442 retry = eina_list_remove(retry, sd);
443 sd->thumb.retry = EINA_FALSE;
448 ecore_event_handler_del(sd->eeh);
455 static int _elm_need_ethumb = 0;
456 static void _on_die_cb(void *, Ethumb_Client *);
459 _connect_cb(void *data __UNUSED__,
465 ethumb_client_on_server_die_callback_set(c, _on_die_cb, NULL, NULL);
466 _elm_ethumb_connected = EINA_TRUE;
467 ecore_event_add(ELM_ECORE_EVENT_ETHUMB_CONNECT, NULL, NULL, NULL);
470 _elm_ethumb_client = NULL;
474 _on_die_cb(void *data __UNUSED__,
475 Ethumb_Client *c __UNUSED__)
477 ethumb_client_disconnect(_elm_ethumb_client);
479 _elm_ethumb_client = NULL;
480 _elm_ethumb_connected = EINA_FALSE;
481 _elm_ethumb_client = ethumb_client_connect(_connect_cb, NULL, NULL);
487 _elm_unneed_ethumb(void)
490 if (--_elm_need_ethumb) return;
492 ethumb_client_disconnect(_elm_ethumb_client);
493 _elm_ethumb_client = NULL;
494 ethumb_client_shutdown();
495 ELM_ECORE_EVENT_ETHUMB_CONNECT = 0;
500 _elm_thumb_dnd_cb(void *data __UNUSED__,
502 Elm_Selection_Data *drop)
504 if ((!o) || (!drop) || (!drop->data)) return EINA_FALSE;
505 elm_thumb_file_set(o, drop->data, NULL);
510 elm_need_ethumb(void)
513 if (_elm_need_ethumb++) return EINA_TRUE;
515 ELM_ECORE_EVENT_ETHUMB_CONNECT = ecore_event_type_new();
516 ethumb_client_init();
517 _elm_ethumb_client = ethumb_client_connect(_connect_cb, NULL, NULL);
526 _elm_thumb_smart_add(Evas_Object *obj)
528 EVAS_SMART_DATA_ALLOC(obj, Elm_Thumb_Smart_Data);
530 ELM_WIDGET_CLASS(_elm_thumb_parent_sc)->base.add(obj);
532 elm_widget_resize_object_set(obj, elm_layout_add(obj));
535 (ELM_WIDGET_DATA(priv)->resize_obj, "thumb", "base",
536 elm_widget_style_get(obj));
542 priv->on_hold = EINA_FALSE;
543 priv->is_video = EINA_FALSE;
544 priv->was_video = EINA_FALSE;
546 #ifdef HAVE_ELEMENTARY_ETHUMB
547 priv->thumb.thumb_path = NULL;
548 priv->thumb.thumb_key = NULL;
549 priv->thumb.request = NULL;
550 evas_object_event_callback_add
551 (obj, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, priv);
552 evas_object_event_callback_add
553 (obj, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, priv);
556 elm_widget_can_focus_set(obj, EINA_FALSE);
560 _elm_thumb_smart_del(Evas_Object *obj)
562 ELM_THUMB_DATA_GET(obj, sd);
564 #ifdef HAVE_ELEMENTARY_ETHUMB
565 if (sd->thumb.request)
567 ethumb_client_thumb_async_cancel(_elm_ethumb_client, sd->thumb.request);
568 sd->thumb.request = NULL;
572 retry = eina_list_remove(retry, sd);
573 sd->thumb.retry = EINA_FALSE;
576 eina_stringshare_del(sd->thumb.thumb_path);
577 eina_stringshare_del(sd->thumb.thumb_key);
580 eina_stringshare_del(sd->file);
581 eina_stringshare_del(sd->key);
583 if (sd->eeh) ecore_event_handler_del(sd->eeh);
585 ELM_WIDGET_CLASS(_elm_thumb_parent_sc)->base.del(obj);
589 _elm_thumb_smart_set_user(Elm_Thumb_Smart_Class *sc)
591 ELM_WIDGET_CLASS(sc)->base.add = _elm_thumb_smart_add;
592 ELM_WIDGET_CLASS(sc)->base.del = _elm_thumb_smart_del;
594 ELM_WIDGET_CLASS(sc)->base.show = _elm_thumb_smart_show;
595 ELM_WIDGET_CLASS(sc)->base.hide = _elm_thumb_smart_hide;
598 EAPI const Elm_Thumb_Smart_Class *
599 elm_thumb_smart_class_get(void)
601 static Elm_Thumb_Smart_Class _sc =
602 ELM_THUMB_SMART_CLASS_INIT_NAME_VERSION(ELM_THUMB_SMART_NAME);
603 static const Elm_Thumb_Smart_Class *class = NULL;
604 Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
606 if (class) return class;
608 _elm_thumb_smart_set(&_sc);
609 esc->callbacks = _smart_callbacks;
616 elm_thumb_add(Evas_Object *parent)
620 EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
622 obj = elm_widget_add(_elm_thumb_smart_class_new(), parent);
623 if (!obj) return NULL;
625 if (!elm_widget_sub_object_add(parent, obj))
626 ERR("could not add %p as sub object of %p", obj, parent);
632 elm_thumb_reload(Evas_Object *obj)
634 ELM_THUMB_CHECK(obj);
635 ELM_THUMB_DATA_GET(obj, sd);
637 eina_stringshare_replace(&(sd->thumb.file), NULL);
638 eina_stringshare_replace(&(sd->thumb.key), NULL);
640 #ifdef HAVE_ELEMENTARY_ETHUMB
641 if (evas_object_visible_get(obj))
647 elm_thumb_file_set(Evas_Object *obj,
651 Eina_Bool file_replaced, key_replaced;
653 ELM_THUMB_CHECK(obj);
654 ELM_THUMB_DATA_GET(obj, sd);
656 file_replaced = eina_stringshare_replace(&(sd->file), file);
657 key_replaced = eina_stringshare_replace(&(sd->key), key);
662 const char **ext, *ptr;
663 static const char *extensions[] =
665 ".avi", ".mp4", ".ogv", ".mov", ".mpg", ".wmv", NULL
668 prefix_size = eina_stringshare_strlen(sd->file) - 4;
669 if (prefix_size >= 0)
671 ptr = sd->file + prefix_size;
672 sd->is_video = EINA_FALSE;
673 for (ext = extensions; *ext; ext++)
674 if (!strcasecmp(ptr, *ext))
676 sd->is_video = EINA_TRUE;
682 eina_stringshare_replace(&(sd->thumb.file), NULL);
683 eina_stringshare_replace(&(sd->thumb.key), NULL);
685 #ifdef HAVE_ELEMENTARY_ETHUMB
686 if (((file_replaced) || (key_replaced)) && (evas_object_visible_get(obj)))
694 elm_thumb_file_get(const Evas_Object *obj,
698 ELM_THUMB_CHECK(obj);
699 ELM_THUMB_DATA_GET(obj, sd);
708 elm_thumb_path_get(const Evas_Object *obj,
712 ELM_THUMB_CHECK(obj);
713 ELM_THUMB_DATA_GET(obj, sd);
716 *file = sd->thumb.file;
718 *key = sd->thumb.key;
722 elm_thumb_animate_set(Evas_Object *obj,
723 Elm_Thumb_Animation_Setting setting)
725 ELM_THUMB_CHECK(obj);
726 ELM_THUMB_DATA_GET(obj, sd);
728 EINA_SAFETY_ON_TRUE_RETURN(setting >= ELM_THUMB_ANIMATION_LAST);
730 sd->anim_setting = setting;
731 if (setting == ELM_THUMB_ANIMATION_LOOP)
732 edje_object_signal_emit(sd->view, "animate_loop", "");
733 else if (setting == ELM_THUMB_ANIMATION_START)
734 edje_object_signal_emit(sd->view, "animate", "");
735 else if (setting == ELM_THUMB_ANIMATION_STOP)
736 edje_object_signal_emit(sd->view, "animate_stop", "");
739 EAPI Elm_Thumb_Animation_Setting
740 elm_thumb_animate_get(const Evas_Object *obj)
742 ELM_THUMB_CHECK(obj) ELM_THUMB_ANIMATION_LAST;
743 ELM_THUMB_DATA_GET(obj, sd);
745 return sd->anim_setting;
749 elm_thumb_ethumb_client_get(void)
751 return _elm_ethumb_client;
755 elm_thumb_ethumb_client_connected_get(void)
757 return _elm_ethumb_connected;
761 elm_thumb_editable_set(Evas_Object *obj,
764 ELM_THUMB_CHECK(obj) EINA_FALSE;
765 ELM_THUMB_DATA_GET(obj, sd);
768 if (sd->edit == edit) return EINA_TRUE;
772 elm_drop_target_add(obj, ELM_SEL_FORMAT_IMAGE, _elm_thumb_dnd_cb, obj);
774 elm_drop_target_del(obj);
780 elm_thumb_editable_get(const Evas_Object *obj)
782 ELM_THUMB_CHECK(obj) EINA_FALSE;
783 ELM_THUMB_DATA_GET(obj, sd);