fix vgd issue with anchors in entires.
[framework/uifw/elementary.git] / src / lib / elm_photo.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "els_icon.h"
4 #include "els_scroller.h"
5
6 typedef struct _Widget_Data Widget_Data;
7
8 struct _Widget_Data
9 {
10    Evas_Object *frm;
11    Evas_Object *img;
12    int size;
13    Eina_Bool fill;
14    Ecore_Timer *longtimer;
15
16 #ifdef HAVE_ELEMENTARY_ETHUMB
17    struct
18      {
19         int id;
20
21         struct
22           {
23              const char *path;
24              const char *key;
25           } file, thumb;
26
27         Ethumb_Exists *exists;
28
29         Ecore_Event_Handler *eeh;
30
31         Ethumb_Thumb_Format format;
32
33       Eina_Bool retry : 1;
34    } thumb;
35 #endif
36
37 };
38
39 #ifdef HAVE_ELEMENTARY_ETHUMB
40 static Eina_List *_elm_icon_retry = NULL;
41 static int _icon_pending_request = 0;
42
43 static void _icon_thumb_exists(void *data, Ethumb_Client *client __UNUSED__, Ethumb_Exists *thread, Eina_Bool exists);
44 static void _icon_thumb_stop(Widget_Data *wd, void *ethumbd);
45 #endif
46
47 static const char *widtype = NULL;
48 static void _del_hook(Evas_Object *obj);
49 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
50 static void _theme_hook(Evas_Object *obj);
51 static void _sizing_eval(Evas_Object *obj);
52 static void _mouse_up(void *data, Evas *e, Evas_Object *obj, void *event_info);
53 static void _mouse_move(void *data, Evas *e, Evas_Object *obj, void *event_info);
54
55 static const char SIG_CLICKED[] = "clicked";
56 static const char SIG_DRAG_START[] = "drag,start";
57 static const char SIG_DRAG_END[] = "drag,end";
58
59 static const Evas_Smart_Cb_Description _signals[] = {
60    {SIG_CLICKED, ""},
61    {SIG_DRAG_START, ""},
62    {SIG_DRAG_END, ""},
63    {NULL, NULL}
64 };
65
66
67 static void
68 _del_hook(Evas_Object *obj)
69 {
70    Widget_Data *wd = elm_widget_data_get(obj);
71 #ifdef HAVE_ELEMENTARY_ETHUMB
72    Ethumb_Client *ethumbd;
73 #endif
74    if (!wd) return;
75
76 #ifdef HAVE_ELEMENTARY_ETHUMB
77    ethumbd = elm_thumb_ethumb_client_get();
78    _icon_thumb_stop(wd, ethumbd);
79
80    eina_stringshare_del(wd->thumb.file.path);
81    eina_stringshare_del(wd->thumb.file.key);
82    eina_stringshare_del(wd->thumb.thumb.path);
83    eina_stringshare_del(wd->thumb.thumb.key);
84
85    if (wd->thumb.eeh)
86      ecore_event_handler_del(wd->thumb.eeh);
87 #endif
88
89    free(wd);
90 }
91
92 static void
93 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
94 {
95    Widget_Data *wd = elm_widget_data_get(obj);
96    if (!wd) return;
97    edje_object_mirrored_set(wd->frm, rtl);
98 }
99
100 static void
101 _theme_hook(Evas_Object *obj)
102 {
103    Widget_Data *wd = elm_widget_data_get(obj);
104    if (!wd) return;
105    _elm_widget_mirrored_reload(obj);
106    _mirrored_set(wd->frm, elm_widget_mirrored_get(obj));
107    _elm_theme_object_set(obj, wd->frm, "photo", "base",
108                          elm_widget_style_get(obj));
109    edje_object_part_swallow(wd->frm, "elm.swallow.content", wd->img);
110    edje_object_scale_set(wd->frm, elm_widget_scale_get(obj) *
111                          _elm_config->scale);
112    _sizing_eval(obj);
113 }
114
115 static void
116 _sizing_eval(Evas_Object *obj)
117 {
118    Widget_Data *wd = elm_widget_data_get(obj);
119    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
120
121    if (!wd) return;
122    if (wd->size > 0)
123      {
124         double scale = 0.0;
125
126         scale = (wd->size * elm_widget_scale_get(obj) * _elm_config->scale);
127         evas_object_size_hint_min_set(wd->img, scale, scale);
128         edje_object_part_swallow(wd->frm, "elm.swallow.content", wd->img);
129         elm_coords_finger_size_adjust(1, &minw, 1, &minh);
130         edje_object_size_min_restricted_calc(wd->frm, &minw, &minh, minw, minh);
131         elm_coords_finger_size_adjust(1, &minw, 1, &minh);
132         maxw = minw;
133         maxh = minh;
134         evas_object_size_hint_min_set(obj, minw, minh);
135         evas_object_size_hint_max_set(obj, maxw, maxh);
136      }
137 }
138
139 static void
140 _icon_move_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
141 {
142    Evas_Coord w, h;
143    Widget_Data *wd = elm_widget_data_get(data);
144
145    if (!wd) return;
146    if (wd->fill)
147      {
148         Edje_Message_Int_Set *msg;
149         Evas_Object *icon = _els_smart_icon_object_get(wd->img);
150
151         evas_object_geometry_get(icon, NULL, NULL, &w, &h);
152         msg = alloca(sizeof(Edje_Message_Int_Set) + (sizeof(int)));
153         msg->count=2;
154         msg->val[0] = (int)w;
155         msg->val[1] = (int)h;
156
157         edje_object_message_send(wd->frm, EDJE_MESSAGE_INT_SET, 0, msg);
158      }
159
160 #ifdef HAVE_ELEMENTARY_ETHUMB
161    if (wd->thumb.file.path)
162      elm_photo_thumb_set(data, wd->thumb.file.path, wd->thumb.file.key);
163 #endif
164 }
165
166
167 static void
168 _drag_done_cb(void *unused __UNUSED__, Evas_Object *obj)
169 {
170    elm_object_scroll_freeze_pop(obj);
171    evas_object_smart_callback_call(obj, SIG_DRAG_END, NULL);
172 }
173
174 static Eina_Bool
175 _longpress(void *objv)
176 {
177    Widget_Data *wd = elm_widget_data_get(objv);
178    Evas_Object *tmp;
179    const char *file;
180
181    DBG("Long press: start drag!");
182    wd->longtimer = NULL; /* clear: must return NULL now */
183    evas_object_event_callback_del(objv, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move);
184
185    tmp = _els_smart_icon_object_get(wd->img);
186    file = NULL;
187    evas_object_image_file_get(tmp,&file,NULL);
188    if (file)
189      {
190         char buf[4096 + 7];
191         /* FIXME: Deal with relative paths; use PATH_MAX */
192         snprintf(buf, sizeof(buf), "file://%s", file);
193         if (elm_drag_start(objv, ELM_SEL_FORMAT_IMAGE,
194                            buf, _drag_done_cb, NULL))
195           {
196              elm_object_scroll_freeze_push(objv);
197              evas_object_smart_callback_call(objv, SIG_DRAG_START, NULL);
198           }
199      }
200
201    return 0; /* Don't call again */
202 }
203
204 static void
205 _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event)
206 {
207    Widget_Data *wd = elm_widget_data_get(data);
208    Evas_Event_Mouse_Move *move = event;
209
210    /* Sanity */
211    if (!wd->longtimer)
212      {
213         evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move);
214         return;
215      }
216
217    /* if the event is held, stop waiting */
218    if (move->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
219      {
220         /* Moved too far: No longpress for you! */
221         ecore_timer_del(wd->longtimer);
222         wd->longtimer = NULL;
223         evas_object_event_callback_del(obj, EVAS_CALLBACK_MOUSE_MOVE,
224                                        _mouse_move);
225      }
226 }
227
228 static void
229 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
230 {
231    Widget_Data *wd = elm_widget_data_get(data);
232
233    if (wd->longtimer) ecore_timer_del(wd->longtimer);
234
235    /* FIXME: Hard coded timeout */
236    wd->longtimer = ecore_timer_add(0.7, _longpress, data);
237    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_MOVE,
238                                   _mouse_move, data);
239 }
240
241 static void
242 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
243 {
244    Widget_Data *wd = elm_widget_data_get(data);
245
246    if ((wd) && (wd->longtimer))
247      {
248         ecore_timer_del(wd->longtimer);
249         wd->longtimer = NULL;
250      }
251
252    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
253 }
254
255 static inline int
256 _icon_size_min_get(Evas_Object *icon)
257 {
258    int size;
259    _els_smart_icon_size_get(icon, &size, NULL);
260    return (size < 32) ? 32 : size;
261 }
262
263
264 #ifdef HAVE_ELEMENTARY_ETHUMB
265 static void
266 _icon_thumb_stop(Widget_Data *wd, void *ethumbd)
267 {
268    if (wd->thumb.id >= 0)
269      {
270         ethumb_client_generate_cancel(ethumbd, wd->thumb.id, NULL, NULL, NULL);
271         wd->thumb.id = -1;
272         _icon_pending_request--;
273      }
274
275    if (wd->thumb.exists)
276      {
277         ethumb_client_thumb_exists_cancel(wd->thumb.exists);
278         wd->thumb.exists = NULL;
279         _icon_pending_request--;
280      }
281
282    if (wd->thumb.retry)
283      {
284         _elm_icon_retry = eina_list_remove(_elm_icon_retry, wd);
285         wd->thumb.retry = EINA_FALSE;
286      }
287 }
288
289 static Eina_Bool
290 _icon_thumb_display(Widget_Data *wd)
291 {
292    Eina_Bool ret = EINA_FALSE;
293
294    if (wd->thumb.format == ETHUMB_THUMB_EET)
295      {
296         static const char *extensions[] = {
297           ".avi", ".mp4", ".ogv", ".mov", ".mpg", ".wmv", NULL
298         };
299         const char **ext, *ptr;
300         int prefix_size;
301         Eina_Bool video = EINA_FALSE;
302
303         prefix_size = eina_stringshare_strlen(wd->thumb.file.path) - 4;
304         if (prefix_size >= 0)
305           {
306              ptr = wd->thumb.file.path + prefix_size;
307              for (ext = extensions; *ext; ++ext)
308                if (!strcasecmp(ptr, *ext))
309                  {
310                     video = EINA_TRUE;
311                     break;
312                  }
313           }
314
315         if (video)
316           ret = _els_smart_icon_file_edje_set(wd->img, wd->thumb.thumb.path, wd->thumb.thumb.key);
317      }
318
319    if (!ret)
320      ret = _els_smart_icon_file_key_set(wd->img, wd->thumb.thumb.path, wd->thumb.thumb.key);
321
322    return ret;
323 }
324
325 static Eina_Bool
326 _icon_thumb_retry(Widget_Data *wd)
327 {
328    return _icon_thumb_display(wd);
329 }
330
331 static void
332 _icon_thumb_cleanup(Ethumb_Client *ethumbd)
333 {
334    Eina_List *l, *ll;
335    Widget_Data *wd;
336
337    EINA_LIST_FOREACH_SAFE(_elm_icon_retry, l, ll, wd)
338      if (_icon_thumb_retry(wd))
339        {
340           _elm_icon_retry = eina_list_remove_list(_elm_icon_retry, l);
341           wd->thumb.retry = EINA_FALSE;
342        }
343
344    if (_icon_pending_request == 0)
345      EINA_LIST_FREE(_elm_icon_retry, wd)
346        _icon_thumb_stop(wd, ethumbd);
347 }
348
349 static void
350 _icon_thumb_finish(Widget_Data *wd, Ethumb_Client *ethumbd)
351 {
352    const char *file = NULL, *group = NULL;
353    Eina_Bool ret;
354
355    _els_smart_icon_file_get(wd->img, &file, &group);
356    file = eina_stringshare_ref(file);
357    group = eina_stringshare_ref(group);
358
359    ret = _icon_thumb_display(wd);
360
361    if (!ret && file)
362      {
363         const char *p;
364
365         if (!wd->thumb.retry)
366           {
367              _elm_icon_retry = eina_list_append(_elm_icon_retry, wd);
368              wd->thumb.retry = EINA_TRUE;
369           }
370
371         /* Back to previous image */
372         if (((p = strrchr(file, '.'))) && (!strcasecmp(p, ".edj")))
373           _els_smart_icon_file_edje_set(wd->img, file, group);
374         else
375           _els_smart_icon_file_key_set(wd->img, file, group);
376      }
377
378    _icon_thumb_cleanup(ethumbd);
379
380    eina_stringshare_del(file);
381    eina_stringshare_del(group);
382 }
383
384 static void
385 _icon_thumb_cb(void *data,
386                Ethumb_Client *ethumbd,
387                int id,
388                const char *file __UNUSED__,
389                const char *key __UNUSED__,
390                const char *thumb_path,
391                const char *thumb_key,
392                Eina_Bool success)
393 {
394    Widget_Data *wd = data;
395
396    EINA_SAFETY_ON_FALSE_RETURN(wd->thumb.id == id);
397    wd->thumb.id = -1;
398
399    _icon_pending_request--;
400
401    if (success)
402      {
403         eina_stringshare_replace(&wd->thumb.thumb.path, thumb_path);
404         eina_stringshare_replace(&wd->thumb.thumb.key, thumb_key);
405         wd->thumb.format = ethumb_client_format_get(ethumbd);
406
407         _icon_thumb_finish(wd, ethumbd);
408      }
409    else
410      {
411         ERR("could not generate thumbnail for %s (key: %s)", file, key);
412         _icon_thumb_cleanup(ethumbd);
413      }
414 }
415
416 static void
417 _icon_thumb_exists(void *data, Ethumb_Client *client __UNUSED__, Ethumb_Exists *thread, Eina_Bool exists)
418 {
419    Widget_Data *wd = data;
420    Ethumb_Client *ethumbd;
421
422    if (ethumb_client_thumb_exists_check(thread))
423      return ;
424
425    wd->thumb.exists = NULL;
426
427    ethumbd = elm_thumb_ethumb_client_get();
428
429    if (exists)
430      {
431         const char *thumb_path, *thumb_key;
432
433         _icon_pending_request--;
434         ethumb_client_thumb_path_get(ethumbd, &thumb_path, &thumb_key);
435         eina_stringshare_replace(&wd->thumb.thumb.path, thumb_path);
436         eina_stringshare_replace(&wd->thumb.thumb.key, thumb_key);
437         wd->thumb.format = ethumb_client_format_get(ethumbd);
438
439         _icon_thumb_finish(wd, ethumbd);
440      }
441    else if ((wd->thumb.id = ethumb_client_generate(ethumbd, _icon_thumb_cb, wd, NULL)) == -1)
442      {
443         ERR("Generate was unable to start !");
444         /* Failed to generate thumbnail */
445         _icon_pending_request--;
446      }
447 }
448
449 static void
450 _icon_thumb_apply(Widget_Data *wd)
451 {
452    Ethumb_Client *ethumbd;
453
454    ethumbd = elm_thumb_ethumb_client_get();
455
456    _icon_thumb_stop(wd, ethumbd);
457
458    if (!wd->thumb.file.path) return ;
459
460    _icon_pending_request++;
461    if (!ethumb_client_file_set(ethumbd, wd->thumb.file.path, wd->thumb.file.key)) return ;
462    ethumb_client_size_set(ethumbd, _icon_size_min_get(wd->img), _icon_size_min_get(wd->img));
463    wd->thumb.exists = ethumb_client_thumb_exists(ethumbd, _icon_thumb_exists, wd);
464 }
465
466 static Eina_Bool
467 _icon_thumb_apply_cb(void *data, int type __UNUSED__, void *ev __UNUSED__)
468 {
469    Widget_Data *wd = data;
470
471    _icon_thumb_apply(wd);
472    return ECORE_CALLBACK_RENEW;
473 }
474 #endif
475
476 EAPI Evas_Object *
477 elm_photo_add(Evas_Object *parent)
478 {
479    Evas_Object *obj;
480    Evas *e;
481    Widget_Data *wd;
482    Evas_Object *icon;
483
484    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
485
486    ELM_SET_WIDTYPE(widtype, "photo");
487    elm_widget_type_set(obj, "photo");
488    elm_widget_sub_object_add(parent, obj);
489    elm_widget_data_set(obj, wd);
490    elm_widget_del_hook_set(obj, _del_hook);
491    elm_widget_theme_hook_set(obj, _theme_hook);
492    elm_widget_can_focus_set(obj, EINA_FALSE);
493
494    wd->frm = edje_object_add(e);
495    _elm_theme_object_set(obj, wd->frm, "photo", "base", "default");
496    elm_widget_resize_object_set(obj, wd->frm);
497
498    wd->img = _els_smart_icon_add(e);
499    _els_smart_icon_scale_up_set(wd->img, 1);
500    _els_smart_icon_scale_down_set(wd->img, 1);
501    _els_smart_icon_smooth_scale_set(wd->img, 1);
502    _els_smart_icon_fill_inside_set(wd->img, 0);
503    _els_smart_icon_scale_size_set(wd->img, 0);
504    wd->fill = EINA_FALSE;
505    _els_smart_icon_scale_set(wd->img,
506                              elm_widget_scale_get(obj) * _elm_config->scale);
507    evas_object_event_callback_add(wd->img, EVAS_CALLBACK_MOUSE_UP,
508                                   _mouse_up, obj);
509    evas_object_event_callback_add(wd->img, EVAS_CALLBACK_MOUSE_DOWN,
510                                   _mouse_down, obj);
511    evas_object_repeat_events_set(wd->img, 1);
512    edje_object_part_swallow(wd->frm, "elm.swallow.content", wd->img);
513    evas_object_show(wd->img);
514    elm_widget_sub_object_add(obj, wd->img);
515
516    wd->longtimer = NULL;
517
518    icon = _els_smart_icon_object_get(wd->img);
519    evas_object_event_callback_add(icon, EVAS_CALLBACK_MOVE,
520                                   _icon_move_resize, obj);
521    evas_object_event_callback_add(icon, EVAS_CALLBACK_RESIZE,
522                                   _icon_move_resize, obj);
523
524    evas_object_smart_callbacks_descriptions_set(obj, _signals);
525
526 #ifdef HAVE_ELEMENTARY_ETHUMB
527    wd->thumb.id = -1;
528 #endif
529
530    _mirrored_set(obj, elm_widget_mirrored_get(obj));
531    _sizing_eval(obj);
532    return obj;
533 }
534
535 EAPI Eina_Bool
536 elm_photo_file_set(Evas_Object *obj, const char *file)
537 {
538    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
539    Widget_Data *wd = elm_widget_data_get(obj);
540
541    if (!wd) return EINA_FALSE;
542    if (!_els_smart_icon_file_key_set(wd->img, file, NULL))
543      return EINA_FALSE;
544
545    _sizing_eval(obj);
546    return EINA_TRUE;
547 }
548
549 EAPI void
550 elm_photo_size_set(Evas_Object *obj, int size)
551 {
552    ELM_CHECK_WIDTYPE(obj, widtype);
553    Widget_Data *wd = elm_widget_data_get(obj);
554
555    if (!wd) return;
556    wd->size = (size > 0) ? size : 0;
557
558    _els_smart_icon_scale_size_set(wd->img, wd->size);
559
560    _sizing_eval(obj);
561 }
562
563 EAPI void
564 elm_photo_fill_inside_set(Evas_Object *obj, Eina_Bool fill)
565 {
566    ELM_CHECK_WIDTYPE(obj, widtype);
567    Widget_Data *wd = elm_widget_data_get(obj);
568
569    if (!wd) return;
570    _els_smart_icon_fill_inside_set(wd->img, fill);
571    wd->fill = fill;
572    _sizing_eval(obj);
573 }
574
575 EAPI void
576 elm_photo_editable_set(Evas_Object *obj, Eina_Bool set)
577 {
578    ELM_CHECK_WIDTYPE(obj, widtype);
579    Widget_Data *wd = elm_widget_data_get(obj);
580
581    if (!wd) return;;
582    _els_smart_icon_edit_set(wd->img, set, obj);
583 }
584
585 EAPI void
586 elm_photo_thumb_set(const Evas_Object *obj, const char *file, const char *group)
587 {
588    ELM_CHECK_WIDTYPE(obj, widtype);
589    Widget_Data *wd = elm_widget_data_get(obj);
590    if (!wd) return;
591
592 #ifdef HAVE_ELEMENTARY_ETHUMB
593    eina_stringshare_replace(&wd->thumb.file.path, file);
594    eina_stringshare_replace(&wd->thumb.file.key, group);
595
596    if (elm_thumb_ethumb_client_connected_get())
597      {
598         _icon_thumb_apply(wd);
599         return ;
600      }
601
602    if (!wd->thumb.eeh)
603      {
604         wd->thumb.eeh = ecore_event_handler_add(ELM_ECORE_EVENT_ETHUMB_CONNECT, _icon_thumb_apply_cb, wd);
605      }
606 #else
607    (void) obj;
608    (void) file;
609    (void) group;
610 #endif
611 }
612
613 EAPI void
614 elm_photo_aspect_fixed_set(Evas_Object *obj, Eina_Bool fixed)
615 {
616    ELM_CHECK_WIDTYPE(obj, widtype);
617    Widget_Data *wd = elm_widget_data_get(obj);
618    if (!wd) return;
619    return _els_smart_icon_aspect_fixed_set(wd->img, fixed);
620 }
621
622 EAPI Eina_Bool
623 elm_photo_aspect_fixed_get(const Evas_Object *obj)
624 {
625    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
626    Widget_Data *wd = elm_widget_data_get(obj);
627    if (!wd) return EINA_FALSE;
628    return _els_smart_icon_aspect_fixed_get(wd->img);
629 }
630
631
632
633 /* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-3f0^-2{2(0W1st0 :*/