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