elementary: photocam fix region get, here x and y can be negative
[framework/uifw/elementary.git] / src / lib / elm_photocam.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "els_scroller.h"
4
5 /*
6  * TODO (maybe - optional future stuff):
7  *
8  * 1. wrap photo in theme edje so u can have styling around photo (like white
9  *    photo bordering).
10  * 2. exif handling
11  * 3. rotation flags in exif handling (nasty! should have rot in evas)
12  */
13
14 typedef struct _Widget_Data Widget_Data;
15 typedef struct _Pan Pan;
16 typedef struct _Grid Grid;
17 typedef struct _Grid_Item Grid_Item;
18
19 struct _Grid_Item
20 {
21    Widget_Data *wd;
22    Evas_Object *img;
23    struct
24    {
25       int x, y, w, h;
26    } src, out;
27    Eina_Bool want : 1;
28    Eina_Bool have : 1;
29 };
30
31 struct _Grid
32 {
33    int tsize; // size of tile (tsize x tsize pixels)
34    int zoom; // zoom level tiles want for optimal display (1, 2, 4, 8)
35    int iw, ih; // size of image in pixels
36    int w, h; // size of grid image in pixels (represented by grid)
37    int gw, gh; // size of grid in tiles
38    Grid_Item *grid; // the grid (gw * gh items)
39    Eina_Bool dead : 1; // old grid. will die as soon as anim is over
40 };
41
42 struct _Widget_Data
43 {
44    Evas_Object *obj;
45    Evas_Object *scr;
46    Evas_Object *pan_smart;
47    Pan *pan;
48    Evas_Coord pan_x, pan_y, minw, minh;
49
50    double zoom;
51    Elm_Photocam_Zoom_Mode mode;
52    const char *file;
53
54    Ecore_Job *calc_job;
55    Ecore_Timer *scr_timer;
56    Ecore_Timer *long_timer;
57    Ecore_Animator *zoom_animator;
58    double t_start, t_end;
59    struct
60    {
61       int imw, imh;
62       int w, h;
63       int ow, oh, nw, nh;
64       struct
65       {
66          double x, y;
67       } spos;
68    } size;
69    struct
70    {
71       Eina_Bool show : 1;
72       Evas_Coord x, y ,w ,h;
73    } show;
74    int tsize;
75    Evas_Object *img; // low res version of image (scale down == 8)
76    int nosmooth;
77    int preload_num;
78    Eina_List *grids;
79    Eina_Bool main_load_pending : 1;
80    Eina_Bool resized : 1;
81    Eina_Bool longpressed : 1;
82    Eina_Bool on_hold : 1;
83    Eina_Bool paused : 1;
84    Eina_Bool do_region : 1;
85 };
86
87 struct _Pan
88 {
89    Evas_Object_Smart_Clipped_Data __clipped_data;
90    Widget_Data *wd;
91 };
92
93 static const char *widtype = NULL;
94 static void _del_hook(Evas_Object *obj);
95 static void _theme_hook(Evas_Object *obj);
96 static void _on_focus_hook(void *data, Evas_Object *obj);
97 //static void _show_region_hook(void *data, Evas_Object *obj);
98 static void _sizing_eval(Evas_Object *obj);
99 static void _calc_job(void *data);
100 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__,
101                              Evas_Callback_Type type, void *event_info);
102 static void grid_place(Evas_Object *obj, Grid *g, Evas_Coord px, Evas_Coord py, Evas_Coord ox, Evas_Coord oy, Evas_Coord ow, Evas_Coord oh);
103 static void grid_clear(Evas_Object *obj, Grid *g);
104 static Grid *grid_create(Evas_Object *obj);
105 static void grid_load(Evas_Object *obj, Grid *g);
106
107 static const char SIG_CLICKED[] = "clicked";
108 static const char SIG_PRESS[] = "press";
109 static const char SIG_LONGPRESSED[] = "longpressed";
110 static const char SIG_CLICKED_DOUBLE[] = "clicked,double";
111 static const char SIG_LOAD[] = "load";
112 static const char SIG_LOADED[] = "loaded";
113 static const char SIG_LOAD_DETAIL[] = "load,detail";
114 static const char SIG_LOADED_DETAIL[] = "loaded,detail";
115 static const char SIG_ZOOM_START[] = "zoom,start";
116 static const char SIG_ZOOM_STOP[] = "zoom,stop";
117 static const char SIG_ZOOM_CHANGE[] = "zoom,change";
118 static const char SIG_SCROLL[] = "scroll";
119 static const char SIG_SCROLL_ANIM_START[] = "scroll,anim,start";
120 static const char SIG_SCROLL_ANIM_STOP[] = "scroll,anim,stop";
121 static const char SIG_SCROLL_DRAG_START[] = "scroll,drag,start";
122 static const char SIG_SCROLL_DRAG_STOP[] = "scroll,drag,stop";
123
124 static const Evas_Smart_Cb_Description _signals[] = {
125    {SIG_CLICKED, ""},
126    {SIG_PRESS, ""},
127    {SIG_LONGPRESSED, ""},
128    {SIG_CLICKED_DOUBLE, ""},
129    {SIG_LOAD, ""},
130    {SIG_LOADED, ""},
131    {SIG_LOAD_DETAIL, ""},
132    {SIG_LOADED_DETAIL, ""},
133    {SIG_ZOOM_START, ""},
134    {SIG_ZOOM_STOP, ""},
135    {SIG_ZOOM_CHANGE, ""},
136    {SIG_SCROLL, ""},
137    {SIG_SCROLL_ANIM_START, ""},
138    {SIG_SCROLL_ANIM_STOP, ""},
139    {SIG_SCROLL_DRAG_START, ""},
140    {SIG_SCROLL_DRAG_STOP, ""},
141    {NULL, NULL}
142 };
143
144
145 static int
146 nearest_pow2(int num)
147 {
148    unsigned int n = num - 1;
149    n |= n >> 1;
150    n |= n >> 2;
151    n |= n >> 4;
152    n |= n >> 8;
153    n |= n >> 16;
154    return n + 1;
155 }
156
157 static void
158 img_place(Evas_Object *obj, Evas_Coord px, Evas_Coord py, Evas_Coord ox, Evas_Coord oy, Evas_Coord ow, Evas_Coord oh)
159 {
160    Widget_Data *wd = elm_widget_data_get(obj);
161    Evas_Coord ax, ay, gw, gh;
162    if (!wd) return;
163    ax = 0;
164    ay = 0;
165    gw = wd->size.w;
166    gh = wd->size.h;
167    if (ow > gw) ax = (ow - gw) / 2;
168    if (oh > gh) ay = (oh - gh) / 2;
169    evas_object_move(wd->img, ox + 0 - px + ax, oy + 0 - py + ay);
170    evas_object_resize(wd->img, gw, gh);
171
172    if (wd->show.show)
173      {
174         wd->show.show = EINA_FALSE;
175         elm_smart_scroller_child_region_show(wd->scr, wd->show.x, wd->show.y, wd->show.w, wd->show.h);
176      }
177 }
178
179 static void
180 grid_place(Evas_Object *obj, Grid *g, Evas_Coord px, Evas_Coord py, Evas_Coord ox, Evas_Coord oy, Evas_Coord ow, Evas_Coord oh)
181 {
182    Widget_Data *wd = elm_widget_data_get(obj);
183    Evas_Coord ax, ay, gw, gh, tx, ty;
184    int x, y;
185    if (!wd) return;
186    ax = 0;
187    ay = 0;
188    gw = wd->size.w;
189    gh = wd->size.h;
190    if (ow > gw) ax = (ow - gw) / 2;
191    if (oh > gh) ay = (oh - gh) / 2;
192    for (y = 0; y < g->gh; y++)
193      {
194         for (x = 0; x < g->gw; x++)
195           {
196              int tn, xx, yy, ww, hh;
197
198              tn = (y * g->gw) + x;
199              xx = g->grid[tn].out.x;
200              yy = g->grid[tn].out.y;
201              ww = g->grid[tn].out.w;
202              hh = g->grid[tn].out.h;
203              if ((gw != g->w) && (g->w > 0))
204                {
205                   tx = xx;
206                   xx = (gw * xx) / g->w;
207                   ww = ((gw * (tx + ww)) / g->w) - xx;
208                }
209              if ((gh != g->h) && (g->h > 0))
210                {
211                   ty = yy;
212                   yy = (gh * yy) / g->h;
213                   hh = ((gh * (ty + hh)) / g->h) - yy;
214                }
215              evas_object_move(g->grid[tn].img,
216                               ox + xx - px + ax,
217                               oy + yy - py + ay);
218              evas_object_resize(g->grid[tn].img, ww, hh);
219           }
220      }
221 }
222
223 static void
224 grid_clear(Evas_Object *obj, Grid *g)
225 {
226    Widget_Data *wd = elm_widget_data_get(obj);
227    int x, y;
228    if (!wd) return;
229    if (!g->grid) return;
230    for (y = 0; y < g->gh; y++)
231      {
232         for (x = 0; x < g->gw; x++)
233           {
234              int tn;
235
236              tn = (y * g->gw) + x;
237              evas_object_del(g->grid[tn].img);
238              if (g->grid[tn].want)
239                {
240                   wd->preload_num--;
241                   if (!wd->preload_num)
242                     {
243                        edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
244                                                "elm,state,busy,stop", "elm");
245                        evas_object_smart_callback_call(obj, SIG_LOAD_DETAIL, NULL);
246                     }
247                }
248           }
249      }
250    free(g->grid);
251    g->grid = NULL;
252    g->gw = 0;
253    g->gh = 0;
254 }
255
256 static void
257 _tile_preloaded(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
258 {
259    Grid_Item *git = data;
260
261    if (git->want)
262      {
263         git->want = 0;
264         evas_object_show(git->img);
265         git->have = 1;
266         git->wd->preload_num--;
267         if (!git->wd->preload_num)
268           {
269              edje_object_signal_emit(elm_smart_scroller_edje_object_get(git->wd->scr),
270                                      "elm,state,busy,stop", "elm");
271              evas_object_smart_callback_call(git->wd->obj, SIG_LOADED_DETAIL, NULL);
272           }
273      }
274 }
275
276 static int
277 grid_zoom_calc(double zoom)
278 {
279    int z = zoom;
280    if (z < 1) z = 1;
281    return nearest_pow2(z);
282 }
283
284 static Grid *
285 grid_create(Evas_Object *obj)
286 {
287    Widget_Data *wd = elm_widget_data_get(obj);
288    int x, y;
289    Grid *g;
290
291    if (!wd) return NULL;
292    g = calloc(1, sizeof(Grid));
293    if (!g) return NULL;
294
295    g->zoom = grid_zoom_calc(wd->zoom);
296    g->tsize = wd->tsize;
297    g->iw = wd->size.imw;
298    g->ih = wd->size.imh;
299
300    g->w = g->iw / g->zoom;
301    g->h = g->ih / g->zoom;
302    if (g->zoom >= 8)
303      {
304         free(g);
305         return NULL;
306      }
307    if (wd->do_region)
308      {
309         g->gw = (g->w + g->tsize - 1) / g->tsize;
310         g->gh = (g->h + g->tsize - 1) / g->tsize;
311      }
312    else
313      {
314         g->gw = 1;
315         g->gh = 1;
316      }
317    g->grid = calloc(1, sizeof(Grid_Item) * g->gw * g->gh);
318    if (!g->grid)
319      {
320         g->gw = 0;
321         g->gh = 0;
322         return g;
323      }
324    for (y = 0; y < g->gh; y++)
325      {
326         for (x = 0; x < g->gw; x++)
327           {
328              int tn;
329
330              tn = (y * g->gw) + x;
331              g->grid[tn].src.x = x * g->tsize;
332              if (x == (g->gw - 1))
333                g->grid[tn].src.w = g->w - ((g->gw - 1) * g->tsize);
334              else
335                g->grid[tn].src.w = g->tsize;
336              g->grid[tn].src.y = y * g->tsize;
337              if (y == (g->gh - 1))
338                g->grid[tn].src.h = g->h - ((g->gh - 1) * g->tsize);
339              else
340                g->grid[tn].src.h = g->tsize;
341
342              g->grid[tn].out.x = g->grid[tn].src.x;
343              g->grid[tn].out.y = g->grid[tn].src.y;
344              g->grid[tn].out.w = g->grid[tn].src.w;
345              g->grid[tn].out.h = g->grid[tn].src.h;
346
347              g->grid[tn].wd = wd;
348              g->grid[tn].img =
349                 evas_object_image_add(evas_object_evas_get(obj));
350              evas_object_image_load_orientation_set(g->grid[tn].img, EINA_TRUE);
351              evas_object_image_scale_hint_set
352                 (g->grid[tn].img, EVAS_IMAGE_SCALE_HINT_DYNAMIC);
353              evas_object_pass_events_set(g->grid[tn].img, EINA_TRUE);
354              evas_object_smart_member_add(g->grid[tn].img,
355                                           wd->pan_smart);
356              elm_widget_sub_object_add(obj, g->grid[tn].img);
357              evas_object_image_filled_set(g->grid[tn].img, 1);
358              evas_object_event_callback_add(g->grid[tn].img,
359                                             EVAS_CALLBACK_IMAGE_PRELOADED,
360                                             _tile_preloaded,
361                                             &(g->grid[tn]));
362           }
363      }
364    return g;
365 }
366
367 static void
368 grid_load(Evas_Object *obj, Grid *g)
369 {
370    Widget_Data *wd = elm_widget_data_get(obj);
371    int x, y;
372    Evas_Coord ox, oy, ow, oh, cvx, cvy, cvw, cvh, gw, gh, tx, ty;
373    if (!wd) return;
374    evas_object_geometry_get(wd->pan_smart, &ox, &oy, &ow, &oh);
375    evas_output_viewport_get(evas_object_evas_get(wd->obj), &cvx, &cvy, &cvw, &cvh);
376    gw = wd->size.w;
377    gh = wd->size.h;
378    for (y = 0; y < g->gh; y++)
379      {
380         for (x = 0; x < g->gw; x++)
381           {
382              int tn, xx, yy, ww, hh;
383              Eina_Bool visible = EINA_FALSE;
384
385              tn = (y * g->gw) + x;
386              xx = g->grid[tn].out.x;
387              yy = g->grid[tn].out.y;
388              ww = g->grid[tn].out.w;
389              hh = g->grid[tn].out.h;
390              if ((gw != g->w) && (g->w > 0))
391                {
392                   tx = xx;
393                   xx = (gw * xx) / g->w;
394                   ww = ((gw * (tx + ww)) / g->w) - xx;
395                }
396              if ((gh != g->h) && (g->h > 0))
397                {
398                   ty = yy;
399                   yy = (gh * yy) / g->h;
400                   hh = ((gh * (ty + hh)) / g->h) - yy;
401                }
402              if (ELM_RECTS_INTERSECT(xx - wd->pan_x + ox,
403                                      yy  - wd->pan_y + oy,
404                                      ww, hh,
405                                      cvx, cvy, cvw, cvh))
406                visible = 1;
407              if ((visible) && (!g->grid[tn].have) && (!g->grid[tn].want))
408                {
409                   g->grid[tn].want = 1;
410                   evas_object_hide(g->grid[tn].img);
411                   evas_object_image_file_set(g->grid[tn].img, NULL, NULL);
412                   evas_object_image_load_scale_down_set(g->grid[tn].img, g->zoom);
413                   evas_object_image_load_region_set(g->grid[tn].img,
414                                                     g->grid[tn].src.x,
415                                                     g->grid[tn].src.y,
416                                                     g->grid[tn].src.w,
417                                                     g->grid[tn].src.h);
418                   evas_object_image_file_set(g->grid[tn].img, wd->file, NULL);
419                   evas_object_image_preload(g->grid[tn].img, 0);
420                   wd->preload_num++;
421                   if (wd->preload_num == 1)
422                     {
423                        edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
424                                                "elm,state,busy,start", "elm");
425                        evas_object_smart_callback_call(obj, SIG_LOAD_DETAIL, NULL);
426                     }
427                }
428              else if ((g->grid[tn].want) && (!visible))
429                {
430                   wd->preload_num--;
431                   if (!wd->preload_num)
432                     {
433                        edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
434                                                "elm,state,busy,stop", "elm");
435                        evas_object_smart_callback_call(obj, SIG_LOADED_DETAIL, NULL);
436                     }
437                   g->grid[tn].want = 0;
438                   evas_object_hide(g->grid[tn].img);
439                   evas_object_image_preload(g->grid[tn].img, 1);
440                   evas_object_image_file_set(g->grid[tn].img, NULL, NULL);
441                }
442              else if ((g->grid[tn].have) && (!visible))
443                {
444                   g->grid[tn].have = 0;
445                   evas_object_hide(g->grid[tn].img);
446                   evas_object_image_preload(g->grid[tn].img, 1);
447                   evas_object_image_file_set(g->grid[tn].img, NULL, NULL);
448                }
449           }
450      }
451 }
452
453 static void
454 grid_clearall(Evas_Object *obj)
455 {
456    Widget_Data *wd = elm_widget_data_get(obj);
457    Grid *g;
458    if (!wd) return;
459    EINA_LIST_FREE(wd->grids, g)
460      {
461         grid_clear(obj, g);
462         free(g);
463      }
464 }
465
466 static void
467 _smooth_update(Evas_Object *obj)
468 {
469    Widget_Data *wd = elm_widget_data_get(obj);
470    int x, y;
471    Eina_List *l;
472    Grid *g;
473    if (!wd) return;
474    EINA_LIST_FOREACH(wd->grids, l, g)
475      {
476         for (y = 0; y < g->gh; y++)
477           {
478              for (x = 0; x < g->gw; x++)
479                {
480                   int tn;
481
482                   tn = (y * g->gw) + x;
483                   evas_object_image_smooth_scale_set(g->grid[tn].img, (!wd->nosmooth));
484                }
485           }
486      }
487    evas_object_image_smooth_scale_set(wd->img, (!wd->nosmooth));
488 }
489
490 static void
491 _grid_raise(Grid *g)
492 {
493    int x, y;
494
495    for (y = 0; y < g->gh; y++)
496      {
497         for (x = 0; x < g->gw; x++)
498           {
499              int tn;
500
501              tn = (y * g->gw) + x;
502              evas_object_raise(g->grid[tn].img);
503           }
504      }
505 }
506
507 static Eina_Bool
508 _scr_timeout(void *data)
509 {
510    Widget_Data *wd = elm_widget_data_get(data);
511    if (!wd) return ECORE_CALLBACK_CANCEL;
512    wd->nosmooth--;
513    if (!wd->nosmooth) _smooth_update(data);
514    wd->scr_timer = NULL;
515    return ECORE_CALLBACK_CANCEL;
516 }
517
518 static void
519 _scr(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
520 {
521    Widget_Data *wd = elm_widget_data_get(data);
522    if (!wd) return;
523    if (!wd->scr_timer)
524      {
525         wd->nosmooth++;
526         if (wd->nosmooth == 1) _smooth_update(data);
527      }
528    if (wd->scr_timer) ecore_timer_del(wd->scr_timer);
529    wd->scr_timer = ecore_timer_add(0.5, _scr_timeout, data);
530 }
531
532 static void
533 _main_preloaded(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
534 {
535    Evas_Object *obj = data;
536    Widget_Data *wd = elm_widget_data_get(obj);
537    Grid *g;
538    if (!wd) return;
539    evas_object_show(wd->img);
540    wd->main_load_pending = 0;
541    g = grid_create(obj);
542    if (g)
543      {
544         wd->grids = eina_list_prepend(wd->grids, g);
545         grid_load(wd->obj, g);
546      }
547    if (wd->calc_job) ecore_job_del(wd->calc_job);
548    wd->calc_job = ecore_job_add(_calc_job, wd);
549    evas_object_smart_callback_call(data, SIG_LOADED, NULL);
550    wd->preload_num--;
551    if (!wd->preload_num)
552      {
553         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
554                                 "elm,state,busy,stop", "elm");
555         evas_object_smart_callback_call(obj, SIG_LOADED_DETAIL, NULL);
556      }
557 }
558
559 static Eina_Bool
560 zoom_do(Evas_Object *obj, double t)
561 {
562    Widget_Data *wd = elm_widget_data_get(obj);
563    Evas_Coord xx, yy, ow, oh;
564    if (!wd) return ECORE_CALLBACK_CANCEL;
565    wd->size.w = (wd->size.ow * (1.0 - t)) + (wd->size.nw * t);
566    wd->size.h = (wd->size.oh * (1.0 - t)) + (wd->size.nh * t);
567    elm_smart_scroller_child_viewport_size_get(wd->scr, &ow, &oh);
568    xx = (wd->size.spos.x * wd->size.w) - (ow / 2);
569    yy = (wd->size.spos.y * wd->size.h) - (oh / 2);
570    if (xx < 0) xx = 0;
571    else if (xx > (wd->size.w - ow)) xx = wd->size.w - ow;
572    if (yy < 0) yy = 0;
573    else if (yy > (wd->size.h - oh)) yy = wd->size.h - oh;
574
575    wd->show.show = EINA_TRUE;
576    wd->show.x = xx;
577    wd->show.y = yy;
578    wd->show.w = ow;
579    wd->show.h = oh;
580
581    if (wd->calc_job) ecore_job_del(wd->calc_job);
582    wd->calc_job = ecore_job_add(_calc_job, wd);
583    if (t >= 1.0)
584      {
585         Eina_List *l, *l_next;
586         Grid *g;
587
588         EINA_LIST_FOREACH_SAFE(wd->grids, l, l_next, g)
589           {
590              if (g->dead)
591                {
592                   wd->grids = eina_list_remove_list(wd->grids, l);
593                   grid_clear(obj, g);
594                   free(g);
595                }
596           }
597         return ECORE_CALLBACK_CANCEL;
598      }
599    return ECORE_CALLBACK_RENEW;
600 }
601
602
603 static Eina_Bool
604 _zoom_anim(void *data)
605 {
606    Evas_Object *obj = data;
607    Widget_Data *wd = elm_widget_data_get(obj);
608    double t;
609    Eina_Bool go;
610    if (!wd) return ECORE_CALLBACK_CANCEL;
611    t = ecore_loop_time_get();
612    if (t >= wd->t_end)
613      t = 1.0;
614    else if (wd->t_end > wd->t_start)
615      t = (t - wd->t_start) / (wd->t_end - wd->t_start);
616    else
617      t = 1.0;
618    t = 1.0 - t;
619    t = 1.0 - (t * t);
620    go = zoom_do(obj, t);
621    if (!go)
622      {
623         wd->nosmooth--;
624         if (!wd->nosmooth) _smooth_update(data);
625         wd->zoom_animator = NULL;
626         evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
627      }
628    return go;
629 }
630
631 static void
632 _mouse_move(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
633 {
634    Widget_Data *wd = elm_widget_data_get(data);
635    //   Evas_Event_Mouse_Move *ev = event_info;
636    if (!wd) return;
637 }
638
639 static Eina_Bool
640 _long_press(void *data)
641 {
642    Widget_Data *wd = elm_widget_data_get(data);
643    if (!wd) return ECORE_CALLBACK_CANCEL;
644    wd->long_timer = NULL;
645    wd->longpressed = EINA_TRUE;
646    evas_object_smart_callback_call(data, SIG_LONGPRESSED, NULL);
647    return ECORE_CALLBACK_CANCEL;
648 }
649
650 static void
651 _mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
652 {
653    Widget_Data *wd = elm_widget_data_get(data);
654    Evas_Event_Mouse_Down *ev = event_info;
655    if (!wd) return;
656    if (ev->button != 1) return;
657    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) wd->on_hold = EINA_TRUE;
658    else wd->on_hold = EINA_FALSE;
659    if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
660      evas_object_smart_callback_call(data, SIG_CLICKED_DOUBLE, NULL);
661    else
662      evas_object_smart_callback_call(data, SIG_PRESS, NULL);
663    wd->longpressed = EINA_FALSE;
664    if (wd->long_timer) ecore_timer_del(wd->long_timer);
665    wd->long_timer = ecore_timer_add(_elm_config->longpress_timeout, _long_press, data);
666 }
667
668 static void
669 _mouse_up(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
670 {
671    Widget_Data *wd = elm_widget_data_get(data);
672    Evas_Event_Mouse_Up *ev = event_info;
673    if (!wd) return;
674    if (ev->button != 1) return;
675    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) wd->on_hold = EINA_TRUE;
676    else wd->on_hold = EINA_FALSE;
677    if (wd->long_timer)
678      {
679         ecore_timer_del(wd->long_timer);
680         wd->long_timer = NULL;
681      }
682    if (!wd->on_hold)
683      evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
684    wd->on_hold = EINA_FALSE;
685 }
686
687 static Evas_Smart_Class _pan_sc = EVAS_SMART_CLASS_INIT_NULL;
688
689 static void
690 _del_hook(Evas_Object *obj)
691 {
692    Widget_Data *wd = elm_widget_data_get(obj);
693    Grid *g;
694    if (!wd) return;
695    EINA_LIST_FREE(wd->grids, g)
696      {
697         if (g->grid) free(g->grid);
698         free(g);
699      }
700    evas_object_del(wd->pan_smart);
701    wd->pan_smart = NULL;
702    if (wd->file) eina_stringshare_del(wd->file);
703    if (wd->calc_job) ecore_job_del(wd->calc_job);
704    if (wd->scr_timer) ecore_timer_del(wd->scr_timer);
705    if (wd->zoom_animator) ecore_animator_del(wd->zoom_animator);
706    if (wd->long_timer) ecore_timer_del(wd->long_timer);
707    free(wd);
708 }
709
710 static void
711 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
712 {
713    Widget_Data *wd = elm_widget_data_get(obj);
714    if (!wd) return;
715    if (elm_widget_focus_get(obj))
716      {
717         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr), "elm,action,focus", "elm");
718         evas_object_focus_set(wd->obj, EINA_TRUE);
719      }
720    else
721      {
722         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr), "elm,action,unfocus", "elm");
723         evas_object_focus_set(wd->obj, EINA_FALSE);
724      }
725 }
726
727 static void
728 _theme_hook(Evas_Object *obj)
729 {
730    Widget_Data *wd = elm_widget_data_get(obj);
731    if (!wd) return;
732    elm_smart_scroller_object_theme_set(obj, wd->scr, "photocam", "base", elm_widget_style_get(obj));
733    //   edje_object_scale_set(wd->scr, elm_widget_scale_get(obj) * _elm_config->scale);
734    _sizing_eval(obj);
735 }
736
737 /*
738 static void
739 _show_region_hook(void *data, Evas_Object *obj)
740 {
741    Widget_Data *wd = elm_widget_data_get(data);
742    Evas_Coord x, y, w, h;
743    if (!wd) return;
744    elm_widget_show_region_get(obj, &x, &y, &w, &h);
745    elm_smart_scroller_child_region_show(wd->scr, x, y, w, h);
746 }
747 */
748
749 static void
750 _sizing_eval(Evas_Object *obj)
751 {
752    Widget_Data *wd = elm_widget_data_get(obj);
753    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
754    if (!wd) return;
755    //   evas_object_size_hint_min_get(wd->scr, &minw, &minh);
756    evas_object_size_hint_max_get(wd->scr, &maxw, &maxh);
757    //   minw = -1;
758    //   minh = -1;
759    //   if (wd->mode != ELM_LIST_LIMIT) minw = -1;
760    evas_object_size_hint_min_set(obj, minw, minh);
761    evas_object_size_hint_max_set(obj, maxw, maxh);
762 }
763
764 static void
765 _calc_job(void *data)
766 {
767    Widget_Data *wd = data;
768    Evas_Coord minw, minh;
769    if (!wd) return;
770    minw = wd->size.w;
771    minh = wd->size.h;
772    if (wd->resized)
773      {
774         wd->resized = 0;
775         if (wd->mode != ELM_PHOTOCAM_ZOOM_MODE_MANUAL)
776           {
777              double tz = wd->zoom;
778              wd->zoom = 0.0;
779              elm_photocam_zoom_set(wd->obj, tz);
780           }
781      }
782    if ((minw != wd->minw) || (minh != wd->minh))
783      {
784         wd->minw = minw;
785         wd->minh = minh;
786         evas_object_smart_callback_call(wd->pan_smart, "changed", NULL);
787         _sizing_eval(wd->obj);
788      }
789    wd->calc_job = NULL;
790    evas_object_smart_changed(wd->pan_smart);
791 }
792
793 static void
794 _pan_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
795 {
796    Pan *sd = evas_object_smart_data_get(obj);
797    if (!sd) return;
798    if ((x == sd->wd->pan_x) && (y == sd->wd->pan_y)) return;
799    sd->wd->pan_x = x;
800    sd->wd->pan_y = y;
801    evas_object_smart_changed(obj);
802 }
803
804 static void
805 _pan_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
806 {
807    Pan *sd = evas_object_smart_data_get(obj);
808    if (!sd) return;
809    if (x) *x = sd->wd->pan_x;
810    if (y) *y = sd->wd->pan_y;
811 }
812
813 static void
814 _pan_max_get(Evas_Object *obj, Evas_Coord *x, Evas_Coord *y)
815 {
816    Pan *sd = evas_object_smart_data_get(obj);
817    Evas_Coord ow, oh;
818    if (!sd) return;
819    evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
820    ow = sd->wd->minw - ow;
821    if (ow < 0) ow = 0;
822    oh = sd->wd->minh - oh;
823    if (oh < 0) oh = 0;
824    if (x) *x = ow;
825    if (y) *y = oh;
826 }
827
828 static void
829 _pan_min_get(Evas_Object *obj __UNUSED__, Evas_Coord *x, Evas_Coord *y)
830 {
831    if (x) *x = 0;
832    if (y) *y = 0;
833 }
834
835 static void
836 _pan_child_size_get(Evas_Object *obj, Evas_Coord *w, Evas_Coord *h)
837 {
838    Pan *sd = evas_object_smart_data_get(obj);
839    if (!sd) return;
840    if (w) *w = sd->wd->minw;
841    if (h) *h = sd->wd->minh;
842 }
843
844 static void
845 _pan_add(Evas_Object *obj)
846 {
847    Pan *sd;
848    Evas_Object_Smart_Clipped_Data *cd;
849    _pan_sc.add(obj);
850    cd = evas_object_smart_data_get(obj);
851    if (!cd) return;
852    sd = calloc(1, sizeof(Pan));
853    if (!sd) return;
854    sd->__clipped_data = *cd;
855    free(cd);
856    evas_object_smart_data_set(obj, sd);
857 }
858
859 static void
860 _pan_del(Evas_Object *obj)
861 {
862    Pan *sd = evas_object_smart_data_get(obj);
863    if (!sd) return;
864    _pan_sc.del(obj);
865 }
866
867 static void
868 _pan_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
869 {
870    Pan *sd = evas_object_smart_data_get(obj);
871    Evas_Coord ow, oh;
872    if (!sd) return;
873    evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
874    if ((ow == w) && (oh == h)) return;
875    sd->wd->resized = 1;
876    if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job);
877    sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd);
878 }
879
880 static void
881 _pan_calculate(Evas_Object *obj)
882 {
883    Pan *sd = evas_object_smart_data_get(obj);
884    Evas_Coord ox, oy, ow, oh;
885    Eina_List *l;
886    Grid *g;
887    if (!sd) return;
888    evas_object_geometry_get(obj, &ox, &oy, &ow, &oh);
889    img_place(sd->wd->obj, sd->wd->pan_x, sd->wd->pan_y, ox, oy, ow, oh);
890    EINA_LIST_FOREACH(sd->wd->grids, l, g)
891      {
892         grid_load(sd->wd->obj, g);
893         grid_place(sd->wd->obj, g, sd->wd->pan_x, sd->wd->pan_y, ox, oy, ow, oh);
894      }
895 }
896
897 static void
898 _pan_move(Evas_Object *obj, Evas_Coord x __UNUSED__, Evas_Coord y __UNUSED__)
899 {
900    Pan *sd = evas_object_smart_data_get(obj);
901    if (!sd) return;
902    if (sd->wd->calc_job) ecore_job_del(sd->wd->calc_job);
903    sd->wd->calc_job = ecore_job_add(_calc_job, sd->wd);
904 }
905
906 static void
907 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
908 {
909    Widget_Data *wd = elm_widget_data_get(obj);
910    if (!wd) return;
911    elm_smart_scroller_hold_set(wd->scr, 1);
912 }
913
914 static void
915 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
916 {
917    Widget_Data *wd = elm_widget_data_get(obj);
918    if (!wd) return;
919    elm_smart_scroller_hold_set(wd->scr, 0);
920 }
921
922 static void
923 _freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
924 {
925    Widget_Data *wd = elm_widget_data_get(obj);
926    if (!wd) return;
927    elm_smart_scroller_freeze_set(wd->scr, 1);
928 }
929
930 static void
931 _freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
932 {
933    Widget_Data *wd = elm_widget_data_get(obj);
934    if (!wd) return;
935    elm_smart_scroller_freeze_set(wd->scr, 0);
936 }
937
938 static void
939 _scr_anim_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
940 {
941    evas_object_smart_callback_call(data, SIG_SCROLL_ANIM_START, NULL);
942 }
943
944 static void
945 _scr_anim_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
946 {
947    evas_object_smart_callback_call(data, SIG_SCROLL_ANIM_STOP, NULL);
948 }
949
950 static void
951 _scr_drag_start(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
952 {
953    evas_object_smart_callback_call(data, SIG_SCROLL_DRAG_START, NULL);
954 }
955
956 static void
957 _scr_drag_stop(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
958 {
959    evas_object_smart_callback_call(data, SIG_SCROLL_DRAG_STOP, NULL);
960 }
961
962 static void
963 _scr_scroll(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
964 {
965    evas_object_smart_callback_call(data, SIG_SCROLL, NULL);
966 }
967
968 static Eina_Bool
969 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__,
970             Evas_Callback_Type type, void *event_info)
971 {
972    double zoom;
973    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
974    Evas_Event_Key_Down *ev = event_info;
975    Widget_Data *wd = elm_widget_data_get(obj);
976    if (!wd) return EINA_FALSE;
977    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
978
979    Evas_Coord x = 0;
980    Evas_Coord y = 0;
981    Evas_Coord step_x = 0;
982    Evas_Coord step_y = 0;
983    Evas_Coord v_w = 0;
984    Evas_Coord v_h = 0;
985    Evas_Coord page_x = 0;
986    Evas_Coord page_y = 0;
987
988    elm_smart_scroller_child_pos_get(wd->scr, &x, &y);
989    elm_smart_scroller_step_size_get(wd->scr, &step_x, &step_y);
990    elm_smart_scroller_page_size_get(wd->scr, &page_x, &page_y);
991    elm_smart_scroller_child_viewport_size_get(wd->scr, &v_w, &v_h);
992
993    if ((!strcmp(ev->keyname, "Left")) ||
994        (!strcmp(ev->keyname, "KP_Left")))
995      {
996         x -= step_x;
997      }
998    else if ((!strcmp(ev->keyname, "Right")) ||
999             (!strcmp(ev->keyname, "KP_Right")))
1000      {
1001         x += step_x;
1002      }
1003    else if ((!strcmp(ev->keyname, "Up"))  ||
1004             (!strcmp(ev->keyname, "KP_Up")))
1005      {
1006         y -= step_y;
1007      }
1008    else if ((!strcmp(ev->keyname, "Down")) ||
1009             (!strcmp(ev->keyname, "KP_Down")))
1010      {
1011         y += step_y;
1012      }
1013    else if ((!strcmp(ev->keyname, "Prior")) ||
1014             (!strcmp(ev->keyname, "KP_Prior")))
1015      {
1016         if (page_y < 0)
1017           y -= -(page_y * v_h) / 100;
1018         else
1019           y -= page_y;
1020      }
1021    else if ((!strcmp(ev->keyname, "Next")) ||
1022             (!strcmp(ev->keyname, "KP_Next")))
1023      {
1024         if (page_y < 0)
1025           y += -(page_y * v_h) / 100;
1026         else
1027           y += page_y;
1028      }
1029    else if ((!strcmp(ev->keyname, "KP_Add")))
1030      {
1031         zoom = elm_photocam_zoom_get(obj);
1032         zoom -= 0.5;
1033         elm_photocam_zoom_mode_set(obj, ELM_PHOTOCAM_ZOOM_MODE_MANUAL);
1034         elm_photocam_zoom_set(obj, zoom);
1035         return EINA_TRUE;
1036      }
1037    else if ((!strcmp(ev->keyname, "KP_Subtract")))
1038      {
1039         zoom = elm_photocam_zoom_get(obj);
1040         zoom += 0.5;
1041         elm_photocam_zoom_mode_set(obj, ELM_PHOTOCAM_ZOOM_MODE_MANUAL);
1042         elm_photocam_zoom_set(obj, zoom);
1043         return EINA_TRUE;
1044      }
1045    else return EINA_FALSE;
1046
1047    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
1048    elm_smart_scroller_child_pos_set(wd->scr, x, y);
1049
1050    return EINA_TRUE;
1051 }
1052
1053 EAPI Evas_Object *
1054 elm_photocam_add(Evas_Object *parent)
1055 {
1056    Evas_Object *obj;
1057    Evas *e;
1058    Widget_Data *wd;
1059    Evas_Coord minw, minh;
1060    static Evas_Smart *smart = NULL;
1061    Eina_Bool bounce = _elm_config->thumbscroll_bounce_enable;
1062
1063    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
1064
1065    ELM_SET_WIDTYPE(widtype, "photocam");
1066    elm_widget_type_set(obj, "photocam");
1067    elm_widget_sub_object_add(parent, obj);
1068    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
1069    elm_widget_data_set(obj, wd);
1070    elm_widget_del_hook_set(obj, _del_hook);
1071    elm_widget_theme_hook_set(obj, _theme_hook);
1072    elm_widget_can_focus_set(obj, EINA_TRUE);
1073    elm_widget_event_hook_set(obj, _event_hook);
1074
1075    wd->scr = elm_smart_scroller_add(e);
1076    elm_smart_scroller_widget_set(wd->scr, obj);
1077    elm_smart_scroller_object_theme_set(obj, wd->scr, "photocam", "base", "default");
1078    evas_object_smart_callback_add(wd->scr, "scroll", _scr, obj);
1079    evas_object_smart_callback_add(wd->scr, "drag", _scr, obj);
1080    elm_widget_resize_object_set(obj, wd->scr);
1081
1082    evas_object_smart_callback_add(wd->scr, "animate,start", _scr_anim_start, obj);
1083    evas_object_smart_callback_add(wd->scr, "animate,stop", _scr_anim_stop, obj);
1084    evas_object_smart_callback_add(wd->scr, "drag,start", _scr_drag_start, obj);
1085    evas_object_smart_callback_add(wd->scr, "drag,stop", _scr_drag_stop, obj);
1086    evas_object_smart_callback_add(wd->scr, "scroll", _scr_scroll, obj);
1087
1088    elm_smart_scroller_bounce_allow_set(wd->scr, bounce, bounce);
1089
1090    wd->obj = obj;
1091
1092    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
1093    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
1094    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
1095    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
1096
1097    if (!smart)
1098      {
1099         static Evas_Smart_Class sc;
1100
1101         evas_object_smart_clipped_smart_set(&_pan_sc);
1102         sc = _pan_sc;
1103         sc.name = "elm_photocam_pan";
1104         sc.version = EVAS_SMART_CLASS_VERSION;
1105         sc.add = _pan_add;
1106         sc.del = _pan_del;
1107         sc.resize = _pan_resize;
1108         sc.move = _pan_move;
1109         sc.calculate = _pan_calculate;
1110         smart = evas_smart_class_new(&sc);
1111      }
1112    if (smart)
1113      {
1114         wd->pan_smart = evas_object_smart_add(e, smart);
1115         wd->pan = evas_object_smart_data_get(wd->pan_smart);
1116         wd->pan->wd = wd;
1117      }
1118
1119    elm_smart_scroller_extern_pan_set(wd->scr, wd->pan_smart,
1120                                      _pan_set, _pan_get, _pan_max_get,
1121                                      _pan_min_get, _pan_child_size_get);
1122
1123    wd->zoom = 1;
1124    wd->mode = ELM_PHOTOCAM_ZOOM_MODE_MANUAL;
1125
1126    wd->tsize = 512;
1127
1128    wd->img = evas_object_image_add(e);
1129    evas_object_image_load_orientation_set(wd->img, EINA_TRUE);
1130    evas_object_image_scale_hint_set(wd->img, EVAS_IMAGE_SCALE_HINT_DYNAMIC);
1131    evas_object_event_callback_add(wd->img, EVAS_CALLBACK_MOUSE_DOWN,
1132                                   _mouse_down, obj);
1133    evas_object_event_callback_add(wd->img, EVAS_CALLBACK_MOUSE_UP,
1134                                   _mouse_up, obj);
1135    evas_object_event_callback_add(wd->img, EVAS_CALLBACK_MOUSE_MOVE,
1136                                   _mouse_move, obj);
1137    evas_object_image_scale_hint_set(wd->img, EVAS_IMAGE_SCALE_HINT_STATIC);
1138    evas_object_smart_member_add(wd->img, wd->pan_smart);
1139    elm_widget_sub_object_add(obj, wd->img);
1140    evas_object_image_filled_set(wd->img, 1);
1141    evas_object_event_callback_add(wd->img, EVAS_CALLBACK_IMAGE_PRELOADED,
1142                                   _main_preloaded, obj);
1143
1144    edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr),
1145                              &minw, &minh);
1146    evas_object_size_hint_min_set(obj, minw, minh);
1147
1148    evas_object_smart_callbacks_descriptions_set(obj, _signals);
1149
1150    _sizing_eval(obj);
1151    return obj;
1152 }
1153
1154 EAPI Evas_Load_Error
1155 elm_photocam_file_set(Evas_Object *obj, const char *file)
1156 {
1157    ELM_CHECK_WIDTYPE(obj, widtype) EVAS_LOAD_ERROR_NONE;
1158    Widget_Data *wd = elm_widget_data_get(obj);
1159    int w, h;
1160    if (!wd) return EVAS_LOAD_ERROR_GENERIC;
1161    if (!eina_stringshare_replace(&wd->file, file)) return EVAS_LOAD_ERROR_NONE;
1162    grid_clearall(obj);
1163
1164    evas_object_hide(wd->img);
1165    evas_object_image_smooth_scale_set(wd->img, (wd->nosmooth == 0));
1166    evas_object_image_file_set(wd->img, NULL, NULL);
1167    evas_object_image_load_scale_down_set(wd->img, 0);
1168    evas_object_image_file_set(wd->img, wd->file, NULL);
1169    evas_object_image_size_get(wd->img, &w, &h);
1170    wd->do_region = evas_object_image_region_support_get(wd->img);
1171    wd->size.imw = w;
1172    wd->size.imh = h;
1173    wd->size.w = wd->size.imw / wd->zoom;
1174    wd->size.h = wd->size.imh / wd->zoom;
1175    if (wd->zoom_animator)
1176      {
1177         wd->nosmooth--;
1178         if (wd->nosmooth == 0) _smooth_update(obj);
1179         ecore_animator_del(wd->zoom_animator);
1180         wd->zoom_animator = NULL;
1181      }
1182    evas_object_image_file_set(wd->img, NULL, NULL);
1183    evas_object_image_load_scale_down_set(wd->img, 8);
1184    evas_object_image_file_set(wd->img, wd->file, NULL);
1185    evas_object_image_preload(wd->img, 0);
1186    wd->main_load_pending = 1;
1187    if (wd->calc_job) ecore_job_del(wd->calc_job);
1188    wd->calc_job = ecore_job_add(_calc_job, wd);
1189    evas_object_smart_callback_call(obj, SIG_LOAD, NULL);
1190    wd->preload_num++;
1191    if (wd->preload_num == 1)
1192      {
1193         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
1194                                 "elm,state,busy,start", "elm");
1195         evas_object_smart_callback_call(obj, SIG_LOAD_DETAIL, NULL);
1196      }
1197      {
1198         double tz = wd->zoom;
1199         wd->zoom = 0.0;
1200         elm_photocam_zoom_set(wd->obj, tz);
1201      }
1202    return evas_object_image_load_error_get(wd->img);
1203 }
1204
1205 EAPI const char *
1206 elm_photocam_file_get(const Evas_Object *obj)
1207 {
1208    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1209    Widget_Data *wd = elm_widget_data_get(obj);
1210    if (!wd) return NULL;
1211    return wd->file;
1212 }
1213
1214 EAPI void
1215 elm_photocam_zoom_set(Evas_Object *obj, double zoom)
1216 {
1217    ELM_CHECK_WIDTYPE(obj, widtype);
1218    Widget_Data *wd = elm_widget_data_get(obj);
1219    Eina_List *l;
1220    Grid *g, *g_zoom = NULL;
1221    Evas_Coord pw, ph, rx, ry, rw, rh;
1222    double z;
1223    int zoom_changed = 0, started = 0;
1224    Ecore_Animator *an;
1225    if (!wd) return;
1226    if (zoom <= (1.0 / 256.0)) zoom = (1.0 / 256.0);
1227    if (zoom == wd->zoom) return;
1228    wd->zoom = zoom;
1229    wd->size.ow = wd->size.w;
1230    wd->size.oh = wd->size.h;
1231    elm_smart_scroller_child_pos_get(wd->scr, &rx, &ry);
1232    elm_smart_scroller_child_viewport_size_get(wd->scr, &rw, &rh);
1233    if ((rw <= 0) || (rh <= 0)) return;
1234
1235    if (wd->mode == ELM_PHOTOCAM_ZOOM_MODE_MANUAL)
1236      {
1237         wd->size.nw = (double)wd->size.imw / wd->zoom;
1238         wd->size.nh = (double)wd->size.imh / wd->zoom;
1239      }
1240    else if (wd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FIT)
1241      {
1242         if ((wd->size.imw < 1) || (wd->size.imh < 1))
1243           {
1244              wd->size.nw = 0;
1245              wd->size.nh = 0;
1246           }
1247         else
1248           {
1249              ph = (wd->size.imh * rw) / wd->size.imw;
1250              if (ph > rh)
1251                {
1252                   pw = (wd->size.imw * rh) / wd->size.imh;
1253                   ph = rh;
1254                }
1255              else
1256                {
1257                   pw = rw;
1258                }
1259              if (wd->size.imw > wd->size.imh)
1260                z = (double)wd->size.imw / pw;
1261              else
1262                z = (double)wd->size.imh / ph;
1263              if (z != wd->zoom)
1264                zoom_changed = 1;
1265              wd->zoom = z;
1266              wd->size.nw = pw;
1267              wd->size.nh = ph;
1268           }
1269      }
1270    else if (wd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FILL)
1271      {
1272         if ((wd->size.imw < 1) || (wd->size.imh < 1))
1273           {
1274              wd->size.nw = 0;
1275              wd->size.nw = 0;
1276           }
1277         else
1278           {
1279              ph = (wd->size.imh * rw) / wd->size.imw;
1280              if (ph < rh)
1281                {
1282                   pw = (wd->size.imw * rh) / wd->size.imh;
1283                   ph = rh;
1284                }
1285              else
1286                {
1287                   pw = rw;
1288                }
1289              if (wd->size.imw > wd->size.imh)
1290                z = (double)wd->size.imw / pw;
1291              else
1292                z = (double)wd->size.imh / ph;
1293              if (z != wd->zoom)
1294                zoom_changed = 1;
1295              wd->zoom = z;
1296              wd->size.nw = pw;
1297              wd->size.nh = ph;
1298           }
1299      }
1300    else if (wd->mode == ELM_PHOTOCAM_ZOOM_MODE_AUTO_FIT_IN)
1301      {
1302         if ((wd->size.imw < 1) || (wd->size.imh < 1))
1303           {
1304              wd->size.nw = 0;
1305              wd->size.nh = 0;
1306           }
1307         else if ((wd->size.imw < rw) && (wd->size.imh < rh))
1308           {
1309              if (1 != wd->zoom) zoom_changed = 1;
1310              wd->zoom = 1;
1311              wd->size.nw = wd->size.imw;
1312              wd->size.nh = wd->size.imh;
1313           }
1314         else
1315           {
1316              ph = (wd->size.imh * rw) / wd->size.imw;
1317              if (ph > rh)
1318                {
1319                   pw = (wd->size.imw * rh) / wd->size.imh;
1320                   ph = rh;
1321                }
1322              else
1323                pw = rw;
1324              if (wd->size.imw > wd->size.imh)
1325                z = (double)wd->size.imw / pw;
1326              else
1327                z = (double)wd->size.imh / ph;
1328              if (z != wd->zoom)
1329                zoom_changed = 1;
1330              wd->zoom = z;
1331              wd->size.nw = pw;
1332              wd->size.nh = ph;
1333           }
1334      }
1335    if (wd->main_load_pending)
1336      {
1337         wd->size.w = wd->size.nw;
1338         wd->size.h = wd->size.nh;
1339         goto done;
1340      }
1341    EINA_LIST_FOREACH(wd->grids, l, g)
1342      {
1343         if (g->zoom == grid_zoom_calc(wd->zoom))
1344           {
1345              wd->grids = eina_list_remove(wd->grids, g);
1346              wd->grids = eina_list_prepend(wd->grids, g);
1347              _grid_raise(g);
1348              goto done;
1349           }
1350      }
1351    g = grid_create(obj);
1352    if (g)
1353      {
1354         if (eina_list_count(wd->grids) > 1)
1355           {
1356              g_zoom = eina_list_last(wd->grids)->data;
1357              wd->grids = eina_list_remove(wd->grids, g_zoom);
1358              grid_clear(obj, g_zoom);
1359              free(g_zoom);
1360              EINA_LIST_FOREACH(wd->grids, l, g_zoom)
1361                {
1362                   g_zoom->dead = 1;
1363                }
1364           }
1365         wd->grids = eina_list_prepend(wd->grids, g);
1366      }
1367    else
1368      {
1369         EINA_LIST_FREE(wd->grids, g)
1370           {
1371              grid_clear(obj, g);
1372              free(g);
1373           }
1374      }
1375 done:
1376    wd->t_start = ecore_loop_time_get();
1377    wd->t_end = wd->t_start + _elm_config->zoom_friction;
1378    if ((wd->size.w > 0) && (wd->size.h > 0))
1379      {
1380         wd->size.spos.x = (double)(rx + (rw / 2)) / (double)wd->size.w;
1381         wd->size.spos.y = (double)(ry + (rh / 2)) / (double)wd->size.h;
1382      }
1383    else
1384      {
1385         wd->size.spos.x = 0.5;
1386         wd->size.spos.y = 0.5;
1387      }
1388    if (rw > wd->size.w) wd->size.spos.x = 0.5;
1389    if (rh > wd->size.h) wd->size.spos.y = 0.5;
1390    if (wd->size.spos.x > 1.0) wd->size.spos.x = 1.0;
1391    if (wd->size.spos.y > 1.0) wd->size.spos.y = 1.0;
1392    if (wd->paused)
1393      {
1394         zoom_do(obj, 1.0);
1395      }
1396    else
1397      {
1398         if (!wd->zoom_animator)
1399           {
1400              wd->zoom_animator = ecore_animator_add(_zoom_anim, obj);
1401              wd->nosmooth++;
1402              if (wd->nosmooth == 1) _smooth_update(obj);
1403              started = 1;
1404           }
1405      }
1406    an = wd->zoom_animator;
1407    if (an)
1408      {
1409         if (!_zoom_anim(obj))
1410           {
1411              ecore_animator_del(an);
1412              an = NULL;
1413           }
1414      }
1415    if (wd->calc_job) ecore_job_del(wd->calc_job);
1416    wd->calc_job = ecore_job_add(_calc_job, wd);
1417    if (!wd->paused)
1418      {
1419         if (started)
1420           evas_object_smart_callback_call(obj, SIG_ZOOM_START, NULL);
1421         if (!an)
1422           evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1423      }
1424    if (zoom_changed)
1425      evas_object_smart_callback_call(obj, SIG_ZOOM_CHANGE, NULL);
1426 }
1427
1428 EAPI double
1429 elm_photocam_zoom_get(const Evas_Object *obj)
1430 {
1431    ELM_CHECK_WIDTYPE(obj, widtype) 1.0;
1432    Widget_Data *wd = elm_widget_data_get(obj);
1433    if (!wd) return 1.0;
1434    return wd->zoom;
1435 }
1436
1437 EAPI void
1438 elm_photocam_zoom_mode_set(Evas_Object *obj, Elm_Photocam_Zoom_Mode mode)
1439 {
1440    ELM_CHECK_WIDTYPE(obj, widtype);
1441    Widget_Data *wd = elm_widget_data_get(obj);
1442    if (!wd) return;
1443    if (wd->mode == mode) return;
1444    wd->mode = mode;
1445      {
1446         double tz = wd->zoom;
1447         wd->zoom = 0.0;
1448         elm_photocam_zoom_set(wd->obj, tz);
1449      }
1450 }
1451
1452 EAPI Elm_Photocam_Zoom_Mode
1453 elm_photocam_zoom_mode_get(const Evas_Object *obj)
1454 {
1455    ELM_CHECK_WIDTYPE(obj, widtype) ELM_PHOTOCAM_ZOOM_MODE_LAST;
1456    Widget_Data *wd = elm_widget_data_get(obj);
1457    if (!wd) return ELM_PHOTOCAM_ZOOM_MODE_LAST;
1458    return wd->mode;
1459 }
1460
1461 EAPI void
1462 elm_photocam_image_size_get(const Evas_Object *obj, int *w, int *h)
1463 {
1464    ELM_CHECK_WIDTYPE(obj, widtype);
1465    Widget_Data *wd = elm_widget_data_get(obj);
1466    if (!wd) return;
1467    if (w) *w = wd->size.imw;
1468    if (h) *h = wd->size.imh;
1469 }
1470
1471 EAPI void
1472 elm_photocam_region_get(const Evas_Object *obj, int *x, int *y, int *w, int *h)
1473 {
1474    ELM_CHECK_WIDTYPE(obj, widtype);
1475    Widget_Data *wd = elm_widget_data_get(obj);
1476    Evas_Coord sx, sy, sw, sh;
1477    if (!wd) return;
1478    elm_smart_scroller_child_pos_get(wd->scr, &sx, &sy);
1479    elm_smart_scroller_child_viewport_size_get(wd->scr, &sw, &sh);
1480    if (wd->size.w > 0)
1481      {
1482         if (x)
1483           {
1484              *x = (wd->size.imw * sx) / wd->size.w;
1485              if (*x > wd->size.imw) *x = wd->size.imw;
1486           }
1487         if (w)
1488           {
1489              *w = (wd->size.imw * sw) / wd->size.w;
1490              if (*w > wd->size.imw) *w = wd->size.imw;
1491              else if (*w < 0) *w = 0;
1492           }
1493      }
1494    else
1495      {
1496         if (x) *x = 0;
1497         if (w) *w = 0;
1498      }
1499
1500    if (wd->size.h > 0)
1501      {
1502         if (y)
1503           {
1504              *y = (wd->size.imh * sy) / wd->size.h;
1505              if (*y > wd->size.imh) *y = wd->size.imh;
1506           }
1507         if (h)
1508           {
1509              *h = (wd->size.imh * sh) / wd->size.h;
1510              if (*h > wd->size.imh) *h = wd->size.imh;
1511              else if (*h < 0) *h = 0;
1512           }
1513      }
1514    else
1515      {
1516         if (y) *y = 0;
1517         if (h) *h = 0;
1518      }
1519 }
1520
1521 EAPI void
1522 elm_photocam_image_region_show(Evas_Object *obj, int x, int y, int w, int h __UNUSED__)
1523 {
1524    ELM_CHECK_WIDTYPE(obj, widtype);
1525    Widget_Data *wd = elm_widget_data_get(obj);
1526    int rx, ry, rw, rh;
1527    if (!wd) return;
1528    if ((wd->size.imw < 1) || (wd->size.imh < 1)) return;
1529    rx = (x * wd->size.w) / wd->size.imw;
1530    ry = (y * wd->size.h) / wd->size.imh;
1531    rw = (w * wd->size.w) / wd->size.imw;
1532    rh = (h * wd->size.h) / wd->size.imh;
1533    if (rw < 1) rw = 1;
1534    if (rh < 1) rh = 1;
1535    if ((rx + rw) > wd->size.w) rx = wd->size.w - rw;
1536    if ((ry + rh) > wd->size.h) ry = wd->size.h - rh;
1537    if (wd->zoom_animator)
1538      {
1539         wd->nosmooth--;
1540         ecore_animator_del(wd->zoom_animator);
1541         wd->zoom_animator = NULL;
1542         zoom_do(obj, 1.0);
1543         evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1544      }
1545    elm_smart_scroller_child_region_show(wd->scr, rx, ry, rw, rh);
1546 }
1547
1548 EAPI void
1549 elm_photocam_image_region_bring_in(Evas_Object *obj, int x, int y, int w, int h __UNUSED__)
1550 {
1551    ELM_CHECK_WIDTYPE(obj, widtype);
1552    Widget_Data *wd = elm_widget_data_get(obj);
1553    int rx, ry, rw, rh;
1554    if (!wd) return;
1555    if ((wd->size.imw < 1) || (wd->size.imh < 1)) return;
1556    rx = (x * wd->size.w) / wd->size.imw;
1557    ry = (y * wd->size.h) / wd->size.imh;
1558    rw = (w * wd->size.w) / wd->size.imw;
1559    rh = (h * wd->size.h) / wd->size.imh;
1560    if (rw < 1) rw = 1;
1561    if (rh < 1) rh = 1;
1562    if ((rx + rw) > wd->size.w) rx = wd->size.w - rw;
1563    if ((ry + rh) > wd->size.h) ry = wd->size.h - rh;
1564    if (wd->zoom_animator)
1565      {
1566         wd->nosmooth--;
1567         if (!wd->nosmooth) _smooth_update(obj);
1568         ecore_animator_del(wd->zoom_animator);
1569         wd->zoom_animator = NULL;
1570         zoom_do(obj, 1.0);
1571         evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1572      }
1573    elm_smart_scroller_region_bring_in(wd->scr, rx, ry, rw, rh);
1574 }
1575
1576 EAPI void
1577 elm_photocam_paused_set(Evas_Object *obj, Eina_Bool paused)
1578 {
1579    ELM_CHECK_WIDTYPE(obj, widtype);
1580    Widget_Data *wd = elm_widget_data_get(obj);
1581    if (!wd) return;
1582    if (wd->paused == !!paused) return;
1583    wd->paused = paused;
1584    if (wd->paused)
1585      {
1586         if (wd->zoom_animator)
1587           {
1588              ecore_animator_del(wd->zoom_animator);
1589              wd->zoom_animator = NULL;
1590              zoom_do(obj, 1.0);
1591              evas_object_smart_callback_call(obj, SIG_ZOOM_STOP, NULL);
1592           }
1593      }
1594 }
1595
1596 EAPI Eina_Bool
1597 elm_photocam_paused_get(const Evas_Object *obj)
1598 {
1599    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1600    Widget_Data *wd = elm_widget_data_get(obj);
1601    if (!wd) return EINA_FALSE;
1602    return wd->paused;
1603 }
1604
1605 EAPI Evas_Object *
1606 elm_photocam_internal_image_get(const Evas_Object *obj)
1607 {
1608    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1609    Widget_Data *wd = elm_widget_data_get(obj);
1610    if (!wd) return NULL;
1611    return wd->img;
1612 }
1613
1614 EAPI void
1615 elm_photocam_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce)
1616 {
1617    ELM_CHECK_WIDTYPE(obj, widtype);
1618    Widget_Data *wd = elm_widget_data_get(obj);
1619    if (!wd) return;
1620    elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce);
1621 }
1622
1623 EAPI void
1624 elm_photocam_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
1625 {
1626    ELM_CHECK_WIDTYPE(obj, widtype);
1627    Widget_Data *wd = elm_widget_data_get(obj);
1628    if (!wd) return;
1629    elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce);
1630 }
1631