Merge "[Password]: New design based changes, a new style removed password mode contro...
[framework/uifw/elementary.git] / src / lib / elc_anchorview.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Anchorview Anchorview
6  *
7  * This is just like the Anchorblock object, but provides a scroller to hold
8  * the text automatically.
9  *
10  * Signals that you can add callbacks for are:
11  *
12  * "anchor,clicked" - achor called was clicked. event_info is anchor info -
13  *                    Elm_Entry_Anchorview_Info
14  */
15 typedef struct _Widget_Data Widget_Data;
16 typedef struct _Elm_Anchorview_Item_Provider Elm_Anchorview_Item_Provider;
17
18 struct _Widget_Data
19 {
20    Evas_Object *scroller, *entry;
21    Evas_Object *hover_parent;
22    Evas_Object *pop, *hover;
23    Eina_List *item_providers;
24    const char *hover_style;
25 };
26
27 struct _Elm_Anchorview_Item_Provider
28 {
29    Evas_Object *(*func) (void *data, Evas_Object *anchorview, const char *item);
30    void *data;
31 };
32
33 static const char *widtype = NULL;
34
35 static const char SIG_ANCHOR_CLICKED[] = "anchor,clicked";
36 static const Evas_Smart_Cb_Description _signals[] = {
37        {SIG_ANCHOR_CLICKED, ""}, /* TODO: declare the type properly, as data is
38                                   * being passed
39                                   */
40        {NULL, NULL}
41 };
42
43 static void _del_pre_hook(Evas_Object *obj);
44 static void _del_hook(Evas_Object *obj);
45 static void _sizing_eval(Evas_Object *obj);
46 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
47 static void _parent_del(void *data, Evas *e, Evas_Object *obj, void *event_info);
48 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
49
50 static void
51 _del_pre_hook(Evas_Object *obj)
52 {
53    elm_anchorview_hover_end(obj);
54    elm_anchorview_hover_parent_set(obj, NULL);
55 }
56
57 static void
58 _del_hook(Evas_Object *obj)
59 {
60    Widget_Data *wd = elm_widget_data_get(obj);
61    Elm_Anchorview_Item_Provider *ip;
62    if (!wd) return;
63    if (wd->hover_style) eina_stringshare_del(wd->hover_style);
64    EINA_LIST_FREE(wd->item_providers, ip)
65      {
66         free(ip);
67      }
68    free(wd);
69 }
70
71 static void
72 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
73 {
74    Widget_Data *wd = elm_widget_data_get(obj);
75    if (!wd) return;
76    elm_widget_mirrored_set(wd->hover, rtl);
77 }
78
79 static void
80 _theme_hook(Evas_Object *obj)
81 {
82    Widget_Data *wd = elm_widget_data_get(obj);
83    if (!wd) return;
84    _elm_widget_mirrored_reload(obj);
85    _mirrored_set(obj, elm_widget_mirrored_get(obj));
86 }
87
88 static void
89 _sizing_eval(Evas_Object *obj)
90 {
91    Widget_Data *wd = elm_widget_data_get(obj);
92    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
93    if (!wd) return;
94    evas_object_size_hint_min_set(obj, minw, minh);
95    evas_object_size_hint_max_set(obj, maxw, maxh);
96 }
97
98 static void
99 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
100 {
101    _sizing_eval(data);
102 }
103
104 static void
105 _hover_clicked(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
106 {
107    elm_anchorview_hover_end(data);
108 }
109
110 static void
111 _anchor_clicked(void *data, Evas_Object *obj, void *event_info)
112 {
113    Widget_Data *wd = elm_widget_data_get(data);
114    Elm_Entry_Anchor_Info *info = event_info;
115    Evas_Object *hover_parent;
116    Elm_Entry_Anchorview_Info ei;
117    Evas_Coord x, w, y, h, px, py;
118    if (!wd) return;
119    wd->pop = elm_icon_add(obj);
120    evas_object_move(wd->pop, info->x, info->y);
121    evas_object_resize(wd->pop, info->w, info->h);
122    wd->hover = elm_hover_add(obj);
123    elm_widget_mirrored_set(wd->hover, elm_widget_mirrored_get((Evas_Object *) data));
124    if (wd->hover_style) elm_object_style_set(wd->hover, wd->hover_style);
125    hover_parent = wd->hover_parent;
126    if (!hover_parent) hover_parent = obj;
127    elm_hover_parent_set(wd->hover, hover_parent);
128    elm_hover_target_set(wd->hover, wd->pop);
129    ei.name = info->name;
130    ei.button = info->button;
131    ei.hover = wd->hover;
132    ei.anchor.x = info->x;
133    ei.anchor.y = info->y;
134    ei.anchor.w = info->w;
135    ei.anchor.h = info->h;
136    evas_object_geometry_get(hover_parent, &x, &y, &w, &h);
137    ei.hover_parent.x = x;
138    ei.hover_parent.y = y;
139    ei.hover_parent.w = w;
140    ei.hover_parent.h = h;
141    px = info->x + (info->w / 2);
142    py = info->y + (info->h / 2);
143    ei.hover_left = 1;
144    if (px < (x + (w / 3))) ei.hover_left = 0;
145    ei.hover_right = 1;
146    if (px > (x + ((w * 2) / 3))) ei.hover_right = 0;
147    ei.hover_top = 1;
148    if (py < (y + (h / 3))) ei.hover_top = 0;
149    ei.hover_bottom = 1;
150    if (py > (y + ((h * 2) / 3))) ei.hover_bottom = 0;
151
152    if (elm_widget_mirrored_get(wd->hover))
153      {  /* Swap right and left because they switch sides in RTL */
154         Eina_Bool tmp = ei.hover_left;
155         ei.hover_left = ei.hover_right;
156         ei.hover_right = tmp;
157      }
158
159    evas_object_smart_callback_call(data, SIG_ANCHOR_CLICKED, &ei);
160    evas_object_smart_callback_add(wd->hover, "clicked", _hover_clicked, data);
161    evas_object_show(wd->hover);
162 }
163
164 static void
165 _parent_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
166 {
167    Widget_Data *wd = elm_widget_data_get(data);
168    if (!wd) return;
169    wd->hover_parent = NULL;
170 }
171
172 static Evas_Object *
173 _item_provider(void *data, Evas_Object *entry __UNUSED__, const char *item)
174 {
175    Widget_Data *wd = elm_widget_data_get(data);
176    Eina_List *l;
177    Elm_Anchorview_Item_Provider *ip;
178
179    EINA_LIST_FOREACH(wd->item_providers, l, ip)
180      {
181         Evas_Object *o;
182
183         o = ip->func(ip->data, data, item);
184         if (o) return o;
185      }
186    return NULL;
187 }
188
189 /**
190  * Add a new Anchorview object
191  *
192  * @param parent The parent object
193  * @return The new object or NULL if it cannot be created
194  *
195  * @ingroup Anchorview
196  */
197 EAPI Evas_Object *
198 elm_anchorview_add(Evas_Object *parent)
199 {
200    Evas_Object *obj;
201    Evas *e;
202    Widget_Data *wd;
203
204    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
205
206    ELM_SET_WIDTYPE(widtype, "anchorview");
207    elm_widget_type_set(obj, "anchorview");
208    elm_widget_sub_object_add(parent, obj);
209    elm_widget_data_set(obj, wd);
210    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
211    elm_widget_del_hook_set(obj, _del_hook);
212    elm_widget_theme_hook_set(obj, _theme_hook);
213    elm_widget_can_focus_set(obj, EINA_TRUE);
214
215    wd->scroller = elm_scroller_add(parent);
216    elm_widget_resize_object_set(obj, wd->scroller);
217    wd->entry = elm_entry_add(parent);
218    elm_entry_item_provider_prepend(wd->entry, _item_provider, obj);
219    elm_entry_editable_set(wd->entry, 0);
220    evas_object_size_hint_weight_set(wd->entry, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
221    evas_object_size_hint_align_set(wd->entry, EVAS_HINT_FILL, EVAS_HINT_FILL);
222    elm_scroller_content_set(wd->scroller, wd->entry);
223    evas_object_show(wd->entry);
224
225    evas_object_event_callback_add(wd->entry, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
226                                   _changed_size_hints, obj);
227
228    elm_entry_entry_set(wd->entry, "");
229
230    evas_object_smart_callback_add(wd->entry, "anchor,clicked",
231                                   _anchor_clicked, obj);
232
233    _mirrored_set(obj, elm_widget_mirrored_get(obj));
234    _sizing_eval(obj);
235
236    // TODO: convert Elementary to subclassing of Evas_Smart_Class
237    // TODO: and save some bytes, making descriptions per-class and not instance!
238    evas_object_smart_callbacks_descriptions_set(obj, _signals);
239    return obj;
240 }
241
242 /**
243  * Set the text markup of the anchorview
244  *
245  * This sets the text of the anchorview to be the text given as @p text. This
246  * text is in markup format with \<a href=XXX\> beginning an achor with the
247  * string link of 'XXX', and \</\> or \</a\> ending the link. Other markup can
248  * be used dependign on the style support.
249  *
250  * @param obj The anchorview object
251  * @param text The text to set, or NULL to clear
252  *
253  * @ingroup Anchorview
254  */
255 EAPI void
256 elm_anchorview_text_set(Evas_Object *obj, const char *text)
257 {
258    ELM_CHECK_WIDTYPE(obj, widtype);
259    Widget_Data *wd = elm_widget_data_get(obj);
260    if (!wd) return;
261    elm_entry_entry_set(wd->entry, text);
262    if (wd->hover) evas_object_del(wd->hover);
263    if (wd->pop) evas_object_del(wd->pop);
264    wd->hover = NULL;
265    wd->pop = NULL;
266    _sizing_eval(obj);
267 }
268
269 /**
270  * Get the markup text set for the anchorview
271  *
272  * This retrieves back the string set by @c elm_anchorview_text_set().
273  *
274  * @param obj The anchorview object
275  * @return text The markup text set or @c NULL, either if it was not set
276  * or an error occurred
277  *
278  * @ingroup Anchorview
279  */
280 EAPI const char*
281 elm_anchorview_text_get(const Evas_Object *obj)
282 {
283    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
284    Widget_Data *wd = elm_widget_data_get(obj);
285    if (!wd) return NULL;
286    return elm_entry_entry_get(wd->entry);
287 }
288
289 /**
290  * Set the parent of the hover popup
291  *
292  * This sets the parent of the hover that anchorview will create. See hover
293  * objects for more information on this.
294  *
295  * @param obj The anchorview object
296  * @param parent The parent the hover should use
297  *
298  * @ingroup Anchorview
299  */
300 EAPI void
301 elm_anchorview_hover_parent_set(Evas_Object *obj, Evas_Object *parent)
302 {
303    ELM_CHECK_WIDTYPE(obj, widtype);
304    Widget_Data *wd = elm_widget_data_get(obj);
305    if (!wd) return;
306    if (wd->hover_parent)
307      evas_object_event_callback_del_full(wd->hover_parent, EVAS_CALLBACK_DEL, _parent_del, obj);
308    wd->hover_parent = parent;
309    if (wd->hover_parent)
310      evas_object_event_callback_add(wd->hover_parent, EVAS_CALLBACK_DEL, _parent_del, obj);
311 }
312
313 /**
314  * Get the parent of the hover popup
315  *
316  * This gets the parent of the hover that anchorview will created. See hover
317  * objects for more information on this.
318  *
319  * @param obj The anchorview object
320  * @return The parent used by hover
321  *
322  * @ingroup Anchorview
323  */
324 EAPI Evas_Object *
325 elm_anchorview_hover_parent_get(const Evas_Object *obj)
326 {
327    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
328    Widget_Data *wd = elm_widget_data_get(obj);
329    if (!wd) return NULL;
330    return wd->hover_parent;
331 }
332
333 /**
334  * Set the style that the hover should use
335  *
336  * This sets the style for the hover that anchorview will create. See hover
337  * objects for more information
338  *
339  * @param obj The anchorview object
340  * @param style The style to use
341  *
342  * @ingroup Anchorview
343  */
344 EAPI void
345 elm_anchorview_hover_style_set(Evas_Object *obj, const char *style)
346 {
347    ELM_CHECK_WIDTYPE(obj, widtype);
348    Widget_Data *wd = elm_widget_data_get(obj);
349    if (!wd) return;
350    eina_stringshare_replace(&wd->hover_style, style);
351 }
352
353 /**
354  * Get the style that the hover should use
355  *
356  * This gets the style for the hover that anchorview will create. See hover
357  * objects for more information
358  *
359  * @param obj The anchorview object
360  * @return The style defined
361  *
362  * @ingroup Anchorview
363  */
364 EAPI const char *
365 elm_anchorview_hover_style_get(const Evas_Object *obj)
366 {
367    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
368    Widget_Data *wd = elm_widget_data_get(obj);
369    if (!wd) return NULL;
370    return wd->hover_style;
371 }
372
373 /**
374  * Stop the hover popup in the anchorview
375  *
376  * This will stop the hover popup in the anchorview if it is currently active.
377  *
378  * @param obj The anchorview object
379  *
380  * @ingroup Anchorview
381  */
382 EAPI void
383 elm_anchorview_hover_end(Evas_Object *obj)
384 {
385    ELM_CHECK_WIDTYPE(obj, widtype);
386    Widget_Data *wd = elm_widget_data_get(obj);
387    if (!wd) return;
388    if (wd->hover) evas_object_del(wd->hover);
389    if (wd->pop) evas_object_del(wd->pop);
390    wd->hover = NULL;
391    wd->pop = NULL;
392 }
393
394 /**
395  * Set bounce mode
396  *
397  * This will enable or disable the scroller bounce mode for the anchorview. See
398  * elm_scroller_bounce_set() for details
399  *
400  * @param obj The anchorview anchorview
401  * @param h_bounce Allow bounce horizontally
402  * @param v_bounce Allow bounce vertically
403  *
404  * @ingroup Anchorview
405  */
406 EAPI void
407 elm_anchorview_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce)
408 {
409    ELM_CHECK_WIDTYPE(obj, widtype);
410    Widget_Data *wd = elm_widget_data_get(obj);
411    if (!wd) return;
412    elm_scroller_bounce_set(wd->scroller, h_bounce, v_bounce);
413 }
414
415 /**
416  * Get the bounce mode
417  *
418  * @param obj The Anchorview object
419  * @param h_bounce Allow bounce horizontally
420  * @param v_bounce Allow bounce vertically
421  *
422  * @ingroup Anchorview
423  */
424 EAPI void
425 elm_anchorview_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
426 {
427    ELM_CHECK_WIDTYPE(obj, widtype);
428    Widget_Data *wd = elm_widget_data_get(obj);
429    if (!wd) return;
430    elm_scroller_bounce_get(wd->scroller, h_bounce, v_bounce);
431 }
432
433 /**
434  * This appends a custom item provider to the list for that anchorview
435  *
436  * This appends the given callback. The list is walked from beginning to end
437  * with each function called given the item href string in the text. If the
438  * function returns an object handle other than NULL (it should create an
439  * and object to do this), then this object is used to replace that item. If
440  * not the next provider is called until one provides an item object, or the
441  * default provider in anchorview does.
442  *
443  * @param obj The anchorview object
444  * @param func The function called to provide the item object
445  * @param data The data passed to @p func
446  *
447  * @ingroup Anchorview
448  */
449 EAPI void
450 elm_anchorview_item_provider_append(Evas_Object *obj, Evas_Object *(*func) (void *data, Evas_Object *anchorview, const char *item), void *data)
451 {
452    ELM_CHECK_WIDTYPE(obj, widtype);
453    Widget_Data *wd = elm_widget_data_get(obj);
454    if (!wd) return;
455    EINA_SAFETY_ON_NULL_RETURN(func);
456    Elm_Anchorview_Item_Provider *ip = calloc(1, sizeof(Elm_Anchorview_Item_Provider));
457    if (!ip) return;
458    ip->func = func;
459    ip->data = data;
460    wd->item_providers = eina_list_append(wd->item_providers, ip);
461 }
462
463 /**
464  * This prepends a custom item provider to the list for that anchorview
465  *
466  * This prepends the given callback. See elm_anchorview_item_provider_append() for
467  * more information
468  *
469  * @param obj The anchorview object
470  * @param func The function called to provide the item object
471  * @param data The data passed to @p func
472  *
473  * @ingroup Anchorview
474  */
475 EAPI void
476 elm_anchorview_item_provider_prepend(Evas_Object *obj, Evas_Object *(*func) (void *data, Evas_Object *anchorview, const char *item), void *data)
477 {
478    ELM_CHECK_WIDTYPE(obj, widtype);
479    Widget_Data *wd = elm_widget_data_get(obj);
480    if (!wd) return;
481    EINA_SAFETY_ON_NULL_RETURN(func);
482    Elm_Anchorview_Item_Provider *ip = calloc(1, sizeof(Elm_Anchorview_Item_Provider));
483    if (!ip) return;
484    ip->func = func;
485    ip->data = data;
486    wd->item_providers = eina_list_prepend(wd->item_providers, ip);
487 }
488
489 /**
490  * This removes a custom item provider to the list for that anchorview
491  *
492  * This removes the given callback. See elm_anchorview_item_provider_append() for
493  * more information
494  *
495  * @param obj The anchorview object
496  * @param func The function called to provide the item object
497  * @param data The data passed to @p func
498  *
499  * @ingroup Anchorview
500  */
501 EAPI void
502 elm_anchorview_item_provider_remove(Evas_Object *obj, Evas_Object *(*func) (void *data, Evas_Object *anchorview, const char *item), void *data)
503 {
504    ELM_CHECK_WIDTYPE(obj, widtype);
505    Widget_Data *wd = elm_widget_data_get(obj);
506    Eina_List *l;
507    Elm_Anchorview_Item_Provider *ip;
508    if (!wd) return;
509    EINA_SAFETY_ON_NULL_RETURN(func);
510    EINA_LIST_FOREACH(wd->item_providers, l, ip)
511      {
512         if ((ip->func == func) && (ip->data == data))
513           {
514              wd->item_providers = eina_list_remove_list(wd->item_providers, l);
515              free(ip);
516              return;
517           }
518      }
519 }