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