Commit c2a944da authored by Jürg Billeter's avatar Jürg Billeter Committed by Jürg Billeter

src/nautilus-image-converter.c src/nautilus-image-rotator.c

2006-12-07  Juerg Billeter  <j@bitron.ch>

	* src/nautilus-image-converter.c
	* src/nautilus-image-rotator.c
	* src/nautilus-image-rotator.h
	* src/nautilus-image-rotate.glade
	* src/Makefile.am
	
	Support image rotation.
	
	* configure.ac: Bump version to 0.0.7

svn path=/trunk/; revision=13
parent 7ef0eba1
2006-12-07 Jürg Billeter <j@bitron.ch>
* src/nautilus-image-converter.c
* src/nautilus-image-rotator.c
* src/nautilus-image-rotator.h
* src/nautilus-image-rotate.glade
* src/Makefile.am
Support image rotation.
* configure.ac: Bump version to 0.0.7
2006-12-06 Jürg Billeter <j@bitron.ch>
* configure.ac: use new IT_PROG_INTLTOOL macro
......
AC_INIT([nautilus-image-converter], [0.0.6], [j@bitron.ch], [nautilus-image-converter])
AC_INIT([nautilus-image-converter], [0.0.7], [j@bitron.ch], [nautilus-image-converter])
AC_CONFIG_SRCDIR([Makefile.am])
AC_CONFIG_HEADERS(config.h)
AM_INIT_AUTOMAKE([dist-bzip2])
......
......@@ -22,6 +22,8 @@ libnautilus_image_converter_la_SOURCES = \
nautilus-image-converter.h \
nautilus-image-resizer.c \
nautilus-image-resizer.h \
nautilus-image-rotator.c \
nautilus-image-rotator.h \
$(NULL)
libnautilus_image_converter_la_LDFLAGS = -module -avoid-version
......@@ -29,4 +31,5 @@ libnautilus_image_converter_la_LIBADD = $(NAUTILUS_LIBS)
dist_pkgdata_DATA = \
nautilus-image-resize.glade \
nautilus-image-rotate.glade \
$(NULL)
......@@ -27,6 +27,7 @@
#include "nautilus-image-converter.h"
#include "nautilus-image-resizer.h"
#include "nautilus-image-rotator.h"
#include <libnautilus-extension/nautilus-menu-provider.h>
......@@ -86,6 +87,14 @@ image_resize_callback (NautilusMenuItem *item,
nautilus_image_resizer_show_dialog (resizer);
}
static void
image_rotate_callback (NautilusMenuItem *item,
GList *files)
{
NautilusImageRotator *rotator = nautilus_image_rotator_new (image_converter_filter_images (files));
nautilus_image_rotator_show_dialog (rotator);
}
static GList *
nautilus_image_converter_get_background_items (NautilusMenuProvider *provider,
GtkWidget *window,
......@@ -115,7 +124,7 @@ nautilus_image_converter_get_file_items (NautilusMenuProvider *provider,
items = g_list_prepend (items, item);
/*item = nautilus_menu_item_new ("NautilusImageConverter::rotate",
item = nautilus_menu_item_new ("NautilusImageConverter::rotate",
_("Ro_tate Images..."),
_("Rotate each selected image"),
"stock_rotate");
......@@ -123,7 +132,7 @@ nautilus_image_converter_get_file_items (NautilusMenuProvider *provider,
G_CALLBACK (image_rotate_callback),
nautilus_file_info_list_copy (files));
items = g_list_prepend (items, item);*/
items = g_list_prepend (items, item);
items = g_list_reverse (items);
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--*- mode: xml -*-->
<glade-interface>
<widget class="GtkDialog" id="rotate_dialog">
<property name="visible">True</property>
<property name="border_width">12</property>
<property name="title" translatable="yes">Rotate Images</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<widget class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="border_width">12</property>
<property name="spacing">12</property>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Image Rotation&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<widget class="GtkLabel" id="label5">
<property name="visible">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox2">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkHBox" id="hbox4">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkRadioButton" id="default_angle_radiobutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Select an angle:</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkComboBox" id="angle_combobox">
<property name="visible">True</property>
<property name="items" translatable="yes">90° clockwise
90° counter-clockwise
180°</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox8">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkRadioButton" id="custom_angle_radiobutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Custom angle:</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<property name="group">default_angle_radiobutton</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkSpinButton" id="angle_spinbutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="xalign">1</property>
<property name="adjustment">90 1 360 1 45 45</property>
<property name="climb_rate">1</property>
<property name="numeric">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label15">
<property name="visible">True</property>
<property name="label" translatable="yes">degrees clockwise</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;b&gt;Filename&lt;/b&gt;</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox6">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<widget class="GtkLabel" id="label12">
<property name="visible">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkHBox" id="hbox7">
<property name="visible">True</property>
<property name="spacing">6</property>
<child>
<widget class="GtkRadioButton" id="append_radiobutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Append</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="name_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text" translatable="yes">.rotated</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="label" translatable="yes">to file title</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkRadioButton" id="inplace_radiobutton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label" translatable="yes">Rotate in place</property>
<property name="use_underline">True</property>
<property name="draw_indicator">True</property>
<property name="group">append_radiobutton</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child internal-child="action_area">
<widget class="GtkHButtonBox" id="dialog-action_area1">
<property name="visible">True</property>
<property name="layout_style">GTK_BUTTONBOX_END</property>
<child>
<widget class="GtkButton" id="cancelbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">gtk-cancel</property>
<property name="use_stock">True</property>
<property name="response_id">-6</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="okbutton1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="label">_Rotate</property>
<property name="use_underline">True</property>
<property name="response_id">-5</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="pack_type">GTK_PACK_END</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>
/*
* nautilus-image-rotator.c
*
* Copyright (C) 2004-2006 Jürg Billeter
*
* This library 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 library 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 library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Author: Jürg Billeter <j@bitron.ch>
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h> /* for GETTEXT_PACKAGE */
#endif
#include "nautilus-image-rotator.h"
#include <string.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libnautilus-extension/nautilus-file-info.h>
#define PKGDATADIR DATADIR "/" PACKAGE
typedef struct _NautilusImageRotatorPrivate NautilusImageRotatorPrivate;
struct _NautilusImageRotatorPrivate {
GList *files;
gchar *suffix;
int images_rotated;
int images_total;
gboolean cancelled;
gchar *angle;
GtkDialog *rotate_dialog;
GtkRadioButton *default_angle_radiobutton;
GtkComboBox *angle_combobox;
GtkRadioButton *custom_angle_radiobutton;
GtkSpinButton *angle_spinbutton;
GtkRadioButton *append_radiobutton;
GtkEntry *name_entry;
GtkRadioButton *inplace_radiobutton;
GtkWidget *progress_dialog;
GtkWidget *progress_bar;
GtkWidget *progress_label;
};
#define NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NAUTILUS_TYPE_IMAGE_ROTATOR, NautilusImageRotatorPrivate))
G_DEFINE_TYPE (NautilusImageRotator, nautilus_image_rotator, G_TYPE_OBJECT)
enum {
PROP_FILES = 1,
};
typedef enum {
/* Place Signal Types Here */
SIGNAL_TYPE_EXAMPLE,
LAST_SIGNAL
} NautilusImageRotatorSignalType;
static void
nautilus_image_rotator_finalize(GObject *object)
{
NautilusImageRotator *dialog = NAUTILUS_IMAGE_ROTATOR (object);
NautilusImageRotatorPrivate *priv = NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE (dialog);
g_free (priv->suffix);
G_OBJECT_CLASS(nautilus_image_rotator_parent_class)->finalize(object);
}
static void
nautilus_image_rotator_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
NautilusImageRotator *dialog = NAUTILUS_IMAGE_ROTATOR (object);
NautilusImageRotatorPrivate *priv = NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE (dialog);
switch (property_id) {
case PROP_FILES:
priv->files = g_value_get_pointer (value);
priv->images_total = g_list_length (priv->files);
break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
break;
}
}
static void
nautilus_image_rotator_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
NautilusImageRotator *self = NAUTILUS_IMAGE_ROTATOR (object);
NautilusImageRotatorPrivate *priv = NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE (self);
switch (property_id) {
case PROP_FILES:
g_value_set_pointer (value, priv->files);
break;
default:
/* We don't have any other property... */
G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec);
break;
}
}
static void
nautilus_image_rotator_class_init(NautilusImageRotatorClass *klass)
{
g_type_class_add_private (klass, sizeof (NautilusImageRotatorPrivate));
GObjectClass *object_class = G_OBJECT_CLASS(klass);
GParamSpec *files_param_spec;
object_class->finalize = nautilus_image_rotator_finalize;
object_class->set_property = nautilus_image_rotator_set_property;
object_class->get_property = nautilus_image_rotator_get_property;
files_param_spec = g_param_spec_pointer ("files",
"Files",
"Set selected files",
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_FILES,
files_param_spec);
}
static void run_op (NautilusImageRotator *rotator);
static char *
nautilus_image_rotator_transform_uri (NautilusImageRotator *rotator, char *text_uri)
{
NautilusImageRotatorPrivate *priv = NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE (rotator);
GnomeVFSURI *uri;
GnomeVFSURI *parent;
GnomeVFSURI *new_uri;
char *escaped_basename;
char *basename;
char *extension;
char *new_basename;
char *new_text_uri;
uri = gnome_vfs_uri_new (text_uri);
parent = gnome_vfs_uri_get_parent (uri);
escaped_basename = gnome_vfs_uri_extract_short_path_name (uri);
basename = gnome_vfs_unescape_string (escaped_basename, "/");
g_free (escaped_basename);
gnome_vfs_uri_unref (uri);
extension = g_strdup (strrchr (basename, '.'));
if (extension != NULL)
basename[strlen (basename) - strlen (extension)] = '\0';
new_basename = g_strdup_printf ("%s%s%s", basename,
priv->suffix == NULL ? ".tmp" : priv->suffix,
extension == NULL ? "" : extension);
g_free (basename);
g_free (extension);
new_uri = gnome_vfs_uri_append_file_name (parent, new_basename);
gnome_vfs_uri_unref (parent);
g_free (new_basename);
new_text_uri = gnome_vfs_uri_to_string (new_uri, GNOME_VFS_URI_HIDE_NONE);
gnome_vfs_uri_unref (new_uri);
return new_text_uri;
}
static void
op_finished (GPid pid, gint status, gpointer data)
{
NautilusImageRotator *rotator = NAUTILUS_IMAGE_ROTATOR (data);
NautilusImageRotatorPrivate *priv = NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE (rotator);
gboolean retry = TRUE;
NautilusFileInfo *file = NAUTILUS_FILE_INFO (priv->files->data);
if (status != 0) {
/* rotating failed */
char *name = nautilus_file_info_get_name (file);
GtkWidget *msg_dialog = gtk_message_dialog_new (GTK_WINDOW (priv->progress_dialog),
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
GTK_BUTTONS_NONE,
"'%s' cannot be rotated. Check whether you have permission to write to this folder.",
name);
g_free (name);
gtk_dialog_add_button (GTK_DIALOG (msg_dialog), _("_Skip"), 1);
gtk_dialog_add_button (GTK_DIALOG (msg_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (msg_dialog), _("_Retry"), 0);
gtk_dialog_set_default_response (GTK_DIALOG (msg_dialog), 0);
int response_id = gtk_dialog_run (GTK_DIALOG (msg_dialog));
gtk_widget_destroy (msg_dialog);
if (response_id == 0) {
retry = TRUE;
} else if (response_id == GTK_RESPONSE_CANCEL) {
priv->cancelled = TRUE;
} else if (response_id == 1) {
retry = FALSE;
}
} else if (priv->suffix == NULL) {
/* rotate image in place */
char *uri = nautilus_file_info_get_uri (file);
char *new_uri = nautilus_image_rotator_transform_uri (rotator, uri);
gnome_vfs_move (new_uri, uri, TRUE);
}
if (status == 0 || !retry) {
/* image has been successfully rotated (or skipped) */
priv->images_rotated++;
priv->files = priv->files->next;
}
if (!priv->cancelled && priv->files != NULL) {
/* process next image */
run_op (rotator);
} else {
/* cancel/terminate operation */
gtk_widget_destroy (priv->progress_dialog);
}
}
static void
nautilus_image_rotator_cancel_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
{
NautilusImageRotator *rotator = NAUTILUS_IMAGE_ROTATOR (user_data);
NautilusImageRotatorPrivate *priv = NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE (rotator);
priv->cancelled = TRUE;
gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_CANCEL, FALSE);
}
static void
run_op (NautilusImageRotator *rotator)
{
NautilusImageRotatorPrivate *priv = NAUTILUS_IMAGE_ROTATOR_GET_PRIVATE (rotator);
g_return_if_fail (priv->files != NULL);
if (priv->progress_dialog == NULL) {
GtkWidget *vbox;
GtkWidget *label;
priv->progress_dialog = gtk_dialog_new ();
gtk_window_set_title (GTK_WINDOW (priv->progress_dialog), "Rotating files");
gtk_dialog_add_button (GTK_DIALOG (priv->progress_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
gtk_dialog_set_has_separator (GTK_DIALOG (priv->progress_dialog), FALSE);
g_signal_connect (priv->progress_dialog, "response", G_CALLBACK (nautilus_image_rotator_cancel_cb), rotator);
vbox = GTK_DIALOG (priv->progress_dialog)->vbox;