[gthumb] rename_series: added a dialog to edit the template



commit 74669730259b9a514b0f789fba1c6b772fb67c7a
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Apr 3 17:53:46 2011 +0200

    rename_series: added a dialog to edit the template
    
    The dialog allows to user to edit the template without the
    need to remember the available special codes.  The user
    can choose the code, the date format if the code specifies
    a date field or an attribute if the code specifies a file
    attributes.
    
    [new feature]

 extensions/rename_series/Makefile.am               |    4 +
 extensions/rename_series/data/ui/Makefile.am       |    2 +-
 extensions/rename_series/data/ui/code-selector.ui  |  265 ++++++++++
 extensions/rename_series/data/ui/rename-series.ui  |   85 +++-
 extensions/rename_series/dlg-rename-series.c       |   64 +++
 .../rename_series/gth-template-editor-dialog.c     |  326 ++++++++++++
 .../rename_series/gth-template-editor-dialog.h     |   62 +++
 extensions/rename_series/gth-template-selector.c   |  540 ++++++++++++++++++++
 extensions/rename_series/gth-template-selector.h   |   78 +++
 gthumb/glib-utils.c                                |    2 +-
 gthumb/glib-utils.h                                |    2 +
 11 files changed, 1404 insertions(+), 26 deletions(-)
---
diff --git a/extensions/rename_series/Makefile.am b/extensions/rename_series/Makefile.am
index 21db13e..e09ad62 100644
--- a/extensions/rename_series/Makefile.am
+++ b/extensions/rename_series/Makefile.am
@@ -12,6 +12,10 @@ librename_series_la_SOURCES = 		\
 	dlg-rename-series.h		\
 	gth-rename-task.c		\
 	gth-rename-task.h		\
+	gth-template-editor-dialog.c	\
+	gth-template-editor-dialog.h	\
+	gth-template-selector.c		\
+	gth-template-selector.h		\
 	main.c				\
 	preferences.h
 
diff --git a/extensions/rename_series/data/ui/Makefile.am b/extensions/rename_series/data/ui/Makefile.am
index b8017c8..a4c4e41 100644
--- a/extensions/rename_series/data/ui/Makefile.am
+++ b/extensions/rename_series/data/ui/Makefile.am
@@ -1,5 +1,5 @@
 uidir = $(pkgdatadir)/ui
-ui_DATA = rename-series.ui
+ui_DATA = code-selector.ui rename-series.ui
 EXTRA_DIST = $(ui_DATA)
 
 -include $(top_srcdir)/git.mk
diff --git a/extensions/rename_series/data/ui/code-selector.ui b/extensions/rename_series/data/ui/code-selector.ui
new file mode 100644
index 0000000..a94ded8
--- /dev/null
+++ b/extensions/rename_series/data/ui/code-selector.ui
@@ -0,0 +1,265 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkHBox" id="code_selector">
+    <property name="visible">True</property>
+    <property name="border_width">2</property>
+    <property name="spacing">6</property>
+    <child>
+      <object class="GtkComboBox" id="type_combobox">
+        <property name="visible">True</property>
+        <property name="model">type_liststore</property>
+        <child>
+          <object class="GtkCellRendererText" id="cellrenderertext1"/>
+          <attributes>
+            <attribute name="text">1</attribute>
+          </attributes>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkNotebook" id="type_notebook">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="show_tabs">False</property>
+        <property name="show_border">False</property>
+        <property name="homogeneous">True</property>
+        <child>
+          <object class="GtkEntry" id="text_entry">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">&#x25CF;</property>
+          </object>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="hbox3">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkSpinButton" id="enumerator_digits_spinbutton">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+                <property name="adjustment">enumerator_digits_adjustment</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">digits</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label2">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="position">1</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label3">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="position">2</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="hbox4">
+            <property name="visible">True</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="label5">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">format:</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="date_format_combobox">
+                <property name="visible">True</property>
+                <property name="model">date_format_liststore</property>
+                <child>
+                  <object class="GtkCellRendererText" id="cellrenderertext2"/>
+                  <attributes>
+                    <attribute name="text">1</attribute>
+                  </attributes>
+                </child>
+              </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="custom_date_format_entry">
+                <property name="can_focus">True</property>
+                <property name="invisible_char">&#x25CF;</property>
+              </object>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">3</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label6">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="position">3</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkComboBox" id="attribute_combobox">
+            <property name="visible">True</property>
+            <property name="model">attribute_liststore</property>
+            <child>
+              <object class="GtkCellRendererText" id="cellrenderertext3"/>
+              <attributes>
+                <attribute name="text">1</attribute>
+              </attributes>
+            </child>
+          </object>
+          <packing>
+            <property name="position">4</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label7">
+            <property name="visible">True</property>
+          </object>
+          <packing>
+            <property name="position">4</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHBox" id="hbox2">
+        <property name="visible">True</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkButton" id="remove_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">remove</property>
+            <child>
+              <object class="GtkImage" id="image2">
+                <property name="visible">True</property>
+                <property name="stock">gtk-remove</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="add_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="tooltip_text" translatable="yes">add</property>
+            <child>
+              <object class="GtkImage" id="image1">
+                <property name="visible">True</property>
+                <property name="stock">gtk-add</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="position">2</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkListStore" id="type_liststore">
+    <columns>
+      <!-- column-name data -->
+      <column type="gpointer"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="date_format_liststore">
+    <columns>
+      <!-- column-name format -->
+      <column type="gchararray"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkAdjustment" id="enumerator_digits_adjustment">
+    <property name="value">1</property>
+    <property name="lower">1</property>
+    <property name="upper">1000</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkListStore" id="attribute_liststore">
+    <columns>
+      <!-- column-name id -->
+      <column type="gchararray"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>
diff --git a/extensions/rename_series/data/ui/rename-series.ui b/extensions/rename_series/data/ui/rename-series.ui
index bd3d348..b54a8fa 100644
--- a/extensions/rename_series/data/ui/rename-series.ui
+++ b/extensions/rename_series/data/ui/rename-series.ui
@@ -10,13 +10,11 @@
     <child internal-child="vbox">
       <object class="GtkVBox" id="dialog-vbox1">
         <property name="visible">True</property>
