[totem] plugins: add rotation plugin



commit 0a684af858b42a886a931ea484b32246a6916239
Author: Simon Wenner <simon wenner ch>
Date:   Sun Jul 31 15:31:38 2011 +0200

    plugins: add rotation plugin
    
    A plugin to rotate videos.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=589399

 configure.in                                    |   14 +-
 po/POTFILES.in                                  |    2 +
 src/plugins/rotation/Makefile.am                |   19 ++
 src/plugins/rotation/rotation.plugin.in         |    8 +
 src/plugins/rotation/totem-rotation-plugin.vala |  224 +++++++++++++++++++++++
 5 files changed, 264 insertions(+), 3 deletions(-)
---
diff --git a/configure.in b/configure.in
index a74759b..da8fcd4 100644
--- a/configure.in
+++ b/configure.in
@@ -47,7 +47,7 @@ GTK_REQS=2.99.3
 TOTEM_PLPARSER_REQS=2.32.4
 GNOMEICON_REQS=2.15.90
 DBUS_REQS=0.82
-VALA_REQS=0.11.1
+VALA_REQS=0.12.1
 PEAS_REQS=1.1.0
 PYTHON_REQS=2.3
 PYGOBJECT_REQS=2.27.0
@@ -68,7 +68,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 dbusservice im-status gromit iplayer lirc media-player-keys ontop opensubtitles properties publish pythonconsole save-file samplepython sample-vala screensaver screenshot sidebar-test skipto youtube zeitgeist-dp grilo"
+allowed_plugins="bemused brasero-disc-recorder chapters dbusservice im-status gromit iplayer lirc media-player-keys ontop opensubtitles properties publish pythonconsole rotation save-file samplepython sample-vala screensaver screenshot sidebar-test skipto youtube zeitgeist-dp grilo"
 
 PLUGINDIR='${libdir}/totem/plugins'
 AC_SUBST(PLUGINDIR)
@@ -80,6 +80,7 @@ GST_REQS=0.10.30
 GSTPLUG_REQS=0.10.30
 CLUTTER_REQS=1.6.8
 CLUTTER_GST_REQS=1.3.9
+CLUTTER_GTK_REQS=1.0.2
 
 HAVE_GSTREAMER=no
 
@@ -219,7 +220,7 @@ PKG_CHECK_MODULES([DEPENDENCY],[
   gmodule-2.0
   totem-plparser >= $TOTEM_PLPARSER_REQS
   gstreamer-tag-0.10 >= 0.10.26
-  clutter-gtk-1.0
+  clutter-gtk-1.0 >= $CLUTTER_GTK_REQS
   cairo])
 
 PKG_CHECK_MODULES(MM, $MM)
@@ -456,6 +457,12 @@ for plugin in ${used_plugins}; do
 				add_plugin="0"
 			fi
 		;;
+		rotation)
+			if test "${with_vala}" != "yes" ; then
+				plugin_error_or_ignore "you need vala installed to use the rotation plugin"
+				add_plugin="0"
+			fi
+		;;
 		sample-vala)
 			if test "${with_vala}" != "yes" ; then
 				plugin_error_or_ignore "you need vala installed to use the sample-vala plugin"
@@ -781,6 +788,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/rotation/Makefile
 src/plugins/save-file/Makefile
 src/plugins/sidebar-test/Makefile
 src/plugins/skipto/Makefile
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9a6ac0b..bcf7fc2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -70,6 +70,8 @@ src/plugins/publish/totem-publish.c
 [type: gettext/ini]src/plugins/publish/publish.plugin.in
 [type: gettext/glade]src/plugins/publish/publish-plugin.ui
 src/plugins/pythonconsole/org.gnome.totem.plugins.pythonconsole.gschema.xml.in.in
+[type: gettext/ini]src/plugins/rotation/rotation.plugin.in
+src/plugins/rotation/totem-rotation-plugin.vala
 src/plugins/save-file/totem-save-file.c
 [type: gettext/ini]src/plugins/screensaver/screensaver.plugin.in
 src/plugins/screensaver/totem-screensaver.c
