[totem] save-file: Add save file plugin



commit cce123f328297fe7b6cadeba5525eae94df0f2bb
Author: Bastien Nocera <hadess hadess net>
Date:   Mon Dec 6 13:30:41 2010 +0000

    save-file: Add save file plugin
    
    https://bugzilla.gnome.org/show_bug.cgi?id=350261

 configure.in                              |   17 ++-
 data/org.gnome.totem.gschema.xml.in.in    |    2 +-
 data/totem.ui                             |    5 +-
 src/plugins/save-file/Makefile.am         |   15 ++
 src/plugins/save-file/save-file.plugin.in |    9 +
 src/plugins/save-file/totem-save-file.c   |  351 +++++++++++++++++++++++++++++
 6 files changed, 395 insertions(+), 4 deletions(-)
---
diff --git a/configure.in b/configure.in
index 63a6f5a..1c28b6e 100644
--- a/configure.in
+++ b/configure.in
@@ -65,7 +65,7 @@ AC_SUBST(TOTEM_API_VERSION)
 AC_DEFINE_UNQUOTED(TOTEM_API_VERSION, ["$TOTEM_API_VERSION"], [Define to the Totem plugin API version])
 
 # The full list of plugins
-allowed_plugins="bemused brasero-disc-recorder chapters coherence_upnp dbus-service galago gromit iplayer jamendo lirc media-player-keys ontop opensubtitles properties publish pythonconsole sample-python screensaver screenshot sidebar-test skipto thumbnail tracker youtube"
+allowed_plugins="bemused brasero-disc-recorder chapters coherence_upnp dbus-service galago gromit iplayer jamendo lirc media-player-keys ontop opensubtitles properties publish pythonconsole save-file sample-python screensaver screenshot sidebar-test skipto thumbnail tracker youtube"
 
 PLUGINDIR='${libdir}/totem/plugins'
 AC_SUBST(PLUGINDIR)
@@ -442,7 +442,7 @@ for plugin in ${used_plugins}; do
 		tracker)
 			PKG_CHECK_MODULES(TRACKER, tracker-client-0.8 >= 0.8.1, [HAVE_TRACKER=yes], [HAVE_TRACKER=no])
 			if test "${HAVE_TRACKER}" != "yes" ; then
-			   	PKG_CHECK_MODULES(TRACKER, tracker-client-0.9 >= 0.9.0, [HAVE_TRACKER=yes], [HAVE_TRACKER=no])
+				PKG_CHECK_MODULES(TRACKER, tracker-client-0.9 >= 0.9.0, [HAVE_TRACKER=yes], [HAVE_TRACKER=no])
 			fi
 
 			if test "${HAVE_TRACKER}" != "yes" ; then
@@ -467,6 +467,18 @@ for plugin in ${used_plugins}; do
 				add_plugin="0"
 			fi
 		;;
+		save-file)
+			dnl the nautilus check should be a run-time check
+			dnl but it might not always be running
+			PKG_CHECK_MODULES(SAVE_FILE_CHECK, gio-2.0 >= 2.26 libnautilus-extension >= 2.91.3,
+					  [BUILD_SAVE_FILE=yes], [BUILD_SAVE_FILE=no])
+			if test "${BUILD_SAVE_FILE}" != "yes" ; then
+				plugin_error_or_ignore "you need gio-2.0 >=2.26 to use the save-file plugin"
+				add_plugin="0"
+			else
+				PKG_CHECK_MODULES(SAVE_FILE, gio-2.0 >= 2.26)
+			fi
+		;;
 		youtube)
 			PKG_CHECK_MODULES(LIBGDATA, libgdata >= 0.4.0,
 				[HAVE_LIBGDATA=yes], [HAVE_LIBGDATA=no])
@@ -755,6 +767,7 @@ src/plugins/media-player-keys/Makefile
 src/plugins/opensubtitles/Makefile
 src/plugins/opensubtitles/org.gnome.totem.plugins.opensubtitles.gschema.xml.in
 src/plugins/properties/Makefile
