[banshee] Support embedded and external subtitles (bgo#534581)
- From: Gabriel Burt <gburt src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [banshee] Support embedded and external subtitles (bgo#534581)
- Date: Thu, 2 Dec 2010 22:55:03 +0000 (UTC)
commit 9e86951d4234d3abdd25d301837f86475795687e
Author: Olivier Dufour <olivier duff gmail com>
Date: Wed Nov 10 23:23:48 2010 +0100
Support embedded and external subtitles (bgo#534581)
Signed-off-by: Gabriel Burt <gabriel burt gmail com>
libbanshee/banshee-player-private.h | 1 +
libbanshee/banshee-player-video.c | 37 ++++-
libbanshee/banshee-player.c | 114 ++++++++++
libbanshee/libbanshee.sln | 47 +++--
.../Banshee.GStreamer/PlayerEngine.cs | 58 +++++-
.../Banshee.GStreamerSharp/PlayerEngine.cs | 24 ++
.../Banshee.MediaEngine/NullPlayerEngine.cs | 17 ++
.../Banshee.MediaEngine/PlayerEngine.cs | 15 ++
.../Banshee.MediaEngine/PlayerEngineService.cs | 18 ++
.../Banshee.Gui.Widgets/MainMenu.cs | 3 +
.../Banshee.Gui/PlaybackActions.cs | 11 +-
.../Banshee.Gui/PlaybackSubtitleActions.cs | 220 ++++++++++++++++++++
.../Banshee.ThickClient/Banshee.ThickClient.csproj | 1 +
src/Core/Banshee.ThickClient/Makefile.am | 1 +
.../Resources/core-ui-actions-layout.xml | 1 +
15 files changed, 545 insertions(+), 23 deletions(-)
---
diff --git a/libbanshee/banshee-player-private.h b/libbanshee/banshee-player-private.h
index f3e28a7..4d9bf44 100644
--- a/libbanshee/banshee-player-private.h
+++ b/libbanshee/banshee-player-private.h
@@ -41,6 +41,7 @@
#include <gdk/gdk.h>
#include <gst/fft/gstfftf32.h>
#include <gst/pbutils/pbutils.h>
+#include <gst/tag/tag.h>
#if defined(GDK_WINDOWING_X11)
# include <gdk/gdkx.h>
diff --git a/libbanshee/banshee-player-video.c b/libbanshee/banshee-player-video.c
index 8319403..b901354 100644
--- a/libbanshee/banshee-player-video.c
+++ b/libbanshee/banshee-player-video.c
@@ -57,7 +57,7 @@ bp_video_find_xoverlay (BansheePlayer *player)
g_mutex_unlock (player->video_mutex);
return FALSE;
}
-
+
xoverlay = GST_IS_BIN (video_sink)
? gst_bin_get_by_interface (GST_BIN (video_sink), GST_TYPE_X_OVERLAY)
: video_sink;
@@ -89,6 +89,41 @@ bp_video_find_xoverlay (BansheePlayer *player)
#endif /* GDK_WINDOWING_X11 || GDK_WINDOWING_WIN32 */
+P_INVOKE int
+bp_get_subtitle_count (BansheePlayer *player)
+{
+ g_return_val_if_fail (IS_BANSHEE_PLAYER (player), 0);
+
+ int n_text;
+ g_object_get (G_OBJECT (player->playbin), "n-text", &n_text, NULL);
+ return n_text;
+}
+
+P_INVOKE void
+bp_set_subtitle (BansheePlayer *player, int index)
+{
+ g_return_if_fail (IS_BANSHEE_PLAYER (player));
+
+ int n_text = bp_get_subtitle_count (player);
+
+ if (n_text == 0 || index < -1 || index >= n_text)
+ return;
+
+ bp_debug ("[subtitle]: set subtitle to %d.", index);
+
+ gint flags;
+ g_object_get (G_OBJECT (player->playbin), "flags", &flags, NULL);
+
+ if (index == -1) {
+ flags &= ~(1 << 2);//GST_PLAY_FLAG_TEXT
+ g_object_set (G_OBJECT (player->playbin), "flags", flags, NULL);
+ } else {
+ flags |= (1 << 2);//GST_PLAY_FLAG_TEXT
+ g_object_set (G_OBJECT (player->playbin), "flags", flags, NULL);
+ g_object_set (G_OBJECT (player->playbin), "current-text", index, NULL);
+ }
+}
+
static void
bp_video_sink_element_added (GstBin *videosink, GstElement *element, BansheePlayer *player)
{
diff --git a/libbanshee/banshee-player.c b/libbanshee/banshee-player.c
index ef15efd..03a9d2d 100644
--- a/libbanshee/banshee-player.c
+++ b/libbanshee/banshee-player.c
@@ -49,6 +49,47 @@ bp_pipeline_set_state (BansheePlayer *player, GstState state)
}
}
+static void
+bp_lookup_for_subtitle (BansheePlayer *player, const gchar *uri)
+{
+ gchar *scheme, *filename, *subfile, *dot, *suburi;
+ int j;
+ // Always enable rendering of subtitles
+ gint flags;
+ g_object_get (G_OBJECT (player->playbin), "flags", &flags, NULL);
+ flags |= (1 << 2);//GST_PLAY_FLAG_TEXT
+ g_object_set (G_OBJECT (player->playbin), "flags", flags, NULL);
+
+ bp_debug ("[subtitle]: lookup for subtitle for video file.");
+ scheme = g_uri_parse_scheme (uri);
+ static gchar *subtitle_extensions[] = { ".srt", ".sub", ".smi", ".txt", ".mpl", ".dks", ".qtx" };
+ if (scheme == NULL || strcmp (scheme, "file") != 0) {
+ g_free (scheme);
+ return;
+ }
+ g_free (scheme);
+
+ dot = g_strrstr (uri, ".");
+ if (dot == NULL)
+ return;
+ filename = g_filename_from_uri (g_strndup (uri, dot - uri), NULL, NULL);
+
+ for (j = 0; j < G_N_ELEMENTS (subtitle_extensions); j++) {
+ subfile = g_strconcat (filename, subtitle_extensions[j], NULL);
+ if (g_file_test (subfile, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
+ bp_debug ("[subtitle]: Found srt file: %s", subfile);
+ suburi = g_filename_to_uri (subfile, NULL, NULL);
+ g_object_set (G_OBJECT (player->playbin), "suburi", suburi, NULL);
+ g_free (suburi);
+ g_free (subfile);
+ g_free (filename);
+ return;
+ }
+ g_free (subfile);
+ }
+ g_free (filename);
+}
+
// ---------------------------------------------------------------------------
// Public Functions
// ---------------------------------------------------------------------------
@@ -128,6 +169,9 @@ bp_open (BansheePlayer *player, const gchar *uri)
// Pass the request off to playbin
g_object_set (G_OBJECT (player->playbin), "uri", uri, NULL);
+ // Lookup for subtitle files with same name/folder
+ bp_lookup_for_subtitle (player, uri);
+
player->in_gapless_transition = FALSE;
return TRUE;
@@ -172,6 +216,7 @@ bp_set_next_track (BansheePlayer *player, const gchar *uri)
g_return_val_if_fail (IS_BANSHEE_PLAYER (player), FALSE);
g_return_val_if_fail (player->playbin != NULL, FALSE);
g_object_set (G_OBJECT (player->playbin), "uri", uri, NULL);
+ bp_lookup_for_subtitle (player, uri);
return TRUE;
}
@@ -373,3 +418,72 @@ bp_set_about_to_finish_callback (BansheePlayer *player, BansheePlayerAboutToFini
{
SET_CALLBACK (about_to_finish_cb);
}
+
+P_INVOKE void
+bp_set_subtitle_uri (BansheePlayer *player, const gchar *uri)
+{
+ g_return_if_fail (IS_BANSHEE_PLAYER (player));
+ gint64 pos = -1;
+ GstState state;
+ GstFormat format = GST_FORMAT_BYTES;
+ gboolean paused = FALSE;
+
+ // Gstreamer playbin do not support to set suburi during playback
+ // so have to stop/play and seek
+ gst_element_get_state (player->playbin, &state, NULL, 0);
+ paused = (state == GST_STATE_PAUSED);
+ if (state >= GST_STATE_PAUSED) {
+ gst_element_query_position (player->playbin, &format, &pos);
+ gst_element_set_state (player->playbin, GST_STATE_READY);
+ // Force to wait asynch operation
+ gst_element_get_state (player->playbin, &state, NULL, -1);
+ }
+
+ g_object_set (G_OBJECT (player->playbin), "suburi", uri, NULL);
+ gst_element_set_state (player->playbin, paused ? GST_STATE_PAUSED : GST_STATE_PLAYING);
+
+ // Force to wait asynch operation
+ gst_element_get_state (player->playbin, &state, NULL, -1);
+
+ if (pos != -1) {
+ gst_element_seek_simple (player->playbin, format, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, pos);
+ }
+}
+
+P_INVOKE gchar *
+bp_get_subtitle_uri (BansheePlayer *player)
+{
+ gchar *uri;
+ g_return_val_if_fail (IS_BANSHEE_PLAYER (player), "");
+ g_object_get (G_OBJECT (player->playbin), "suburi", &uri, NULL);
+ return uri;
+}
+
+P_INVOKE gchar *
+bp_get_subtitle_description (BansheePlayer *player, int i)
+{
+ gchar *code;
+ gchar *desc = NULL;
+ GstTagList *tags = NULL;
+
+ g_return_val_if_fail (IS_BANSHEE_PLAYER (player), NULL);
+
+ g_signal_emit_by_name (G_OBJECT (player->playbin), "get-text-tags", i, &tags);
+ if (G_LIKELY(tags)) {
+ gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &code);
+ gst_tag_list_free (tags);
+
+ g_return_val_if_fail (code != NULL, NULL);
+
+ // ISO 639-2 undetermined language
+ if (strcmp ((const gchar *)code, "und") == 0) {
+ return NULL;
+ }
+ bp_debug ("[subtitle]: iso 639-2 subtitle code %s", code);
+ desc = (gchar *) gst_tag_get_language_name ((const gchar *)&code);
+ bp_debug ("[subtitle]: subtitle language: %s", desc);
+
+ g_free (code);
+ }
+ return desc;
+}
diff --git a/libbanshee/libbanshee.sln b/libbanshee/libbanshee.sln
index 4b058d4..3a03817 100644
--- a/libbanshee/libbanshee.sln
+++ b/libbanshee/libbanshee.sln
@@ -1,20 +1,27 @@
-
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual C++ Express 2008
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbanshee", "libbanshee.vcproj", "{8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Win32 = Debug|Win32
- Release|Win32 = Release|Win32
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Debug|Win32.ActiveCfg = Debug|Win32
- {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Debug|Win32.Build.0 = Debug|Win32
- {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Release|Win32.ActiveCfg = Release|Win32
- {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Release|Win32.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{2857B73E-F847-4B02-9238-064979017E93}") = "libbanshee", "libbanshee.cproj", "{6B781836-AB65-49EF-BECD-CCC193C5D589}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {6B781836-AB65-49EF-BECD-CCC193C5D589}.Debug|Win32.ActiveCfg = Debug|Any CPU
+ {6B781836-AB65-49EF-BECD-CCC193C5D589}.Debug|Win32.Build.0 = Debug|Any CPU
+ {6B781836-AB65-49EF-BECD-CCC193C5D589}.Release|Win32.ActiveCfg = Debug|Any CPU
+ {6B781836-AB65-49EF-BECD-CCC193C5D589}.Release|Win32.Build.0 = Debug|Any CPU
+ {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Debug|Win32.Build.0 = Debug|Win32
+ {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Release|Win32.ActiveCfg = Release|Win32
+ {8045CB14-6CFB-4CBE-9A09-77FAD23B8F83}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ StartupItem = libbanshee.cproj
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs b/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
index 1d4cf75..9319dd1 100644
--- a/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
+++ b/src/Backends/Banshee.GStreamer/Banshee.GStreamer/PlayerEngine.cs
@@ -691,6 +691,47 @@ namespace Banshee.GStreamer
}
}
+ public override int SubtitleCount {
+ get { return bp_get_subtitle_count (handle); }
+ }
+
+ public override int SubtitleIndex {
+ set { bp_set_subtitle (handle, value); }
+ }
+
+ public override SafeUri SubtitleUri {
+ set {
+ if (value != null) {
+ IntPtr uri_ptr = GLib.Marshaller.StringToPtrGStrdup (value.AbsoluteUri);
+ try {
+ bp_set_subtitle_uri (handle, uri_ptr);
+ } finally {
+ GLib.Marshaller.Free (uri_ptr);
+ }
+ }
+ }
+ get {
+ IntPtr uri_ptr = IntPtr.Zero;
+ try {
+ uri_ptr = bp_get_subtitle_uri (handle);
+ string uri = GLib.Marshaller.Utf8PtrToString (uri_ptr);
+ return new SafeUri(uri);
+ } finally {
+ GLib.Marshaller.Free (uri_ptr);
+ }
+ }
+ }
+
+ public override string GetSubtitleDescription (int index)
+ {
+ IntPtr desc_ptr = IntPtr.Zero;
+ try {
+ desc_ptr = bp_get_subtitle_description (handle, index);
+ return GLib.Marshaller.Utf8PtrToString (desc_ptr);
+ } finally {
+ GLib.Marshaller.Free (desc_ptr);
+ }
+ }
#region ISupportClutter
@@ -949,5 +990,20 @@ namespace Banshee.GStreamer
[DllImport ("libbanshee.dll")]
private static extern IntPtr clutter_gst_video_sink_new (IntPtr texture);
- }
+
+ [DllImport ("libbanshee.dll")]
+ private static extern int bp_get_subtitle_count (HandleRef player);
+
+ [DllImport ("libbanshee.dll")]
+ private static extern void bp_set_subtitle (HandleRef player, int index);
+
+ [DllImport ("libbanshee.dll")]
+ private static extern void bp_set_subtitle_uri (HandleRef player, IntPtr uri);
+
+ [DllImport ("libbanshee.dll")]
+ private static extern IntPtr bp_get_subtitle_uri (HandleRef player);
+
+ [DllImport ("libbanshee.dll")]
+ private static extern IntPtr bp_get_subtitle_description (HandleRef player, int index);
+ }
}
diff --git a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs
index e05fe74..e60320f 100644
--- a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs
+++ b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs
@@ -175,6 +175,13 @@ namespace Banshee.GStreamerSharp
OnStateChanged (PlayerState.Paused);
}
+ public override string GetSubtitleDescription (int index)
+ {
+ return playbin.GetTextTags (index)
+ .GetTag (Gst.Tag.LanguageCode)
+ .FirstOrDefault (t => t != null);
+ }
+
public override ushort Volume {
get { return (ushort) Math.Round (playbin.Volume * 100.0); }
set { playbin.Volume = (value / 100.0); }
@@ -229,5 +236,22 @@ namespace Banshee.GStreamerSharp
public override VideoDisplayContextType VideoDisplayContextType {
get { return VideoDisplayContextType.Unsupported; }
}
+
+ public override int SubtitleCount {
+ get { return playbin.NText; }
+ }
+
+ public override int SubtitleIndex {
+ set {
+ if (value >= 0 && value < SubtitleCount) {
+ playbin.CurrentText = value;
+ }
+ }
+ }
+
+ public override SafeUri SubtitleUri {
+ set { playbin.Suburi = value.AbsoluteUri; }
+ get { return playbin.Suburi; }
+ }
}
}
diff --git a/src/Core/Banshee.Services/Banshee.MediaEngine/NullPlayerEngine.cs b/src/Core/Banshee.Services/Banshee.MediaEngine/NullPlayerEngine.cs
index 32a846b..5dd3a6a 100644
--- a/src/Core/Banshee.Services/Banshee.MediaEngine/NullPlayerEngine.cs
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/NullPlayerEngine.cs
@@ -93,5 +93,22 @@ namespace Banshee.MediaEngine
get { return "Null Player Engine"; }
}
+ public override int SubtitleCount {
+ get { return 0; }
+ }
+
+ public override int SubtitleIndex {
+ set { return; }
+ }
+
+ public override SafeUri SubtitleUri {
+ set { return; }
+ get { return null; }
+ }
+
+ public override string GetSubtitleDescription (int index)
+ {
+ return string.Empty;
+ }
}
}
diff --git a/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs b/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
index f34febc..5365747 100644
--- a/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngine.cs
@@ -256,6 +256,8 @@ namespace Banshee.MediaEngine
OnEventChanged (PlayerEvent.TrackInfoUpdated);
}
+ public abstract string GetSubtitleDescription (int index);
+
public TrackInfo CurrentTrack {
get { return current_track; }
}
@@ -318,5 +320,18 @@ namespace Banshee.MediaEngine
set { }
get { return IntPtr.Zero; }
}
+
+ public abstract int SubtitleCount {
+ get;
+ }
+
+ public abstract int SubtitleIndex {
+ set;
+ }
+
+ public abstract SafeUri SubtitleUri {
+ set;
+ get;
+ }
}
}
diff --git a/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs b/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
index 5d52c56..8d30711 100644
--- a/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
+++ b/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
@@ -534,6 +534,11 @@ namespace Banshee.MediaEngine
CurrentState == PlayerState.Contacting;
}
+ public string GetSubtitleDescription (int index)
+ {
+ return active_engine.GetSubtitleDescription (index);
+ }
+
private void CheckPending ()
{
if (pending_engine != null && pending_engine != active_engine) {
@@ -626,6 +631,19 @@ namespace Banshee.MediaEngine
get { return ((active_engine is IEqualizer) && active_engine.SupportsEqualizer); }
}
+ public int SubtitleCount {
+ get { return active_engine.SubtitleCount; }
+ }
+
+ public int SubtitleIndex {
+ set { active_engine.SubtitleIndex = value; }
+ }
+
+ public SafeUri SubtitleUri {
+ set { active_engine.SubtitleUri = value; }
+ get { return active_engine.SubtitleUri; }
+ }
+
public VideoDisplayContextType VideoDisplayContextType {
get { return active_engine.VideoDisplayContextType; }
}
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/MainMenu.cs b/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/MainMenu.cs
index 2c71331..2879951 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/MainMenu.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui.Widgets/MainMenu.cs
@@ -48,6 +48,9 @@ namespace Banshee.Gui.Widgets
((PlaybackShuffleActions)interface_service.FindActionGroup ("PlaybackShuffle")).AttachSubmenu (
"/MainMenu/PlaybackMenu/ShuffleMenu");
+ ((PlaybackSubtitleActions)interface_service.FindActionGroup ("PlaybackSubtitle")).AttachSubmenu (
+ "/MainMenu/PlaybackMenu/SubtitleMenu");
+
menu.Show ();
PackStart (menu, true, true, 0);
}
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
index 25006c5..02de26d 100644
--- a/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackActions.cs
@@ -49,6 +49,7 @@ namespace Banshee.Gui
private Gtk.Action play_pause_action;
private PlaybackRepeatActions repeat_actions;
private PlaybackShuffleActions shuffle_actions;
+ private PlaybackSubtitleActions subtitle_actions;
public PlaybackRepeatActions RepeatActions {
get { return repeat_actions; }
@@ -58,6 +59,10 @@ namespace Banshee.Gui
get { return shuffle_actions; }
}
+ public PlaybackSubtitleActions SubtitleActions {
+ get { return subtitle_actions; }
+ }
+
public PlaybackActions () : base ("Playback")
{
ImportantByDefault = false;
@@ -116,6 +121,7 @@ namespace Banshee.Gui
repeat_actions = new PlaybackRepeatActions (Actions);
shuffle_actions = new PlaybackShuffleActions (Actions, this);
+ subtitle_actions = new PlaybackSubtitleActions (Actions);
}
private void OnPlayerEvent (PlayerEventArgs args)
@@ -144,9 +150,12 @@ namespace Banshee.Gui
}
switch (args.Current) {
+ case PlayerState.Loaded:
+ ShowStopAction ();
+ subtitle_actions.ReloadEmbeddedSubtitle ();
+ break;
case PlayerState.Contacting:
case PlayerState.Loading:
- case PlayerState.Loaded:
case PlayerState.Playing:
ShowStopAction ();
break;
diff --git a/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackSubtitleActions.cs b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackSubtitleActions.cs
new file mode 100644
index 0000000..73a8bc9
--- /dev/null
+++ b/src/Core/Banshee.ThickClient/Banshee.Gui/PlaybackSubtitleActions.cs
@@ -0,0 +1,220 @@
+//
+// PlaybackSubtitleActions.cs
+//
+// Author:
+// Olivier Dufour <olivier duff gmail com>
+//
+// Copyright 2010 Olivier Dufour
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+
+using Gtk;
+
+using Banshee.ServiceStack;
+using Banshee.I18n;
+using Banshee.Collection;
+
+using Hyena;
+
+namespace Banshee.Gui
+{
+ public class PlaybackSubtitleActions : BansheeActionGroup, IEnumerable<RadioAction>
+ {
+ private readonly List<RadioActionEntry> embedded_subtitles_actions = new List<RadioActionEntry> ();
+ public event EventHandler Changed;
+ private Menu mainMenu;
+
+ public new bool Sensitive {
+ get { return base.Sensitive; }
+ set {
+ base.Sensitive = value;
+ OnChanged ();
+ }
+ }
+
+ public PlaybackSubtitleActions (InterfaceActionService actionService)
+ : base (actionService, "PlaybackSubtitle")
+ {
+ Actions.AddActionGroup (this);
+
+ Add (new ActionEntry [] {
+ new ActionEntry ("SubtitleMenuAction", null,
+ Catalog.GetString ("Subtitle"), null,
+ Catalog.GetString ("Subtitle"), null),
+ new ActionEntry ("LoadSubtitleAction", null,
+ Catalog.GetString ("Load subtitle"), null,
+ Catalog.GetString ("Load subtitle file"), OnLoadSubtitleAction)
+ });
+
+ this["SubtitleMenuAction"].Sensitive = true;
+
+ ServiceManager.PlaybackController.TrackStarted += OnPlaybackTrackStarted;
+
+ //TODO: Set default sub
+ }
+
+ private void OnLoadSubtitleAction (object o, EventArgs args)
+ {
+ var chooser = new Banshee.Gui.Dialogs.FileChooserDialog (
+ Catalog.GetString ("Load Subtitle"),
+ ServiceManager.Get<Banshee.Gui.GtkElementsService> ().PrimaryWindow,
+ FileChooserAction.Open
+ );
+
+ chooser.DefaultResponse = ResponseType.Ok;
+ chooser.SelectMultiple = false;
+
+ chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
+ chooser.AddButton (Catalog.GetString ("L_oad"), ResponseType.Ok);
+
+ Hyena.Gui.GtkUtilities.SetChooserShortcuts (chooser,
+ ServiceManager.SourceManager.VideoLibrary.BaseDirectory
+ );
+
+ var filter = new FileFilter();
+ filter.AddMimeType ("text/x-pango-markup");
+ filter.AddMimeType ("text/plain");
+ filter.Name = Catalog.GetString ("Subtitle files");
+ chooser.AddFilter (filter);
+
+ if (chooser.Run () == (int)ResponseType.Ok) {
+ ServiceManager.PlayerEngine.SubtitleUri = new SafeUri (chooser.Uri);
+ }
+
+ chooser.Destroy ();
+ }
+
+ private void OnPlaybackTrackStarted (object o, EventArgs args)
+ {
+ var current_track = ServiceManager.PlaybackController.CurrentTrack;
+
+ if (current_track != null &&
+ (current_track.MediaAttributes & TrackMediaAttributes.VideoStream) != 0) {
+ //TODO: activate load subtitle file menu else unactivate
+ }
+ }
+
+ private void ClearEmbeddedSubtitles ()
+ {
+ foreach (RadioActionEntry action in embedded_subtitles_actions) {
+ this.Remove (action.name);
+ }
+ }
+
+ private void AddEmbeddedSubtitle (int i)
+ {
+ string desc = ServiceManager.PlayerEngine.GetSubtitleDescription (i);
+ if (String.IsNullOrEmpty (desc)) {
+ desc = String.Format (Catalog.GetString ("Subtitle {0}"), i);
+ }
+ RadioActionEntry new_action = new RadioActionEntry (String.Format ("Subtitle{0}", i), null,
+ desc, null,
+ String.Format (Catalog.GetString ("Activate embedded subtitle {0}"), i), i);
+ embedded_subtitles_actions.Add (new_action);
+
+ }
+
+ public void ReloadEmbeddedSubtitle ()
+ {
+ ClearEmbeddedSubtitles ();
+ int sub_count = ServiceManager.PlayerEngine.SubtitleCount;
+ if (sub_count == 0) {
+ RefreshMenu ();
+ return;
+ }
+ embedded_subtitles_actions.Add (new RadioActionEntry ("None", null,
+ Catalog.GetString ("None"), null,
+ Catalog.GetString ("Hide subtitles"), -1));
+ for (int i = 0; i < sub_count; i++) {
+ AddEmbeddedSubtitle (i);
+ }
+ Add (embedded_subtitles_actions.ToArray (), 0, OnActionChanged);
+ RefreshMenu ();
+ }
+
+ private void OnActionChanged (object o, ChangedArgs args)
+ {
+ Log.Debug (string.Format ("[sub] Set sub {0}", args.Current.Value));
+ ServiceManager.PlayerEngine.SubtitleIndex = args.Current.Value;
+ }
+
+ private void OnChanged ()
+ {
+ EventHandler handler = Changed;
+ if (handler != null) {
+ handler (this, EventArgs.Empty);
+ }
+ }
+
+ public IEnumerator<RadioAction> GetEnumerator ()
+ {
+ foreach (RadioActionEntry entry in embedded_subtitles_actions) {
+ yield return (RadioAction)this[entry.name];
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator ()
+ {
+ return GetEnumerator ();
+ }
+
+ public void AttachSubmenu (string menuItemPath)
+ {
+ MenuItem menu = Actions.UIManager.GetWidget (menuItemPath) as MenuItem;
+ menu.Submenu = CreateMenu ();
+ }
+
+ private void RefreshMenu ()
+ {
+ foreach (Widget w in mainMenu.Children) {
+ //RadioMenuItems are embedded subtitle ones
+ if (w is RadioMenuItem) {
+ mainMenu.Remove (w);
+ }
+ }
+ AddEmbeddedSubtitleMenu ();
+ mainMenu.ShowAll ();
+ }
+
+ public Menu CreateMenu ()
+ {
+ mainMenu = new Gtk.Menu ();
+
+ mainMenu.Append (this["LoadSubtitleAction"].CreateMenuItem ());
+ mainMenu.Append (new SeparatorMenuItem ());
+ AddEmbeddedSubtitleMenu ();
+
+ mainMenu.ShowAll ();
+ return mainMenu;
+ }
+
+ public void AddEmbeddedSubtitleMenu ()
+ {
+ foreach (RadioAction action in this) {
+ mainMenu.Append (action.CreateMenuItem ());
+ Log.Debug (string.Format ("[sub] Add {0}", action.Name));
+ }
+ }
+ }
+}
+
diff --git a/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj b/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
index f9a0442..134527b 100644
--- a/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
+++ b/src/Core/Banshee.ThickClient/Banshee.ThickClient.csproj
@@ -316,6 +316,7 @@
<Compile Include="Banshee.Gui.Widgets\CoverArtDisplay.cs" />
<Compile Include="Banshee.CairoGlyphs\BansheeLineLogo.cs" />
<Compile Include="Banshee.Gui\IGlobalUIActions.cs" />
+ <Compile Include="Banshee.Gui\PlaybackSubtitleActions.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
diff --git a/src/Core/Banshee.ThickClient/Makefile.am b/src/Core/Banshee.ThickClient/Makefile.am
index 62b6fb5..acebf12 100644
--- a/src/Core/Banshee.ThickClient/Makefile.am
+++ b/src/Core/Banshee.ThickClient/Makefile.am
@@ -123,6 +123,7 @@ SOURCES = \
Banshee.Gui/PlaybackActions.cs \
Banshee.Gui/PlaybackRepeatActions.cs \
Banshee.Gui/PlaybackShuffleActions.cs \
+ Banshee.Gui/PlaybackSubtitleActions.cs \
Banshee.Gui/SourceActions.cs \
Banshee.Gui/TrackActions.cs \
Banshee.Gui/ViewActions.cs \
diff --git a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
index f614071..8c60550 100644
--- a/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
+++ b/src/Core/Banshee.ThickClient/Resources/core-ui-actions-layout.xml
@@ -108,6 +108,7 @@
<placeholder name="PlaybackMenuAdditions"/>
<menuitem name="RepeatMenu" action="RepeatMenuAction"/>
<menuitem name="ShuffleMenu" action="ShuffleMenuAction"/>
+ <menuitem name="SubtitleMenu" action="SubtitleMenuAction"/>
</menu>
<menu name="ToolsMenu" action="ToolsMenuAction">
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]