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