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