evolution-data-server r9791 - trunk/libedataserverui



Author: mbarnes
Date: Tue Dec  2 16:34:44 2008
New Revision: 9791
URL: http://svn.gnome.org/viewvc/evolution-data-server?rev=9791&view=rev

Log:
2008-12-02  Matthew Barnes  <mbarnes redhat com>

	** Fixes part of bug #348299

	* libedataserverui/Makefile.am:
	Add new files and test program.

	* libedataserverui/e-category-completion.c:
	* libedataserverui/e-category-completion.h:
	New files implement category completion for GtkEntry widgets.

	* libedataserverui/e-categories-dialog.c:
	Fix inline searches in the category list.
	Use ECategoryCompletion in the entry box.
	Put spaces after commas in the entry box.
	Listen for category changes and rebuild the list store.

	* libedataserverui/test-category-completion.c:
	Test program for ECategoryCompletion.



Added:
   trunk/libedataserverui/e-category-completion.c
   trunk/libedataserverui/e-category-completion.h
   trunk/libedataserverui/test-category-completion.c
Modified:
   trunk/libedataserverui/ChangeLog
   trunk/libedataserverui/Makefile.am
   trunk/libedataserverui/e-categories-dialog.c

Modified: trunk/libedataserverui/Makefile.am
==============================================================================
--- trunk/libedataserverui/Makefile.am	(original)
+++ trunk/libedataserverui/Makefile.am	Tue Dec  2 16:34:44 2008
@@ -12,6 +12,7 @@
 
 lib_LTLIBRARIES = libedataserverui-1.2.la
 noinst_PROGRAMS = 			\
+	test-category-completion		\
 	test-source-combo-box		\
 	test-source-selector		\
 	test-contact-store		\
@@ -20,6 +21,7 @@
 libedataserverui_1_2_la_SOURCES =	\
 	$(MARSHAL_GENERATED)		\
 	e-categories-dialog.c		\
+	e-category-completion.c		\
 	e-destination-store.c		\
 	e-book-auth-util.c		\
 	e-contact-store.c		\
@@ -48,6 +50,7 @@
 
 libedataserveruiinclude_HEADERS =	\
 	e-categories-dialog.h		\
+	e-category-completion.h		\
 	e-destination-store.h		\
 	e-book-auth-util.h		\
 	e-contact-store.h		\
@@ -65,6 +68,12 @@
 	e-cell-renderer-color.h
 
 # Test programs
+test_category_completion_SOURCES = test-category-completion.c
+test_category_completion_LDADD = 					\
+	libedataserverui-1.2.la 				\
+	$(top_builddir)/libedataserver/libedataserver-1.2.la	\
+	$(E_DATA_SERVER_UI_LIBS)
+
 test_source_selector_SOURCES = test-source-selector.c
 test_source_selector_LDADD = 					\
 	libedataserverui-1.2.la 				\

Modified: trunk/libedataserverui/e-categories-dialog.c
==============================================================================
--- trunk/libedataserverui/e-categories-dialog.c	(original)
+++ trunk/libedataserverui/e-categories-dialog.c	Tue Dec  2 16:34:44 2008
@@ -28,6 +28,7 @@
 #include "libedataserver/e-categories.h"
 #include "libedataserver/libedataserver-private.h"
 #include "e-categories-dialog.h"
