use the new escape text set to clear up issues if theme has TEXT vs
[framework/uifw/elementary.git] / src / lib / elm_video.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 #ifdef HAVE_EMOTION
5 # include <Emotion.h>
6 #endif
7
8 /* TODO: add buffering support to Emotion and display buffering progression in the theme when needed */
9
10 typedef struct _Widget_Data Widget_Data;
11 struct _Widget_Data
12 {
13    Evas_Object *layout;
14    Evas_Object *emotion;
15
16    Ecore_Timer *timer;
17
18    Eina_Bool stop : 1;
19    Eina_Bool remember : 1;
20 };
21
22 #ifdef HAVE_EMOTION
23 static const char *widtype = NULL;
24
25 static const Evas_Smart_Cb_Description _signals[] = {
26   { NULL, NULL }
27 };
28
29 static void _del_hook(Evas_Object *obj);
30 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
31 static void _theme_hook(Evas_Object *obj);
32 static void _sizing_eval(Evas_Object *obj);
33 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
34 static void _on_focus_hook(void *data, Evas_Object *obj);
35 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
36                              Evas_Callback_Type type, void *event_info);
37
38 static Eina_Bool
39 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
40 {
41    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
42    Evas_Event_Key_Down *ev = event_info;
43    Widget_Data *wd = elm_widget_data_get(obj);
44    if (!wd) return EINA_FALSE;
45    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
46    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
47    if ((!strcmp(ev->keyname, "Left")) ||
48        ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)))
49      {
50         double current, last;
51
52         current = elm_video_play_position_get(obj);
53         last = elm_video_play_length_get(obj);
54
55         if (current < last)
56           {
57              current += last / 100;
58              elm_video_play_position_set(obj, current);
59           }
60
61         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
62         return EINA_TRUE;
63      }
64    if ((!strcmp(ev->keyname, "Right")) ||
65        ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)))
66      {
67         double current, last;
68
69         current = elm_video_play_position_get(obj);
70         last = elm_video_play_length_get(obj);
71
72         if (current > 0)
73           {
74              current -= last / 100;
75              if (current < 0) current = 0;
76              elm_video_play_position_set(obj, current);
77           }
78
79         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
80         return EINA_TRUE;
81      }
82    if (!strcmp(ev->keyname, "space"))
83      {
84         if (elm_video_is_playing_get(obj))
85           elm_video_pause(obj);
86         else
87           elm_video_play(obj);
88         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
89         return EINA_TRUE;
90      }
91    fprintf(stderr, "keyname: '%s' not handle\n", ev->keyname);
92    return EINA_FALSE;
93 }
94
95 static void
96 _del_hook(Evas_Object *obj)
97 {
98    Widget_Data *wd = elm_widget_data_get(obj);
99
100    if (!wd) return;
101    if (wd->timer) ecore_timer_del(wd->timer);
102    free(wd);
103 }
104
105 static void
106 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
107 {
108    Widget_Data *wd = elm_widget_data_get(obj);
109    if (!wd) return;
110    if (elm_widget_focus_get(obj))
111      {
112         edje_object_signal_emit(wd->layout, "elm,action,focus", "elm");
113         evas_object_focus_set(wd->layout, EINA_TRUE);
114      }
115    else
116      {
117         edje_object_signal_emit(wd->layout, "elm,action,unfocus", "elm");
118         evas_object_focus_set(wd->layout, EINA_FALSE);
119      }
120 }
121
122 static void
123 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
124 {
125    Widget_Data *wd = elm_widget_data_get(obj);
126    if (!wd) return;
127    edje_object_mirrored_set(wd->layout, rtl);
128 }
129
130 static void
131 _theme_hook(Evas_Object *obj)
132 {
133    Widget_Data *wd = elm_widget_data_get(obj);
134    if (!wd) return;
135    _elm_widget_mirrored_reload(obj);
136    _mirrored_set(obj, elm_widget_mirrored_get(obj));
137    _elm_theme_object_set(obj, wd->layout, "video", "base", elm_widget_style_get(obj));
138    edje_object_scale_set(wd->layout, elm_widget_scale_get(obj) *
139                          _elm_config->scale);
140    _sizing_eval(obj);
141 }
142
143 static void
144 _sizing_eval(Evas_Object *obj)
145 {
146    Widget_Data *wd = elm_widget_data_get(obj);
147    Evas_Coord minw = -1, minh = -1;
148    Evas_Coord w, h;
149
150    if (!wd) return;
151    evas_object_size_hint_request_get(wd->emotion, &minw, &minh);
152    evas_object_size_hint_aspect_set(wd->emotion, EVAS_ASPECT_CONTROL_BOTH, minw, minh);
153    edje_object_size_min_calc(wd->layout, &w, &h);
154
155    if (w != 0 && h != 0)
156      {
157         minw = w;
158         minh = h;
159      }
160    evas_object_size_hint_aspect_set(obj, EVAS_ASPECT_CONTROL_BOTH, minw, minh);
161 }
162
163 static void
164 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
165 {
166    _sizing_eval(data);
167 }
168
169 static void
170 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
171 {
172    Widget_Data *wd = elm_widget_data_get(obj);
173
174    if (wd->remember) emotion_object_last_position_save(wd->emotion);
175 }
176
177 static void
178 _open_done(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
179 {
180    Widget_Data *wd = elm_widget_data_get(data);
181
182    edje_object_signal_emit(wd->layout, "elm,video,open", "elm");
183 }
184
185 static void
186 _playback_started(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
187 {
188    Widget_Data *wd = elm_widget_data_get(data);
189
190    edje_object_signal_emit(wd->layout, "elm,video,play", "elm");
191 }
192
193 static void
194 _playback_finished(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
195 {
196    Widget_Data *wd = elm_widget_data_get(data);
197
198    edje_object_signal_emit(wd->layout, "elm,video,end", "elm");
199 }
200
201 static void
202 _update_aspect_ratio(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
203 {
204    _sizing_eval(data);
205 }
206
207 static void
208 _title_change(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
209 {
210    Widget_Data *wd = elm_widget_data_get(data);
211    const char *title;
212
213    title = emotion_object_title_get(wd->emotion);
214    edje_object_part_text_escaped_set(wd->layout, "elm,title", title);
215    edje_object_signal_emit(wd->layout, "elm,video,title", "elm");
216 }
217
218 static void
219 _audio_level_change(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
220 {
221    (void) data;
222 }
223
224 static Eina_Bool
225 _suspend_cb(void *data)
226 {
227    Widget_Data *wd = elm_widget_data_get(data);
228    double interval;
229
230    interval = ecore_timer_interval_get(wd->timer);
231    if (interval <= 20)
232      emotion_object_suspend_set(wd->emotion, EMOTION_SLEEP);
233    else if (interval <= 30)
234      emotion_object_suspend_set(wd->emotion, EMOTION_DEEP_SLEEP);
235    else
236      {
237         emotion_object_suspend_set(wd->emotion, EMOTION_HIBERNATE);
238         wd->timer = NULL;
239         return ECORE_CALLBACK_CANCEL;
240      }
241
242    ecore_timer_interval_set(wd->timer, interval + 10);
243    return ECORE_CALLBACK_RENEW;
244 }
245 #endif
246
247 Eina_Bool
248 _elm_video_check(Evas_Object *video)
249 {
250 #ifdef HAVE_EMOTION
251   ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
252   return EINA_TRUE;
253 #else
254   (void) video;
255   return EINA_FALSE;
256 #endif
257 }
258
259 EAPI Evas_Object *
260 elm_video_add(Evas_Object *parent)
261 {
262 #ifdef HAVE_EMOTION
263    Evas_Object *obj;
264    Evas *e;
265    Widget_Data *wd;
266
267    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
268    ELM_SET_WIDTYPE(widtype, "video");
269    elm_widget_type_set(obj, "video");
270    elm_widget_sub_object_add(parent, obj);
271    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
272    elm_widget_data_set(obj, wd);
273    elm_widget_del_hook_set(obj, _del_hook);
274    elm_widget_theme_hook_set(obj, _theme_hook);
275    elm_widget_can_focus_set(obj, EINA_TRUE);
276    elm_widget_event_hook_set(obj, _event_hook);
277
278    wd->stop = EINA_FALSE;
279    wd->remember = EINA_FALSE;
280
281    wd->layout = edje_object_add(e);
282    _elm_theme_object_set(obj, wd->layout, "video", "base", "default");
283    elm_widget_resize_object_set(obj, wd->layout);
284    evas_object_show(wd->layout);
285    evas_object_size_hint_weight_set(wd->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
286
287    wd->emotion = emotion_object_add(e);
288    emotion_object_init(wd->emotion, NULL);
289    elm_widget_sub_object_add(obj, wd->emotion);
290    edje_object_part_swallow(wd->layout, "elm.swallow.video", wd->emotion);
291
292    evas_object_smart_callback_add(wd->emotion, "open_done", _open_done, obj);
293    evas_object_smart_callback_add(wd->emotion, "playback_started", _playback_started, obj);
294    evas_object_smart_callback_add(wd->emotion, "playback_finished", _playback_finished, obj);
295    evas_object_smart_callback_add(wd->emotion, "frame_resize", _update_aspect_ratio, obj);
296    evas_object_smart_callback_add(wd->emotion, "title_change", _title_change, obj);
297    evas_object_smart_callback_add(wd->emotion, "audio_level_change", _audio_level_change, obj);
298
299    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
300    evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, NULL);
301    evas_object_smart_callbacks_descriptions_set(obj, _signals);
302
303    _mirrored_set(obj, elm_widget_mirrored_get(obj));
304    _sizing_eval(obj);
305
306    wd->timer = ecore_timer_add(20.0, _suspend_cb, obj);
307
308    return obj;
309 #else
310    (void) parent;
311    return NULL;
312 #endif
313 }
314
315 EAPI Eina_Bool
316 elm_video_file_set(Evas_Object *video, const char *filename)
317 {
318 #ifdef HAVE_EMOTION
319    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
320    Widget_Data *wd = elm_widget_data_get(video);
321
322    if (wd->remember) emotion_object_last_position_save(wd->emotion);
323    wd->stop = EINA_FALSE;
324    if (!emotion_object_file_set(wd->emotion, filename)) return EINA_FALSE;
325
326    if ((!strncmp(filename, "file://", 7)) || (!strstr(filename, "://")))
327      emotion_object_last_position_load(wd->emotion);
328
329    edje_object_signal_emit(wd->layout, "elm,video,load", "elm");
330
331    return EINA_TRUE;
332 #else
333    (void) video;
334    (void) filename;
335
336    return EINA_FALSE;
337 #endif
338 }
339
340 EAPI Evas_Object *
341 elm_video_emotion_get(const Evas_Object *video)
342 {
343 #ifdef HAVE_EMOTION
344    ELM_CHECK_WIDTYPE(video, widtype) NULL;
345    Widget_Data *wd = elm_widget_data_get(video);
346
347    return wd->emotion;
348 #else
349    (void) video;
350    return NULL;
351 #endif
352 }
353
354 EAPI void
355 elm_video_play(Evas_Object *video)
356 {
357 #ifdef HAVE_EMOTION
358    ELM_CHECK_WIDTYPE(video, widtype);
359    Widget_Data *wd = elm_widget_data_get(video);
360
361    if (emotion_object_play_get(wd->emotion)) return ;
362
363    if (wd->timer) ecore_timer_del(wd->timer);
364    wd->timer = NULL;
365    wd->stop = EINA_FALSE;
366    emotion_object_play_set(wd->emotion, EINA_TRUE);
367 #else
368    (void) video;
369 #endif
370 }
371
372 /* FIXME: pause will setup timer and go into sleep or
373  * hibernate after a while without activity.
374  */
375
376 EAPI void
377 elm_video_pause(Evas_Object *video)
378 {
379 #ifdef HAVE_EMOTION
380    ELM_CHECK_WIDTYPE(video, widtype);
381    Widget_Data *wd = elm_widget_data_get(video);
382
383    if (!emotion_object_play_get(wd->emotion)) return ;
384
385    if (!wd->timer) wd->timer = ecore_timer_add(20.0, _suspend_cb, video);
386    emotion_object_play_set(wd->emotion, EINA_FALSE);
387    edje_object_signal_emit(wd->layout, "elm,video,pause", "elm");
388 #else
389    (void) video;
390 #endif
391 }
392
393 /* FIXME: stop should go into hibernate state directly.
394  */
395 EAPI void
396 elm_video_stop(Evas_Object *video)
397 {
398 #ifdef HAVE_EMOTION
399    ELM_CHECK_WIDTYPE(video, widtype);
400    Widget_Data *wd = elm_widget_data_get(video);
401
402    if (!emotion_object_play_get(wd->emotion) && wd->stop) return ;
403
404    if (wd->timer) ecore_timer_del(wd->timer);
405    wd->timer = NULL;
406    wd->stop = EINA_TRUE;
407    emotion_object_play_set(wd->emotion, EINA_FALSE);
408    edje_object_signal_emit(wd->layout, "elm,video,stop", "elm");
409    emotion_object_suspend_set(wd->emotion, EMOTION_HIBERNATE);
410 #else
411    (void) video;
412 #endif
413 }
414
415 EAPI Eina_Bool
416 elm_video_is_playing_get(const Evas_Object *video)
417 {
418 #ifdef HAVE_EMOTION
419    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
420    Widget_Data *wd = elm_widget_data_get(video);
421
422    return emotion_object_play_get(wd->emotion);
423 #else
424    (void) video;
425    return EINA_FALSE;
426 #endif
427 }
428
429 EAPI Eina_Bool
430 elm_video_is_seekable_get(const Evas_Object *video)
431 {
432 #ifdef HAVE_EMOTION
433    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
434    Widget_Data *wd = elm_widget_data_get(video);
435
436    return emotion_object_seekable_get(wd->emotion);
437 #else
438    (void) video;
439    return EINA_FALSE;
440 #endif
441 }
442
443 EAPI Eina_Bool
444 elm_video_audio_mute_get(const Evas_Object *video)
445 {
446 #ifdef HAVE_EMOTION
447    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
448    Widget_Data *wd = elm_widget_data_get(video);
449
450    return emotion_object_audio_mute_get(wd->emotion);
451 #else
452    (void) video;
453    return EINA_FALSE;
454 #endif
455 }
456
457 EAPI void
458 elm_video_audio_mute_set(Evas_Object *video, Eina_Bool mute)
459 {
460 #ifdef HAVE_EMOTION
461    ELM_CHECK_WIDTYPE(video, widtype);
462    Widget_Data *wd = elm_widget_data_get(video);
463
464    emotion_object_audio_mute_set(wd->emotion, mute);
465 #else
466    (void) video;
467    (void) mute;
468 #endif
469 }
470
471 EAPI double
472 elm_video_audio_level_get(const Evas_Object *video)
473 {
474 #ifdef HAVE_EMOTION
475    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
476    Widget_Data *wd = elm_widget_data_get(video);
477
478    return emotion_object_audio_volume_get(wd->emotion);
479 #else
480    (void) video;
481    return 0.0;
482 #endif
483 }
484
485 EAPI void
486 elm_video_audio_level_set(Evas_Object *video, double volume)
487 {
488 #ifdef HAVE_EMOTION
489    ELM_CHECK_WIDTYPE(video, widtype);
490    Widget_Data *wd = elm_widget_data_get(video);
491
492    emotion_object_audio_volume_set(wd->emotion, volume);
493 #else
494    (void) video;
495    (void) volume;
496 #endif
497 }
498
499 EAPI double
500 elm_video_play_position_get(const Evas_Object *video)
501 {
502 #ifdef HAVE_EMOTION
503    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
504    Widget_Data *wd = elm_widget_data_get(video);
505
506    return emotion_object_position_get(wd->emotion);
507 #else
508    (void) video;
509    return 0.0;
510 #endif
511 }
512
513 EAPI void
514 elm_video_play_position_set(Evas_Object *video, double position)
515 {
516 #ifdef HAVE_EMOTION
517    ELM_CHECK_WIDTYPE(video, widtype);
518    Widget_Data *wd = elm_widget_data_get(video);
519
520    emotion_object_position_set(wd->emotion, position);
521 #else
522    (void) video;
523    (void) position;
524 #endif
525 }
526
527 EAPI double
528 elm_video_play_length_get(const Evas_Object *video)
529 {
530 #ifdef HAVE_EMOTION
531    ELM_CHECK_WIDTYPE(video, widtype) 0.0;
532    Widget_Data *wd = elm_widget_data_get(video);
533
534    return emotion_object_play_length_get(wd->emotion);
535 #else
536    (void) video;
537    return 0.0;
538 #endif
539 }
540
541 EAPI const char *
542 elm_video_title_get(const Evas_Object *video)
543 {
544 #ifdef HAVE_EMOTION
545    ELM_CHECK_WIDTYPE(video, widtype) NULL;
546    Widget_Data *wd = elm_widget_data_get(video);
547
548    return emotion_object_title_get(wd->emotion);
549 #else
550    (void) video;
551    return NULL;
552 #endif
553 }
554
555 EAPI void
556 elm_video_remember_position_set(Evas_Object *video, Eina_Bool remember)
557 {
558 #ifdef HAVE_EMOTION
559    ELM_CHECK_WIDTYPE(video, widtype);
560    Widget_Data *wd = elm_widget_data_get(video);
561
562    wd->remember = remember;
563 #else
564    (void) video;
565    (void) remember;
566 #endif
567 }
568
569 EAPI Eina_Bool
570 elm_video_remember_position_get(const Evas_Object *video)
571 {
572 #ifdef HAVE_EMOTION
573    ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE;
574    Widget_Data *wd = elm_widget_data_get(video);
575
576    return wd->remember;
577 #else
578    (void) video;
579    return EINA_FALSE;
580 #endif
581 }