[yelp/yelp-3-0] Adding YelpLocationEntry to yelp-3-0 branch
- From: Shaun McCance <shaunm src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [yelp/yelp-3-0] Adding YelpLocationEntry to yelp-3-0 branch
- Date: Wed, 9 Sep 2009 03:09:17 +0000 (UTC)
commit c54f10a187e7b738edff2f7930c82d6ca495bb99
Author: Shaun McCance <shaunm gnome org>
Date: Tue Sep 8 22:08:53 2009 -0500
Adding YelpLocationEntry to yelp-3-0 branch
configure.in | 2 +-
libyelp/Makefile.am | 1 +
libyelp/yelp-location-entry.c | 618 +++++++++++++++++++++++++++++++++++++++++
libyelp/yelp-location-entry.h | 72 +++++
tests/Makefile.am | 7 +-
tests/test-location-entry.c | 291 +++++++++++++++++++
6 files changed, 989 insertions(+), 2 deletions(-)
---
diff --git a/configure.in b/configure.in
index 5afa805..20cfde0 100644
--- a/configure.in
+++ b/configure.in
@@ -59,7 +59,7 @@ PKG_CHECK_MODULES(YELP,
gio-unix-2.0
gnome-doc-utils >= 0.17.2
gtk+-unix-print-2.0
- gtk+-2.0 >= 2.10.0
+ gtk+-2.0 >= 2.16.0
libxml-2.0 >= 2.6.5
libxslt >= 1.1.4
libexslt >= 0.8.1
diff --git a/libyelp/Makefile.am b/libyelp/Makefile.am
index e51e225..7ee9080 100644
--- a/libyelp/Makefile.am
+++ b/libyelp/Makefile.am
@@ -2,6 +2,7 @@ noinst_LTLIBRARIES = libyelp.la
libyelp_la_SOURCES = \
yelp-debug.c \
+ yelp-location-entry.c \
yelp-uri.c \
yelp-view.c
diff --git a/libyelp/yelp-location-entry.c b/libyelp/yelp-location-entry.c
new file mode 100644
index 0000000..3380505
--- /dev/null
+++ b/libyelp/yelp-location-entry.c
@@ -0,0 +1,618 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2009 Shaun McCance <shaunm gnome org>
+ *
+ * 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 Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "yelp-location-entry.h"
+
+struct _YelpLocationEntryPrivate
+{
+ GtkWidget *text_entry;
+
+ gint icon_column;
+ gint flags_column;
+ gboolean enable_search;
+
+ GtkTreeRowReference *row;
+
+ gboolean search_mode;
+
+ GtkCellRenderer *icon_cell;
+};
+
+#define YELP_LOCATION_ENTRY_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE((object), YELP_TYPE_LOCATION_ENTRY, YelpLocationEntryPrivate))
+
+static void location_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void location_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void location_entry_start_search (YelpLocationEntry *entry,
+ gboolean clear);
+static void location_entry_set_entry (YelpLocationEntry *entry,
+ gboolean emit);
+static gboolean location_entry_pulse (gpointer data);
+
+/* GtkTreeModel callbacks */
+static void yelp_location_entry_row_changed (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data);
+
+/* GtkComboBox callbacks */
+static void combo_box_changed_cb (GtkComboBox *widget,
+ gpointer user_data);
+static gboolean combo_box_row_separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data);
+
+/* GtkEntry callbacks */
+static gboolean entry_focus_in_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer user_data);
+static gboolean entry_focus_out_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer user_data);
+static void entry_activate_cb (GtkEntry *text_entry,
+ gpointer user_data);
+static void entry_icon_press_cb (GtkEntry *entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ gpointer user_data);
+static gboolean entry_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data);
+
+
+static GtkComboBoxEntryClass *parent_class = NULL;
+
+enum {
+ LOCATION_SELECTED,
+ SEARCH_ACTIVATED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_0,
+ PROP_ICON_COLUMN,
+ PROP_FLAGS_COLUMN,
+ PROP_ENABLE_SEARCH
+};
+
+static guint location_entry_signals[LAST_SIGNAL] = {0,};
+
+G_DEFINE_TYPE (YelpLocationEntry, yelp_location_entry, GTK_TYPE_COMBO_BOX_ENTRY)
+
+static void
+yelp_location_entry_class_init (YelpLocationEntryClass *klass)
+{
+ GObjectClass *gobject_class;
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkComboBoxEntryClass *entry_class;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ widget_class = GTK_WIDGET_CLASS (klass);
+ entry_class = GTK_COMBO_BOX_ENTRY_CLASS (klass);
+
+ gobject_class->get_property = location_entry_get_property;
+ gobject_class->set_property = location_entry_set_property;
+
+ /**
+ * YelpLocationEntry::location-selected
+ * @widget: The object which received the signal
+ */
+ location_entry_signals[LOCATION_SELECTED] =
+ g_signal_new ("location_selected",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ /**
+ * YelpLocationEntry::search_activated
+ * @widget: The object which received the signal
+ * @text: The search text
+ */
+ location_entry_signals[SEARCH_ACTIVATED] =
+ g_signal_new ("search-activated",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ g_object_class_install_property (gobject_class,
+ PROP_ICON_COLUMN,
+ g_param_spec_int ("icon-column",
+ N_("Icon Column"),
+ N_("A column in the model to get icon names from"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_FLAGS_COLUMN,
+ g_param_spec_int ("flags-column",
+ N_("Flags Column"),
+ N_("A column in the model with YelpLocationEntryFlags flags"),
+ -1,
+ G_MAXINT,
+ -1,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_ENABLE_SEARCH,
+ g_param_spec_boolean ("enable-search",
+ N_("Enable Search"),
+ N_("Whether the location entry can be used as a search field"),
+ TRUE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private ((GObjectClass *) klass,
+ sizeof (YelpLocationEntryPrivate));
+}
+
+static void yelp_location_entry_init (YelpLocationEntry *entry)
+{
+ GList *cells;
+ entry->priv = YELP_LOCATION_ENTRY_GET_PRIVATE (entry);
+
+ entry->priv->enable_search = TRUE;
+ entry->priv->search_mode = FALSE;
+
+ entry->priv->text_entry = gtk_bin_get_child (GTK_BIN (entry));
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_PRIMARY,
+ "help-browser");
+ gtk_entry_set_icon_activatable (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_PRIMARY,
+ FALSE);
+ gtk_entry_set_icon_activatable (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_PRIMARY,
+ TRUE);
+
+ /* Trying to get the text to line up with the text in the GtkEntry.
+ * The text cell is the zeroeth cell right now, since we haven't
+ * yet called reorder. I realize using a guesstimate pixel value
+ * won't be perfect all the time, but 4 looks niceish.
+ */
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (entry));
+ g_object_set (cells->data, "xpad", 4, NULL);
+ g_list_free (cells);
+
+ entry->priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry), entry->priv->icon_cell, FALSE);
+ gtk_cell_layout_reorder (GTK_CELL_LAYOUT (entry), entry->priv->icon_cell, 0);
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (combo_box_changed_cb), NULL);
+
+ g_signal_connect (entry->priv->text_entry, "focus-in-event",
+ G_CALLBACK (entry_focus_in_cb), entry);
+ g_signal_connect (entry->priv->text_entry, "focus-out-event",
+ G_CALLBACK (entry_focus_out_cb), entry);
+ g_signal_connect (entry->priv->text_entry, "icon-press",
+ G_CALLBACK (entry_icon_press_cb), entry);
+ g_signal_connect (entry->priv->text_entry, "key-press-event",
+ G_CALLBACK (entry_key_press_cb), entry);
+ g_signal_connect (entry->priv->text_entry, "activate",
+ G_CALLBACK (entry_activate_cb), entry);
+}
+
+static void
+location_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_COLUMN:
+ g_value_set_int (value, entry->priv->icon_column);
+ break;
+ case PROP_FLAGS_COLUMN:
+ g_value_set_int (value, entry->priv->flags_column);
+ break;
+ case PROP_ENABLE_SEARCH:
+ g_value_set_boolean (value, entry->priv->enable_search);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+location_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (object);
+
+ switch (prop_id)
+ {
+ case PROP_ICON_COLUMN:
+ entry->priv->icon_column = g_value_get_int (value);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (entry),
+ entry->priv->icon_cell,
+ "icon-name",
+ entry->priv->icon_column,
+ NULL);
+ break;
+ case PROP_FLAGS_COLUMN:
+ entry->priv->flags_column = g_value_get_int (value);
+ /* If this is in yelp_location_entry_init, it gets called the first
+ * time before flags_column is set, and isn't called again until the
+ * "row-changed" signal on the model. The prevents application code
+ * from populating the model before initializing the widget.
+ */
+ gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (entry),
+ (GtkTreeViewRowSeparatorFunc)
+ combo_box_row_separator_func,
+ entry, NULL);
+ break;
+ case PROP_ENABLE_SEARCH:
+ entry->priv->enable_search = g_value_get_boolean (value);
+ gtk_editable_set_editable (GTK_EDITABLE (entry->priv->text_entry),
+ entry->priv->enable_search);
+ if (!entry->priv->enable_search)
+ {
+ entry->priv->search_mode = FALSE;
+ location_entry_set_entry (entry, FALSE);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+location_entry_start_search (YelpLocationEntry *entry,
+ gboolean clear)
+{
+ if (!entry->priv->enable_search)
+ return;
+ if (clear && !entry->priv->search_mode)
+ {
+ gtk_entry_set_text (GTK_ENTRY (entry->priv->text_entry), "");
+ }
+ entry->priv->search_mode = TRUE;
+ location_entry_set_entry (entry, FALSE);
+ gtk_widget_grab_focus (entry->priv->text_entry);
+}
+
+static void
+location_entry_set_entry (YelpLocationEntry *entry, gboolean emit)
+{
+ GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (entry));
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ gchar *icon_name;
+
+ if (entry->priv->search_mode)
+ {
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_PRIMARY,
+ "system-search");
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ "edit-clear");
+ gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ "Clear the search text");
+ return;
+ }
+
+ if (entry->priv->row)
+ path = gtk_tree_row_reference_get_path (entry->priv->row);
+
+ if (path)
+ {
+ gchar *text;
+ gint flags;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ gtk_combo_box_entry_get_text_column (GTK_COMBO_BOX_ENTRY (entry)),
+ &text,
+ entry->priv->icon_column, &icon_name,
+ entry->priv->flags_column, &flags,
+ -1);
+ if (flags & YELP_LOCATION_ENTRY_IS_LOADING)
+ {
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_PRIMARY,
+ "image-loading");
+ g_timeout_add (80, location_entry_pulse, entry);
+ }
+ else
+ {
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_PRIMARY,
+ icon_name);
+ }
+ if (flags & YELP_LOCATION_ENTRY_CAN_BOOKMARK)
+ {
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ "bookmark-new");
+ gtk_entry_set_icon_tooltip_text (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ "Bookmark this page");
+ }
+ else
+ {
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ NULL);
+ }
+ gtk_entry_set_text (GTK_ENTRY (entry->priv->text_entry), text);
+ if (emit)
+ g_signal_emit (entry, location_entry_signals[LOCATION_SELECTED], 0);
+ g_free (text);
+ gtk_tree_path_free (path);
+ }
+ else
+ {
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_PRIMARY,
+ "help-browser");
+ gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry->priv->text_entry),
+ GTK_ENTRY_ICON_SECONDARY,
+ NULL);
+ }
+}
+
+static gboolean
+location_entry_pulse (gpointer data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (data);
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gint flags;
+
+ model = gtk_tree_row_reference_get_model (entry->priv->row);
+ path = gtk_tree_row_reference_get_path (entry->priv->row);
+ if (path)
+ {
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ entry->priv->flags_column, &flags,
+ -1);
+ gtk_tree_path_free (path);
+ }
+
+ if (flags & YELP_LOCATION_ENTRY_IS_LOADING && !entry->priv->search_mode)
+ {
+ gtk_entry_progress_pulse (GTK_ENTRY (entry->priv->text_entry));
+ }
+ else
+ {
+ gtk_entry_set_progress_fraction (GTK_ENTRY (entry->priv->text_entry), 0.0);
+ }
+
+ return flags & YELP_LOCATION_ENTRY_IS_LOADING;
+}
+
+static void
+combo_box_changed_cb (GtkComboBox *widget,
+ gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (widget);
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (entry));
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (entry), &iter))
+ {
+ gint flags;
+ gtk_tree_model_get (model, &iter,
+ entry->priv->flags_column, &flags,
+ -1);
+ if (flags & YELP_LOCATION_ENTRY_IS_SEARCH)
+ {
+ location_entry_start_search (entry, TRUE);
+ }
+ else
+ {
+ path = gtk_tree_model_get_path (model, &iter);
+ if (entry->priv->row)
+ gtk_tree_row_reference_free (entry->priv->row);
+ entry->priv->row = gtk_tree_row_reference_new (model, path);
+ gtk_tree_path_free (path);
+ entry->priv->search_mode = FALSE;
+ location_entry_set_entry (YELP_LOCATION_ENTRY (widget), TRUE);
+ }
+ }
+}
+
+static gboolean
+combo_box_row_separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+ gint flags;
+ gtk_tree_model_get (model, iter,
+ entry->priv->flags_column, &flags,
+ -1);
+ return (flags & YELP_LOCATION_ENTRY_IS_SEPARATOR);
+}
+
+static void
+yelp_location_entry_row_changed (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ /* FIXME: Should we bother checking path/iter against entry->priv->row?
+ It doesn't really make a difference, since set_entry is pretty much
+ safe to call whenever. Does it make a performance impact?
+ */
+ location_entry_set_entry (YELP_LOCATION_ENTRY (user_data), FALSE);
+}
+
+static gboolean
+entry_focus_in_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer user_data)
+{
+ GtkEntry *text_entry = GTK_ENTRY (widget);
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+
+ if (entry->priv->enable_search && !entry->priv->search_mode)
+ location_entry_start_search (entry, TRUE);
+
+ return FALSE;
+}
+
+static gboolean
+entry_focus_out_cb (GtkWidget *widget,
+ GdkEventFocus *event,
+ gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+
+ if (gtk_entry_get_text_length (GTK_ENTRY (widget)) == 0)
+ {
+ entry->priv->search_mode = FALSE;
+ location_entry_set_entry (entry, FALSE);
+ }
+}
+
+static void
+entry_activate_cb (GtkEntry *text_entry,
+ gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+ gchar *text = g_strdup (gtk_entry_get_text (text_entry));
+
+ if (!entry->priv->enable_search)
+ return;
+
+ if (!entry->priv->search_mode || text == NULL || strlen(text) == 0)
+ return;
+
+ g_signal_emit (entry, location_entry_signals[SEARCH_ACTIVATED], 0, text);
+
+ g_free (text);
+}
+
+static void
+entry_icon_press_cb (GtkEntry *text_entry,
+ GtkEntryIconPosition icon_pos,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+ if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
+ {
+ const gchar *name = gtk_entry_get_icon_name (text_entry, icon_pos);
+ if (g_str_equal (name, "edit-clear"))
+ {
+ entry->priv->search_mode = FALSE;
+ location_entry_set_entry (entry, FALSE);
+ }
+ else if (g_str_equal (name, "bookmark-new"))
+ {
+ /* FIXME: emit bookmark signal */
+ }
+ }
+}
+
+static gboolean
+entry_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+ if (event->keyval == GDK_Escape)
+ {
+ entry->priv->search_mode = FALSE;
+ location_entry_set_entry (entry, FALSE);
+ return TRUE;
+ }
+ else if (!entry->priv->search_mode)
+ {
+ location_entry_start_search (entry, FALSE);
+ }
+
+ return FALSE;
+}
+
+/**
+ * yelp_location_entry_ew_with_model:
+ * @model: A #GtkTreeModel.
+ * @text_column: The column in @model containing the title of each entry.
+ * @icon_column: The column in @model containing the icon name of each entry.
+ * @bookmark_column: The column in @model containing a gboolean specifying
+ * whether or not each entry is bookmarked.
+ *
+ * Creates a new #YelpLocationEntry widget.
+ *
+ * Return value: A new #YelpLocationEntry.
+ */
+GtkWidget*
+yelp_location_entry_new_with_model (GtkTreeModel *model,
+ gint text_column,
+ gint icon_column,
+ gint flags_column)
+{
+ GtkWidget *ret;
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
+
+ ret = GTK_WIDGET (g_object_new (YELP_TYPE_LOCATION_ENTRY,
+ "model", model,
+ "text-column", text_column,
+ "icon-column", icon_column,
+ "flags-column", flags_column,
+ NULL));
+
+ /* FIXME: This isn't a good place for this. Probably should
+ * put it in a notify handler for the model property.
+ */
+ g_signal_connect (model, "row-changed",
+ G_CALLBACK (yelp_location_entry_row_changed),
+ YELP_LOCATION_ENTRY (ret));
+ return ret;
+}
+
+void
+yelp_location_entry_start_search (YelpLocationEntry *entry)
+{
+ location_entry_start_search (entry, TRUE);
+}
diff --git a/libyelp/yelp-location-entry.h b/libyelp/yelp-location-entry.h
new file mode 100644
index 0000000..11af99f
--- /dev/null
+++ b/libyelp/yelp-location-entry.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2009 Shaun McCance <shaunm gnome org>
+ *
+ * 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 Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Shaun McCance <shaunm gnome org>
+ */
+
+typedef struct _YelpLocationEntry YelpLocationEntry;
+typedef struct _YelpLocationEntryClass YelpLocationEntryClass;
+typedef struct _YelpLocationEntryPrivate YelpLocationEntryPrivate;
+
+#include <gtk/gtk.h>
+
+#define YELP_TYPE_LOCATION_ENTRY (yelp_location_entry_get_type ())
+#define YELP_LOCATION_ENTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), YELP_TYPE_LOCATION_ENTRY, \
+ YelpLocationEntry))
+#define YELP_LOCATION_ENTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), YELP_TYPE_LOCATION_ENTRY, \
+ YelpLocationEntryClass))
+#define YELP_IS_LOCATION_ENTRY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), YELP_TYPE_LOCATION_ENTRY))
+#define YELP_IS_LOCATION_ENTRY_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), YELP_TYPE_LOCATION_ENTRY))
+#define YELP_LOCATION_ENTRY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), YELP_TYPE_LOCATION_ENTRY, \
+ YelpLocationEntryClass))
+
+struct _YelpLocationEntry
+{
+ GtkComboBoxEntry parent;
+ YelpLocationEntryPrivate *priv;
+};
+
+struct _YelpLocationEntryClass
+{
+ GtkComboBoxEntryClass parent;
+};
+
+typedef enum {
+ YELP_LOCATION_ENTRY_CAN_BOOKMARK = 1 << 0,
+ YELP_LOCATION_ENTRY_IS_LOADING = 1 << 1,
+ YELP_LOCATION_ENTRY_IS_SEPARATOR = 1 << 2,
+ YELP_LOCATION_ENTRY_IS_SEARCH = 1 << 3
+} YelpLocationEntryFlags;
+
+G_BEGIN_DECLS
+
+GType yelp_location_entry_get_type (void);
+GtkWidget* yelp_location_entry_new_with_model (GtkTreeModel *model,
+ gint text_column,
+ gint icon_column,
+ gint flags_column);
+
+void yelp_location_entry_start_search (YelpLocationEntry *entry);
+
+G_END_DECLS
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 6277334..b1efc6a 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -7,7 +7,12 @@ YELP_COMMON_LDADD = \
$(YELP_LIBS) \
$(top_builddir)/libyelp/libyelp.la
-check_PROGRAMS = test-uri
+check_PROGRAMS = \
+ test-location-entry \
+ test-uri
+
+test_location_entry_CFLAGS = $(YELP_COMMON_CFLAGS)
+test_location_entry_LDADD = $(YELP_COMMON_LDADD)
test_uri_CFLAGS = $(YELP_COMMON_CFLAGS)
test_uri_LDADD = $(YELP_COMMON_LDADD)
diff --git a/tests/test-location-entry.c b/tests/test-location-entry.c
new file mode 100644
index 0000000..37200df
--- /dev/null
+++ b/tests/test-location-entry.c
@@ -0,0 +1,291 @@
+#include <gtk/gtk.h>
+#include "yelp-location-entry.h"
+
+enum {
+ COL_ICON,
+ COL_TITLE,
+ COL_FLAGS,
+ COL_URI,
+ COL_TERMS
+};
+
+YelpLocationEntry *entry;
+GtkListStore *event_list;
+
+static gboolean
+loading_callback (gpointer data)
+{
+ GtkTreeRowReference *row = (GtkTreeRowReference *) data;
+ GtkTreePath *path = gtk_tree_row_reference_get_path (row);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_row_reference_get_model (row);
+ gtk_tree_model_get_iter (model, &iter, path);
+
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ COL_ICON, "gnome-main-menu",
+ COL_TITLE, "Desktop Help",
+ COL_FLAGS, YELP_LOCATION_ENTRY_CAN_BOOKMARK,
+ -1);
+ gtk_tree_path_free (path);
+ gtk_tree_row_reference_free (row);
+ return FALSE;
+}
+
+static void
+check_clicked (GtkToggleButton *button, gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+ g_object_set (G_OBJECT (entry), "enable-search",
+ gtk_toggle_button_get_active (button), NULL);
+}
+
+static void
+search_clicked (GtkToggleButton *button, gpointer user_data)
+{
+ YelpLocationEntry *entry = YELP_LOCATION_ENTRY (user_data);
+ yelp_location_entry_start_search (entry);
+}
+
+static void
+button_clicked (GtkButton *button, gpointer user_data)
+{
+ GtkListStore *model = GTK_LIST_STORE (user_data);
+ const gchar *label = gtk_button_get_label (button);
+ GtkTreeIter iter;
+ gchar *uri = NULL, *icon, *title;
+ gboolean loading = FALSE;
+
+ if (g_str_equal (label, "Empathy"))
+ {
+ uri = "help:empathy";
+ icon = "empathy";
+ }
+ else if (g_str_equal (label, "Calculator"))
+ {
+ uri = "help:gcalctool";
+ icon = "accessories-calculator";
+ }
+ else if (g_str_equal (label, "Terminal"))
+ {
+ uri = "help:gnome-terminal";
+ icon = "gnome-terminal";
+ }
+ else if (g_str_equal (label, "Slow-loading document"))
+ {
+ uri = "help:gnumeric";
+ icon = NULL;
+ loading = TRUE;
+ }
+
+ if (uri)
+ {
+ gchar *iter_uri;
+ gboolean cont;
+ cont = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
+ while (cont)
+ {
+ gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
+ COL_URI, &iter_uri,
+ -1);
+ if (iter_uri && g_str_equal (uri, iter_uri))
+ {
+ g_free (iter_uri);
+ break;
+ }
+ else
+ {
+ g_free (iter_uri);
+ cont = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
+ }
+ }
+ if (cont)
+ {
+ GtkTreeIter first;
+ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &first);
+ gtk_list_store_move_before (model, &iter, &first);
+ }
+ else
+ {
+ gtk_list_store_prepend (model, &iter);
+ if (!loading)
+ {
+ title = g_strconcat (label, " Help", NULL);
+ }
+ else
+ {
+ GtkTreePath *path;
+ GtkTreeRowReference *row;
+ title = g_strdup ("Loading...");
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
+ row = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path);
+ gtk_tree_path_free (path);
+ g_timeout_add_seconds (5, loading_callback, row);
+ }
+ gtk_list_store_set (model, &iter,
+ COL_ICON, icon,
+ COL_TITLE, title,
+ COL_FLAGS, YELP_LOCATION_ENTRY_CAN_BOOKMARK | (loading ? YELP_LOCATION_ENTRY_IS_LOADING : 0),
+ COL_URI, uri,
+ -1);
+ g_free (title);
+ }
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (entry), &iter);
+ }
+}
+
+static void
+location_selected_cb (GtkEntry *entry, gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter, first;
+ gchar *title, *icon;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (entry));
+ gtk_combo_box_get_active_iter (GTK_COMBO_BOX (entry), &iter);
+ gtk_tree_model_get_iter_first (model, &first);
+
+ gtk_list_store_move_before (GTK_LIST_STORE (model), &iter, &first);
+
+ gtk_tree_model_get (model, &iter,
+ COL_TITLE, &title,
+ COL_ICON, &icon,
+ -1);
+
+ gtk_list_store_prepend (event_list, &iter);
+ gtk_list_store_set (event_list, &iter,
+ 0, icon,
+ 1, title,
+ -1);
+ g_free (icon);
+ g_free (title);
+}
+
+static void
+search_activated_cb (GtkEntry *entry, gchar *text, gpointer user_data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (entry));
+ gtk_list_store_prepend (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ COL_ICON, "folder-saved-search",
+ COL_TITLE, text,
+ COL_FLAGS, 0,
+ COL_URI, "search:",
+ -1);
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (entry), &iter);
+}
+
+int
+main (int argc, char **argv)
+{
+ GtkWidget *window, *vbox, *scroll, *list, *hbox, *button;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *cell;
+ GtkListStore *model;
+ GtkTreeIter iter;
+
+ gtk_init (&argc, &argv);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 450, 600);
+ gtk_container_set_border_width (GTK_CONTAINER (window), 6);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
+ model = gtk_list_store_new (5,
+ G_TYPE_STRING, /* icon name */
+ G_TYPE_STRING, /* title */
+ G_TYPE_INT, /* flags */
+ G_TYPE_STRING, /* uri */
+ G_TYPE_STRING /* search terms */
+ );
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ COL_FLAGS, YELP_LOCATION_ENTRY_IS_SEPARATOR,
+ -1);
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter,
+ COL_ICON, "system-search",
+ COL_TITLE, "Search...",
+ COL_FLAGS, YELP_LOCATION_ENTRY_IS_SEARCH,
+ -1);
+
+ entry = (YelpLocationEntry *) yelp_location_entry_new_with_model (GTK_TREE_MODEL (model),
+ COL_TITLE,
+ COL_ICON,
+ COL_FLAGS);
+ g_signal_connect (entry, "location-selected",
+ G_CALLBACK (location_selected_cb), NULL);
+ g_signal_connect (entry, "search-activated",
+ G_CALLBACK (search_activated_cb), NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (entry), FALSE, FALSE, 0);
+
+ scroll = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
+ event_list = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_list));
+ col = gtk_tree_view_column_new ();
+ gtk_tree_view_append_column (GTK_TREE_VIEW (list), col);
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (col), cell, FALSE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (col), cell,
+ "icon-name", 0,
+ NULL);
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (col), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (col), cell,
+ "text", 1,
+ NULL);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
+ gtk_container_add (GTK_CONTAINER (scroll), list);
+
+ hbox = gtk_hbox_new (TRUE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ button = gtk_check_button_new_with_label ("Enable search");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (check_clicked), entry);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+ button = gtk_button_new_with_label ("Search");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (search_clicked), entry);
+ gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, TRUE, 0);
+
+ hbox = gtk_hbox_new (TRUE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ button = gtk_button_new_with_label ("Empathy");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (button_clicked), model);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+ button = gtk_button_new_with_label ("Calculator");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (button_clicked), model);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+ button = gtk_button_new_with_label ("Terminal");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (button_clicked), model);
+ gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
+
+ button = gtk_button_new_with_label ("Slow-loading document");
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (button_clicked), model);
+ gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (GTK_WIDGET (window));
+ gtk_widget_grab_focus (list);
+
+ gtk_main ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]