diff options
Diffstat (limited to 'image-converter/caja-image-rotator.c')
-rw-r--r-- | image-converter/caja-image-rotator.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/image-converter/caja-image-rotator.c b/image-converter/caja-image-rotator.c new file mode 100644 index 0000000..32c3d09 --- /dev/null +++ b/image-converter/caja-image-rotator.c @@ -0,0 +1,406 @@ +/* + * caja-image-rotator.c + * + * Copyright (C) 2004-2008 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Author: Jürg Billeter <[email protected]> + * + */ + +#ifdef HAVE_CONFIG_H + #include <config.h> /* for GETTEXT_PACKAGE */ +#endif + +#include "caja-image-rotator.h" + +#include <string.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> + +#include <libcaja-extension/caja-file-info.h> + +typedef struct _CajaImageRotatorPrivate CajaImageRotatorPrivate; + +struct _CajaImageRotatorPrivate { + 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 CAJA_IMAGE_ROTATOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CAJA_TYPE_IMAGE_ROTATOR, CajaImageRotatorPrivate)) + +G_DEFINE_TYPE (CajaImageRotator, caja_image_rotator, G_TYPE_OBJECT) + +enum { + PROP_FILES = 1, +}; + +typedef enum { + /* Place Signal Types Here */ + SIGNAL_TYPE_EXAMPLE, + LAST_SIGNAL +} CajaImageRotatorSignalType; + +static void +caja_image_rotator_finalize(GObject *object) +{ + CajaImageRotator *dialog = CAJA_IMAGE_ROTATOR (object); + CajaImageRotatorPrivate *priv = CAJA_IMAGE_ROTATOR_GET_PRIVATE (dialog); + + g_free (priv->suffix); + + G_OBJECT_CLASS(caja_image_rotator_parent_class)->finalize(object); +} + +static void +caja_image_rotator_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + CajaImageRotator *dialog = CAJA_IMAGE_ROTATOR (object); + CajaImageRotatorPrivate *priv = CAJA_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 +caja_image_rotator_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + CajaImageRotator *self = CAJA_IMAGE_ROTATOR (object); + CajaImageRotatorPrivate *priv = CAJA_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 +caja_image_rotator_class_init(CajaImageRotatorClass *klass) +{ + g_type_class_add_private (klass, sizeof (CajaImageRotatorPrivate)); + + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *files_param_spec; + + object_class->finalize = caja_image_rotator_finalize; + object_class->set_property = caja_image_rotator_set_property; + object_class->get_property = caja_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 (CajaImageRotator *rotator); + +static GFile * +caja_image_rotator_transform_filename (CajaImageRotator *rotator, GFile *orig_file) +{ + CajaImageRotatorPrivate *priv = CAJA_IMAGE_ROTATOR_GET_PRIVATE (rotator); + + GFile *parent_file, *new_file; + char *basename, *extension, *new_basename; + + g_return_val_if_fail (G_IS_FILE (orig_file), NULL); + + parent_file = g_file_get_parent (orig_file); + + basename = g_strdup (g_file_get_basename (orig_file)); + + 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_file = g_file_get_child (parent_file, new_basename); + + g_object_unref (parent_file); + g_free (new_basename); + + return new_file; +} + +static void +op_finished (GPid pid, gint status, gpointer data) +{ + CajaImageRotator *rotator = CAJA_IMAGE_ROTATOR (data); + CajaImageRotatorPrivate *priv = CAJA_IMAGE_ROTATOR_GET_PRIVATE (rotator); + + gboolean retry = TRUE; + + CajaFileInfo *file = CAJA_FILE_INFO (priv->files->data); + + if (status != 0) { + /* rotating failed */ + char *name = caja_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 */ + GFile *orig_location = caja_file_info_get_location (file); + GFile *new_location = caja_image_rotator_transform_filename (rotator, orig_location); + g_file_move (new_location, orig_location, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, NULL); + g_object_unref (orig_location); + g_object_unref (new_location); + } + + 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 +run_op (CajaImageRotator *rotator) +{ + CajaImageRotatorPrivate *priv = CAJA_IMAGE_ROTATOR_GET_PRIVATE (rotator); + + g_return_if_fail (priv->files != NULL); + + CajaFileInfo *file = CAJA_FILE_INFO (priv->files->data); + + GFile *orig_location = caja_file_info_get_location (file); + char *filename = g_file_get_path (orig_location); + GFile *new_location = caja_image_rotator_transform_filename (rotator, orig_location); + char *new_filename = g_file_get_path (new_location); + g_object_unref (orig_location); + g_object_unref (new_location); + + /* FIXME: check whether new_uri already exists and provide "Replace _All", "_Skip", and "_Replace" options */ + + gchar *argv[8]; + argv[0] = "/usr/bin/convert"; + argv[1] = filename; + argv[2] = "-rotate"; + argv[3] = priv->angle; + argv[4] = "-orient"; + argv[5] = "TopLeft"; + argv[6] = new_filename; + argv[7] = NULL; + + pid_t pid; + + if (!g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL)) { + // FIXME: error handling + return; + } + + g_free (filename); + g_free (new_filename); + + g_child_watch_add (pid, op_finished, rotator); + + char *tmp; + + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progress_bar), (double) (priv->images_rotated + 1) / priv->images_total); + tmp = g_strdup_printf (_("Rotating image: %d of %d"), priv->images_rotated + 1, priv->images_total); + gtk_progress_bar_set_text (GTK_PROGRESS_BAR (priv->progress_bar), tmp); + g_free (tmp); + + char *name = caja_file_info_get_name (file); + tmp = g_strdup_printf (_("<i>Rotating \"%s\"</i>"), name); + g_free (name); + gtk_label_set_markup (GTK_LABEL (priv->progress_label), tmp); + g_free (tmp); + +} + +static void +caja_image_rotator_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) +{ + CajaImageRotator *rotator = CAJA_IMAGE_ROTATOR (user_data); + CajaImageRotatorPrivate *priv = CAJA_IMAGE_ROTATOR_GET_PRIVATE (rotator); + + if (response_id == GTK_RESPONSE_OK) { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->append_radiobutton))) { + if (strlen (gtk_entry_get_text (priv->name_entry)) == 0) { + GtkWidget *msg_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, _("Please enter a valid filename suffix!")); + gtk_dialog_run (GTK_DIALOG (msg_dialog)); + gtk_widget_destroy (msg_dialog); + return; + } + priv->suffix = g_strdup (gtk_entry_get_text (priv->name_entry)); + } + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->default_angle_radiobutton))) { + switch (gtk_combo_box_get_active (GTK_COMBO_BOX (priv->angle_combobox))) { + case 0: + priv->angle = g_strdup_printf ("90"); + break; + case 1: + priv->angle = g_strdup_printf ("-90"); + break; + case 2: + priv->angle = g_strdup_printf ("180"); + break; + default: + g_assert_not_reached (); + } + } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->custom_angle_radiobutton))) { + priv->angle = g_strdup_printf ("%d", (int) gtk_spin_button_get_value (priv->angle_spinbutton)); + } else { + g_assert_not_reached (); + } + + run_op (rotator); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +caja_image_rotator_init(CajaImageRotator *rotator) +{ + CajaImageRotatorPrivate *priv = CAJA_IMAGE_ROTATOR_GET_PRIVATE (rotator); + + GtkBuilder *ui; + gchar *path; + guint result; + GError *err = NULL; + + /* Let's create our gtkbuilder and load the xml file */ + ui = gtk_builder_new (); + gtk_builder_set_translation_domain (ui, GETTEXT_PACKAGE); + path = g_build_filename (DATADIR, PACKAGE, "caja-image-rotate.ui", NULL); + result = gtk_builder_add_from_file (ui, path, &err); + g_free (path); + + /* If we're unable to load the xml file */ + if (result == 0) { + g_warning ("%s", err->message); + g_error_free (err); + return; + } + + /* Grab some widgets */ + priv->rotate_dialog = GTK_DIALOG (gtk_builder_get_object (ui, "rotate_dialog")); + priv->default_angle_radiobutton = + GTK_RADIO_BUTTON (gtk_builder_get_object (ui, "default_angle_radiobutton")); + priv->angle_combobox = GTK_COMBO_BOX (gtk_builder_get_object (ui, "angle_combobox")); + priv->custom_angle_radiobutton = + GTK_RADIO_BUTTON (gtk_builder_get_object (ui, "custom_angle_radiobutton")); + priv->angle_spinbutton = + GTK_SPIN_BUTTON (gtk_builder_get_object (ui, "angle_spinbutton")); + priv->append_radiobutton = + GTK_RADIO_BUTTON (gtk_builder_get_object (ui, "append_radiobutton")); + priv->name_entry = GTK_ENTRY (gtk_builder_get_object (ui, "name_entry")); + priv->inplace_radiobutton = + GTK_RADIO_BUTTON (gtk_builder_get_object (ui, "inplace_radiobutton")); + + /* Set default value for combobox */ + gtk_combo_box_set_active (priv->angle_combobox, 0); /* 90° clockwise */ + + /* Connect the signal */ + g_signal_connect (G_OBJECT (priv->rotate_dialog), "response", + (GCallback) caja_image_rotator_response_cb, + rotator); +} + +CajaImageRotator * +caja_image_rotator_new (GList *files) +{ + return g_object_new (CAJA_TYPE_IMAGE_ROTATOR, "files", files, NULL); +} + +void +caja_image_rotator_show_dialog (CajaImageRotator *rotator) +{ + CajaImageRotatorPrivate *priv = CAJA_IMAGE_ROTATOR_GET_PRIVATE (rotator); + + gtk_widget_show (GTK_WIDGET (priv->rotate_dialog)); +} |