diff --git a/src/plugins/rotation/Makefile.am b/src/plugins/rotation/Makefile.am
new file mode 100644
index 0000000..5317da3
--- /dev/null
+++ b/src/plugins/rotation/Makefile.am
@@ -0,0 +1,19 @@
+include $(top_srcdir)/src/plugins/Makefile.plugins
+
+plugindir = $(PLUGINDIR)/rotation
+plugin_LTLIBRARIES = librotation.la
+
+plugin_in_files = rotation.plugin.in
+
+# here we are explicitly specifying gtk+-3.0 and clutter-1.0 to use the vapi
+# because vala still cannot parse the gir
+VALAFLAGS = \
+	--girdir=$(top_srcdir)/src	\
+	--pkg Totem-1.0 --pkg Peas-1.0 --pkg GtkClutter-1.0 --pkg clutter-1.0 --pkg gtk+-3.0
+
+librotation_la_SOURCES = totem-rotation-plugin.vala
+librotation_la_LDFLAGS = $(plugin_ldflags)
+librotation_la_CFLAGS = $(plugin_cflags) -include $(CONFIG_HEADER)
+librotation_la_LIBADD = $(plugin_libadd)
+
+-include $(top_srcdir)/git.mk
diff --git a/src/plugins/rotation/rotation.plugin.in b/src/plugins/rotation/rotation.plugin.in
new file mode 100644
index 0000000..509129b
--- /dev/null
+++ b/src/plugins/rotation/rotation.plugin.in
@@ -0,0 +1,8 @@
+[Plugin]
+Module=rotation
+IAge=1
+_Name=Rotation Plugin
+_Description=Allows videos to be rotated if they're in the wrong orientation.
+Authors=Simon Wenner <simon wenner ch>
+Copyright=Copyright  2011 Simon Wenner
+Website=http://www.gnome.org/projects/totem/
diff --git a/src/plugins/rotation/totem-rotation-plugin.vala b/src/plugins/rotation/totem-rotation-plugin.vala
new file mode 100644
index 0000000..f20b3db
--- /dev/null
+++ b/src/plugins/rotation/totem-rotation-plugin.vala
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) Simon Wenner 2011 <simon wenner ch>
+ *
+ * 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
+ */
+
+using GLib;
+using Totem;
+using Peas;
+using Clutter;
+using GtkClutter;
+
+public const string GIO_ROTATION_FILE_ATTRIBUTE = "metadata::totem::rotation";
+
+class RotationPlugin: GLib.Object, Peas.Activatable
+{
+    private enum Rotation
+    {
+        _IDENTITY = 0,
+        _90R = 1,
+        _180 = 2,
+        _90L = 3
+    }
+    private const int STATE_COUNT = 4;
+    public weak GLib.Object object { get; construct; }
+    private weak Clutter.Actor video = null;
+    private uint ui_id;
+    private Gtk.ActionGroup action_group;
+    private Rotation state = Rotation._IDENTITY;
+    private float width;
+    private float height;
+
+    public void activate ()
+    {
+        Totem.Object t = (Totem.Object) this.object;
+        GtkClutter.Embed bvw = (GtkClutter.Embed) Totem.get_video_widget (t);
+        unowned Clutter.Stage stage = (Clutter.Stage) bvw.get_stage ();
+        string mrl = t.get_current_mrl ();
+
+        // add interface elements to control the rotation
+        unowned Gtk.UIManager ui_manager = t.get_ui_manager ();
+        this.ui_id = ui_manager.new_merge_id ();
+        ui_manager.add_ui (this.ui_id, "/ui/tmw-menubar/view/next-angle",
+                "rotate-left", "rotate-left", Gtk.UIManagerItemType.AUTO, false);
+        ui_manager.add_ui (this.ui_id, "/ui/tmw-menubar/view/next-angle",
+                "rotate-right", "rotate-right", Gtk.UIManagerItemType.AUTO, false);
+
+        var rotate_right  = new Gtk.Action ("rotate-right", _("_Rotate Clockwise"), null, null);
+        rotate_right.activate.connect (this.cb_rotate_right);
+        var rotate_left = new Gtk.Action ("rotate-left", _("Rotate Counterc_lockwise"), null, null);
+        rotate_left.activate.connect (this.cb_rotate_left);
+
+        this.action_group = new Gtk.ActionGroup ("RotationActions");
+        this.action_group.add_action_with_accel (rotate_right, "<ctrl>R");
+        this.action_group.add_action_with_accel (rotate_left, "<ctrl><shift>R");
+        if (mrl == null) {
+            this.action_group.sensitive = false;
+        }
+        ui_manager.insert_action_group (this.action_group, 0);
+
+        // search for the actor which contains the video
+        for (int i = 0; i < stage.get_n_children (); i++) {
+            Clutter.Actor actor = stage.get_nth_child (i);
+            if (actor.name == "frame") {
+                this.video = actor;
+                break;
+            }
+        }
+
+        if (video == null) {
+            GLib.critical ("Could not find the clutter actor 'frame'.");
+            return;
+        }
+
+        this.width = this.video.width;
+        this.height = this.video.height;
+
+        // read the state of the current video from the GIO attribute
+        if (mrl != null) {
+            this.try_restore_state (mrl);
+        }
+
+        // get notified if the video gets resized
+        this.video.allocation_changed.connect (this.cb_allocation_changed);
+
+        t.file_closed.connect (this.cb_file_closed);
+        t.file_opened.connect (this.cb_file_opened);
+    }
+
+    public void deactivate ()
+    {
+        Totem.Object t = (Totem.Object) this.object;
+
+        // disconnect callbacks
+        this.video.allocation_changed.disconnect (this.cb_allocation_changed);
+        t.file_closed.disconnect (this.cb_file_closed);
+        t.file_opened.disconnect (this.cb_file_opened);
+
+        // remove interface elements to control the rotation
+        unowned Gtk.UIManager ui_manager = t.get_ui_manager ();
+        ui_manager.remove_ui (this.ui_id);
+        ui_manager.remove_action_group (this.action_group);
+
+        // undo transformations
+        this.state = Rotation._IDENTITY;
+        this.video.set_rotation (Clutter.RotateAxis.Z_AXIS, 0, 0, 0, 0);
+        this.video.set_scale_full (1, 1, 0, 0);
+    }
+
+    public void update_state ()
+    {
+        this.update_video_geometry ();
+    }
+
+    private void update_video_geometry ()
+    {
+        float center_x = this.width * 0.5f;
+        float center_y = this.height * 0.5f;
+        double scale = 1.0;
+
+        // scale so that the larger side fits the smaller side
+        if (this.state % 2 == 1) { // _90R or _90L
+            if (this.width > this.height) {
+                scale = this.height / (double) this.width;
+            } else {
+                scale = this.width / (double) this.height;
+            }
+        }
+
+        this.video.set_rotation (Clutter.RotateAxis.Z_AXIS, 90.0 * this.state, center_x, center_y, 0);
+        this.video.set_scale_full (scale, scale, center_x, center_y);
+    }
+
+    private void cb_allocation_changed (Clutter.ActorBox box, Clutter.AllocationFlags flags)
+    {
+        this.width = box.x2 - box.x1;
+        this.height = box.y2 - box.y1;
+        this.update_video_geometry ();
+    }
+
+    private void cb_rotate_left ()
+    {
+        this.state = (this.state - 1) % STATE_COUNT;
+        this.update_video_geometry ();
+        this.store_state ();
+    }
+
+    private void cb_rotate_right ()
+    {
+        this.state = (this.state + 1) % STATE_COUNT;
+        this.update_video_geometry ();
+        this.store_state ();
+    }
+
+    private void cb_file_closed ()
+    {
+        // reset the rotation
+        this.state = Rotation._IDENTITY;
+        this.update_video_geometry ();
+        this.action_group.sensitive = false;
+    }
+
+    private void cb_file_opened (string mrl)
+    {
+        this.action_group.sensitive = true;
+        this.try_restore_state (mrl);
+    }
+
+    private void store_state ()
+    {
+        Totem.Object t = (Totem.Object) this.object;
+        string mrl = t.get_current_mrl ();
+
+        if (mrl == null) {
+            return;
+        }
+
+        var file = GLib.File.new_for_uri (mrl);
+        try {
+            string state_str = "";
+            if (this.state != Rotation._IDENTITY) {
+                state_str = "%u".printf ((uint) this.state);
+            }
+            file.set_attribute_string (GIO_ROTATION_FILE_ATTRIBUTE, state_str,
+                    GLib.FileQueryInfoFlags.NONE);
+        } catch (GLib.Error e) {
+            GLib.warning ("Could not store file attribute: %s", e.message);
+        }
+    }
+
+    private void try_restore_state (string mrl)
+    {
+        var file = GLib.File.new_for_uri (mrl);
+        try {
+            var file_info = file.query_info (GIO_ROTATION_FILE_ATTRIBUTE,
+                    GLib.FileQueryInfoFlags.NONE);
+            string state_str = file_info.get_attribute_string (GIO_ROTATION_FILE_ATTRIBUTE);
+            if (state_str != null) {
+                this.state = (Rotation) uint64.parse (state_str);
+                this.update_video_geometry ();
+            }
+        } catch (GLib.Error e) {
+            GLib.warning ("Could not query file attribute: %s", e.message);
+        }
+    }
+}
+
+[ModuleInit]
+public void peas_register_types (GLib.TypeModule module) {
+    var objmodule = module as Peas.ObjectModule;
+    objmodule.register_extension_type (typeof (Peas.Activatable), typeof (RotationPlugin));
+}



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