+#include "e-category-completion.h"
 
 enum {
 	COLUMN_ACTIVE,
@@ -110,6 +111,70 @@
 G_DEFINE_TYPE (ECategoriesDialog, e_categories_dialog, GTK_TYPE_DIALOG)
 
 static void
+categories_dialog_build_model (ECategoriesDialog *dialog)
+{
+	GtkTreeView *tree_view;
+	GtkListStore *store;
+	GList *list, *iter;
+
+	store = gtk_list_store_new (
+		N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING);
+
+	gtk_tree_sortable_set_sort_column_id (
+		GTK_TREE_SORTABLE (store),
+		COLUMN_CATEGORY, GTK_SORT_ASCENDING);
+
+	list = e_categories_get_list ();
+	for (iter = list; iter != NULL; iter = iter->next) {
+		const gchar *category_name = iter->data;
+		const gchar *filename;
+		GdkPixbuf *pixbuf = NULL;
+		GtkTreeIter iter;
+		gboolean active;
+
+		/* Only add user-visible categories. */
+		if (!e_categories_is_searchable (category_name))
+			continue;
+
+		active = (g_hash_table_lookup (
+			dialog->priv->selected_categories,
+			category_name) != NULL);
+
+		filename = e_categories_get_icon_file_for (category_name);
+		if (filename != NULL)
+			pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+
+		gtk_list_store_append (store, &iter);
+
+		gtk_list_store_set (
+			store, &iter,
+			COLUMN_ACTIVE, active,
+			COLUMN_ICON, pixbuf,
+			COLUMN_CATEGORY, category_name,
+			-1);
+
+		if (pixbuf != NULL)
+			g_object_unref (pixbuf);
+	}
+
+	tree_view = GTK_TREE_VIEW (dialog->priv->categories_list);
+	gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store));
+
+	/* This has to be reset everytime we install a new model. */
+	gtk_tree_view_set_search_column (tree_view, COLUMN_CATEGORY);
+
+	g_list_free (list);
+	g_object_unref (store);
+}
+
+static void
+categories_dialog_listener_cb (gpointer useless_pointer,
+                               ECategoriesDialog *dialog)
+{
+	categories_dialog_build_model (dialog);
+}
+
+static void
 e_categories_dialog_dispose (GObject *object)
 {
 	ECategoriesDialogPrivate *priv = E_CATEGORIES_DIALOG (object)->priv;
@@ -132,6 +197,9 @@
 {
 	ECategoriesDialogPrivate *priv = E_CATEGORIES_DIALOG (object)->priv;
 
+	e_categories_unregister_change_listener (
+		G_CALLBACK (categories_dialog_listener_cb), object);
+
 	g_free (priv);
 	E_CATEGORIES_DIALOG (object)->priv = NULL;
 
@@ -155,7 +223,7 @@
 	GString **str = user_data;
 
 	if (strlen ((*str)->str) > 0)
-		*str = g_string_append (*str, ",");
+		*str = g_string_append (*str, ", ");
 
 	*str = g_string_append (*str, (const char *) key);
 }
@@ -247,7 +315,6 @@
 		if (gtk_dialog_run (GTK_DIALOG (prop_dialog->the_dialog)) == GTK_RESPONSE_OK) {
 			const char *category_name;
 			char *correct_category_name;
-			GtkTreeIter iter;
 
 			category_name = gtk_entry_get_text (GTK_ENTRY (prop_dialog->category_name));
 			correct_category_name = check_category_name (category_name);
@@ -266,27 +333,12 @@
 				g_free (correct_category_name);
 			} else {
 				gchar *category_icon;
-				GdkPixbuf *icon = NULL;
-				GtkListStore *list_store = GTK_LIST_STORE (
-								gtk_tree_view_get_model (GTK_TREE_VIEW (prop_dialog->parent->priv->categories_list)));
 
 				category_icon = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (prop_dialog->category_icon));
-				if (category_icon)
-					icon = gdk_pixbuf_new_from_file (category_icon, NULL);
 
 				e_categories_add (correct_category_name, NULL, category_icon, TRUE);
 
-				gtk_list_store_append (list_store, &iter);
-				gtk_list_store_set (list_store, &iter,
-						    COLUMN_ACTIVE, FALSE,
-						    COLUMN_ICON, icon,
-						    COLUMN_CATEGORY,correct_category_name,
-						    -1);
-
-				if (icon)
-					g_object_unref (icon);
-				if (category_icon)
-					g_free (category_icon);
+				g_free (category_icon);
 				g_free (correct_category_name);
 
 				break;
@@ -376,17 +428,15 @@
 
 	gtk_tree_model_get (model, &iter, COLUMN_CATEGORY, &category_name, -1);
 	e_categories_remove (category_name);
-	gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
 }
 
 static void
 e_categories_dialog_init (ECategoriesDialog *dialog)
 {
 	ECategoriesDialogPrivate *priv;
-	GList *cat_list;
 	GtkCellRenderer *renderer;
+	GtkEntryCompletion *completion;
 	GtkTreeViewColumn *column;
-	GtkListStore *model;
 	GtkWidget *main_widget;
 	char *gladefile;
 
@@ -412,6 +462,10 @@
 	priv->categories_entry = glade_xml_get_widget (priv->gui, "entry-categories");
 	priv->categories_list = glade_xml_get_widget (priv->gui, "categories-list");
 
+	completion = e_category_completion_new ();
+	gtk_entry_set_completion (GTK_ENTRY (priv->categories_entry), completion);
+	g_object_unref (completion);
+
 	priv->new_button = glade_xml_get_widget (priv->gui, "button-new");
 	g_signal_connect (G_OBJECT (priv->new_button), "clicked", G_CALLBACK (new_button_clicked_cb), dialog);
 	priv->edit_button = glade_xml_get_widget (priv->gui, "button-edit");
@@ -425,37 +479,6 @@
 	gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
 	gtk_window_set_title (GTK_WINDOW (dialog), _("Categories"));
 
-	/* set up the categories list */
-	model = gtk_list_store_new (
-		N_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING);
-	gtk_tree_sortable_set_sort_column_id (
-		GTK_TREE_SORTABLE (model),
-		COLUMN_CATEGORY, GTK_SORT_ASCENDING);
-	cat_list = e_categories_get_list ();
-	while (cat_list != NULL) {
-		GtkTreeIter iter;
-
-		/* only add categories that are user-visible */
-		if (e_categories_is_searchable ((const char *) cat_list->data)) {
-			const gchar *icon_file;
-			GdkPixbuf *icon = NULL;
-			icon_file = e_categories_get_icon_file_for ((const char *) cat_list->data);
-			if (icon_file)
-				icon = gdk_pixbuf_new_from_file (icon_file, NULL);
-			gtk_list_store_append (model, &iter);
-			gtk_list_store_set (model, &iter,
-					    COLUMN_ACTIVE, FALSE,
-					    COLUMN_ICON,   icon,
-					    COLUMN_CATEGORY,  cat_list->data,
-					    -1);
-			if (icon)
-				g_object_unref (icon);
-		}
-
-		cat_list = g_list_remove (cat_list, cat_list->data);
-	}
-	gtk_tree_view_set_model (GTK_TREE_VIEW (priv->categories_list), GTK_TREE_MODEL (model));
-
 	renderer = gtk_cell_renderer_toggle_new ();
 	g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (category_toggled_cb), dialog);
 	column = gtk_tree_view_column_new_with_attributes ("?", renderer,
@@ -472,8 +495,10 @@
 							   "text", COLUMN_CATEGORY, NULL);
 	gtk_tree_view_append_column (GTK_TREE_VIEW (priv->categories_list), column);
 
