From 33b5c26f419aab0c072a7ac49f450e861459b852 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 1 Jul 2015 12:51:20 -0700 Subject: [PATCH] file chooser: Add and use a model search engine This search engine reuses the GFileInfo that is already loaded for the file list, to ensure that hits from the current directory always appear promptly. --- gtk/Makefile.am | 2 + gtk/gtkfilechooserwidget.c | 13 ++- gtk/gtksearchengine.c | 57 ++++++++++- gtk/gtksearchengine.h | 4 + gtk/gtksearchenginemodel.c | 191 +++++++++++++++++++++++++++++++++++++ gtk/gtksearchenginemodel.h | 44 +++++++++ 6 files changed, 306 insertions(+), 5 deletions(-) create mode 100644 gtk/gtksearchenginemodel.c create mode 100644 gtk/gtksearchenginemodel.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 861f186370..4795f84222 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -496,6 +496,7 @@ gtk_private_h_sources = \ gtkscaleprivate.h \ gtksearchengine.h \ gtksearchenginesimple.h \ + gtksearchenginemodel.h \ gtksearchentryprivate.h \ gtkselectionprivate.h \ gtksidebarrowprivate.h \ @@ -545,6 +546,7 @@ gtk_base_c_sources = \ gtksearchentry.c \ gtksearchengine.c \ gtksearchenginesimple.c \ + gtksearchenginemodel.c \ fnmatch.c \ gtkaboutdialog.c \ gtkaccelgroup.c \ diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index eb34ef7eb7..32931702cf 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -248,6 +248,7 @@ struct _GtkFileChooserWidgetPrivate { GtkSearchEngine *search_engine; GtkQuery *search_query; GtkFileSystemModel *search_model; + GtkFileSystemModel *model_for_search; /* OPERATION_MODE_RECENT */ GtkRecentManager *recent_manager; @@ -659,6 +660,7 @@ gtk_file_chooser_widget_finalize (GObject *object) stop_loading_and_clear_list_model (impl, FALSE); search_clear_model (impl, FALSE); recent_clear_model (impl, FALSE); + g_clear_object (&impl->priv->model_for_search); /* stopping the load above should have cleared this */ g_assert (priv->load_timeout_id == 0); @@ -2600,6 +2602,7 @@ operation_mode_stop (GtkFileChooserWidget *impl, OperationMode mode) break; case OPERATION_MODE_SEARCH: + g_clear_object (&impl->priv->model_for_search); search_stop_searching (impl, FALSE); search_clear_model (impl, TRUE); break; @@ -3684,6 +3687,8 @@ load_set_model (GtkFileChooserWidget *impl) profile_msg (" gtk_tree_view_set_model end", NULL); priv->list_sort_ascending = TRUE; + g_set_object (&priv->model_for_search, priv->browse_files_model); + profile_end ("end", NULL); } @@ -6419,12 +6424,11 @@ search_clear_model (GtkFileChooserWidget *impl, { GtkFileChooserWidgetPrivate *priv = impl->priv; + g_clear_object (&priv->search_model); + if (!priv->search_model) return; - g_object_unref (priv->search_model); - priv->search_model = NULL; - if (remove_from_treeview) gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL); } @@ -6537,6 +6541,7 @@ search_start_query (GtkFileChooserWidget *impl, g_object_unref (file); } + _gtk_search_engine_set_model (priv->search_engine, priv->model_for_search); _gtk_search_engine_set_query (priv->search_engine, priv->search_query); g_signal_connect (priv->search_engine, "hits-added", @@ -6734,6 +6739,8 @@ populate_model_with_recent_items (GtkFileChooserWidget *impl, GList *items) if (limit != -1 && n >= limit) break; } + + g_set_object (&priv->model_for_search, priv->recent_model); } static void diff --git a/gtk/gtksearchengine.c b/gtk/gtksearchengine.c index 67110f936b..8a92a220c1 100644 --- a/gtk/gtksearchengine.c +++ b/gtk/gtksearchengine.c @@ -23,6 +23,7 @@ #include "gtksearchengine.h" #include "gtksearchenginesimple.h" #include "gtksearchenginetracker.h" +#include "gtksearchenginemodel.h" #include "gtksearchenginequartz.h" #include /* for GDK_WINDOWING_QUARTZ */ @@ -40,9 +41,15 @@ struct _GtkSearchEnginePrivate { gboolean simple_running; gchar *simple_error; + GtkSearchEngine *model; + gboolean model_running; + gchar *model_error; + gboolean running; gboolean recursive; GHashTable *hits; + + GtkQuery *query; }; enum @@ -61,11 +68,16 @@ static void set_query (GtkSearchEngine *engine, GtkQuery *query) { + g_set_object (&engine->priv->query, query); + if (engine->priv->native) _gtk_search_engine_set_query (engine->priv->native, query); if (engine->priv->simple) _gtk_search_engine_set_query (engine->priv->simple, query); + + if (engine->priv->model) + _gtk_search_engine_set_query (engine->priv->model, query); } static void @@ -87,6 +99,13 @@ start (GtkSearchEngine *engine) engine->priv->simple_running = TRUE; } + if (engine->priv->model) + { + g_clear_pointer (&engine->priv->model_error, g_free); + _gtk_search_engine_start (engine->priv->model); + engine->priv->model_running = TRUE; + } + engine->priv->running = TRUE; } @@ -105,6 +124,12 @@ stop (GtkSearchEngine *engine) engine->priv->simple_running = FALSE; } + if (engine->priv->model) + { + _gtk_search_engine_stop (engine->priv->model); + engine->priv->model_running = FALSE; + } + engine->priv->running = FALSE; g_hash_table_remove_all (engine->priv->hits); @@ -121,8 +146,13 @@ finalize (GObject *object) g_clear_object (&engine->priv->simple); g_free (engine->priv->simple_error); + g_clear_object (&engine->priv->model); + g_free (engine->priv->model_error); + g_clear_pointer (&engine->priv->hits, g_hash_table_unref); + g_clear_object (&engine->priv->query); + G_OBJECT_CLASS (_gtk_search_engine_parent_class)->finalize (object); } @@ -222,6 +252,8 @@ update_status (GtkSearchEngine *engine) _gtk_search_engine_error (engine, engine->priv->native_error); else if (engine->priv->simple_error) _gtk_search_engine_error (engine, engine->priv->simple_error); + else if (engine->priv->model_error) + _gtk_search_engine_error (engine, engine->priv->model_error); else _gtk_search_engine_finished (engine); } @@ -238,6 +270,8 @@ finished (GtkSearchEngine *engine, composite->priv->native_running = FALSE; else if (engine == composite->priv->simple) composite->priv->simple_running = FALSE; + else if (engine == composite->priv->model) + composite->priv->model_running = FALSE; update_status (composite); } @@ -257,10 +291,16 @@ error (GtkSearchEngine *engine, } else if (engine == composite->priv->simple) { - g_free (composite->priv->native_error); - composite->priv->native_error = g_strdup (message); + g_free (composite->priv->simple_error); + composite->priv->simple_error = g_strdup (message); composite->priv->simple_running = FALSE; } + else if (engine == composite->priv->model) + { + g_free (composite->priv->model_error); + composite->priv->model_error = g_strdup (message); + composite->priv->model_running = FALSE; + } update_status (composite); } @@ -432,3 +472,16 @@ _gtk_search_engine_get_recursive (GtkSearchEngine *engine) return engine->priv->recursive; } + +void +_gtk_search_engine_set_model (GtkSearchEngine *engine, + GtkFileSystemModel *model) +{ + g_clear_object (&engine->priv->model); + if (model) + { + engine->priv->model = _gtk_search_engine_model_new (model); + if (engine->priv->query) + _gtk_search_engine_set_query (engine->priv->model, engine->priv->query); + } +} diff --git a/gtk/gtksearchengine.h b/gtk/gtksearchengine.h index 9e92e3185d..e49eeb8c79 100644 --- a/gtk/gtksearchengine.h +++ b/gtk/gtksearchengine.h @@ -23,6 +23,7 @@ #define __GTK_SEARCH_ENGINE_H__ #include "gtkquery.h" +#include "gtkfilesystemmodel.h" #include G_BEGIN_DECLS @@ -91,6 +92,9 @@ gboolean _gtk_search_engine_get_recursive (GtkSearchEngine *engine); void _gtk_search_hit_free (GtkSearchHit *hit); GtkSearchHit *_gtk_search_hit_dup (GtkSearchHit *hit); +void _gtk_search_engine_set_model (GtkSearchEngine *engine, + GtkFileSystemModel *model); + G_END_DECLS #endif /* __GTK_SEARCH_ENGINE_H__ */ diff --git a/gtk/gtksearchenginemodel.c b/gtk/gtksearchenginemodel.c new file mode 100644 index 0000000000..93003b5b55 --- /dev/null +++ b/gtk/gtksearchenginemodel.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015 Red Hat, Inc + * + * This library 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) any later version. + * + * This library 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 this library. If not, see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include + +#include + +#include "gtksearchenginemodel.h" +#include "gtkprivate.h" + +#include + +#define BATCH_SIZE 500 + +struct _GtkSearchEngineModel +{ + GtkSearchEngine parent; + + GtkFileSystemModel *model; + GtkQuery *query; + + gboolean query_finished; + guint idle; +}; + +struct _GtkSearchEngineModelClass +{ + GtkSearchEngineClass parent_class; +}; + +G_DEFINE_TYPE (GtkSearchEngineModel, _gtk_search_engine_model, GTK_TYPE_SEARCH_ENGINE) + +static void +gtk_search_engine_model_dispose (GObject *object) +{ + GtkSearchEngineModel *model = GTK_SEARCH_ENGINE_MODEL (object); + + g_clear_object (&model->query); + g_clear_object (&model->model); + + G_OBJECT_CLASS (_gtk_search_engine_model_parent_class)->dispose (object); +} + +gboolean +info_matches_query (GtkQuery *query, + GFileInfo *info) +{ + const gchar *display_name; + + display_name = g_file_info_get_display_name (info); + if (display_name == NULL) + return FALSE; + + if (g_file_info_get_is_hidden (info)) + return FALSE; + + if (!gtk_query_matches_string (query, display_name)) + return FALSE; + + return TRUE; +} + +static gboolean +do_search (gpointer data) +{ + GtkSearchEngineModel *model = data; + GtkTreeIter iter; + GList *hits = NULL; + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model->model), &iter)) + { + do + { + GFileInfo *info; + GFile *file; + gchar *uri; + + info = _gtk_file_system_model_get_info (model->model, &iter); + if (info_matches_query (model->query, info)) + { + file = _gtk_file_system_model_get_file (model->model, &iter); + uri = g_file_get_uri (file); + hits = g_list_prepend (hits, uri); + } + } + while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model->model), &iter)); + + if (hits) + { + _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (model), hits); + g_list_free_full (hits, g_free); + } + } + + model->idle = 0; + + return G_SOURCE_REMOVE; +} + +static void +gtk_search_engine_model_start (GtkSearchEngine *engine) +{ + GtkSearchEngineModel *model; + + model = GTK_SEARCH_ENGINE_MODEL (engine); + + if (model->query == NULL) + return; + + model->idle = g_idle_add (do_search, engine); +} + +static void +gtk_search_engine_model_stop (GtkSearchEngine *engine) +{ + GtkSearchEngineModel *model; + + model = GTK_SEARCH_ENGINE_MODEL (engine); + + if (model->idle != 0) + { + g_source_remove (model->idle); + model->idle = 0; + } +} + +static void +gtk_search_engine_model_set_query (GtkSearchEngine *engine, + GtkQuery *query) +{ + GtkSearchEngineModel *model; + + model = GTK_SEARCH_ENGINE_MODEL (engine); + + if (query) + g_object_ref (query); + + if (model->query) + g_object_unref (model->query); + + model->query = query; +} + +static void +_gtk_search_engine_model_class_init (GtkSearchEngineModelClass *class) +{ + GObjectClass *gobject_class; + GtkSearchEngineClass *engine_class; + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->dispose = gtk_search_engine_model_dispose; + + engine_class = GTK_SEARCH_ENGINE_CLASS (class); + engine_class->set_query = gtk_search_engine_model_set_query; + engine_class->start = gtk_search_engine_model_start; + engine_class->stop = gtk_search_engine_model_stop; +} + +static void +_gtk_search_engine_model_init (GtkSearchEngineModel *engine) +{ +} + +GtkSearchEngine * +_gtk_search_engine_model_new (GtkFileSystemModel *model) +{ + GtkSearchEngineModel *engine; + + engine = GTK_SEARCH_ENGINE_MODEL (g_object_new (GTK_TYPE_SEARCH_ENGINE_MODEL, NULL)); + engine->model = g_object_ref (model); + + return GTK_SEARCH_ENGINE (engine); +} diff --git a/gtk/gtksearchenginemodel.h b/gtk/gtksearchenginemodel.h new file mode 100644 index 0000000000..a7cedab2a0 --- /dev/null +++ b/gtk/gtksearchenginemodel.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015 Red Hat, Inc + * + * This library 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) any later version. + * + * This library 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 this library. If not, see . + * + * Author: Matthias Clasen + */ + +#ifndef __GTK_SEARCH_ENGINE_MODEL_H__ +#define __GTK_SEARCH_ENGINE_MODEL_H__ + +#include "gtksearchengine.h" +#include "gtkfilesystemmodel.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_SEARCH_ENGINE_MODEL (_gtk_search_engine_model_get_type ()) +#define GTK_SEARCH_ENGINE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SEARCH_ENGINE_MODEL, GtkSearchEngineModel)) +#define GTK_SEARCH_ENGINE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SEARCH_ENGINE_MODEL, GtkSearchEngineModelClass)) +#define GTK_IS_SEARCH_ENGINE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SEARCH_ENGINE_MODEL)) +#define GTK_IS_SEARCH_ENGINE_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SEARCH_ENGINE_MODEL)) +#define GTK_SEARCH_ENGINE_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SEARCH_ENGINE_MODEL, GtkSearchEngineModelClass)) + +typedef struct _GtkSearchEngineModel GtkSearchEngineModel; +typedef struct _GtkSearchEngineModelClass GtkSearchEngineModelClass; + +GType _gtk_search_engine_model_get_type (void); + +GtkSearchEngine *_gtk_search_engine_model_new (GtkFileSystemModel *model); + +G_END_DECLS + +#endif /* __GTK_SEARCH_ENGINE_MODEL_H__ */