-        <property name="orientation">vertical</property>
         <property name="spacing">2</property>
         <child>
           <object class="GtkVBox" id="vbox1">
             <property name="visible">True</property>
             <property name="border_width">6</property>
-            <property name="orientation">vertical</property>
             <property name="spacing">12</property>
             <child>
               <object class="GtkTable" id="table1">
@@ -110,18 +108,44 @@
                 <child>
                   <object class="GtkVBox" id="vbox2">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
                     <property name="spacing">6</property>
                     <child>
-                      <object class="GtkEntry" id="template_entry">
+                      <object class="GtkHBox" id="hbox1">
                         <property name="visible">True</property>
-                        <property name="can_focus">True</property>
-                        <property name="invisible_char">&#x25CF;</property>
-                        <property name="activates_default">True</property>
-                        <property name="secondary_icon_stock">gtk-help</property>
-                        <property name="secondary_icon_activatable">True</property>
-                        <property name="secondary_icon_sensitive">True</property>
-                        <property name="secondary_icon_tooltip_text">Help</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkEntry" id="template_entry">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">&#x25CF;</property>
+                            <property name="activates_default">True</property>
+                            <property name="secondary_icon_stock">gtk-help</property>
+                            <property name="secondary_icon_activatable">True</property>
+                            <property name="secondary_icon_sensitive">True</property>
+                            <property name="secondary_icon_tooltip_text">Help</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="edit_template_button">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="tooltip_text" translatable="yes">Edit template</property>
+                            <child>
+                              <object class="GtkImage" id="image2">
+                                <property name="visible">True</property>
+                                <property name="stock">gtk-edit</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
                         <property name="position">0</property>
@@ -384,14 +408,20 @@
                 <child>
                   <object class="GtkVBox" id="vbox3">
                     <property name="visible">True</property>
-                    <property name="orientation">vertical</property>
                     <child>
-                      <object class="GtkLabel" id="template_label">
+                      <object class="GtkVBox" id="template_label_box">
                         <property name="visible">True</property>
-                        <property name="xalign">0</property>
-                        <property name="label" translatable="yes">_Template:</property>
-                        <property name="use_underline">True</property>
-                        <property name="mnemonic_widget">template_entry</property>
+                        <child>
+                          <object class="GtkLabel" id="template_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">_Template:</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
                       </object>
                       <packing>
                         <property name="expand">False</property>
@@ -404,11 +434,20 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkHBox" id="sort_by_box">
+                  <object class="GtkHBox" id="hbox">
                     <property name="visible">True</property>
                     <property name="spacing">6</property>
                     <child>
-                      <placeholder/>
+                      <object class="GtkHBox" id="sort_by_box">
+                        <property name="visible">True</property>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
                     </child>
                     <child>
                       <object class="GtkCheckButton" id="reverse_order_checkbutton">
@@ -421,7 +460,6 @@
                       </object>
                       <packing>
                         <property name="expand">False</property>
-                        <property name="pack_type">end</property>
                         <property name="position">1</property>
                       </packing>
                     </child>
@@ -442,7 +480,6 @@
             <child>
               <object class="GtkVBox" id="vbox51">
                 <property name="visible">True</property>
-                <property name="orientation">vertical</property>
                 <property name="spacing">6</property>
                 <child>
                   <object class="GtkLabel" id="preview_label">
@@ -460,8 +497,8 @@
                 </child>
                 <child>
                   <object class="GtkScrolledWindow" id="preview_scrolledwindow">
-                    <property name="width_request">400</property>
-                    <property name="height_request">200</property>
+                    <property name="width_request">500</property>
+                    <property name="height_request">300</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="hscrollbar_policy">automatic</property>
@@ -558,7 +595,7 @@
     <property name="mode">vertical</property>
     <widgets>
       <widget name="template_entry"/>
-      <widget name="template_label"/>
+      <widget name="template_label_box"/>
     </widgets>
   </object>
   <object class="GtkAdjustment" id="start_at_adjustment">
diff --git a/extensions/rename_series/dlg-rename-series.c b/extensions/rename_series/dlg-rename-series.c
index b54b2d1..b2e1e6e 100644
--- a/extensions/rename_series/dlg-rename-series.c
+++ b/extensions/rename_series/dlg-rename-series.c
@@ -20,9 +20,12 @@
  */
 
 #include <config.h>
+#include <glib/gi18n.h>
 #include <gtk/gtk.h>
 #include <gthumb.h>
 #include "dlg-rename-series.h"
+#include "gth-template-editor-dialog.h"
+#include "gth-template-selector.h"
 #include "gth-rename-task.h"
 #include "preferences.h"
 