+src/plugins/save-file/Makefile
 src/plugins/sidebar-test/Makefile
 src/plugins/skipto/Makefile
 src/plugins/sample-python/Makefile
diff --git a/data/org.gnome.totem.gschema.xml.in.in b/data/org.gnome.totem.gschema.xml.in.in
index f95a19a..4d45eb8 100644
--- a/data/org.gnome.totem.gschema.xml.in.in
+++ b/data/org.gnome.totem.gschema.xml.in.in
@@ -116,7 +116,7 @@
 			<_summary>Whether to remember the position of played audio/video files when pausing or closing them</_summary>
 		</key>
 		<key name="active-plugins" type="as">
-			<default>['skipto','chapters','screenshot','media_player_keys','screensaver','movie-properties']</default>
+			<default>['skipto','chapters','screenshot','media_player_keys','screensaver','movie-properties','save-file']</default>
 			<_summary>Active plugins list</_summary>
 			<_description>A list of the names of the plugins which are currently active (loaded and running).</_description>
 		</key>
diff --git a/data/totem.ui b/data/totem.ui
index e5ed8ed..5951af6 100644
--- a/data/totem.ui
+++ b/data/totem.ui
@@ -421,7 +421,8 @@
       <menubar name="tmw-menubar">
          <menu name="movie" action="movie-menu">
             <menuitem name="open" action="open"/>
-            <menuitem name="open-location" action="open-location"/>
+	    <menuitem name="open-location" action="open-location"/>
+	    <placeholder name="save-placeholder"/>
             <placeholder name="devices-placeholder"/>
             <separator name="recent-separator"/>
             <placeholder name="recent-placeholder"/>
@@ -509,6 +510,8 @@
          <menuitem name="fullscreen" action="fullscreen"/>
          <menuitem name="select-subtitle" action="select-subtitle"/>
          <separator/>
+         <placeholder name="save-placeholder"/>
+         <separator/>
          <menu name="popup-languages" action="languages-menu">
             <placeholder name="placeholder"/>
          </menu>
