[gthumb/ext: 8/15] started work on the organize task



commit 189074ff5702f3681f98f457b0ad94e6e680c891
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Dec 13 22:37:50 2009 +0100

    started work on the organize task

 extensions/catalogs/Makefile.am                    |    4 +
 extensions/catalogs/callbacks.c                    |   26 ++-
 extensions/catalogs/data/ui/Makefile.am            |    5 +-
 extensions/catalogs/data/ui/organize-files-task.ui |  248 ++++++++++++
 extensions/catalogs/data/ui/organize-files.ui      |  275 +++++++++++++
 extensions/catalogs/dlg-organize-files.c           |  116 ++++++
 extensions/catalogs/dlg-organize-files.h           |   31 ++
 extensions/catalogs/gth-catalog.c                  |    8 +
 extensions/catalogs/gth-catalog.h                  |    2 +
 extensions/catalogs/gth-organize-task.c            |  426 ++++++++++++++++++++
 extensions/catalogs/gth-organize-task.h            |   67 +++
 gthumb/gio-utils.c                                 |  153 ++++++--
 gthumb/glib-utils.c                                |   36 ++-
 gthumb/glib-utils.h                                |    3 +
 gthumb/gth-embedded-dialog.c                       |    1 +
 gthumb/gth-main.c                                  |    2 +-
 16 files changed, 1364 insertions(+), 39 deletions(-)
---
diff --git a/extensions/catalogs/Makefile.am b/extensions/catalogs/Makefile.am
index 46219ad..aea69f3 100644
--- a/extensions/catalogs/Makefile.am
+++ b/extensions/catalogs/Makefile.am
@@ -12,10 +12,14 @@ libcatalogs_la_SOURCES = 		\
 	dlg-add-to-catalog.h		\
 	dlg-catalog-properties.c	\
 	dlg-catalog-properties.h	\
+	dlg-organize-files.c		\
+	dlg-organize-files.h		\
 	gth-catalog.c			\
 	gth-catalog.h			\
 	gth-file-source-catalogs.c 	\
 	gth-file-source-catalogs.h	\
+	gth-organize-task.c		\
+	gth-organize-task.h		\
 	main.c
 
 libcatalogs_la_CFLAGS = $(GTHUMB_CFLAGS) $(WARNINGS) -I$(top_srcdir) -I$(top_builddir)/gthumb 
diff --git a/extensions/catalogs/callbacks.c b/extensions/catalogs/callbacks.c
index a6f6de6..5702f30 100644
--- a/extensions/catalogs/callbacks.c
+++ b/extensions/catalogs/callbacks.c
@@ -27,6 +27,7 @@
 #include <gthumb.h>
 #include <gth-catalog.h>
 #include "dlg-catalog-properties.h"
+#include "dlg-organize-files.h"
 #include "gth-file-source-catalogs.h"
 #include "actions.h"
 
@@ -153,6 +154,7 @@ typedef struct {
 	gboolean        catalog_menu_loaded;
 	guint           monitor_events;
 	GtkWidget      *properties_button;
+	GtkWidget      *organize_button;
 } BrowserData;
 
 
@@ -578,6 +580,14 @@ properties_button_clicked_cb (GtkButton  *button,
 }
 
 
+static void
+organize_button_clicked_cb (GtkButton  *button,
+			    GthBrowser *browser)
+{
+	dlg_organize_files (browser, gth_browser_get_location (browser));
+}
+
+
 void
 catalogs__gth_browser_load_location_after_cb (GthBrowser   *browser,
 					      GthFileData  *location_data,
@@ -655,6 +665,18 @@ catalogs__gth_browser_update_extra_widget_cb (GthBrowser *browser)
 					  browser);
 		}
 	}
-	else if (GTH_IS_FILE_SOURCE_VFS (gth_browser_get_location_source (browser)))
-		gedit_message_area_add_button (GEDIT_MESSAGE_AREA (gth_browser_get_list_extra_widget (browser)), _("Organize"), _RESPONSE_ORGANIZE);
+	else if (GTH_IS_FILE_SOURCE_VFS (gth_browser_get_location_source (browser))) {
+		if (data->organize_button == NULL) {
+			data->organize_button = gtk_button_new ();
+			gtk_container_add (GTK_CONTAINER (data->organize_button), gtk_label_new (_("Organize")));
+			g_object_add_weak_pointer (G_OBJECT (data->organize_button), (gpointer *)&data->organize_button);
+			gtk_button_set_relief (GTK_BUTTON (data->organize_button), GTK_RELIEF_NONE);
+			gtk_widget_show_all (data->organize_button);
+			gedit_message_area_add_action_widget (GEDIT_MESSAGE_AREA (gth_browser_get_list_extra_widget (browser)), data->organize_button, _RESPONSE_ORGANIZE);
+			g_signal_connect (data->organize_button,
+					  "clicked",
+					  G_CALLBACK (organize_button_clicked_cb),
+					  browser);
+		}
+	}
 }
diff --git a/extensions/catalogs/data/ui/Makefile.am b/extensions/catalogs/data/ui/Makefile.am
index 4b3c318..75fa8a5 100644
--- a/extensions/catalogs/data/ui/Makefile.am
+++ b/extensions/catalogs/data/ui/Makefile.am
@@ -1,5 +1,8 @@
 uidir = $(datadir)/gthumb-2.0/ui
-ui_DATA = add-to-catalog.ui catalog-properties.ui
+ui_DATA = 			\
+	add-to-catalog.ui	\
+	catalog-properties.ui	\
+	organize-files.ui
 EXTRA_DIST = $(ui_DATA)
 
 -include $(top_srcdir)/git.mk
diff --git a/extensions/catalogs/data/ui/organize-files-task.ui b/extensions/catalogs/data/ui/organize-files-task.ui
new file mode 100644
index 0000000..e961f5a
--- /dev/null
+++ b/extensions/catalogs/data/ui/organize-files-task.ui
@@ -0,0 +1,248 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="results_liststore">
+    <columns>
+      <!-- column-name catalog -->
+      <column type="gchararray"/>
+      <!-- column-name cardinality -->
+      <column type="gint"/>
+      <!-- column-name create -->
+      <column type="gboolean"/>
+    </columns>
+  </object>
+  <object class="GtkDialog" id="organize_files_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Organize Files</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox3">
+        <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">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkHBox" id="hbox3">
+                <property name="visible">True</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment1">
+                    <property name="visible">True</property>
+                    <property name="yalign">0</property>
+                    <property name="top_padding">1</property>
+                    <child>
+                      <object class="GtkImage" id="icon_image">
+                        <property name="visible">True</property>
+                        <property name="yalign">0</property>
+                        <property name="stock">gtk-dialog-info</property>
+                        <property name="pixel_size">24</property>
+                        <property name="icon-size">6</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox2">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="label3">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Organizing files</property>
+                        <attributes>
+                          <attribute name="weight" value="bold"/>
+                          <attribute name="size" value="11000"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkHBox" id="hbox2">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="progress_label">
+                            <property name="visible">True</property>
+                            <property name="xalign">0</property>
+                            <property name="label" translatable="yes">Searching in...</property>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVBox" id="vbox3">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <child>
+                          <object class="GtkHBox" id="hbox1">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="label2">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">Organization:</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="n_catalogs_label">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                              </object>
+                              <packing>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox4">
+                            <property name="visible">True</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkScrolledWindow" id="scrolledwindow1">
+                                <property name="width_request">300</property>
+                                <property name="height_request">150</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="hscrollbar_policy">automatic</property>
+                                <property name="vscrollbar_policy">automatic</property>
+                                <property name="shadow_type">in</property>
+                                <child>
+                                  <object class="GtkTreeView" id="treeview1">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="model">results_liststore</property>
+                                    <property name="headers_clickable">False</property>
+                                    <property name="search_column">0</property>
+                                    <child>
+                                      <object class="GtkTreeViewColumn" id="treeviewcolumn1">
+                                        <property name="title">Date</property>
+                                        <property name="expand">True</property>
+                                        <child>
+                                          <object class="GtkCellRendererText" id="cellrenderertext1"/>
+                                          <attributes>
+                                            <attribute name="text">0</attribute>
+                                          </attributes>
+                                        </child>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkTreeViewColumn" id="treeviewcolumn2">
+                                        <property name="title">Files</property>
+                                        <property name="expand">True</property>
+                                        <child>
+                                          <object class="GtkCellRendererText" id="cellrenderertext2"/>
+                                          <attributes>
+                                            <attribute name="text">1</attribute>
+                                          </attributes>
+                                        </child>
+                                      </object>
+                                    </child>
+                                    <child>
+                                      <object class="GtkTreeViewColumn" id="treeviewcolumn3">
+                                        <property name="title">Create Catalog</property>
+                                        <child>
+                                          <object class="GtkCellRendererToggle" id="cellrenderertoggle2"/>
+                                          <attributes>
+                                            <attribute name="active">2</attribute>
+                                          </attributes>
+                                        </child>
+                                      </object>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area3">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">cancel_button</action-widget>
+    </action-widgets>
+  </object>
+</interface>
diff --git a/extensions/catalogs/data/ui/organize-files.ui b/extensions/catalogs/data/ui/organize-files.ui
new file mode 100644
index 0000000..8c34484
--- /dev/null
+++ b/extensions/catalogs/data/ui/organize-files.ui
@@ -0,0 +1,275 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkDialog" id="organize_files_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Organize Files</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <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">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkHBox" id="hbox3">
+                <property name="visible">True</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkImage" id="icon_image">
+                    <property name="visible">True</property>
+                    <property name="yalign">0</property>
+                    <property name="stock">gtk-dialog-info</property>
+                    <property name="pixel_size">24</property>
+                    <property name="icon-size">6</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox2">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkVBox" id="vbox4">
+                        <property name="visible">True</property>
+                        <property name="orientation">vertical</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkAlignment" id="alignment2">
+                            <property name="visible">True</property>
+                            <property name="bottom_padding">6</property>
+                            <child>
+                              <object class="GtkLabel" id="label3">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">A catalog will be created for each group.</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkHBox" id="hbox6">
+                            <property name="visible">True</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkLabel" id="label1">
+                                <property name="visible">True</property>
+                                <property name="xalign">0</property>
+                                <property name="label" translatable="yes">_Group by:</property>
+                                <property name="use_underline">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkComboBox" id="group_by_combobox">
+                                <property name="visible">True</property>
+                                <property name="model">group_by_liststore</property>
+                                <property name="active">0</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">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkCheckButton" id="include_subfolders_checkbutton">
+                                <property name="label" translatable="yes">_Include sub-folders</property>
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="use_underline">True</property>
+                                <property name="draw_indicator">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkCheckButton" id="create_singletons_checkbutton">
+                            <property name="label" translatable="yes">Do not create catalogs with a single file</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="draw_indicator">True</property>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkAlignment" id="alignment1">
+                            <property name="visible">True</property>
+                            <property name="left_padding">12</property>
+                            <child>
+                              <object class="GtkHBox" id="hbox1">
+                                <property name="visible">True</property>
+                                <property name="spacing">6</property>
+                                <child>
+                                  <object class="GtkCheckButton" id="use_singletons_catalog_checkbutton">
+                                    <property name="label" translatable="yes">Put single files in the catalog:</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">False</property>
+                                    <property name="inconsistent">True</property>
+                                    <property name="draw_indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkEntry" id="single_catalog_entry">
+                                    <property name="visible">True</property>
+                                    <property name="sensitive">False</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="invisible_char">&#x25CF;</property>
+                                    <property name="text" translatable="yes">Singles</property>
+                                  </object>
+                                  <packing>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">0</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">end</property>
+            <child>
+              <object class="GtkButton" id="help_button">
+                <property name="label">gtk-help</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="start_button">
+                <property name="label">gtk-execute</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</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="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">help_button</action-widget>
+      <action-widget response="0">cancel_button</action-widget>
+      <action-widget response="0">start_button</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkListStore" id="group_by_liststore">
+    <columns>
+      <!-- column-name type -->
+      <column type="gint"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0">0</col>
+        <col id="1" translatable="yes">date photo was taken</col>
+      </row>
+      <row>
+        <col id="0">1</col>
+        <col id="1" translatable="yes">file modified date</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="stock">gtk-execute</property>
+  </object>
+</interface>
diff --git a/extensions/catalogs/dlg-organize-files.c b/extensions/catalogs/dlg-organize-files.c
new file mode 100644
index 0000000..8d7b712
--- /dev/null
+++ b/extensions/catalogs/dlg-organize-files.c
@@ -0,0 +1,116 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include "dlg-organize-files.h"
+#include "gth-organize-task.h"
+
+
+#define GET_WIDGET(name) _gtk_builder_get_widget (data->builder, (name))
+
+
+typedef struct {
+	GthBrowser *browser;
+	GtkBuilder *builder;
+	GtkWidget  *dialog;
+	GFile      *folder;
+} DialogData;
+
+
+static void
+destroy_cb (GtkWidget  *widget,
+	    DialogData *data)
+{
+	g_object_ref (data->folder);
+	g_object_unref (data->builder);
+	g_free (data);
+}
+
+
+static void
+start_button_clicked_cb (GtkWidget  *widget,
+			 DialogData *data)
+{
+	GthTask *task;
+
+	task = gth_organize_task_new (data->browser, data->folder, gtk_combo_box_get_active (GTK_COMBO_BOX (GET_WIDGET ("group_by_combobox"))));
+	gth_organize_task_set_recursive (GTH_ORGANIZE_TASK (task), gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("include_subfolders_checkbutton"))));
+	gth_organize_task_set_create_singletons (GTH_ORGANIZE_TASK (task), gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("create_singletons_checkbutton"))));
+	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (GET_WIDGET ("use_singletons_catalog_checkbutton"))))
+		gth_organize_task_set_singletons_catalog (GTH_ORGANIZE_TASK (task), gtk_entry_get_text (GTK_ENTRY (GET_WIDGET ("single_catalog_entry"))));
+	gth_browser_exec_task (data->browser, task, FALSE);
+
+	gtk_widget_destroy (data->dialog);
+	g_object_unref (task);
+}
+
+
+static void
+help_button_clicked_cb (GtkWidget  *widget,
+			DialogData *data)
+{
+	show_help_dialog (GTK_WINDOW (data->dialog), "organize-files");
+}
+
+
+void
+dlg_organize_files (GthBrowser *browser,
+		    GFile      *folder)
+{
+	DialogData *data;
+
+	g_return_if_fail (folder != NULL);
+
+	data = g_new0 (DialogData, 1);
+	data->browser = browser;
+	data->folder = g_file_dup (folder);
+	data->builder = _gtk_builder_new_from_file ("organize-files.ui", "catalogs");
+	data->dialog = GET_WIDGET ("organize_files_dialog");
+
+	/*gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (GET_WIDGET ("start_at_filechooserbutton")), data->folder, NULL); FIXME */
+
+	/* Set the signals handlers. */
+
+	g_signal_connect (G_OBJECT (data->dialog),
+			  "destroy",
+			  G_CALLBACK (destroy_cb),
+			  data);
+	g_signal_connect_swapped (G_OBJECT (GET_WIDGET ("cancel_button")),
+				  "clicked",
+				  G_CALLBACK (gtk_widget_destroy),
+				  data->dialog);
+	g_signal_connect (G_OBJECT (GET_WIDGET ("help_button")),
+			  "clicked",
+			  G_CALLBACK (help_button_clicked_cb),
+			  data);
+	g_signal_connect (G_OBJECT (GET_WIDGET ("start_button")),
+			  "clicked",
+			  G_CALLBACK (start_button_clicked_cb),
+			  data);
+
+	/* run dialog. */
+
+	gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (browser));
+	gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+	gtk_widget_show (data->dialog);
+}
diff --git a/extensions/catalogs/dlg-organize-files.h b/extensions/catalogs/dlg-organize-files.h
new file mode 100644
index 0000000..fba64c9
--- /dev/null
+++ b/extensions/catalogs/dlg-organize-files.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_ORGANIZE_FILES_H
+#define DLG_ORGANIZE_FILES_H
+
+#include <gthumb.h>
+
+void dlg_organize_files (GthBrowser *browser,
+			 GFile      *folder);
+
+#endif /* DLG_ORGANIZE_FILES_H */
diff --git a/extensions/catalogs/gth-catalog.c b/extensions/catalogs/gth-catalog.c
index b6c84f8..91bf1c3 100644
--- a/extensions/catalogs/gth-catalog.c
+++ b/extensions/catalogs/gth-catalog.c
@@ -392,6 +392,14 @@ gth_catalog_insert_file (GthCatalog *catalog,
 }
 
 
