From: WooHyun Jung <wh0705.jung@samsung.com>
[framework/uifw/elementary.git] / src / lib / elm_icon.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 #ifdef ELM_EFREET
5 #define NON_EXISTING (void *)-1
6 static const char *icon_theme = NULL;
7 #endif
8
9 /**
10  * @defgroup Icon Icon
11  *
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
15  * channels.
16  *
17  * Signals that you can add callbacks for are:
18  *
19  * clicked - This is called when a user has clicked the icon
20  */
21
22 typedef struct _Widget_Data Widget_Data;
23
24 struct _Widget_Data
25 {
26    Evas_Object *img;
27    const char *stdicon;
28    Elm_Icon_Lookup_Order lookup_order;
29 #ifdef ELM_EFREET
30    struct {
31       int requested_size;
32       Eina_Bool use : 1;
33    } freedesktop;
34 #endif
35    Eina_Bool scale_up : 1;
36    Eina_Bool scale_down : 1;
37    Eina_Bool smooth : 1;
38    Eina_Bool fill_outside : 1;
39    Eina_Bool no_scale : 1;
40 };
41
42 static const char *widtype = NULL;
43 static void _del_hook(Evas_Object *obj);
44 static void _theme_hook(Evas_Object *obj);
45 static void _sizing_eval(Evas_Object *obj);
46 static void _mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
47
48 static Eina_Bool _icon_standard_set(Widget_Data *wd, Evas_Object *obj, const char *name);
49 static Eina_Bool _icon_freedesktop_set(Widget_Data *wd, Evas_Object *obj, const char *name, int size);
50
51 //FIXME: move this code to ecore
52 #ifdef _WIN32
53 static Eina_Bool
54 _path_is_absolute(const char *path)
55 {
56    //TODO: Check if this works with all absolute paths in windows
57    return ((isalpha (*path)) && (*(path + 1) == ':') && ((*(path + 2) == '\\') || (*(path + 2) == '/')));
58 }
59 #else
60 static Eina_Bool
61 _path_is_absolute(const char *path)
62 {
63    return  (*path == '/');
64 }
65 #endif
66
67 static void
68 _del_hook(Evas_Object *obj)
69 {
70    Widget_Data *wd = elm_widget_data_get(obj);
71
72    if (!wd) return;
73    if (wd->stdicon) eina_stringshare_del(wd->stdicon);
74    free(wd);
75 }
76
77 static void
78 _theme_hook(Evas_Object *obj)
79 {
80    Widget_Data *wd = elm_widget_data_get(obj);
81    if (!wd) return;
82    if (wd->stdicon)
83      _elm_theme_object_icon_set(obj, wd->img, wd->stdicon, elm_widget_style_get(obj));
84    _sizing_eval(obj);
85 }
86
87 static void
88 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
89 {
90    Widget_Data *wd = elm_widget_data_get(obj);
91    if (!wd) return;
92    Evas_Object *icon_edje;
93    icon_edje = _els_smart_icon_edje_get(wd->img);
94    if (!icon_edje) return;
95    edje_object_signal_emit(icon_edje, emission, source);
96 }
97
98 static void
99 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
100 {
101    Widget_Data *wd = elm_widget_data_get(obj);
102    if (!wd) return;
103    Evas_Object *icon_edje;
104    icon_edje = _els_smart_icon_edje_get(wd->img);
105    if (!icon_edje) return;
106    edje_object_signal_callback_add(icon_edje, emission, source, func_cb, data);
107 }
108
109 static void
110 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
111 {
112    Widget_Data *wd = elm_widget_data_get(obj);
113    if (!wd) return;
114    Evas_Object *icon_edje;
115    icon_edje = _els_smart_icon_edje_get(wd->img);
116    if (!icon_edje) return;
117    edje_object_signal_callback_del_full(icon_edje, emission, source, func_cb,
118                                         data);
119 }
120
121 static void
122 _sizing_eval(Evas_Object *obj)
123 {
124    Widget_Data *wd = elm_widget_data_get(obj);
125    if (!wd) return;
126    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
127    int w, h;
128
129    _els_smart_icon_size_get(wd->img, &w, &h);
130 #ifdef ELM_EFREET
131    if ((wd->freedesktop.use) && (!((w - wd->freedesktop.requested_size) % 16)))
132      {
133         /* This icon has been set to a freedesktop icon, and the requested
134            appears to have a different size than the requested size, so try to
135            request another, higher resolution, icon.
136            FIXME: Find a better heuristic to determine if there should be
137            an icon with a different resolution. */
138         _icon_freedesktop_set(wd, obj, wd->stdicon, w);
139      }
140 #endif
141    _els_smart_icon_scale_up_set(wd->img, wd->scale_up);
142    _els_smart_icon_scale_down_set(wd->img, wd->scale_down);
143    _els_smart_icon_smooth_scale_set(wd->img, wd->smooth);
144    _els_smart_icon_fill_inside_set(wd->img, !(wd->fill_outside));
145    if (wd->no_scale) _els_smart_icon_scale_set(wd->img, 1.0);
146    else
147      {
148         _els_smart_icon_scale_set(wd->img, elm_widget_scale_get(obj) *
149                                   _elm_config->scale);
150         _els_smart_icon_size_get(wd->img, &w, &h);
151      }
152    if (!wd->scale_down)
153      {
154         minw = w;
155         minh = h;
156      }
157    if (!wd->scale_up)
158      {
159         maxw = w;
160         maxh = h;
161      }
162    evas_object_size_hint_min_set(obj, minw, minh);
163    evas_object_size_hint_max_set(obj, maxw, maxh);
164 }
165
166 static void
167 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
168 {
169    Evas_Event_Mouse_Up *ev = event_info;
170    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
171    evas_object_smart_callback_call(data, "clicked", event_info);
172 }
173
174 /**
175  * Add a new icon to the parent
176  *
177  * @param parent The parent object
178  * @return The new object or NULL if it cannot be created
179  *
180  * @ingroup Icon
181  */
182 EAPI Evas_Object *
183 elm_icon_add(Evas_Object *parent)
184 {
185    Evas_Object *obj;
186    Evas *e;
187    Widget_Data *wd;
188
189    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
190
191    wd = ELM_NEW(Widget_Data);
192    e = evas_object_evas_get(parent);
193    if (!e) return NULL;
194    obj = elm_widget_add(e);
195    ELM_SET_WIDTYPE(widtype, "icon");
196    elm_widget_type_set(obj, "icon");
197    elm_widget_can_focus_set(obj, EINA_FALSE);
198    elm_widget_sub_object_add(parent, obj);
199    elm_widget_data_set(obj, wd);
200    elm_widget_del_hook_set(obj, _del_hook);
201    elm_widget_theme_hook_set(obj, _theme_hook);
202    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
203    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
204    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
205
206    wd->lookup_order = ELM_ICON_LOOKUP_THEME_FDO;
207    wd->img = _els_smart_icon_add(e);
208    evas_object_event_callback_add(wd->img, EVAS_CALLBACK_MOUSE_UP,
209                                   _mouse_up, obj);
210    evas_object_repeat_events_set(wd->img, EINA_TRUE);
211    elm_widget_resize_object_set(obj, wd->img);
212
213    wd->smooth = EINA_TRUE;
214    wd->scale_up = EINA_TRUE;
215    wd->scale_down = EINA_TRUE;
216
217    _sizing_eval(obj);
218    return obj;
219 }
220
221 /**
222  * Set the file that will be used as icon
223  *
224  * @param obj The icon object
225  * @param file The path to file that will be used as icon
226  * @param group The group that the icon belongs in edje file
227  *
228  * @return (1 = success, 0 = error)
229  *
230  * @ingroup Icon
231  */
232 EAPI Eina_Bool
233 elm_icon_file_set(Evas_Object *obj, const char *file, const char *group)
234 {
235    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
236    Widget_Data *wd = elm_widget_data_get(obj);
237    Eina_Bool ret;
238    const char *p;
239
240    if (!wd) return EINA_FALSE;
241    EINA_SAFETY_ON_NULL_RETURN_VAL(file, EINA_FALSE);
242    if (wd->stdicon) eina_stringshare_del(wd->stdicon);
243    wd->stdicon = NULL;
244    if (((p = strrchr(file, '.'))) && (!strcasecmp(p, ".edj")))
245      ret = _els_smart_icon_file_edje_set(wd->img, file, group);
246    else
247      ret = _els_smart_icon_file_key_set(wd->img, file, group);
248    _sizing_eval(obj);
249    return ret;
250 }
251
252 /**
253  * Get the file that will be used as icon
254  *
255  * @param obj The icon object
256  * @param file The path to file that will be used as icon
257  * @param group The group that the icon belongs in edje file
258  *
259  * @ingroup Icon
260  */
261 EAPI void
262 elm_icon_file_get(const Evas_Object *obj, const char **file, const char **group)
263 {
264    ELM_CHECK_WIDTYPE(obj, widtype);
265    Widget_Data *wd = elm_widget_data_get(obj);
266    if (!wd) return;
267    _els_smart_icon_file_get(wd->img, file, group);
268 }
269
270 static Eina_Bool
271 _icon_standard_set(Widget_Data *wd, Evas_Object *obj, const char *name)
272 {
273    if (_elm_theme_object_icon_set(obj, wd->img, name, "default"))
274      {
275 #ifdef ELM_EFREET
276         /* TODO: elm_unneed_efreet() */
277         wd->freedesktop.use = EINA_FALSE;
278 #endif
279         return EINA_TRUE;
280      }
281    return EINA_FALSE;
282 }
283
284 static Eina_Bool
285 _icon_file_set(Widget_Data *wd, Evas_Object *obj, const char *path)
286 {
287    if (elm_icon_file_set(obj, path, NULL))
288      {
289 #ifdef ELM_EFREET
290         /* TODO: elm_unneed_efreet() */
291         wd->freedesktop.use = EINA_FALSE;
292 #endif
293         return EINA_TRUE;
294      }
295    return EINA_FALSE;
296 }
297
298 static Eina_Bool
299 _icon_freedesktop_set(Widget_Data *wd, Evas_Object *obj, const char *name, int size)
300 {
301 #ifdef ELM_EFREET
302    const char *path;
303
304    elm_need_efreet();
305    if (icon_theme == NON_EXISTING) return EINA_FALSE;
306    if (!icon_theme)
307      {
308         Efreet_Icon_Theme *theme;
309         /* TODO: Listen for EFREET_EVENT_ICON_CACHE_UPDATE */
310         theme = efreet_icon_theme_find(getenv("E_ICON_THEME"));
311         if (!theme)
312           {
313              const char **itr;
314              static const char *themes[] = {
315                   "gnome", "Human", "oxygen", "hicolor", NULL
316              };
317              for (itr = themes; *itr; itr++)
318                {
319                   theme = efreet_icon_theme_find(*itr);
320                   if (theme) break;
321                }
322           }
323
324         if (!theme)
325           {
326              icon_theme = NON_EXISTING;
327              return EINA_FALSE;
328           }
329         else
330           icon_theme = eina_stringshare_add(theme->name.internal);
331      }
332    path = efreet_icon_path_find(icon_theme, name, size);
333    wd->freedesktop.use = !!path;
334    if (wd->freedesktop.use)
335      {
336         wd->freedesktop.requested_size = size;
337         elm_icon_file_set(obj, path, NULL);
338         return EINA_TRUE;
339      }
340 #endif
341    return EINA_FALSE;
342 }
343
344 static inline int
345 _icon_size_min_get(Evas_Object *icon)
346 {
347    int size;
348    _els_smart_icon_size_get(icon, &size, NULL);
349    return (size < 32) ? 32 : size;
350 }
351
352 /**
353  * Set the theme, as standard, for a icon.
354  * If theme was not found and it is the absolute path of an image file, this
355  * image will be used.
356  *
357  * @param obj The icon object
358  * @param name The theme name
359  *
360  * @return (1 = success, 0 = error)
361  *
362  * @ingroup Icon
363  */
364 EAPI Eina_Bool
365 elm_icon_standard_set(Evas_Object *obj, const char *name)
366 {
367    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
368    Widget_Data *wd = elm_widget_data_get(obj);
369    char *tmp;
370    Eina_Bool ret;
371
372    if ((!wd) || (!name)) return EINA_FALSE;
373
374    /* try locating the icon using the specified lookup order */
375    switch (wd->lookup_order)
376    {
377    case ELM_ICON_LOOKUP_FDO:
378       ret = _icon_freedesktop_set(wd, obj, name, _icon_size_min_get(wd->img));
379       break;
380    case ELM_ICON_LOOKUP_THEME:
381       ret = _icon_standard_set(wd, obj, name);
382       break;
383    case ELM_ICON_LOOKUP_THEME_FDO:
384       ret = _icon_standard_set(wd, obj, name) ||
385             _icon_freedesktop_set(wd, obj, name, _icon_size_min_get(wd->img));
386       break;
387    case ELM_ICON_LOOKUP_FDO_THEME:
388    default:
389       ret = _icon_freedesktop_set(wd, obj, name, _icon_size_min_get(wd->img)) ||
390             _icon_standard_set(wd, obj, name);
391       break;
392    }
393
394    if (ret)
395      {
396         eina_stringshare_replace(&wd->stdicon, name);
397         _sizing_eval(obj);
398         return EINA_TRUE;
399      }
400
401    if (_path_is_absolute(name))
402       return _icon_file_set(wd, obj, name);
403
404    /* if that fails, see if icon name is in the format size/name. if so,
405       try locating a fallback without the size specification */
406    if (!(tmp = strchr(name, '/'))) return EINA_FALSE;
407    ++tmp;
408    if (*tmp) return elm_icon_standard_set(obj, tmp);
409
410    /* give up */
411    return EINA_FALSE;
412 }
413
414 /**
415  * Get the theme, as standard, for a icon
416  *
417  * @param obj The icon object
418  * @return The theme name
419  *
420  * @ingroup Icon
421  */
422 EAPI const char *
423 elm_icon_standard_get(const Evas_Object *obj)
424 {
425    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
426    Widget_Data *wd = elm_widget_data_get(obj);
427    if (!wd) return NULL;
428    return wd->stdicon;
429 }
430
431 /**
432  * Sets icon lookup order, used by elm_icon_standard_set().
433  *
434  * @param obj The icon object
435  * @param order The icon lookup order
436  *
437  * @ingroup Icon
438  */
439 EAPI void
440 elm_icon_order_lookup_set(Evas_Object *obj, Elm_Icon_Lookup_Order order)
441 {
442    ELM_CHECK_WIDTYPE(obj, widtype);
443    Widget_Data *wd = elm_widget_data_get(obj);
444    if (wd) wd->lookup_order = order;
445 }
446
447 /**
448  * Gets the icon lookup order.
449  *
450  * @param obj The icon object
451  * @return The icon lookup order
452  *
453  * @ingroup Icon
454  */
455 EAPI Elm_Icon_Lookup_Order
456 elm_icon_order_lookup_get(const Evas_Object *obj)
457 {
458    ELM_CHECK_WIDTYPE(obj, widtype) ELM_ICON_LOOKUP_THEME_FDO;
459    Widget_Data *wd = elm_widget_data_get(obj);
460    if (!wd) return ELM_ICON_LOOKUP_THEME_FDO;
461    return wd->lookup_order;
462 }
463
464 /**
465  * Set the smooth effect for a icon
466  *
467  * @param obj The icon object
468  * @param smooth A bool to set (or no) smooth effect
469  * (1 = smooth, 0 = not smooth)
470  *
471  * @ingroup Icon
472  */
473 EAPI void
474 elm_icon_smooth_set(Evas_Object *obj, Eina_Bool smooth)
475 {
476    ELM_CHECK_WIDTYPE(obj, widtype);
477    Widget_Data *wd = elm_widget_data_get(obj);
478
479    if (!wd) return;
480    wd->smooth = smooth;
481    _sizing_eval(obj);
482 }
483
484 /**
485  * Get the smooth effect for a icon
486  *
487  * @param obj The icon object
488  * @return If setted smooth effect
489  *
490  * @ingroup Icon
491  */
492 EAPI Eina_Bool
493 elm_icon_smooth_get(const Evas_Object *obj)
494 {
495    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
496    Widget_Data *wd = elm_widget_data_get(obj);
497
498    if (!wd) return EINA_FALSE;
499    return wd->smooth;
500 }
501
502 /**
503  * Set if the object is scalable
504  *
505  * @param obj The icon object
506  * @param no_scale A bool to set scale (or no)
507  * (1 = no_scale, 0 = scale)
508  *
509  * @ingroup Icon
510  */
511 EAPI void
512 elm_icon_no_scale_set(Evas_Object *obj, Eina_Bool no_scale)
513 {
514    ELM_CHECK_WIDTYPE(obj, widtype);
515    Widget_Data *wd = elm_widget_data_get(obj);
516
517    if (!wd) return;
518    wd->no_scale = no_scale;
519    _sizing_eval(obj);
520 }
521
522 /**
523  * Get if the object isn't scalable
524  *
525  * @param obj The icon object
526  * @return If isn't scalable
527  *
528  * @ingroup Icon
529  */
530 EAPI Eina_Bool
531 elm_icon_no_scale_get(const Evas_Object *obj)
532 {
533    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
534    Widget_Data *wd = elm_widget_data_get(obj);
535    if (!wd) return EINA_FALSE;
536    return wd->no_scale;
537 }
538
539 /**
540  * Set if the object is (up/down) scalable
541  *
542  * @param obj The icon object
543  * @param scale_up A bool to set if the object is scalable up
544  * @param scale_down A bool to set if the object is scalable down
545  *
546  * @ingroup Icon
547  */
548 EAPI void
549 elm_icon_scale_set(Evas_Object *obj, Eina_Bool scale_up, Eina_Bool scale_down)
550 {
551    ELM_CHECK_WIDTYPE(obj, widtype);
552    Widget_Data *wd = elm_widget_data_get(obj);
553
554    if (!wd) return;
555    wd->scale_up = scale_up;
556    wd->scale_down = scale_down;
557    _sizing_eval(obj);
558 }
559
560 /**
561  * Get if the object is (up/down) scalable
562  *
563  * @param obj The icon object
564  * @param scale_up A bool to set if the object is scalable up
565  * @param scale_down A bool to set if the object is scalable down
566  *
567  * @ingroup Icon
568  */
569 EAPI void
570 elm_icon_scale_get(const Evas_Object *obj, Eina_Bool *scale_up, Eina_Bool *scale_down)
571 {
572    ELM_CHECK_WIDTYPE(obj, widtype);
573    Widget_Data *wd = elm_widget_data_get(obj);
574    if (!wd) return;
575    if (scale_up) *scale_up = wd->scale_up;
576    if (scale_down) *scale_down = wd->scale_down;
577 }
578
579 /**
580  * Set if the object is filled outside
581  *
582  * @param obj The icon object
583  * @param fill_outside A bool to set if the object is filled outside
584  * (1 = filled, 0 = no filled)
585  *
586  * @ingroup Icon
587  */
588 EAPI void
589 elm_icon_fill_outside_set(Evas_Object *obj, Eina_Bool fill_outside)
590 {
591    ELM_CHECK_WIDTYPE(obj, widtype);
592    Widget_Data *wd = elm_widget_data_get(obj);
593
594    if (!wd) return;
595    wd->fill_outside = fill_outside;
596    _sizing_eval(obj);
597 }
598
599 /**
600  * Get if the object is filled outside
601  *
602  * @param obj The icon object
603  * @return If the object is filled outside
604  *
605  * @ingroup Icon
606  */
607 EAPI Eina_Bool
608 elm_icon_fill_outside_get(const Evas_Object *obj)
609 {
610    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
611    Widget_Data *wd = elm_widget_data_get(obj);
612
613    if (!wd) return EINA_FALSE;
614    return wd->fill_outside;
615 }
616
617 /**
618  * Set the prescale size for the icon
619  *
620  * @param obj The icon object
621  * @param size The prescale size
622  *
623  * @ingroup Icon
624  */
625 EAPI void
626 elm_icon_prescale_set(Evas_Object *obj, int size)
627 {
628    ELM_CHECK_WIDTYPE(obj, widtype);
629    Widget_Data *wd = elm_widget_data_get(obj);
630
631    if (!wd) return;
632    _els_smart_icon_scale_size_set(wd->img, size);
633 }
634
635 /**
636  * Get the prescale size for the icon
637  *
638  * @param obj The icon object
639  * @return The prescale size
640  *
641  * @ingroup Icon
642  */
643 EAPI int
644 elm_icon_prescale_get(const Evas_Object *obj)
645 {
646    ELM_CHECK_WIDTYPE(obj, widtype) 0;
647    Widget_Data *wd = elm_widget_data_get(obj);
648
649    if (!wd) return 0;
650    return _els_smart_icon_scale_size_get(wd->img);
651 }