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