@@ -55,6 +58,18 @@ enum {
 #define DEFAULT_CHANGE_CASE    GTH_CHANGE_CASE_NONE
 
 
+static GthTemplateCode Rename_Special_Codes[] = {
+	{ GTH_TEMPLATE_CODE_TYPE_TEXT, N_("Text"), 0 },
+	{ GTH_TEMPLATE_CODE_TYPE_ENUMERATOR, N_("Enumerator"), '#' },
+	{ GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Original filename"), 'F' },
+	{ GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Original extension"), 'E' },
+	{ GTH_TEMPLATE_CODE_TYPE_SIMPLE, N_("Original enumerator"), 'N' },
+	{ GTH_TEMPLATE_CODE_TYPE_DATE, N_("Modification date"), 'M' },
+	{ GTH_TEMPLATE_CODE_TYPE_DATE, N_("Digitalization date"), 'D' },
+	{ GTH_TEMPLATE_CODE_TYPE_FILE_ATTRIBUTE, N_("File attribute"), 'A' }
+};
+
+
 typedef struct {
 	GthBrowser    *browser;
 	GList         *file_list;
@@ -549,6 +564,51 @@ update_preview_cb (GtkWidget  *widget,
 }
 
 
+static void
+template_editor_dialog_response_cb (GtkDialog *dialog,
+				    int        response_id,
+				    gpointer   user_data)
+{
+	DialogData *data = user_data;
+	char       *template;
+	GError     *error = NULL;
+
+	if (response_id != GTK_RESPONSE_OK) {
+		gtk_widget_destroy (GTK_WIDGET (dialog));
+		return;
+	}
+
+	template = gth_template_editor_dialog_get_template (GTH_TEMPLATE_EDITOR_DIALOG (dialog), &error);
+	if (error != NULL) {
+		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (dialog), _("Could not save the template"), &error);
+		return;
+	}
+
+	gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("template_entry")), template);
+	gtk_widget_destroy (GTK_WIDGET (dialog));
+
+	g_free (template);
+}
+
+
+static void
+edit_template_button_clicked_cb (GtkWidget  *widget,
+				 DialogData *data)
+{
+	GtkWidget *dialog;
+
+	dialog = gth_template_editor_dialog_new (Rename_Special_Codes, 8, _("Edit Template"), GTK_WINDOW (data->dialog));
+	gth_template_editor_dialog_set_template (GTH_TEMPLATE_EDITOR_DIALOG (dialog),
+						 gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("template_entry"))));
+	g_signal_connect (dialog,
+			  "response",
+			  G_CALLBACK (template_editor_dialog_response_cb),
+			  data);
+	gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+	gtk_window_present (GTK_WINDOW (dialog));
+}
+
+
 void
 dlg_rename_series (GthBrowser *browser,
 		   GList      *file_list)
@@ -731,6 +791,10 @@ dlg_rename_series (GthBrowser *browser,
 			  "toggled",
 			  G_CALLBACK (update_preview_cb),
 			  data);
+        g_signal_connect (GET_WIDGET ("edit_template_button"),
+                          "clicked",
+                          G_CALLBACK (edit_template_button_clicked_cb),
+                          data);
 
 	/* Run dialog. */
 
