[gthumb/ext: 5/20] allow to reorder the metadata in the thumbnail caption



commit 3794bbc29b0b6e12b0556b638e65c44477f95683
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Nov 21 00:16:04 2009 +0100

    allow to reorder the metadata in the thumbnail caption

 data/ui/preferences.ui        |  158 ++++++++-----
 gthumb/Makefile.am            |    2 +
 gthumb/dlg-preferences.c      |  185 ++-------------
 gthumb/gth-metadata-chooser.c |  521 +++++++++++++++++++++++++++++++++++++++++
 gthumb/gth-metadata-chooser.h |   63 +++++
 gthumb/gth-uri-list.c         |    4 +-
 6 files changed, 708 insertions(+), 225 deletions(-)
---
diff --git a/data/ui/preferences.ui b/data/ui/preferences.ui
index 37bfe3d..bdf5c5e 100644
--- a/data/ui/preferences.ui
+++ b/data/ui/preferences.ui
@@ -35,8 +35,10 @@
                         <child>
                           <object class="GtkLabel" id="label156">
                             <property name="visible">True</property>
-                            <property name="label" translatable="yes">&lt;b&gt;Interface&lt;/b&gt;</property>
-                            <property name="use_markup">True</property>
+                            <property name="label" translatable="yes">Appearance</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
                           </object>
                           <packing>
                             <property name="expand">False</property>
@@ -106,9 +108,11 @@
                         <child>
                           <object class="GtkLabel" id="label86">
                             <property name="visible">True</property>
-                            <property name="label" translatable="yes">&lt;b&gt;On startup:&lt;/b&gt;</property>
-                            <property name="use_markup">True</property>
+                            <property name="label" translatable="yes">On startup:</property>
                             <property name="justify">center</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
                           </object>
                           <packing>
                             <property name="expand">False</property>
@@ -185,8 +189,8 @@
                                 <child>
                                   <object class="GtkFileChooserButton" id="startup_dir_filechooserbutton">
                                     <property name="visible">True</property>
-                                    <property name="local_only">False</property>
                                     <property name="action">select-folder</property>
+                                    <property name="local_only">False</property>
                                     <property name="title" translatable="yes">Choose startup folder</property>
                                   </object>
                                   <packing>
@@ -249,9 +253,11 @@
                         <child>
                           <object class="GtkLabel" id="label183">
                             <property name="visible">True</property>
-                            <property name="label" translatable="yes">&lt;b&gt;Other&lt;/b&gt;</property>
-                            <property name="use_markup">True</property>
+                            <property name="label" translatable="yes">Other</property>
                             <property name="justify">center</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
                           </object>
                           <packing>
                             <property name="expand">False</property>
@@ -336,38 +342,95 @@
                 <property name="orientation">vertical</property>
                 <property name="spacing">12</property>
                 <child>
-                  <object class="GtkVBox" id="vbox30">
+                  <object class="GtkVBox" id="vbox5">
                     <property name="visible">True</property>
                     <property name="orientation">vertical</property>
                     <property name="spacing">6</property>
                     <child>
-                      <object class="GtkCheckButton" id="slow_mime_type_checkbutton">
-                        <property name="label" translatable="yes">D_etermine image type from content (slower)</property>
+                      <object class="GtkHBox" id="hbox2">
                         <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>
+                        <property name="spacing">5</property>
+                        <child>
+                          <object class="GtkLabel" id="label4">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Caption</property>
+                            <property name="justify">center</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </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="position">0</property>
                       </packing>
                     </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment7">
+                        <property name="visible">True</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkScrolledWindow" id="caption_scrolledwindow">
+                            <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>
+                              <placeholder/>
+                            </child>
+                          </object>
+                        </child>
+                      </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="GtkFrame" id="frame1">
+                  <object class="GtkVBox" id="vbox2">
                     <property name="visible">True</property>
-                    <property name="label_xalign">0</property>
-                    <property name="shadow_type">none</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">6</property>
                     <child>
-                      <object class="GtkAlignment" id="alignment1">
+                      <object class="GtkHBox" id="hbox1">
+                        <property name="visible">True</property>
+                        <property name="spacing">5</property>
+                        <child>
+                          <object class="GtkLabel" id="label3">
+                            <property name="visible">True</property>
+                            <property name="label" translatable="yes">Other</property>
+                            <property name="justify">center</property>
+                            <attributes>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </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="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment6">
                         <property name="visible">True</property>
-                        <property name="top_padding">6</property>
                         <property name="left_padding">12</property>
                         <child>
                           <object class="GtkVBox" id="vbox1">
