[totem] plugins: add rotation plugin
- From: Bastien Nocera <hadess src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [totem] plugins: add rotation plugin
- Date: Wed, 3 Aug 2011 13:02:05 +0000 (UTC)
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]