move elementary to trunk base. out of TMP/st.
[framework/uifw/elementary.git] / src / lib / elm_scroller.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Scroller Scroller
6  *
7  * A scroller holds a single object and "scrolls it around". This means that
8  * it allows the user to use a scrollbar (or a finger) to drag the viewable
9  * region around, allowing to move through a much larger object that is
10  * contained in the scroller. The scroiller will always have a small minimum
11  * size by default as it won't be limited by the contents of the scroller.
12  *
13  * Signals that you can add callbacks for are:
14  *
15  * edge,left - the left edge of the content has been reached
16  *
17  * edge,right - the right edge of the content has been reached
18  *
19  * edge,top - the top edge of the content has been reached
20  *
21  * edge,bottom - the bottom edge of the content has been reached
22  *
23  * scroll - the content has been scrolled (moved)
24  *
25  * scroll,anim,start - scrolling animation has started
26  *
27  * scroll,anim,stop - scrolling animation has stopped
28  *
29  * scroll,drag,start - dragging the contents around has started
30  *
31  * scroll,drag,stop - dragging the contents around has stopped
32  */
33 typedef struct _Widget_Data Widget_Data;
34
35 struct _Widget_Data
36 {
37    Evas_Object *scr;
38    Evas_Object *content;
39    const char *widget_name, *widget_base;
40    Eina_Bool min_w : 1;
41    Eina_Bool min_h : 1;
42    double pagerel_h, pagerel_v;
43    Evas_Coord pagesize_h, pagesize_v;
44 };
45
46 static const char *widtype = NULL;
47 static void _del_hook(Evas_Object *obj);
48 static void _theme_hook(Evas_Object *obj);
49 static void _show_region_hook(void *data, Evas_Object *obj);
50 static void _sizing_eval(Evas_Object *obj);
51 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
52 static void _on_focus_hook(void *data, Evas_Object *obj);
53 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
54                              Evas_Callback_Type type, void *event_info);
55
56
57 static const char SIG_SCROLL[] = "scroll";
58 static const char SIG_SCROLL_ANIM_START[] = "scroll,anim,start";
59 static const char SIG_SCROLL_ANIM_STOP[] = "scroll,anim,stop";
60 static const char SIG_SCROLL_DRAG_START[] = "scroll,drag,start";
61 static const char SIG_SCROLL_DRAG_STOP[] = "scroll,drag,stop";
62 static const char SIG_EDGE_LEFT[] = "edge,left";
63 static const char SIG_EDGE_RIGHT[] = "edge,right";
64 static const char SIG_EDGE_TOP[] = "edge,top";
65 static const char SIG_EDGE_BOTTOM[] = "edge,bottom";
66 static const Evas_Smart_Cb_Description _signals[] = {
67   {SIG_SCROLL, ""},
68   {SIG_SCROLL_ANIM_START, ""},
69   {SIG_SCROLL_ANIM_STOP, ""},
70   {SIG_SCROLL_DRAG_START, ""},
71   {SIG_SCROLL_DRAG_STOP, ""},
72   {SIG_EDGE_LEFT, ""},
73   {SIG_EDGE_RIGHT, ""},
74   {SIG_EDGE_TOP, ""},
75   {SIG_EDGE_BOTTOM, ""},
76   {NULL, NULL}
77 };
78
79 static Eina_Bool
80 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
81 {
82    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
83    Evas_Event_Key_Down *ev = event_info;
84    Widget_Data *wd = elm_widget_data_get(obj);
85    if (!wd) return EINA_FALSE;
86    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
87    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
88
89    Evas_Coord x = 0;
90    Evas_Coord y = 0;
91    Evas_Coord step_x = 0;
92    Evas_Coord step_y = 0;
93    Evas_Coord max_x = 0;
94    Evas_Coord max_y = 0;
95    Evas_Coord v_w = 0;
96    Evas_Coord v_h = 0;
97    Evas_Coord page_x = 0;
98    Evas_Coord page_y = 0;
99
100    elm_smart_scroller_child_pos_get(wd->scr, &x, &y);
101    elm_smart_scroller_step_size_get(wd->scr, &step_x, &step_y);
102    elm_smart_scroller_page_size_get(wd->scr, &page_x, &page_y);
103    elm_smart_scroller_child_viewport_size_get(wd->scr, &v_w, &v_h);
104    elm_scroller_child_size_get(obj, &max_x, &max_y);
105
106    if ((!strcmp(ev->keyname, "Left")) || (!strcmp(ev->keyname, "KP_Left")))
107      {
108         x -= step_x;
109      }
110    else if ((!strcmp(ev->keyname, "Right")) || (!strcmp(ev->keyname, "KP_Right")))
111      {
112         x += step_x;
113      }
114    else if ((!strcmp(ev->keyname, "Up"))  || (!strcmp(ev->keyname, "KP_Up")))
115      {
116         y -= step_y;
117      }
118    else if ((!strcmp(ev->keyname, "Down")) || (!strcmp(ev->keyname, "KP_Down")))
119      {
120         y += step_y;
121      }
122    else if ((!strcmp(ev->keyname, "Home")) || (!strcmp(ev->keyname, "KP_Home")))
123      {
124         y = 0;
125      }
126    else if ((!strcmp(ev->keyname, "End")) || (!strcmp(ev->keyname, "KP_End")))
127      {
128         y = max_y - v_h;
129      }
130    else if ((!strcmp(ev->keyname, "Prior")) || (!strcmp(ev->keyname, "KP_Prior")))
131      {
132         if (page_y < 0)
133           y -= -(page_y * v_h) / 100;
134         else
135            y -= page_y;
136      }
137    else if ((!strcmp(ev->keyname, "Next")) || (!strcmp(ev->keyname, "KP_Next")))
138      {
139         if (page_y < 0)
140           y += -(page_y * v_h) / 100;
141         else
142           y += page_y;
143      }
144    else return EINA_FALSE;
145
146    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
147    elm_smart_scroller_child_pos_set(wd->scr, x, y);
148    return EINA_TRUE;
149 }
150
151 static void
152 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
153 {
154    Widget_Data *wd = elm_widget_data_get(obj);
155    if (!wd) return;
156    if (elm_widget_focus_get(obj))
157      {
158         edje_object_signal_emit(wd->scr, "elm,action,focus", "elm");
159         evas_object_focus_set(wd->scr, EINA_TRUE);
160      }
161    else
162      {
163         edje_object_signal_emit(wd->scr, "elm,action,unfocus", "elm");
164         evas_object_focus_set(wd->scr, EINA_FALSE);
165      }
166 }
167
168 static void
169 _del_hook(Evas_Object *obj)
170 {
171    Widget_Data *wd = elm_widget_data_get(obj);
172    if (!wd) return;
173    free(wd);
174 }
175
176 static void
177 _theme_hook(Evas_Object *obj)
178 {
179    Widget_Data *wd = elm_widget_data_get(obj);
180    if (!wd) return;
181    if (wd->scr)
182      {
183         Evas_Object *edj;
184         const char *str;
185
186         elm_smart_scroller_object_theme_set(obj, wd->scr, 
187                                             wd->widget_name, wd->widget_base,
188                                             elm_widget_style_get(obj));
189 //        edje_object_scale_set(wd->scr, elm_widget_scale_get(obj) * _elm_config->scale);
190         edj = elm_smart_scroller_edje_object_get(wd->scr);
191         str = edje_object_data_get(edj, "focus_highlight");
192         if ((str) && (!strcmp(str, "on")))
193           elm_widget_highlight_in_theme_set(obj, EINA_TRUE);
194         else
195           elm_widget_highlight_in_theme_set(obj, EINA_FALSE);
196      }
197    _sizing_eval(obj);
198 }
199
200 static Eina_Bool
201 _elm_scroller_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
202 {
203    Widget_Data *wd = elm_widget_data_get(obj);
204    Evas_Object *cur;
205
206    if ((!wd) || (!wd->content))
207      return EINA_FALSE;
208
209    cur = wd->content;
210
211    /* Try Focus cycle in subitem */
212    if ((elm_widget_can_focus_get(cur)) || (elm_widget_child_can_focus_get(cur)))
213       return elm_widget_focus_next_get(cur, dir, next);
214
215    /* Return */
216    *next = (Evas_Object *)obj;
217    return !elm_widget_focus_get(obj);
218 }
219
220 static void
221 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
222 {
223    Widget_Data *wd = elm_widget_data_get(obj);
224    if (!wd) return;
225    edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
226          emission, source);
227 }
228
229 static void
230 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
231 {
232    Widget_Data *wd = elm_widget_data_get(obj);
233    if (!wd) return;
234    edje_object_signal_callback_add(elm_smart_scroller_edje_object_get(wd->scr),
235          emission, source, func_cb, data);
236 }
237
238 static void
239 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
240 {
241    Widget_Data *wd = elm_widget_data_get(obj);
242    edje_object_signal_callback_del_full(
243          elm_smart_scroller_edje_object_get(wd->scr), emission, source,
244          func_cb, data);
245 }
246
247 static void
248 _show_region_hook(void *data, Evas_Object *obj)
249 {
250    Widget_Data *wd = elm_widget_data_get(data);
251    Evas_Coord x, y, w, h;
252    if (!wd) return;
253    elm_widget_show_region_get(obj, &x, &y, &w, &h);
254    if (wd->scr)
255      elm_smart_scroller_child_region_show(wd->scr, x, y, w, h);
256 }
257
258 static void
259 _focus_region_hook(Evas_Object *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
260 {
261    Widget_Data *wd = elm_widget_data_get(obj);
262    if (wd->scr)
263      elm_smart_scroller_child_region_show(wd->scr, x, y, w, h);
264 }
265
266 static void
267 _sizing_eval(Evas_Object *obj)
268 {
269    Widget_Data *wd = elm_widget_data_get(obj);
270    Evas_Coord  vw, vh, minw, minh, maxw, maxh, w, h, vmw, vmh;
271    double xw, yw;
272
273    if (!wd) return;
274    evas_object_size_hint_min_get(wd->content, &minw, &minh);
275    evas_object_size_hint_max_get(wd->content, &maxw, &maxh);
276    evas_object_size_hint_weight_get(wd->content, &xw, &yw);
277    if (wd->scr)
278      {
279         elm_smart_scroller_child_viewport_size_get(wd->scr, &vw, &vh);
280         if (xw > 0.0)
281           {
282              if ((minw > 0) && (vw < minw)) vw = minw;
283              else if ((maxw > 0) && (vw > maxw)) vw = maxw;
284           }
285         else if (minw > 0) vw = minw;
286         if (yw > 0.0)
287           {
288              if ((minh > 0) && (vh < minh)) vh = minh;
289              else if ((maxh > 0) && (vh > maxh)) vh = maxh;
290           }
291         else if (minh > 0) vh = minh;
292         evas_object_resize(wd->content, vw, vh);
293         w = -1;
294         h = -1;
295         edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr), &vmw, &vmh);
296         if (wd->min_w) w = vmw + minw;
297         if (wd->min_h) h = vmh + minh;
298         evas_object_size_hint_max_get(obj, &maxw, &maxh);
299         if ((maxw > 0) && (w > maxw)) w = maxw;
300         if ((maxh > 0) && (h > maxh)) h = maxh;
301         evas_object_size_hint_min_set(obj, w, h);
302      }
303 }
304
305 static void
306 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
307 {
308    _sizing_eval(data);
309 }
310
311 static void
312 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
313 {
314    Widget_Data *wd = elm_widget_data_get(obj);
315    Evas_Object *sub = event_info;
316
317    if (!wd) return;
318    if (sub == wd->content)
319      {
320         elm_widget_on_show_region_hook_set(wd->content, NULL, NULL);
321         evas_object_event_callback_del_full (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
322            _changed_size_hints, obj);
323         wd->content = NULL;
324         _sizing_eval(obj);
325      }
326    else if (sub == wd->scr)
327      wd->scr = NULL;
328 }
329
330 static void
331 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
332 {
333    Widget_Data *wd = elm_widget_data_get(obj);
334
335    if (!wd) return;
336    if (wd->scr)
337      elm_smart_scroller_hold_set(wd->scr, 1);
338 }
339
340 static void
341 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
342 {
343    Widget_Data *wd = elm_widget_data_get(obj);
344
345    if (!wd) return;
346    if (wd->scr)
347      elm_smart_scroller_hold_set(wd->scr, 0);
348 }
349
350 static void
351 _freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
352 {
353    Widget_Data *wd = elm_widget_data_get(obj);
354
355    if (!wd) return;
356    if (wd->scr)
357      elm_smart_scroller_freeze_set(wd->scr, 1);
358 }
359
360 static void
361 _freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
362 {
363    Widget_Data *wd = elm_widget_data_get(obj);
364
365    if (!wd) return;
366    if (wd->scr)
367      elm_smart_scroller_freeze_set(wd->scr, 0);
368 }
369
370 static void
371 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
372 {
373    _sizing_eval(data);
374 }
375
376 static void
377 _edge_left(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
378 {
379    evas_object_smart_callback_call(data, SIG_EDGE_LEFT, NULL);
380 }
381
382 static void
383 _edge_right(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
384 {
385    evas_object_smart_callback_call(data, SIG_EDGE_RIGHT, NULL);
386 }
387
388 static void
389 _edge_top(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
390 {
391    evas_object_smart_callback_call(data, SIG_EDGE_TOP, NULL);
392 }
393
394 static void
395 _edge_bottom(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
396 {
397    evas_object_smart_callback_call(data, SIG_EDGE_BOTTOM, NULL);
398 }
399
400 static void
401 _scroll(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
402 {
403    evas_object_smart_callback_call(data, SIG_SCROLL, NULL);
404 }
405
406 static void
407 _scroll_anim_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
408 {
409    evas_object_smart_callback_call(data, SIG_SCROLL_ANIM_START, NULL);
410 }
411
412 static void
413 _scroll_anim_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
414 {
415    evas_object_smart_callback_call(data, SIG_SCROLL_ANIM_STOP, NULL);
416 }
417
418 static void
419 _scroll_drag_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
420 {
421    evas_object_smart_callback_call(data, SIG_SCROLL_DRAG_START, NULL);
422 }
423
424 static void
425 _scroll_drag_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
426 {
427    evas_object_smart_callback_call(data, SIG_SCROLL_DRAG_STOP, NULL);
428 }
429
430 /**
431  * Add a new scroller to the parent
432  *
433  * @param parent The parent object
434  * @return The new object or NULL if it cannot be created
435  *
436  * @ingroup Scroller
437  */
438 EAPI Evas_Object *
439 elm_scroller_add(Evas_Object *parent)
440 {
441    Evas_Object *obj;
442    Evas *e;
443    Widget_Data *wd;
444    Evas_Coord minw, minh;
445
446    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
447
448    wd = ELM_NEW(Widget_Data);
449    e = evas_object_evas_get(parent);
450    if (!e) return NULL;
451    obj = elm_widget_add(e);
452    ELM_SET_WIDTYPE(widtype, "scroller");
453    elm_widget_type_set(obj, "scroller");
454    elm_widget_sub_object_add(parent, obj);
455    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
456    elm_widget_data_set(obj, wd);
457    elm_widget_del_hook_set(obj, _del_hook);
458    elm_widget_theme_hook_set(obj, _theme_hook);
459    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
460    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
461    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
462    elm_widget_focus_next_hook_set(obj, _elm_scroller_focus_next_hook);
463    elm_widget_can_focus_set(obj, EINA_TRUE);
464    elm_widget_event_hook_set(obj, _event_hook);
465    elm_widget_focus_region_hook_set(obj, _focus_region_hook);
466
467    wd->widget_name = eina_stringshare_add("scroller");
468    wd->widget_base = eina_stringshare_add("base");
469    
470    wd->scr = elm_smart_scroller_add(e);
471    elm_smart_scroller_widget_set(wd->scr, obj);
472    _theme_hook(obj);
473    elm_widget_resize_object_set(obj, wd->scr);
474    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
475                                   _changed_size_hints, obj);
476
477    edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr), &minw, &minh);
478    evas_object_size_hint_min_set(obj, minw, minh);
479    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
480
481    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
482    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
483    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
484    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
485    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
486
487    evas_object_smart_callback_add(wd->scr, "edge,left", _edge_left, obj);
488    evas_object_smart_callback_add(wd->scr, "edge,right", _edge_right, obj);
489    evas_object_smart_callback_add(wd->scr, "edge,top", _edge_top, obj);
490    evas_object_smart_callback_add(wd->scr, "edge,bottom", _edge_bottom, obj);
491    evas_object_smart_callback_add(wd->scr, "scroll", _scroll, obj);
492    evas_object_smart_callback_add(wd->scr, "animate,start", _scroll_anim_start, obj);
493    evas_object_smart_callback_add(wd->scr, "animate,stop", _scroll_anim_stop, obj);
494    evas_object_smart_callback_add(wd->scr, "drag,start", _scroll_drag_start, obj);
495    evas_object_smart_callback_add(wd->scr, "drag,stop", _scroll_drag_stop, obj);
496
497    _sizing_eval(obj);
498
499    // TODO: convert Elementary to subclassing of Evas_Smart_Class
500    // TODO: and save some bytes, making descriptions per-class and not instance!
501    evas_object_smart_callbacks_descriptions_set(obj, _signals);
502    return obj;
503 }
504
505 Evas_Object *
506 _elm_scroller_edje_object_get(Evas_Object *obj)
507 {
508    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
509    Widget_Data *wd = elm_widget_data_get(obj);
510    if (!wd) return NULL;
511    return elm_smart_scroller_edje_object_get(wd->scr);
512 }
513
514 /**
515  * Set the content of the scroller widget (the object to be scrolled around).
516  *
517  * Once the content object is set, a previously set one will be deleted.
518  * If you want to keep that old content object, use the
519  * elm_scroller_content_unset() function.
520  *
521  * @param obj The scroller object
522  * @param content The new content object
523  *
524  * @ingroup Scroller
525  */
526 EAPI void
527 elm_scroller_content_set(Evas_Object *obj, Evas_Object *content)
528 {
529    ELM_CHECK_WIDTYPE(obj, widtype);
530    Widget_Data *wd = elm_widget_data_get(obj);
531    if (!wd) return;
532    if (wd->content == content) return;
533    if (wd->content) evas_object_del(wd->content);
534    wd->content = content;
535    if (content)
536      {
537         elm_widget_on_show_region_hook_set(content, _show_region_hook, obj);
538         elm_widget_sub_object_add(obj, content);
539         if (wd->scr)
540           elm_smart_scroller_child_set(wd->scr, content);
541         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
542                                        _changed_size_hints, obj);
543      }
544    _sizing_eval(obj);
545 }
546
547 /**
548  * Get the content of the scroller widget
549  *
550  * Return the content object which is set for this widget
551  *
552  * @param obj The slider object
553  * @return The content that is being used
554  *
555  * @ingroup Scroller
556  */
557 EAPI Evas_Object *
558 elm_scroller_content_get(const Evas_Object *obj)
559 {
560    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
561    Widget_Data *wd = elm_widget_data_get(obj);
562    if (!wd) return NULL;
563    return wd->content;
564 }
565
566 /**
567  * Unset the content of the scroller widget
568  *
569  * Unparent and return the content object which was set for this widget
570  *
571  * @param obj The slider object
572  * @return The content that was being used
573  *
574  * @ingroup Scroller
575  */
576 EAPI Evas_Object *
577 elm_scroller_content_unset(Evas_Object *obj)
578 {
579    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
580    Widget_Data *wd = elm_widget_data_get(obj);
581    Evas_Object *content;
582    if (!wd) return NULL;
583    if (!wd->content) return NULL;
584    content = wd->content;
585    elm_widget_sub_object_del(obj, wd->content);
586    edje_object_part_unswallow(wd->scr, wd->content);
587    wd->content = NULL;
588    return content;
589 }
590
591 /**
592  * Set custom theme elements for the scroller
593  * 
594  * @param obj The scroller object
595  * @param widget The widget name to use (default is "scroller")
596  * @param base The base name to use (default is "base")
597  *
598  * @ingroup Scroller
599  */
600 EAPI void
601 elm_scroller_custom_widget_base_theme_set(Evas_Object *obj, const char *widget, const char *base)
602 {
603    ELM_CHECK_WIDTYPE(obj, widtype);
604    Widget_Data *wd = elm_widget_data_get(obj);
605    if (!wd) return;
606    EINA_SAFETY_ON_NULL_RETURN(widget);
607    EINA_SAFETY_ON_NULL_RETURN(base);
608    if (eina_stringshare_replace(&wd->widget_name, widget) |
609        eina_stringshare_replace(&wd->widget_base, base))
610      _theme_hook(obj);
611 }
612
613 /**
614  * Make the scroller minimum size limited to the minimum size of the content
615  *
616  * By default the scroller will be as small as its design allows, irrespective
617  * of its content. This will make the scroller minimum size the right size
618  * horizontally and/or vertically to perfectly fit its content.
619  *
620  * @param obj The scroller object
621  * @param w Enable limiting minimum size horizontally
622  * @param h Enable limiting minimum size vertically
623  *
624  * @ingroup Scroller
625  */
626 EAPI void
627 elm_scroller_content_min_limit(Evas_Object *obj, Eina_Bool w, Eina_Bool h)
628 {
629    ELM_CHECK_WIDTYPE(obj, widtype);
630    Widget_Data *wd = elm_widget_data_get(obj);
631    if (!wd) return;
632    wd->min_w = w;
633    wd->min_h = h;
634    _sizing_eval(obj);
635 }
636
637 /**
638  * Show a specific virtual region within the scroller content object
639  *
640  * This will ensure all (or part if it does not fit) of the designated
641  * region in the virtual content object (0, 0 starting at the top-left of the
642  * virtual content object) is shown within the scroller.
643  *
644  * @param obj The scroller object
645  * @param x X coordinate of the region
646  * @param y Y coordinate of the region
647  * @param w Width of the region
648  * @param h Height of the region
649  *
650  * @ingroup Scroller
651  */
652 EAPI void
653 elm_scroller_region_show(Evas_Object *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
654 {
655    ELM_CHECK_WIDTYPE(obj, widtype);
656    Widget_Data *wd = elm_widget_data_get(obj);
657    if ((!wd) || (!wd->scr)) return;
658    elm_smart_scroller_child_region_show(wd->scr, x, y, w, h);
659 }
660
661 /**
662  * Set the scroller scrollbar policy
663  *
664  * This sets the scrollbar visibility policy for the given scroller.
665  * ELM_SMART_SCROLLER_POLICY_AUTO means the scrollber is made visible if it
666  * is needed, and otherwise kept hidden. ELM_SMART_SCROLLER_POLICY_ON turns
667  * it on all the time, and ELM_SMART_SCROLLER_POLICY_OFF always keeps it off.
668  * This applies respectively for the horizontal and vertical scrollbars.
669  *
670  * @param obj The scroller object
671  * @param policy_h Horizontal scrollbar policy
672  * @param policy_v Vertical scrollbar policy
673  *
674  * @ingroup Scroller
675  */
676 EAPI void
677 elm_scroller_policy_set(Evas_Object *obj, Elm_Scroller_Policy policy_h, Elm_Scroller_Policy policy_v)
678 {
679    ELM_CHECK_WIDTYPE(obj, widtype);
680    Widget_Data *wd = elm_widget_data_get(obj);
681    const Elm_Scroller_Policy map[3] =
682      {
683         ELM_SMART_SCROLLER_POLICY_AUTO,
684           ELM_SMART_SCROLLER_POLICY_ON,
685           ELM_SMART_SCROLLER_POLICY_OFF
686      };
687    if ((!wd) || (!wd->scr)) return;
688    if ((policy_h >= 3) || (policy_v >= 3)) return;
689    elm_smart_scroller_policy_set(wd->scr, map[policy_h], map[policy_v]);
690 }
691
692 EAPI void
693 elm_scroller_policy_get(const Evas_Object *obj, Elm_Scroller_Policy *policy_h, Elm_Scroller_Policy *policy_v)
694 {
695    ELM_CHECK_WIDTYPE(obj, widtype);
696    Widget_Data *wd = elm_widget_data_get(obj);
697    if ((!wd) || (!wd->scr)) return;
698    elm_smart_scroller_policy_get(wd->scr,
699                                  (Elm_Smart_Scroller_Policy *) policy_h,
700                                  (Elm_Smart_Scroller_Policy *) policy_v);
701 }
702
703 /**
704  * Get the currently visible content region
705  *
706  * This gets the current region in the content object that is visible through
707  * the scroller. Also see elm_scroller_region_show(). The region co-ordinates
708  * are returned in the @p x, @p y, @p w, @p h values pointed to.
709  *
710  * @param obj The scroller object
711  * @param x X coordinate of the region
712  * @param y Y coordinate of the region
713  * @param w Width of the region
714  * @param h Height of the region
715  *
716  * @ingroup Scroller
717  */
718 EAPI void
719 elm_scroller_region_get(const Evas_Object *obj, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h)
720 {
721    ELM_CHECK_WIDTYPE(obj, widtype);
722    Widget_Data *wd = elm_widget_data_get(obj);
723    if ((!wd) || (!wd->scr)) return;
724    if ((x) || (y)) elm_smart_scroller_child_pos_get(wd->scr, x, y);
725    if ((w) || (h)) elm_smart_scroller_child_viewport_size_get(wd->scr, w, h);
726 }
727
728 /**
729  * Get the size of the content child object
730  *
731  * This gets the size of the child object of the scroller. Actually the
732  * content of a scroller doesn't specifically need to be an actual object
733  * as it can be virtual and defined purely by callbacks.
734  *
735  * @param obj The scroller object
736  * @param w Width return
737  * @param h Height return
738  *
739  * @ingroup Scroller
740  */
741 EAPI void
742 elm_scroller_child_size_get(const Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
743 {
744    ELM_CHECK_WIDTYPE(obj, widtype);
745    Widget_Data *wd = elm_widget_data_get(obj);
746    if (!wd) return;
747    evas_object_geometry_get(wd->content, NULL, NULL, w, h);
748 }
749
750 /**
751  * Set bouncing behavior
752  *
753  * When scrolling, the scroller may "bounce" when reaching an edge of the child
754  * object. This is a visual way to indicate the end has been reached. This is
755  * enabled by default for both axes. This will set if it is enabled for that
756  * axis with the boolean parameters for each axis.
757  *
758  * @param obj The scroller object
759  * @param h_bounce Will the scroller bounce horizontally or not
760  * @param v_bounce Will the scroller bounce vertically or not
761  *
762  * @ingroup Scroller
763  */
764 EAPI void
765 elm_scroller_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce)
766 {
767    ELM_CHECK_WIDTYPE(obj, widtype);
768    Widget_Data *wd = elm_widget_data_get(obj);
769    if ((!wd) || (!wd->scr)) return;
770    elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce);
771 }
772
773 /**
774  * Get the bounce mode
775  *
776  * @param obj The Scroller object
777  * @param h_bounce Allow bounce horizontally
778  * @param v_bounce Allow bounce vertically
779  *
780  * @ingroup Scroller
781  */
782 EAPI void
783 elm_scroller_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
784 {
785    ELM_CHECK_WIDTYPE(obj, widtype);
786    Widget_Data *wd = elm_widget_data_get(obj);
787    if (!wd) return;
788    elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce);
789 }
790
791 /**
792  * Set scroll page size relative to viewport size.
793  *
794  * The scroller is capable of limiting scrolling by the user to "pages". That
795  * is to jump by and only show a "whole page" at a time as if the continuous
796  * area of the scroller content is split into page sized pieces. This sets
797  * the size of a page relative to the viewport of the scroller. 1.0 is "1
798  * viewport" is size (horizontally or vertically). 0.0 turns it off in that
799  * axis. This is mutually exclusive with page size
800  * (see elm_scroller_page_size_set()  for more information). Likewise 0.5
801  * is "half a viewport". Sane usable valus are normally between 0.0 and 1.0
802  * including 1.0. If you only want 1 axis to be page "limited", use 0.0 for
803  * the other axis.
804  *
805  * @param obj The scroller object
806  * @param h_pagerel The horizontal page relative size
807  * @param v_pagerel The vertical page relative size
808  *
809  * @ingroup Scroller
810  */
811 EAPI void
812 elm_scroller_page_relative_set(Evas_Object *obj, double h_pagerel, double v_pagerel)
813 {
814    ELM_CHECK_WIDTYPE(obj, widtype);
815    Widget_Data *wd = elm_widget_data_get(obj);
816    if (!wd) return;
817    wd->pagerel_h = h_pagerel;
818    wd->pagerel_v = v_pagerel;
819    if (wd->scr)
820      elm_smart_scroller_paging_set(wd->scr, wd->pagerel_h, wd->pagerel_v,
821                                    wd->pagesize_h, wd->pagesize_v);
822 }
823
824 /**
825  * Set scroll page size.
826  *
827  * See also elm_scroller_page_relative_set(). This, instead of a page size
828  * being relative to the viewport, sets it to an absolute fixed value, with
829  * 0 turning it off for that axis.
830  *
831  * @param obj The scroller object
832  * @param h_pagesize The horizontal page size
833  * @param v_pagesize The vertical page size
834  *
835  * @ingroup Scroller
836  */
837 EAPI void
838 elm_scroller_page_size_set(Evas_Object *obj, Evas_Coord h_pagesize, Evas_Coord v_pagesize)
839 {
840    ELM_CHECK_WIDTYPE(obj, widtype);
841    Widget_Data *wd = elm_widget_data_get(obj);
842    if (!wd) return;
843    wd->pagesize_h = h_pagesize;
844    wd->pagesize_v = v_pagesize;
845    if (wd->scr)
846      elm_smart_scroller_paging_set(wd->scr, wd->pagerel_h, wd->pagerel_v,
847                                    wd->pagesize_h, wd->pagesize_v);
848 }
849
850 /**
851  * Show a specific virtual region within the scroller content object.
852  *
853  * This will ensure all (or part if it does not fit) of the designated
854  * region in the virtual content object (0, 0 starting at the top-left of the
855  * virtual content object) is shown within the scroller. Unlike
856  * elm_scroller_region_show(), this allow the scroller to "smoothly slide"
857  * to this location (if configuration in general calls for transitions). It
858  * may not jump immediately to the new location and make take a while and
859  * show other content along the way.
860  *
861  * @param obj The scroller object
862  * @param x X coordinate of the region
863  * @param y Y coordinate of the region
864  * @param w Width of the region
865  * @param h Height of the region
866  *
867  * @ingroup Scroller
868  */
869 EAPI void
870 elm_scroller_region_bring_in(Evas_Object *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
871 {
872    ELM_CHECK_WIDTYPE(obj, widtype);
873    Widget_Data *wd = elm_widget_data_get(obj);
874    if ((!wd) || (!wd->scr)) return;
875    elm_smart_scroller_region_bring_in(wd->scr, x, y, w, h);
876 }
877
878
879 /**
880  * Set event propagation on a scroller
881  *
882  * This enables or disabled event propagation from the scroller content to
883  * the scroller and its parent. By default event propagation is disabled.
884  * 
885  * @param obj The scroller object
886  * @param propagation If propagation is enabled or not
887  *
888  * @ingroup Scroller
889  */
890 EAPI void
891 elm_scroller_propagate_events_set(Evas_Object *obj, Eina_Bool propagation)
892 {
893    ELM_CHECK_WIDTYPE(obj, widtype);
894    Widget_Data *wd = elm_widget_data_get(obj);
895    if (!wd) return;
896
897    evas_object_propagate_events_set(wd->scr, propagation);
898 }
899
900 /**
901  * Get event propagation for a scroller
902  *
903  * This gets the event propagation for a scroller. See 
904  * elm_scroller_propagate_events_set() for more information
905  * 
906  * @param obj The scroller object
907  * @return The propagation state
908  *
909  * @ingroup Scroller
910  */
911 EAPI Eina_Bool
912 elm_scroller_propagate_events_get(const Evas_Object *obj)
913 {
914    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
915    Widget_Data *wd = elm_widget_data_get(obj);
916    if (!wd) return EINA_FALSE;
917
918    return evas_object_propagate_events_get(wd->scr);
919 }