diff --git a/legacy/elementary/data/themes/default.edc b/legacy/elementary/data/themes/default.edc index 8bc28fae6f..9d77b8c9db 100644 --- a/legacy/elementary/data/themes/default.edc +++ b/legacy/elementary/data/themes/default.edc @@ -10285,121 +10285,63 @@ collections { /////////////////////////////////////////////////////////////////////////////// - group { name: "elm/icon/home/default"; alias: "elm/icon/toolbar/home/default"; min: 32 32; - images.image: "icon_home.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_home.png"; } } } } - group { name: "elm/icon/close/default"; alias: "elm/icon/toolbar/close/default"; min: 32 32; - images.image: "icon_close.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_close.png"; } } } } - group { name: "elm/icon/apps/default"; alias: "elm/icon/toolbar/apps/default"; min: 32 32; - images.image: "icon_apps.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_apps.png"; } } } } - group { name: "elm/icon/arrow_up/default"; alias: "elm/icon/toolbar/arrow_up/default"; min: 32 32; - images.image: "icon_arrow_up.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_arrow_up.png"; } } } } +#define GROUP_ALIAS_ICON(Name, Alias, File, Min, Max) \ + group { name: "elm/icon/"##Name##"/default"; min: Min Min; max: Max Max; \ + alias: "elm/icon/"##Alias##"/default"; \ + images.image: File COMP; parts { part { name: "base"; \ + description { aspect: 1.0 1.0; aspect_preference: BOTH; \ + image.normal: File; } } } } + +#define GROUP_ICON(Name, File, Min, Max) \ + group { name: "elm/icon/"##Name##"/default"; min: Min Min; max: Max Max; \ + images.image: File COMP; parts { part { name: "base"; \ + description { aspect: 1.0 1.0; aspect_preference: BOTH; \ + image.normal: File; } } } } + group { name: "elm/icon/arrow_down/default"; alias: "elm/icon/toolbar/arrow_down/default"; alias: "elm/icon/toolbar/more_menu/default"; min: 32 32; images.image: "icon_arrow_down.png" COMP; parts { part { name: "base"; description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; image.normal: "icon_arrow_down.png"; } } } } - group { name: "elm/icon/arrow_left/default"; alias: "elm/icon/toolbar/arrow_left/default"; min: 32 32; - images.image: "icon_arrow_left.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_arrow_left.png"; } } } } - group { name: "elm/icon/arrow_right/default"; alias: "elm/icon/toolbar/arrow_right/default"; min: 32 32; - images.image: "icon_arrow_right.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_arrow_right.png"; } } } } - group { name: "elm/icon/chat/default"; alias: "elm/icon/toolbar/chat/default"; min: 32 32; - images.image: "icon_chat.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_chat.png"; } } } } - group { name: "elm/icon/clock/default"; alias: "elm/icon/toolbar/clock/default"; min: 32 32; - images.image: "icon_clock.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_clock.png"; } } } } - group { name: "elm/icon/delete/default"; alias: "elm/icon/toolbar/delete/default"; min: 32 32; - images.image: "icon_delete.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_delete.png"; } } } } - group { name: "elm/icon/edit/default"; alias: "elm/icon/toolbar/edit/default"; min: 32 32; - images.image: "icon_edit.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_edit.png"; } } } } - group { name: "elm/icon/refresh/default"; alias: "elm/icon/toolbar/refresh/default"; min: 32 32; - images.image: "icon_refresh.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_refresh.png"; } } } } - group { name: "elm/icon/folder/default"; alias: "elm/icon/toolbar/folder/default"; min: 32 32; - images.image: "icon_folder.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_folder.png"; } } } } - group { name: "elm/icon/file/default"; alias: "elm/icon/toolbar/file/default"; min: 32 32; - images.image: "icon_file.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_file.png"; } } } } + + GROUP_ALIAS_ICON("home", "toolbar/home", "icon_home.png", 32, 0); + GROUP_ALIAS_ICON("close", "toolbar/close", "icon_close.png", 32, 0); + GROUP_ALIAS_ICON("apps", "toolbar/apps", "icon_apps.png", 32, 0); + GROUP_ALIAS_ICON("arrow_up", "toolbar/arrow_up", "icon_arrow_up.png", 32, 0); + GROUP_ALIAS_ICON("arrow_left", "toolbar/arrow_left", "icon_arrow_left.png", 32, 0); + GROUP_ALIAS_ICON("arrow_right", "toolbar/arrow_right", "icon_arrow_right.png", 32, 0); + GROUP_ALIAS_ICON("chat", "toolbar/chat", "icon_chat.png", 32, 0); + GROUP_ALIAS_ICON("clock", "toolbar/clock", "icon_clock.png", 32, 0); + GROUP_ALIAS_ICON("delete", "toolbar/delete", "icon_delete.png", 32, 0); + GROUP_ALIAS_ICON("edit", "toolbar/edit", "icon_edit.png", 32, 0); + GROUP_ALIAS_ICON("refresh", "toolbar/refresh", "icon_refresh.png", 32, 0); + GROUP_ALIAS_ICON("folder", "toolbar/folder", "icon_folder.png", 32, 0); + GROUP_ALIAS_ICON("file", "toolbar/file", "icon_file.png", 32, 0); /////////////////////////////////////////////////////////////////////////////// - group { name: "elm/icon/menu/home/default"; min: 24 24; max: 24 24; - images.image: "icon_home.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_home.png"; } } } } - group { name: "elm/icon/menu/close/default"; min: 24 24; max: 24 24; - images.image: "icon_close.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_close.png"; } } } } - group { name: "elm/icon/menu/apps/default"; min: 24 24; max: 24 24; - images.image: "icon_apps.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_apps.png"; } } } } - group { name: "elm/icon/menu/arrow_up/default"; min: 24 24; max: 24 24; - images.image: "icon_arrow_up.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_arrow_up.png"; } } } } - group { name: "elm/icon/menu/arrow_down/default"; min: 24 24; max: 24 24; - images.image: "icon_arrow_down.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_arrow_down.png"; } } } } - group { name: "elm/icon/menu/arrow_left/default"; min: 24 24; max: 24 24; - images.image: "icon_arrow_left.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_arrow_left.png"; } } } } - group { name: "elm/icon/menu/arrow_right/default"; min: 24 24; max: 24 24; - images.image: "icon_arrow_right.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_arrow_right.png"; } } } } - group { name: "elm/icon/menu/chat/default"; min: 24 24; max: 24 24; - images.image: "icon_chat.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_chat.png"; } } } } - group { name: "elm/icon/menu/clock/default"; min: 24 24; max: 24 24; - images.image: "icon_clock.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_clock.png"; } } } } - group { name: "elm/icon/menu/delete/default"; min: 24 24; max: 24 24; - images.image: "icon_delete.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_delete.png"; } } } } - group { name: "elm/icon/menu/edit/default"; min: 24 24; max: 24 24; - images.image: "icon_edit.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_edit.png"; } } } } - group { name: "elm/icon/menu/refresh/default"; min: 24 24; max: 24 24; - images.image: "icon_refresh.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_refresh.png"; } } } } - group { name: "elm/icon/menu/folder/default"; min: 24 24; max: 24 24; - images.image: "icon_folder.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_folder.png"; } } } } - group { name: "elm/icon/menu/file/default"; min: 24 24; max: 24 24; - images.image: "icon_file.png" COMP; parts { part { name: "base"; - description { state: "default" 0.0; aspect: 1.0 1.0; aspect_preference: BOTH; - image.normal: "icon_file.png"; } } } } + GROUP_ICON("menu/home", "icon_home.png", 24, 24); + GROUP_ICON("menu/close", "icon_close.png", 24, 24); + GROUP_ICON("menu/apps", "icon_apps.png", 24, 24); + GROUP_ICON("menu/arrow_up", "icon_arrow_up.png", 24, 24); + GROUP_ICON("menu/arrow_down", "icon_arrow_down.png", 24, 24); + GROUP_ICON("menu/arrow_left", "icon_arrow_left.png", 24, 24); + GROUP_ICON("menu/arrow_right", "icon_arrow_right.png", 24, 24); + GROUP_ICON("menu/chat", "icon_chat.png", 24, 24); + GROUP_ICON("menu/clock", "icon_clock.png", 24, 24); + GROUP_ICON("menu/delete", "icon_delete.png", 24, 24); + GROUP_ICON("menu/edit", "icon_edit.png", 24, 24); + GROUP_ICON("menu/refresh", "icon_refresh.png", 24, 24); + GROUP_ICON("menu/folder", "icon_folder.png", 24, 24); + GROUP_ICON("menu/file", "icon_file.png", 24, 24); + + GROUP_ICON("media_player/forward", "mp_forward.png", 16, 0); + GROUP_ICON("media_player/info", "mp_info.png", 16, 0); + GROUP_ICON("media_player/next", "mp_next.png", 16, 0); + GROUP_ICON("media_player/pause", "mp_pause.png", 16, 0); + GROUP_ICON("media_player/play", "mp_play.png", 16, 0); + GROUP_ICON("media_player/prev", "mp_prev.png", 16, 0); + GROUP_ICON("media_player/rewind", "mp_rewind.png", 16, 0); + GROUP_ICON("media_player/stop", "mp_stop.png", 16, 0); /////////////////////////////////////////////////////////////////////////////// group { name: "elm/toolbar/base/default"; @@ -36505,6 +36447,191 @@ collections { } } + group { + name: "elm/player/base/default"; + min: 290 26; + + parts { + part { + type: SWALLOW; + name: "media_player/slider"; + + description { + rel2 { + relative: 1.0 0.0; + to_x: "media_player/forward"; + } + } + } + part { + type: SWALLOW; + name: "media_player/rewind"; + + description { + rel1 { + to_y: "media_player/slider"; + offset: 42 0; + relative: 0.0 1.0; + } + rel2 { + relative: 0.0 1.0; + } + } + } + part { + type: SWALLOW; + name: "media_player/prev"; + + description { + rel1 { + to: "media_player/rewind"; + relative: 1.0 0.0; + } + rel2 { + to: "media_player/rewind"; + relative: 2.0 1.0; + } + } + } + part { + type: SWALLOW; + name: "media_player/play"; + + description { + rel1 { + to: "media_player/prev"; + relative: 1.0 0.0; + } + rel2 { + to: "media_player/prev"; + relative: 2.0 1.0; + } + } + description { + state: hidden 0.0; + inherit: default 0.0; + visible: 0; + } + } + part { + type: SWALLOW; + name: "media_player/pause"; + + description { + rel1.to: "media_player/play"; + rel2.to: "media_player/play"; + } + description { + state: hidden 0.0; + inherit: default 0.0; + visible: 0; + } + } + part { + type: SWALLOW; + name: "media_player/info"; + description { + rel1 { + to: "media_player/play"; + relative: 1.0 0.0; + } + rel2 { + to: "media_player/play"; + relative: 2.0 1.0; + } + } + } + + part { + type: SWALLOW; + name: "media_player/stop"; + description { + rel1 { + to: "media_player/info"; + relative: 3.0 0.0; + } + rel2 { + to: "media_player/info"; + relative: 4.0 1.0; + } + } + } + part { + type: SWALLOW; + name: "media_player/next"; + description { + rel1 { + to: "media_player/stop"; + relative: 1.0 0.0; + } + rel2 { + to: "media_player/stop"; + relative: 2.0 1.0; + } + } + } + part { + type: SWALLOW; + name: "media_player/forward"; + description { + rel1 { + to: "media_player/next"; + relative: 1.0 0.0; + } + rel2 { + to: "media_player/next"; + relative: 2.0 1.0; + } + } + } + } + programs { + program { + signal: "show"; + source: ""; + after: "pause/0"; + } + program { + signal: "elm,player,play"; + source: "elm"; + after: "play/0"; + } + program { + signal: "elm,player,pause"; + source: "elm"; + after: "pause/0"; + } + program { + name: "play/0"; + + action: STATE_SET "hidden" 0.0; + target: "media_player/play"; + + after: "play/1"; + } + program { + name: "play/1"; + + action: STATE_SET "default" 0.0; + target: "media_player/pause"; + } + program { + name: "pause/0"; + + action: STATE_SET "hidden" 0.0; + target: "media_player/pause"; + + after: "pause/1"; + } + program { + name: "pause/1"; + + action: STATE_SET "default" 0.0; + target: "media_player/play"; + } + } + } + group { name: "elm/video/base/default"; @@ -36526,7 +36653,8 @@ collections { type: SWALLOW; clip_to: "clipper"; - mouse_events: 0; + mouse_events: 1; + repeat_events: 1; description { aspect_preference: BOTH; diff --git a/legacy/elementary/src/lib/Elementary.h.in b/legacy/elementary/src/lib/Elementary.h.in index 0c8383475d..3362baaf87 100644 --- a/legacy/elementary/src/lib/Elementary.h.in +++ b/legacy/elementary/src/lib/Elementary.h.in @@ -6429,6 +6429,7 @@ extern "C" { EAPI void elm_video_pause(Evas_Object *video); EAPI void elm_video_stop(Evas_Object *video); EAPI Eina_Bool elm_video_is_playing(Evas_Object *video); + EAPI Eina_Bool elm_video_is_seekable(Evas_Object *video); EAPI Eina_Bool elm_video_audio_mute_get(Evas_Object *video); EAPI void elm_video_audio_mute_set(Evas_Object *video, Eina_Bool mute); EAPI double elm_video_audio_level_get(Evas_Object *video); @@ -6440,6 +6441,9 @@ extern "C" { EAPI Eina_Bool elm_video_remember_position_get(Evas_Object *video); EAPI const char *elm_video_title_get(Evas_Object *video); + EAPI Evas_Object *elm_player_add(Evas_Object *parent); + EAPI void elm_player_video_set(Evas_Object *player, Evas_Object *video); + #ifdef __cplusplus } #endif diff --git a/legacy/elementary/src/lib/Makefile.am b/legacy/elementary/src/lib/Makefile.am index 0982fa353d..b9bb743c9b 100644 --- a/legacy/elementary/src/lib/Makefile.am +++ b/legacy/elementary/src/lib/Makefile.am @@ -48,6 +48,7 @@ elc_fileselector_button.c \ elc_fileselector.c \ elc_fileselector_entry.c \ elc_hoversel.c \ +elc_player.c \ elc_scrolled_entry.c \ elm_actionslider.c \ elm_animator.c \ diff --git a/legacy/elementary/src/lib/elc_player.c b/legacy/elementary/src/lib/elc_player.c new file mode 100644 index 0000000000..65d54ca0a7 --- /dev/null +++ b/legacy/elementary/src/lib/elc_player.c @@ -0,0 +1,591 @@ +#include +#include "elm_priv.h" + +#ifdef HAVE_EMOTION +# include +#endif + +/** + * @defgroup Video Video + * + * This object display an player that let you control an Elm_Video object. It take care of updating + * it's content according to what is going on inside the Emotion object. It does activate the remember + * function on the linked Elm_Video object. + * + * Signals that you cann add callback for are : + * + * "forward,clicked" - the user clicked the forward button. + * "info,clicked" - the user clicked the info button. + * "next,clicked" - the user clicked the next button. + * "pause,clicked" - the user clicked the pause button. + * "play,clicked" - the user clicked the play button. + * "prev,clicked" - the user clicked the prev button. + * "rewind,clicked" - the user clicked the rewind button. + * "stop,clicked" - the user clicked the stop button. + */ + +typedef struct _Widget_Data Widget_Data; +struct _Widget_Data +{ + Evas_Object *layout; + Evas_Object *video; + Evas_Object *emotion; + + Evas_Object *forward; + Evas_Object *info; + Evas_Object *next; + Evas_Object *pause; + Evas_Object *play; + Evas_Object *prev; + Evas_Object *rewind; + Evas_Object *stop; + + Evas_Object *slider; +}; + +#ifdef HAVE_EMOTION +static const char *widtype = NULL; + +static const char SIG_FORWARD_CLICKED[] = "forward,clicked"; +static const char SIG_INFO_CLICKED[] = "info,clicked"; +static const char SIG_NEXT_CLICKED[] = "next,clicked"; +static const char SIG_PAUSE_CLICKED[] = "pause,clicked"; +static const char SIG_PLAY_CLICKED[] = "play,clicked"; +static const char SIG_PREV_CLICKED[] = "prev,clicked"; +static const char SIG_REWIND_CLICKED[] = "rewind,clicked"; +static const char SIG_STOP_CLICKED[] = "stop,clicked"; + +static const Evas_Smart_Cb_Description _signals[] = { + { SIG_FORWARD_CLICKED, "" }, + { SIG_INFO_CLICKED, "" }, + { SIG_NEXT_CLICKED, "" }, + { SIG_PAUSE_CLICKED, "" }, + { SIG_PLAY_CLICKED, "" }, + { SIG_PREV_CLICKED, "" }, + { SIG_REWIND_CLICKED, "" }, + { SIG_STOP_CLICKED, "" }, + { NULL, NULL } +}; + +static void _del_hook(Evas_Object *obj); +static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl); +static void _theme_hook(Evas_Object *obj); +static void _sizing_eval(Evas_Object *obj); +static void _on_focus_hook(void *data, Evas_Object *obj); +static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src, + Evas_Callback_Type type, void *event_info); + +static Eina_Bool +_event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info) +{ + if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE; + Evas_Event_Key_Down *ev = event_info; + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return EINA_FALSE; + if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE; + if (elm_widget_disabled_get(obj)) return EINA_FALSE; + if (!wd->video) return EINA_FALSE; + if ((!strcmp(ev->keyname, "Left")) || (!strcmp(ev->keyname, "KP_Left"))) + { + double current, last; + + current = elm_video_play_position_get(wd->video); + last = elm_video_play_length_get(wd->video); + + if (current < last) + { + current += last / 100; + elm_video_play_position_set(wd->video, current); + } + + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + return EINA_TRUE; + } + if ((!strcmp(ev->keyname, "Right")) || (!strcmp(ev->keyname, "KP_Right"))) + { + double current, last; + + current = elm_video_play_position_get(wd->video); + last = elm_video_play_length_get(wd->video); + + if (current > 0) + { + current -= last / 100; + if (current < 0) current = 0; + elm_video_play_position_set(wd->video, current); + } + + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + return EINA_TRUE; + } + if (!strcmp(ev->keyname, "space")) + { + if (elm_video_is_playing(wd->video)) + elm_video_pause(wd->video); + else + elm_video_play(wd->video); + ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD; + return EINA_TRUE; + } + fprintf(stderr, "keyname: '%s' not handle\n", ev->keyname); + return EINA_FALSE; +} + +static void +_on_focus_hook(void *data __UNUSED__, Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + if (elm_widget_focus_get(obj)) + { + edje_object_signal_emit(wd->layout, "elm,action,focus", "elm"); + evas_object_focus_set(wd->layout, EINA_TRUE); + } + else + { + edje_object_signal_emit(wd->layout, "elm,action,unfocus", "elm"); + evas_object_focus_set(wd->layout, EINA_FALSE); + } +} + +static void +_mirrored_set(Evas_Object *obj, Eina_Bool rtl) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + edje_object_mirrored_set(wd->layout, rtl); +} + +static void +_theme_hook(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + _elm_widget_mirrored_reload(obj); + _mirrored_set(obj, elm_widget_mirrored_get(obj)); + _elm_theme_object_set(obj, wd->layout, "video", "base", elm_widget_style_get(obj)); + edje_object_scale_set(wd->layout, elm_widget_scale_get(obj) * + _elm_config->scale); + +#define UPDATE_THEME(Obj, Target, Layout, Name) \ + if (Target) \ + { \ + elm_object_style_set(Target, elm_widget_style_get(Obj)); \ + if (!edje_object_part_swallow(Layout, Name, Target)) \ + evas_object_hide(Target); \ + elm_object_disabled_set(Target, elm_widget_disabled_get(Obj)); \ + } + + UPDATE_THEME(obj, wd->forward, wd->layout, "media_player/forward"); + UPDATE_THEME(obj, wd->info, wd->layout, "media_player/info"); + UPDATE_THEME(obj, wd->next, wd->layout, "media_player/next"); + UPDATE_THEME(obj, wd->pause, wd->layout, "media_player/pause"); + UPDATE_THEME(obj, wd->play, wd->layout, "media_player/play"); + UPDATE_THEME(obj, wd->prev, wd->layout, "media_player/prev"); + UPDATE_THEME(obj, wd->rewind, wd->layout, "media_player/rewind"); + UPDATE_THEME(obj, wd->next, wd->layout, "media_player/next"); + UPDATE_THEME(obj, wd->slider, wd->layout, "media_player/slider"); + + _sizing_eval(obj); +} + +static void +_sizing_eval(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + Evas_Coord w, h; + + if (!wd) return; + edje_object_size_min_get(wd->layout, &w, &h); + edje_object_size_min_restricted_calc(wd->layout, &w, &h, w, h); + evas_object_size_hint_min_set(obj, w, h); +} + +static void +_update_slider(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + double pos, length; + Eina_Bool seekable; + + if (!wd) return ; + seekable = elm_video_is_seekable(wd->video); + length = elm_video_play_length_get(wd->video); + pos = elm_video_play_position_get(wd->video); + + elm_object_disabled_set(wd->slider, !seekable); + elm_slider_min_max_set(wd->slider, 0, length); + elm_slider_value_set(wd->slider, pos); +} + +static void +_update_position(void *data, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + elm_video_play_position_set(wd->video, elm_slider_value_get(wd->slider)); +} + +static void +_forward(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + double pos, length; + + if (!wd) return ; + + pos = elm_video_play_position_get(wd->video); + length = elm_video_play_length_get(wd->video); + + pos += length * 0.3; + elm_video_play_position_set(wd->video, pos); + + evas_object_smart_callback_call(player, SIG_FORWARD_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,forward", "elm"); +} + +static void +_info(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + + evas_object_smart_callback_call(player, SIG_INFO_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,info", "elm"); +} + +static void +_next(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + double pos, length; + + if (!wd) return ; + + pos = elm_video_play_position_get(wd->video); + length = elm_video_play_length_get(wd->video); + + pos += length * 0.1; + elm_video_play_position_set(wd->video, pos); + + evas_object_smart_callback_call(player, SIG_NEXT_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,next", "elm"); +} + +static void +_pause(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + + edje_object_signal_emit(wd->layout, "elm,player,pause", "elm"); + elm_video_pause(wd->video); + + evas_object_smart_callback_call(player, SIG_PAUSE_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,pause", "elm"); +} + +static void +_play(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + elm_video_play(wd->video); + evas_object_smart_callback_call(player, SIG_PLAY_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,play", "elm"); +} + +static void +_prev(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + double pos, length; + + if (!wd) return ; + + pos = elm_video_play_position_get(wd->video); + length = elm_video_play_length_get(wd->video); + + pos -= length * 0.1; + elm_video_play_position_set(wd->video, pos); + evas_object_smart_callback_call(player, SIG_PREV_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,prev", "elm"); +} + +static void +_rewind(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + elm_video_play_position_set(wd->video, 0); + evas_object_smart_callback_call(player, SIG_REWIND_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,rewind", "elm"); +} + +static void +_stop(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + + evas_object_smart_callback_call(player, SIG_STOP_CLICKED, NULL); + edje_object_signal_emit(wd->layout, "elm,button,stop", "elm"); +} + +static void +_play_started(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + + edje_object_signal_emit(wd->layout, "elm,player,play", "elm"); +} + +static void +_play_finished(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__) +{ + Evas_Object *player = data; + Widget_Data *wd = elm_widget_data_get(player); + + if (!wd) return ; + + edje_object_signal_emit(wd->layout, "elm,player,pause", "elm"); +} + +static void +_cleanup_callback(Widget_Data *wd) +{ + if (!wd || !wd->emotion) return; + + evas_object_smart_callback_del(wd->emotion, "frame_decode", + _update_slider); + evas_object_smart_callback_del(wd->emotion, "frame_resize", + _update_slider); + evas_object_smart_callback_del(wd->emotion, "length_change", + _update_slider); + evas_object_smart_callback_del(wd->emotion, "position_update", + _update_slider); + evas_object_smart_callback_del(wd->emotion, "playback_started", + _play_started); + evas_object_smart_callback_del(wd->emotion, "playback_finished", + _play_finished); + elm_object_disabled_set(wd->slider, EINA_TRUE); + elm_object_disabled_set(wd->forward, EINA_TRUE); + elm_object_disabled_set(wd->info, EINA_TRUE); + elm_object_disabled_set(wd->next, EINA_TRUE); + elm_object_disabled_set(wd->pause, EINA_TRUE); + elm_object_disabled_set(wd->play, EINA_TRUE); + elm_object_disabled_set(wd->prev, EINA_TRUE); + elm_object_disabled_set(wd->rewind, EINA_TRUE); + elm_object_disabled_set(wd->next, EINA_TRUE); + wd->video = NULL; + wd->emotion = NULL; +} + +static void +_track_video(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +{ + Widget_Data *wd = elm_widget_data_get(obj); + + _cleanup_callback(wd); +} + +static void +_del_hook(Evas_Object *obj) +{ + Widget_Data *wd = elm_widget_data_get(obj); + + if (!wd) return; + _cleanup_callback(wd); + free(wd); +} + +static Evas_Object * +_player_button_add(Evas_Object *parent, Evas_Object *obj, Evas_Object *layout, const char *name, Evas_Smart_Cb func) +{ + Evas_Object *ic; + Evas_Object *bt; + + ic = elm_icon_add(parent); + elm_icon_standard_set(ic, name); + evas_object_size_hint_aspect_set(ic, EVAS_ASPECT_CONTROL_VERTICAL, 1, 1); + bt = elm_button_add(parent); + elm_widget_mirrored_automatic_set(bt, EINA_FALSE); + elm_button_icon_set(bt, ic); + evas_object_size_hint_align_set(bt, 0.0, 0.0); + elm_object_style_set(bt, "anchor"); + evas_object_smart_callback_add(bt, "clicked", func, obj); + elm_widget_sub_object_add(obj, bt); + + if (!edje_object_part_swallow(layout, name, bt)) + evas_object_hide(bt); + return bt; +} + +static const char * +_double_to_time(double value) +{ + char buf[256]; + int ph, pm, ps, pf; + + ph = value / 3600; + pm = value / 60 - (ph * 60); + ps = value - (pm * 60); + pf = value * 100 - (ps * 100) - (pm * 60 * 100) - (ph * 60 * 60 * 100); + if (ph) + snprintf(buf, sizeof(buf), "%i:%02i:%02i.%02i", + ph, pm, ps, pf); + else if (pm) + snprintf(buf, sizeof(buf), "%02i:%02i.%02i", + pm, ps, pf); + else + snprintf(buf, sizeof(buf), "%02i.%02i", + ps, pf); + + return eina_stringshare_add(buf); +} +#endif + +EAPI Evas_Object * +elm_player_add(Evas_Object *parent) +{ +#ifdef HAVE_EMOTION + Evas_Object *obj; + Evas *e; + Widget_Data *wd; + + ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL); + ELM_SET_WIDTYPE(widtype, "player"); + elm_widget_type_set(obj, "player"); + elm_widget_sub_object_add(parent, obj); + elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL); + elm_widget_data_set(obj, wd); + elm_widget_del_hook_set(obj, _del_hook); + elm_widget_theme_hook_set(obj, _theme_hook); + elm_widget_can_focus_set(obj, EINA_TRUE); + elm_widget_event_hook_set(obj, _event_hook); + + wd->layout = edje_object_add(e); + _elm_theme_object_set(obj, wd->layout, "player", "base", "default"); + elm_widget_resize_object_set(obj, wd->layout); + elm_widget_sub_object_add(obj, wd->layout); + evas_object_show(wd->layout); + evas_object_size_hint_weight_set(wd->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + + wd->forward = _player_button_add(parent, obj, wd->layout, "media_player/forward", _forward); + wd->info = _player_button_add(parent, obj, wd->layout, "media_player/info", _info); + wd->next = _player_button_add(parent, obj, wd->layout, "media_player/next", _next); + wd->pause = _player_button_add(parent, obj, wd->layout, "media_player/pause", _pause); + wd->play = _player_button_add(parent, obj, wd->layout, "media_player/play", _play); + wd->prev = _player_button_add(parent, obj, wd->layout, "media_player/prev", _prev); + wd->rewind = _player_button_add(parent, obj, wd->layout, "media_player/rewind", _rewind); + wd->stop = _player_button_add(parent, obj, wd->layout, "media_player/stop", _stop); + + wd->slider = elm_slider_add(parent); + elm_widget_sub_object_add(obj, wd->slider); + elm_slider_indicator_format_function_set(wd->slider, _double_to_time, eina_stringshare_del); + elm_slider_units_format_function_set(wd->slider, _double_to_time, eina_stringshare_del); + elm_slider_min_max_set(wd->slider, 0, 0); + elm_slider_value_set(wd->slider, 0); + elm_object_disabled_set(wd->slider, EINA_TRUE); + evas_object_size_hint_align_set(wd->slider, EVAS_HINT_FILL, 0.5); + evas_object_size_hint_weight_set(wd->slider, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + edje_object_part_swallow(wd->layout, "media_player/slider", wd->slider); + evas_object_smart_callback_add(wd->slider, "changed", _update_position, obj); + + wd->emotion = NULL; + wd->video = NULL; + + _mirrored_set(obj, elm_widget_mirrored_get(obj)); + _sizing_eval(obj); + + return obj; +#else + (void) parent; + return NULL; +#endif +} + +EAPI void +elm_player_video_set(Evas_Object *player, Evas_Object *video) +{ +#ifdef HAVE_EMOTION + ELM_CHECK_WIDTYPE(player, widtype); + Widget_Data *wd = elm_widget_data_get(player); + double pos, length; + Eina_Bool seekable; + + if (!_elm_video_check(video)) return ; + + _cleanup_callback(wd); + + wd->video = video; + + if (!wd->video) + { + wd->emotion = NULL; + return ; + } + + elm_object_disabled_set(wd->slider, EINA_FALSE); + elm_object_disabled_set(wd->forward, EINA_FALSE); + elm_object_disabled_set(wd->info, EINA_FALSE); + elm_object_disabled_set(wd->next, EINA_FALSE); + elm_object_disabled_set(wd->pause, EINA_FALSE); + elm_object_disabled_set(wd->play, EINA_FALSE); + elm_object_disabled_set(wd->prev, EINA_FALSE); + elm_object_disabled_set(wd->rewind, EINA_FALSE); + elm_object_disabled_set(wd->next, EINA_FALSE); + + wd->emotion = elm_video_emotion_get(wd->video); + evas_object_event_callback_add(wd->video, EVAS_CALLBACK_DEL, + _track_video, player); + + seekable = elm_video_is_seekable(wd->video); + length = elm_video_play_length_get(wd->video); + pos = elm_video_play_position_get(wd->video); + + elm_object_disabled_set(wd->slider, !seekable); + elm_slider_min_max_set(wd->slider, 0, length); + elm_slider_value_set(wd->slider, pos); + + if (elm_video_is_playing(wd->video)) edje_object_signal_emit(wd->layout, "elm,player,play", "elm"); + else edje_object_signal_emit(wd->layout, "elm,player,pause", "elm"); + + evas_object_smart_callback_add(wd->emotion, "frame_decode", + _update_slider, player); + evas_object_smart_callback_add(wd->emotion, "frame_resize", + _update_slider, player); + evas_object_smart_callback_add(wd->emotion, "length_change", + _update_slider, player); + evas_object_smart_callback_add(wd->emotion, "position_update", + _update_slider, player); + evas_object_smart_callback_add(wd->emotion, "playback_started", + _play_started, player); + evas_object_smart_callback_add(wd->emotion, "playback_finished", + _play_finished, player); + + /* FIXME: track info from video */ +#else + (void) player; + (void) video; +#endif +} diff --git a/legacy/elementary/src/lib/elm_priv.h b/legacy/elementary/src/lib/elm_priv.h index e64e64578c..db363fc59a 100644 --- a/legacy/elementary/src/lib/elm_priv.h +++ b/legacy/elementary/src/lib/elm_priv.h @@ -216,6 +216,9 @@ Evas_Object *_elm_scroller_edje_object_get(Evas_Object *obj); char *_elm_util_mkup_to_text(const char *mkup); char *_elm_util_text_to_mkup(const char *text); +Eina_Bool _elm_video_check(Evas_Object *video); + + extern char *_elm_appname; extern Elm_Config *_elm_config; extern const char *_elm_data_dir; diff --git a/legacy/elementary/src/lib/elm_video.c b/legacy/elementary/src/lib/elm_video.c index 844557f535..c2c2305363 100644 --- a/legacy/elementary/src/lib/elm_video.c +++ b/legacy/elementary/src/lib/elm_video.c @@ -251,6 +251,13 @@ _suspend_cb(void *data) } #endif +Eina_Bool +_elm_video_check(Evas_Object *video) +{ + ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE; + return EINA_TRUE; +} + EAPI Evas_Object * elm_video_add(Evas_Object *parent) { @@ -432,6 +439,20 @@ elm_video_is_playing(Evas_Object *video) #endif } +EAPI Eina_Bool +elm_video_is_seekable(Evas_Object *video) +{ +#ifdef HAVE_EMOTION + ELM_CHECK_WIDTYPE(video, widtype) EINA_FALSE; + Widget_Data *wd = elm_widget_data_get(video); + + return emotion_object_seekable_get(wd->emotion); +#else + (void) video; + return EINA_FALSE; +#endif +} + EAPI Eina_Bool elm_video_audio_mute_get(Evas_Object *video) {