svn update: 51469 (latest:51480)
[framework/uifw/elementary.git] / src / lib / elc_hoversel.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Hoversel
6  *
7  * A hoversel is a button that pops up a list of items (automatically
8  * choosing the direction to display) that have a lable and/or an icon to
9  * select from. It is a convenience widget to avoid the need to do all the
10  * piecing together yourself. It is intended for a small number of items in
11  * the hoversel menu (no more than 8), though is capable of many more.
12  *
13  * Signals that you can add callbacks for are:
14  *
15  * clicked  - the user clicked the hoversel button and popped up the sel
16  *
17  * selected - an item in the hoversel list is selected. event_info is the item
18  * selected - Elm_Hoversel_Item
19  *
20  * dismissed - the hover is dismissed
21  */
22 typedef struct _Widget_Data Widget_Data;
23
24 struct _Widget_Data
25 {
26    Evas_Object *btn, *hover;
27    Evas_Object *hover_parent;
28    Eina_List *items;
29    Eina_Bool horizontal : 1;
30 };
31
32 struct _Elm_Hoversel_Item
33 {
34    Evas_Object *obj;
35    const char *label;
36    const char *icon_file;
37    const char *icon_group;
38    Elm_Icon_Type icon_type;
39    Evas_Smart_Cb func;
40    Evas_Smart_Cb del_cb;
41    void *data;
42 };
43
44 static const char *widtype = NULL;
45 static void _del_pre_hook(Evas_Object *obj);
46 static void _del_hook(Evas_Object *obj);
47 static void _disable_hook(Evas_Object *obj);
48 static void _sizing_eval(Evas_Object *obj);
49 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
50 static void _parent_del(void *data, Evas *e, Evas_Object *obj, void *event_info);
51
52 static void
53 _del_pre_hook(Evas_Object *obj)
54 {
55    Elm_Hoversel_Item *it;
56    Widget_Data *wd = elm_widget_data_get(obj);
57    if (!wd) return;
58    elm_hoversel_hover_end(obj);
59    elm_hoversel_hover_parent_set(obj, NULL);
60    EINA_LIST_FREE(wd->items, it)
61      {
62         if (it->del_cb) it->del_cb((void *)it->data, it->obj, it);
63         eina_stringshare_del(it->label);
64         eina_stringshare_del(it->icon_file);
65         eina_stringshare_del(it->icon_group);
66         free(it);
67      }
68 }
69
70 static void
71 _del_hook(Evas_Object *obj)
72 {
73    Widget_Data *wd = elm_widget_data_get(obj);
74    if (!wd) return;
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    char buf[4096];
83    if (!wd) return;
84    elm_hoversel_hover_end(obj);
85    if (wd->horizontal)
86      snprintf(buf, sizeof(buf), "hoversel_horizontal/%s", elm_widget_style_get(obj));
87    else
88      snprintf(buf, sizeof(buf), "hoversel_vertical/%s", elm_widget_style_get(obj));
89    elm_object_style_set(wd->btn, buf);
90 }
91
92 static void
93 _disable_hook(Evas_Object *obj)
94 {
95    Widget_Data *wd = elm_widget_data_get(obj);
96    if (!wd) return;
97    if (elm_widget_disabled_get(obj))
98      elm_widget_disabled_set(wd->btn, 1);
99    else
100      elm_widget_disabled_set(wd->btn, 0);
101 }
102
103 static void
104 _sizing_eval(Evas_Object *obj)
105 {
106    Widget_Data *wd = elm_widget_data_get(obj);
107    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
108    if (!wd) return;
109    evas_object_size_hint_min_get(wd->btn, &minw, &minh);
110    evas_object_size_hint_max_get(wd->btn, &maxw, &maxh);
111    evas_object_size_hint_min_set(obj, minw, minh);
112    evas_object_size_hint_max_set(obj, maxw, maxh);
113 }
114
115 static void
116 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
117 {
118    _sizing_eval(data);
119 }
120
121 static void
122 _hover_clicked(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
123 {
124    elm_hoversel_hover_end(data);
125 }
126
127 static void
128 _item_clicked(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
129 {
130    Elm_Hoversel_Item *it = data;
131    Evas_Object *obj2 = it->obj;
132
133    elm_hoversel_hover_end(obj2);
134    if (it->func) it->func(it->data, obj2, it);
135    evas_object_smart_callback_call(obj2, "selected", it);
136 }
137
138 static void
139 _activate(Evas_Object *obj)
140 {
141    Widget_Data *wd = elm_widget_data_get(obj);
142    Evas_Object *bt, *bx, *ic;
143    const Eina_List *l;
144    const Elm_Hoversel_Item *it;
145    char buf[4096];
146
147    if (!wd) return;
148    if (elm_widget_disabled_get(obj)) return;
149    wd->hover = elm_hover_add(obj);
150    if (wd->horizontal)
151      snprintf(buf, sizeof(buf), "hoversel_horizontal/%s", elm_widget_style_get(obj));
152    else
153      snprintf(buf, sizeof(buf), "hoversel_vertical/%s", elm_widget_style_get(obj));
154    elm_object_style_set(wd->hover, buf);
155    evas_object_smart_callback_add(wd->hover, "clicked", _hover_clicked, obj);
156    elm_hover_parent_set(wd->hover, wd->hover_parent);
157    elm_hover_target_set(wd->hover, wd->btn);
158
159    bx = elm_box_add(wd->hover);
160    elm_box_homogenous_set(bx, 1);
161    
162    elm_box_horizontal_set(bx, wd->horizontal);
163    
164    if (wd->horizontal)
165      snprintf(buf, sizeof(buf), "hoversel_horizontal_entry/%s",
166               elm_widget_style_get(obj));
167    else
168      snprintf(buf, sizeof(buf), "hoversel_vertical_entry/%s",
169               elm_widget_style_get(obj));
170    EINA_LIST_FOREACH(wd->items, l, it)
171      {
172         bt = elm_button_add(wd->hover);
173         elm_object_style_set(bt, buf);
174         elm_button_label_set(bt, it->label);
175         if (it->icon_file)
176           {
177              ic = elm_icon_add(obj);
178              elm_icon_scale_set(ic, 0, 1);
179              if (it->icon_type == ELM_ICON_FILE)
180                elm_icon_file_set(ic, it->icon_file, it->icon_group);
181              else if (it->icon_type == ELM_ICON_STANDARD)
182                elm_icon_standard_set(ic, it->icon_file);
183              elm_button_icon_set(bt, ic);
184              evas_object_show(ic);
185           }
186         evas_object_size_hint_weight_set(bt, 1.0, 0.0);
187         evas_object_size_hint_align_set(bt, -1.0, -1.0);
188         elm_box_pack_end(bx, bt);
189         evas_object_smart_callback_add(bt, "clicked", _item_clicked, it);
190         evas_object_show(bt);
191      }
192
193    if (wd->horizontal)
194      elm_hover_content_set(wd->hover,
195                            elm_hover_best_content_location_get(wd->hover,
196                                                                ELM_HOVER_AXIS_HORIZONTAL),
197                            bx);
198    else
199      elm_hover_content_set(wd->hover,
200                            elm_hover_best_content_location_get(wd->hover,
201                                                                ELM_HOVER_AXIS_VERTICAL),
202                            bx);
203    evas_object_show(bx);
204
205    evas_object_show(wd->hover);
206    evas_object_smart_callback_call(obj, "clicked", NULL);
207
208 //   if (wd->horizontal) evas_object_hide(wd->btn);
209 }
210
211 static void
212 _button_clicked(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
213 {
214    _activate(data);
215 }
216
217 static void
218 _parent_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
219 {
220    Widget_Data *wd = elm_widget_data_get(data);
221    if (!wd) return;
222    wd->hover_parent = NULL;
223 }
224
225 /**
226  * Add a new Hoversel object
227  *
228  * @param parent The parent object
229  * @return The new object or NULL if it cannot be created
230  *
231  * @ingroup Hoversel
232  */
233 EAPI Evas_Object *
234 elm_hoversel_add(Evas_Object *parent)
235 {
236    Evas_Object *obj;
237    Evas *e;
238    Widget_Data *wd;
239    char buf[4096];
240
241    wd = ELM_NEW(Widget_Data);
242    e = evas_object_evas_get(parent);
243    obj = elm_widget_add(e);
244    ELM_SET_WIDTYPE(widtype, "hoversel");
245    elm_widget_type_set(obj, "hoversel");
246    elm_widget_sub_object_add(parent, obj);
247    elm_widget_data_set(obj, wd);
248    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
249    elm_widget_del_hook_set(obj, _del_hook);
250    elm_widget_theme_hook_set(obj, _theme_hook);
251    elm_widget_disable_hook_set(obj, _disable_hook);
252
253    wd->btn = elm_button_add(parent);
254    if (wd->horizontal)
255      snprintf(buf, sizeof(buf), "hoversel_horizontal/%s", elm_widget_style_get(obj));
256    else
257      snprintf(buf, sizeof(buf), "hoversel_vertical/%s", elm_widget_style_get(obj));
258    elm_object_style_set(wd->btn, buf);
259    elm_widget_resize_object_set(obj, wd->btn);
260    evas_object_event_callback_add(wd->btn, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
261                                   _changed_size_hints, obj);
262    evas_object_smart_callback_add(wd->btn, "clicked", _button_clicked, obj);
263    elm_hoversel_hover_parent_set(obj, parent);
264    _sizing_eval(obj);
265    return obj;
266 }
267
268 /**
269  * Set the Hover parent
270  *
271  * Sets the hover parent object. Should probably be the window that the hoversel
272  * is in.  See Hover objects for more information.
273  *
274  * @param obj The hoversel object
275  * @param parent The parent to use
276  *
277  * @ingroup Hoversel
278  */
279 EAPI void
280 elm_hoversel_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
281 {
282    ELM_CHECK_WIDTYPE(obj, widtype);
283    Widget_Data *wd = elm_widget_data_get(obj);
284    if (!wd) return;
285    if (wd->hover_parent)
286      evas_object_event_callback_del_full(wd->hover_parent, EVAS_CALLBACK_DEL,
287                                     _parent_del, obj);
288    wd->hover_parent = parent;
289    if (wd->hover_parent)
290      evas_object_event_callback_add(wd->hover_parent, EVAS_CALLBACK_DEL,
291                                     _parent_del, obj);
292 }
293
294 /**
295  * Set the hoversel button label
296  *
297  * This sets the label of the button that is always visible (before it is
298  * clicked and expanded). Also see elm_button_label_set().
299  *
300  * @param obj The hoversel object
301  * @param label The label text.
302  *
303  * @ingroup Hoversel
304  */
305 EAPI void
306 elm_hoversel_label_set(Evas_Object *obj, const char *label)
307 {
308    ELM_CHECK_WIDTYPE(obj, widtype);
309    Widget_Data *wd = elm_widget_data_get(obj);
310    if (!wd) return;
311    elm_button_label_set(wd->btn, label);
312 }
313
314 /**
315  * Get the hoversel button label
316  *
317  * @param obj The hoversel object
318  * @return The label text.
319  *
320  * @ingroup Hoversel
321  */
322 EAPI const char *
323 elm_hoversel_label_get(const Evas_Object *obj)
324 {
325    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
326    Widget_Data *wd = elm_widget_data_get(obj);
327    if ((!wd) || (!wd->btn)) return NULL;
328    return elm_button_label_get(wd->btn);
329 }
330
331 /**
332  * This sets the hoversel to expand horizontally.  The initial button
333  * will display horizontally regardless of this setting.
334  *
335  * @param obj The hoversel object
336  * @param horizontal If true, the hover will expand horizontally to the right.
337  *
338  * @ingroup Hoversel
339  */
340 EAPI void
341 elm_hoversel_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
342 {
343    ELM_CHECK_WIDTYPE(obj, widtype);
344    Widget_Data *wd = elm_widget_data_get(obj);
345    if (!wd) return;
346    wd->horizontal = !!horizontal;
347 }
348
349
350 /**
351  * This returns whether the hoversel is set to expand horizontally.
352  *
353  * @param obj The hoversel object
354  * @return If true, the hover will expand horizontally to the right.
355  *
356  * @ingroup Hoversel
357  */
358 EAPI Eina_Bool
359 elm_hoversel_horizontal_get(const Evas_Object *obj)
360 {
361    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
362    Widget_Data *wd = elm_widget_data_get(obj);
363    if (!wd) return EINA_FALSE;
364    return wd->horizontal;
365 }
366
367 /**
368  * Set the icon of the hoversel button
369  *
370  * Sets the icon of the button that is always visible (before it is clicked
371  * and expanded). Also see elm_button_icon_set().
372  *
373  * @param obj The hoversel object
374  * @param icon The icon object
375  *
376  * @ingroup Hoversel
377  */
378 EAPI void
379 elm_hoversel_icon_set(Evas_Object *obj, Evas_Object *icon)
380 {
381    ELM_CHECK_WIDTYPE(obj, widtype);
382    Widget_Data *wd = elm_widget_data_get(obj);
383    if (!wd) return;
384    elm_button_icon_set(wd->btn, icon);
385 }
386
387 /**
388  * Get the icon of the hoversel button
389  *
390  * Get the icon of the button that is always visible (before it is clicked
391  * and expanded). Also see elm_button_icon_get().
392  *
393  * @param obj The hoversel object
394  * @return The icon object
395  *
396  * @ingroup Hoversel
397  */
398 EAPI Evas_Object *
399 elm_hoversel_icon_get(const Evas_Object *obj)
400 {
401    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
402    Widget_Data *wd = elm_widget_data_get(obj);
403    if ((!wd) || (!wd->btn)) return NULL;
404    return elm_button_icon_get(wd->btn);
405 }
406
407 /**
408  * This triggers the hoversel popup from code, the same as though the
409  * user clicked the button.
410  *
411  * @param obj The hoversel object
412  *
413  * @ingroup Hoversel
414  */
415 EAPI void
416 elm_hoversel_hover_begin(Evas_Object *obj)
417 {
418    ELM_CHECK_WIDTYPE(obj, widtype);
419    Widget_Data *wd = elm_widget_data_get(obj);
420    if (!wd) return;
421    if (wd->hover) return;
422    _activate(obj);
423 }
424
425 /**
426  * This ends the hoversel popup as though the user clicked outside the hover.
427  *
428  * @param obj The hoversel object
429  *
430  * @ingroup Hoversel
431  */
432 EAPI void
433 elm_hoversel_hover_end(Evas_Object *obj)
434 {
435    ELM_CHECK_WIDTYPE(obj, widtype);
436    Widget_Data *wd = elm_widget_data_get(obj);
437    if (!wd) return;
438    if (!wd->hover) return;
439    evas_object_del(wd->hover);
440    wd->hover = NULL;
441    evas_object_smart_callback_call(obj, "dismissed", NULL);
442 }
443
444 /**
445  * Returns whether the hoversel is expanded.
446  *
447  * @param obj The hoversel object
448  * @return  This will return EINA_TRUE if the hoversel
449  * is expanded or EINA_FALSE if it is not expanded.
450  *
451  * @ingroup Hoversel
452  */
453 EAPI Eina_Bool
454 elm_hoversel_expanded_get(Evas_Object *obj)
455 {
456    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
457    Widget_Data *wd = elm_widget_data_get(obj);
458    if (!wd) return EINA_FALSE;
459    return (wd->hover) ? EINA_TRUE : EINA_FALSE;
460 }
461   
462 /**
463  * This will remove all the children items from the hoversel. (should not be
464  * called while the hoversel is active; use elm_hoversel_expanded_get()
465  * to check first).
466  *
467  * @param obj The hoversel object
468  *
469  * @ingroup Hoversel
470  */
471 EAPI void
472 elm_hoversel_clear(Evas_Object *obj)
473 {
474    Elm_Hoversel_Item *it;
475    Eina_List *l, *ll;
476    ELM_CHECK_WIDTYPE(obj, widtype);
477    Widget_Data *wd = elm_widget_data_get(obj);
478    if (!wd) return;
479    EINA_LIST_FOREACH_SAFE(wd->items, l, ll, it) elm_hoversel_item_del(it);
480 }
481
482 /**
483  * Get the list of items within the given hoversel.
484  *
485  * @param obj The hoversel object
486  * @return Returns a list of Elm_Hoversel_Item*
487  *
488  * @ingroup Hoversel
489  */
490 EAPI const Eina_List *
491 elm_hoversel_items_get(const Evas_Object *obj)
492 {
493    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
494    Widget_Data *wd = elm_widget_data_get(obj);
495    if (!wd) return NULL;
496    return wd->items;
497 }
498
499 /**
500  * Add an item to the hoversel button
501  *
502  * This adds an item to the hoversel to show when it is clicked. Note: if you
503  * need to use an icon from an edje file then use elm_hoversel_item_icon_set()
504  * right after the this function, and set icon_file to NULL here.
505  *
506  * @param obj The hoversel object
507  * @param label The text label to use for the item (NULL if not desired)
508  * @param icon_file An image file path on disk to use for the icon or standard
509  * icon name (NULL if not desired)
510  * @param icon_type The icon type if relevant
511  * @param func Convenience function to call when this item is selected
512  * @param data Data to pass to item-related functions
513  * @return A handle to the item added.
514  *
515  * @ingroup Hoversel
516  */
517 EAPI Elm_Hoversel_Item *
518 elm_hoversel_item_add(Evas_Object *obj, const char *label, const char *icon_file, Elm_Icon_Type icon_type, Evas_Smart_Cb func, const void *data)
519 {
520    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
521    Widget_Data *wd = elm_widget_data_get(obj);
522    if (!wd) return NULL;
523    Elm_Hoversel_Item *it = calloc(1, sizeof(Elm_Hoversel_Item));
524    if (!it) return NULL;
525    wd->items = eina_list_append(wd->items, it);
526    it->obj = obj;
527    it->label = eina_stringshare_add(label);
528    it->icon_file = eina_stringshare_add(icon_file);
529    it->icon_type = icon_type;
530    it->func = func;
531    it->data = (void *)data;
532    return it;
533 }
534
535 /**
536  * Delete an item from the hoversel
537  *
538  * This deletes the item from the hoversel (should not be called while the
539  * hoversel is active; use elm_hoversel_expanded_get()
540  * to check first).
541  *
542  * @param it The item to delete
543  *
544  * @ingroup Hoversel
545  */
546 EAPI void
547 elm_hoversel_item_del(Elm_Hoversel_Item *it)
548 {
549    if (!it) return;
550    Widget_Data *wd = elm_widget_data_get(it->obj);
551    if (it->del_cb) it->del_cb((void *)it->data, it->obj, it);
552    if (!wd) return;
553    elm_hoversel_hover_end(it->obj);
554    wd->items = eina_list_remove(wd->items, it);
555    eina_stringshare_del(it->label);
556    eina_stringshare_del(it->icon_file);
557    eina_stringshare_del(it->icon_group);
558    free(it);
559 }
560
561 /**
562  * Set the function called when an item within the hoversel
563  * is freed. That function will receive these parameters:
564  *
565  * void *item_data
566  * Evas_Object *the_item_object
567  * Elm_Hoversel_Item *the_object_struct
568  *
569  * @param it The item to set the callback on
570  * @param func The function called
571  *
572  * @ingroup Hoversel
573  */
574 EAPI void
575 elm_hoversel_item_del_cb_set(Elm_Hoversel_Item *it, Evas_Smart_Cb func)
576 {
577    if (!it) return;
578    it->del_cb = func;
579 }
580
581 /**
582  * This returns the data pointer supplied with elm_hoversel_item_add() that
583  * will be passed to associated function callbacks.
584  *
585  * @param it The item to get the data from
586  * @return The data pointer set with elm_hoversel_item_add()
587  *
588  * @ingroup Hoversel
589  */
590 EAPI void *
591 elm_hoversel_item_data_get(Elm_Hoversel_Item *it)
592 {
593    if (!it) return NULL;
594    return it->data;
595 }
596
597 /**
598  * This returns the label text of the given hoversel item.
599  *
600  * @param it The item to get the label
601  * @return The label text of the hoversel item
602  *
603  * @ingroup Hoversel
604  */
605 EAPI const char *
606 elm_hoversel_item_label_get(Elm_Hoversel_Item *it)
607 {
608    if (!it) return NULL;
609    return it->label;
610 }
611
612 /**
613  * This sets the icon for the given hoversel item. The icon can be loaded from
614  * the standard set, from an image file, or from an edje file.
615  *
616  * @param it The item to set the icon
617  * @param icon_file An image file path on disk to use for the icon or standard
618  * icon name
619  * @param icon_group The edje group to use if @p icon_file is an edje file. Set this
620  * to NULL if the icon is not an edje file
621  * @param icon_type The icon type
622  *
623  * @ingroup Hoversel
624  */
625 EAPI void
626 elm_hoversel_item_icon_set(Elm_Hoversel_Item *it, const char *icon_file, const char *icon_group, Elm_Icon_Type icon_type)
627 {
628    if (!it) return;
629    eina_stringshare_replace(&it->icon_file, icon_file);
630    eina_stringshare_replace(&it->icon_group, icon_group);
631    it->icon_type = icon_type;
632 }
633
634 /**
635  * Get the icon object of the hoversel item
636  *
637  * @param it The item to get the icon from
638  * @param icon_file The image file path on disk used for the icon or standard
639  * icon name
640  * @param icon_group The edje group used if @p icon_file is an edje file. NULL
641  * if the icon is not an edje file
642  * @param icon_type The icon type
643  *
644  * @ingroup Hoversel
645  */
646 EAPI void
647 elm_hoversel_item_icon_get(Elm_Hoversel_Item *it, const char **icon_file, const char **icon_group, Elm_Icon_Type *icon_type)
648 {
649    if (!it) return;
650    if (icon_file) *icon_file = it->icon_file;
651    if (icon_group) *icon_group = it->icon_group;
652    if (icon_type) *icon_type = it->icon_type;
653 }
654