Commit 0a684af8 authored by Simon Wenner's avatar Simon Wenner Committed by Bastien Nocera

plugins: add rotation plugin

A plugin to rotate videos.

https://bugzilla.gnome.org/show_bug.cgi?id=589399
parent 6ad79e0a
......@@ -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
......
......@@ -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
......
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
[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/
/*
* 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));
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment