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