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