@@ -375,14 +438,14 @@
                             <property name="orientation">vertical</property>
                             <property name="spacing">6</property>
                             <child>
-                              <object class="GtkHBox" id="hbox1">
+                              <object class="GtkHBox" id="hbox3">
                                 <property name="visible">True</property>
                                 <property name="spacing">6</property>
                                 <child>
                                   <object class="GtkLabel" id="size_label">
                                     <property name="visible">True</property>
                                     <property name="xalign">0</property>
-                                    <property name="label" translatable="yes">_Size:</property>
+                                    <property name="label" translatable="yes">Thumbnail _size:</property>
                                     <property name="use_underline">True</property>
                                     <property name="justify">center</property>
                                   </object>
@@ -405,42 +468,17 @@
                                 </child>
                               </object>
                               <packing>
-                                <property name="expand">False</property>
                                 <property name="position">0</property>
                               </packing>
                             </child>
                             <child>
-                              <object class="GtkVBox" id="vbox2">
+                              <object class="GtkCheckButton" id="slow_mime_type_checkbutton">
+                                <property name="label" translatable="yes">D_etermine image type from content (slower)</property>
                                 <property name="visible">True</property>
-                                <property name="orientation">vertical</property>
-                                <child>
-                                  <object class="GtkLabel" id="caption_label">
-                                    <property name="visible">True</property>
-                                    <property name="xalign">0</property>
-                                    <property name="label" translatable="yes">C_aption:</property>
-                                    <property name="use_underline">True</property>
-                                  </object>
-                                  <packing>
-                                    <property name="expand">False</property>
-                                    <property name="position">0</property>
-                                  </packing>
-                                </child>
-                                <child>
-                                  <object class="GtkScrolledWindow" id="caption_scrolledwindow">
-                                    <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>
-                                      <placeholder/>
-                                    </child>
-                                  </object>
-                                  <packing>
-                                    <property name="position">1</property>
-                                  </packing>
-                                </child>
+                                <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="position">1</property>
@@ -449,19 +487,13 @@
                           </object>
                         </child>
                       </object>
-                    </child>
-                    <child type="label">
-                      <object class="GtkLabel" id="label2">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Thumbnail</property>
-                        <property name="use_markup">True</property>
-                        <attributes>
-                          <attribute name="weight" value="bold"/>
-                        </attributes>
-                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
                     </child>
                   </object>
                   <packing>
+                    <property name="expand">False</property>
                     <property name="position">1</property>
                   </packing>
                 </child>
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index d9ab7d7..91d0b70 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -71,6 +71,7 @@ PUBLIC_HEADER_FILES = 					\
 	gth-location-chooser.h				\
 	gth-main.h					\
 	gth-metadata.h					\
+	gth-metadata-chooser.h				\
 	gth-metadata-provider.h				\
 	gth-monitor.h					\
 	gth-multipage.h					\
@@ -188,6 +189,7 @@ gthumb_SOURCES = 					\
 	gth-main-default-tests.c			\
 	gth-main-default-types.c			\
 	gth-metadata.c					\
+	gth-metadata-chooser.c				\
 	gth-metadata-provider.c				\
 	gth-metadata-provider-file.c			\
 	gth-monitor.c					\
diff --git a/gthumb/dlg-preferences.c b/gthumb/dlg-preferences.c
index c2c850c..406f4d5 100644
--- a/gthumb/dlg-preferences.c
+++ b/gthumb/dlg-preferences.c
@@ -28,6 +28,7 @@
 #include "gth-enum-types.h"
 #include "gth-file-source-vfs.h"
 #include "gth-main.h"
+#include "gth-metadata-chooser.h"
 #include "gth-preferences.h"
 #include "gtk-utils.h"
 #include "glib-utils.h"
@@ -36,12 +37,6 @@
 
 #define GET_WIDGET(name) _gtk_builder_get_widget (data->builder, (name))
 