diff --git a/extensions/rename_series/gth-template-editor-dialog.c b/extensions/rename_series/gth-template-editor-dialog.c
new file mode 100644
index 0000000..1cd5134
--- /dev/null
+++ b/extensions/rename_series/gth-template-editor-dialog.c
@@ -0,0 +1,326 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <gthumb.h>
+#include "gth-template-editor-dialog.h"
+#include "gth-template-selector.h"
+
+
+#define GET_WIDGET(name) _gtk_builder_get_widget (self->priv->builder, (name))
+
+
+static gpointer parent_class = NULL;
+
+
+struct _GthTemplateEditorDialogPrivate {
+	GtkWidget       *content;
+	GRegex          *re;
+	GthTemplateCode *allowed_codes;
+	int              n_codes;
+};
+
+
+static void
+gth_template_editor_dialog_finalize (GObject *object)
+{
+	GthTemplateEditorDialog *dialog;
+
+	dialog = GTH_TEMPLATE_EDITOR_DIALOG (object);
+
+	if (dialog->priv != NULL) {
+		if (dialog->priv->re != NULL)
+			g_regex_unref (dialog->priv->re);
+		g_free (dialog->priv);
+		dialog->priv = NULL;
+	}
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_template_editor_dialog_class_init (GthTemplateEditorDialogClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	object_class = (GObjectClass*) class;
+
+	object_class->finalize = gth_template_editor_dialog_finalize;
+}
+
+
+static void
+gth_template_editor_dialog_init (GthTemplateEditorDialog *dialog)
+{
+	dialog->priv = g_new0 (GthTemplateEditorDialogPrivate, 1);
+	dialog->priv->re = NULL;
+}
+
+
+GType
+gth_template_editor_dialog_get_type (void)
+{
+        static GType type = 0;
+
+        if (! type) {
+                GTypeInfo type_info = {
+			sizeof (GthTemplateEditorDialogClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_template_editor_dialog_class_init,
+			NULL,
+			NULL,
+			sizeof (GthTemplateEditorDialog),
+			0,
+			(GInstanceInitFunc) gth_template_editor_dialog_init
+		};
+
+		type = g_type_register_static (GTK_TYPE_DIALOG,
+					       "GthTemplateEditorDialog",
+					       &type_info,
+					       0);
+	}
+
+        return type;
+}
+
+
+static void
+_gth_template_editor_update_sensitivity (GthTemplateEditorDialog *self)
+{
+	GList    *children;
+	gboolean  many_selectors;
+	GList    *scan;
+
+	children = gtk_container_get_children (GTK_CONTAINER (self->priv->content));
+	many_selectors = (children != NULL) && (children->next != NULL);
+	for (scan = children; scan; scan = scan->next)
+		gth_template_selector_can_remove (GTH_TEMPLATE_SELECTOR (children->data), many_selectors);
+
+	g_list_free (children);
+}
+
+
+static GtkWidget * _gth_template_editor_create_selector (GthTemplateEditorDialog *self);
+
+
+static void
+selector_add_template_cb (GthTemplateSelector     *selector,
+			  GthTemplateEditorDialog *self)
+{
+	GtkWidget *child;
+
+	child = _gth_template_editor_create_selector (self);
+	gtk_box_pack_start (GTK_BOX (self->priv->content), child, FALSE, FALSE, 0);
+	gtk_box_reorder_child (GTK_BOX (self->priv->content), child, _gtk_container_get_pos (GTK_CONTAINER (self->priv->content), GTK_WIDGET (selector)) + 1);
+	_gth_template_editor_update_sensitivity (self);
+}
+
+
+static void
+selector_remove_template_cb (GthTemplateSelector     *selector,
+			     GthTemplateEditorDialog *self)
+{
+	gtk_widget_destroy (GTK_WIDGET (selector));
+	_gth_template_editor_update_sensitivity (self);
+}
+
+
+static GtkWidget *
+_gth_template_editor_create_selector (GthTemplateEditorDialog *self)
+{
+	GtkWidget *child;
+
+	child = gth_template_selector_new (self->priv->allowed_codes, self->priv->n_codes);
+	gtk_widget_show (child);
+
+	g_signal_connect (child,
+			  "add_template",
+			  G_CALLBACK (selector_add_template_cb),
+			  self);
+	g_signal_connect (child,
+			  "remove_template",
+			  G_CALLBACK (selector_remove_template_cb),
+			  self);
+
+	return child;
+}
+
+
+static void
+gth_template_editor_dialog_construct (GthTemplateEditorDialog *self,
+				      GthTemplateCode         *allowed_codes,
+				      int                      n_codes,
+				      const char              *title,
+			              GtkWindow               *parent)
+{
+	GtkWidget *child;
+	GString   *regexp;
+	GString   *special_codes;
+	int        i;
+
+	self->priv->allowed_codes = allowed_codes;
+	self->priv->n_codes = n_codes;
+
+	if (title != NULL)
+    		gtk_window_set_title (GTK_WINDOW (self), title);
+  	if (parent != NULL)
+    		gtk_window_set_transient_for (GTK_WINDOW (self), parent);
+    	gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+	gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
+	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 5);
+	gtk_container_set_border_width (GTK_CONTAINER (self), 5);
+
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+	gtk_dialog_add_button (GTK_DIALOG (self), GTK_STOCK_OK, GTK_RESPONSE_OK);
+
+    	self->priv->content = gtk_vbox_new (FALSE, 6);
+    	gtk_container_set_border_width (GTK_CONTAINER (self->priv->content), 5);
+    	gtk_widget_show (self->priv->content);
+  	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), self->priv->content, TRUE, TRUE, 0);
+
+	child = _gth_template_editor_create_selector (self);
+	gtk_box_pack_start (GTK_BOX (self->priv->content), child, FALSE, FALSE, 0);
+
+	_gth_template_editor_update_sensitivity (self);
+
+	/* build the regular expression to compile the template */
+
+	regexp = g_string_new ("");
+	special_codes = g_string_new ("");
+
+	for (i = 0; i < n_codes; i++) {
+		GthTemplateCode *code = &allowed_codes[i];
+
+		switch (code->type) {
+		case GTH_TEMPLATE_CODE_TYPE_TEXT:
+			break;
+		case GTH_TEMPLATE_CODE_TYPE_ENUMERATOR:
+			if (regexp->len > 0)
+				g_string_append (regexp, "|");
+			g_string_append (regexp, "(");
+			g_string_append_c (regexp, code->code);
+			g_string_append (regexp, "+)");
+			break;
+		case GTH_TEMPLATE_CODE_TYPE_SIMPLE:
+		case GTH_TEMPLATE_CODE_TYPE_DATE:
+		case GTH_TEMPLATE_CODE_TYPE_FILE_ATTRIBUTE:
+			g_string_append_c (special_codes, code->code);
+			break;
+		}
+	}
+
+	if (special_codes->len > 0) {
+		/* special code with a custom format */
+
+		if (regexp->len > 0)
+			g_string_append (regexp, "|");
+		g_string_append (regexp, "(%[");
+		g_string_append (regexp, special_codes->str);
+		g_string_append (regexp, "]{[^}]+\\})");
+
+		/* special codes without a custom format */
+
+		g_string_append (regexp, "|");
+		g_string_append (regexp, "(%[");
+		g_string_append (regexp, special_codes->str);
+		g_string_append (regexp, "])");
+
+	}
+
+	self->priv->re = g_regex_new (regexp->str, 0, 0, NULL);
+
+	g_string_free (special_codes, TRUE);
+	g_string_free (regexp, TRUE);
+}
+
+
+GtkWidget *
+gth_template_editor_dialog_new (GthTemplateCode *allowed_codes,
+	 	     	     	int              n_codes,
+	 	     	     	const char      *title,
+			        GtkWindow       *parent)
+{
+	GthTemplateEditorDialog *self;
+
+	self = g_object_new (GTH_TYPE_TEMPLATE_EDITOR_DIALOG, NULL);
+	gth_template_editor_dialog_construct (self, allowed_codes, n_codes, title, parent);
+
+	return (GtkWidget *) self;
+}
+
+
+void
+gth_template_editor_dialog_set_template (GthTemplateEditorDialog *self,
+					 const char              *template)
+{
+	char **template_v;
+	int    i;
+
+	_gtk_container_remove_children (GTK_CONTAINER (self->priv->content), NULL, NULL);
+
+	template_v = g_regex_split (self->priv->re, template, 0);
+	for (i = 0; template_v[i] != NULL; i++) {
+		GtkWidget *child;
+
+		if ((template_v[i] == NULL) || g_str_equal (template_v[i], ""))
+			continue;
+
+		child = _gth_template_editor_create_selector (self);
+		gtk_box_pack_start (GTK_BOX (self->priv->content), child, FALSE, FALSE, 0);
+		gth_template_selector_set_value (GTH_TEMPLATE_SELECTOR (child), template_v[i]);
+	}
+
+	_gth_template_editor_update_sensitivity (self);
+
+	g_strfreev (template_v);
+}
+
+
+char *
+gth_template_editor_dialog_get_template (GthTemplateEditorDialog  *self,
+				         GError                  **error)
+{
+	GString *template;
+	GList   *children;
+	GList   *scan;
+
+	template = g_string_new ("");
+	children = gtk_container_get_children (GTK_CONTAINER (self->priv->content));
+	for (scan = children; scan; scan = scan->next) {
+		GtkWidget *child = scan->data;
+		char      *value;
+
+		value = gth_template_selector_get_value (GTH_TEMPLATE_SELECTOR (child), NULL);
+		if (value != NULL) {
+			g_string_append (template, value);
+			g_free (value);
+		}
+	}
+
+	g_list_free (children);
+
+	return g_string_free (template, FALSE);
+}
diff --git a/extensions/rename_series/gth-template-editor-dialog.h b/extensions/rename_series/gth-template-editor-dialog.h
new file mode 100644
index 0000000..7e55e2e
--- /dev/null
+++ b/extensions/rename_series/gth-template-editor-dialog.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_TEMPLATE_EDITOR_DIALOG_H
+#define GTH_TEMPLATE_EDITOR_DIALOG_H
+
+#include <gtk/gtk.h>
+#include "gth-template-selector.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_TEMPLATE_EDITOR_DIALOG            (gth_template_editor_dialog_get_type ())
+#define GTH_TEMPLATE_EDITOR_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_TEMPLATE_EDITOR_DIALOG, GthTemplateEditorDialog))
+#define GTH_TEMPLATE_EDITOR_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_TEMPLATE_EDITOR_DIALOG, GthTemplateEditorDialogClass))
+#define GTH_IS_TEMPLATE_EDITOR_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_TEMPLATE_EDITOR_DIALOG))
+#define GTH_IS_TEMPLATE_EDITOR_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_TEMPLATE_EDITOR_DIALOG))
+#define GTH_TEMPLATE_EDITOR_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_TEMPLATE_EDITOR_DIALOG, GthTemplateEditorDialogClass))
+
+typedef struct _GthTemplateEditorDialog        GthTemplateEditorDialog;
+typedef struct _GthTemplateEditorDialogClass   GthTemplateEditorDialogClass;
+typedef struct _GthTemplateEditorDialogPrivate GthTemplateEditorDialogPrivate;
+
+struct _GthTemplateEditorDialog {
+	GtkDialog parent_instance;
+	GthTemplateEditorDialogPrivate *priv;
+};
+
+struct _GthTemplateEditorDialogClass {
+	GtkDialogClass parent_class;
+};
+
+GType       gth_template_editor_dialog_get_type     (void);
+GtkWidget * gth_template_editor_dialog_new          (GthTemplateCode          *allowed_codes,
+		 	 	 	 	     int                       n_codes,
+		 	 	 	 	     const char               *title,
+						     GtkWindow                *parent);
+void        gth_template_editor_dialog_set_template (GthTemplateEditorDialog  *self,
+						     const char               *template);
+char *      gth_template_editor_dialog_get_template (GthTemplateEditorDialog  *self,
+						     GError                  **error);
+
+G_END_DECLS
+
+#endif /* GTH_TEMPLATE_EDITOR_DIALOG_H */
diff --git a/extensions/rename_series/gth-template-selector.c b/extensions/rename_series/gth-template-selector.c
new file mode 100644
index 0000000..7670f55
--- /dev/null
+++ b/extensions/rename_series/gth-template-selector.c
@@ -0,0 +1,540 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gthumb.h>
+#include "gth-template-selector.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (self->priv->builder, (x)))
+
+
+enum {
+	TYPE_DATA_COLUMN,
+	TYPE_NAME_COLUMN,
+	TYPE_N_COLUMNS
+};
+
+enum {
+	DATE_FORMAT_FORMAT_COLUMN,
+	DATE_FORMAT_NAME_COLUMN,
+	DATE_FORMAT_N_COLUMNS
+};
+
+enum {
+	ATTRIBUTE_ID_COLUMN,
+	ATTRIBUTE_NAME_COLUMN,
+	ATTRIBUTE_N_COLUMNS,
+};
+
+enum {
+        ADD_TEMPLATE,
+        REMOVE_TEMPLATE,
+        LAST_SIGNAL
+};
+
+
+struct _GthTemplateSelectorPrivate {
+	GtkBuilder *builder;
+};
+
+static char * Date_Formats[] = { "%x %X", "%Y-%m-%d--%H.%M.%S", "%q", NULL };
+
+static gpointer parent_class = NULL;
+static guint gth_template_selector_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gth_template_selector_finalize (GObject *object)
+{
+	GthTemplateSelector *selector;
+
+	selector = GTH_TEMPLATE_SELECTOR (object);
+
+	if (selector->priv != NULL) {
+		_g_object_unref (selector->priv->builder);
+		g_free (selector->priv);
+		selector->priv = NULL;
+	}
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_template_selector_class_init (GthTemplateSelectorClass *klass)
+{
+	parent_class = g_type_class_peek_parent (klass);
+
+	G_OBJECT_CLASS (klass)->finalize = gth_template_selector_finalize;
+
+	/* signals */
+
+	gth_template_selector_signals[ADD_TEMPLATE] =
+		g_signal_new ("add-template",
+			      G_TYPE_FROM_CLASS (klass),
+ 			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GthTemplateSelectorClass, add_template),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+	gth_template_selector_signals[REMOVE_TEMPLATE] =
+		g_signal_new ("remove-template",
+			      G_TYPE_FROM_CLASS (klass),
+ 			      G_SIGNAL_RUN_LAST,
+			      G_STRUCT_OFFSET (GthTemplateSelectorClass, remove_template),
+			      NULL, NULL,
+			      g_cclosure_marshal_VOID__VOID,
+			      G_TYPE_NONE,
+			      0);
+}
+
+
+static void
+gth_template_selector_instance_init (GthTemplateSelector *self)
+{
+	self->priv = g_new0 (GthTemplateSelectorPrivate, 1);
+	self->priv->builder = NULL;
+}
+
+
+GType
+gth_template_selector_get_type (void)
+{
+	static GType type_id = 0;
+
+	if (type_id == 0) {
+		static const GTypeInfo type_info = {
+			sizeof (GthTemplateSelectorClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) gth_template_selector_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,
+			sizeof (GthTemplateSelector),
+			0,
+			(GInstanceInitFunc) gth_template_selector_instance_init,
+			NULL
+		};
+		type_id = g_type_register_static (GTK_TYPE_HBOX,
+						  "GthTemplateSelector",
+						  &type_info,
+						  0);
+	}
+	return type_id;
+}
+
+
+static void
+add_button_clicked_cb (GtkButton           *button,
+		       GthTemplateSelector *self)
+{
+	g_signal_emit (self, gth_template_selector_signals[ADD_TEMPLATE], 0);
+}
+
+
+static void
+remove_button_clicked_cb (GtkButton           *button,
+			  GthTemplateSelector *self)
+{
+	g_signal_emit (self, gth_template_selector_signals[REMOVE_TEMPLATE], 0);
+}
+
+
+static void
+type_combobox_changed_cb (GtkComboBox         *combo_box,
+			  GthTemplateSelector *self)
+{
+	GtkTreeIter      iter;
+	GthTemplateCode *code;
+
+	if (! gtk_combo_box_get_active_iter (combo_box, &iter))
+		return;
+
+	gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("type_liststore")),
+			    &iter,
+			    TYPE_DATA_COLUMN, &code,
+			    -1);
+
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (GET_WIDGET ("type_notebook")), code->type);
+}
+
+
+static void
+date_format_combobox_changed_cb (GtkComboBox         *combo_box,
+				 GthTemplateSelector *self)
+{
+	gboolean custom_format;
+
+	custom_format = gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("date_format_combobox"))) == G_N_ELEMENTS (Date_Formats) - 1;
+	if (custom_format) {
+		gtk_widget_hide (GET_WIDGET ("date_format_combobox"));
+		gtk_widget_show (GET_WIDGET ("custom_date_format_entry"));
+		gtk_widget_grab_focus (GET_WIDGET ("custom_date_format_entry"));
+	}
+	else {
+		gtk_widget_show (GET_WIDGET ("date_format_combobox"));
+		gtk_widget_hide (GET_WIDGET ("custom_date_format_entry"));
+	}
+}
+
+
+static void
+gth_template_selector_construct (GthTemplateSelector *self,
+				 GthTemplateCode     *allowed_codes,
+				 int                  n_codes)
+{
+	GtkListStore  *list_store;
+	GtkTreeIter    iter;
+	int            i;
+	GTimeVal       timeval;
+	char         **attributes_v;
+
+	gtk_box_set_spacing (GTK_BOX (self), 6);
+	gtk_container_set_border_width (GTK_CONTAINER (self), 0);
+
+	self->priv->builder = _gtk_builder_new_from_file("code-selector.ui", "rename_series");
+	gtk_container_add (GTK_CONTAINER (self), GET_WIDGET ("code_selector"));
+
+	gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("enumerator_digits_spinbutton")), 1.0);
+
+	/* code types */
+
+	list_store = (GtkListStore *) GET_WIDGET ("type_liststore");
+	for (i = 0; i < n_codes; i++) {
+		GthTemplateCode *code = &allowed_codes[i];
+
+		gtk_list_store_append (list_store, &iter);
+		gtk_list_store_set (list_store, &iter,
+				    TYPE_DATA_COLUMN, code,
+				    TYPE_NAME_COLUMN, _(code->description),
+				    -1);
+	}
+
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (GET_WIDGET ("type_notebook")), GTH_TEMPLATE_CODE_TYPE_SIMPLE);
+
+	/* date formats */
+
+	g_get_current_time (&timeval);
+	list_store = (GtkListStore *) GET_WIDGET ("date_format_liststore");
+	for (i = 0; Date_Formats[i] != NULL; i++) {
+		char *example;
+
+		example = _g_time_val_strftime (&timeval, Date_Formats[i]);
+
+		gtk_list_store_append (list_store, &iter);
+		gtk_list_store_set (list_store, &iter,
+				    DATE_FORMAT_FORMAT_COLUMN, Date_Formats[i],
+				    DATE_FORMAT_NAME_COLUMN, example,
+				    -1);
+
+		g_free (example);
+	}
+
+	gtk_list_store_append (list_store, &iter);
+	gtk_list_store_set (list_store, &iter,
+			    DATE_FORMAT_FORMAT_COLUMN, "",
+			    DATE_FORMAT_NAME_COLUMN, _("Custom"),
+			    -1);
+
+	gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("date_format_combobox")), 0);
+
+	/* attributes */
+
+	list_store = (GtkListStore *) GET_WIDGET ("attribute_liststore");
+	attributes_v = gth_main_get_metadata_attributes ("*");
+	for (i = 0; attributes_v[i] != NULL; i++) {
+		GthMetadataInfo *info;
+		const char      *name;
+
+		info = gth_main_get_metadata_info (attributes_v[i]);
+		if (info == NULL)
+			continue;
+		if ((info->flags & GTH_METADATA_ALLOW_IN_PROPERTIES_VIEW) == 0)
+			continue;
+
+		name = info->display_name;
+		if (name == NULL)
+			name = info->id;
+
+		gtk_list_store_append (list_store, &iter);
+		gtk_list_store_set (list_store, &iter,
+				    ATTRIBUTE_ID_COLUMN, info->id,
+				    ATTRIBUTE_NAME_COLUMN, name,
+				    -1);
+	}
+	g_strfreev (attributes_v);
+
+	/* signals */
+
+	g_signal_connect (GET_WIDGET ("add_button"),
+			  "clicked",
+			  G_CALLBACK (add_button_clicked_cb),
+			  self);
+	g_signal_connect (GET_WIDGET ("remove_button"),
+			  "clicked",
+			  G_CALLBACK (remove_button_clicked_cb),
+			  self);
+	g_signal_connect (GET_WIDGET ("type_combobox"),
+			  "changed",
+			  G_CALLBACK (type_combobox_changed_cb),
+			  self);
+	g_signal_connect (GET_WIDGET ("date_format_combobox"),
+			  "changed",
+			  G_CALLBACK (date_format_combobox_changed_cb),
+			  self);
+}
+
+
+GtkWidget *
+gth_template_selector_new (GthTemplateCode *allowed_codes,
+		           int              n_codes)
+{
+	GthTemplateSelector *self;
+
+	self = g_object_new (GTH_TYPE_TEMPLATE_SELECTOR, NULL);
+	gth_template_selector_construct (self, allowed_codes, n_codes);
+
+	return (GtkWidget *) self;
+}
+
+
+static char *
+get_format_from_value (const char *value)
+{
+	char    *format = NULL;
+	GRegex  *re;
+	char   **a;
+	int      i;
+
+	re = g_regex_new ("%.\\{([^}]+)\\}", 0, 0, NULL);
+	a = g_regex_split (re, value, 0);
+	for (i = 1; i < g_strv_length (a); i += 2)
+		format = g_strstrip (g_strdup (a[i]));
+
+	g_strfreev (a);
+	g_regex_unref (re);
+
+	return format;
+}
+
+
+void
+gth_template_selector_set_value (GthTemplateSelector *self,
+				 const char          *value)
+{
+	GthTemplateCode *code = NULL;
+	GtkTreeModel    *tree_model;
+	GtkTreeIter      iter;
+	GtkTreeIter      text_iter;
+	gboolean         allows_text = FALSE;
+	int              i;
+	gboolean         predefined_format;
+	char            *format;
+	char            *attribute_id;
+
+	tree_model = (GtkTreeModel *) GET_WIDGET ("type_liststore");
+	if (gtk_tree_model_get_iter_first (tree_model, &iter)) {
+		do {
+			GthTemplateCode *iter_code = NULL;
+
+			gtk_tree_model_get (tree_model,
+					    &iter,
+					    TYPE_DATA_COLUMN, &iter_code,
+					    -1);
+
+			if (iter_code->type == GTH_TEMPLATE_CODE_TYPE_TEXT) {
+				allows_text = TRUE;
+				text_iter = iter;
+			}
+
+			if ((value[0] == '%')
+			    && ((iter_code->type == GTH_TEMPLATE_CODE_TYPE_SIMPLE)
+				|| (iter_code->type == GTH_TEMPLATE_CODE_TYPE_DATE)
+				|| (iter_code->type == GTH_TEMPLATE_CODE_TYPE_FILE_ATTRIBUTE))
+			    && (value[1] == iter_code->code))
+			{
+				code = iter_code;
+			}
+			else if ((iter_code->type == GTH_TEMPLATE_CODE_TYPE_ENUMERATOR) && (value[0] == iter_code->code)) {
+				code = iter_code;
+			}
+		}
+		while ((code == NULL) && gtk_tree_model_iter_next (tree_model, &iter));
+	}
+
+	if ((code == NULL) && ! allows_text)
+		return;
+
+	if ((code == NULL) && allows_text) {
+		gtk_combo_box_set_active_iter (GTK_COMBO_BOX (GET_WIDGET ("type_combobox")), &text_iter);
+		gtk_notebook_set_current_page (GTK_NOTEBOOK (GET_WIDGET ("type_notebook")), GTH_TEMPLATE_CODE_TYPE_TEXT);
+		gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("text_entry")), value);
+		return;
+	}
+
+	gtk_combo_box_set_active_iter (GTK_COMBO_BOX (GET_WIDGET ("type_combobox")), &iter);
+	gtk_notebook_set_current_page (GTK_NOTEBOOK (GET_WIDGET ("type_notebook")), code->type);
+
+	switch (code->type) {
+	case GTH_TEMPLATE_CODE_TYPE_ENUMERATOR:
+		gtk_spin_button_set_value (GTK_SPIN_BUTTON (GET_WIDGET ("enumerator_digits_spinbutton")), strlen (value));
+		break;
+
+	case GTH_TEMPLATE_CODE_TYPE_DATE:
+		predefined_format = FALSE;
+		format = get_format_from_value (value);
+		if (format == NULL)
+			format = g_strdup (DEFAULT_STRFTIME_FORMAT);
+		for (i = 0; Date_Formats[i] != NULL; i++) {
+			if (g_str_equal (format, Date_Formats[i])) {
+				gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("date_format_combobox")), i);
+				predefined_format = TRUE;
+				break;
+			}
+		}
+		if (! predefined_format) {
+			gtk_combo_box_set_active (GTK_COMBO_BOX (GET_WIDGET ("date_format_combobox")), G_N_ELEMENTS (Date_Formats) - 1);
+			gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("custom_date_format_entry")), format);
+		}
+		g_free (format);
+		break;
+
+	case GTH_TEMPLATE_CODE_TYPE_FILE_ATTRIBUTE:
+		attribute_id = get_format_from_value (value);
+		if (attribute_id != NULL) {
+			GtkTreeModel *tree_model;
+			GtkTreeIter   iter;
+
+			tree_model = (GtkTreeModel *) GET_WIDGET ("attribute_liststore");
+			if (gtk_tree_model_get_iter_first (tree_model, &iter)) {
+				do {
+					char *iter_id;
+
+					gtk_tree_model_get (tree_model,
+							    &iter,
+							    ATTRIBUTE_ID_COLUMN, &iter_id,
+							    -1);
+
+					if (g_strcmp0 (attribute_id, iter_id) == 0) {
+						gtk_combo_box_set_active_iter (GTK_COMBO_BOX (GET_WIDGET ("attribute_combobox")), &iter);
+						g_free (iter_id);
+						break;
+					}
+
+					g_free (iter_id);
+				}
+				while (gtk_tree_model_iter_next (tree_model, &iter));
+			}
+		}
+		g_free (attribute_id);
+		break;
+
+	default:
+		break;
+	}
+}
+
+
+char *
+gth_template_selector_get_value (GthTemplateSelector  *self,
+				 GError              **error)
+{
+	GString         *value;
+	GtkTreeIter      iter;
+	GthTemplateCode *code;
+	int              i;
+
+	if (! gtk_combo_box_get_active_iter (GTK_COMBO_BOX (GET_WIDGET ("type_combobox")), &iter))
+		return NULL;
+
+	gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("type_liststore")),
+			    &iter,
+			    TYPE_DATA_COLUMN, &code,
+			    -1);
+
+	value = g_string_new ("");
+
+	switch (code->type) {
+	case GTH_TEMPLATE_CODE_TYPE_TEXT:
+		g_string_append (value, gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("text_entry"))));
+		break;
+
+	case GTH_TEMPLATE_CODE_TYPE_ENUMERATOR:
+		for (i = 0; i < gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (GET_WIDGET ("enumerator_digits_spinbutton"))); i++)
+			g_string_append_c (value, code->code);
+		break;
+
+	case GTH_TEMPLATE_CODE_TYPE_SIMPLE:
+		g_string_append (value, "%");
+		g_string_append_c (value, code->code);
+		break;
+
+	case GTH_TEMPLATE_CODE_TYPE_DATE:
+		g_string_append (value, "%");
+		g_string_append_c (value, code->code);
+		if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (GET_WIDGET ("date_format_combobox")), &iter)) {
+			char *format;
+
+			gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("date_format_liststore")),
+					    &iter,
+					    DATE_FORMAT_FORMAT_COLUMN, &format,
+					    -1);
+
+			if ((format == NULL) || (strcmp (format, "") == 0))
+				format = g_strdup (gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("custom_date_format_entry"))));
+
+			if ((format != NULL) && (strcmp (format, "") != 0) && (strcmp (format, DEFAULT_STRFTIME_FORMAT) != 0))
+				g_string_append_printf (value, "{ %s }", format);
+
+			g_free (format);
+		}
+		break;
+
+	case GTH_TEMPLATE_CODE_TYPE_FILE_ATTRIBUTE:
+		if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (GET_WIDGET ("attribute_combobox")), &iter)) {
+			char *attribute_id;
+
+			gtk_tree_model_get (GTK_TREE_MODEL (GET_WIDGET ("attribute_liststore")),
+					    &iter,
+					    ATTRIBUTE_ID_COLUMN, &attribute_id,
+					    -1);
+			if ((attribute_id != NULL) && (strcmp (attribute_id, "") != 0)) {
+				g_string_append_printf (value, "%%%c{ %s }", code->code, attribute_id);
+			}
+
+			g_free (attribute_id);
+		}
+		break;
+	}
+
+	return g_string_free (value, FALSE);
+}
+
+
+void
+gth_template_selector_can_remove (GthTemplateSelector *self,
+				  gboolean             value)
+{
+	gtk_widget_set_sensitive (GET_WIDGET ("remove_button"), value);
+}
diff --git a/extensions/rename_series/gth-template-selector.h b/extensions/rename_series/gth-template-selector.h
new file mode 100644
index 0000000..e3027e8
--- /dev/null
+++ b/extensions/rename_series/gth-template-selector.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_TEMPLATE_SELECTOR_H
+#define GTH_TEMPLATE_SELECTOR_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	GTH_TEMPLATE_CODE_TYPE_TEXT,
+	GTH_TEMPLATE_CODE_TYPE_ENUMERATOR,
+	GTH_TEMPLATE_CODE_TYPE_SIMPLE,
+	GTH_TEMPLATE_CODE_TYPE_DATE,
+	GTH_TEMPLATE_CODE_TYPE_FILE_ATTRIBUTE
+} GthTemplateCodeType;
+
+typedef struct {
+	GthTemplateCodeType  type;
+	char                *description;
+	char                 code;
+} GthTemplateCode;
+
+#define GTH_TYPE_TEMPLATE_SELECTOR            (gth_template_selector_get_type ())
+#define GTH_TEMPLATE_SELECTOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_TEMPLATE_SELECTOR, GthTemplateSelector))
+#define GTH_TEMPLATE_SELECTOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_TEMPLATE_SELECTOR, GthTemplateSelectorClass))
+#define GTH_IS_TEMPLATE_SELECTOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_TEMPLATE_SELECTOR))
+#define GTH_IS_TEMPLATE_SELECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_TEMPLATE_SELECTOR))
+#define GTH_TEMPLATE_SELECTOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_TEMPLATE_SELECTOR, GthTemplateSelectorClass))
+
+typedef struct _GthTemplateSelector        GthTemplateSelector;
+typedef struct _GthTemplateSelectorClass   GthTemplateSelectorClass;
+typedef struct _GthTemplateSelectorPrivate GthTemplateSelectorPrivate;
+
+struct _GthTemplateSelector {
+	GtkHBox parent_instance;
+	GthTemplateSelectorPrivate *priv;
+};
+
+struct _GthTemplateSelectorClass {
+	GtkHBoxClass parent_class;
+
+	void (*add_template)    (GthTemplateSelector *selector);
+	void (*remove_template) (GthTemplateSelector *selector);
+};
+
+GType       gth_template_selector_get_type      (void);
+GtkWidget * gth_template_selector_new           (GthTemplateCode      *allowed_codes,
+						 int                   n_codes);
+void        gth_template_selector_set_value     (GthTemplateSelector  *selector,
+					         const char           *value);
+char *      gth_template_selector_get_value     (GthTemplateSelector  *selector,
+					         GError              **error);
+void        gth_template_selector_can_remove    (GthTemplateSelector  *selector,
+					         gboolean              value);
+
+G_END_DECLS
+
+#endif /* GTH_TEMPLATE_SELECTOR_H */
diff --git a/gthumb/glib-utils.c b/gthumb/glib-utils.c
index 120ad3b..a53ffa4 100644
--- a/gthumb/glib-utils.c
+++ b/gthumb/glib-utils.c
@@ -555,7 +555,7 @@ _g_time_val_strftime (GTimeVal   *time_,
 	struct tm *tm;
 
 	if ((format == NULL) || (*format == '\0'))
-		format = "%Y-%m-%d--%H.%M.%S";
+		format = DEFAULT_STRFTIME_FORMAT;
 
 	if (strcmp (format, "%q") == 0)
 		format = "%Y-%m-%d";
diff --git a/gthumb/glib-utils.h b/gthumb/glib-utils.h
index 06ccc98..6405407 100644
--- a/gthumb/glib-utils.h
+++ b/gthumb/glib-utils.h
@@ -67,6 +67,8 @@ G_BEGIN_DECLS
 #define G_TYPE_ERROR (g_error_get_type ())
 #endif
 
+#define DEFAULT_STRFTIME_FORMAT "%Y-%m-%d--%H.%M.%S"
+
 /* signals */
 
 #define g_signal_handlers_disconnect_by_data(instance, data) \



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]