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