-enum {
-	CAPTION_COLUMN_NAME,
-	CAPTION_COLUMN_ID,
-	CAPTION_COLUMN_USED,
-	CAPTION_COLUMNS
-};
 
 typedef struct {
 	GthBrowser *browser;
@@ -49,7 +44,7 @@ typedef struct {
 	GtkWidget  *dialog;
 	GtkWidget  *toolbar_style_combobox;
 	GtkWidget  *thumbnail_size_combobox;
-	GtkWidget  *thumbnail_caption_list_view;
+	GtkWidget  *thumbnail_caption_chooser;
 } DialogData;
 
 static int thumb_size[] = { 48, 64, 85, 95, 112, 128, 164, 200, 256 };
@@ -78,10 +73,8 @@ destroy_cb (GtkWidget *widget,
 }
 
 
-/* called when the "apply" button is clicked. */
 static void
-apply_cb (GtkWidget  *widget,
-	  DialogData *data)
+apply_changes (DialogData *data)
 {
 	/* Startup dir. */
 
@@ -101,20 +94,18 @@ apply_cb (GtkWidget  *widget,
 }
 
 
-/* called when the "close" button is clicked. */
 static void
-close_cb (GtkWidget  *widget,
-	  DialogData *data)
+close_button_clicked_cb (GtkWidget  *widget,
+			 DialogData *data)
 {
-	apply_cb (widget, data);
+	apply_changes (data);
 	gtk_widget_destroy (data->dialog);
 }
 
 
-/* called when the "help" button is clicked. */
 static void
-help_cb (GtkWidget  *widget,
-	 DialogData *data)
+help_button_clicked_cb (GtkWidget  *widget,
+			DialogData *data)
 {
 	show_help_dialog (GTK_WINDOW (data->dialog), "preferences");
 }
@@ -184,107 +175,15 @@ fast_file_type_toggled_cb (GtkToggleButton *button,
 
 
 static void
-update_caption_from_list_view (DialogData *data)
-{
-	GtkTreeModel *tree_model;
-	GtkTreeIter   iter;
-	GString      *attributes;
-
-	attributes = g_string_new ("");
-	tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (data->thumbnail_caption_list_view));
-	if (gtk_tree_model_get_iter_first (tree_model, &iter)) {
-		do {
-			gboolean  used;
-			char     *attribute_id;
-
-			gtk_tree_model_get (tree_model, &iter,
-					    CAPTION_COLUMN_ID, &attribute_id,
-					    CAPTION_COLUMN_USED, &used,
-					    -1);
-
-			if (used) {
-				if (attributes->len > 0)
-					g_string_append (attributes, ",");
-				g_string_append (attributes, attribute_id);
-			}
-
-			g_free (attribute_id);
-		}
-		while (gtk_tree_model_iter_next (tree_model, &iter));
-	}
-
-	if (attributes->len > 0)
-		eel_gconf_set_string (PREF_THUMBNAIL_CAPTION, attributes->str);
-	else
-		eel_gconf_set_string (PREF_THUMBNAIL_CAPTION, "none");
-
-	g_string_free (attributes, TRUE);
-}
-
-
-static void
-cell_renderer_toggle_toggled_cb (GtkCellRendererToggle *cell_renderer,
-				 char                  *path,
-                                 gpointer               user_data)
-{
-	DialogData   *data = user_data;
-	GtkTreePath  *tpath;
-	GtkTreeModel *tree_model;
-	GtkTreeIter   iter;
-
-	tpath = gtk_tree_path_new_from_string (path);
-	if (tpath == NULL)
-		return;
-
-	tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (data->thumbnail_caption_list_view));
-	if (gtk_tree_model_get_iter (tree_model, &iter, tpath)) {
-		gboolean used;
-
-		gtk_tree_model_get (tree_model, &iter,
-				    CAPTION_COLUMN_USED, &used,
-				    -1);
-		gtk_list_store_set (GTK_LIST_STORE (tree_model), &iter,
-				    CAPTION_COLUMN_USED, ! used,
-				    -1);
-	}
-
-	update_caption_from_list_view (data);
-
-	gtk_tree_path_free (tpath);
-}
-
-
-static void
-caption_list_view_add_columns (GtkTreeView *treeview,
-			       DialogData  *data)
+thumbnail_caption_chooser_changed_cb (GthMetadataChooser *chooser,
+				      DialogData         *data)
 {
-	GtkTreeViewColumn *column;
-	GtkCellRenderer   *renderer;
+	char *attributes;
 
-	/* the checkbox column */
+	attributes = gth_metadata_chooser_get_selection (chooser);
+	eel_gconf_set_string (PREF_THUMBNAIL_CAPTION, attributes);
 
-	column = gtk_tree_view_column_new ();
-	renderer = gtk_cell_renderer_toggle_new ();
-	g_signal_connect (renderer,
-			  "toggled",
-			  G_CALLBACK (cell_renderer_toggle_toggled_cb),
-			  data);
-	gtk_tree_view_column_pack_start (column, renderer, FALSE);
-	gtk_tree_view_column_set_attributes (column, renderer,
-					     "active", CAPTION_COLUMN_USED,
-					     NULL);
-	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
-
-	/* the name column. */
-
-	column = gtk_tree_view_column_new ();
-	renderer = gtk_cell_renderer_text_new ();
-        gtk_tree_view_column_pack_start (column, renderer, TRUE);
-        gtk_tree_view_column_set_attributes (column, renderer,
-                                             "text", CAPTION_COLUMN_NAME,
-                                             NULL);
-        gtk_tree_view_column_set_expand (column, TRUE);
-	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+	g_free (attributes);
 }
 
 
@@ -294,12 +193,7 @@ dlg_preferences (GthBrowser *browser)
 	DialogData       *data;
 	char             *startup_location;
 	GthFileSource    *file_source;
-	GtkListStore     *caption_store;
-	GtkTreeIter       iter;
 	char             *current_caption;
-	char            **attributes_v;
-	char            **active_attributes_v;
-	int               i;
 
 	if (gth_browser_get_dialog (browser, "preferences") != NULL) {
 		gtk_window_present (GTK_WINDOW (gth_browser_get_dialog (browser, "preferences")));
@@ -321,46 +215,14 @@ dlg_preferences (GthBrowser *browser)
 	data->toolbar_style_combobox = _gtk_combo_box_new_with_texts (_("System settings"), _("Text below icons"), _("Text beside icons"), _("Icons only"), _("Text only"), NULL);
 	data->thumbnail_size_combobox = _gtk_combo_box_new_with_texts (_("48"), _("64"), _("85"), _("95"), _("112"), _("128"), _("164"), _("200"), _("256"), NULL);
 
-	caption_store = gtk_list_store_new (CAPTION_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
-	data->thumbnail_caption_list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (caption_store));
-	g_object_unref (caption_store);
-
-	gtk_tree_view_set_reorderable (GTK_TREE_VIEW (data->thumbnail_caption_list_view), FALSE);
-        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (data->thumbnail_caption_list_view), FALSE);
-
-	caption_list_view_add_columns (GTK_TREE_VIEW (data->thumbnail_caption_list_view), data);
-
-	gtk_widget_show (data->thumbnail_caption_list_view);
-	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("caption_scrolledwindow")), data->thumbnail_caption_list_view);
-
 	/* caption list */
 
