1 #include <Elementary.h>
5 #define NON_EXISTING (void *)-1
6 static const char *icon_theme = NULL;
12 * A standard icon that may be provided by the theme (delete, edit,
13 * arrows etc.) or a custom file (PNG, JPG, EDJE etc.) used for an
14 * icon. The Icon may scale or not and of course... support alpha
17 * Signals that you can add callbacks for are:
19 * "clicked" - This is called when a user has clicked the icon
22 typedef struct _Widget_Data Widget_Data;
28 Elm_Icon_Lookup_Order lookup_order;
30 #ifdef HAVE_ELEMENTARY_ETHUMB
39 Ethumb_Exists *exists;
41 Ecore_Event_Handler *eeh;
43 Ethumb_Thumb_Format format;
55 Eina_Bool scale_up : 1;
56 Eina_Bool scale_down : 1;
58 Eina_Bool fill_outside : 1;
59 Eina_Bool no_scale : 1;
62 #ifdef HAVE_ELEMENTARY_ETHUMB
63 static Eina_List *_elm_icon_retry = NULL;
64 static int _icon_pending_request = 0;
66 static void _icon_thumb_exists(Ethumb_Client *client __UNUSED__, Ethumb_Exists *thread, Eina_Bool exists, void *data);
69 static const char *widtype = NULL;
70 static void _del_hook(Evas_Object *obj);
71 static void _theme_hook(Evas_Object *obj);
72 static void _sizing_eval(Evas_Object *obj);
73 static void _mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
75 static Eina_Bool _icon_standard_set(Widget_Data *wd, Evas_Object *obj, const char *name);
76 static Eina_Bool _icon_freedesktop_set(Widget_Data *wd, Evas_Object *obj, const char *name, int size);
78 static const char SIG_CLICKED[] = "clicked";
80 static const Evas_Smart_Cb_Description _signals[] = {
86 //FIXME: move this code to ecore
89 _path_is_absolute(const char *path)
91 //TODO: Check if this works with all absolute paths in windows
92 return ((isalpha (*path)) && (*(path + 1) == ':') && ((*(path + 2) == '\\') || (*(path + 2) == '/')));
96 _path_is_absolute(const char *path)
98 return (*path == '/');
102 #ifdef HAVE_ELEMENTARY_ETHUMB
104 _icon_thumb_stop(Widget_Data *wd, void *ethumbd)
106 if (wd->thumb.id > 0)
108 ethumb_client_generate_cancel(ethumbd, wd->thumb.id, NULL, NULL, NULL);
110 _icon_pending_request--;
113 if (wd->thumb.exists)
115 ethumb_client_thumb_exists_cancel(wd->thumb.exists, _icon_thumb_exists, wd);
116 wd->thumb.exists = NULL;
117 _icon_pending_request--;
122 _elm_icon_retry = eina_list_remove(_elm_icon_retry, wd);
123 wd->thumb.retry = EINA_FALSE;
128 _icon_thumb_display(Widget_Data *wd)
130 Eina_Bool ret = EINA_FALSE;
132 if (wd->thumb.format == ETHUMB_THUMB_EET)
134 static const char *extensions[] = {
135 ".avi", ".mp4", ".ogv", ".mov", ".mpg", ".wmv", NULL
137 const char **ext, *ptr;
139 Eina_Bool video = EINA_FALSE;
141 prefix_size = eina_stringshare_strlen(wd->thumb.file.path) - 4;
142 if (prefix_size >= 0)
144 ptr = wd->thumb.file.path + prefix_size;
145 for (ext = extensions; *ext; ++ext)
146 if (!strcasecmp(ptr, *ext))
154 ret = _els_smart_icon_file_edje_set(wd->img, wd->thumb.thumb.path, "movie/thumb");
158 ret = _els_smart_icon_file_key_set(wd->img, wd->thumb.thumb.path, wd->thumb.thumb.key);
164 _icon_thumb_retry(Widget_Data *wd)
166 return _icon_thumb_display(wd);
170 _icon_thumb_cleanup(Ethumb_Client *ethumbd)
175 EINA_LIST_FOREACH_SAFE(_elm_icon_retry, l, ll, wd)
176 if (_icon_thumb_retry(wd))
178 _elm_icon_retry = eina_list_remove_list(_elm_icon_retry, l);
179 wd->thumb.retry = EINA_FALSE;
182 if (_icon_pending_request == 0)
183 EINA_LIST_FREE(_elm_icon_retry, wd)
184 _icon_thumb_stop(wd, ethumbd);
188 _icon_thumb_finish(Widget_Data *wd, Ethumb_Client *ethumbd)
190 const char *file = NULL, *group = NULL;
193 _els_smart_icon_file_get(wd->img, &file, &group);
194 file = eina_stringshare_ref(file);
195 group = eina_stringshare_ref(group);
197 ret = _icon_thumb_display(wd);
203 if (!wd->thumb.retry)
205 _elm_icon_retry = eina_list_append(_elm_icon_retry, wd);
206 wd->thumb.retry = EINA_TRUE;
209 /* Back to previous image */
210 if (((p = strrchr(file, '.'))) && (!strcasecmp(p, ".edj")))
211 _els_smart_icon_file_edje_set(wd->img, file, group);
213 _els_smart_icon_file_key_set(wd->img, file, group);
216 _icon_thumb_cleanup(ethumbd);
218 eina_stringshare_del(file);
219 eina_stringshare_del(group);
223 _icon_thumb_cb(void *data,
224 Ethumb_Client *ethumbd,
226 const char *file __UNUSED__,
227 const char *key __UNUSED__,
228 const char *thumb_path,
229 const char *thumb_key,
232 Widget_Data *wd = data;
234 EINA_SAFETY_ON_FALSE_RETURN(wd->thumb.id == id);
237 _icon_pending_request--;
241 eina_stringshare_replace(&wd->thumb.thumb.path, thumb_path);
242 eina_stringshare_replace(&wd->thumb.thumb.key, thumb_key);
243 wd->thumb.format = ethumb_client_format_get(ethumbd);
245 _icon_thumb_finish(wd, ethumbd);
249 ERR("could not generate thumbnail for %s (key: %s)", file, key);
250 _icon_thumb_cleanup(ethumbd);
255 _icon_thumb_exists(Ethumb_Client *client __UNUSED__, Ethumb_Exists *thread, Eina_Bool exists, void *data)
257 Widget_Data *wd = data;
258 Ethumb_Client *ethumbd;
260 if (ethumb_client_thumb_exists_check(thread))
263 wd->thumb.exists = NULL;
265 ethumbd = elm_thumb_ethumb_client_get();
269 const char *thumb_path, *thumb_key;
271 _icon_pending_request--;
272 ethumb_client_thumb_path_get(ethumbd, &thumb_path, &thumb_key);
273 eina_stringshare_replace(&wd->thumb.thumb.path, thumb_path);
274 eina_stringshare_replace(&wd->thumb.thumb.key, thumb_key);
275 wd->thumb.format = ethumb_client_format_get(ethumbd);
277 _icon_thumb_finish(wd, ethumbd);
279 else if ((wd->thumb.id = ethumb_client_generate(ethumbd, _icon_thumb_cb, wd, NULL)) == -1)
281 /* Failed to generate thumbnail */
282 _icon_pending_request--;
287 _icon_thumb_apply(Widget_Data *wd)
289 Ethumb_Client *ethumbd;
291 ethumbd = elm_thumb_ethumb_client_get();
293 _icon_thumb_stop(wd, ethumbd);
295 if (!wd->thumb.file.path) return ;
297 _icon_pending_request++;
298 ethumb_client_file_set(ethumbd, wd->thumb.file.path, wd->thumb.file.key);
299 ethumb_client_size_set(ethumbd, 32, 32);
300 wd->thumb.exists = ethumb_client_thumb_exists(ethumbd, _icon_thumb_exists, wd);
304 _icon_thumb_apply_cb(void *data, int type __UNUSED__, void *ev __UNUSED__)
306 Widget_Data *wd = data;
308 _icon_thumb_apply(wd);
309 return ECORE_CALLBACK_RENEW;
314 _del_hook(Evas_Object *obj)
316 Widget_Data *wd = elm_widget_data_get(obj);
317 #ifdef HAVE_ELEMENTARY_ETHUMB
318 Ethumb_Client *ethumbd;
322 if (wd->stdicon) eina_stringshare_del(wd->stdicon);
324 #ifdef HAVE_ELEMENTARY_ETHUMB
325 ethumbd = elm_thumb_ethumb_client_get();
326 _icon_thumb_stop(wd, ethumbd);
328 eina_stringshare_del(wd->thumb.file.path);
329 eina_stringshare_del(wd->thumb.file.key);
330 eina_stringshare_del(wd->thumb.thumb.path);
331 eina_stringshare_del(wd->thumb.thumb.key);
334 ecore_event_handler_del(wd->thumb.eeh);
341 _theme_hook(Evas_Object *obj)
343 Widget_Data *wd = elm_widget_data_get(obj);
346 _elm_theme_object_icon_set(obj, wd->img, wd->stdicon, elm_widget_style_get(obj));
351 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
353 Widget_Data *wd = elm_widget_data_get(obj);
355 Evas_Object *icon_edje;
356 icon_edje = _els_smart_icon_edje_get(wd->img);
357 if (!icon_edje) return;
358 edje_object_signal_emit(icon_edje, emission, source);
362 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
364 Widget_Data *wd = elm_widget_data_get(obj);
366 Evas_Object *icon_edje;
367 icon_edje = _els_smart_icon_edje_get(wd->img);
368 if (!icon_edje) return;
369 edje_object_signal_callback_add(icon_edje, emission, source, func_cb, data);
373 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
375 Widget_Data *wd = elm_widget_data_get(obj);
377 Evas_Object *icon_edje;
378 icon_edje = _els_smart_icon_edje_get(wd->img);
379 if (!icon_edje) return;
380 edje_object_signal_callback_del_full(icon_edje, emission, source, func_cb,
385 _sizing_eval(Evas_Object *obj)
387 Widget_Data *wd = elm_widget_data_get(obj);
389 Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
392 _els_smart_icon_size_get(wd->img, &w, &h);
394 if ((wd->freedesktop.use) && (!((w - wd->freedesktop.requested_size) % 16)))
396 /* This icon has been set to a freedesktop icon, and the requested
397 appears to have a different size than the requested size, so try to
398 request another, higher resolution, icon.
399 FIXME: Find a better heuristic to determine if there should be
400 an icon with a different resolution. */
401 _icon_freedesktop_set(wd, obj, wd->stdicon, w);
404 _els_smart_icon_scale_up_set(wd->img, wd->scale_up);
405 _els_smart_icon_scale_down_set(wd->img, wd->scale_down);
406 _els_smart_icon_smooth_scale_set(wd->img, wd->smooth);
407 _els_smart_icon_fill_inside_set(wd->img, !(wd->fill_outside));
408 if (wd->no_scale) _els_smart_icon_scale_set(wd->img, 1.0);
411 _els_smart_icon_scale_set(wd->img, elm_widget_scale_get(obj) *
413 _els_smart_icon_size_get(wd->img, &w, &h);
425 evas_object_size_hint_min_set(obj, minw, minh);
426 evas_object_size_hint_max_set(obj, maxw, maxh);
430 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
432 Evas_Event_Mouse_Up *ev = event_info;
433 if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
434 evas_object_smart_callback_call(data, SIG_CLICKED, event_info);
438 * Add a new icon to the parent
440 * @param parent The parent object
441 * @return The new object or NULL if it cannot be created
446 elm_icon_add(Evas_Object *parent)
452 ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
454 ELM_SET_WIDTYPE(widtype, "icon");
455 elm_widget_type_set(obj, "icon");
456 elm_widget_can_focus_set(obj, EINA_FALSE);
457 elm_widget_sub_object_add(parent, obj);
458 elm_widget_data_set(obj, wd);
459 elm_widget_del_hook_set(obj, _del_hook);
460 elm_widget_theme_hook_set(obj, _theme_hook);
461 elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
462 elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
463 elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
465 wd->lookup_order = ELM_ICON_LOOKUP_THEME_FDO;
466 wd->img = _els_smart_icon_add(e);
467 evas_object_event_callback_add(wd->img, EVAS_CALLBACK_MOUSE_UP,
469 evas_object_repeat_events_set(wd->img, EINA_TRUE);
470 elm_widget_resize_object_set(obj, wd->img);
472 evas_object_smart_callbacks_descriptions_set(obj, _signals);
474 #ifdef HAVE_ELEMENTARY_ETHUMB
478 wd->smooth = EINA_TRUE;
479 wd->scale_up = EINA_TRUE;
480 wd->scale_down = EINA_TRUE;
487 * Set the file that will be used as icon
489 * @param obj The icon object
490 * @param file The path to file that will be used as icon
491 * @param group The group that the icon belongs in edje file
493 * @return (1 = success, 0 = error)
498 elm_icon_file_set(Evas_Object *obj, const char *file, const char *group)
500 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
501 Widget_Data *wd = elm_widget_data_get(obj);
505 if (!wd) return EINA_FALSE;
506 EINA_SAFETY_ON_NULL_RETURN_VAL(file, EINA_FALSE);
507 if (wd->stdicon) eina_stringshare_del(wd->stdicon);
509 if (((p = strrchr(file, '.'))) && (!strcasecmp(p, ".edj")))
510 ret = _els_smart_icon_file_edje_set(wd->img, file, group);
512 ret = _els_smart_icon_file_key_set(wd->img, file, group);
518 * Get the file that will be used as icon
520 * @param obj The icon object
521 * @param file The path to file that will be used as icon
522 * @param group The group that the icon belongs in edje file
527 elm_icon_file_get(const Evas_Object *obj, const char **file, const char **group)
529 ELM_CHECK_WIDTYPE(obj, widtype);
530 Widget_Data *wd = elm_widget_data_get(obj);
532 _els_smart_icon_file_get(wd->img, file, group);
536 elm_icon_thumb_set(const Evas_Object *obj, const char *file, const char *group)
538 ELM_CHECK_WIDTYPE(obj, widtype);
539 Widget_Data *wd = elm_widget_data_get(obj);
542 #ifdef HAVE_ELEMENTARY_ETHUMB
543 eina_stringshare_replace(&wd->thumb.file.path, file);
544 eina_stringshare_replace(&wd->thumb.file.key, group);
546 if (elm_thumb_ethumb_client_connected())
548 _icon_thumb_apply(wd);
554 wd->thumb.eeh = ecore_event_handler_add(ELM_ECORE_EVENT_ETHUMB_CONNECT, _icon_thumb_apply_cb, wd);
564 _icon_standard_set(Widget_Data *wd, Evas_Object *obj, const char *name)
566 if (_elm_theme_object_icon_set(obj, wd->img, name, "default"))
569 /* TODO: elm_unneed_efreet() */
570 wd->freedesktop.use = EINA_FALSE;
578 _icon_file_set(Widget_Data *wd, Evas_Object *obj, const char *path)
580 if (elm_icon_file_set(obj, path, NULL))
583 /* TODO: elm_unneed_efreet() */
584 wd->freedesktop.use = EINA_FALSE;
592 _icon_freedesktop_set(Widget_Data *wd, Evas_Object *obj, const char *name, int size)
598 if (icon_theme == NON_EXISTING) return EINA_FALSE;
601 Efreet_Icon_Theme *theme;
602 /* TODO: Listen for EFREET_EVENT_ICON_CACHE_UPDATE */
603 theme = efreet_icon_theme_find(getenv("E_ICON_THEME"));
607 static const char *themes[] = {
608 "gnome", "Human", "oxygen", "hicolor", NULL
610 for (itr = themes; *itr; itr++)
612 theme = efreet_icon_theme_find(*itr);
619 icon_theme = NON_EXISTING;
623 icon_theme = eina_stringshare_add(theme->name.internal);
625 path = efreet_icon_path_find(icon_theme, name, size);
626 wd->freedesktop.use = !!path;
627 if (wd->freedesktop.use)
629 wd->freedesktop.requested_size = size;
630 elm_icon_file_set(obj, path, NULL);
638 _icon_size_min_get(Evas_Object *icon)
641 _els_smart_icon_size_get(icon, &size, NULL);
642 return (size < 32) ? 32 : size;
646 * Set the theme, as standard, for an icon.
647 * If theme was not found and it is the absolute path of an image file, this
648 * image will be used.
650 * @param obj The icon object
651 * @param name The theme name
653 * @return (1 = success, 0 = error)
658 elm_icon_standard_set(Evas_Object *obj, const char *name)
660 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
661 Widget_Data *wd = elm_widget_data_get(obj);
665 if ((!wd) || (!name)) return EINA_FALSE;
667 /* try locating the icon using the specified lookup order */
668 switch (wd->lookup_order)
670 case ELM_ICON_LOOKUP_FDO:
671 ret = _icon_freedesktop_set(wd, obj, name, _icon_size_min_get(wd->img));
673 case ELM_ICON_LOOKUP_THEME:
674 ret = _icon_standard_set(wd, obj, name);
676 case ELM_ICON_LOOKUP_THEME_FDO:
677 ret = _icon_standard_set(wd, obj, name) ||
678 _icon_freedesktop_set(wd, obj, name, _icon_size_min_get(wd->img));
680 case ELM_ICON_LOOKUP_FDO_THEME:
682 ret = _icon_freedesktop_set(wd, obj, name, _icon_size_min_get(wd->img)) ||
683 _icon_standard_set(wd, obj, name);
689 eina_stringshare_replace(&wd->stdicon, name);
694 if (_path_is_absolute(name))
695 return _icon_file_set(wd, obj, name);
697 /* if that fails, see if icon name is in the format size/name. if so,
698 try locating a fallback without the size specification */
699 if (!(tmp = strchr(name, '/'))) return EINA_FALSE;
701 if (*tmp) return elm_icon_standard_set(obj, tmp);
708 * Get the theme, as standard, for an icon
710 * @param obj The icon object
711 * @return The theme name
716 elm_icon_standard_get(const Evas_Object *obj)
718 ELM_CHECK_WIDTYPE(obj, widtype) NULL;
719 Widget_Data *wd = elm_widget_data_get(obj);
720 if (!wd) return NULL;
725 * Sets icon lookup order, used by elm_icon_standard_set().
727 * @param obj The icon object
728 * @param order The icon lookup order
733 elm_icon_order_lookup_set(Evas_Object *obj, Elm_Icon_Lookup_Order order)
735 ELM_CHECK_WIDTYPE(obj, widtype);
736 Widget_Data *wd = elm_widget_data_get(obj);
737 if (wd) wd->lookup_order = order;
741 * Gets the icon lookup order.
743 * @param obj The icon object
744 * @return The icon lookup order
748 EAPI Elm_Icon_Lookup_Order
749 elm_icon_order_lookup_get(const Evas_Object *obj)
751 ELM_CHECK_WIDTYPE(obj, widtype) ELM_ICON_LOOKUP_THEME_FDO;
752 Widget_Data *wd = elm_widget_data_get(obj);
753 if (!wd) return ELM_ICON_LOOKUP_THEME_FDO;
754 return wd->lookup_order;
758 * Set the smooth effect for an icon
760 * @param obj The icon object
761 * @param smooth A bool to set (or no) smooth effect
762 * (1 = smooth, 0 = not smooth)
767 elm_icon_smooth_set(Evas_Object *obj, Eina_Bool smooth)
769 ELM_CHECK_WIDTYPE(obj, widtype);
770 Widget_Data *wd = elm_widget_data_get(obj);
778 * Get the smooth effect for an icon
780 * @param obj The icon object
781 * @return If setted smooth effect
786 elm_icon_smooth_get(const Evas_Object *obj)
788 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
789 Widget_Data *wd = elm_widget_data_get(obj);
791 if (!wd) return EINA_FALSE;
796 * Set if the object is scalable
798 * @param obj The icon object
799 * @param no_scale A bool to set scale (or no)
800 * (1 = no_scale, 0 = scale)
805 elm_icon_no_scale_set(Evas_Object *obj, Eina_Bool no_scale)
807 ELM_CHECK_WIDTYPE(obj, widtype);
808 Widget_Data *wd = elm_widget_data_get(obj);
811 wd->no_scale = no_scale;
816 * Get if the object isn't scalable
818 * @param obj The icon object
819 * @return If isn't scalable
824 elm_icon_no_scale_get(const Evas_Object *obj)
826 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
827 Widget_Data *wd = elm_widget_data_get(obj);
828 if (!wd) return EINA_FALSE;
833 * Set if the object is (up/down) scalable
835 * @param obj The icon object
836 * @param scale_up A bool to set if the object is scalable up
837 * @param scale_down A bool to set if the object is scalable down
842 elm_icon_scale_set(Evas_Object *obj, Eina_Bool scale_up, Eina_Bool scale_down)
844 ELM_CHECK_WIDTYPE(obj, widtype);
845 Widget_Data *wd = elm_widget_data_get(obj);
848 wd->scale_up = scale_up;
849 wd->scale_down = scale_down;
854 * Get if the object is (up/down) scalable
856 * @param obj The icon object
857 * @param scale_up A bool to set if the object is scalable up
858 * @param scale_down A bool to set if the object is scalable down
863 elm_icon_scale_get(const Evas_Object *obj, Eina_Bool *scale_up, Eina_Bool *scale_down)
865 ELM_CHECK_WIDTYPE(obj, widtype);
866 Widget_Data *wd = elm_widget_data_get(obj);
868 if (scale_up) *scale_up = wd->scale_up;
869 if (scale_down) *scale_down = wd->scale_down;
873 * Set if the object is filled outside
875 * @param obj The icon object
876 * @param fill_outside A bool to set if the object is filled outside
877 * (1 = filled, 0 = no filled)
882 elm_icon_fill_outside_set(Evas_Object *obj, Eina_Bool fill_outside)
884 ELM_CHECK_WIDTYPE(obj, widtype);
885 Widget_Data *wd = elm_widget_data_get(obj);
888 wd->fill_outside = fill_outside;
893 * Get if the object is filled outside
895 * @param obj The icon object
896 * @return If the object is filled outside
901 elm_icon_fill_outside_get(const Evas_Object *obj)
903 ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
904 Widget_Data *wd = elm_widget_data_get(obj);
906 if (!wd) return EINA_FALSE;
907 return wd->fill_outside;
911 * Set the prescale size for the icon
913 * @param obj The icon object
914 * @param size The prescale size
919 elm_icon_prescale_set(Evas_Object *obj, int size)
921 ELM_CHECK_WIDTYPE(obj, widtype);
922 Widget_Data *wd = elm_widget_data_get(obj);
925 _els_smart_icon_scale_size_set(wd->img, size);
929 * Get the prescale size for the icon
931 * @param obj The icon object
932 * @return The prescale size
937 elm_icon_prescale_get(const Evas_Object *obj)
939 ELM_CHECK_WIDTYPE(obj, widtype) 0;
940 Widget_Data *wd = elm_widget_data_get(obj);
943 return _els_smart_icon_scale_size_get(wd->img);