diff --git a/src/plugins/save-file/Makefile.am b/src/plugins/save-file/Makefile.am
new file mode 100644
index 0000000..39b6b64
--- /dev/null
+++ b/src/plugins/save-file/Makefile.am
@@ -0,0 +1,15 @@
+include $(top_srcdir)/src/plugins/Makefile.plugins
+
+plugindir = $(PLUGINDIR)/save-file
+plugin_LTLIBRARIES = libsave-file.la
+
+plugin_in_files = save-file.plugin.in
+
+libsave_file_la_SOURCES = totem-save-file.c
+libsave_file_la_LDFLAGS = $(plugin_ldflags)
+libsave_file_la_LIBADD = $(SAVE_FILE_LIBS)
+libsave_file_la_CFLAGS = \
+	$(plugin_cflags)		\
+	$(SAVE_FILE_CFLAGS)
+
+-include $(top_srcdir)/git.mk
diff --git a/src/plugins/save-file/save-file.plugin.in b/src/plugins/save-file/save-file.plugin.in
new file mode 100644
index 0000000..b07355f
--- /dev/null
+++ b/src/plugins/save-file/save-file.plugin.in
@@ -0,0 +1,9 @@
+[Plugin]
+Module=save-file
+IAge=1
+Builtin=true
+_Name=Save copy
+_Description=Save a copy of the currently playing movie
+Authors=Bastien Nocera
+Copyright=Copyright © 2010 Bastien Nocera
+Website=http://projects.gnome.org/totem
diff --git a/src/plugins/save-file/totem-save-file.c b/src/plugins/save-file/totem-save-file.c
new file mode 100644
index 0000000..638d8c1
--- /dev/null
+++ b/src/plugins/save-file/totem-save-file.c
@@ -0,0 +1,351 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) Bastien Nocera 2010 <hadess hadess net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <gmodule.h>
+#include <gdk/gdkx.h>
+#include <libpeas/peas-extension-base.h>
+#include <libpeas/peas-object-module.h>
+#include <libpeas/peas-activatable.h>
+
+#include "totem-plugin.h"
+#include "totem-interface.h"
+
+#define TOTEM_TYPE_SAVE_FILE_PLUGIN		(totem_save_file_plugin_get_type ())
+#define TOTEM_SAVE_FILE_PLUGIN(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), TOTEM_TYPE_SAVE_FILE_PLUGIN, TotemSaveFilePlugin))
+#define TOTEM_SAVE_FILE_PLUGIN_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), TOTEM_TYPE_SAVE_FILE_PLUGIN, TotemSaveFilePluginClass))
+#define TOTEM_IS_SAVE_FILE_PLUGIN(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), TOTEM_TYPE_SAVE_FILE_PLUGIN))
+#define TOTEM_IS_SAVE_FILE_PLUGIN_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), TOTEM_TYPE_SAVE_FILE_PLUGIN))
+#define TOTEM_SAVE_FILE_PLUGIN_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), TOTEM_TYPE_SAVE_FILE_PLUGIN, TotemSaveFilePluginClass))
+
+typedef struct {
+	TotemObject *totem;
+	GtkWidget   *bvw;
+
+	char        *mrl;
+	char        *name;
+	char        *save_uri;
+	gboolean     is_tmp;
+
+	GtkActionGroup *action_group;
+	guint ui_merge_id;
+} TotemSaveFilePluginPrivate;
+
+TOTEM_PLUGIN_REGISTER(TOTEM_TYPE_SAVE_FILE_PLUGIN, TotemSaveFilePlugin, totem_save_file_plugin)
+
+static void totem_save_file_plugin_copy (GtkAction *action,
+					 TotemSaveFilePlugin *pi);
+
+static GtkActionEntry totem_save_file_plugin_actions [] = {
+	{ "SaveFile", "save-as", N_("Save a Copy..."), NULL,
+		N_("Save a copy of the movie"),
+		G_CALLBACK (totem_save_file_plugin_copy) },
+};
+
+static void
+copy_uris_with_nautilus (const char *source,
+			 const char *src_name,
+			 const char *dest)
+{
+	GError *error = NULL;
+	GDBusProxyFlags flags;
+	GDBusProxy *proxy;
+	GFile *dest_file, *parent;
+	char *dest_name, *dest_dir;
+
+	g_return_if_fail (source != NULL);
+	g_return_if_fail (dest != NULL);
+	g_return_if_fail (src_name != NULL); /* Must be "" or something interesting, not NULL */
+
+	flags = G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES;
+	proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+					       flags,
+					       NULL, /* GDBusInterfaceInfo */
+					       "org.gnome.Nautilus",
+					       "/org/gnome/Nautilus",
+					       "org.gnome.Nautilus.FileOperations",
+					       NULL, /* GCancellable */
+					       &error);
+	if (proxy == NULL) {
+		g_warning ("Could not contact nautilus: %s", error->message);
+		g_error_free (error);
+		return;
+	}
+
+	dest_file = g_file_new_for_uri (dest);
+	dest_name = g_file_get_basename (dest_file);
+	parent = g_file_get_parent (dest_file);
+	g_object_unref (dest_file);
+	dest_dir = g_file_get_uri (parent);
+	g_object_unref (parent);
+
+	if (g_dbus_proxy_call_sync (proxy,
+				"CopyFile", g_variant_new ("(&s&s&s&s)", source, src_name, dest_dir, dest_name),
+				G_DBUS_CALL_FLAGS_NONE,
+				-1, NULL, &error) == FALSE) {
+		g_warning ("Could not get nautilus to copy file: %s", error->message);
+		g_error_free (error);
+	}
+
+	g_free (dest_dir);
+	g_free (dest_name);
+	g_object_unref (proxy);
+}
+
+static void
+totem_save_file_plugin_copy (GtkAction *action,
+			     TotemSaveFilePlugin *pi)
+{
+	GtkWidget *fs;
+	char *filename;
+	int response;
+
+	g_assert (pi->priv->mrl != NULL);
+
+
+	fs = gtk_file_chooser_dialog_new (_("Save a Copy"),
+					  totem_get_main_window (pi->priv->totem),
+					  GTK_FILE_CHOOSER_ACTION_SAVE,
+					  GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+					  GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+					  NULL);
+	gtk_dialog_set_default_response (GTK_DIALOG (fs), GTK_RESPONSE_ACCEPT);
+	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), FALSE);
+	gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fs), TRUE);
+
+	if (pi->priv->name != NULL) {
+		filename = g_strdup (pi->priv->name);
+	} else {
+		GFile *file;
+		char *basename;
+
+		/* Try to get a nice filename from the URI */
+		file = g_file_new_for_uri (pi->priv->mrl);
+		basename = g_file_get_basename (file);
+		g_object_unref (file);
+
+		if (g_utf8_validate (basename, -1, NULL) == FALSE) {
+			g_free (basename);
+			filename = NULL;
+		} else {
+			filename = basename;
+		}
+	}
+
+	if (filename == NULL) {
+		/* translators: Movie is the default saved movie filename,
+		 * without the suffix */
+		filename = g_strdup (_("Movie"));
+	}
+
+	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fs), filename);
+	g_free (filename);
+
+	if (pi->priv->save_uri != NULL) {
+		gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (fs),
+							 pi->priv->save_uri);
+	}
+
+	response = gtk_dialog_run (GTK_DIALOG (fs));
+	gtk_widget_hide (fs);
+
+	if (response == GTK_RESPONSE_ACCEPT) {
+		char *dest_uri;
+
+		dest_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fs));
+		/* translators: "Movie stream" is what will show up in the
+		 * nautilus copy dialogue as the source, when saving a streamed
+		 * movie */
+		copy_uris_with_nautilus (pi->priv->mrl,
+					 pi->priv->is_tmp ? _("Movie stream") : "",
+					 dest_uri);
+
+		g_free (pi->priv->save_uri);
+		pi->priv->save_uri = g_path_get_dirname (dest_uri);
+		g_free (dest_uri);
+	}
+	gtk_widget_destroy (fs);
+}
+
+static void
+totem_save_file_file_closed (TotemObject *totem,
+				 TotemSaveFilePlugin *pi)
+{
+	GtkAction *action;
+
+	g_free (pi->priv->mrl);
+	pi->priv->mrl = NULL;
+	g_free (pi->priv->name);
+	pi->priv->name = NULL;
+
+	action = gtk_action_group_get_action (pi->priv->action_group, "SaveFile");
+	gtk_action_set_sensitive (action, FALSE);
+}
+
+static void
+totem_save_file_file_opened (TotemObject *totem,
+			     const char *mrl,
+			     TotemSaveFilePlugin *pi)
+{
+	TotemSaveFilePluginPrivate *priv = pi->priv;
+	GtkAction *action;
+
+	g_assert (pi->priv->mrl == NULL);
+
+	if (mrl == NULL)
+		return;
+
+	if (g_str_has_prefix (mrl, "file:") || g_str_has_prefix (mrl, "smb:")) {
+		/* We can always copy files from file:/// URIs */
+		action = gtk_action_group_get_action (priv->action_group, "SaveFile");
+		gtk_action_set_sensitive (action, TRUE);
+		pi->priv->mrl = g_strdup (mrl);
+		pi->priv->name = totem_get_short_title (pi->priv->totem);
+		pi->priv->is_tmp = FALSE;
+	}
+}
+
+static void
+totem_save_file_download_filename (GObject    *gobject,
+				   GParamSpec *pspec,
+				   TotemSaveFilePlugin *pi)
+{
+	GtkAction *action;
+	char *filename;
+
+	/* We're already ready to copy it */
+	if (pi->priv->mrl != NULL)
+		return;
+
+	filename = NULL;
+	g_object_get (G_OBJECT (pi->priv->bvw), "download-filename", &filename, NULL);
+	if (filename == NULL)
+		return;
+
+	pi->priv->mrl = g_filename_to_uri (filename, NULL, NULL);
+	g_free (filename);
+	pi->priv->name = totem_get_short_title (pi->priv->totem);
+	pi->priv->is_tmp = TRUE;
+
+	action = gtk_action_group_get_action (pi->priv->action_group, "SaveFile");
+	gtk_action_set_sensitive (action, TRUE);
+}
+
+static void
+impl_activate (PeasActivatable *plugin)
+{
+	TotemSaveFilePlugin *pi = TOTEM_SAVE_FILE_PLUGIN (plugin);
+	TotemSaveFilePluginPrivate *priv = pi->priv;
+	GtkUIManager *uimanager = NULL;
+	GtkAction *action;
+	char *path;
+	char *mrl;
+
+	/* make sure nautilus is in the path */
+	path = g_find_program_in_path ("nautilus");
+	if (!path)
+		return;
+	g_free (path);
+
+	priv->totem = g_object_get_data (G_OBJECT (plugin), "object");
+	priv->bvw = totem_get_video_widget (priv->totem);
+
+	g_signal_connect (priv->totem,
+			  "file-opened",
+			  G_CALLBACK (totem_save_file_file_opened),
+			  plugin);
+	g_signal_connect (priv->totem,
+			  "file-closed",
+			  G_CALLBACK (totem_save_file_file_closed),
+			  plugin);
+	g_signal_connect (priv->bvw,
+			  "notify::download-filename",
+			  G_CALLBACK (totem_save_file_download_filename),
+			  plugin);
+
+	/* add UI */
+	priv->action_group = gtk_action_group_new ("SaveFileActions");
+	gtk_action_group_set_translation_domain (priv->action_group, GETTEXT_PACKAGE);
+	gtk_action_group_add_actions (priv->action_group,
+				      totem_save_file_plugin_actions,
+				      G_N_ELEMENTS (totem_save_file_plugin_actions),
+				      pi);
+
+	uimanager = totem_get_ui_manager (priv->totem);
+	gtk_ui_manager_insert_action_group (uimanager, priv->action_group, -1);
+	g_object_unref (priv->action_group);
+
+	priv->ui_merge_id = gtk_ui_manager_new_merge_id (uimanager);
+
+	gtk_ui_manager_add_ui (uimanager,
+			       priv->ui_merge_id,
+			       "/ui/tmw-menubar/movie/save-placeholder",
+			       "SaveFile",
+			       "SaveFile",
+			       GTK_UI_MANAGER_MENUITEM,
+			       TRUE);
+	gtk_ui_manager_add_ui (uimanager,
+			       priv->ui_merge_id,
+			       "/ui/totem-main-popup/save-placeholder",
+			       "SaveFile",
+			       "SaveFile",
+			       GTK_UI_MANAGER_MENUITEM,
+			       TRUE);
+
+	action = gtk_action_group_get_action (priv->action_group, "SaveFile");
+	gtk_action_set_sensitive (action, FALSE);
+
+	mrl = totem_get_current_mrl (priv->totem);
+	totem_save_file_file_opened (priv->totem, mrl, pi);
+	totem_save_file_download_filename (NULL, NULL, pi);
+	g_free (mrl);
+}
+
+static void
+impl_deactivate (PeasActivatable *plugin)
+{
+	TotemSaveFilePlugin *pi = TOTEM_SAVE_FILE_PLUGIN (plugin);
+	TotemSaveFilePluginPrivate *priv = pi->priv;
+	GtkUIManager *uimanager = NULL;
+
+	g_signal_handlers_disconnect_by_func (priv->totem, totem_save_file_file_opened, plugin);
+	g_signal_handlers_disconnect_by_func (priv->totem, totem_save_file_file_closed, plugin);
+	g_signal_handlers_disconnect_by_func (priv->bvw, totem_save_file_download_filename, plugin);
+
+	uimanager = totem_get_ui_manager (priv->totem);
+	gtk_ui_manager_remove_ui (uimanager, priv->ui_merge_id);
+	gtk_ui_manager_remove_action_group (uimanager, priv->action_group);
+
+	priv->totem = NULL;
+	priv->bvw = NULL;
+
+	g_free (priv->mrl);
+	priv->mrl = NULL;
+	g_free (priv->name);
+	priv->mrl = NULL;
+	g_free (priv->save_uri);
+	priv->save_uri = NULL;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]