+	data->thumbnail_caption_chooser = gth_metadata_chooser_new (GTH_METADATA_ALLOW_IN_FILE_LIST);
+	gtk_widget_show (data->thumbnail_caption_chooser);
+	gtk_container_add (GTK_CONTAINER (GET_WIDGET ("caption_scrolledwindow")), data->thumbnail_caption_chooser);
+
 	current_caption = eel_gconf_get_string (PREF_THUMBNAIL_CAPTION, DEFAULT_THUMBNAIL_CAPTION);
-	active_attributes_v = g_strsplit (current_caption, ",", -1);
-	attributes_v = gth_main_get_metadata_attributes ("*");
-	for (i = 0; attributes_v[i] != NULL; i++) {
-		GthMetadataInfo *info;
-		const char      *name;
-		gboolean         used;
-
-		info = gth_main_get_metadata_info (attributes_v[i]);
-		if ((info == NULL) || ((info->flags & GTH_METADATA_ALLOW_IN_FILE_LIST) == 0))
-			continue;
-
-		gtk_list_store_append (caption_store, &iter);
-
-		name = info->display_name;
-		if (name == NULL)
-			name = info->id;
-		used = _g_strv_find (active_attributes_v, info->id) != -1;
-		gtk_list_store_set (caption_store, &iter,
-				    CAPTION_COLUMN_NAME, name,
-				    CAPTION_COLUMN_ID, info->id,
-				    CAPTION_COLUMN_USED, used,
-				    -1);
-	}
-	g_strfreev (attributes_v);
-	g_strfreev (active_attributes_v);
+	gth_metadata_chooser_set_selection (GTH_METADATA_CHOOSER (data->thumbnail_caption_chooser), current_caption);
 	g_free (current_caption);
 
 	gtk_widget_show (data->toolbar_style_combobox);
@@ -369,7 +231,6 @@ dlg_preferences (GthBrowser *browser)
 	gtk_box_pack_start (GTK_BOX (GET_WIDGET ("toolbar_style_combobox_box")), data->toolbar_style_combobox, FALSE, FALSE, 0);
 	gtk_box_pack_start (GTK_BOX (GET_WIDGET ("thumbnail_size_box")), data->thumbnail_size_combobox, FALSE, FALSE, 0);
 
-	gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("caption_label")), data->thumbnail_caption_list_view);
 	gtk_label_set_mnemonic_widget (GTK_LABEL (GET_WIDGET ("size_label")), data->thumbnail_size_combobox);
 
 	/* * general */
