diff options
author | Benjamin Otte <otte@gnome.org> | 2009-07-08 17:37:41 +0200 |
---|---|---|
committer | Benjamin Otte <otte@gnome.org> | 2009-07-08 17:37:41 +0200 |
commit | f18f7da25030e022abab3bcb9b2a89edf3eeef3a (patch) | |
tree | 4a41704b2521e949deada9d6759346a81157d8dc | |
parent | 81ee91ce9622f8bf269fdb2b4e6b18cb617cfcbf (diff) |
Add another test application to play with entry completion
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | ephy-entry-completion.c | 313 |
3 files changed, 325 insertions, 2 deletions
@@ -1,3 +1,4 @@ *.o +ephy-entry-completion ephy-query-history @@ -1,4 +1,5 @@ APP = ephy-query-history +APP2 = ephy-entry-completion CC = gcc CFLAGS = -g -DEPIPHANY_COMPILATION -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter -Wold-style-definition -Wdeclaration-after-statement -Wmissing-declarations -Wmissing-prototypes -Wredundant-decls -Wmissing-noreturn -Wshadow -Wpointer-arith -Wcast-align -Wwrite-strings -Winline -Wformat-nonliteral -Wformat-security -Wswitch-enum -Wswitch-default -Winit-self -Wmissing-include-dirs -Wundef -Waggregate-return -Wmissing-format-attribute -Wnested-externs -Wunsafe-loop-optimizations -Wpacked -Winvalid-pch -Wlogical-op -Werror @@ -6,13 +7,18 @@ PKG = gtk+-2.0 gthread-2.0 sqlite3 PKG_CFLAGS = `pkg-config --cflags $(PKG)` PKG_LIBS = `pkg-config --libs $(PKG)` -SRCS = \ +SRCS = \ ephy-history.c \ ephy-query-history.c +SRCS2 = \ + ephy-history.c \ + ephy-entry-completion.c + OBJS = $(SRCS:.c=.o) +OBJS2 = $(SRCS2:.c=.o) -all: $(APP) +all: $(APP) $(APP2) .c.o: $(CC) $(CFLAGS) $(PKG_CFLAGS) -c $< -o $@ @@ -20,6 +26,9 @@ all: $(APP) $(APP): $(OBJS) $(CC) $(OBJS) $(PKG_LIBS) -o$(APP) +$(APP2): $(OBJS2) + $(CC) $(OBJS2) $(PKG_LIBS) -o$(APP2) + clean: rm *.o $(APP) diff --git a/ephy-entry-completion.c b/ephy-entry-completion.c new file mode 100644 index 0000000..4c0c065 --- /dev/null +++ b/ephy-entry-completion.c @@ -0,0 +1,313 @@ +/* + * Copyright © 2009 Benjamin Otte <otte@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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gtk/gtk.h> + +#include "ephy-history.h" + +enum { + LOCATION_COLUMN_ID, + LOCATION_COLUMN_FAVICON, + LOCATION_COLUMN_TEXT, + LOCATION_COLUMN_RELEVANCE, + + LOCATION_N_COLUMNS +}; + +static gboolean +return_true (GtkEntryCompletion *completion, + const char *key, + GtkTreeIter *iter, + gpointer data) +{ + return TRUE; +} + +static GtkEntryCompletion * +create_completion (void) +{ + GtkEntryCompletion *completion; + GtkCellRenderer *cell; + + completion = gtk_entry_completion_new (); + +#if 0 + cell = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), + cell, FALSE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion), + cell, "pixbuf", LOCATION_COLUMN_FAVICON); +#endif + + cell = gtk_cell_renderer_text_new (); + g_object_set (cell, + "ellipsize", PANGO_ELLIPSIZE_END, + "ellipsize-set", TRUE, + NULL); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), + cell, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion), + cell, "markup", LOCATION_COLUMN_TEXT); + gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (cell), 2); + +#if 0 + cell = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (completion), + cell, FALSE); + gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion), + cell, extracell_data_func, + entry, + NULL); +#endif + + gtk_entry_completion_set_match_func (completion, return_true, NULL, NULL); + + return completion; +} + +typedef struct { + EphyHistory *history; + GtkListStore *store; + GCancellable *cancellable; + GtkTreeIter iter; + gboolean iter_valid; +} ListStoreData; + +static void +history_more_data_cb (GObject *history, GAsyncResult *res, gpointer _data) +{ + ListStoreData *data = _data; + GSList *rows; + GError *error = NULL; + static int columns[] = { LOCATION_COLUMN_ID, LOCATION_COLUMN_TEXT, LOCATION_COLUMN_RELEVANCE }; + + rows = ephy_history_select_finish (EPHY_HISTORY (history), res, &error); + if (rows == NULL) + { + if (error) + g_error_free (error); + else + { + g_object_unref (data->cancellable); + data->cancellable = NULL; + while (data->iter_valid) + data->iter_valid = gtk_list_store_remove (data->store, + &data->iter); + } + return; + } + + if (data->iter_valid) + { + gint64 tree_id, row_id, tree_relevance, row_relevance; + GValueArray *row = rows->data; + + /* try to match rows, so we just do updates */ + gtk_tree_model_get (GTK_TREE_MODEL (data->store), + &data->iter, + LOCATION_COLUMN_ID, &tree_id, + LOCATION_COLUMN_RELEVANCE, &tree_relevance, + -1); + row_id = g_value_get_int64 (&row->values[0]); + row_relevance = g_value_get_int64 (&row->values[2]); + for (;;) + { + if (row_id == tree_id) + { + /* + * id of columns match - update values and advance iters + */ + gtk_list_store_set_value (data->store, &data->iter, + LOCATION_COLUMN_TEXT, &row->values[1]); + data->iter_valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (data->store), + &data->iter); + rows = rows->next; + if (rows == NULL) + break; + row = rows->data; + row_id = g_value_get_int64 (&row->values[0]); + row_relevance = g_value_get_int64 (&row->values[2]); + } + else if (tree_relevance >= row_relevance) + { + /* + * The row in the tree has relevance that cannot be matched anymore. + * Remove it. + */ + data->iter_valid = gtk_list_store_remove (data->store, + &data->iter); + } + else + { + /* + * Row has a higher relevance than current row in tree model. + * So insert the row before the row in the tree model. + */ + GtkTreeIter insert; + gtk_list_store_insert_before (data->store, + &insert, + &data->iter); + gtk_list_store_set_valuesv (data->store, + &insert, + columns, + row->values, + G_N_ELEMENTS (columns)); + rows = rows->next; + if (rows == NULL) + break; + row = rows->data; + row_id = g_value_get_int64 (&row->values[0]); + row_relevance = g_value_get_int64 (&row->values[2]); + /* no need to requery data that didn't change */ + continue; + } + if (!data->iter_valid) + break; + gtk_tree_model_get (GTK_TREE_MODEL (data->store), + &data->iter, + LOCATION_COLUMN_ID, &tree_id, + LOCATION_COLUMN_RELEVANCE, &tree_relevance, + -1); + } + } + + /* add rows at the end */ + for (;rows; rows = rows->next) + { + GValueArray *row = rows->data; + GtkTreeIter insert; + + gtk_list_store_insert_with_valuesv (data->store, + &insert, + G_MAXINT, /* end */ + columns, + row->values, + G_N_ELEMENTS (columns)); + } + +#if 0 + { + GtkTreeIter iter; + gint64 relevance, id; + char *text; + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (data->store), &iter)) + do { + gtk_tree_model_get (GTK_TREE_MODEL (data->store), &iter, + LOCATION_COLUMN_RELEVANCE, &relevance, + LOCATION_COLUMN_ID, &id, + LOCATION_COLUMN_TEXT, &text, + -1); + g_print ("%5lld %5lld %s\n", relevance, id, text); + g_free (text); + } + while (gtk_tree_model_iter_next (GTK_TREE_MODEL (data->store), &iter)); + g_print ("\n"); + } +#endif +} + +static void +entry_changed_cb (GtkEntry *entry, ListStoreData *data) +{ + const char *text; + char *markup; + + if (data->cancellable) + { + g_cancellable_cancel (data->cancellable); + g_object_unref (data->cancellable); + } + + data->cancellable = g_cancellable_new (); + text = gtk_entry_get_text (entry); + markup = g_regex_escape_string (text, -1); + + ephy_history_select_async (data->history, + 30, + 100, + history_more_data_cb, + data, + data->cancellable, + "select id, markup (%Q, url), n_visits from history where url match %Q order by n_visits desc limit 300", + markup, text); + data->iter_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (data->store), &data->iter); + g_free (markup); +} + +int +main (int argc, char *argv[]) +{ + static char *filename = NULL; + static GOptionEntry options[] = { + { "filename", 'f', 0, G_OPTION_ARG_FILENAME, &filename, "file to use as history instead of default", "FILENAME" }, + { NULL } + }; + GError *error = NULL; + GtkWidget *entry, *window; + GtkEntryCompletion *completion; + ListStoreData data; + + g_thread_init (NULL); + + gtk_init_with_args (&argc, &argv, + (char *) "Test location entry performance", + options, + NULL, + &error); + + if (error) + { + g_printerr ("Error parsing command line arguments: %s\n", error->message); + g_error_free (error); + return 1; + } + + data.history = ephy_history_new (filename); + data.store = gtk_list_store_new (LOCATION_N_COLUMNS, + G_TYPE_INT64, /* LOCATION_COLUMN_ID */ + GDK_TYPE_PIXBUF, /* LOCATION_COLUMN_FAVICON */ + G_TYPE_STRING, /* LOCATION_COLUMN_TEXT */ + G_TYPE_INT64); /* LOCATION_COLUMN_RELEVANCE */ + data.cancellable = NULL; + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + entry = gtk_entry_new (); + gtk_entry_set_width_chars (GTK_ENTRY (entry), 100); + gtk_container_add (GTK_CONTAINER (window), entry); + g_signal_connect (entry, + "changed", + G_CALLBACK (entry_changed_cb), + &data); + + completion = create_completion (); + gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (data.store)); + gtk_entry_set_completion (GTK_ENTRY (entry), completion); + g_object_unref (completion); + + gtk_widget_show_all (window); + gtk_main (); + + return 0; +} + |