photocam: apply key binding
[platform/upstream/elementary.git] / src / lib / elm_photocam.c
1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4
5 #include <Elementary.h>
6
7 #include "elm_priv.h"
8 #include "elm_widget_photocam.h"
9 #include "elm_interface_scrollable.h"
10
11 #define MY_PAN_CLASS ELM_OBJ_PHOTOCAM_PAN_CLASS
12
13 #define MY_PAN_CLASS_NAME "Elm_Photocam_Pan"
14 #define MY_PAN_CLASS_NAME_LEGACY "elm_photocam_pan"
15
16 #define MY_CLASS ELM_OBJ_PHOTOCAM_CLASS
17
18 #define MY_CLASS_NAME "Elm_Photocam"
19 #define MY_CLASS_NAME_LEGACY "elm_photocam"
20
21 /*
22  * TODO (maybe - optional future stuff):
23  *
24  * 1. wrap photo in theme edje so u can have styling around photo (like white
25  *    photo bordering).
26  * 2. exif handling
27  * 3. rotation flags in exif handling (nasty! should have rot in evas)
28  */
29
30 static const char SIG_CLICKED[] = "clicked";
31 static const char SIG_PRESS[] = "press";
32 static const char SIG_LONGPRESSED[] = "longpressed";
33 static const char SIG_CLICKED_DOUBLE[] = "clicked,double";
34 static const char SIG_LOAD[] = "load";
35 static const char SIG_LOADED[] = "loaded";
36 static const char SIG_LOAD_DETAIL[] = "load,detail";
37 static const char SIG_LOADED_DETAIL[] = "loaded,detail";
38 static const char SIG_ZOOM_START[] = "zoom,start";
39 static const char SIG_ZOOM_STOP[] = "zoom,stop";
40 static const char SIG_ZOOM_CHANGE[] = "zoom,change";
41 static const char SIG_SCROLL[] = "scroll";
42 static const char SIG_SCROLL_ANIM_START[] = "scroll,anim,start";
43 static const char SIG_SCROLL_ANIM_STOP[] = "scroll,anim,stop";
44 static const char SIG_SCROLL_DRAG_START[] = "scroll,drag,start";
45 static const char SIG_SCROLL_DRAG_STOP[] = "scroll,drag,stop";
46 static const char SIG_DOWNLOAD_START[] = "download,start";
47 static const char SIG_DOWNLOAD_PROGRESS[] = "download,progress";
48 static const char SIG_DOWNLOAD_DONE[] = "download,done";
49 static const char SIG_DOWNLOAD_ERROR[] = "download,error";
50 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
51    {SIG_CLICKED, ""},
52    {SIG_PRESS, ""},
53    {SIG_LONGPRESSED, ""},
54    {SIG_CLICKED_DOUBLE, ""},
55    {SIG_LOAD, ""},
56    {SIG_LOADED, ""},
57    {SIG_LOAD_DETAIL, ""},
58    {SIG_LOADED_DETAIL, ""},
59    {SIG_ZOOM_START, ""},
60    {SIG_ZOOM_STOP, ""},
61    {SIG_ZOOM_CHANGE, ""},
62    {SIG_SCROLL, ""},
63    {SIG_SCROLL_ANIM_START, ""},
64    {SIG_SCROLL_ANIM_STOP, ""},
65    {SIG_SCROLL_DRAG_START, ""},
66    {SIG_SCROLL_DRAG_STOP, ""},
67    {SIG_DOWNLOAD_START, ""},
68    {SIG_DOWNLOAD_PROGRESS, ""},
69    {SIG_DOWNLOAD_DONE, ""},
70    {SIG_DOWNLOAD_ERROR, ""},
71    {SIG_WIDGET_FOCUSED, ""}, /**< handled by elm_widget */
72    {SIG_WIDGET_UNFOCUSED, ""}, /**< handled by elm_widget */
73    {NULL, NULL}
74 };
75
76 static Eina_Bool _key_action_move(Evas_Object *obj, const char *params);
77 static Eina_Bool _key_action_zoom(Evas_Object *obj, const char *params);
78
79 static const Elm_Action key_actions[] = {
80    {"move", _key_action_move},
81    {"zoom", _key_action_zoom},
82    {NULL, NULL}
83 };
84
85 static inline void
86 _photocam_image_file_set(Evas_Object *obj, Elm_Photocam_Data *sd)
87 {
88    if (sd->f)
89      evas_object_image_mmap_set(obj, sd->f, NULL);
90    else
91      evas_object_image_file_set(obj, sd->file, NULL);
92 }
93
94 static void
95 _sizing_eval(Evas_Object *obj)
96 {
97    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
98    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
99
100    evas_object_size_hint_max_get
101      (wd->resize_obj, &maxw, &maxh);
102    evas_object_size_hint_min_set(obj, minw, minh);
103    evas_object_size_hint_max_set(obj, maxw, maxh);
104 }
105
106 static void
107 _calc_job_cb(void *data)
108 {
109    Evas_Object *obj = data;
110    ELM_PHOTOCAM_DATA_GET(obj, sd);
111    Evas_Coord minw, minh;
112
113    minw = sd->size.w;
114    minh = sd->size.h;
115    if (sd->resized)
116      {
117         sd->resized = EINA_FALSE;
118         if (sd->mode != ELM_PHOTOCAM_ZOOM_MODE_MANUAL)
119           {
120              double tz = sd->zoom;
121              sd->zoom = 0.0;
122              elm_photocam_zoom_set(obj, tz);
123           }
124      }
125    if ((minw != sd->minw) || (minh != sd->minh))
126      {
127         sd->minw = minw;
128         sd->minh = minh;
129
130         evas_object_smart_callback_call(sd->pan_obj, "changed", NULL);
131         _sizing_eval(obj);
132      }
133    sd->calc_job = NULL;
134    evas_object_smart_changed(sd->pan_obj);
135 }
136
137 EOLIAN static void
138 _elm_photocam_pan_evas_smart_move(Eo *obj EINA_UNUSED, Elm_Photocam_Pan_Data *psd, Evas_Coord x EINA_UNUSED, Evas_Coord y EINA_UNUSED)
139 {
140    ecore_job_del(psd->wsd->calc_job);
141    psd->wsd->calc_job = ecore_job_add(_calc_job_cb, psd->wobj);
142 }
143
144 EOLIAN static void
145 _elm_photocam_pan_evas_smart_resize(Eo *obj, Elm_Photocam_Pan_Data *psd, Evas_Coord w, Evas_Coord h)
146 {
147    Evas_Coord ow, oh;
148
149    evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
150    if ((ow == w) && (oh == h)) return;
151
152    psd->wsd->resized = EINA_TRUE;
153    ecore_job_del(psd->wsd->calc_job);
154    psd->wsd->calc_job = ecore_job_add(_calc_job_cb, psd->wobj);
155 }
156
157 static void
158 _image_place(Evas_Object *obj,
159              Evas_Coord px,
160              Evas_Coord py,
161              Evas_Coord ox,
162              Evas_Coord oy,
163              Evas_Coord ow,
164              Evas_Coord oh)
165 {
166    Evas_Coord ax, ay, gw, gh;
167
168    ELM_PHOTOCAM_DATA_GET(obj, sd);
169
170    ax = 0;
171    ay = 0;
172    gw = sd->size.w;
173    gh = sd->size.h;
174    if (!sd->zoom_g_layer)
175      {
176         if (ow > gw) ax = (ow - gw) / 2;
177         if (oh > gh) ay = (oh - gh) / 2;
178      }
179    evas_object_move(sd->img, ox + 0 - px + ax, oy + 0 - py + ay);
180    evas_object_resize(sd->img, gw, gh);
181
182    if (sd->show.show)
183      {
184         sd->show.show = EINA_FALSE;
185         eo_do(obj, elm_interface_scrollable_content_region_show
186               (sd->show.x, sd->show.y, sd->show.w, sd->show.h));
187      }
188 }
189
190 static void
191 _grid_load(Evas_Object *obj,
192            Elm_Phocam_Grid *g)
193 {
194    int x, y;
195    Evas_Coord ox, oy, ow, oh, cvx, cvy, cvw, cvh, gw, gh, tx, ty;
196
197    ELM_PHOTOCAM_DATA_GET(obj, sd);
198    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
199
200    evas_object_geometry_get(sd->pan_obj, &ox, &oy, &ow, &oh);
201    evas_output_viewport_get(evas_object_evas_get(obj), &cvx, &cvy, &cvw, &cvh);
202
203    gw = sd->size.w;
204    gh = sd->size.h;
205    for (y = 0; y < g->gh; y++)
206      {
207         for (x = 0; x < g->gw; x++)
208           {
209              int tn, xx, yy, ww, hh;
210              Eina_Bool visible = EINA_FALSE;
211
212              tn = (y * g->gw) + x;
213              xx = g->grid[tn].out.x;
214              yy = g->grid[tn].out.y;
215              ww = g->grid[tn].out.w;
216              hh = g->grid[tn].out.h;
217              if ((gw != g->w) && (g->w > 0))
218                {
219                   tx = xx;
220                   xx = (gw * xx) / g->w;
221                   ww = ((gw * (tx + ww)) / g->w) - xx;
222                }
223              if ((gh != g->h) && (g->h > 0))
224                {
225                   ty = yy;
226                   yy = (gh * yy) / g->h;
227                   hh = ((gh * (ty + hh)) / g->h) - yy;
228                }
229              if (ELM_RECTS_INTERSECT(xx - sd->pan_x + ox,
230                                      yy - sd->pan_y + oy,
231                                      ww, hh, cvx, cvy, cvw, cvh))
232                visible = EINA_TRUE;
233              if ((visible) && (!g->grid[tn].have) && (!g->grid[tn].want))
234                {
235                   g->grid[tn].want = 1;
236                   evas_object_hide(g->grid[tn].img);
237                   evas_object_image_file_set(g->grid[tn].img, NULL, NULL);
238                   evas_object_image_load_scale_down_set
239                     (g->grid[tn].img, g->zoom);
240                   evas_object_image_load_region_set
241                     (g->grid[tn].img, g->grid[tn].src.x, g->grid[tn].src.y,
242                     g->grid[tn].src.w, g->grid[tn].src.h);
243                   _photocam_image_file_set(g->grid[tn].img, sd);
244                   evas_object_image_preload(g->grid[tn].img, 0);
245                   sd->preload_num++;
246                   if (sd->preload_num == 1)
247                     {
248                        edje_object_signal_emit
249                          (wd->resize_obj,
250                          "elm,state,busy,start", "elm");
251                        evas_object_smart_callback_call
252                          (obj, SIG_LOAD_DETAIL, NULL);
253                     }
254                }
255              else if ((g->grid[tn].want) && (!visible))
256                {
257                   sd->preload_num--;
258                   if (!sd->preload_num)
259                     {
260                        edje_object_signal_emit
261                          (wd->resize_obj,
262                          "elm,state,busy,stop", "elm");
263                        evas_object_smart_callback_call
264                          (obj, SIG_LOADED_DETAIL, NULL);
265                     }
266                   g->grid[tn].want = 0;
267                   evas_object_hide(g->grid[tn].img);
268                   evas_object_image_preload(g->grid[tn].img, 1);
269                   evas_object_image_file_set(g->grid[tn].img, NULL, NULL);
270                }
271              else if ((g->grid[tn].have) && (!visible))
272                {
273                   g->grid[tn].have = 0;
274                   evas_object_hide(g->grid[tn].img);
275                   evas_object_image_preload(g->grid[tn].img, 1);
276                   evas_object_image_file_set(g->grid[tn].img, NULL, NULL);
277                }
278           }
279      }
280 }
281
282 static void
283 _grid_place(Evas_Object *obj,
284             Elm_Phocam_Grid *g,
285             Evas_Coord px,
286             Evas_Coord py,
287             Evas_Coord ox,
288             Evas_Coord oy,
289             Evas_Coord ow,
290             Evas_Coord oh)
291 {
292    Evas_Coord ax, ay, gw, gh, tx, ty;
293    int x, y;
294
295    ELM_PHOTOCAM_DATA_GET(obj, sd);
296
297    ax = 0;
298    ay = 0;
299    gw = sd->size.w;
300    gh = sd->size.h;
301    if (!sd->zoom_g_layer)
302      {
303         if (ow > gw) ax = (ow - gw) / 2;
304         if (oh > gh) ay = (oh - gh) / 2;
305      }
306    for (y = 0; y < g->gh; y++)
307      {
308         for (x = 0; x < g->gw; x++)
309           {
310              int tn, xx, yy, ww, hh;
311
312              tn = (y * g->gw) + x;
313              xx = g->grid[tn].out.x;
314              yy = g->grid[tn].out.y;
315              ww = g->grid[tn].out.w;
316              hh = g->grid[tn].out.h;
317              if ((gw != g->w) && (g->w > 0))
318                {
319                   tx = xx;
320                   xx = (gw * xx) / g->w;
321                   ww = ((gw * (tx + ww)) / g->w) - xx;
322                }
323              if ((gh != g->h) && (g->h > 0))
324                {
325                   ty = yy;
326                   yy = (gh * yy) / g->h;
327                   hh = ((gh * (ty + hh)) / g->h) - yy;
328                }
329              evas_object_move(g->grid[tn].img,
330                               ox + xx - px + ax,
331                               oy + yy - py + ay);
332              evas_object_resize(g->grid[tn].img, ww, hh);
333           }
334      }
335 }
336
337 EOLIAN static void
338 _elm_photocam_pan_evas_smart_calculate(Eo *obj, Elm_Photocam_Pan_Data *psd)
339 {
340    Elm_Phocam_Grid *g;
341    Eina_List *l;
342    Evas_Coord ox, oy, ow, oh;
343
344    ELM_WIDGET_DATA_GET_OR_RETURN(psd->wobj, wd);
345
346    evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
347    _image_place(
348        wd->obj, psd->wsd->pan_x, psd->wsd->pan_y,
349        ox - psd->wsd->g_layer_zoom.imx, oy - psd->wsd->g_layer_zoom.imy, ow,
350        oh);
351
352    EINA_LIST_FOREACH(psd->wsd->grids, l, g)
353      {
354         _grid_load(wd->obj, g);
355         _grid_place(
356               wd->obj, g, psd->wsd->pan_x,
357              psd->wsd->pan_y, ox - psd->wsd->g_layer_zoom.imx,
358              oy - psd->wsd->g_layer_zoom.imy, ow, oh);
359      }
360 }
361
362 EOLIAN static void
363 _elm_photocam_pan_elm_pan_pos_set(Eo *obj, Elm_Photocam_Pan_Data *psd, Evas_Coord x, Evas_Coord y)
364 {
365    if ((x == psd->wsd->pan_x) && (y == psd->wsd->pan_y)) return;
366    psd->wsd->pan_x = x;
367    psd->wsd->pan_y = y;
368    evas_object_smart_changed(obj);
369 }
370
371 EOLIAN static void
372 _elm_photocam_pan_elm_pan_pos_get(Eo *obj EINA_UNUSED, Elm_Photocam_Pan_Data *psd, Evas_Coord *x, Evas_Coord *y)
373 {
374    if (x) *x = psd->wsd->pan_x;
375    if (y) *y = psd->wsd->pan_y;
376 }
377
378 EOLIAN static void
379 _elm_photocam_pan_elm_pan_pos_max_get(Eo *obj, Elm_Photocam_Pan_Data *psd, Evas_Coord *x, Evas_Coord *y)
380 {
381    Evas_Coord ow, oh;
382
383    evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
384    ow = psd->wsd->minw - ow;
385    if (ow < 0) ow = 0;
386    oh = psd->wsd->minh - oh;
387    if (oh < 0) oh = 0;
388    if (x) *x = ow;
389    if (y) *y = oh;
390 }
391
392 EOLIAN static void
393 _elm_photocam_pan_elm_pan_pos_min_get(Eo *obj EINA_UNUSED, Elm_Photocam_Pan_Data *_pd EINA_UNUSED, Evas_Coord *x, Evas_Coord *y)
394 {
395    if (x) *x = 0;
396    if (y) *y = 0;
397 }
398
399 EOLIAN static void
400 _elm_photocam_pan_elm_pan_content_size_get(Eo *obj EINA_UNUSED, Elm_Photocam_Pan_Data *psd, Evas_Coord *w, Evas_Coord *h)
401 {
402    if (w) *w = psd->wsd->minw;
403    if (h) *h = psd->wsd->minh;
404 }
405
406 EOLIAN static void
407 _elm_photocam_pan_eo_base_destructor(Eo *obj, Elm_Photocam_Pan_Data *psd)
408 {
409    eo_data_unref(psd->wobj, psd->wsd);
410    eo_do_super(obj, MY_PAN_CLASS, eo_destructor());
411 }
412
413 static void
414 _elm_photocam_pan_class_constructor(Eo_Class *klass)
415 {
416    evas_smart_legacy_type_register(MY_PAN_CLASS_NAME_LEGACY, klass);
417 }
418
419 #include "elm_photocam_pan.eo.c"
420
421 static int
422 _nearest_pow2_get(int num)
423 {
424    unsigned int n = num - 1;
425
426    n |= n >> 1;
427    n |= n >> 2;
428    n |= n >> 4;
429    n |= n >> 8;
430    n |= n >> 16;
431
432    return n + 1;
433 }
434
435 static void
436 _grid_clear(Evas_Object *obj,
437             Elm_Phocam_Grid *g)
438 {
439    int x, y;
440
441    ELM_PHOTOCAM_DATA_GET(obj, sd);
442    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
443
444    if (!g->grid) return;
445    for (y = 0; y < g->gh; y++)
446      {
447         for (x = 0; x < g->gw; x++)
448           {
449              int tn;
450
451              tn = (y * g->gw) + x;
452              evas_object_del(g->grid[tn].img);
453              if (g->grid[tn].want)
454                {
455                   sd->preload_num--;
456                   if (!sd->preload_num)
457                     {
458                        edje_object_signal_emit
459                          (wd->resize_obj,
460                          "elm,state,busy,stop", "elm");
461                        evas_object_smart_callback_call
462                          (obj, SIG_LOAD_DETAIL, NULL);
463                     }
464                }
465           }
466      }
467
468    ELM_SAFE_FREE(g->grid, free);
469    g->gw = 0;
470    g->gh = 0;
471 }
472
473 static void
474 _tile_preloaded_cb(void *data,
475                    Evas *e EINA_UNUSED,
476                    Evas_Object *o EINA_UNUSED,
477                    void *event_info EINA_UNUSED)
478 {
479    Elm_Photocam_Grid_Item *git = data;
480    ELM_PHOTOCAM_DATA_GET(git->obj, sd);
481    ELM_WIDGET_DATA_GET_OR_RETURN(git->obj, wd);
482
483    if (git->want)
484      {
485         git->want = 0;
486         evas_object_show(git->img);
487         git->have = 1;
488         sd->preload_num--;
489         if (!sd->preload_num)
490           {
491              edje_object_signal_emit
492                (wd->resize_obj, "elm,state,busy,stop",
493                "elm");
494              evas_object_smart_callback_call
495                (wd->obj, SIG_LOADED_DETAIL, NULL);
496           }
497      }
498 }
499
500 static int
501 _grid_zoom_calc(double zoom)
502 {
503    int z = zoom;
504
505    if (z < 1) z = 1;
506    return _nearest_pow2_get(z);
507 }
508
509 static Elm_Phocam_Grid *
510 _grid_create(Evas_Object *obj)
511 {
512    int x, y;
513    Elm_Phocam_Grid *g;
514
515    ELM_PHOTOCAM_DATA_GET(obj, sd);
516
517    g = calloc(1, sizeof(Elm_Phocam_Grid));
518    if (!g) return NULL;
519
520    g->zoom = _grid_zoom_calc(sd->zoom);
521    g->tsize = sd->tsize;
522    g->iw = sd->size.imw;
523    g->ih = sd->size.imh;
524
525    g->w = g->iw / g->zoom;
526    g->h = g->ih / g->zoom;
527    if (g->zoom >= 8)
528      {
529         free(g);
530
531         return NULL;
532      }
533    if (sd->do_region)
534      {
535         g->gw = (g->w + g->tsize - 1) / g->tsize;
536         g->gh = (g->h + g->tsize - 1) / g->tsize;
537      }
538    else
539      {
540         g->gw = 1;
541         g->gh = 1;
542      }
543
544    g->grid = calloc(1, sizeof(Elm_Photocam_Grid_Item) * g->gw * g->gh);
545    if (!g->grid)
546      {
547         g->gw = 0;
548         g->gh = 0;
549
550         return g;
551      }
552
553    for (y = 0; y < g->gh; y++)
554      {
555         for (x = 0; x < g->gw; x++)
556           {
557              int tn;
558
559              tn = (y * g->gw) + x;
560              g->grid[tn].src.x = x * g->tsize;
561              if (x == (g->gw - 1))
562                g->grid[tn].src.w = g->w - ((g->gw - 1) * g->tsize);
563              else
564                g->grid[tn].src.w = g->tsize;
565              g->grid[tn].src.y = y * g->tsize;
566              if (y == (g->gh - 1))
567                g->grid[tn].src.h = g->h - ((g->gh - 1) * g->tsize);
568              else
569                g->grid[tn].src.h = g->tsize;
570
571              g->grid[tn].out.x = g->grid[tn].src.x;
572              g->grid[tn].out.y = g->grid[tn].src.y;
573              g->grid[tn].out.w = g->grid[tn].src.w;
574              g->grid[tn].out.h = g->grid[tn].src.h;
575
576              g->grid[tn].obj = obj;
577              g->grid[tn].img =
578                evas_object_image_add(evas_object_evas_get(obj));
579              evas_object_image_load_orientation_set(g->grid[tn].img, EINA_TRUE);
580              evas_object_image_scale_hint_set
581                (g->grid[tn].img, EVAS_IMAGE_SCALE_HINT_DYNAMIC);
582              evas_object_pass_events_set(g->grid[tn].img, EINA_TRUE);
583
584              /* XXX: check this */
585              evas_object_smart_member_add(g->grid[tn].img, sd->pan_obj);
586              elm_widget_sub_object_add(obj, g->grid[tn].img);
587              evas_object_image_filled_set(g->grid[tn].img, 1);
588              evas_object_event_callback_add
589                (g->grid[tn].img, EVAS_CALLBACK_IMAGE_PRELOADED,
590                _tile_preloaded_cb, &(g->grid[tn]));
591           }
592      }
593
594    return g;
595 }
596
597 static void
598 _grid_clear_all(Evas_Object *obj)
599 {
600    Elm_Phocam_Grid *g;
601
602    ELM_PHOTOCAM_DATA_GET(obj, sd);
603
604    EINA_LIST_FREE(sd->grids, g)
605      {
606         _grid_clear(obj, g);
607         free(g);
608      }
609 }
610
611 static void
612 _smooth_update(Evas_Object *obj)
613 {
614    Elm_Phocam_Grid *g;
615    int x, y;
616    Eina_List *l;
617
618    ELM_PHOTOCAM_DATA_GET(obj, sd);
619
620    EINA_LIST_FOREACH(sd->grids, l, g)
621      {
622         for (y = 0; y < g->gh; y++)
623           {
624              for (x = 0; x < g->gw; x++)
625                {
626                   int tn;
627
628                   tn = (y * g->gw) + x;
629                   evas_object_image_smooth_scale_set
630                     (g->grid[tn].img, (!sd->no_smooth));
631                }
632           }
633      }
634
635    evas_object_image_smooth_scale_set(sd->img, (!sd->no_smooth));
636 }
637
638 static void
639 _grid_raise(Elm_Phocam_Grid *g)
640 {
641    int x, y;
642
643    for (y = 0; y < g->gh; y++)
644      {
645         for (x = 0; x < g->gw; x++)
646           {
647              int tn;
648
649              tn = (y * g->gw) + x;
650              evas_object_raise(g->grid[tn].img);
651           }
652      }
653 }
654
655 static Eina_Bool
656 _scroll_timeout_cb(void *data)
657 {
658    ELM_PHOTOCAM_DATA_GET(data, sd);
659
660    sd->no_smooth--;
661    if (!sd->no_smooth) _smooth_update(data);
662
663    sd->scr_timer = NULL;
664
665    return ECORE_CALLBACK_CANCEL;
666 }
667
668 static void
669 _main_img_preloaded_cb(void *data,
670                        Evas *e EINA_UNUSED,
671                        Evas_Object *o EINA_UNUSED,
672                        void *event_info EINA_UNUSED)
673 {
674    Evas_Object *obj = data;
675    Elm_Phocam_Grid *g;
676
677    ELM_PHOTOCAM_DATA_GET(data, sd);
678    ELM_WIDGET_DATA_GET_OR_RETURN(data, wd);
679
680    evas_object_show(sd->img);
681    sd->main_load_pending = 0;
682    g = _grid_create(obj);
683    if (g)
684      {
685         sd->grids = eina_list_prepend(sd->grids, g);
686         _grid_load(obj, g);
687      }
688    ecore_job_del(sd->calc_job);
689    sd->calc_job = ecore_job_add(_calc_job_cb, data);
690    evas_object_smart_callback_call(data, SIG_LOADED, NULL);
691    sd->preload_num--;
692    if (!sd->preload_num)
693      {
694         edje_object_signal_emit
695           (wd->resize_obj, "elm,state,busy,stop", "elm");
696         evas_object_smart_callback_call(obj, SIG_LOADED_DETAIL, NULL);
697      }
698 }
699
700 static Eina_Bool
701 _zoom_do(Evas_Object *obj,
702          double t)
703 {
704    Evas_Coord xx, yy, ow = 0, oh = 0;
705
706    ELM_PHOTOCAM_DATA_GET(obj, sd);
707
708    sd->size.w = (sd->size.ow * (1.0 - t)) + (sd->size.nw * t);
709    sd->size.h = (sd->size.oh * (1.0 - t)) + (sd->size.nh * t);
710    eo_do(obj, elm_interface_scrollable_content_viewport_size_get(&ow, &oh));
711    xx = (sd->size.spos.x * sd->size.w) - (ow / 2);
712    yy = (sd->size.spos.y * sd->size.h) - (oh / 2);
713    if (xx < 0) xx = 0;
714    else if (xx > (sd->size.w - ow))
715      xx = sd->size.w - ow;
716    if (yy < 0) yy = 0;
717    else if (yy > (sd->size.h - oh))
718      yy = sd->size.h - oh;
719
720    sd->show.show = EINA_TRUE;
721    sd->show.x = xx;
722    sd->show.y = yy;
723    sd->show.w = ow;
724    sd->show.h = oh;
725
726    ecore_job_del(sd->calc_job);
727    sd->calc_job = ecore_job_add(_calc_job_cb, obj);
728    if (t >= 1.0)
729      {
730         Eina_List *l, *l_next;
731         Elm_Phocam_Grid *g;
732
733         EINA_LIST_FOREACH_SAFE(sd->grids, l, l_next, g)
734           {
735              if (g->dead)
736                {
737                   sd->grids = eina_list_remove_list(sd->grids, l);
738                   _grid_clear(obj, g);
739                   free(g);
740                }
741           }
742         return EINA_FALSE;
743      }
744
745    return EINA_TRUE;
746 }
747
748 static Eina_Bool
749 _zoom_anim_cb(void *data)
750 {
751    double t;
752    Eina_Bool go;
753    Evas_Object *obj = data;
754
755    ELM_PHOTOCAM_DATA_GET(obj, sd);
756
757    t = ecore_loop_time_get();
758    if (t >= sd->t_end)
759      t = 1.0;
760    else if (sd->t_end > sd->t_start)
761      t = (t - sd->t_start) / (sd->t_end - sd->t_start);
762    else
763      t = 1.0;
764    t = 1.0 - t;
765    t = 1.0 - (t * t);
766    go = _zoom_do(obj, t);
767    if (!go)
768      {
769         sd->no_smooth--;
770         if (!sd->no_smooth) _smooth_update(data);
771         sd->zoom_animator = NULL;
772         evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
773      }
774
775    return go;
776 }
777
778 static Eina_Bool
779 _long_press_cb(void *data)
780 {
781    ELM_PHOTOCAM_DATA_GET(data, sd);
782
783    sd->long_timer = NULL;
784    sd->longpressed = EINA_TRUE;
785    evas_object_smart_callback_call(data, SIG_LONGPRESSED, NULL);
786
787    return ECORE_CALLBACK_CANCEL;
788 }
789
790 static void
791 _mouse_down_cb(void *data,
792                Evas *evas EINA_UNUSED,
793                Evas_Object *obj EINA_UNUSED,
794                void *event_info)
795 {
796    Evas_Event_Mouse_Down *ev = event_info;
797
798    ELM_PHOTOCAM_DATA_GET(data, sd);
799
800    if (ev->button != 1) return;
801    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) sd->on_hold = EINA_TRUE;
802    else sd->on_hold = EINA_FALSE;
803    if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
804      evas_object_smart_callback_call(data, SIG_CLICKED_DOUBLE, NULL);
805    else
806      evas_object_smart_callback_call(data, SIG_PRESS, NULL);
807    sd->longpressed = EINA_FALSE;
808    ecore_timer_del(sd->long_timer);
809    sd->long_timer = ecore_timer_add
810        (_elm_config->longpress_timeout, _long_press_cb, data);
811 }
812
813 static void
814 _mouse_up_cb(void *data,
815              Evas *evas EINA_UNUSED,
816              Evas_Object *obj EINA_UNUSED,
817              void *event_info)
818 {
819    Evas_Event_Mouse_Up *ev = event_info;
820
821    ELM_PHOTOCAM_DATA_GET(data, sd);
822
823    if (ev->button != 1) return;
824    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) sd->on_hold = EINA_TRUE;
825    else sd->on_hold = EINA_FALSE;
826    ELM_SAFE_FREE(sd->long_timer, ecore_timer_del);
827    if (!sd->on_hold)
828      evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
829    sd->on_hold = EINA_FALSE;
830 }
831
832 EOLIAN static Eina_Bool
833 _elm_photocam_elm_widget_on_focus(Eo *obj, Elm_Photocam_Data *_pd EINA_UNUSED)
834 {
835    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, EINA_FALSE);
836    Eina_Bool int_ret = EINA_FALSE;
837
838    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_on_focus());
839    if (!int_ret) return EINA_FALSE;
840
841    if (elm_widget_focus_get(obj))
842      {
843         edje_object_signal_emit
844           (wd->resize_obj, "elm,action,focus", "elm");
845         evas_object_focus_set(wd->resize_obj, EINA_TRUE);
846      }
847    else
848      {
849         edje_object_signal_emit
850           (wd->resize_obj, "elm,action,unfocus", "elm");
851         evas_object_focus_set(wd->resize_obj, EINA_FALSE);
852      }
853
854    return EINA_TRUE;
855 }
856
857 EOLIAN static Eina_Bool
858 _elm_photocam_elm_widget_theme_apply(Eo *obj, Elm_Photocam_Data *sd EINA_UNUSED)
859 {
860    Eina_Bool int_ret = EINA_FALSE;
861    eo_do_super(obj, MY_CLASS, int_ret = elm_obj_widget_theme_apply());
862    if (!int_ret) return EINA_FALSE;
863
864    _sizing_eval(obj);
865
866    return EINA_TRUE;
867 }
868
869 static void
870 _scroll_animate_start_cb(Evas_Object *obj,
871                          void *data EINA_UNUSED)
872 {
873    evas_object_smart_callback_call(obj, SIG_SCROLL_ANIM_START, NULL);
874 }
875
876 static void
877 _scroll_animate_stop_cb(Evas_Object *obj,
878                         void *data EINA_UNUSED)
879 {
880    evas_object_smart_callback_call(obj, SIG_SCROLL_ANIM_STOP, NULL);
881 }
882
883 static void
884 _scroll_drag_start_cb(Evas_Object *obj,
885                       void *data EINA_UNUSED)
886 {
887    evas_object_smart_callback_call(obj, SIG_SCROLL_DRAG_START, NULL);
888 }
889
890 static void
891 _scroll_drag_stop_cb(Evas_Object *obj,
892                      void *data EINA_UNUSED)
893 {
894    evas_object_smart_callback_call(obj, SIG_SCROLL_DRAG_STOP, NULL);
895 }
896
897 static void
898 _scroll_cb(Evas_Object *obj,
899            void *data EINA_UNUSED)
900 {
901    ELM_PHOTOCAM_DATA_GET(obj, sd);
902
903    if (!sd->scr_timer)
904      {
905         sd->no_smooth++;
906         if (sd->no_smooth == 1) _smooth_update(obj);
907      }
908
909    ecore_timer_del(sd->scr_timer);
910    sd->scr_timer = ecore_timer_add(0.5, _scroll_timeout_cb, obj);
911
912    evas_object_smart_callback_call(obj, SIG_SCROLL, NULL);
913 }
914
915 static Eina_Bool
916 _key_action_move(Evas_Object *obj, const char *params)
917 {
918    const char *dir = params;
919
920    Evas_Coord x = 0;
921    Evas_Coord y = 0;
922    Evas_Coord v_h = 0;
923    Evas_Coord step_x = 0;
924    Evas_Coord step_y = 0;
925    Evas_Coord page_x = 0;
926    Evas_Coord page_y = 0;
927
928    eo_do(obj,
929          elm_interface_scrollable_content_pos_get(&x, &y),
930          elm_interface_scrollable_step_size_get(&step_x, &step_y),
931          elm_interface_scrollable_page_size_get(&page_x, &page_y),
932          elm_interface_scrollable_content_viewport_size_get(NULL, &v_h));
933
934    if (!strcmp(dir, "left"))
935      {
936         x -= step_x;
937      }
938    else if (!strcmp(dir, "right"))
939      {
940         x += step_x;
941      }
942    else if (!strcmp(dir, "up"))
943      {
944         y -= step_y;
945      }
946    else if (!strcmp(dir, "down"))
947      {
948         y += step_y;
949      }
950    else if (!strcmp(dir, "prior"))
951      {
952         if (page_y < 0)
953           y -= -(page_y * v_h) / 100;
954         else
955           y -= page_y;
956      }
957    else if (!strcmp(dir, "next"))
958      {
959         if (page_y < 0)
960           y += -(page_y * v_h) / 100;
961         else
962           y += page_y;
963      }
964    else return EINA_FALSE;
965
966    eo_do(obj, elm_interface_scrollable_content_pos_set(x, y, EINA_TRUE));
967    return EINA_TRUE;
968 }
969
970 static Eina_Bool
971 _key_action_zoom(Evas_Object *obj, const char *params)
972 {
973    const char *dir = params;
974    double zoom;
975
976    if (!strcmp(dir, "in"))
977      {
978         zoom = elm_photocam_zoom_get(obj);
979         zoom -= 0.5;
980         elm_photocam_zoom_mode_set(obj, ELM_PHOTOCAM_ZOOM_MODE_MANUAL);
981         elm_photocam_zoom_set(obj, zoom);
982      }
983    else if (!strcmp(dir, "out"))
984      {
985         zoom = elm_photocam_zoom_get(obj);
986         zoom += 0.5;
987         elm_photocam_zoom_mode_set(obj, ELM_PHOTOCAM_ZOOM_MODE_MANUAL);
988         elm_photocam_zoom_set(obj, zoom);
989      }
990    else return EINA_FALSE;
991
992    return EINA_TRUE;
993 }
994
995 EOLIAN static Eina_Bool
996 _elm_photocam_elm_widget_event(Eo *obj, Elm_Photocam_Data *_pd EINA_UNUSED, Evas_Object *src, Evas_Callback_Type type, void *event_info)
997 {
998    (void) src;
999    Evas_Event_Key_Down *ev = event_info;
1000
1001    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
1002    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
1003    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
1004
1005    if (!_elm_config_key_binding_call(obj, ev, key_actions))
1006      return EINA_FALSE;
1007
1008    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
1009    return EINA_TRUE;
1010 }
1011
1012 Eina_Bool
1013 _bounce_eval(void *data)
1014 {
1015    Evas_Object *obj = data;
1016    ELM_PHOTOCAM_DATA_GET(obj, sd);
1017    double t, tt;
1018
1019    if ((sd->g_layer_zoom.imx == sd->g_layer_zoom.bounce.x_end) &&
1020        (sd->g_layer_zoom.imy == sd->g_layer_zoom.bounce.y_end))
1021      {
1022         sd->g_layer_zoom.imx = 0;
1023         sd->g_layer_zoom.imy = 0;
1024         sd->zoom_g_layer = EINA_FALSE;
1025         sd->g_layer_zoom.bounce.animator = NULL;
1026
1027         eo_do(obj, elm_interface_scrollable_freeze_set(EINA_FALSE));
1028
1029         return ECORE_CALLBACK_CANCEL;
1030      }
1031
1032    t = ecore_loop_time_get();
1033    tt = (t - sd->g_layer_zoom.bounce.t_start) /
1034      (sd->g_layer_zoom.bounce.t_end -
1035       sd->g_layer_zoom.bounce.t_start);
1036    tt = 1.0 - tt;
1037    tt = 1.0 - (tt * tt);
1038
1039    if (t > sd->g_layer_zoom.bounce.t_end)
1040      {
1041         sd->g_layer_zoom.imx = 0;
1042         sd->g_layer_zoom.imy = 0;
1043         sd->zoom_g_layer = EINA_FALSE;
1044
1045         eo_do(obj, elm_interface_scrollable_freeze_set(EINA_FALSE));
1046
1047         _zoom_do(obj, 1.0);
1048         sd->g_layer_zoom.bounce.animator = NULL;
1049         return ECORE_CALLBACK_CANCEL;
1050      }
1051
1052    if (sd->g_layer_zoom.imx != sd->g_layer_zoom.bounce.x_end)
1053      sd->g_layer_zoom.imx =
1054        sd->g_layer_zoom.bounce.x_start * (1.0 - tt) +
1055        sd->g_layer_zoom.bounce.x_end * tt;
1056
1057    if (sd->g_layer_zoom.imy != sd->g_layer_zoom.bounce.y_end)
1058      sd->g_layer_zoom.imy =
1059        sd->g_layer_zoom.bounce.y_start * (1.0 - tt) +
1060        sd->g_layer_zoom.bounce.y_end * tt;
1061
1062    _zoom_do(obj, 1.0 - (1.0 - tt));
1063
1064    return ECORE_CALLBACK_RENEW;
1065 }
1066
1067 static void
1068 _g_layer_zoom_do(Evas_Object *obj,
1069                  Evas_Coord px,
1070                  Evas_Coord py,
1071                  Elm_Gesture_Zoom_Info *g_layer)
1072 {
1073    int regx, regy, regw, regh, ix, iy, iw, ih;
1074    Evas_Coord rx, ry, rw = 0, rh = 0;
1075    int xx, yy;
1076
1077    ELM_PHOTOCAM_DATA_GET(obj, sd);
1078    sd->mode = ELM_PHOTOCAM_ZOOM_MODE_MANUAL;
1079    sd->zoom = sd->g_layer_start / g_layer->zoom;
1080    sd->size.ow = sd->size.w;
1081    sd->size.oh = sd->size.h;
1082    eo_do(obj, elm_interface_scrollable_content_pos_get(&rx, &ry));
1083    eo_do(obj, elm_interface_scrollable_content_viewport_size_get(&rw, &rh));
1084    if ((rw <= 0) || (rh <= 0)) return;
1085
1086    sd->size.nw = (double)sd->size.imw / sd->zoom;
1087    sd->size.nh = (double)sd->size.imh / sd->zoom;
1088
1089    elm_photocam_image_region_get(obj, &regx, &regy, &regw, &regh);
1090    evas_object_geometry_get(sd->img, &ix, &iy, &iw, &ih);
1091
1092    sd->pvx = g_layer->x;
1093    sd->pvy = g_layer->y;
1094
1095    xx = (px / sd->zoom) - sd->pvx;
1096    yy = (py / sd->zoom) - sd->pvy;
1097    sd->g_layer_zoom.imx = 0;
1098    sd->g_layer_zoom.imy = 0;
1099
1100    if ((xx < 0) || (rw > sd->size.nw))
1101      {
1102         sd->g_layer_zoom.imx = xx;
1103         xx = 0;
1104      }
1105    else if ((xx + rw) > sd->size.nw)
1106      {
1107         sd->g_layer_zoom.imx = xx + rw - sd->size.nw;
1108         xx = sd->size.nw - rw;
1109      }
1110
1111    if ((yy < 0) || (rh > sd->size.nh))
1112      {
1113         sd->g_layer_zoom.imy = yy;
1114         yy = 0;
1115      }
1116    else if ((yy + rh) > sd->size.nh)
1117      {
1118         sd->g_layer_zoom.imy = yy + rh - sd->size.nh;
1119         yy = sd->size.nh - rh;
1120      }
1121
1122    sd->size.spos.x = (double)(xx + (rw / 2)) / (double)(sd->size.nw);
1123    sd->size.spos.y = (double)(yy + (rh / 2)) / (double)(sd->size.nh);
1124
1125    _zoom_do(obj, 1.0);
1126 }
1127
1128 static Evas_Event_Flags
1129 _g_layer_zoom_start_cb(void *data,
1130                        void *event_info)
1131 {
1132    Evas_Object *obj = data;
1133    Elm_Gesture_Zoom_Info *p = event_info;
1134    ELM_PHOTOCAM_DATA_GET(obj, sd);
1135    double marginx = 0, marginy = 0;
1136    Evas_Coord rw = 0, rh = 0;
1137    int x, y, w, h;
1138
1139    ELM_SAFE_FREE(sd->g_layer_zoom.bounce.animator, ecore_animator_del);
1140    sd->zoom_g_layer = EINA_TRUE;
1141
1142    eo_do(obj, elm_interface_scrollable_freeze_set(EINA_TRUE));
1143
1144    elm_photocam_image_region_get(obj, &x, &y, &w, &h);
1145    eo_do(obj, elm_interface_scrollable_content_viewport_size_get(&rw, &rh));
1146
1147    if (rw > sd->size.nw)
1148      marginx = (rw - sd->size.nw) / 2;
1149    if (rh > sd->size.nh)
1150      marginy = (rh - sd->size.nh) / 2;
1151
1152    sd->g_layer_start = sd->zoom;
1153
1154    sd->zoom_point_x = x + ((p->x - marginx) * sd->zoom) +
1155      sd->g_layer_zoom.imx;
1156    sd->zoom_point_y = y + ((p->y - marginy) * sd->zoom) +
1157      sd->g_layer_zoom.imy;
1158
1159    return EVAS_EVENT_FLAG_NONE;
1160 }
1161
1162 static Evas_Event_Flags
1163 _g_layer_zoom_move_cb(void *data,
1164                       void *event_info)
1165 {
1166    Elm_Photocam_Data *sd = eo_data_scope_get(data, MY_CLASS);
1167    Elm_Gesture_Zoom_Info *p = event_info;
1168
1169    _g_layer_zoom_do(data, sd->zoom_point_x, sd->zoom_point_y, p);
1170
1171    return EVAS_EVENT_FLAG_NONE;
1172 }
1173
1174 static Evas_Event_Flags
1175 _g_layer_zoom_end_cb(void *data,
1176                      void *event_info EINA_UNUSED)
1177 {
1178    Evas_Object *obj = data;
1179    ELM_PHOTOCAM_DATA_GET(obj, sd);
1180    Evas_Coord rw, rh;
1181
1182    eo_do(obj, elm_interface_scrollable_content_viewport_size_get(&rw, &rh));
1183    sd->g_layer_start = 1.0;
1184
1185    if (sd->g_layer_zoom.imx || sd->g_layer_zoom.imy)
1186      {
1187         double t;
1188
1189         t = ecore_loop_time_get();
1190         sd->g_layer_zoom.bounce.x_start = sd->g_layer_zoom.imx;
1191         sd->g_layer_zoom.bounce.y_start = sd->g_layer_zoom.imy;
1192         sd->g_layer_zoom.bounce.x_end = 0;
1193         sd->g_layer_zoom.bounce.y_end = 0;
1194
1195         if (rw > sd->size.nw &&
1196             rh > sd->size.nh)
1197           {
1198              Evas_Coord pw, ph;
1199              double z;
1200
1201              if ((sd->size.imw < rw) && (sd->size.imh < rh))
1202                {
1203                   sd->zoom = 1;
1204                   sd->size.nw = sd->size.imw;
1205                   sd->size.nh = sd->size.imh;
1206                }
1207              else
1208                {
1209                   ph = (sd->size.imh * rw) / sd->size.imw;
1210                   if (ph > rh)
1211                     {
1212                        pw = (sd->size.imw * rh) / sd->size.imh;
1213                        ph = rh;
1214                     }
1215                   else
1216                     {
1217                        pw = rw;
1218                     }
1219                   if (sd->size.imw > sd->size.imh)
1220                     z = (double)sd->size.imw / pw;
1221                   else
1222                     z = (double)sd->size.imh / ph;
1223
1224                   sd->zoom = z;
1225                   sd->size.nw = pw;
1226                   sd->size.nh = ph;
1227                }
1228              sd->g_layer_zoom.bounce.x_end = (sd->size.nw - rw) / 2;
1229              sd->g_layer_zoom.bounce.y_end = (sd->size.nh - rh) / 2;
1230           }
1231         else
1232           {
1233              int xx, yy;
1234
1235              xx = (sd->zoom_point_x / sd->zoom) - sd->pvx;
1236              yy = (sd->zoom_point_y / sd->zoom) - sd->pvy;
1237
1238              if (xx < 0) xx = 0;
1239              if (yy < 0) yy = 0;
1240
1241              if (rw > sd->size.nw)
1242                sd->g_layer_zoom.bounce.x_end = (sd->size.nw - rw) / 2;
1243              if ((xx + rw) > sd->size.nw)
1244                xx = sd->size.nw - rw;
1245
1246              if (rh > sd->size.nh)
1247                sd->g_layer_zoom.bounce.y_end = (sd->size.nh - rh) / 2;
1248              if ((yy + rh) > sd->size.nh)
1249                yy = sd->size.nh - rh;
1250
1251              sd->size.spos.x = (double)(xx + (rw / 2)) / (double)(sd->size.nw);
1252              sd->size.spos.y = (double)(yy + (rh / 2)) / (double)(sd->size.nh);
1253           }
1254
1255         sd->g_layer_zoom.bounce.t_start = t;
1256         sd->g_layer_zoom.bounce.t_end = t +
1257           _elm_config->page_scroll_friction;
1258
1259         sd->g_layer_zoom.bounce.animator =
1260           ecore_animator_add(_bounce_eval, obj);
1261      }
1262    else
1263      {
1264         eo_do(obj, elm_interface_scrollable_freeze_set(EINA_FALSE));
1265         sd->zoom_g_layer = EINA_FALSE;
1266      }
1267
1268    return EVAS_EVENT_FLAG_NONE;
1269 }
1270
1271 EOLIAN static void
1272 _elm_photocam_evas_smart_add(Eo *obj, Elm_Photocam_Data *priv)
1273 {
1274    Eina_Bool bounce = _elm_config->thumbscroll_bounce_enable;
1275    Elm_Photocam_Pan_Data *pan_data;
1276    Evas_Object *edje;
1277    Evas_Coord minw, minh;
1278
1279    elm_widget_sub_object_parent_add(obj);
1280
1281    edje = edje_object_add(evas_object_evas_get(obj));
1282    elm_widget_resize_object_set(obj, edje, EINA_TRUE);
1283
1284    eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
1285
1286    elm_widget_theme_object_set
1287       (obj, edje, "photocam", "base", elm_widget_style_get(obj));
1288
1289    priv->hit_rect = evas_object_rectangle_add(evas_object_evas_get(obj));
1290    evas_object_smart_member_add(priv->hit_rect, obj);
1291    elm_widget_sub_object_add(obj, priv->hit_rect);
1292
1293    /* common scroller hit rectangle setup */
1294    evas_object_color_set(priv->hit_rect, 0, 0, 0, 0);
1295    evas_object_show(priv->hit_rect);
1296    evas_object_repeat_events_set(priv->hit_rect, EINA_TRUE);
1297
1298    elm_widget_can_focus_set(obj, EINA_TRUE);
1299
1300    eo_do(obj, elm_interface_scrollable_objects_set(edje, priv->hit_rect));
1301
1302    eo_do(obj,
1303          elm_interface_scrollable_animate_start_cb_set(_scroll_animate_start_cb),
1304          elm_interface_scrollable_animate_stop_cb_set(_scroll_animate_stop_cb),
1305          elm_interface_scrollable_drag_start_cb_set(_scroll_drag_start_cb),
1306          elm_interface_scrollable_drag_stop_cb_set(_scroll_drag_stop_cb),
1307          elm_interface_scrollable_scroll_cb_set(_scroll_cb));
1308
1309    eo_do(obj, elm_interface_scrollable_bounce_allow_set(bounce, bounce));
1310
1311    priv->pan_obj = eo_add(MY_PAN_CLASS, evas_object_evas_get(obj));
1312    pan_data = eo_data_scope_get(priv->pan_obj, MY_PAN_CLASS);
1313    eo_data_ref(obj, NULL);
1314    pan_data->wobj = obj;
1315    pan_data->wsd = priv;
1316
1317    eo_do(obj, elm_interface_scrollable_extern_pan_set(priv->pan_obj));
1318
1319    priv->g_layer_start = 1.0;
1320    priv->zoom = 1;
1321    priv->mode = ELM_PHOTOCAM_ZOOM_MODE_MANUAL;
1322    priv->tsize = 512;
1323
1324    priv->img = evas_object_image_add(evas_object_evas_get(obj));
1325    evas_object_image_load_orientation_set(priv->img, EINA_TRUE);
1326    evas_object_image_scale_hint_set(priv->img, EVAS_IMAGE_SCALE_HINT_DYNAMIC);
1327    evas_object_event_callback_add
1328      (priv->img, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down_cb, obj);
1329    evas_object_event_callback_add
1330      (priv->img, EVAS_CALLBACK_MOUSE_UP, _mouse_up_cb, obj);
1331    evas_object_image_scale_hint_set(priv->img, EVAS_IMAGE_SCALE_HINT_STATIC);
1332
1333    /* XXX: mmm... */
1334    evas_object_smart_member_add(priv->img, priv->pan_obj);
1335
1336    elm_widget_sub_object_add(obj, priv->img);
1337    evas_object_image_filled_set(priv->img, EINA_TRUE);
1338    evas_object_event_callback_add
1339      (priv->img, EVAS_CALLBACK_IMAGE_PRELOADED, _main_img_preloaded_cb, obj);
1340
1341    edje_object_size_min_calc(edje, &minw, &minh);
1342    evas_object_size_hint_min_set(obj, minw, minh);
1343
1344    _sizing_eval(obj);
1345
1346 }
1347
1348 EOLIAN static void
1349 _elm_photocam_evas_smart_del(Eo *obj, Elm_Photocam_Data *sd)
1350 {
1351    Elm_Phocam_Grid *g;
1352
1353    EINA_LIST_FREE(sd->grids, g)
1354      {
1355         free(g->grid);
1356         free(g);
1357      }
1358    eo_unref(sd->pan_obj);
1359    ELM_SAFE_FREE(sd->pan_obj, evas_object_del);
1360
1361    if (sd->f) eina_file_close(sd->f);
1362    free(sd->remote_data);
1363    if (sd->remote) _elm_url_cancel(sd->remote);
1364    eina_stringshare_del(sd->file);
1365    ecore_job_del(sd->calc_job);
1366    ecore_timer_del(sd->scr_timer);
1367    ecore_timer_del(sd->long_timer);
1368    ecore_animator_del(sd->zoom_animator);
1369    ecore_animator_del(sd->g_layer_zoom.bounce.animator);
1370
1371    eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
1372 }
1373
1374 EOLIAN static void
1375 _elm_photocam_evas_smart_move(Eo *obj, Elm_Photocam_Data *sd, Evas_Coord x, Evas_Coord y)
1376 {
1377    eo_do_super(obj, MY_CLASS, evas_obj_smart_move(x, y));
1378
1379    evas_object_move(sd->hit_rect, x, y);
1380 }
1381
1382 EOLIAN static void
1383 _elm_photocam_evas_smart_resize(Eo *obj, Elm_Photocam_Data *sd, Evas_Coord w, Evas_Coord h)
1384 {
1385    eo_do_super(obj, MY_CLASS, evas_obj_smart_resize(w, h));
1386
1387    evas_object_resize(sd->hit_rect, w, h);
1388 }
1389
1390 EOLIAN static void
1391 _elm_photocam_evas_smart_member_add(Eo *obj, Elm_Photocam_Data *sd, Evas_Object *member)
1392 {
1393
1394    eo_do_super(obj, MY_CLASS, evas_obj_smart_member_add(member));
1395
1396    if (sd->hit_rect)
1397      evas_object_raise(sd->hit_rect);
1398 }
1399
1400 EAPI Evas_Object *
1401 elm_photocam_add(Evas_Object *parent)
1402 {
1403    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1404    Evas_Object *obj = eo_add(MY_CLASS, parent);
1405    eo_unref(obj);
1406    return obj;
1407 }
1408
1409 EOLIAN static void
1410 _elm_photocam_eo_base_constructor(Eo *obj, Elm_Photocam_Data *_pd EINA_UNUSED)
1411 {
1412    eo_do_super(obj, MY_CLASS, eo_constructor());
1413    eo_do(obj,
1414          evas_obj_type_set(MY_CLASS_NAME_LEGACY),
1415          evas_obj_smart_callbacks_descriptions_set(_smart_callbacks));
1416 }
1417
1418 static void
1419 _internal_file_set(Eo *obj, Elm_Photocam_Data *sd, const char *file, Eina_File *f, Evas_Load_Error *ret)
1420 {
1421    ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd);
1422    Evas_Load_Error err;
1423    int w, h;
1424    double tz;
1425
1426    if (!eina_stringshare_replace(&sd->file, file)) return;
1427    sd->f = eina_file_dup(f);
1428
1429    evas_object_image_smooth_scale_set(sd->img, (sd->no_smooth == 0));
1430    evas_object_image_file_set(sd->img, NULL, NULL);
1431    evas_object_image_load_scale_down_set(sd->img, 0);
1432    _photocam_image_file_set(sd->img, sd);
1433    err = evas_object_image_load_error_get(sd->img);
1434    if (err != EVAS_LOAD_ERROR_NONE)
1435      {
1436         ERR("Things are going bad for '%s' (%p) : %i", file, sd->img, err);
1437         if (ret) *ret = err;
1438         return;
1439      }
1440    evas_object_image_size_get(sd->img, &w, &h);
1441
1442    sd->do_region = evas_object_image_region_support_get(sd->img);
1443    sd->size.imw = w;
1444    sd->size.imh = h;
1445    sd->size.w = sd->size.imw / sd->zoom;
1446    sd->size.h = sd->size.imh / sd->zoom;
1447    evas_object_image_file_set(sd->img, NULL, NULL);
1448    evas_object_image_load_scale_down_set(sd->img, 8);
1449    _photocam_image_file_set(sd->img, sd);
1450    err = evas_object_image_load_error_get(sd->img);
1451    if (err != EVAS_LOAD_ERROR_NONE)
1452      {
1453         ERR("Things are going bad for '%s' (%p)", file, sd->img);
1454         if (ret) *ret = err;
1455         return;
1456      }
1457
1458    evas_object_image_preload(sd->img, 0);
1459    sd->main_load_pending = EINA_TRUE;
1460
1461    sd->calc_job = ecore_job_add(_calc_job_cb, obj);
1462    evas_object_smart_callback_call(obj, SIG_LOAD, NULL);
1463    sd->preload_num++;
1464    if (sd->preload_num == 1)
1465      {
1466         edje_object_signal_emit
1467           (wd->resize_obj, "elm,state,busy,start", "elm");
1468         evas_object_smart_callback_call(obj, SIG_LOAD_DETAIL, NULL);
1469      }
1470
1471    tz = sd->zoom;
1472    sd->zoom = 0.0;
1473    elm_photocam_zoom_set(obj, tz);
1474
1475    if (ret) *ret = evas_object_image_load_error_get(sd->img);
1476 }
1477
1478 static void
1479 _elm_photocam_download_done(void *data, Elm_Url *url EINA_UNUSED, Eina_Binbuf *download)
1480 {
1481    Eo *obj = data;
1482    Elm_Photocam_Data *sd = eo_data_scope_get(obj, MY_CLASS);
1483    Eina_File *f;
1484    size_t length;
1485    Evas_Load_Error ret = EVAS_LOAD_ERROR_NONE;
1486
1487    free(sd->remote_data);
1488    length = eina_binbuf_length_get(download);
1489    sd->remote_data = eina_binbuf_string_steal(download);
1490    f = eina_file_virtualize(_elm_url_get(url),
1491                             sd->remote_data, length,
1492                             EINA_FALSE);
1493    _internal_file_set(obj, sd, _elm_url_get(url), f, &ret);
1494    eina_file_close(f);
1495
1496    if (ret != EVAS_LOAD_ERROR_NONE)
1497      {
1498         Elm_Photocam_Error err = { 0, EINA_TRUE };
1499
1500         free(sd->remote_data);
1501         sd->remote_data = NULL;
1502         evas_object_smart_callback_call(obj, SIG_DOWNLOAD_ERROR, &err);
1503      }
1504    else
1505      {
1506         evas_object_smart_callback_call(obj, SIG_DOWNLOAD_DONE, NULL);
1507      }
1508
1509    sd->remote = NULL;
1510 }
1511
1512 static void
1513 _elm_photocam_download_cancel(void *data, Elm_Url *url EINA_UNUSED, int error)
1514 {
1515    Eo *obj = data;
1516    Elm_Photocam_Data *sd = eo_data_scope_get(obj, MY_CLASS);
1517    Elm_Photocam_Error err = { error, EINA_FALSE };
1518
1519    evas_object_smart_callback_call(obj, SIG_DOWNLOAD_ERROR, &err);
1520
1521    sd->remote = NULL;
1522 }
1523
1524 static void
1525 _elm_photocam_download_progress(void *data, Elm_Url *url EINA_UNUSED, double now, double total)
1526 {
1527    Eo *obj = data;
1528    Elm_Photocam_Progress progress;
1529
1530    progress.now = now;
1531    progress.total = total;
1532    evas_object_smart_callback_call(obj, SIG_DOWNLOAD_PROGRESS, &progress);
1533 }
1534
1535 static const char *remote_uri[] = {
1536   "http://", "https://", "ftp://"
1537 };
1538
1539 EOLIAN static Evas_Load_Error
1540 _elm_photocam_file_set(Eo *obj, Elm_Photocam_Data *sd, const char *file)
1541 {
1542    Evas_Load_Error ret = EVAS_LOAD_ERROR_NONE;
1543    unsigned int i;
1544
1545    _grid_clear_all(obj);
1546    ELM_SAFE_FREE(sd->g_layer_zoom.bounce.animator, ecore_animator_del);
1547    if (sd->zoom_animator)
1548      {
1549         sd->no_smooth--;
1550         if (sd->no_smooth == 0) _smooth_update(obj);
1551         ecore_animator_del(sd->zoom_animator);
1552         sd->zoom_animator = NULL;
1553      }
1554    ecore_job_del(sd->calc_job);
1555    evas_object_hide(sd->img);
1556    if (sd->f) eina_file_close(sd->f);
1557    sd->f = NULL;
1558
1559    free(sd->remote_data);
1560    if (sd->remote) _elm_url_cancel(sd->remote);
1561    sd->remote = NULL;
1562
1563    for (i = 0; i < sizeof (remote_uri) / sizeof (remote_uri[0]); ++i)
1564      if (!strncmp(remote_uri[i], file, strlen(remote_uri[i])))
1565        {
1566           // Found a remote target !
1567           sd->remote = _elm_url_download(file,
1568                                         _elm_photocam_download_done,
1569                                         _elm_photocam_download_cancel,
1570                                         _elm_photocam_download_progress,
1571                                         obj);
1572           if (sd->remote)
1573             {
1574                evas_object_smart_callback_call(obj, SIG_DOWNLOAD_START, NULL);
1575                return ret;
1576             }
1577           break;
1578        }
1579
1580    _internal_file_set(obj, sd, file, NULL, &ret);
1581
1582    return ret;
1583 }
1584
1585 EOLIAN static const char*
1586 _elm_photocam_file_get(Eo *obj EINA_UNUSED, Elm_Photocam_Data *sd)
1587 {
1588    return sd->file;
1589 }
1590
1591 EOLIAN static void
1592 _elm_photocam_zoom_set(Eo *obj, Elm_Photocam_Data *sd, double zoom)
1593 {
1594    double z;
1595    Eina_List *l;
1596    Ecore_Animator *an;
1597    Elm_Phocam_Grid *g, *g_zoom = NULL;
1598    Evas_Coord pw, ph, rx, ry, rw, rh;
1599    int zoom_changed = 0, started = 0;
1600
1601    if (zoom <= (1.0 / 256.0)) zoom = (1.0 / 256.0);
1602    if (zoom == sd->zoom) return;
1603
1604    sd->zoom = zoom;
1605    sd->size.ow = sd->size.w;
1606    sd->size.oh = sd->size.h;
1607    eo_do(obj, elm_interface_scrollable_content_pos_get(&rx, &ry));
1608    eo_do(obj, elm_interface_scrollable_content_viewport_size_get(&rw, &rh));
1609    if ((rw <= 0) || (rh <= 0)) return;
1610
1611    if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_MANUAL)
1612      {
1613         sd->size.nw = (double)sd->size.imw / sd->zoom;
1614         sd->size.nh = (double)sd->size.imh / sd->zoom;
1615      }
1616    else if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FIT)
1617      {
1618         if ((sd->size.imw < 1) || (sd->size.imh < 1))
1619           {
1620              sd->size.nw = 0;
1621              sd->size.nh = 0;
1622           }
1623         else
1624           {
1625              ph = (sd->size.imh * rw) / sd->size.imw;
1626              if (ph > rh)
1627                {
1628                   pw = (sd->size.imw * rh) / sd->size.imh;
1629                   ph = rh;
1630                }
1631              else
1632                {
1633                   pw = rw;
1634                }
1635              if (sd->size.imw > sd->size.imh)
1636                z = (double)sd->size.imw / pw;
1637              else
1638                z = (double)sd->size.imh / ph;
1639              if (z != sd->zoom)
1640                zoom_changed = 1;
1641              sd->zoom = z;
1642              sd->size.nw = pw;
1643              sd->size.nh = ph;
1644           }
1645      }
1646    else if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FILL)
1647      {
1648         if ((sd->size.imw < 1) || (sd->size.imh < 1))
1649           {
1650              sd->size.nw = 0;
1651              sd->size.nw = 0;
1652           }
1653         else
1654           {
1655              ph = (sd->size.imh * rw) / sd->size.imw;
1656              if (ph < rh)
1657                {
1658                   pw = (sd->size.imw * rh) / sd->size.imh;
1659                   ph = rh;
1660                }
1661              else
1662                {
1663                   pw = rw;
1664                }
1665              if (sd->size.imw > sd->size.imh)
1666                z = (double)sd->size.imw / pw;
1667              else
1668                z = (double)sd->size.imh / ph;
1669              if (z != sd->zoom)
1670                zoom_changed = 1;
1671              sd->zoom = z;
1672              sd->size.nw = pw;
1673              sd->size.nh = ph;
1674           }
1675      }
1676    else if (sd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FIT_IN)
1677      {
1678         if ((sd->size.imw < 1) || (sd->size.imh < 1))
1679           {
1680              sd->size.nw = 0;
1681              sd->size.nh = 0;
1682           }
1683         else if ((sd->size.imw < rw) && (sd->size.imh < rh))
1684           {
1685              if (1 != sd->zoom) zoom_changed = 1;
1686              sd->zoom = 1;
1687              sd->size.nw = sd->size.imw;
1688              sd->size.nh = sd->size.imh;
1689           }
1690         else
1691           {
1692              ph = (sd->size.imh * rw) / sd->size.imw;
1693              if (ph > rh)
1694                {
1695                   pw = (sd->size.imw * rh) / sd->size.imh;
1696                   ph = rh;
1697                }
1698              else
1699                pw = rw;
1700              if (sd->size.imw > sd->size.imh)
1701                z = (double)sd->size.imw / pw;
1702              else
1703                z = (double)sd->size.imh / ph;
1704              if (z != sd->zoom)
1705                zoom_changed = 1;
1706              sd->zoom = z;
1707              sd->size.nw = pw;
1708              sd->size.nh = ph;
1709           }
1710      }
1711
1712    if (sd->main_load_pending)
1713      {
1714         sd->size.w = sd->size.nw;
1715         sd->size.h = sd->size.nh;
1716
1717         goto done;
1718      }
1719
1720    EINA_LIST_FOREACH(sd->grids, l, g)
1721      {
1722         if (g->zoom == _grid_zoom_calc(sd->zoom))
1723           {
1724              sd->grids = eina_list_remove(sd->grids, g);
1725              sd->grids = eina_list_prepend(sd->grids, g);
1726              _grid_raise(g);
1727              goto done;
1728           }
1729      }
1730
1731    g = _grid_create(obj);
1732    if (g)
1733      {
1734         if (eina_list_count(sd->grids) > 1)
1735           {
1736              g_zoom = eina_list_last(sd->grids)->data;
1737              sd->grids = eina_list_remove(sd->grids, g_zoom);
1738              _grid_clear(obj, g_zoom);
1739              free(g_zoom);
1740              EINA_LIST_FOREACH(sd->grids, l, g_zoom)
1741                {
1742                   g_zoom->dead = 1;
1743                }
1744           }
1745         sd->grids = eina_list_prepend(sd->grids, g);
1746      }
1747    else
1748      {
1749         EINA_LIST_FREE(sd->grids, g)
1750           {
1751              _grid_clear(obj, g);
1752              free(g);
1753           }
1754      }
1755
1756 done:
1757    sd->t_start = ecore_loop_time_get();
1758    sd->t_end = sd->t_start + _elm_config->zoom_friction;
1759    if ((sd->size.w > 0) && (sd->size.h > 0))
1760      {
1761         sd->size.spos.x = (double)(rx + (rw / 2)) / (double)sd->size.w;
1762         sd->size.spos.y = (double)(ry + (rh / 2)) / (double)sd->size.h;
1763      }
1764    else
1765      {
1766         sd->size.spos.x = 0.5;
1767         sd->size.spos.y = 0.5;
1768      }
1769    if (rw > sd->size.w) sd->size.spos.x = 0.5;
1770    if (rh > sd->size.h) sd->size.spos.y = 0.5;
1771    if (sd->size.spos.x > 1.0) sd->size.spos.x = 1.0;
1772    if (sd->size.spos.y > 1.0) sd->size.spos.y = 1.0;
1773
1774    if (sd->paused)
1775      {
1776         _zoom_do(obj, 1.0);
1777      }
1778    else
1779      {
1780         if (!sd->zoom_animator)
1781           {
1782              sd->zoom_animator = ecore_animator_add(_zoom_anim_cb, obj);
1783              sd->no_smooth++;
1784              if (sd->no_smooth == 1) _smooth_update(obj);
1785              started = 1;
1786           }
1787      }
1788
1789    an = sd->zoom_animator;
1790    if (an)
1791      {
1792         if (!_zoom_anim_cb(obj))
1793           {
1794              ecore_animator_del(an);
1795              an = NULL;
1796           }
1797      }
1798
1799    ecore_job_del(sd->calc_job);
1800    sd->calc_job = ecore_job_add(_calc_job_cb, obj);
1801    if (!sd->paused)
1802      {
1803         if (started)
1804           evas_object_smart_callback_call(obj, SIG_ZOOM_START, NULL);
1805         if (!an)
1806           evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1807      }
1808    if (zoom_changed)
1809      evas_object_smart_callback_call(obj, SIG_ZOOM_CHANGE, NULL);
1810 }
1811
1812 EOLIAN static double
1813 _elm_photocam_zoom_get(Eo *obj EINA_UNUSED, Elm_Photocam_Data *sd)
1814 {
1815    return sd->zoom;
1816 }
1817
1818 EOLIAN static void
1819 _elm_photocam_zoom_mode_set(Eo *obj, Elm_Photocam_Data *sd, Elm_Photocam_Zoom_Mode mode)
1820 {
1821    double tz;
1822
1823    if (sd->mode == mode) return;
1824    sd->mode = mode;
1825
1826    tz = sd->zoom;
1827    sd->zoom = 0.0;
1828    elm_photocam_zoom_set(obj, tz);
1829 }
1830
1831 EOLIAN static Elm_Photocam_Zoom_Mode
1832 _elm_photocam_zoom_mode_get(Eo *obj EINA_UNUSED, Elm_Photocam_Data *sd)
1833 {
1834    return sd->mode;
1835 }
1836
1837 EOLIAN static void
1838 _elm_photocam_image_size_get(Eo *obj EINA_UNUSED, Elm_Photocam_Data *sd, int *w, int *h)
1839 {
1840    if (w) *w = sd->size.imw;
1841    if (h) *h = sd->size.imh;
1842 }
1843
1844 EOLIAN static void
1845 _elm_photocam_image_region_get(Eo *obj, Elm_Photocam_Data *sd, int *x, int *y, int *w, int *h)
1846 {
1847    Evas_Coord sx, sy, sw, sh;
1848
1849    eo_do((Eo *)obj, elm_interface_scrollable_content_pos_get(&sx, &sy));
1850    eo_do((Eo *)obj, elm_interface_scrollable_content_viewport_size_get(&sw, &sh));
1851    if (sd->size.w > 0)
1852      {
1853         if (x)
1854           {
1855              *x = (sd->size.imw * sx) / sd->size.w;
1856              if (*x > sd->size.imw) *x = sd->size.imw;
1857           }
1858         if (w)
1859           {
1860              *w = (sd->size.imw * sw) / sd->size.w;
1861              if (*w > sd->size.imw) *w = sd->size.imw;
1862              else if (*w < 0)
1863                *w = 0;
1864           }
1865      }
1866    else
1867      {
1868         if (x) *x = 0;
1869         if (w) *w = 0;
1870      }
1871
1872    if (sd->size.h > 0)
1873      {
1874         if (y)
1875           {
1876              *y = (sd->size.imh * sy) / sd->size.h;
1877              if (*y > sd->size.imh) *y = sd->size.imh;
1878           }
1879         if (h)
1880           {
1881              *h = (sd->size.imh * sh) / sd->size.h;
1882              if (*h > sd->size.imh) *h = sd->size.imh;
1883              else if (*h < 0)
1884                *h = 0;
1885           }
1886      }
1887    else
1888      {
1889         if (y) *y = 0;
1890         if (h) *h = 0;
1891      }
1892 }
1893
1894 EOLIAN static void
1895 _elm_photocam_image_region_show(Eo *obj, Elm_Photocam_Data *sd, int x, int y, int w, int h)
1896 {
1897    int rx, ry, rw, rh;
1898
1899    if ((sd->size.imw < 1) || (sd->size.imh < 1)) return;
1900    rx = (x * sd->size.w) / sd->size.imw;
1901    ry = (y * sd->size.h) / sd->size.imh;
1902    rw = (w * sd->size.w) / sd->size.imw;
1903    rh = (h * sd->size.h) / sd->size.imh;
1904    if (rw < 1) rw = 1;
1905    if (rh < 1) rh = 1;
1906    if ((rx + rw) > sd->size.w) rx = sd->size.w - rw;
1907    if ((ry + rh) > sd->size.h) ry = sd->size.h - rh;
1908    if (sd->g_layer_zoom.bounce.animator)
1909      {
1910         ecore_animator_del(sd->g_layer_zoom.bounce.animator);
1911         sd->g_layer_zoom.bounce.animator = NULL;
1912         _zoom_do(obj, 1.0);
1913      }
1914    if (sd->zoom_animator)
1915      {
1916         sd->no_smooth--;
1917         ecore_animator_del(sd->zoom_animator);
1918         sd->zoom_animator = NULL;
1919         _zoom_do(obj, 1.0);
1920         evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1921      }
1922    eo_do(obj, elm_interface_scrollable_content_region_show(rx, ry, rw, rh));
1923 }
1924
1925 EAPI void
1926 elm_photocam_image_region_bring_in(Evas_Object *obj,
1927                                    int x,
1928                                    int y,
1929                                    int w,
1930                                    int h EINA_UNUSED)
1931 {
1932    ELM_PHOTOCAM_CHECK(obj);
1933    eo_do(obj, elm_interface_scrollable_region_bring_in(x, y, w, h));
1934 }
1935
1936 EOLIAN static void
1937 _elm_photocam_elm_interface_scrollable_region_bring_in(Eo *obj, Elm_Photocam_Data *sd, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
1938 {
1939    int rx, ry, rw, rh;
1940
1941    if ((sd->size.imw < 1) || (sd->size.imh < 1)) return;
1942    rx = (x * sd->size.w) / sd->size.imw;
1943    ry = (y * sd->size.h) / sd->size.imh;
1944    rw = (w * sd->size.w) / sd->size.imw;
1945    rh = (h * sd->size.h) / sd->size.imh;
1946    if (rw < 1) rw = 1;
1947    if (rh < 1) rh = 1;
1948    if ((rx + rw) > sd->size.w) rx = sd->size.w - rw;
1949    if ((ry + rh) > sd->size.h) ry = sd->size.h - rh;
1950    if (sd->g_layer_zoom.bounce.animator)
1951      {
1952         ecore_animator_del(sd->g_layer_zoom.bounce.animator);
1953         sd->g_layer_zoom.bounce.animator = NULL;
1954         _zoom_do(obj, 1.0);
1955      }
1956    if (sd->zoom_animator)
1957      {
1958         sd->no_smooth--;
1959         if (!sd->no_smooth) _smooth_update(obj);
1960         ecore_animator_del(sd->zoom_animator);
1961         sd->zoom_animator = NULL;
1962         _zoom_do(obj, 1.0);
1963         evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1964      }
1965    eo_do_super(obj, MY_CLASS, elm_interface_scrollable_region_bring_in(rx, ry, rw, rh));
1966 }
1967
1968 EOLIAN static void
1969 _elm_photocam_paused_set(Eo *obj, Elm_Photocam_Data *sd, Eina_Bool paused)
1970 {
1971    paused = !!paused;
1972
1973    if (sd->paused == paused) return;
1974    sd->paused = paused;
1975    if (!sd->paused) return;
1976
1977    if (sd->g_layer_zoom.bounce.animator)
1978      {
1979         ecore_animator_del(sd->g_layer_zoom.bounce.animator);
1980         sd->g_layer_zoom.bounce.animator = NULL;
1981         _zoom_do(obj, 1.0);
1982      }
1983    if (sd->zoom_animator)
1984      {
1985         ecore_animator_del(sd->zoom_animator);
1986         sd->zoom_animator = NULL;
1987         _zoom_do(obj, 1.0);
1988         evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1989      }
1990 }
1991
1992 EOLIAN static Eina_Bool
1993 _elm_photocam_paused_get(Eo *obj EINA_UNUSED, Elm_Photocam_Data *sd)
1994 {
1995    return sd->paused;
1996 }
1997
1998 EOLIAN static Evas_Object*
1999 _elm_photocam_internal_image_get(Eo *obj EINA_UNUSED, Elm_Photocam_Data *sd)
2000 {
2001    return sd->img;
2002 }
2003
2004 EAPI void
2005 elm_photocam_bounce_set(Evas_Object *obj,
2006                         Eina_Bool h_bounce,
2007                         Eina_Bool v_bounce)
2008 {
2009    ELM_PHOTOCAM_CHECK(obj);
2010
2011    eo_do(obj, elm_interface_scrollable_bounce_allow_set(h_bounce, v_bounce));
2012 }
2013
2014 EAPI void
2015 elm_photocam_bounce_get(const Evas_Object *obj,
2016                         Eina_Bool *h_bounce,
2017                         Eina_Bool *v_bounce)
2018 {
2019    ELM_PHOTOCAM_CHECK(obj);
2020
2021    eo_do((Eo *)obj, elm_interface_scrollable_bounce_allow_get(h_bounce, v_bounce));
2022 }
2023
2024 EOLIAN static void
2025 _elm_photocam_gesture_enabled_set(Eo *obj, Elm_Photocam_Data *sd, Eina_Bool gesture)
2026 {
2027    gesture = !!gesture;
2028
2029    if (sd->do_gesture == gesture) return;
2030
2031    sd->do_gesture = gesture;
2032
2033    ELM_SAFE_FREE(sd->g_layer, evas_object_del);
2034
2035    if (!gesture) return;
2036
2037    sd->g_layer = elm_gesture_layer_add(obj);
2038    if (!sd->g_layer) return;
2039
2040    elm_gesture_layer_attach(sd->g_layer, obj);
2041    elm_gesture_layer_cb_set
2042      (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_START,
2043      _g_layer_zoom_start_cb, obj);
2044    elm_gesture_layer_cb_set
2045      (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_MOVE,
2046      _g_layer_zoom_move_cb, obj);
2047    elm_gesture_layer_cb_set
2048      (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_END,
2049      _g_layer_zoom_end_cb, obj);
2050    elm_gesture_layer_cb_set
2051      (sd->g_layer, ELM_GESTURE_ZOOM, ELM_GESTURE_STATE_ABORT,
2052      _g_layer_zoom_end_cb, obj);
2053 }
2054
2055 EOLIAN static Eina_Bool
2056 _elm_photocam_gesture_enabled_get(Eo *obj EINA_UNUSED, Elm_Photocam_Data *sd)
2057 {
2058    return sd->do_gesture;
2059 }
2060
2061 static void
2062 _elm_photocam_class_constructor(Eo_Class *klass)
2063 {
2064    evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
2065 }
2066
2067 #include "elm_photocam.eo.c"