@@ -423,11 +284,11 @@ dlg_preferences (GthBrowser *browser)
 			  data);
 	g_signal_connect (G_OBJECT (GET_WIDGET ("close_button")),
 			  "clicked",
-			  G_CALLBACK (close_cb),
+			  G_CALLBACK (close_button_clicked_cb),
 			  data);
 	g_signal_connect (G_OBJECT (GET_WIDGET ("help_button")),
 			  "clicked",
-			  G_CALLBACK (help_cb),
+			  G_CALLBACK (help_button_clicked_cb),
 			  data);
 
 	/* general */
@@ -459,6 +320,10 @@ dlg_preferences (GthBrowser *browser)
 			  "toggled",
 			  G_CALLBACK (fast_file_type_toggled_cb),
 			  data);
+	g_signal_connect (G_OBJECT (data->thumbnail_caption_chooser),
+			  "changed",
+			  G_CALLBACK (thumbnail_caption_chooser_changed_cb),
+			  data);
 
 	/* run dialog. */
 
diff --git a/gthumb/gth-metadata-chooser.c b/gthumb/gth-metadata-chooser.c
new file mode 100644
index 0000000..f650432
--- /dev/null
+++ b/gthumb/gth-metadata-chooser.c
@@ -0,0 +1,521 @@
+/* -*- 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 <glib/gi18n.h>
+#include "glib-utils.h"
+#include "gth-main.h"
+#include "gth-metadata-chooser.h"
+
+
+#define ORDER_CHANGED_DELAY 250
+
+
+enum {
+	NAME_COLUMN,
+	ID_COLUMN,
+	USED_COLUMN,
+	SEPARATOR_COLUMN,
+	N_COLUMNS
+};
+
+
+/* Signals */
+enum {
+        CHANGED,
+        LAST_SIGNAL
+};
+
+
+struct _GthMetadataChooserPrivate {
+	GthMetadataFlags allowed_flags;
+	gulong           row_inserted_event;
+	gulong           row_deleted_event;
+	guint            changed_id;
+};
+
+
+static gpointer parent_class = NULL;
+static guint gth_metadata_chooser_signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gth_metadata_chooser_finalize (GObject *obj)
+{
+/*	GthMetadataChooser *self;
+
+	self = GTH_METADATA_CHOOSER (obj); FIXME: delete if not needed */
+
+	G_OBJECT_CLASS (parent_class)->finalize (obj);
+}
+
+
+static void
+gth_metadata_chooser_class_init (GthMetadataChooserClass *klass)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GthMetadataChooserPrivate));
+
+	object_class = (GObjectClass*) (klass);
+	object_class->finalize = gth_metadata_chooser_finalize;
+
+	/* signals */
+
+	gth_metadata_chooser_signals[CHANGED] =
+                g_signal_new ("changed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GthMetadataChooserClass, changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
+}
+
+
+/* -- gth_metadata_chooser_reorder_list -- */
+
+
+typedef struct {
+	int       pos;
+	char     *name;
+	char     *id;
+	gboolean  used;
+	gboolean  separator;
+} ItemData;
+
+
+static void
+item_data_free (ItemData *item_data)
+{
+	g_free (item_data->name);
+	g_free (item_data->id);
+	g_free (item_data);
+}
+
+
+static int
+item_data_compare_func (gconstpointer a,
+			gconstpointer b)
+{
+	ItemData *item_a = (ItemData *) a;
+	ItemData *item_b = (ItemData *) b;
+
+	if (item_a->separator) {
+		if (item_b->used)
+			return 1;
+		else
+			return -1;
+	}
+
+	if (item_b->separator) {
+		if (item_a->used)
+			return -1;
+		else
+			return 1;
+	}
+
+	if (item_a->used == item_b->used) {
+		if (item_a->used) {
+			/* keep the user defined order for the used items */
+			if (item_a->pos < item_b->pos)
+				return -1;
+			else if (item_a->pos > item_b->pos)
+				return 1;
+			else
+				return 0;
+		}
+		else {
+			/* sort by name for the unused items */
+			return g_utf8_collate (item_a->name, item_b->name);
+		}
+	}
+	else if (item_a->used)
+		return -1;
+	else
+		return 1;
+}
+
+
+static gboolean
+gth_metadata_chooser_reorder_list (GthMetadataChooser *self)
+{
+	gboolean      changed = FALSE;
+	GtkTreeModel *model;
+	GtkTreeIter   iter;
+	GList        *list;
+	int           pos;
+	int          *new_order;
+	GList        *scan;
+
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+	if (! gtk_tree_model_get_iter_first (model, &iter))
+		return FALSE;
+
+	list = NULL;
+	pos = 0;
+	do {
+		ItemData *item_data;
+
+		item_data = g_new0 (ItemData, 1);
+		item_data->pos = pos;
+		gtk_tree_model_get (model, &iter,
+				    NAME_COLUMN, &item_data->name,
+				    ID_COLUMN, &item_data->id,
+				    USED_COLUMN, &item_data->used,
+				    SEPARATOR_COLUMN, &item_data->separator,
+				    -1);
+		list = g_list_prepend (list, item_data);
+		pos++;
+	}
+	while (gtk_tree_model_iter_next (model, &iter));
+
+	list = g_list_sort (list, item_data_compare_func);
+	new_order = g_new (int, g_list_length (list));
+	for (pos = 0, scan = list; scan; pos++, scan = scan->next) {
+		ItemData *item_data = scan->data;
+
+		if (pos != item_data->pos)
+			changed = TRUE;
+		new_order[pos] = item_data->pos;
+	}
+	gtk_list_store_reorder (GTK_LIST_STORE (model), new_order);
+
+	g_free (new_order);
+	g_list_foreach (list, (GFunc) item_data_free, NULL);
+	g_list_free (list);
+
+	return changed;
+}
+
+
+static void
+cell_renderer_toggle_toggled_cb (GtkCellRendererToggle *cell_renderer,
+				 char                  *path,
+                                 gpointer               user_data)
+{
+	GthMetadataChooser *self = user_data;
+	GtkTreePath        *tree_path;
+	GtkTreeModel       *tree_model;
+	GtkTreeIter         iter;
+
+	tree_path = gtk_tree_path_new_from_string (path);
+	if (tree_path == NULL)
+		return;
+
+	tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+	if (gtk_tree_model_get_iter (tree_model, &iter, tree_path)) {
+		gboolean used;
+
+		gtk_tree_model_get (tree_model, &iter,
+				    USED_COLUMN, &used,
+				    -1);
+		gtk_list_store_set (GTK_LIST_STORE (tree_model), &iter,
+				    USED_COLUMN, ! used,
+				    -1);
+		gth_metadata_chooser_reorder_list (self);
+		g_signal_emit (self, gth_metadata_chooser_signals[CHANGED], 0);
+	}
+
+	gtk_tree_path_free (tree_path);
+}
+
+
+static gboolean
+row_separator_func (GtkTreeModel *model,
+                    GtkTreeIter  *iter,
+                    gpointer      data)
+{
+	gboolean separator;
+
+	gtk_tree_model_get (model, iter, SEPARATOR_COLUMN, &separator, -1);
+
+	return separator;
+}
+
+
+
+static gboolean
+order_changed (gpointer user_data)
+{
+	GthMetadataChooser *self = user_data;
+
+	if (self->priv->changed_id != 0)
+		g_source_remove (self->priv->changed_id);
+	self->priv->changed_id = 0;
+
+	gth_metadata_chooser_reorder_list (self);
+	g_signal_emit (self, gth_metadata_chooser_signals[CHANGED], 0);
+
+	return FALSE;
+}
+
+
+static void
+row_deleted_cb (GtkTreeModel *tree_model,
+		GtkTreePath  *path,
+		gpointer      user_data)
+{
+	GthMetadataChooser *self = user_data;
+
+	if (self->priv->changed_id != 0)
+		g_source_remove (self->priv->changed_id);
+	self->priv->changed_id = gdk_threads_add_timeout (ORDER_CHANGED_DELAY, order_changed, self);
+}
+
+
+static void
+row_inserted_cb (GtkTreeModel *tree_model,
+		 GtkTreePath  *path,
+		 GtkTreeIter  *iter,
+		 gpointer      user_data)
+{
+	GthMetadataChooser *self = user_data;
+
+	if (self->priv->changed_id != 0)
+		g_source_remove (self->priv->changed_id);
+	self->priv->changed_id = gdk_threads_add_timeout (ORDER_CHANGED_DELAY, order_changed, self);
+}
+
+
+static void
+gth_metadata_chooser_instance_init (GthMetadataChooser *self)
+{
+	GtkListStore      *store;
+	GtkTreeViewColumn *column;
+	GtkCellRenderer   *renderer;
+
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_METADATA_CHOOSER, GthMetadataChooserPrivate);
+
+	/* the list view */
+
+	store = gtk_list_store_new (N_COLUMNS,
+				    G_TYPE_STRING,
+				    G_TYPE_STRING,
+				    G_TYPE_BOOLEAN,
+				    G_TYPE_BOOLEAN);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (self), GTK_TREE_MODEL (store));
+	g_object_unref (store);
+	gtk_tree_view_set_reorderable (GTK_TREE_VIEW (self), TRUE);
+        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (self), FALSE);
+        gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (self),
+       					      row_separator_func,
+       					      self,
+       					      NULL);
+
+        self->priv->row_inserted_event = g_signal_connect (store,
+							   "row-inserted",
+							   G_CALLBACK (row_inserted_cb),
+							   self);
+        self->priv->row_deleted_event = g_signal_connect (store,
+							  "row-deleted",
+							  G_CALLBACK (row_deleted_cb),
+							  self);
+
+	/* the checkbox column */
+
+	column = gtk_tree_view_column_new ();
+	renderer = gtk_cell_renderer_toggle_new ();
+	g_signal_connect (renderer,
+			  "toggled",
+			  G_CALLBACK (cell_renderer_toggle_toggled_cb),
+			  self);
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+					     "active", USED_COLUMN,
+					     NULL);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
+
+	/* the name column. */
+
+	column = gtk_tree_view_column_new ();
+	renderer = gtk_cell_renderer_text_new ();
+        gtk_tree_view_column_pack_start (column, renderer, TRUE);
+        gtk_tree_view_column_set_attributes (column, renderer,
+                                             "text", NAME_COLUMN,
+                                             NULL);
+        gtk_tree_view_column_set_expand (column, TRUE);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (self), column);
+}
+
+
+GType
+gth_metadata_chooser_get_type (void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo g_define_type_info = {
+			sizeof (GthMetadataChooserClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_metadata_chooser_class_init,
+			NULL,
+			NULL,
+			sizeof (GthMetadataChooser),
+			0,
+			(GInstanceInitFunc) gth_metadata_chooser_instance_init,
+			NULL
+		};
+		type = g_type_register_static (GTK_TYPE_TREE_VIEW,
+					       "GthMetadataChooser",
+					       &g_define_type_info,
+					       0);
+	}
+
+	return type;
+}
+
+
+GtkWidget *
+gth_metadata_chooser_new (GthMetadataFlags allowed_flags)
+{
+	GthMetadataChooser *self;
+
+	self = g_object_new (GTH_TYPE_METADATA_CHOOSER, NULL);
+	self->priv->allowed_flags = allowed_flags;
+	gth_metadata_chooser_set_selection (self, "");
+
+	return (GtkWidget *) self;
+}
+
+
+void
+gth_metadata_chooser_set_selection (GthMetadataChooser *self,
+				    char               *ids)
+{
+	GtkListStore  *store;
+	char         **attributes_v;
+	char         **ids_v;
+	int            i;
+	GtkTreeIter    iter;
+
+	store = (GtkListStore *) gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+
+	g_signal_handler_block (store, self->priv->row_inserted_event);
+	g_signal_handler_block (store, self->priv->row_deleted_event);
+
+	gtk_list_store_clear (store);
+
+	attributes_v = gth_main_get_metadata_attributes ("*");
+	ids_v = g_strsplit (ids, ",", -1);
+	for (i = 0; ids_v[i] != NULL; i++) {
+		int              idx;
+		GthMetadataInfo *info;
+		const char      *name;
+
+		idx = _g_strv_find (attributes_v, ids_v[i]);
+		if (idx < 0)
+			continue;
+
+		info = gth_main_get_metadata_info (attributes_v[idx]);
+		if ((info == NULL) || ((info->flags & self->priv->allowed_flags) == 0))
+			continue;
+
+		name = info->display_name;
+		if (name == NULL)
+			name = info->id;
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter,
+				    NAME_COLUMN, name,
+				    ID_COLUMN, info->id,
+				    USED_COLUMN, TRUE,
+				    SEPARATOR_COLUMN, FALSE,
+				    -1);
+	}
+
+	gtk_list_store_append (store, &iter);
+	gtk_list_store_set (store, &iter,
+			    SEPARATOR_COLUMN, TRUE,
+			    -1);
+
+	for (i = 0; attributes_v[i] != NULL; i++) {
+		GtkTreeIter      iter;
+		GthMetadataInfo *info;
+		const char      *name;
+
+		if (_g_strv_find (ids_v, attributes_v[i]) >= 0)
+			continue;
+
+		info = gth_main_get_metadata_info (attributes_v[i]);
+		if ((info == NULL) || ((info->flags & self->priv->allowed_flags) == 0))
+			continue;
+
+		name = info->display_name;
+		if (name == NULL)
+			name = info->id;
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter,
+				    NAME_COLUMN, name,
+				    ID_COLUMN, info->id,
+				    USED_COLUMN, FALSE,
+				    SEPARATOR_COLUMN, FALSE,
+				    -1);
+	}
+
+	g_signal_handler_unblock (store, self->priv->row_inserted_event);
+	g_signal_handler_unblock (store, self->priv->row_deleted_event);
+
+	g_strfreev (attributes_v);
+	g_strfreev (ids_v);
+}
+
+
+char *
+gth_metadata_chooser_get_selection (GthMetadataChooser *self)
+{
+	GString      *selection;
+	GtkTreeModel *model;
+	GtkTreeIter   iter;
+
+	selection = g_string_new ("");
+	model = gtk_tree_view_get_model (GTK_TREE_VIEW (self));
+	if (gtk_tree_model_get_iter_first (model, &iter)) {
+		do {
+			gboolean  used;
+			char     *id;
+
+			gtk_tree_model_get (model, &iter,
+					    ID_COLUMN, &id,
+					    USED_COLUMN, &used,
+					    -1);
+
+			if (used) {
+				if (selection->len > 0)
+					g_string_append (selection, ",");
+				g_string_append (selection, id);
+			}
+
+			g_free (id);
+		}
+		while (gtk_tree_model_iter_next (model, &iter));
+	}
+
+	if (selection->len == 0)
+		g_string_append (selection, "none");
+
+	return g_string_free (selection, FALSE);
+}
diff --git a/gthumb/gth-metadata-chooser.h b/gthumb/gth-metadata-chooser.h
new file mode 100644
index 0000000..e51d1e1
--- /dev/null
+++ b/gthumb/gth-metadata-chooser.h
@@ -0,0 +1,63 @@
+/* -*- 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 GTH_METADATA_CHOOSER_H
+#define GTH_METADATA_CHOOSER_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_METADATA_CHOOSER            (gth_metadata_chooser_get_type ())
+#define GTH_METADATA_CHOOSER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_METADATA_CHOOSER, GthMetadataChooser))
+#define GTH_METADATA_CHOOSER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_METADATA_CHOOSER, GthMetadataChooserClass))
+#define GTH_IS_METADATA_CHOOSER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_METADATA_CHOOSER))
+#define GTH_IS_METADATA_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_METADATA_CHOOSER))
+#define GTH_METADATA_CHOOSER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTH_TYPE_METADATA_CHOOSER, GthMetadataChooserClass))
+
+typedef struct _GthMetadataChooser GthMetadataChooser;
+typedef struct _GthMetadataChooserClass GthMetadataChooserClass;
+typedef struct _GthMetadataChooserPrivate GthMetadataChooserPrivate;
+
+struct _GthMetadataChooser {
+	GtkTreeView parent_instance;
+	GthMetadataChooserPrivate *priv;
+};
+
+struct _GthMetadataChooserClass {
+	GtkTreeViewClass parent_class;
+
+	/*< signals >*/
+
+	void  (*changed)  (GthMetadataChooser *self);
+};
+
+GType        gth_metadata_chooser_get_type      (void);
+GtkWidget *  gth_metadata_chooser_new           (GthMetadataFlags    allowed_flags);
+void         gth_metadata_chooser_set_selection (GthMetadataChooser *self,
+						 char               *ids);
+char *       gth_metadata_chooser_get_selection (GthMetadataChooser *self);
+
+G_END_DECLS
+
+#endif /* GTH_METADATA_CHOOSER_H */
+
diff --git a/gthumb/gth-uri-list.c b/gthumb/gth-uri-list.c
index 97c80cb..c33cae0 100644
--- a/gthumb/gth-uri-list.c
+++ b/gthumb/gth-uri-list.c
@@ -111,7 +111,7 @@ row_deleted_cb (GtkTreeModel *tree_model,
 
 	if (uri_list->priv->changed_id != 0)
 		g_source_remove (uri_list->priv->changed_id);
-	uri_list->priv->changed_id = g_timeout_add (ORDER_CHANGED_DELAY, order_changed, uri_list);
+	uri_list->priv->changed_id = gdk_threads_add_timeout (ORDER_CHANGED_DELAY, order_changed, uri_list);
 }
 
 
@@ -125,7 +125,7 @@ row_inserted_cb (GtkTreeModel *tree_model,
 
 	if (uri_list->priv->changed_id != 0)
 		g_source_remove (uri_list->priv->changed_id);
-	uri_list->priv->changed_id = g_timeout_add (ORDER_CHANGED_DELAY, order_changed, uri_list);
+	uri_list->priv->changed_id = gdk_threads_add_timeout (ORDER_CHANGED_DELAY, order_changed, uri_list);
 }
 
 



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