+void
+gth_catalog_append_file (GthCatalog *catalog,
+		         GFile      *file)
+{
+	catalog->priv->file_list = g_list_append (catalog->priv->file_list, g_file_dup (file));
+}
+
+
 int
 gth_catalog_remove_file (GthCatalog *catalog,
 			 GFile      *file)
diff --git a/extensions/catalogs/gth-catalog.h b/extensions/catalogs/gth-catalog.h
index a60bd94..170985c 100644
--- a/extensions/catalogs/gth-catalog.h
+++ b/extensions/catalogs/gth-catalog.h
@@ -93,6 +93,8 @@ GList *       gth_catalog_get_file_list   (GthCatalog           *catalog);
 gboolean      gth_catalog_insert_file     (GthCatalog           *catalog,
 					   int                   pos,
 					   GFile                *file);
+void          gth_catalog_append_file     (GthCatalog           *catalog,
+					   GFile                *file);
 int           gth_catalog_remove_file     (GthCatalog           *catalog,
 					   GFile                *file);
 void          gth_catalog_set_date        (GthCatalog           *catalog,
diff --git a/extensions/catalogs/gth-organize-task.c b/extensions/catalogs/gth-organize-task.c
new file mode 100644
index 0000000..e15afaf
--- /dev/null
+++ b/extensions/catalogs/gth-organize-task.c
@@ -0,0 +1,426 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gthumb.h>
+#include "gth-catalog.h"
+#include "gth-organize-task.h"
+
+
+#define GET_WIDGET(name) _gtk_builder_get_widget (self->priv->builder, (name))
+
+
+enum {
+	NAME_COLUMN = 0,
+	CARDINALITY_COLUMN,
+	CREATE_CATALOG_COLUMN
+};
+
+
+struct _GthOrganizeTaskPrivate
+{
+	GthBrowser     *browser;
+	GFile          *folder;
+	GthGroupPolicy  group_policy;
+	gboolean        recursive;
+	gboolean        create_singletons;
+	GthCatalog     *singletons_catalog;
+	GtkBuilder     *builder;
+	GtkListStore   *results_liststore;
+	GHashTable     *catalogs;
+};
+
+
+static gpointer parent_class = NULL;
+
+
+static void
+gth_organize_task_finalize (GObject *object)
+{
+	GthOrganizeTask *self;
+
+	self = GTH_ORGANIZE_TASK (object);
+
+	g_object_unref (self->priv->folder);
+	_g_object_unref (self->priv->singletons_catalog);
+	g_object_unref (self->priv->builder);
+	g_hash_table_destroy (self->priv->catalogs);
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+save_catalog (gpointer key,
+	      gpointer value,
+	      gpointer user_data)
+{
+	GthOrganizeTask *self = user_data;
+	GthCatalog      *catalog = value;
+	GFile           *gio_file;
+	char            *data;
+	gsize            size;
+	GError          *error = NULL;
+
+	if (! self->priv->create_singletons) {
+		GList *file_list = gth_catalog_get_file_list (catalog);
+		if ((file_list == NULL) || (file_list->next == NULL))
+			return;
+	}
+
+	gio_file = gth_catalog_file_to_gio_file (gth_catalog_get_file (catalog));
+	data = gth_catalog_to_data (catalog, &size);
+	if (! g_write_file (gio_file,
+			    FALSE,
+			    G_FILE_CREATE_NONE,
+			    data,
+			    size,
+			    gth_task_get_cancellable (GTH_TASK (self)),
+			    &error))
+	{
+		g_warning ("%s", error->message);
+		g_clear_error (&error);
+	}
+
+	g_free (data);
+	g_object_unref (gio_file);
+}
+
+
+static void
+create_singletons_catalog (gpointer key,
+		           gpointer value,
+		           gpointer user_data)
+{
+	GthOrganizeTask *self = user_data;
+	GthCatalog      *catalog = value;
+	GList           *file_list;
+
+	file_list = gth_catalog_get_file_list (catalog);
+	if ((file_list != NULL) && (file_list->next == NULL))
+		gth_catalog_append_file (self->priv->singletons_catalog, (GFile *) file_list->data);
+}
+
+
+static void
+done_func (GError   *error,
+	   gpointer  user_data)
+{
+	GthOrganizeTask *self = user_data;
+
+	if (error != NULL) {
+		gth_task_completed (GTH_TASK (self), error);
+		return;
+	}
+
+	g_hash_table_foreach (self->priv->catalogs, save_catalog, self);
+
+	if (! self->priv->create_singletons && (self->priv->singletons_catalog != NULL)) {
+		GFile *gio_file;
+		char  *data;
+		gsize  size;
+
+		g_hash_table_foreach (self->priv->catalogs, create_singletons_catalog, self);
+
+		gio_file = gth_catalog_file_to_gio_file (gth_catalog_get_file (self->priv->singletons_catalog));
+		data = gth_catalog_to_data (self->priv->singletons_catalog, &size);
+		g_write_file (gio_file,
+			      FALSE,
+			      G_FILE_CREATE_NONE,
+			      data,
+			      size,
+			      gth_task_get_cancellable (GTH_TASK (self)),
+			      NULL);
+
+		g_free (data);
+		g_object_unref (gio_file);
+	}
+
+	gtk_widget_destroy (GET_WIDGET ("organize_files_dialog"));
+	gth_task_completed (GTH_TASK (self), NULL);
+}
+
+
+static GFile *
+get_catalog_file (const char *display_name)
+{
+	GFile *base;
+	char  *name;
+	char  *name_escaped;
+	GFile *catalog_file;
+
+	base = g_file_new_for_uri ("catalog:///");
+	name = g_strdup_printf ("%s.catalog", display_name);
+	name_escaped = _g_utf8_replace (name, "/", ".");
+	catalog_file = g_file_get_child_for_display_name (base, name_escaped, NULL);
+
+	g_free (name_escaped);
+	g_free (name);
+	g_object_unref (base);
+
+	return catalog_file;
+}
+
+
+static void
+for_each_file_func (GFile     *file,
+		    GFileInfo *info,
+		    gpointer   user_data)
+{
+	GthOrganizeTask *self = user_data;
+	GthFileData     *file_data;
+	char            *key;
+	GTimeVal         timeval;
+	GthCatalog      *catalog;
+
+	if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
+		return;
+
+	key = NULL;
+	file_data = gth_file_data_new (file, info);
+	switch (self->priv->group_policy) {
+	case GTH_GROUP_POLICY_DIGITALIZED_DATE: {
+		GObject *metadata;
+
+		metadata = g_file_info_get_attribute_object (info, "Embedded::Image::DateTime");
+		if (metadata != NULL) {
+			if (_g_time_val_from_exif_date (gth_metadata_get_raw (GTH_METADATA (metadata)), &timeval))
+				key = g_strdup (_g_time_val_strftime (&timeval, "%x"));
+		}
+	}
+	break;
+
+	case GTH_GROUP_POLICY_MODIFIED_DATE:
+		timeval = *gth_file_data_get_modification_time (file_data);
+		key = g_strdup (_g_time_val_strftime (&timeval, "%x"));
+		break;
+	}
+
+	if (key == NULL)
+		return;
+
+	catalog = g_hash_table_lookup (self->priv->catalogs, key);
+	if (catalog == NULL) {
+		GthDateTime *date_time;
+		char        *exif_date;
+		GFile       *catalog_file;
+		GtkTreeIter  iter;
+
+		catalog = gth_catalog_new ();
+		date_time = gth_datetime_new ();
+		exif_date = _g_time_val_to_exif_date (&timeval);
+		gth_datetime_from_exif_date (date_time, exif_date);
+		gth_catalog_set_date (catalog, date_time);
+		catalog_file = get_catalog_file (key);
+		gth_catalog_set_file (catalog, catalog_file);
+		g_hash_table_insert (self->priv->catalogs, g_strdup (key), catalog);
+
+		gtk_list_store_append (self->priv->results_liststore, &iter);
+		gtk_list_store_set (self->priv->results_liststore, &iter,
+				    NAME_COLUMN, key,
+				    CARDINALITY_COLUMN, 0,
+				    CREATE_CATALOG_COLUMN, TRUE,
+				    -1);
+
+		g_object_unref (catalog_file);
+		g_free (exif_date);
+		gth_datetime_free (date_time);
+	}
+
+	if (catalog != NULL)
+		gth_catalog_append_file (catalog, file_data->file);
+
+	g_object_unref (file_data);
+	g_free (key);
+}
+
+
+static DirOp
+start_dir_func (GFile      *directory,
+		GFileInfo  *info,
+		GError    **error,
+		gpointer    user_data)
+{
+	GthOrganizeTask *self = user_data;
+	char            *uri;
+	char            *text;
+
+	uri = g_file_get_parse_name (directory);
+	text = g_strdup_printf ("Searching in %s", uri);
+	gtk_label_set_text (GTK_LABEL (GET_WIDGET ("progress_label")), text);
+
+	g_free (text);
+	g_free (uri);
+
+	return DIR_OP_CONTINUE;
+}
+
+
+static void
+gth_organize_task_exec (GthTask *base)
+{
+	GthOrganizeTask *self;
+	const char      *attributes;
+
+	self = GTH_ORGANIZE_TASK (base);
+
+	gtk_list_store_clear (self->priv->results_liststore);
+
+	switch (self->priv->group_policy) {
+	case GTH_GROUP_POLICY_DIGITALIZED_DATE:
+		attributes = "standard::name,standard::type,time::modified,time::modified-usec,Embedded::Image::DateTime";
+		break;
+	case GTH_GROUP_POLICY_MODIFIED_DATE:
+		attributes = "standard::name,standard::type,time::modified,time::modified-usec";
+		break;
+	}
+	g_directory_foreach_child (self->priv->folder,
+				   self->priv->recursive,
+				   TRUE,
+				   attributes,
+				   gth_task_get_cancellable (GTH_TASK (self)),
+				   start_dir_func,
+				   for_each_file_func,
+				   done_func,
+				   self);
+
+	gth_task_dialog (base, TRUE);
+	gtk_window_set_transient_for (GTK_WINDOW (GET_WIDGET ("organize_files_dialog")), GTK_WINDOW (self->priv->browser));
+	gtk_window_set_modal (GTK_WINDOW (GET_WIDGET ("organize_files_dialog")), TRUE);
+	gtk_widget_show (GET_WIDGET ("organize_files_dialog"));
+}
+
+
+static void
+gth_organize_task_cancelled (GthTask *base)
+{
+	/* FIXME */
+}
+
+
+static void
+gth_organize_task_class_init (GthOrganizeTaskClass *klass)
+{
+	GObjectClass *object_class;
+	GthTaskClass *task_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthOrganizeTaskPrivate));
+
+	object_class = (GObjectClass*) klass;
+	object_class->finalize = gth_organize_task_finalize;
+
+	task_class = (GthTaskClass*) klass;
+	task_class->exec = gth_organize_task_exec;
+	task_class->cancelled = gth_organize_task_cancelled;
+}
+
+
+static void
+gth_organize_task_init (GthOrganizeTask *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_ORGANIZE_TASK, GthOrganizeTaskPrivate);
+	self->priv->builder = _gtk_builder_new_from_file ("organize-files-task.ui", "catalogs");
+	self->priv->results_liststore = (GtkListStore *) gtk_builder_get_object (self->priv->builder, "results_liststore");
+	self->priv->catalogs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+
+GType
+gth_organize_task_get_type (void)
+{
+	static GType type = 0;
+
+	if (! type) {
+		GTypeInfo type_info = {
+			sizeof (GthOrganizeTaskClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_organize_task_class_init,
+			NULL,
+			NULL,
+			sizeof (GthOrganizeTask),
+			0,
+			(GInstanceInitFunc) gth_organize_task_init
+		};
+
+		type = g_type_register_static (GTH_TYPE_TASK,
+					       "GthOrganizeTask",
+					       &type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+GthTask *
+gth_organize_task_new (GthBrowser     *browser,
+		       GFile          *folder,
+		       GthGroupPolicy  group_policy)
+{
+	GthOrganizeTask *self;
+
+	self = (GthOrganizeTask *) g_object_new (GTH_TYPE_ORGANIZE_TASK, NULL);
+	self->priv->browser = browser;
+	self->priv->folder = g_file_dup (folder);
+	self->priv->group_policy = group_policy;
+
+	return (GthTask*) self;
+}
+
+
+void
+gth_organize_task_set_recursive (GthOrganizeTask *self,
+				 gboolean         recursive)
+{
+	self->priv->recursive = recursive;
+}
+
+
+void
+gth_organize_task_set_create_singletons (GthOrganizeTask *self,
+					 gboolean         create)
+{
+	self->priv->create_singletons = create;
+}
+
+
+void
+gth_organize_task_set_singletons_catalog (GthOrganizeTask *self,
+					  const char      *catalog_name)
+{
+	GFile *file;
+
+	g_object_unref (self->priv->singletons_catalog);
+	self->priv->singletons_catalog = NULL;
+	if (catalog_name == NULL)
+		return;
+
+	self->priv->singletons_catalog = gth_catalog_new ();
+	file = get_catalog_file (catalog_name);
+	gth_catalog_set_file (self->priv->singletons_catalog, file);
+
+	g_object_unref (file);
+}
diff --git a/extensions/catalogs/gth-organize-task.h b/extensions/catalogs/gth-organize-task.h
new file mode 100644
index 0000000..6b88f7e
--- /dev/null
+++ b/extensions/catalogs/gth-organize-task.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2009 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, write to the Free Software
+ *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTH_ORGANIZE_TASK_H
+#define GTH_ORGANIZE_TASK_H
+
+#include <glib-object.h>
+#include <gthumb.h>
+
+typedef enum {
+	GTH_GROUP_POLICY_DIGITALIZED_DATE,
+	GTH_GROUP_POLICY_MODIFIED_DATE
+} GthGroupPolicy;
+
+#define GTH_TYPE_ORGANIZE_TASK         (gth_organize_task_get_type ())
+#define GTH_ORGANIZE_TASK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_ORGANIZE_TASK, GthOrganizeTask))
+#define GTH_ORGANIZE_TASK_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTH_TYPE_ORGANIZE_TASK, GthOrganizeTaskClass))
+#define GTH_IS_ORGANIZE_TASK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTH_TYPE_ORGANIZE_TASK))
+#define GTH_IS_ORGANIZE_TASK_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTH_TYPE_ORGANIZE_TASK))
+#define GTH_ORGANIZE_TASK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTH_TYPE_ORGANIZE_TASK, GthOrganizeTaskClass))
+
+typedef struct _GthOrganizeTask         GthOrganizeTask;
+typedef struct _GthOrganizeTaskPrivate  GthOrganizeTaskPrivate;
+typedef struct _GthOrganizeTaskClass    GthOrganizeTaskClass;
+
+struct _GthOrganizeTask
+{
+	GthTask __parent;
+	GthOrganizeTaskPrivate *priv;
+};
+
+struct _GthOrganizeTaskClass
+{
+	GthTaskClass __parent_class;
+};
+
+GType       gth_organize_task_get_type                 (void) G_GNUC_CONST;
+GthTask *   gth_organize_task_new                      (GthBrowser      *browser,
+						        GFile           *folder,
+						        GthGroupPolicy   group_policy);
+void        gth_organize_task_set_recursive            (GthOrganizeTask *self,
+							gboolean         recursive);
+void        gth_organize_task_set_create_singletons    (GthOrganizeTask *self,
+						        gboolean         create);
+void        gth_organize_task_set_singletons_catalog   (GthOrganizeTask *self,
+							const char      *catalog_name);
+
+#endif /* GTH_ORGANIZE_TASK_H */
diff --git a/gthumb/gio-utils.c b/gthumb/gio-utils.c
index 7bf16cf..a947523 100644
--- a/gthumb/gio-utils.c
+++ b/gthumb/gio-utils.c
@@ -26,6 +26,7 @@
 #include <gio/gio.h>
 #include "gth-file-data.h"
 #include "gth-hook.h"
+#include "gth-metadata-provider.h"
 #include "gth-overwrite-dialog.h"
 #include "glib-utils.h"
 #include "gio-utils.h"
@@ -188,6 +189,9 @@ typedef struct {
 	GFileEnumerator      *enumerator;
 	GError               *error;
 	guint                 source_id;
+	gboolean              metadata_attributes;
+	GList                *children;
+	GList                *current_child;
 } ForEachChildData;
 
 
@@ -311,19 +315,115 @@ for_each_child_close_enumerator (GObject      *source_object,
 }
 
 
+static void for_each_child_next_files_ready (GObject      *source_object,
+					     GAsyncResult *result,
+					     gpointer      user_data);
+
+
+static void
+for_each_child_read_next_files (ForEachChildData *fec)
+{
+	_g_object_list_unref (fec->children);
+	fec->children = NULL;
+	g_file_enumerator_next_files_async (fec->enumerator,
+					    N_FILES_PER_REQUEST,
+					    G_PRIORITY_DEFAULT,
+					    fec->cancellable,
+					    for_each_child_next_files_ready,
+					    fec);
+}
+
+
+static void for_each_child_read_current_child_metadata (ForEachChildData *fec);
+
+
+static void
+for_each_child_read_next_child_metadata (ForEachChildData *fec)
+{
+	fec->current_child = fec->current_child->next;
+	if (fec->current_child != NULL)
+		for_each_child_read_current_child_metadata (fec);
+	else
+		for_each_child_read_next_files (fec);
+}
+
+
+static void
+for_each_child_compute_child (ForEachChildData *fec,
+			      GFile            *file,
+			      GFileInfo        *info)
+{
+	if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) {
+		char *id;
+
+		/* avoid to visit a directory more than ones */
+
+		id = g_strdup (g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE));
+		if (id == NULL)
+			id = g_file_get_uri (file);
+
+		if (g_hash_table_lookup (fec->already_visited, id) == NULL) {
+			g_hash_table_insert (fec->already_visited, g_strdup (id), GINT_TO_POINTER (1));
+			fec->to_visit = g_list_append (fec->to_visit, child_data_new (file, info));
+		}
+
+		g_free (id);
+	}
+
+	fec->for_each_file_func (file, info, fec->user_data);
+}
+
+
+static void
+for_each_child_metadata_ready_func (GList    *files,
+				    GError   *error,
+				    gpointer  user_data)
+{
+	ForEachChildData *fec = user_data;
+
+	if (error == NULL) {
+		GthFileData *child_data = files->data;
+		for_each_child_compute_child (fec, child_data->file, child_data->info);
+	}
+
+	for_each_child_read_next_child_metadata (fec);
+}
+
+
+static void
+for_each_child_read_current_child_metadata (ForEachChildData *fec)
+{
+	GFileInfo   *child_info = fec->current_child->data;
+	GFile       *child_file;
+	GList       *file_list;
+	GthFileData *child_data;
+
+	child_file = g_file_get_child (fec->current->file, g_file_info_get_name (child_info));
+	child_data = gth_file_data_new (child_file, child_info);
+	file_list = g_list_append (NULL, child_data);
+	_g_query_metadata_async  (file_list,
+				  fec->attributes,
+				  NULL, /* FIXME: cannot use fec->cancellable here */
+				  for_each_child_metadata_ready_func,
+				  fec);
+
+	_g_object_list_unref (file_list);
+	g_object_unref (child_file);
+}
+
+
 static void
 for_each_child_next_files_ready (GObject      *source_object,
 				 GAsyncResult *result,
 				 gpointer      user_data)
 {
 	ForEachChildData *fec = user_data;
-	GList            *children, *scan;
 
-	children = g_file_enumerator_next_files_finish (fec->enumerator,
-							result,
-							&(fec->error));
+	fec->children = g_file_enumerator_next_files_finish (fec->enumerator,
+							     result,
+							     &(fec->error));
 
-	if (children == NULL) {
+	if (fec->children == NULL) {
 		g_file_enumerator_close_async (fec->enumerator,
 					       G_PRIORITY_DEFAULT,
 					       fec->cancellable,
@@ -332,42 +432,28 @@ for_each_child_next_files_ready (GObject      *source_object,
 		return;
 	}
 
-	for (scan = children; scan; scan = scan->next) {
-		GFileInfo *child_info = scan->data;
-		GFile     *file;
-
-		file = g_file_get_child (fec->current->file, g_file_info_get_name (child_info));
-
-		if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) {
-			char *id;
-
-			/* avoid to visit a directory more than ones */
+	if (fec->metadata_attributes) {
+		fec->current_child = fec->children;
+		for_each_child_read_current_child_metadata (fec);
+	}
+	else {
+		GList *scan;
 
-			id = g_strdup (g_file_info_get_attribute_string (child_info, G_FILE_ATTRIBUTE_ID_FILE));
-			if (id == NULL)
-				id = g_file_get_uri (file);
+		for (scan = fec->children; scan; scan = scan->next) {
+			GFileInfo *child_info = scan->data;
+			GFile     *child_file;
 
-			if (g_hash_table_lookup (fec->already_visited, id) == NULL) {
-				g_hash_table_insert (fec->already_visited, g_strdup (id), GINT_TO_POINTER (1));
-				fec->to_visit = g_list_append (fec->to_visit, child_data_new (file, child_info));
-			}
+			child_file = g_file_get_child (fec->current->file, g_file_info_get_name (child_info));
+			for_each_child_compute_child (fec, child_file, child_info);
 
-			g_free (id);
+			g_object_unref (child_file);
 		}
 
-		fec->for_each_file_func (file, child_info, fec->user_data);
-
-		g_object_unref (file);
+		for_each_child_read_next_files (fec);
 	}
-
-	g_file_enumerator_next_files_async (fec->enumerator,
-					    N_FILES_PER_REQUEST,
-					    G_PRIORITY_DEFAULT,
-					    fec->cancellable,
-					    for_each_child_next_files_ready,
-					    fec);
 }
 
+
 static void
 for_each_child_ready (GObject      *source_object,
 		      GAsyncResult *result,
@@ -496,6 +582,7 @@ g_directory_foreach_child (GFile                *directory,
 						      g_str_equal,
 						      g_free,
 						      NULL);
+	fec->metadata_attributes = ! _g_file_attributes_matches_mask (fec->attributes, GIO_ATTRIBUTES);
 
 	g_file_query_info_async (fec->base_directory,
 				 fec->attributes,
diff --git a/gthumb/glib-utils.c b/gthumb/glib-utils.c
index 6cec036..1a7877f 100644
--- a/gthumb/glib-utils.c
+++ b/gthumb/glib-utils.c
@@ -2121,10 +2121,42 @@ attribute_matches_mask (const char *attribute,
 }
 
 
-static gboolean
+gboolean
 _g_file_attributes_matches_mask (const char *attributes,
 			         const char *mask)
 {
+	gboolean   matches_all_mask = TRUE;
+	char     **attributes_v;
+	char     **mask_v;
+	int        j;
+
+	attributes_v = g_strsplit (attributes, ",", -1);
+	mask_v = g_strsplit (mask, ",", -1);
+	for (j = 0; matches_all_mask && (attributes_v[j] != NULL); j++) {
+		gboolean matches = FALSE;
+		int      i;
+
+		for (i = 0; ! matches && (mask_v[i] != NULL); i++) {
+			matches = attribute_matches_mask (attributes_v[j], mask_v[i]);
+#if 0
+			g_print ("attr: %s <=> mask: %s : %d\n", attributes_v[j], mask_v[i], matches);
+#endif
+		}
+
+		matches_all_mask = matches;
+	}
+
+	g_strfreev (mask_v);
+	g_strfreev (attributes_v);
+
+	return matches_all_mask;
+}
+
+
+static gboolean
+_attributes_matches_mask (const char *attributes,
+			  const char *mask)
+{
 	gboolean   matches = FALSE;
 	char     **attributes_v;
 	char     **mask_v;
@@ -2154,7 +2186,7 @@ gboolean
 _g_file_attributes_matches (const char *attributes,
 			    const char *mask)
 {
-	return _g_file_attributes_matches_mask (attributes, mask) || _g_file_attributes_matches_mask (mask, attributes);
+	return _attributes_matches_mask (attributes, mask) || _attributes_matches_mask (mask, attributes);
 }
 
 
diff --git a/gthumb/glib-utils.h b/gthumb/glib-utils.h
index 65a5f70..94bb22e 100644
--- a/gthumb/glib-utils.h
+++ b/gthumb/glib-utils.h
@@ -53,6 +53,7 @@ G_BEGIN_DECLS
 #define GFILE_STANDARD_ATTRIBUTES (DEFINE_STANDARD_ATTRIBUTES(""))
 #define GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE (DEFINE_STANDARD_ATTRIBUTES(",standard::fast-content-type"))
 #define GFILE_STANDARD_ATTRIBUTES_WITH_CONTENT_TYPE (DEFINE_STANDARD_ATTRIBUTES(",standard::fast-content-type,standard::content-type"))
+#define GIO_ATTRIBUTES ("standard::*,etag::*,id::*,access::*,mountable::*,time::*,unix::*,dos::*,owner::*,thumbnail::*,filesystem::*,gvfs::*,xattr::*,xattr-sys::*,selinux::*")
 
 #define GNOME_COPIED_FILES (gdk_atom_intern_static_string ("x-special/gnome-copied-files"))
 #define IROUND(x) ((int)floor(((double)x) + 0.5))
@@ -249,6 +250,8 @@ GFile *         _g_file_append_prefix            (GFile      *file,
 						  const char *prefix);
 GFile *         _g_file_append_path              (GFile      *file,
 						  const char *path);
+gboolean        _g_file_attributes_matches_mask  (const char *attributes,
+						  const char *mask);
 gboolean        _g_file_attributes_matches       (const char *attributes,
 						  const char *mask);
 void            _g_file_info_swap_attributes     (GFileInfo  *info,
diff --git a/gthumb/gth-embedded-dialog.c b/gthumb/gth-embedded-dialog.c
index 3074bbb..1bb7946 100644
--- a/gthumb/gth-embedded-dialog.c
+++ b/gthumb/gth-embedded-dialog.c
@@ -120,6 +120,7 @@ gth_embedded_dialog_construct (GthEmbeddedDialog *self)
 	gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
 	gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
 	gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
+	gtk_misc_set_padding (GTK_MISC (primary_label), 0, 6);
 	GTK_WIDGET_SET_FLAGS (primary_label, GTK_CAN_FOCUS);
 	gtk_label_set_selectable (GTK_LABEL (primary_label), TRUE);
 	
diff --git a/gthumb/gth-main.c b/gthumb/gth-main.c
index 9f0d1c7..269f7b5 100644
--- a/gthumb/gth-main.c
+++ b/gthumb/gth-main.c
@@ -1262,7 +1262,7 @@ attribute_list_reaload_required (const char *old_attributes,
 	new_attributes_len = g_strv_length (new_attributes_v);
 
 	for (i = 0; i < new_attributes_len; i++) {
-		if (_g_file_attributes_matches (new_attributes_v[i], "standard::*,etag::*,id::*,access::*,mountable::*,time::*,unix::*,dos::*,owner::*,thumbnail::*,filesystem::*,gvfs::*,xattr::*,xattr-sys::*,selinux::*")) {
+		if (_g_file_attributes_matches (new_attributes_v[i], GIO_ATTRIBUTES)) {
 			g_free (new_attributes_v[i]);
 			new_attributes_v[i] = NULL;
 		}



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