diff options
Diffstat (limited to 'image-converter')
-rw-r--r-- | image-converter/Makefile.am | 30 | ||||
-rw-r--r-- | image-converter/caja-image-converter.c | 201 | ||||
-rw-r--r-- | image-converter/caja-image-converter.h | 55 | ||||
-rw-r--r-- | image-converter/caja-image-resize.ui | 501 | ||||
-rw-r--r-- | image-converter/caja-image-resizer.c | 396 | ||||
-rw-r--r-- | image-converter/caja-image-resizer.h | 56 | ||||
-rw-r--r-- | image-converter/caja-image-rotate.ui | 309 | ||||
-rw-r--r-- | image-converter/caja-image-rotator.c | 406 | ||||
-rw-r--r-- | image-converter/caja-image-rotator.h | 56 | ||||
-rw-r--r-- | image-converter/image-converter.c | 64 |
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"><b>Image Size</b></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"><b>Filename</b></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° clockwise</col> + </row> + <row> + <col id="0">90° counter-clockwise</col> + </row> + <row> + <col id="0">180°</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"><b>Image Rotation</b></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"><b>Filename</b></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); +} |