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