-	/* free memory */
-	g_object_unref (model);
+	categories_dialog_build_model (dialog);
+
+	e_categories_register_change_listener (
+		G_CALLBACK (categories_dialog_listener_cb), dialog);
 }
 
 /**

Added: trunk/libedataserverui/e-category-completion.c
==============================================================================
--- (empty file)
+++ trunk/libedataserverui/e-category-completion.c	Tue Dec  2 16:34:44 2008
@@ -0,0 +1,477 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#include "e-category-completion.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <libedataserver/e-categories.h>
+
+#define E_CATEGORY_COMPLETION_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_CATEGORY_COMPLETION, ECategoryCompletionPrivate))
+
+struct _ECategoryCompletionPrivate {
+	GtkWidget *last_known_entry;
+	gchar *create;
+	gchar *prefix;
+};
+
+enum {
+	COLUMN_PIXBUF,
+	COLUMN_CATEGORY,
+	COLUMN_NORMALIZED,
+	NUM_COLUMNS
+};
+
+static gpointer parent_class;
+
+/* Forward Declarations */
+
+static void
+category_completion_track_entry (GtkEntryCompletion *completion);
+
+static void
+category_completion_build_model (GtkEntryCompletion *completion)
+{
+	GtkListStore *store;
+	GList *list;
+
+	store = gtk_list_store_new (
+		NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+	list = e_categories_get_list ();
+	while (list != NULL) {
+		const gchar *category = list->data;
+		const gchar *filename;
+		gchar *normalized;
+		gchar *casefolded;
+		GdkPixbuf *pixbuf = NULL;
+		GtkTreeIter iter;
+
+		filename = e_categories_get_icon_file_for (category);
+		if (filename != NULL && *filename != '\0')
+			pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+
+		normalized = g_utf8_normalize (
+			category, -1, G_NORMALIZE_DEFAULT);
+		casefolded = g_utf8_casefold (normalized, -1);
+
+		gtk_list_store_append (store, &iter);
+
+		gtk_list_store_set (
+			store, &iter, COLUMN_PIXBUF, pixbuf,
+			COLUMN_CATEGORY, category, COLUMN_NORMALIZED,
+			casefolded, -1);
+
+		g_free (normalized);
+		g_free (casefolded);
+
+		if (pixbuf != NULL)
+			g_object_unref (pixbuf);
+
+		list = g_list_delete_link (list, list);
+	}
+
+	gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (store));
+}
+
+static void
+category_completion_categories_changed_cb (GObject *some_private_object,
+                                           GtkEntryCompletion *completion)
+{
+	category_completion_build_model (completion);
+}
+
+static void
+category_completion_complete (GtkEntryCompletion *completion,
+                              const gchar *category)
+{
+	GtkEditable *editable;
+	GtkWidget *entry;
+	const gchar *text;
+	const gchar *cp;
+	gint start_pos;
+	gint end_pos;
+	glong offset;
+
+	entry = gtk_entry_completion_get_entry (completion);
+
+	editable = GTK_EDITABLE (entry);
+	text = gtk_entry_get_text (GTK_ENTRY (entry));
+
+	/* Get the cursor position as a character offset. */
+	offset = gtk_editable_get_position (editable);
+
+	/* Find the rightmost comma before the cursor. */
+	cp = g_utf8_offset_to_pointer (text, offset);
+	cp = g_utf8_strrchr (text, (gssize) (cp - text), ',');
+
+	/* Calculate the selection start position as a character offset. */
+	if (cp == NULL)
+		offset = 0;
+	else {
+		cp = g_utf8_next_char (cp);
+		if (g_unichar_isspace (g_utf8_get_char (cp)))
+			cp = g_utf8_next_char (cp);
+		offset = g_utf8_pointer_to_offset (text, cp);
+	}
+	start_pos = (gint) offset;
+
+	/* Find the leftmost comma after the cursor. */
+	cp = g_utf8_offset_to_pointer (text, offset);
+	cp = g_utf8_strchr (cp, -1, ',');
+
+	/* Calculate the selection end position as a character offset. */
+	if (cp == NULL)
+		offset = -1;
+	else {
+		cp = g_utf8_next_char (cp);
+		if (g_unichar_isspace (g_utf8_get_char (cp)))
+			cp = g_utf8_next_char (cp);
+		offset = g_utf8_pointer_to_offset (text, cp);
+	}
+	end_pos = (gint) offset;
+
+	/* Complete the partially typed category. */
+	gtk_editable_delete_text (editable, start_pos, end_pos);
+	gtk_editable_insert_text (editable, category, -1, &start_pos);
+	gtk_editable_insert_text (editable, ", ", 2, &start_pos);
+	gtk_editable_set_position (editable, start_pos);
+}
+
+static gboolean
+category_completion_is_match (GtkEntryCompletion *completion,
+                              const gchar *key,
+                              GtkTreeIter *iter)
+{
+	ECategoryCompletionPrivate *priv;
+	GtkTreeModel *model;
+	GtkWidget *entry;
+	GValue value = { 0, };
+	gboolean match;
+
+	priv = E_CATEGORY_COMPLETION_GET_PRIVATE (completion);
+	entry = gtk_entry_completion_get_entry (completion);
+	model = gtk_entry_completion_get_model (completion);
+
+	/* XXX This would be easier if GtkEntryCompletion had an 'entry'
+	 *     property that we could listen to for notifications. */
+	if (entry != priv->last_known_entry)
+		category_completion_track_entry (completion);
+
+	if (priv->prefix == NULL)
+		return FALSE;
+
+	gtk_tree_model_get_value (model, iter, COLUMN_NORMALIZED, &value);
+	match = g_str_has_prefix (g_value_get_string (&value), priv->prefix);
+	g_value_unset (&value);
+
+	return match;
+}
+
+static void
+category_completion_update_prefix (GtkEntryCompletion *completion)
+{
+	ECategoryCompletionPrivate *priv;
+	GtkEditable *editable;
+	GtkTreeModel *model;
+	GtkWidget *entry;
+	GtkTreeIter iter;
+	const gchar *text;
+	const gchar *start;
+	const gchar *end;
+	const gchar *cp;
+	gboolean valid;
+	gchar *input;
+	glong offset;
+
+	priv = E_CATEGORY_COMPLETION_GET_PRIVATE (completion);
+	entry = gtk_entry_completion_get_entry (completion);
+	model = gtk_entry_completion_get_model (completion);
+
+	/* XXX This would be easier if GtkEntryCompletion had an 'entry'
+	 *     property that we could listen to for notifications. */
+	if (entry != priv->last_known_entry) {
+		category_completion_track_entry (completion);
+		return;
+	}
+
+	editable = GTK_EDITABLE (entry);
+	text = gtk_entry_get_text (GTK_ENTRY (entry));
+
+	/* Get the cursor position as a character offset. */
+	offset = gtk_editable_get_position (editable);
+
+	/* Find the rightmost comma before the cursor. */
+	cp = g_utf8_offset_to_pointer (text, offset);
+	cp = g_utf8_strrchr (text, (gsize) (cp - text), ',');
+
+	/* Mark the start of the prefix. */
+	if (cp == NULL)
+		start = text;
+	else {
+		cp = g_utf8_next_char (cp);
+		if (g_unichar_isspace (g_utf8_get_char (cp)))
+			cp = g_utf8_next_char (cp);
+		start = cp;
+	}
+
+	/* Find the leftmost comma after the cursor. */
+	cp = g_utf8_offset_to_pointer (text, offset);
+	cp = g_utf8_strchr (cp, -1, ',');
+
+	/* Mark the end of the prefix. */
+	if (cp == NULL)
+		end = text + strlen (text);
+	else
+		end = cp;
+
+	if (priv->create != NULL)
+		gtk_entry_completion_delete_action (completion, 0);
+
+	g_free (priv->create);
+	priv->create = NULL;
+
+	g_free (priv->prefix);
+	priv->prefix = NULL;
+
+	if (start == end)
+		return;
+
+	input = g_strstrip (g_strndup (start, end - start));
+	priv->create = input;
+
+	input = g_utf8_normalize (input, -1, G_NORMALIZE_DEFAULT);
+	priv->prefix = g_utf8_casefold (input, -1);
+	g_free (input);
+
+	if (*priv->create == '\0') {
+		g_free (priv->create);
+		priv->create = NULL;
+		return;
+	}
+
+	valid = gtk_tree_model_get_iter_first (model, &iter);
+	while (valid) {
+		GValue value = { 0, };
+
+		gtk_tree_model_get_value (
+			model, &iter, COLUMN_NORMALIZED, &value);
+		if (strcmp (g_value_get_string (&value), priv->prefix) == 0) {
+			g_value_unset (&value);
+			g_free (priv->create);
+			priv->create = NULL;
+			return;
+		}
+		g_value_unset (&value);
+
+		valid = gtk_tree_model_iter_next (model, &iter);
+	}
+
+	input = g_strdup_printf (_("Create category \"%s\""), priv->create);
+	gtk_entry_completion_insert_action_text (completion, 0, input);
+	g_free (input);
+}
+
+static void
+category_completion_track_entry (GtkEntryCompletion *completion)
+{
+	ECategoryCompletionPrivate *priv;
+
+	priv = E_CATEGORY_COMPLETION_GET_PRIVATE (completion);
+
+	if (priv->last_known_entry != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->last_known_entry, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, completion);
+		g_object_unref (priv->last_known_entry);
+	}
+
+	g_free (priv->prefix);
+	priv->prefix = NULL;
+
+	priv->last_known_entry = gtk_entry_completion_get_entry (completion);
+	if (priv->last_known_entry == NULL)
+		return;
+
+	g_object_ref (priv->last_known_entry);
+
+	g_signal_connect_swapped (
+		priv->last_known_entry, "notify::cursor-position",
+		G_CALLBACK (category_completion_update_prefix), completion);
+
+	g_signal_connect_swapped (
+		priv->last_known_entry, "notify::text",
+		G_CALLBACK (category_completion_update_prefix), completion);
+
+	category_completion_update_prefix (completion);
+}
+
+static void
+category_completion_dispose (GObject *object)
+{
+	ECategoryCompletionPrivate *priv;
+
+	priv = E_CATEGORY_COMPLETION_GET_PRIVATE (object);
+
+	if (priv->last_known_entry != NULL) {
+		g_signal_handlers_disconnect_matched (
+			priv->last_known_entry, G_SIGNAL_MATCH_DATA,
+			0, 0, NULL, NULL, object);
+		g_object_unref (priv->last_known_entry);
+		priv->last_known_entry = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+category_completion_finalize (GObject *object)
+{
+	ECategoryCompletionPrivate *priv;
+
+	priv = E_CATEGORY_COMPLETION_GET_PRIVATE (object);
+
+	g_free (priv->create);
+	g_free (priv->prefix);
+
+	e_categories_unregister_change_listener (
+		G_CALLBACK (category_completion_categories_changed_cb),
+		object);
+
+	/* Chain up to parent's finalize() method. */
+	G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+category_completion_match_selected (GtkEntryCompletion *completion,
+                                    GtkTreeModel *model,
+                                    GtkTreeIter *iter)
+{
+	GValue value = { 0, };
+
+	gtk_tree_model_get_value (model, iter, COLUMN_CATEGORY, &value);
+	category_completion_complete (completion, g_value_get_string (&value));
+	g_value_unset (&value);
+
+	return TRUE;
+}
+
+static void
+category_completion_action_activated (GtkEntryCompletion *completion,
+                                      gint index)
+{
+	ECategoryCompletionPrivate *priv;
+	gchar *category;
+
+	priv = E_CATEGORY_COMPLETION_GET_PRIVATE (completion);
+
+	category = g_strdup (priv->create);
+	e_categories_add (category, NULL, NULL, TRUE);
+	category_completion_complete (completion, category);
+	g_free (category);
+}
+
+static void
+category_completion_class_init (ECategoryCompletionClass *class)
+{
+	GObjectClass *object_class;
+	GtkEntryCompletionClass *entry_completion_class;
+
+	parent_class = g_type_class_peek_parent (class);
+	g_type_class_add_private (class, sizeof (ECategoryCompletionPrivate));
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->dispose = category_completion_dispose;
+	object_class->finalize = category_completion_finalize;
+
+	entry_completion_class = GTK_ENTRY_COMPLETION_CLASS (class);
+	entry_completion_class->match_selected = category_completion_match_selected;
+	entry_completion_class->action_activated = category_completion_action_activated;
+}
+
+static void
+category_completion_init (ECategoryCompletion *category_completion)
+{
+	GtkCellRenderer *renderer;
+	GtkEntryCompletion *completion;
+
+	category_completion->priv =
+		E_CATEGORY_COMPLETION_GET_PRIVATE (category_completion);
+
+	completion = GTK_ENTRY_COMPLETION (category_completion);
+
+	gtk_entry_completion_set_match_func (
+		completion, (GtkEntryCompletionMatchFunc)
+		category_completion_is_match, NULL, NULL);
+
+	gtk_entry_completion_set_text_column (completion, COLUMN_CATEGORY);
+
+	renderer = gtk_cell_renderer_pixbuf_new ();
+	gtk_cell_layout_pack_start (
+		GTK_CELL_LAYOUT (completion), renderer, FALSE);
+	gtk_cell_layout_add_attribute (
+		GTK_CELL_LAYOUT (completion),
+		renderer, "pixbuf", COLUMN_PIXBUF);
+	gtk_cell_layout_reorder (
+		GTK_CELL_LAYOUT (completion), renderer, 0);
+
+	e_categories_register_change_listener (
+		G_CALLBACK (category_completion_categories_changed_cb),
+		completion);
+
+	category_completion_build_model (completion);
+}
+
+GType
+e_category_completion_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo type_info = {
+			sizeof (ECategoryCompletionClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) category_completion_class_init,
+			(GClassFinalizeFunc) NULL,
+			NULL,  /* class_data */
+			sizeof (ECategoryCompletion),
+			0,     /* n_preallocs */
+			(GInstanceInitFunc) category_completion_init,
+			NULL   /* value_table */
+		};
+
+		type = g_type_register_static (
+			GTK_TYPE_ENTRY_COMPLETION, "ECategoryCompletion",
+			&type_info, 0);
+	}
+
+	return type;
+}
+
+GtkEntryCompletion *
+e_category_completion_new (void)
+{
+	return g_object_new (E_TYPE_CATEGORY_COMPLETION, NULL);
+}

Added: trunk/libedataserverui/e-category-completion.h
==============================================================================
--- (empty file)
+++ trunk/libedataserverui/e-category-completion.h	Tue Dec  2 16:34:44 2008
@@ -0,0 +1,63 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>  
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ */
+
+#ifndef E_CATEGORY_COMPLETION_H
+#define E_CATEGORY_COMPLETION_H
+
+#include <gtk/gtk.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CATEGORY_COMPLETION \
+	(e_category_completion_get_type ())
+#define E_CATEGORY_COMPLETION(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_CATEGORY_COMPLETION, ECategoryCompletion))
+#define E_CATEGORY_COMPLETION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_CATEGORY_COMPLETION, ECategoryCompletionClass))
+#define E_IS_CATEGORY_COMPLETION(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_CATEGORY_COMPLETION))
+#define E_IS_CATEGORY_COMPLETION_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_CATEGORY_COMPLETION))
+#define E_CATEGORY_COMPLETION_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_CATEGORY_COMPLETION, ECategoryCompletionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECategoryCompletion ECategoryCompletion;
+typedef struct _ECategoryCompletionClass ECategoryCompletionClass;
+typedef struct _ECategoryCompletionPrivate ECategoryCompletionPrivate;
+
+struct _ECategoryCompletion {
+	GtkEntryCompletion parent;
+	ECategoryCompletionPrivate *priv;
+};
+
+struct _ECategoryCompletionClass {
+	GtkEntryCompletionClass parent_class;
+};
+
+GType		e_category_completion_get_type	(void);
+GtkEntryCompletion *
+		e_category_completion_new	(void);
+
+G_END_DECLS
+
+#endif /* E_CATEGORY_COMPLETION_H */

Added: trunk/libedataserverui/test-category-completion.c
==============================================================================
--- (empty file)
+++ trunk/libedataserverui/test-category-completion.c	Tue Dec  2 16:34:44 2008
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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 Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "e-category-completion.h"
+
+static gboolean
+on_idle_create_widget (void)
+{
+	GtkWidget *window;
+	GtkWidget *vbox;
+	GtkWidget *entry;
+	GtkEntryCompletion *completion;
+
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_default_size (GTK_WINDOW (window), 400, 200);
+
+	g_signal_connect (
+		window, "delete-event",
+		G_CALLBACK (gtk_main_quit), NULL);
+
+	vbox = gtk_vbox_new (FALSE, 3);
+	gtk_container_add (GTK_CONTAINER (window), vbox);
+
+	entry = gtk_entry_new ();
+	completion = e_category_completion_new ();
+	gtk_entry_set_completion (GTK_ENTRY (entry), completion);
+	gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 0);
+
+	gtk_widget_show_all (window);
+
+	return FALSE;
+}
+
+int
+main (int argc, char **argv)
+{
+	gtk_init (&argc, &argv);
+
+	g_idle_add ((GSourceFunc) on_idle_create_widget, NULL);
+
+	gtk_main ();
+
+	return 0;
+}



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