[gthumb] Added missing files, oops



commit 621a80d3468817176f072546b9f166305c16a2db
Author: Michael J. Chudobiak <mjc avtechpulse com>
Date:   Tue May 19 07:31:36 2009 -0400

    Added missing files, oops
---
 src/dlg-tags.c                 |  901 ++++++++++++++++++++++++++++++++++++++++
 src/dlg-tags.h                 |   40 ++
 src/gth-tag-selection-dialog.c |  479 +++++++++++++++++++++
 src/gth-tag-selection-dialog.h |   62 +++
 4 files changed, 1482 insertions(+), 0 deletions(-)

diff --git a/src/dlg-tags.c b/src/dlg-tags.c
new file mode 100644
index 0000000..4b0ebc3
--- /dev/null
+++ b/src/dlg-tags.c
@@ -0,0 +1,901 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2001, 2003, 2004 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 <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <libgnomeui/gnome-help.h>
+#include <glade/glade.h>
+
+#include "typedefs.h"
+#include "file-utils.h"
+#include "preferences.h"
+#include "main.h"
+#include "gth-utils.h"
+#include "gth-window.h"
+#include "gtk-utils.h"
+#include "gtkcellrendererthreestates.h"
+#include "gth-file-view.h"
+#include "comments.h"
+#include "dlg-tags.h"
+
+
+typedef void (*SaveFunc) (GList *file_list, gpointer data);
+
+
+#define DIALOG_NAME "tags"
+#define GLADE_FILE "gthumb_comments.glade"
+#define TAG_SEPARATOR ";"
+
+
+enum {
+	IS_EDITABLE_COLUMN,
+	HAS_THIRD_STATE_COLUMN,
+	USE_TAG_COLUMN,
+	TAG_COLUMN,
+	NUM_COLUMNS
+};
+
+
+typedef struct {
+	GladeXML      *gui;
+	GList         *file_list;
+	GList         *default_tags_list;
+	GList        **add_list;
+	GList        **remove_list;
+	SaveFunc       save_func;
+	gpointer       save_data;
+	DoneFunc       done_func;
+	gpointer       done_data;
+
+	GthWindow     *window;
+	GtkWidget     *dialog;
+
+	GtkWidget     *c_ok_button;
+	GtkWidget     *keyword_entry;
+	GtkWidget     *add_key_button;
+	GtkWidget     *remove_key_button;
+	GtkWidget     *keywords_list_view;
+	GtkListStore  *keywords_list_model;
+
+	CommentData   *original_cdata;
+} DialogData;
+
+
+
+static void
+free_dialog_data (DialogData *data)
+{
+	if (data->file_list != NULL) {
+		g_list_foreach (data->file_list, (GFunc) g_free, NULL);
+		g_list_free (data->file_list);
+		data->file_list = NULL;
+	}
+
+	if (data->original_cdata != NULL) {
+		comment_data_free (data->original_cdata);
+		data->original_cdata = NULL;
+	}
+}
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget  *widget,
+	    DialogData *data)
+{
+	if (data->window != NULL)
+		gth_window_set_tags_dlg (data->window, NULL);
+
+	g_object_unref (data->gui);
+	free_dialog_data (data);
+	path_list_free (data->default_tags_list);
+
+	if (data->done_func)
+		(*data->done_func) (data->done_data);
+
+	g_free (data);
+}
+
+
+static gboolean
+unrealize_cb (GtkWidget  *widget,
+	  DialogData *data)
+{
+	pref_util_save_window_geometry (GTK_WINDOW (widget), DIALOG_NAME);
+	return FALSE;
+}
+
+
+static void
+update_tag_entry (DialogData *data)
+{
+	GtkTreeIter   iter;
+	GtkTreeModel *model = GTK_TREE_MODEL (data->keywords_list_model);
+	GString      *tags;
+
+	if (! gtk_tree_model_get_iter_first (model, &iter)) {
+		gtk_entry_set_text (GTK_ENTRY (data->keyword_entry), "");
+		return;
+	}
+
+	tags = g_string_new (NULL);
+	do {
+		guint use_tag;
+		gtk_tree_model_get (model, &iter, USE_TAG_COLUMN, &use_tag, -1);
+		if (use_tag == 1) {
+			char *utf8_name;
+
+			gtk_tree_model_get (model, &iter,
+					    TAG_COLUMN, &utf8_name,
+					    -1);
+
+			if (tags->len > 0)
+				tags = g_string_append (tags, TAG_SEPARATOR " ");
+			tags = g_string_append (tags, utf8_name);
+
+			g_free (utf8_name);
+		}
+	} while (gtk_tree_model_iter_next (model, &iter));
+
+	gtk_entry_set_text (GTK_ENTRY (data->keyword_entry), tags->str);
+	g_string_free (tags, TRUE);
+}
+
+
+/* if state_to_get == -1 get all tags. */
+static GList *
+get_tags (GtkListStore *store,
+          guint         state_to_get)
+{
+	GtkTreeModel *model = GTK_TREE_MODEL (store);
+	GList        *list = NULL;
+	GtkTreeIter   iter;
+
+	if (! gtk_tree_model_get_iter_first (model, &iter))
+		return NULL;
+
+	do {
+		guint  state;
+		char  *utf8_name;
+
+		gtk_tree_model_get (model,
+				    &iter,
+				    USE_TAG_COLUMN, &state,
+				    TAG_COLUMN, &utf8_name,
+				    -1);
+		if ((state_to_get == -1) || (state == state_to_get))
+			list = g_list_prepend (list, utf8_name);
+		else
+			g_free (utf8_name);
+
+	} while (gtk_tree_model_iter_next (model, &iter));
+
+	return g_list_reverse (list);
+}
+
+
+static gboolean
+tag_present (DialogData *data,
+             char       *tag)
+{
+	GList    *all_tags;
+	GList    *scan;
+	gboolean  present = FALSE;
+
+	all_tags = get_tags (data->keywords_list_model, -1);
+	for (scan = all_tags; scan; scan = scan->next) {
+		char *tag2 = scan->data;
+		if (strcmp (tag2, tag) == 0)
+			present = TRUE;
+	}
+	path_list_free (all_tags);
+
+	return present;
+}
+
+
+static gboolean
+tag_name_is_valid (const char *name)
+{
+	gboolean valid = TRUE;
+	if (strchr (name, ',') != NULL)
+		valid = FALSE;
+	return valid;
+}
+
+
+/* called when the "add tag" button is pressed. */
+static void
+add_tag_cb (GtkWidget  *widget,
+            DialogData *data)
+{
+	GtkTreeIter  iter;
+	char        *new_tag;
+
+	new_tag = _gtk_request_dialog_run (GTK_WINDOW (data->dialog),
+						GTK_DIALOG_MODAL,
+						_("Enter the new tag name"),
+						"",
+						1024,
+						GTK_STOCK_CANCEL,
+						_("C_reate"));
+
+	if (new_tag == NULL)
+		return;
+
+	if (! tag_name_is_valid (new_tag)) {
+		_gtk_error_dialog_run (GTK_WINDOW (data->dialog),
+				       _("The name \"%s\" is not valid because it contains the character \",\". " "Please use a different name."),
+				       new_tag);
+
+	} else if (tag_present (data, new_tag)) {
+		_gtk_error_dialog_run (GTK_WINDOW (data->dialog),
+				       _("The tag \"%s\" is already present. Please use a different name."),
+				       new_tag);
+
+	} else {
+		gtk_list_store_append (data->keywords_list_model, &iter);
+		gtk_list_store_set (data->keywords_list_model, &iter,
+				    IS_EDITABLE_COLUMN, TRUE,
+				    USE_TAG_COLUMN, 1,
+				    TAG_COLUMN, new_tag,
+				    -1);
+		update_tag_entry (data);
+	}
+
+	g_free (new_tag);
+}
+
+
+/* called when the "remove tag" button is pressed. */
+static void
+remove_tag_cb (GtkWidget *widget,
+		    DialogData *data)
+{
+	GtkTreeSelection *selection;
+	GtkTreeIter       iter;
+
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->keywords_list_view));
+	if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+		return;
+
+	gtk_list_store_remove (data->keywords_list_model, &iter);
+	update_tag_entry (data);
+}
+
+
+static void
+save_tags (DialogData *data)
+{
+	Bookmarks *tags;
+	GList     *scan;
+	GList     *cat_list;
+
+	cat_list = get_tags (data->keywords_list_model, -1);
+	tags = bookmarks_new (RC_TAGS_FILE);
+	bookmarks_set_max_lines (tags, -1);
+
+	for (scan = cat_list; scan; scan = scan->next) {
+		char *tag = scan->data;
+		bookmarks_add (tags, tag, TRUE, TRUE);
+	}
+
+	bookmarks_write_to_disk (tags);
+
+	path_list_free (cat_list);
+	bookmarks_free (tags);
+}
+
+
+/* called when the "cancel" button is pressed. */
+static void
+cancel_clicked_cb (GtkWidget  *widget,
+		   DialogData *data)
+{
+	save_tags (data);
+	gtk_widget_destroy (data->dialog);
+}
+
+
+/* called when the "ok" button is pressed. */
+static void
+ok_clicked_cb (GtkWidget  *widget,
+	       DialogData *data)
+{
+	save_tags (data);
+
+	if (data->add_list != NULL) {
+		if (*(data->add_list) != NULL)
+			path_list_free (*(data->add_list));
+		*(data->add_list) = get_tags (data->keywords_list_model, 1);
+	}
+
+	if (data->remove_list != NULL) {
+		if (*(data->remove_list) != NULL)
+			path_list_free (*(data->remove_list));
+		*(data->remove_list) = get_tags (data->keywords_list_model, 0);
+	}
+
+	if (data->save_func) {
+		(*data->save_func) (data->file_list, data->save_data);
+		if (data->window != NULL) {
+			gth_window_update_current_image_metadata (data->window);
+			dlg_tags_close (data->dialog);
+		}
+	} else
+		gtk_widget_destroy (data->dialog);
+}
+
+
+/* called when the "help" button in the search dialog is pressed. */
+static void
+help_cb (GtkWidget  *widget,
+	 DialogData *data)
+{
+	gthumb_display_help (GTK_WINDOW (data->dialog), "gthumb-tags");
+}
+
+
+static void
+use_tag_toggled (GtkCellRendererThreeStates *cell,
+		      gchar                      *path_string,
+		      gpointer                    callback_data)
+{
+	DialogData   *data  = callback_data;
+	GtkTreeModel *model = GTK_TREE_MODEL (data->keywords_list_model);
+	GtkTreeIter   iter;
+	GtkTreePath  *path = gtk_tree_path_new_from_string (path_string);
+	guint         value;
+
+	gtk_tree_model_get_iter (model, &iter, path);
+	value = gtk_cell_renderer_three_states_get_next_state (cell);
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter, USE_TAG_COLUMN, value, -1);
+
+	gtk_tree_path_free (path);
+	update_tag_entry (data);
+}
+
+
+static void
+add_saved_tags (DialogData *data)
+{
+	Bookmarks *tags;
+	GList     *scan;
+	GList     *cat_list;
+
+	cat_list = get_tags (data->keywords_list_model, -1);
+	tags = bookmarks_new (RC_TAGS_FILE);
+	bookmarks_load_from_disk (tags);
+
+	for (scan = tags->list; scan; scan = scan->next) {
+		GtkTreeIter  iter;
+		GList       *scan2;
+		gboolean     found = FALSE;
+		char        *tag1 = scan->data;
+
+		for (scan2 = cat_list; scan2 && !found; scan2 = scan2->next) {
+			char *tag2 = scan2->data;
+			if (strcmp (tag1, tag2) == 0)
+				found = TRUE;
+		}
+
+		if (found)
+			continue;
+
+		gtk_list_store_append (data->keywords_list_model, &iter);
+
+		gtk_list_store_set (data->keywords_list_model, &iter,
+				    IS_EDITABLE_COLUMN, FALSE,
+				    USE_TAG_COLUMN, 0,
+				    TAG_COLUMN, tag1,
+				    -1);
+	}
+
+	bookmarks_free (tags);
+	path_list_free (cat_list);
+}
+
+
+static gboolean
+key_in_list (GList      *list,
+	     const char *key)
+{
+	GList *scan;
+
+	for (scan = list; scan; scan = scan->next)
+		if (strcmp (scan->data, key) == 0)
+			return TRUE;
+	return FALSE;
+}
+
+
+static int
+name_column_sort_func (GtkTreeModel *model,
+                       GtkTreeIter  *a,
+                       GtkTreeIter  *b,
+                       gpointer      user_data)
+{
+        char *tag1, *tag2;
+	int   result;
+
+        gtk_tree_model_get (model, a, TAG_COLUMN, &tag1, -1);
+        gtk_tree_model_get (model, b, TAG_COLUMN, &tag2, -1);
+
+	result = g_utf8_collate (tag1, tag2);
+
+	g_free (tag1);
+	g_free (tag2);
+
+        return result;
+}
+
+
+static gboolean
+keyword_equal_func (GtkTreeModel *model,
+		    gint          column,
+		    const gchar  *key,
+		    GtkTreeIter  *iter,
+		    gpointer      search_data)
+{
+	char *cell;
+	gtk_tree_model_get (model, iter, column, &cell, -1);
+	return g_strcasecmp (key, cell) > 0;
+}
+
+
+static GtkWidget*
+dlg_tags_common (GtkWindow     *parent,
+                 GthWindow     *window,
+                 GList         *file_list,
+                 GList         *default_tags_list,
+                 GList        **add_tags_list,
+                 GList        **remove_tags_list,
+                 SaveFunc       save_func,
+                 gpointer       save_data,
+                 DoneFunc       done_func,
+                 gpointer       done_data,
+                 gboolean       modal)
+{
+	DialogData        *data;
+	GtkWidget         *btn_ok;
+	GtkWidget         *btn_cancel;
+	GtkWidget         *btn_help;
+	GtkCellRenderer   *renderer;
+	GtkTreeViewColumn *column;
+
+
+	data = g_new (DialogData, 1);
+
+	data->window = window;
+	data->default_tags_list = path_list_dup (default_tags_list);
+	data->add_list = add_tags_list;
+	data->remove_list = remove_tags_list;
+	data->save_func = save_func;
+	data->save_data = save_data;
+	data->done_func = done_func;
+	data->done_data = done_data;
+	data->original_cdata = NULL;
+
+	data->gui = glade_xml_new (GTHUMB_GLADEDIR "/" GLADE_FILE , NULL, NULL);
+        if (!data->gui) {
+		g_free (data);
+                g_warning ("Could not find " GLADE_FILE "\n");
+                return NULL;
+        }
+
+	if (file_list != NULL)
+		data->file_list = path_list_dup (file_list);
+	else
+		data->file_list = NULL;
+
+	/* Get the widgets. */
+
+	data->dialog = glade_xml_get_widget (data->gui, "tags_dialog");
+
+	data->keyword_entry = glade_xml_get_widget (data->gui, "c_keyword_entry");
+	data->add_key_button = glade_xml_get_widget (data->gui, "c_add_key_button");
+	data->remove_key_button = glade_xml_get_widget (data->gui, "c_remove_key_button");
+	data->keywords_list_view = glade_xml_get_widget (data->gui, "c_keywords_treeview");
+
+	data->c_ok_button = btn_ok = glade_xml_get_widget (data->gui, "c_ok_button");
+	btn_cancel = glade_xml_get_widget (data->gui, "c_cancel_button");
+	btn_help = glade_xml_get_widget (data->gui, "c_help_button");
+
+	/* Set widgets data. */
+
+	g_object_set_data (G_OBJECT (data->dialog), "dialog_data", data);
+
+	data->keywords_list_model = gtk_list_store_new (NUM_COLUMNS,
+							G_TYPE_BOOLEAN,
+							G_TYPE_BOOLEAN,
+							G_TYPE_UINT,
+							G_TYPE_STRING);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (data->keywords_list_view),
+				 GTK_TREE_MODEL (data->keywords_list_model));
+	g_object_unref (data->keywords_list_model);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (data->keywords_list_view), FALSE);
+
+	gtk_tree_view_set_search_column (GTK_TREE_VIEW (data->keywords_list_view),
+					 TAG_COLUMN);
+	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (data->keywords_list_view),
+					     keyword_equal_func,
+					     NULL,
+					     NULL);
+
+	column = gtk_tree_view_column_new ();
+
+	renderer = gtk_cell_renderer_three_states_new ();
+	g_signal_connect (G_OBJECT (renderer),
+			  "toggled",
+			  G_CALLBACK (use_tag_toggled),
+			  data);
+	gtk_tree_view_column_pack_start (column, renderer, FALSE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+					     "state", USE_TAG_COLUMN,
+					     "has_third_state", HAS_THIRD_STATE_COLUMN,
+					     NULL);
+
+	renderer = gtk_cell_renderer_text_new ();
+	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	gtk_tree_view_column_set_attributes (column, renderer,
+					     "text", TAG_COLUMN,
+					     "editable", IS_EDITABLE_COLUMN,
+					     NULL);
+
+	gtk_tree_view_column_set_sort_column_id (column, 0);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (data->keywords_list_view),
+				     column);
+
+	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (data->keywords_list_model), name_column_sort_func, NULL, NULL);
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->keywords_list_model), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+
+	/* Set the signals handlers. */
+
+	g_signal_connect (G_OBJECT (data->dialog),
+			  "destroy",
+			  G_CALLBACK (destroy_cb),
+			  data);
+	g_signal_connect (G_OBJECT (data->dialog),
+			  "unrealize",
+			  G_CALLBACK (unrealize_cb),
+			  data);
+	g_signal_connect (G_OBJECT (btn_ok),
+			  "clicked",
+			  G_CALLBACK (ok_clicked_cb),
+			  data);
+	g_signal_connect (G_OBJECT (btn_cancel),
+			  "clicked",
+			  G_CALLBACK (cancel_clicked_cb),
+			  data);
+	g_signal_connect (G_OBJECT (btn_help),
+			  "clicked",
+			  G_CALLBACK (help_cb),
+			  data);
+
+	g_signal_connect (G_OBJECT (data->add_key_button),
+			  "clicked",
+			  G_CALLBACK (add_tag_cb),
+			  data);
+	g_signal_connect (G_OBJECT (data->remove_key_button),
+			  "clicked",
+			  G_CALLBACK (remove_tag_cb),
+			  data);
+
+	/* run dialog. */
+
+	if (parent != NULL)
+		gtk_window_set_transient_for (GTK_WINDOW (data->dialog), parent);
+
+	gtk_window_set_modal (GTK_WINDOW (data->dialog), modal);
+	pref_util_restore_window_geometry (GTK_WINDOW (data->dialog), DIALOG_NAME);
+	dlg_tags_update (data->dialog);
+
+	gtk_widget_grab_focus (data->keywords_list_view);
+
+	return data->dialog;
+}
+
+
+/**/
+
+
+void
+dlg_choose_tags (GtkWindow     *parent,
+                 GList         *file_list,
+                 GList         *default_tags_list,
+                 GList        **add_tags_list,
+                 GList        **remove_tags_list,
+                 DoneFunc       done_func,
+                 gpointer       done_data)
+{
+	dlg_tags_common (parent,
+                         NULL,
+                         file_list,
+                         default_tags_list,
+                         add_tags_list,
+                         remove_tags_list,
+                         NULL,
+                         NULL,
+                         done_func,
+                         done_data,
+                         TRUE);
+}
+
+
+/**/
+
+
+typedef struct {
+	GthWindow     *window;
+	GList         *add_list;
+	GList         *remove_list;
+} DlgTagsData;
+
+
+static void
+dlg_tags__done (gpointer data)
+{
+	DlgTagsData *dcdata = data;
+
+	path_list_free (dcdata->add_list);
+	dcdata->add_list = NULL;
+
+	path_list_free (dcdata->remove_list);
+	dcdata->remove_list = NULL;
+
+	g_free (dcdata);
+}
+
+
+static void
+dlg_tags__save (GList    *file_list,
+                gpointer  data)
+{
+	DlgTagsData *dcdata = data;
+	GList             *scan;
+
+	for (scan = file_list; scan; scan = scan->next) {
+		const char  *filename = scan->data;
+		CommentData *cdata;
+		GList       *scan2;
+
+		cdata = comments_load_comment (filename, TRUE);
+		if (cdata == NULL)
+			cdata = comment_data_new ();
+		else
+			for (scan2 = dcdata->remove_list; scan2; scan2 = scan2->next) {
+				const char *k = scan2->data;
+				comment_data_remove_keyword (cdata, k);
+			}
+
+		for (scan2 = dcdata->add_list; scan2; scan2 = scan2->next) {
+			const char *k = scan2->data;
+			comment_data_add_keyword (cdata, k);
+		}
+
+		comments_save_tags (filename, cdata);
+		comment_data_free (cdata);
+	}
+
+	path_list_free (dcdata->add_list);
+	dcdata->add_list = NULL;
+
+	path_list_free (dcdata->remove_list);
+	dcdata->remove_list = NULL;
+}
+
+
+GtkWidget*
+dlg_tags_new (GthWindow *window)
+{
+	GtkWidget         *current_dlg;
+	DlgTagsData *dcdata;
+
+	current_dlg = gth_window_get_tags_dlg (window);
+	if (current_dlg != NULL) {
+		gtk_window_present (GTK_WINDOW (current_dlg));
+		return current_dlg;
+	}
+
+	dcdata = g_new0 (DlgTagsData, 1);
+	dcdata->window = window;
+	dcdata->add_list = NULL;
+	dcdata->remove_list = NULL;
+
+	return dlg_tags_common (GTK_WINDOW (window),
+                                window,
+                                NULL,
+                                NULL,
+                                &(dcdata->add_list),
+                                &(dcdata->remove_list),
+                                dlg_tags__save,
+                                dcdata,
+                                dlg_tags__done,
+                                dcdata,
+                                FALSE);
+}
+
+
+void
+dlg_tags_update (GtkWidget *dlg)
+{
+	DialogData    *data;
+	CommentData   *cdata = NULL;
+	GList         *scan;
+	GList         *other_keys = NULL;
+        GSList        *tmp1, *tmp2;
+
+
+	g_return_if_fail (dlg != NULL);
+
+	data = g_object_get_data (G_OBJECT (dlg), "dialog_data");
+	g_return_if_fail (data != NULL);
+
+	gtk_list_store_clear (data->keywords_list_model);
+
+	/**/
+
+	if (data->window != NULL) {
+		free_dialog_data (data);
+		data->file_list = gth_window_get_file_list_selection (data->window);
+	} else if (data->original_cdata != NULL) {
+		comment_data_free (data->original_cdata);
+		data->original_cdata = NULL;
+	}
+
+	if (data->file_list != NULL) {
+		char *first_image = data->file_list->data;
+		data->original_cdata = cdata = comments_load_comment (first_image, TRUE);
+	}
+
+	if (cdata != NULL) {
+		comment_data_free_comment (cdata);
+
+		/* remove a tag if it is not in all comments. */
+		for (scan = data->file_list->next; scan; scan = scan->next) {
+			CommentData *scan_cdata;
+
+			scan_cdata = comments_load_comment (scan->data, TRUE);
+
+			if (scan_cdata == NULL) {
+				comment_data_free_keywords (cdata);
+				break;
+			}
+
+			for (tmp1 = cdata->keywords; tmp1; tmp1 = g_slist_next (tmp1)) {
+				char     *k1 = tmp1->data;
+				gboolean  found = FALSE;
+
+				for (tmp2 =  scan_cdata->keywords; tmp2; tmp2 = g_slist_next (tmp2)) {
+					char *k2 = tmp2->data;
+					if (strcmp (k1, k2) == 0) {
+						found = TRUE;
+						break;
+					}
+				}
+
+				if (!found)
+					comment_data_remove_keyword (cdata, k1);
+			}
+
+			comment_data_free (scan_cdata);
+		}
+	}
+
+	for (scan = data->file_list; scan; scan = scan->next) {
+		CommentData *scan_cdata;
+
+		scan_cdata = comments_load_comment (scan->data, TRUE);
+
+		if (scan_cdata == NULL)
+			continue;
+
+                for (tmp2 =  scan_cdata->keywords; tmp2; tmp2 = g_slist_next (tmp2)) {
+                        char *k2 = tmp2->data;
+			gboolean  found = FALSE;
+
+			if (cdata != NULL)
+                                for (tmp1 = cdata->keywords; tmp1; tmp1 = g_slist_next (tmp1)) {
+                                        char     *k1 = tmp1->data;
+					if (strcmp (k1, k2) == 0) {
+						found = TRUE;
+						break;
+					}
+				}
+
+			if (! found && ! key_in_list (other_keys, k2))
+				other_keys = g_list_prepend (other_keys,
+							     g_strdup (k2));
+		}
+		comment_data_free (scan_cdata);
+	}
+
+	if (cdata != NULL) {
+                for (tmp1 = cdata->keywords; tmp1; tmp1 = g_slist_next (tmp1)) {
+			GtkTreeIter  iter;
+
+			gtk_list_store_append (data->keywords_list_model,
+					       &iter);
+
+			gtk_list_store_set (data->keywords_list_model, &iter,
+					    IS_EDITABLE_COLUMN, FALSE,
+					    HAS_THIRD_STATE_COLUMN, FALSE,
+					    USE_TAG_COLUMN, 1,
+					    TAG_COLUMN, tmp1->data,
+					    -1);
+		}
+	}
+
+	for (scan = data->default_tags_list; scan; scan = scan->next) {
+		char        *keyword = scan->data;
+		GtkTreeIter  iter;
+
+		gtk_list_store_append (data->keywords_list_model,
+				       &iter);
+
+		gtk_list_store_set (data->keywords_list_model, &iter,
+				    IS_EDITABLE_COLUMN, FALSE,
+				    HAS_THIRD_STATE_COLUMN, FALSE,
+				    USE_TAG_COLUMN, 1,
+				    TAG_COLUMN, keyword,
+				    -1);
+	}
+
+	for (scan = other_keys; scan; scan = scan->next) {
+		char        *keyword = scan->data;
+		GtkTreeIter  iter;
+
+		gtk_list_store_append (data->keywords_list_model,
+				       &iter);
+
+		gtk_list_store_set (data->keywords_list_model, &iter,
+				    IS_EDITABLE_COLUMN, FALSE,
+				    HAS_THIRD_STATE_COLUMN, TRUE,
+				    USE_TAG_COLUMN, 2,
+				    TAG_COLUMN, keyword,
+				    -1);
+	}
+
+	if (other_keys != NULL)
+		path_list_free (other_keys);
+
+	add_saved_tags (data);
+	update_tag_entry (data);
+}
+
+
+void
+dlg_tags_close  (GtkWidget *dlg)
+{
+	DialogData *data;
+
+	g_return_if_fail (dlg != NULL);
+
+	data = g_object_get_data (G_OBJECT (dlg), "dialog_data");
+	g_return_if_fail (data != NULL);
+
+	gtk_widget_destroy (dlg);
+}
diff --git a/src/dlg-tags.h b/src/dlg-tags.h
new file mode 100644
index 0000000..ffd7c8c
--- /dev/null
+++ b/src/dlg-tags.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2001 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_TAGS_H
+#define DLG_TAGS_H
+
+#include "typedefs.h"
+#include "gth-window.h"
+
+void        dlg_choose_tags    (GtkWindow     *parent,
+                                GList         *file_list,
+                                GList         *default_tags_list,
+                                GList        **add_tags_list,
+                                GList        **remove_tags_list,
+                                DoneFunc       done_func,
+                                gpointer       done_data);
+GtkWidget*  dlg_tags_new       (GthWindow     *window);
+void        dlg_tags_update    (GtkWidget     *dlg);
+void        dlg_tags_close     (GtkWidget     *dlg);
+
+#endif /* DLG_TAGS_H */
diff --git a/src/gth-tag-selection-dialog.c b/src/gth-tag-selection-dialog.c
new file mode 100644
index 0000000..5508435
--- /dev/null
+++ b/src/gth-tag-selection-dialog.c
@@ -0,0 +1,479 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2007 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 <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include "typedefs.h"
+#include "glib-utils.h"
+#include "file-utils.h"
+#include "bookmarks.h"
+#include "gth-tag-selection-dialog.h"
+
+enum {
+        RESPONSE,
+        LAST_SIGNAL
+};
+
+
+enum {
+	C_USE_TAG_COLUMN,
+	C_TAG_COLUMN,
+	C_NUM_COLUMNS
+};
+
+
+#define TAG_SEPARATOR_C   ';'
+#define TAG_SEPARATOR_STR ";"
+#define SEARCH_GLADE_FILE     "gthumb_search.glade"
+#define DEFAULT_DIALOG_WIDTH  540
+#define DEFAULT_DIALOG_HEIGHT 480
+
+
+struct _GthTagSelectionPrivate
+{
+	char           *tags;
+	gboolean        match_all;
+	gboolean        ok_clicked;
+
+	GladeXML       *gui;
+
+	GtkWidget      *dialog;
+	GtkWidget      *c_tags_entry;
+	GtkWidget      *c_tags_treeview;
+	GtkWidget      *c_ok_button;
+	GtkWidget      *c_cancel_button;
+	GtkWidget      *s_at_least_one_cat_radiobutton;
+	GtkWidget      *s_all_cat_radiobutton;
+
+	GtkListStore   *c_tags_list_model;
+};
+
+
+static GObjectClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+gth_tag_selection_finalize (GObject *object)
+{
+	GthTagSelection *tag_sel;
+
+	tag_sel = GTH_TAG_SELECTION (object);
+
+	if (tag_sel->priv != NULL) {
+
+		if (tag_sel->priv->dialog != NULL)
+			g_signal_handlers_disconnect_by_data (tag_sel->priv->dialog, tag_sel);
+
+		g_free (tag_sel->priv->tags);
+		tag_sel->priv->tags = NULL;
+
+		if (tag_sel->priv->gui != NULL) {
+			g_object_unref (tag_sel->priv->gui);
+			tag_sel->priv->gui = NULL;
+		}
+
+		g_free (tag_sel->priv);
+		tag_sel->priv = NULL;
+	}
+
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+gth_tag_selection_class_init (GthTagSelectionClass *class)
+{
+	GObjectClass *object_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	object_class = (GObjectClass*) class;
+
+	object_class->finalize = gth_tag_selection_finalize;
+
+	signals[RESPONSE] =
+		g_signal_new ("response",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GthTagSelectionClass, response),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__INT,
+                              G_TYPE_NONE,
+                              1, G_TYPE_INT);
+}
+
+
+static void
+gth_tag_selection_init (GthTagSelection *tag_sel)
+{
+	tag_sel->priv = g_new0 (GthTagSelectionPrivate, 1);
+}
+
+
+static void
+update_tag_entry (GthTagSelection *tag_sel)
+{
+	GthTagSelectionPrivate *priv = tag_sel->priv;
+	GtkTreeIter   iter;
+	GtkTreeModel *model = GTK_TREE_MODEL (priv->c_tags_list_model);
+	GString      *tags;
+
+	if (! gtk_tree_model_get_iter_first (model, &iter)) {
+		gtk_entry_set_text (GTK_ENTRY (priv->c_tags_entry), "");
+		return;
+	}
+
+	tags = g_string_new (NULL);
+	do {
+		gboolean use_tag;
+		gtk_tree_model_get (model, &iter, C_USE_TAG_COLUMN, &use_tag, -1);
+		if (use_tag) {
+			char *tag_name;
+
+			gtk_tree_model_get (model, &iter,
+					    C_TAG_COLUMN, &tag_name,
+					    -1);
+			if (tags->len > 0)
+				tags = g_string_append (tags, TAG_SEPARATOR_STR " ");
+			tags = g_string_append (tags, tag_name);
+			g_free (tag_name);
+		}
+	} while (gtk_tree_model_iter_next (model, &iter));
+
+	gtk_entry_set_text (GTK_ENTRY (priv->c_tags_entry), tags->str);
+	g_string_free (tags, TRUE);
+}
+
+
+static GList *
+get_tags_from_entry (GthTagSelection *tag_sel)
+{
+	GthTagSelectionPrivate *priv = tag_sel->priv;
+	GList       *cat_list = NULL;
+	const char  *utf8_text;
+	char       **tags;
+	int          i;
+
+	utf8_text = gtk_entry_get_text (GTK_ENTRY (priv->c_tags_entry));
+	if (utf8_text == NULL)
+		return NULL;
+
+	tags = _g_utf8_strsplit (utf8_text, TAG_SEPARATOR_C);
+
+	for (i = 0; tags[i] != NULL; i++) {
+		char *s;
+
+		s = _g_utf8_strstrip (tags[i]);
+
+		if (s != NULL)
+			cat_list = g_list_prepend (cat_list, s);
+	}
+	g_strfreev (tags);
+
+	return g_list_reverse (cat_list);
+}
+
+
+static void
+add_saved_tags (GthTagSelection *tag_sel,
+                GList           *cat_list)
+{
+	GthTagSelectionPrivate *priv = tag_sel->priv;
+	Bookmarks *tags;
+	GList     *scan;
+
+	tags = bookmarks_new (RC_TAGS_FILE);
+	bookmarks_load_from_disk (tags);
+
+	for (scan = tags->list; scan; scan = scan->next) {
+		GtkTreeIter  iter;
+		GList       *scan2;
+		gboolean     found = FALSE;
+		char        *tag1 = scan->data;
+
+		for (scan2 = cat_list; scan2 && !found; scan2 = scan2->next) {
+			char *tag2 = scan2->data;
+			if (strcmp (tag1, tag2) == 0)
+				found = TRUE;
+		}
+
+		if (found)
+			continue;
+
+		gtk_list_store_append (priv->c_tags_list_model, &iter);
+
+		gtk_list_store_set (priv->c_tags_list_model, &iter,
+				    C_USE_TAG_COLUMN, FALSE,
+				    C_TAG_COLUMN, tag1,
+				    -1);
+	}
+
+	bookmarks_free (tags);
+}
+
+
+static void
+update_list_from_entry (GthTagSelection *tag_sel)
+{
+	GthTagSelectionPrivate *priv = tag_sel->priv;
+	GList *tags = NULL;
+	GList *scan;
+
+	tags = get_tags_from_entry (tag_sel);
+
+	gtk_list_store_clear (priv->c_tags_list_model);
+	for (scan = tags; scan; scan = scan->next) {
+		char        *tag = scan->data;
+		GtkTreeIter  iter;
+
+		gtk_list_store_append (priv->c_tags_list_model, &iter);
+
+		gtk_list_store_set (priv->c_tags_list_model, &iter,
+				    C_USE_TAG_COLUMN, TRUE,
+				    C_TAG_COLUMN, tag,
+				    -1);
+	}
+	add_saved_tags (tag_sel, tags);
+	path_list_free (tags);
+}
+
+
+static void
+use_tag_toggled (GtkCellRendererToggle *cell,
+                 gchar                 *path_string,
+                 gpointer               callback_data)
+{
+	GthTagSelection *tag_sel = callback_data;
+	GthTagSelectionPrivate *priv = tag_sel->priv;
+	GtkTreeModel *model = GTK_TREE_MODEL (priv->c_tags_list_model);
+	GtkTreeIter   iter;
+	GtkTreePath  *path = gtk_tree_path_new_from_string (path_string);
+	gboolean      value;
+
+	gtk_tree_model_get_iter (model, &iter, path);
+	gtk_tree_model_get (model, &iter, C_USE_TAG_COLUMN, &value, -1);
+
+	value = !value;
+	gtk_list_store_set (GTK_LIST_STORE (model), &iter, C_USE_TAG_COLUMN, value, -1);
+
+	gtk_tree_path_free (path);
+	update_tag_entry (tag_sel);
+}
+
+
+static void
+choose_tags_ok_cb (GtkWidget       *widget,
+                   GthTagSelection *tag_sel)
+{
+	GthTagSelectionPrivate *priv = tag_sel->priv;
+
+	priv->ok_clicked = TRUE;
+
+	g_free (priv->tags);
+	priv->tags = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->c_tags_entry)));
+	priv->match_all = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->s_all_cat_radiobutton));
+
+	gtk_widget_destroy (priv->dialog);
+	priv->dialog = NULL;
+
+	g_signal_emit (tag_sel, signals[RESPONSE], 0, GTK_RESPONSE_OK);
+}
+
+
+static gboolean
+tags_dialog__destroy_cb (GtkWidget       *widget,
+                         GthTagSelection *tag_sel)
+{
+	tag_sel->priv->dialog = NULL;
+
+	return FALSE;
+
+	if (! tag_sel->priv->ok_clicked) {
+		g_object_ref (tag_sel);
+		g_signal_emit (tag_sel, signals[RESPONSE], 0, GTK_RESPONSE_CANCEL);
+		g_object_unref (tag_sel);
+	}
+}
+
+
+static void
+gth_tag_selection_construct (GthTagSelection *tag_sel,
+                             GtkWindow       *parent,
+                             const char      *tags,
+                             gboolean         match_all)
+{
+	GthTagSelectionPrivate *priv;
+	GtkCellRenderer             *renderer;
+	GtkTreeViewColumn           *column;
+
+	priv = tag_sel->priv;
+
+	priv->gui = glade_xml_new (GTHUMB_GLADEDIR "/" SEARCH_GLADE_FILE,
+				   NULL,
+				   NULL);
+	if (! priv->gui) {
+		g_warning ("Could not find " SEARCH_GLADE_FILE "\n");
+		return;
+	}
+
+	if (tags != NULL)
+		priv->tags = g_strdup (tags);
+	priv->match_all = match_all;
+
+	/* Get the widgets. */
+
+	priv->dialog = glade_xml_get_widget (priv->gui, "tags_dialog");
+	priv->c_tags_entry = glade_xml_get_widget (priv->gui, "c_tags_entry");
+	priv->c_tags_treeview = glade_xml_get_widget (priv->gui, "c_tags_treeview");
+	priv->c_ok_button = glade_xml_get_widget (priv->gui, "c_ok_button");
+	priv->c_cancel_button = glade_xml_get_widget (priv->gui, "c_cancel_button");
+	priv->s_at_least_one_cat_radiobutton = glade_xml_get_widget (priv->gui, "s_at_least_one_cat_radiobutton");
+	priv->s_all_cat_radiobutton = glade_xml_get_widget (priv->gui, "s_all_cat_radiobutton");
+
+	/* Set widgets data. */
+
+	priv->c_tags_list_model = gtk_list_store_new (C_NUM_COLUMNS,
+							    G_TYPE_BOOLEAN,
+							    G_TYPE_STRING);
+	gtk_tree_view_set_model (GTK_TREE_VIEW (priv->c_tags_treeview),
+				 GTK_TREE_MODEL (priv->c_tags_list_model));
+	g_object_unref (priv->c_tags_list_model);
+	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->c_tags_treeview), FALSE);
+
+	renderer = gtk_cell_renderer_toggle_new ();
+	g_signal_connect (G_OBJECT (renderer),
+			  "toggled",
+			  G_CALLBACK (use_tag_toggled),
+			  tag_sel);
+	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->c_tags_treeview),
+						     -1, "Use",
+						     renderer,
+						     "active", C_USE_TAG_COLUMN,
+						     NULL);
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes ("",
+							   renderer,
+							   "text", C_TAG_COLUMN,
+							   NULL);
+
+	gtk_tree_view_column_set_sort_column_id (column, 0);
+	gtk_tree_view_append_column (GTK_TREE_VIEW (priv->c_tags_treeview),
+				     column);
+
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->c_tags_list_model), C_TAG_COLUMN, GTK_SORT_ASCENDING);
+
+	gtk_entry_set_text (GTK_ENTRY (priv->c_tags_entry), tags);
+	update_list_from_entry (tag_sel);
+
+	if (priv->match_all)
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->s_all_cat_radiobutton), TRUE);
+	else
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->s_at_least_one_cat_radiobutton), TRUE);
+
+	/* Set the signals handlers. */
+
+	g_signal_connect (G_OBJECT (priv->dialog),
+			  "destroy",
+			  G_CALLBACK (tags_dialog__destroy_cb),
+			  tag_sel);
+	g_signal_connect (G_OBJECT (priv->c_ok_button),
+			  "clicked",
+			  G_CALLBACK (choose_tags_ok_cb),
+			  tag_sel);
+	g_signal_connect_swapped (G_OBJECT (priv->c_cancel_button),
+				  "clicked",
+				  G_CALLBACK (gtk_widget_destroy),
+				  G_OBJECT (priv->dialog));
+
+	/* Run dialog. */
+
+	gtk_widget_grab_focus (priv->c_tags_treeview);
+
+	if (parent != NULL) {
+		gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), parent);
+		gtk_window_set_modal (GTK_WINDOW (priv->dialog), TRUE);
+	}
+
+	gtk_widget_show (priv->dialog);
+}
+
+
+GType
+gth_tag_selection_get_type (void)
+{
+        static GType type = 0;
+
+        if (! type) {
+                GTypeInfo type_info = {
+			sizeof (GthTagSelectionClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) gth_tag_selection_class_init,
+			NULL,
+			NULL,
+			sizeof (GthTagSelection),
+			0,
+			(GInstanceInitFunc) gth_tag_selection_init
+		};
+
+		type = g_type_register_static (G_TYPE_OBJECT,
+					       "GthTagSelection",
+					       &type_info,
+					       0);
+	}
+
+        return type;
+}
+
+
+GthTagSelection*
+gth_tag_selection_new (GtkWindow  *parent,
+                       const char *tags,
+                       gboolean    match_all)
+{
+	GthTagSelection *csel;
+
+	csel = GTH_TAG_SELECTION (g_object_new (GTH_TYPE_TAG_SELECTION, NULL));
+	gth_tag_selection_construct (csel, parent, tags, match_all);
+
+	return csel;
+}
+
+
+char *
+gth_tag_selection_get_tags (GthTagSelection *csel)
+{
+	return csel->priv->tags;
+}
+
+
+gboolean
+gth_tag_selection_get_match_all (GthTagSelection *csel)
+{
+	return csel->priv->match_all;
+}
diff --git a/src/gth-tag-selection-dialog.h b/src/gth-tag-selection-dialog.h
new file mode 100644
index 0000000..e573f85
--- /dev/null
+++ b/src/gth-tag-selection-dialog.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2007 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_TAG_SELECTION_DIALOG_H
+#define GTH_TAG_SELECTION_DIALOG_H
+
+#include <gtk/gtk.h>
+
+#define GTH_TYPE_TAG_SELECTION         (gth_tag_selection_get_type ())
+#define GTH_TAG_SELECTION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_TAG_SELECTION, GthTagSelection))
+#define GTH_TAG_SELECTION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTH_TYPE_TAG_SELECTION, GthTagSelectionClass))
+#define GTH_IS_TAG_SELECTION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTH_TYPE_TAG_SELECTION))
+#define GTH_IS_TAG_SELECTION_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTH_TYPE_TAG_SELECTION))
+#define GTH_TAG_SELECTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTH_TYPE_TAG_SELECTION, GthTagSelectionClass))
+
+typedef struct _GthTagSelection         GthTagSelection;
+typedef struct _GthTagSelectionPrivate  GthTagSelectionPrivate;
+typedef struct _GthTagSelectionClass    GthTagSelectionClass;
+
+struct _GthTagSelection
+{
+	GObject __parent;
+	GthTagSelectionPrivate *priv;
+};
+
+struct _GthTagSelectionClass
+{
+	GObjectClass __parent_class;
+
+	/* -- Signals -- */
+
+        void (* response) (GthTagSelection *csel,
+        		   GtkResponseType       response);
+};
+
+GType                  gth_tag_selection_get_type       (void) G_GNUC_CONST;
+GthTagSelection *      gth_tag_selection_new            (GtkWindow            *parent,
+                                                         const char           *tags,
+                                                         gboolean              match_all);
+char *                 gth_tag_selection_get_tags       (GthTagSelection *csel);
+gboolean               gth_tag_selection_get_match_all  (GthTagSelection *csel);
+
+#endif /* GTH_TAG_SELECTION_DIALOG_H */



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