summaryrefslogtreecommitdiff
path: root/image-converter
diff options
context:
space:
mode:
authorStefano Karapetsas <[email protected]>2013-10-18 14:55:24 +0200
committerStefano Karapetsas <[email protected]>2013-10-18 14:55:24 +0200
commit1d56fce29aa71f3d6c1023e7a635b7d5264dbbf7 (patch)
tree6af67b82f29984ff4e41cb6f590f01d27577d06c /image-converter
parent92bafcb948929df5adad5ebcb70d383ae2c75158 (diff)
downloadcaja-extensions-1d56fce29aa71f3d6c1023e7a635b7d5264dbbf7.tar.bz2
caja-extensions-1d56fce29aa71f3d6c1023e7a635b7d5264dbbf7.tar.xz
Add image-converter extension
Diffstat (limited to 'image-converter')
-rw-r--r--image-converter/Makefile.am30
-rw-r--r--image-converter/caja-image-converter.c201
-rw-r--r--image-converter/caja-image-converter.h55
-rw-r--r--image-converter/caja-image-resize.ui501
-rw-r--r--image-converter/caja-image-resizer.c396
-rw-r--r--image-converter/caja-image-resizer.h56
-rw-r--r--image-converter/caja-image-rotate.ui309
-rw-r--r--image-converter/caja-image-rotator.c406
-rw-r--r--image-converter/caja-image-rotator.h56
-rw-r--r--image-converter/image-converter.c64
10 files changed, 2074 insertions, 0 deletions
diff --git a/image-converter/Makefile.am b/image-converter/Makefile.am
new file mode 100644
index 0000000..cc07b21
--- /dev/null
+++ b/image-converter/Makefile.am
@@ -0,0 +1,30 @@
+AM_CPPFLAGS = \
+ -DG_LOG_DOMAIN=\"Caja-Image-Converter\" \
+ -DDATADIR=\"$(datadir)\" \
+ -DMATELOCALEDIR=\""$(datadir)/locale"\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ $(CAJA_CFLAGS) \
+ $(IMAGE_CONVERTER_CFLAGS)
+
+caja_extensiondir=$(CAJA_EXTENSION_DIR)
+
+caja_extension_LTLIBRARIES=libcaja-image-converter.la
+
+libcaja_image_converter_la_SOURCES = \
+ image-converter.c \
+ caja-image-converter.c caja-image-converter.h \
+ caja-image-resizer.c caja-image-resizer.h \
+ caja-image-rotator.c caja-image-rotator.h
+
+libcaja_image_converter_la_LDFLAGS = -module -avoid-version
+libcaja_image_converter_la_LIBADD = $(CAJA_LIBS) $(IMAGE_CONVERTER_LIBS)
+
+builderdir = $(datadir)/caja-image-converter
+builder_DATA = \
+ caja-image-resize.ui \
+ caja-image-rotate.ui
+
+EXTRA_DIST = $(builder_DATA)
diff --git a/image-converter/caja-image-converter.c b/image-converter/caja-image-converter.c
new file mode 100644
index 0000000..8840258
--- /dev/null
+++ b/image-converter/caja-image-converter.c
@@ -0,0 +1,201 @@
+/*
+ * caja-image-converter.c
+ *
+ * Copyright (C) 2004-2005 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-converter.h"
+#include "caja-image-resizer.h"
+#include "caja-image-rotator.h"
+
+#include <libcaja-extension/caja-menu-provider.h>
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <string.h> /* for strcmp */
+
+static void caja_image_converter_instance_init (CajaImageConverter *img);
+static void caja_image_converter_class_init (CajaImageConverterClass *class);
+GList * caja_image_converter_get_file_items (CajaMenuProvider *provider,
+ GtkWidget *window,
+ GList *files);
+
+static GType image_converter_type = 0;
+
+static gboolean
+image_converter_file_is_image (CajaFileInfo *file_info)
+{
+ gchar *uri_scheme;
+ gchar *mime_type;
+ gboolean maybe_image;
+
+ maybe_image = TRUE;
+ uri_scheme = caja_file_info_get_uri_scheme (file_info);
+ if (strcmp (uri_scheme, "file") != 0)
+ maybe_image = FALSE;
+ g_free (uri_scheme);
+
+ mime_type = caja_file_info_get_mime_type (file_info);
+ if (strncmp (mime_type, "image/", 6) != 0)
+ maybe_image = FALSE;
+ g_free (mime_type);
+
+ return maybe_image;
+}
+
+static GList *
+image_converter_filter_images (GList *files)
+{
+ GList *images;
+ GList *file;
+
+ images = NULL;
+
+ for (file = files; file != NULL; file = file->next) {
+ if (image_converter_file_is_image (file->data))
+ images = g_list_prepend (images, file->data);
+ }
+
+ return images;
+}
+
+static void
+image_resize_callback (CajaMenuItem *item,
+ GList *files)
+{
+ CajaImageResizer *resizer = caja_image_resizer_new (image_converter_filter_images (files));
+ caja_image_resizer_show_dialog (resizer);
+}
+
+static void
+image_rotate_callback (CajaMenuItem *item,
+ GList *files)
+{
+ CajaImageRotator *rotator = caja_image_rotator_new (image_converter_filter_images (files));
+ caja_image_rotator_show_dialog (rotator);
+}
+
+static GList *
+caja_image_converter_get_background_items (CajaMenuProvider *provider,
+ GtkWidget *window,
+ CajaFileInfo *file_info)
+{
+ return NULL;
+}
+
+GList *
+caja_image_converter_get_file_items (CajaMenuProvider *provider,
+ GtkWidget *window,
+ GList *files)
+{
+ CajaMenuItem *item;
+ GList *file;
+ GList *items = NULL;
+
+ for (file = files; file != NULL; file = file->next) {
+ if (image_converter_file_is_image (file->data)) {
+ item = caja_menu_item_new ("CajaImageConverter::resize",
+ _("_Resize Images..."),
+ _("Resize each selected image"),
+ "stock_position-size");
+ g_signal_connect (item, "activate",
+ G_CALLBACK (image_resize_callback),
+ caja_file_info_list_copy (files));
+
+ items = g_list_prepend (items, item);
+
+ item = caja_menu_item_new ("CajaImageConverter::rotate",
+ _("Ro_tate Images..."),
+ _("Rotate each selected image"),
+ "stock_rotate");
+ g_signal_connect (item, "activate",
+ G_CALLBACK (image_rotate_callback),
+ caja_file_info_list_copy (files));
+
+ items = g_list_prepend (items, item);
+
+ items = g_list_reverse (items);
+
+ return items;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+caja_image_converter_menu_provider_iface_init (CajaMenuProviderIface *iface)
+{
+ iface->get_background_items = caja_image_converter_get_background_items;
+ iface->get_file_items = caja_image_converter_get_file_items;
+}
+
+static void
+caja_image_converter_instance_init (CajaImageConverter *img)
+{
+}
+
+static void
+caja_image_converter_class_init (CajaImageConverterClass *class)
+{
+}
+
+GType
+caja_image_converter_get_type (void)
+{
+ return image_converter_type;
+}
+
+void
+caja_image_converter_register_type (GTypeModule *module)
+{
+ static const GTypeInfo info = {
+ sizeof (CajaImageConverterClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) caja_image_converter_class_init,
+ NULL,
+ NULL,
+ sizeof (CajaImageConverter),
+ 0,
+ (GInstanceInitFunc) caja_image_converter_instance_init,
+ };
+
+ static const GInterfaceInfo menu_provider_iface_info = {
+ (GInterfaceInitFunc) caja_image_converter_menu_provider_iface_init,
+ NULL,
+ NULL
+ };
+
+ image_converter_type = g_type_module_register_type (module,
+ G_TYPE_OBJECT,
+ "CajaImageConverter",
+ &info, 0);
+
+ g_type_module_add_interface (module,
+ image_converter_type,
+ CAJA_TYPE_MENU_PROVIDER,
+ &menu_provider_iface_info);
+}
diff --git a/image-converter/caja-image-converter.h b/image-converter/caja-image-converter.h
new file mode 100644
index 0000000..09839c7
--- /dev/null
+++ b/image-converter/caja-image-converter.h
@@ -0,0 +1,55 @@
+/*
+ * caja-image-converter.h
+ *
+ * Copyright (C) 2004-2005 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]>
+ *
+ */
+
+#ifndef CAJA_IMAGE_CONVERTER_H
+#define CAJA_IMAGE_CONVERTER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* Declarations for the open terminal extension object. This object will be
+ * instantiated by caja. It implements the GInterfaces
+ * exported by libcaja. */
+
+
+#define CAJA_TYPE_IMAGE_CONVERTER (caja_image_converter_get_type ())
+#define CAJA_IMAGE_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CAJA_TYPE_IMAGE_CONVERTER, CajaImageConverter))
+#define CAJA_IS_IMAGE_CONVERTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CAJA_TYPE_IMAGE_CONVERTER))
+typedef struct _CajaImageConverter CajaImageConverter;
+typedef struct _CajaImageConverterClass CajaImageConverterClass;
+
+struct _CajaImageConverter {
+ GObject parent_slot;
+};
+
+struct _CajaImageConverterClass {
+ GObjectClass parent_slot;
+};
+
+GType caja_image_converter_get_type (void);
+void caja_image_converter_register_type (GTypeModule *module);
+
+G_END_DECLS
+
+#endif
diff --git a/image-converter/caja-image-resize.ui b/image-converter/caja-image-resize.ui
new file mode 100644
index 0000000..09a77fe
--- /dev/null
+++ b/image-converter/caja-image-resize.ui
@@ -0,0 +1,501 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="value">50</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment2">
+ <property name="lower">1</property>
+ <property name="upper">9999</property>
+ <property name="value">1000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkAdjustment" id="adjustment3">
+ <property name="lower">1</property>
+ <property name="upper">9999</property>
+ <property name="value">1000</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkListStore" id="model1">
+ <columns>
+ <!-- column-name gchararray -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">96x96</col>
+ </row>
+ <row>
+ <col id="0">128x128</col>
+ </row>
+ <row>
+ <col id="0">640x480</col>
+ </row>
+ <row>
+ <col id="0">800x600</col>
+ </row>
+ <row>
+ <col id="0">1024x768</col>
+ </row>
+ <row>
+ <col id="0">1280x960</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkDialog" id="resize_dialog">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="title" translatable="yes">Resize Images</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">&lt;b&gt;Image Size&lt;/b&gt;</property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkRadioButton" id="default_size_radiobutton">
+ <property name="label" translatable="yes">Select a size:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="comboboxtext_size">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="has_entry">True</property>
+ <property name="entry_text_column">0</property>
+ <items>
+ <item translatable="yes">96x96</item>
+ <item translatable="yes">128x128</item>
+ <item translatable="yes">640x480</item>
+ <item translatable="yes">800x600</item>
+ <item translatable="yes">1024x768</item>
+ <item translatable="yes">1280x960</item>
+ </items>
+ <child internal-child="entry">
+ <object class="GtkEntry" id="comboboxentry-entry">
+ <property name="can_focus">False</property>
+ <property name="editable">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">pixels</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkRadioButton" id="custom_pct_radiobutton">
+ <property name="label" translatable="yes">Scale:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">default_size_radiobutton</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="pct_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">adjustment1</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">percent</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkRadioButton" id="custom_size_radiobutton">
+ <property name="label" translatable="yes">Custom size:</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">default_size_radiobutton</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label10">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Width:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="width_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">adjustment2</property>
+ <property name="climb_rate">1</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label11">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Height:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="height_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">adjustment3</property>
+ <property name="climb_rate">1</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">pixels</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</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>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkRadioButton" id="append_radiobutton">
+ <property name="label" translatable="yes">Append</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="text" translatable="yes">.resized</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">to file title</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRadioButton" id="inplace_radiobutton">
+ <property name="label" translatable="yes">Resize in place</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">append_radiobutton</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="cancelbutton1">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="okbutton1">
+ <property name="label" translatable="yes">_Resize</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancelbutton1</action-widget>
+ <action-widget response="-5">okbutton1</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/image-converter/caja-image-resizer.c b/image-converter/caja-image-resizer.c
new file mode 100644
index 0000000..3409d91
--- /dev/null
+++ b/image-converter/caja-image-resizer.c
@@ -0,0 +1,396 @@
+/*
+ * caja-image-resizer.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-resizer.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 _CajaImageResizerPrivate CajaImageResizerPrivate;
+
+struct _CajaImageResizerPrivate {
+ GList *files;
+
+ gchar *suffix;
+
+ int images_resized;
+ int images_total;
+ gboolean cancelled;
+
+ gchar *size;
+
+ GtkDialog *resize_dialog;
+ GtkRadioButton *default_size_radiobutton;
+ GtkComboBoxText *size_combobox;
+ GtkRadioButton *custom_pct_radiobutton;
+ GtkSpinButton *pct_spinbutton;
+ GtkRadioButton *custom_size_radiobutton;
+ GtkSpinButton *width_spinbutton;
+ GtkSpinButton *height_spinbutton;
+ GtkRadioButton *append_radiobutton;
+ GtkEntry *name_entry;
+ GtkRadioButton *inplace_radiobutton;
+
+ GtkWidget *progress_dialog;
+ GtkWidget *progress_bar;
+ GtkWidget *progress_label;
+};
+
+#define CAJA_IMAGE_RESIZER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CAJA_TYPE_IMAGE_RESIZER, CajaImageResizerPrivate))
+
+G_DEFINE_TYPE (CajaImageResizer, caja_image_resizer, G_TYPE_OBJECT)
+
+enum {
+ PROP_FILES = 1,
+};
+
+typedef enum {
+ /* Place Signal Types Here */
+ SIGNAL_TYPE_EXAMPLE,
+ LAST_SIGNAL
+} CajaImageResizerSignalType;
+
+static void
+caja_image_resizer_finalize(GObject *object)
+{
+ CajaImageResizer *dialog = CAJA_IMAGE_RESIZER (object);
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_GET_PRIVATE (dialog);
+
+ g_free (priv->suffix);
+
+ G_OBJECT_CLASS(caja_image_resizer_parent_class)->finalize(object);
+}
+
+static void
+caja_image_resizer_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CajaImageResizer *dialog = CAJA_IMAGE_RESIZER (object);
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_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_resizer_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CajaImageResizer *self = CAJA_IMAGE_RESIZER (object);
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_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_resizer_class_init(CajaImageResizerClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (CajaImageResizerPrivate));
+
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ GParamSpec *files_param_spec;
+
+ object_class->finalize = caja_image_resizer_finalize;
+ object_class->set_property = caja_image_resizer_set_property;
+ object_class->get_property = caja_image_resizer_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 (CajaImageResizer *resizer);
+
+static GFile *
+caja_image_resizer_transform_filename (CajaImageResizer *resizer, GFile *orig_file)
+{
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_GET_PRIVATE (resizer);
+
+ 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)
+{
+ CajaImageResizer *resizer = CAJA_IMAGE_RESIZER (data);
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_GET_PRIVATE (resizer);
+
+ gboolean retry = TRUE;
+
+ CajaFileInfo *file = CAJA_FILE_INFO (priv->files->data);
+
+ if (status != 0) {
+ /* resizing 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 resized. 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) {
+ /* resize image in place */
+ GFile *orig_location = caja_file_info_get_location (file);
+ GFile *new_location = caja_image_resizer_transform_filename (resizer, 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 resized (or skipped) */
+ priv->images_resized++;
+ priv->files = priv->files->next;
+ }
+
+ if (!priv->cancelled && priv->files != NULL) {
+ /* process next image */
+ run_op (resizer);
+ } else {
+ /* cancel/terminate operation */
+ gtk_widget_destroy (priv->progress_dialog);
+ }
+}
+
+static void
+run_op (CajaImageResizer *resizer)
+{
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_GET_PRIVATE (resizer);
+
+ 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_resizer_transform_filename (resizer, 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[6];
+ argv[0] = "/usr/bin/convert";
+ argv[1] = filename;
+ argv[2] = "-resize";
+ argv[3] = priv->size;
+ argv[4] = new_filename;
+ argv[5] = 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, resizer);
+
+ char *tmp;
+
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progress_bar), (double) (priv->images_resized + 1) / priv->images_total);
+ tmp = g_strdup_printf (_("Resizing image: %d of %d"), priv->images_resized + 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>Resizing \"%s\"</i>"), name);
+ g_free (name);
+ gtk_label_set_markup (GTK_LABEL (priv->progress_label), tmp);
+ g_free (tmp);
+
+}
+
+static void
+caja_image_resizer_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+ CajaImageResizer *resizer = CAJA_IMAGE_RESIZER (user_data);
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_GET_PRIVATE (resizer);
+
+ 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_size_radiobutton))) {
+ priv->size = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (priv->size_combobox));
+ } else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->custom_pct_radiobutton))) {
+ priv->size = g_strdup_printf ("%d%%", (int) gtk_spin_button_get_value (priv->pct_spinbutton));
+ } else {
+ priv->size = g_strdup_printf ("%dx%d", (int) gtk_spin_button_get_value (priv->width_spinbutton), (int) gtk_spin_button_get_value (priv->height_spinbutton));
+ }
+
+ run_op (resizer);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+caja_image_resizer_init(CajaImageResizer *resizer)
+{
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_GET_PRIVATE (resizer);
+
+ 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-resize.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->resize_dialog = GTK_DIALOG (gtk_builder_get_object (ui, "resize_dialog"));
+ priv->default_size_radiobutton =
+ GTK_RADIO_BUTTON (gtk_builder_get_object (ui, "default_size_radiobutton"));
+ priv->size_combobox = GTK_COMBO_BOX_TEXT (gtk_builder_get_object (ui, "comboboxtext_size"));
+ priv->custom_pct_radiobutton =
+ GTK_RADIO_BUTTON (gtk_builder_get_object (ui, "custom_pct_radiobutton"));
+ priv->pct_spinbutton = GTK_SPIN_BUTTON (gtk_builder_get_object (ui, "pct_spinbutton"));
+ priv->custom_size_radiobutton =
+ GTK_RADIO_BUTTON (gtk_builder_get_object (ui, "custom_size_radiobutton"));
+ priv->width_spinbutton = GTK_SPIN_BUTTON (gtk_builder_get_object (ui, "width_spinbutton"));
+ priv->height_spinbutton = GTK_SPIN_BUTTON (gtk_builder_get_object (ui, "height_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 item in combo box */
+ /* gtk_combo_box_set_active (priv->size_combobox, 4); 1024x768 */
+
+ /* Connect signal */
+ g_signal_connect (G_OBJECT (priv->resize_dialog), "response",
+ (GCallback) caja_image_resizer_response_cb,
+ resizer);
+}
+
+CajaImageResizer *
+caja_image_resizer_new (GList *files)
+{
+ return g_object_new (CAJA_TYPE_IMAGE_RESIZER, "files", files, NULL);
+}
+
+void
+caja_image_resizer_show_dialog (CajaImageResizer *resizer)
+{
+ CajaImageResizerPrivate *priv = CAJA_IMAGE_RESIZER_GET_PRIVATE (resizer);
+
+ gtk_widget_show (GTK_WIDGET (priv->resize_dialog));
+}
diff --git a/image-converter/caja-image-resizer.h b/image-converter/caja-image-resizer.h
new file mode 100644
index 0000000..177165b
--- /dev/null
+++ b/image-converter/caja-image-resizer.h
@@ -0,0 +1,56 @@
+/*
+ * caja-image-resize-dialog.h
+ *
+ * Copyright (C) 2004-2005 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]>
+ *
+ */
+
+#ifndef CAJA_IMAGE_RESIZER_H
+#define CAJA_IMAGE_RESIZER_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CAJA_TYPE_IMAGE_RESIZER (caja_image_resizer_get_type ())
+#define CAJA_IMAGE_RESIZER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CAJA_TYPE_IMAGE_RESIZER, CajaImageResizer))
+#define CAJA_IMAGE_RESIZER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CAJA_TYPE_IMAGE_RESIZER, CajaImageResizerClass))
+#define CAJA_IS_IMAGE_RESIZER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CAJA_TYPE_IMAGE_RESIZER))
+#define CAJA_IS_IMAGE_RESIZER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CAJA_TYPE_IMAGE_RESIZER))
+#define CAJA_IMAGE_RESIZER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CAJA_TYPE_IMAGE_RESIZER, CajaImageResizerClass))
+
+typedef struct _CajaImageResizer CajaImageResizer;
+typedef struct _CajaImageResizerClass CajaImageResizerClass;
+
+struct _CajaImageResizer {
+ GObject parent;
+};
+
+struct _CajaImageResizerClass {
+ GObjectClass parent_class;
+ /* Add Signal Functions Here */
+};
+
+GType caja_image_resizer_get_type (void);
+CajaImageResizer *caja_image_resizer_new (GList *files);
+void caja_image_resizer_show_dialog (CajaImageResizer *dialog);
+
+G_END_DECLS
+
+#endif /* CAJA_IMAGE_RESIZER_H */
diff --git a/image-converter/caja-image-rotate.ui b/image-converter/caja-image-rotate.ui
new file mode 100644
index 0000000..c40ba9a
--- /dev/null
+++ b/image-converter/caja-image-rotate.ui
@@ -0,0 +1,309 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+ <object class="GtkAdjustment" id="adjustment1">
+ <property name="upper">360</property>
+ <property name="lower">1</property>
+ <property name="page_increment">45</property>
+ <property name="step_increment">1</property>
+ <property name="page_size">45</property>
+ <property name="value">90</property>
+ </object>
+ <object class="GtkListStore" id="model1">
+ <columns>
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">90&#xB0; clockwise</col>
+ </row>
+ <row>
+ <col id="0">90&#xB0; counter-clockwise</col>
+ </row>
+ <row>
+ <col id="0">180&#xB0;</col>
+ </row>
+ </data>
+ </object>
+ <object 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">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="border_width">12</property>
+ <property name="spacing">12</property>
+ <child>
+ <object 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>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox4">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object 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>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="angle_combobox">
+ <property name="visible">True</property>
+ <property name="model">model1</property>
+ <child>
+ <object class="GtkCellRendererText" id="renderer1"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox8">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object 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>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="angle_spinbutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="xalign">1</property>
+ <property name="adjustment">adjustment1</property>
+ <property name="climb_rate">1</property>
+ <property name="numeric">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">degrees clockwise</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object 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>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox6">
+ <property name="visible">True</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox3">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox7">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object 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>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="name_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="text" translatable="yes">.rotated</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">to file title</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object 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>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">GTK_BUTTONBOX_END</property>
+ <child>
+ <object 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>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="okbutton1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="label" translatable="yes">_Rotate</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">GTK_PACK_END</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">cancelbutton1</action-widget>
+ <action-widget response="-5">okbutton1</action-widget>
+ </action-widgets>
+ </object>
+</interface>
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));
+}
diff --git a/image-converter/caja-image-rotator.h b/image-converter/caja-image-rotator.h
new file mode 100644
index 0000000..81ea848
--- /dev/null
+++ b/image-converter/caja-image-rotator.h
@@ -0,0 +1,56 @@
+/*
+ * caja-image-rotator.h
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Author: Jürg Billeter <[email protected]>
+ *
+ */
+
+#ifndef CAJA_IMAGE_ROTATOR_H
+#define CAJA_IMAGE_ROTATOR_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CAJA_TYPE_IMAGE_ROTATOR (caja_image_rotator_get_type ())
+#define CAJA_IMAGE_ROTATOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CAJA_TYPE_IMAGE_ROTATOR, CajaImageRotator))
+#define CAJA_IMAGE_ROTATOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CAJA_TYPE_IMAGE_ROTATOR, CajaImageRotatorClass))
+#define CAJA_IS_IMAGE_ROTATOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CAJA_TYPE_IMAGE_ROTATOR))
+#define CAJA_IS_IMAGE_ROTATOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CAJA_TYPE_IMAGE_ROTATOR))
+#define CAJA_IMAGE_ROTATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CAJA_TYPE_IMAGE_ROTATOR, CajaImageRotatorClass))
+
+typedef struct _CajaImageRotator CajaImageRotator;
+typedef struct _CajaImageRotatorClass CajaImageRotatorClass;
+
+struct _CajaImageRotator {
+ GObject parent;
+};
+
+struct _CajaImageRotatorClass {
+ GObjectClass parent_class;
+ /* Add Signal Functions Here */
+};
+
+GType caja_image_rotator_get_type (void);
+CajaImageRotator *caja_image_rotator_new (GList *files);
+void caja_image_rotator_show_dialog (CajaImageRotator *dialog);
+
+G_END_DECLS
+
+#endif /* CAJA_IMAGE_ROTATOR_H */
diff --git a/image-converter/image-converter.c b/image-converter/image-converter.c
new file mode 100644
index 0000000..63749d9
--- /dev/null
+++ b/image-converter/image-converter.c
@@ -0,0 +1,64 @@
+/*
+ * image-converter.c
+ *
+ * Copyright (C) 2004-2005 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>
+#endif
+
+#include "caja-image-converter.h"
+
+#include <libintl.h>
+
+static GType type_list[1];
+
+void caja_module_initialize (GTypeModule *module);
+void caja_module_shutdown (void);
+void caja_module_list_types (const GType **types,
+ int *num_types);
+
+
+void
+caja_module_initialize (GTypeModule *module)
+{
+ g_print ("Initializing caja-image-converter extension\n");
+
+ caja_image_converter_register_type (module);
+ type_list[0] = CAJA_TYPE_IMAGE_CONVERTER;
+
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+}
+
+void
+caja_module_shutdown (void)
+{
+ g_print ("Shutting down caja-image-converter extension\n");
+}
+
+void
+caja_module_list_types (const GType **types,
+ int *num_types)
+{
+ *types = type_list;
+ *num_types = G_N_ELEMENTS (type_list);
+}