From 51530336c844b8f73099e4b0e9ff908a6b7ad8db Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 17 Jan 2007 06:21:30 +0000 Subject: [PATCH] update search patch --- gtk+-2.10.8-search.patch | 4434 ++++++++++++++++++++++++++++++++++++++ gtk2.spec | 2 +- 2 files changed, 4435 insertions(+), 1 deletion(-) create mode 100644 gtk+-2.10.8-search.patch diff --git a/gtk+-2.10.8-search.patch b/gtk+-2.10.8-search.patch new file mode 100644 index 0000000..e1234c0 --- /dev/null +++ b/gtk+-2.10.8-search.patch @@ -0,0 +1,4434 @@ +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchenginetracker.h 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (C) 2005 Mr Jamie McCracken ++ * ++ * 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Jamie McCracken (jamiemcc@gnome.org) ++ * ++ * Based on nautilus-search-engine-tracker.h ++ */ ++ ++#ifndef __GTK_SEARCH_ENGINE_TRACKER_H__ ++#define __GTK_SEARCH_ENGINE_TRACKER_H__ ++ ++#include "gtksearchengine.h" ++ ++G_BEGIN_DECLS ++ ++#define GTK_TYPE_SEARCH_ENGINE_TRACKER (_gtk_search_engine_tracker_get_type ()) ++#define GTK_SEARCH_ENGINE_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SEARCH_ENGINE_TRACKER, GtkSearchEngineTracker)) ++#define GTK_SEARCH_ENGINE_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SEARCH_ENGINE_TRACKER, GtkSearchEngineTrackerClass)) ++#define GTK_IS_SEARCH_ENGINE_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SEARCH_ENGINE_TRACKER)) ++#define GTK_IS_SEARCH_ENGINE_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SEARCH_ENGINE_TRACKER)) ++#define GTK_SEARCH_ENGINE_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SEARCH_ENGINE_TRACKER, GtkSearchEngineTrackerClass)) ++ ++typedef struct _GtkSearchEngineTracker GtkSearchEngineTracker; ++typedef struct _GtkSearchEngineTrackerClass GtkSearchEngineTrackerClass; ++typedef struct _GtkSearchEngineTrackerPrivate GtkSearchEngineTrackerPrivate; ++ ++struct _GtkSearchEngineTracker ++{ ++ GtkSearchEngine parent; ++ ++ GtkSearchEngineTrackerPrivate *priv; ++}; ++ ++struct _GtkSearchEngineTrackerClass ++{ ++ GtkSearchEngineClass parent_class; ++}; ++ ++GType _gtk_search_engine_tracker_get_type (void); ++ ++GtkSearchEngine* _gtk_search_engine_tracker_new (void); ++ ++G_END_DECLS ++ ++#endif /* __GTK_SEARCH_ENGINE_TRACKER_H__ */ +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchengine.h 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2005 Novell, 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Anders Carlsson ++ * ++ * Based on nautilus-search-engine.h ++ */ ++ ++#ifndef __GTK_SEARCH_ENGINE_H__ ++#define __GTK_SEARCH_ENGINE_H__ ++ ++#include ++#include "gtkquery.h" ++ ++G_BEGIN_DECLS ++ ++#define GTK_TYPE_SEARCH_ENGINE (_gtk_search_engine_get_type ()) ++#define GTK_SEARCH_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SEARCH_ENGINE, GtkSearchEngine)) ++#define GTK_SEARCH_ENGINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SEARCH_ENGINE, GtkSearchEngineClass)) ++#define GTK_IS_SEARCH_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SEARCH_ENGINE)) ++#define GTK_IS_SEARCH_ENGINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SEARCH_ENGINE)) ++#define GTK_SEARCH_ENGINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SEARCH_ENGINE, GtkSearchEngineClass)) ++ ++typedef struct _GtkSearchEngine GtkSearchEngine; ++typedef struct _GtkSearchEngineClass GtkSearchEngineClass; ++typedef struct _GtkSearchEnginePrivate GtkSearchEnginePrivate; ++ ++struct _GtkSearchEngine ++{ ++ GObject parent; ++ ++ GtkSearchEnginePrivate *priv; ++}; ++ ++struct _GtkSearchEngineClass ++{ ++ GObjectClass parent_class; ++ ++ /* VTable */ ++ void (*set_query) (GtkSearchEngine *engine, ++ GtkQuery *query); ++ void (*start) (GtkSearchEngine *engine); ++ void (*stop) (GtkSearchEngine *engine); ++ gboolean (*is_indexed) (GtkSearchEngine *engine); ++ ++ /* Signals */ ++ void (*hits_added) (GtkSearchEngine *engine, ++ GList *hits); ++ void (*hits_subtracted) (GtkSearchEngine *engine, ++ GList *hits); ++ void (*finished) (GtkSearchEngine *engine); ++ void (*error) (GtkSearchEngine *engine, ++ const gchar *error_message); ++}; ++ ++GType _gtk_search_engine_get_type (void); ++gboolean _gtk_search_engine_enabled (void); ++ ++GtkSearchEngine* _gtk_search_engine_new (void); ++ ++void _gtk_search_engine_set_query (GtkSearchEngine *engine, ++ GtkQuery *query); ++void _gtk_search_engine_start (GtkSearchEngine *engine); ++void _gtk_search_engine_stop (GtkSearchEngine *engine); ++gboolean _gtk_search_engine_is_indexed (GtkSearchEngine *engine); ++ ++void _gtk_search_engine_hits_added (GtkSearchEngine *engine, ++ GList *hits); ++void _gtk_search_engine_hits_subtracted (GtkSearchEngine *engine, ++ GList *hits); ++void _gtk_search_engine_finished (GtkSearchEngine *engine); ++void _gtk_search_engine_error (GtkSearchEngine *engine, ++ const gchar *error_message); ++ ++G_END_DECLS ++ ++#endif /* __GTK_SEARCH_ENGINE_H__ */ +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchenginebeagle.h 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (C) 2005 Novell, 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Anders Carlsson ++ * ++ * Based on nautilus-search-engine-beagle.h ++ */ ++ ++#ifndef __GTK_SEARCH_ENGINE_BEAGLE_H__ ++#define __GTK_SEARCH_ENGINE_BEAGLE_H__ ++ ++#include "gtksearchengine.h" ++ ++G_BEGIN_DECLS ++ ++#define GTK_TYPE_SEARCH_ENGINE_BEAGLE (_gtk_search_engine_beagle_get_type ()) ++#define GTK_SEARCH_ENGINE_BEAGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SEARCH_ENGINE_BEAGLE, GtkSearchEngineBeagle)) ++#define GTK_SEARCH_ENGINE_BEAGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SEARCH_ENGINE_BEAGLE, GtkSearchEngineBeagleClass)) ++#define GTK_IS_SEARCH_ENGINE_BEAGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SEARCH_ENGINE_BEAGLE)) ++#define GTK_IS_SEARCH_ENGINE_BEAGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SEARCH_ENGINE_BEAGLE)) ++#define GTK_SEARCH_ENGINE_BEAGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SEARCH_ENGINE_BEAGLE, GtkSearchEngineBeagleClass)) ++ ++typedef struct _GtkSearchEngineBeagle GtkSearchEngineBeagle; ++typedef struct _GtkSearchEngineBeagleClass GtkSearchEngineBeagleClass; ++typedef struct _GtkSearchEngineBeaglePrivate GtkSearchEngineBeaglePrivate; ++ ++struct _GtkSearchEngineBeagle ++{ ++ GtkSearchEngine parent; ++ ++ GtkSearchEngineBeaglePrivate *priv; ++}; ++ ++struct _GtkSearchEngineBeagleClass ++{ ++ GtkSearchEngineClass parent_class; ++}; ++ ++GType _gtk_search_engine_beagle_get_type (void); ++ ++GtkSearchEngine* _gtk_search_engine_beagle_new (void); ++ ++G_END_DECLS ++ ++#endif /* __GTK_SEARCH_ENGINE_BEAGLE_H__ */ +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchengine.c 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,197 @@ ++/* ++ * Copyright (C) 2005 Novell, 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Anders Carlsson ++ * ++ * Based on nautilus-search-engine.c ++ */ ++ ++#include ++#include "gtksearchengine.h" ++#include "gtksearchenginebeagle.h" ++#include "gtksearchenginesimple.h" ++#include "gtksearchenginetracker.h" ++ ++#define HAVE_BEAGLE 1 ++#define HAVE_TRACKER 1 ++ ++enum ++{ ++ HITS_ADDED, ++ HITS_SUBTRACTED, ++ FINISHED, ++ ERROR, ++ LAST_SIGNAL ++}; ++ ++static guint signals[LAST_SIGNAL]; ++ ++G_DEFINE_ABSTRACT_TYPE (GtkSearchEngine, _gtk_search_engine, G_TYPE_OBJECT); ++ ++static void ++finalize (GObject *object) ++{ ++ G_OBJECT_CLASS (_gtk_search_engine_parent_class)->finalize (object); ++} ++ ++static void ++_gtk_search_engine_class_init (GtkSearchEngineClass *class) ++{ ++ GObjectClass *gobject_class; ++ ++ gobject_class = G_OBJECT_CLASS (class); ++ gobject_class->finalize = finalize; ++ ++ signals[HITS_ADDED] = ++ g_signal_new ("hits-added", ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GtkSearchEngineClass, hits_added), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__POINTER, ++ G_TYPE_NONE, 1, ++ G_TYPE_POINTER); ++ ++ signals[HITS_SUBTRACTED] = ++ g_signal_new ("hits-subtracted", ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GtkSearchEngineClass, hits_subtracted), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__POINTER, ++ G_TYPE_NONE, 1, ++ G_TYPE_POINTER); ++ ++ signals[FINISHED] = ++ g_signal_new ("finished", ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GtkSearchEngineClass, finished), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); ++ ++ signals[ERROR] = ++ g_signal_new ("error", ++ G_TYPE_FROM_CLASS (class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GtkSearchEngineClass, error), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__STRING, ++ G_TYPE_NONE, 1, ++ G_TYPE_STRING); ++} ++ ++static void ++_gtk_search_engine_init (GtkSearchEngine *engine) ++{ ++} ++ ++GtkSearchEngine * ++_gtk_search_engine_new (void) ++{ ++ GtkSearchEngine *engine; ++ ++#ifdef HAVE_BEAGLE ++ engine = _gtk_search_engine_beagle_new (); ++ if (engine) ++ return engine; ++#endif ++ ++#ifdef HAVE_TRACKER ++ engine = _gtk_search_engine_tracker_new (); ++ if (engine) ++ return engine; ++#endif ++ ++ engine = _gtk_search_engine_simple_new (); ++ return engine; ++} ++ ++void ++_gtk_search_engine_set_query (GtkSearchEngine *engine, ++ GtkQuery *query) ++{ ++ g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine)); ++ g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query != NULL); ++ ++ GTK_SEARCH_ENGINE_GET_CLASS (engine)->set_query (engine, query); ++} ++ ++void ++_gtk_search_engine_start (GtkSearchEngine *engine) ++{ ++ g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine)); ++ g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->start != NULL); ++ ++ GTK_SEARCH_ENGINE_GET_CLASS (engine)->start (engine); ++} ++ ++void ++_gtk_search_engine_stop (GtkSearchEngine *engine) ++{ ++ g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine)); ++ g_return_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop != NULL); ++ ++ GTK_SEARCH_ENGINE_GET_CLASS (engine)->stop (engine); ++} ++ ++gboolean ++_gtk_search_engine_is_indexed (GtkSearchEngine *engine) ++{ ++ g_return_val_if_fail (GTK_IS_SEARCH_ENGINE (engine), FALSE); ++ g_return_val_if_fail (GTK_SEARCH_ENGINE_GET_CLASS (engine)->is_indexed != NULL, FALSE); ++ ++ return GTK_SEARCH_ENGINE_GET_CLASS (engine)->is_indexed (engine); ++} ++ ++void ++_gtk_search_engine_hits_added (GtkSearchEngine *engine, ++ GList *hits) ++{ ++ g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine)); ++ ++ g_signal_emit (engine, signals[HITS_ADDED], 0, hits); ++} ++ ++ ++void ++_gtk_search_engine_hits_subtracted (GtkSearchEngine *engine, ++ GList *hits) ++{ ++ g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine)); ++ ++ g_signal_emit (engine, signals[HITS_SUBTRACTED], 0, hits); ++} ++ ++ ++void ++_gtk_search_engine_finished (GtkSearchEngine *engine) ++{ ++ g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine)); ++ ++ g_signal_emit (engine, signals[FINISHED], 0); ++} ++ ++void ++_gtk_search_engine_error (GtkSearchEngine *engine, ++ const gchar *error_message) ++{ ++ g_return_if_fail (GTK_IS_SEARCH_ENGINE (engine)); ++ ++ g_signal_emit (engine, signals[ERROR], 0, error_message); ++} +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchenginetracker.c 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,362 @@ ++/* ++ * Copyright (C) 2005 Mr Jamie McCracken ++ * ++ * 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Jamie McCracken ++ * ++ * Based on nautilus-search-engine-tracker.c ++ */ ++ ++#include ++#include ++#include "gtksearchenginetracker.h" ++#if 0 ++#include ++#endif ++ ++typedef struct _TrackerClient TrackerClient; ++ ++typedef void (*TrackerArrayReply) (char **result, GError *error, gpointer user_data); ++ ++static TrackerClient * (*tracker_connect) (gboolean enable_warnings) = NULL; ++static void (*tracker_disconnect) (TrackerClient *client) = NULL; ++static void (*tracker_cancel_last_call) (TrackerClient *client) = NULL; ++ ++static void (*tracker_search_metadata_by_text_async) (TrackerClient *client, ++ const char *query, ++ TrackerArrayReply callback, ++ gpointer user_data) = NULL; ++static void (*tracker_search_metadata_by_text_and_mime_async) (TrackerClient *client, ++ const char *query, ++ const char **mimes, ++ TrackerArrayReply callback, ++ gpointer user_data) = NULL; ++static void (*tracker_search_metadata_by_text_and_location_async) (TrackerClient *client, ++ const char *query, ++ const char *location, ++ TrackerArrayReply callback, ++ gpointer user_data) = NULL; ++static void (*tracker_search_metadata_by_text_and_mime_and_location_async) (TrackerClient *client, ++ const char *query, ++ const char **mimes, ++ const char *location, ++ TrackerArrayReply callback, ++ gpointer user_data) = NULL; ++ ++static struct TrackerDlMapping ++{ ++ const char *fn_name; ++ gpointer *fn_ptr_ref; ++} tracker_dl_mapping[] = ++{ ++#define MAP(a) { #a, (gpointer *)&a } ++ MAP (tracker_connect), ++ MAP (tracker_disconnect), ++ MAP (tracker_cancel_last_call), ++ MAP (tracker_search_metadata_by_text_async), ++ MAP (tracker_search_metadata_by_text_and_mime_async), ++ MAP (tracker_search_metadata_by_text_and_location_async), ++ MAP (tracker_search_metadata_by_text_and_mime_and_location_async) ++#undef MAP ++}; ++ ++static void ++open_libtracker (void) ++{ ++ static gboolean done = FALSE; ++ ++ if (!done) ++ { ++ int i; ++ GModule *tracker; ++ ++ done = TRUE; ++ ++ tracker = g_module_open ("libtracker.so.0", G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); ++ if (!tracker) ++ return; ++ ++ for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++) ++ { ++ if (!g_module_symbol (tracker, tracker_dl_mapping[i].fn_name, ++ tracker_dl_mapping[i].fn_ptr_ref)) ++ { ++ g_warning ("Missing symbol '%s' in libtracker\n", ++ tracker_dl_mapping[i].fn_name); ++ g_module_close (tracker); ++ ++ for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++) ++ tracker_dl_mapping[i].fn_ptr_ref = NULL; ++ ++ return; ++ } ++ } ++ } ++} ++ ++struct _GtkSearchEngineTrackerPrivate ++{ ++ GtkQuery *query; ++ TrackerClient *client; ++ gboolean query_pending; ++}; ++ ++G_DEFINE_TYPE (GtkSearchEngineTracker, _gtk_search_engine_tracker, GTK_TYPE_SEARCH_ENGINE); ++ ++ ++static void ++finalize (GObject *object) ++{ ++ GtkSearchEngineTracker *tracker; ++ ++ tracker = GTK_SEARCH_ENGINE_TRACKER (object); ++ ++ if (tracker->priv->query) ++ { ++ g_object_unref (tracker->priv->query); ++ tracker->priv->query = NULL; ++ } ++ ++ tracker_disconnect (tracker->priv->client); ++ ++ G_OBJECT_CLASS (_gtk_search_engine_tracker_parent_class)->finalize (object); ++} ++ ++ ++static void ++search_callback (gchar **results, ++ GError *error, ++ gpointer user_data) ++{ ++ GtkSearchEngineTracker *tracker; ++ gchar **results_p; ++ GList *hit_uris; ++ ++ tracker = GTK_SEARCH_ENGINE_TRACKER (user_data); ++ hit_uris = NULL; ++ ++ tracker->priv->query_pending = FALSE; ++ ++ if (error) ++ { ++ _gtk_search_engine_error ( GTK_SEARCH_ENGINE (tracker), error->message); ++ g_error_free (error); ++ return; ++ } ++ ++ if (!results) ++ return; ++ ++ for (results_p = results; *results_p; results_p++) ++ { ++ gchar *uri; ++ ++ uri = g_filename_to_uri ((char *)*results_p, NULL, NULL); ++ if (uri) ++ hit_uris = g_list_prepend (hit_uris, (char *)uri); ++ } ++ ++ _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (tracker), hit_uris); ++ _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker)); ++ ++ g_strfreev (results); ++ g_list_foreach (hit_uris, (GFunc)g_free, NULL); ++ g_list_free (hit_uris); ++} ++ ++ ++static void ++gtk_search_engine_tracker_start (GtkSearchEngine *engine) ++{ ++ GtkSearchEngineTracker *tracker; ++ GList *mimetypes, *l; ++ gchar *search_text, *location, *location_uri; ++ gchar **mimes; ++ gint i, mime_count; ++ ++ tracker = GTK_SEARCH_ENGINE_TRACKER (engine); ++ ++ if (tracker->priv->query_pending) ++ return; ++ ++ if (tracker->priv->query == NULL) ++ return; ++ ++ search_text = _gtk_query_get_text (tracker->priv->query); ++ ++ mimetypes = _gtk_query_get_mime_types (tracker->priv->query); ++ ++ location_uri = _gtk_query_get_location (tracker->priv->query); ++ ++ if (location_uri) ++ { ++ location = g_filename_from_uri (location_uri, NULL, NULL); ++ g_free (location_uri); ++ } ++ else ++ { ++ location = NULL; ++ } ++ ++ mime_count = g_list_length (mimetypes); ++ ++ i = 0; ++ ++ /* convert list into array */ ++ if (mime_count > 0) ++ { ++ mimes = g_new (gchar *, (mime_count + 1)); ++ ++ for (l = mimetypes; l != NULL; l = l->next) ++ { ++ mimes[i] = g_strdup (l->data); ++ i++; ++ } ++ ++ mimes[mime_count] = NULL; ++ ++ if (location) ++ { ++ tracker_search_metadata_by_text_and_mime_and_location_async (tracker->priv->client, ++ search_text, (const char **)mimes, location, ++ search_callback, ++ tracker); ++ g_free (location); ++ } ++ else ++ { ++ tracker_search_metadata_by_text_and_mime_async (tracker->priv->client, ++ search_text, (const char**)mimes, ++ search_callback, ++ tracker); ++ } ++ ++ g_strfreev (mimes); ++ ++ ++ } ++ else ++ { ++ if (location) ++ { ++ tracker_search_metadata_by_text_and_location_async (tracker->priv->client, ++ search_text, ++ location, ++ search_callback, ++ tracker); ++ g_free (location); ++ } ++ else ++ { ++ tracker_search_metadata_by_text_async (tracker->priv->client, ++ search_text, ++ search_callback, ++ tracker); ++ } ++ } ++ ++ tracker->priv->query_pending = TRUE; ++ g_free (search_text); ++ g_list_foreach (mimetypes, (GFunc)g_free, NULL); ++ g_list_free (mimetypes); ++} ++ ++static void ++gtk_search_engine_tracker_stop (GtkSearchEngine *engine) ++{ ++ GtkSearchEngineTracker *tracker; ++ ++ tracker = GTK_SEARCH_ENGINE_TRACKER (engine); ++ ++ if (tracker->priv->query && tracker->priv->query_pending) ++ { ++ tracker_cancel_last_call (tracker->priv->client); ++ tracker->priv->query_pending = FALSE; ++ } ++} ++ ++static gboolean ++gtk_search_engine_tracker_is_indexed (GtkSearchEngine *engine) ++{ ++ return TRUE; ++} ++ ++static void ++gtk_search_engine_tracker_set_query (GtkSearchEngine *engine, ++ GtkQuery *query) ++{ ++ GtkSearchEngineTracker *tracker; ++ ++ tracker = GTK_SEARCH_ENGINE_TRACKER (engine); ++ ++ if (query) ++ g_object_ref (query); ++ ++ if (tracker->priv->query) ++ g_object_unref (tracker->priv->query); ++ ++ tracker->priv->query = query; ++} ++ ++static void ++_gtk_search_engine_tracker_class_init (GtkSearchEngineTrackerClass *class) ++{ ++ GObjectClass *gobject_class; ++ GtkSearchEngineClass *engine_class; ++ ++ gobject_class = G_OBJECT_CLASS (class); ++ gobject_class->finalize = finalize; ++ ++ engine_class = GTK_SEARCH_ENGINE_CLASS (class); ++ engine_class->set_query = gtk_search_engine_tracker_set_query; ++ engine_class->start = gtk_search_engine_tracker_start; ++ engine_class->stop = gtk_search_engine_tracker_stop; ++ engine_class->is_indexed = gtk_search_engine_tracker_is_indexed; ++ ++ g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineTrackerPrivate)); ++} ++ ++static void ++_gtk_search_engine_tracker_init (GtkSearchEngineTracker *engine) ++{ ++ engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, GTK_TYPE_SEARCH_ENGINE_TRACKER, GtkSearchEngineTrackerPrivate); ++} ++ ++ ++GtkSearchEngine * ++_gtk_search_engine_tracker_new (void) ++{ ++ GtkSearchEngineTracker *engine; ++ TrackerClient *tracker_client; ++ ++ open_libtracker (); ++ ++ if (!tracker_connect) ++ return NULL; ++ ++ tracker_client = tracker_connect (FALSE); ++ ++ if (!tracker_client) ++ return NULL; ++ ++ engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_TRACKER, NULL); ++ ++ engine->priv->client = tracker_client; ++ ++ engine->priv->query_pending = FALSE; ++ ++ return GTK_SEARCH_ENGINE (engine); ++} +--- gtk+-2.10.8/gtk/Makefile.am.search 2007-01-16 22:14:43.000000000 -0500 ++++ gtk+-2.10.8/gtk/Makefile.am 2007-01-17 01:21:08.000000000 -0500 +@@ -332,6 +332,11 @@ + + # GTK+ header files that don't get installed + gtk_private_h_sources = \ ++ gtkquery.h \ ++ gtksearchengine.h \ ++ gtksearchenginebeagle.h \ ++ gtksearchenginetracker.h\ ++ gtksearchenginesimple.h \ + gtkdndcursors.h \ + gtkentryprivate.h \ + gtkfilechooserdefault.h \ +@@ -374,6 +379,11 @@ + + # GTK+ C sources to build the library from + gtk_base_c_sources = \ ++ gtkquery.c \ ++ gtksearchengine.c \ ++ gtksearchenginebeagle.c \ ++ gtksearchenginetracker.c\ ++ gtksearchenginesimple.c \ + fnmatch.c \ + gtkaboutdialog.c \ + gtkaccelgroup.c \ +--- gtk+-2.10.8/gtk/gtkfilechooserdefault.c.search 2007-01-16 22:14:43.000000000 -0500 ++++ gtk+-2.10.8/gtk/gtkfilechooserdefault.c 2007-01-17 01:21:08.000000000 -0500 +@@ -78,6 +78,8 @@ + #include + #include + #include ++#include ++#include + + + +@@ -171,13 +173,20 @@ + SHORTCUTS_COL_PIXBUF, + SHORTCUTS_COL_NAME, + SHORTCUTS_COL_DATA, +- SHORTCUTS_COL_IS_VOLUME, ++ SHORTCUTS_COL_TYPE, + SHORTCUTS_COL_REMOVABLE, + SHORTCUTS_COL_PIXBUF_VISIBLE, + SHORTCUTS_COL_HANDLE, + SHORTCUTS_COL_NUM_COLUMNS + }; + ++typedef enum { ++ SHORTCUT_TYPE_PATH, ++ SHORTCUT_TYPE_VOLUME, ++ SHORTCUT_TYPE_SEPARATOR, ++ SHORTCUT_TYPE_SEARCH ++} ShortcutType; ++ + /* Column numbers for the file list */ + enum { + FILE_LIST_COL_NAME, +@@ -186,6 +195,16 @@ + FILE_LIST_COL_NUM_COLUMNS + }; + ++/* Column numbers for the search model. ++ * Keep this in sync with search_setup_model() ++ */ ++enum { ++ SEARCH_MODEL_COL_PATH, ++ SEARCH_MODEL_COL_DISPLAY_NAME, ++ SEARCH_MODEL_COL_COLLATION_KEY, ++ SEARCH_MODEL_COL_STAT ++}; ++ + /* Identifiers for target types */ + enum { + GTK_TREE_MODEL_ROW, +@@ -225,9 +244,19 @@ + static const int num_file_list_dest_targets = (sizeof (file_list_dest_targets) + / sizeof (file_list_dest_targets[0])); + ++static gboolean ++search_is_possible (GtkFileChooserDefault *impl) ++{ ++ if (impl->search_engine == NULL) ++ impl->search_engine = _gtk_search_engine_new (); ++ ++ return impl->search_engine != NULL; ++} + + /* Interesting places in the shortcuts bar */ + typedef enum { ++ SHORTCUTS_SEARCH, ++ SHORTCUTS_SEARCH_SEPARATOR, + SHORTCUTS_HOME, + SHORTCUTS_DESKTOP, + SHORTCUTS_VOLUMES, +@@ -419,6 +448,14 @@ + GtkFileChooserDefault *impl); + static void location_switch_to_path_bar (GtkFileChooserDefault *impl); + ++static void search_stop_searching (GtkFileChooserDefault *impl); ++static void search_clear_model (GtkFileChooserDefault *impl, ++ gboolean remove_from_treeview); ++static gboolean search_should_respond (GtkFileChooserDefault *impl); ++static void search_switch_to_browse_mode (GtkFileChooserDefault *impl); ++static GSList *search_get_selected_paths (GtkFileChooserDefault *impl); ++static void search_entry_activate_cb (GtkEntry *entry, ++ gpointer data); + + + +@@ -428,26 +465,26 @@ + GtkTreeModelFilter parent; + + GtkFileChooserDefault *impl; +-} ShortcutsModelFilter; ++} ShortcutsPaneModelFilter; + + typedef struct { + GtkTreeModelFilterClass parent_class; +-} ShortcutsModelFilterClass; ++} ShortcutsPaneModelFilterClass; + +-#define SHORTCUTS_MODEL_FILTER_TYPE (_shortcuts_model_filter_get_type ()) +-#define SHORTCUTS_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_FILTER_TYPE, ShortcutsModelFilter)) ++#define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ()) ++#define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter)) + +-static void shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface); ++static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface); + +-G_DEFINE_TYPE_WITH_CODE (ShortcutsModelFilter, +- _shortcuts_model_filter, ++G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter, ++ _shortcuts_pane_model_filter, + GTK_TYPE_TREE_MODEL_FILTER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, +- shortcuts_model_filter_drag_source_iface_init)) ++ shortcuts_pane_model_filter_drag_source_iface_init)) + +-static GtkTreeModel *shortcuts_model_filter_new (GtkFileChooserDefault *impl, +- GtkTreeModel *child_model, +- GtkTreePath *root); ++static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl, ++ GtkTreeModel *child_model, ++ GtkTreePath *root); + + + +@@ -663,6 +700,7 @@ + impl->reload_state = RELOAD_EMPTY; + impl->pending_select_paths = NULL; + impl->location_mode = LOCATION_MODE_PATH_BAR; ++ impl->operation_mode = OPERATION_MODE_BROWSE; + + gtk_box_set_spacing (GTK_BOX (impl), 12); + +@@ -678,22 +716,24 @@ + GtkTreeIter *iter) + { + gpointer col_data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + GtkFileSystemHandle *handle; + + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + SHORTCUTS_COL_HANDLE, &handle, + -1); + + if (handle) + gtk_file_system_cancel_operation (handle); + +- if (!col_data) ++ if (!(shortcut_type == SHORTCUT_TYPE_PATH || ++ shortcut_type == SHORTCUT_TYPE_VOLUME) || ++ !col_data) + return; + +- if (is_volume) ++ if (shortcut_type == SHORTCUT_TYPE_VOLUME) + { + GtkFileSystemVolume *volume; + +@@ -704,6 +744,8 @@ + { + GtkFilePath *path; + ++ g_assert (shortcut_type == SHORTCUT_TYPE_PATH); ++ + path = col_data; + gtk_file_path_free (path); + } +@@ -790,8 +832,11 @@ + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object); + GSList *l; + +- if (impl->shortcuts_filter_model) +- g_object_unref (impl->shortcuts_filter_model); ++ if (impl->shortcuts_pane_filter_model) ++ g_object_unref (impl->shortcuts_pane_filter_model); ++ ++ if (impl->shortcuts_combo_filter_model) ++ g_object_unref (impl->shortcuts_combo_filter_model); + + shortcuts_free (impl); + +@@ -829,6 +874,8 @@ + if (impl->sort_model) + g_object_unref (impl->sort_model); + ++ search_clear_model (impl, FALSE); ++ + g_free (impl->preview_display_name); + + g_free (impl->edited_new_text); +@@ -1080,6 +1127,14 @@ + update_preview_widget_visibility (impl); + } + ++/* Renders a "Search" icon at an appropriate size for a tree view */ ++static GdkPixbuf * ++render_search_icon (GtkFileChooserDefault *impl) ++{ ++ return gtk_widget_render_icon (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_SMALL_TOOLBAR, NULL); ++} ++ ++ + /* Re-reads all the icons for the shortcuts, used when the theme changes */ + struct ReloadIconsData + { +@@ -1150,19 +1205,20 @@ + do + { + gpointer data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + gboolean pixbuf_visible; + GdkPixbuf *pixbuf; + + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, + SHORTCUTS_COL_DATA, &data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible, + -1); + +- if (pixbuf_visible && data) ++ pixbuf = NULL; ++ if (pixbuf_visible) + { +- if (is_volume) ++ if (shortcut_type == SHORTCUT_TYPE_VOLUME) + { + GtkFileSystemVolume *volume; + +@@ -1177,46 +1233,53 @@ + if (pixbuf) + g_object_unref (pixbuf); + } +- else if (gtk_file_system_path_is_local (impl->file_system, (GtkFilePath *)data)) +- { +- const GtkFilePath *path; +- struct ReloadIconsData *info; +- GtkTreePath *tree_path; +- GtkFileSystemHandle *handle; +- +- path = data; +- +- info = g_new0 (struct ReloadIconsData, 1); +- info->impl = g_object_ref (impl); +- tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter); +- info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path); +- gtk_tree_path_free (tree_path); +- +- handle = gtk_file_system_get_info (impl->file_system, path, +- GTK_FILE_INFO_ICON, +- shortcuts_reload_icons_get_info_cb, +- info); +- impl->reload_icon_handles = g_slist_append (impl->reload_icon_handles, handle); +- } +- else ++ else if (shortcut_type == SHORTCUT_TYPE_PATH) ++ { ++ if (gtk_file_system_path_is_local (impl->file_system, (GtkFilePath *)data)) ++ { ++ const GtkFilePath *path; ++ struct ReloadIconsData *info; ++ GtkTreePath *tree_path; ++ GtkFileSystemHandle *handle; ++ ++ path = data; ++ ++ info = g_new0 (struct ReloadIconsData, 1); ++ info->impl = g_object_ref (impl); ++ tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter); ++ info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), tree_path); ++ gtk_tree_path_free (tree_path); ++ ++ handle = gtk_file_system_get_info (impl->file_system, path, ++ GTK_FILE_INFO_ICON, ++ shortcuts_reload_icons_get_info_cb, ++ info); ++ impl->reload_icon_handles = g_slist_append (impl->reload_icon_handles, handle); ++ } ++ else ++ { ++ GtkIconTheme *icon_theme; ++ ++ /* Don't call get_info for remote paths to avoid latency and ++ * auth dialogs. ++ * If we switch to a better bookmarks file format (XBEL), we ++ * should use mime info to get a better icon. ++ */ ++ icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); ++ pixbuf = gtk_icon_theme_load_icon (icon_theme, "gnome-fs-directory", ++ impl->icon_size, 0, NULL); ++ ++ gtk_list_store_set (impl->shortcuts_model, &iter, ++ SHORTCUTS_COL_PIXBUF, pixbuf, ++ -1); ++ ++ if (pixbuf) ++ g_object_unref (pixbuf); ++ } ++ } ++ else if (shortcut_type == SHORTCUT_TYPE_SEARCH) + { +- GtkIconTheme *icon_theme; +- +- /* Don't call get_info for remote paths to avoid latency and +- * auth dialogs. +- * If we switch to a better bookmarks file format (XBEL), we +- * should use mime info to get a better icon. +- */ +- icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); +- pixbuf = gtk_icon_theme_load_icon (icon_theme, "gnome-fs-directory", +- impl->icon_size, 0, NULL); +- +- gtk_list_store_set (impl->shortcuts_model, &iter, +- SHORTCUTS_COL_PIXBUF, pixbuf, +- -1); +- +- if (pixbuf) +- g_object_unref (pixbuf); ++ pixbuf = render_search_icon (impl); + } + } + } +@@ -1348,7 +1411,6 @@ + { + gint pos = -1; + gboolean cancelled = handle->cancelled; +- gboolean is_volume = FALSE; + GdkPixbuf *pixbuf; + GtkTreePath *path; + GtkTreeIter iter; +@@ -1413,12 +1475,15 @@ + SHORTCUTS_COL_PIXBUF, pixbuf, + SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE, + SHORTCUTS_COL_NAME, request->label_copy, +- SHORTCUTS_COL_IS_VOLUME, is_volume, ++ SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_PATH, + SHORTCUTS_COL_REMOVABLE, request->removable, + -1); + +- if (request->impl->shortcuts_filter_model) +- gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_filter_model)); ++ if (request->impl->shortcuts_pane_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_pane_filter_model)); ++ ++ if (request->impl->shortcuts_combo_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->shortcuts_combo_filter_model)); + + if (request->type == SHORTCUTS_CURRENT_FOLDER + && request->impl->save_folder_combo != NULL) +@@ -1430,7 +1495,7 @@ + g_signal_handlers_block_by_func (request->impl->save_folder_combo, + G_CALLBACK (save_folder_combo_changed_cb), + request->impl); +- gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), pos); ++ gtk_combo_box_set_active (GTK_COMBO_BOX (request->impl->save_folder_combo), request->impl->has_search ? pos - 2 : pos); + g_signal_handlers_unblock_by_func (request->impl->save_folder_combo, + G_CALLBACK (save_folder_combo_changed_cb), + request->impl); +@@ -1504,7 +1569,7 @@ + static void + shortcuts_insert_path (GtkFileChooserDefault *impl, + int pos, +- gboolean is_volume, ++ ShortcutType shortcut_type, + GtkFileSystemVolume *volume, + const GtkFilePath *path, + const char *label, +@@ -1517,79 +1582,89 @@ + GtkTreeIter iter; + GtkIconTheme *icon_theme; + +- profile_start ("start", is_volume ? "volume" : (char *) path); ++ profile_start ("start", (shortcut_type == SHORTCUT_TYPE_VOLUME) ? "volume" ++ : ((shortcut_type == SHORTCUT_TYPE_PATH) ? (char *) path : NULL)); + +- if (is_volume) ++ if (shortcut_type == SHORTCUT_TYPE_VOLUME) + { + data = volume; + label_copy = gtk_file_system_volume_get_display_name (impl->file_system, volume); + pixbuf = gtk_file_system_volume_render_icon (impl->file_system, volume, GTK_WIDGET (impl), + impl->icon_size, NULL); + } +- else if (gtk_file_system_path_is_local (impl->file_system, path)) ++ else if (shortcut_type == SHORTCUT_TYPE_PATH) + { +- struct ShortcutsInsertRequest *request; +- GtkFileSystemHandle *handle; +- GtkTreePath *p; +- +- request = g_new0 (struct ShortcutsInsertRequest, 1); +- request->impl = g_object_ref (impl); +- request->path = gtk_file_path_copy (path); +- request->name_only = TRUE; +- request->removable = removable; +- request->pos = pos; +- request->type = type; +- if (label) +- request->label_copy = g_strdup (label); ++ if (gtk_file_system_path_is_local (impl->file_system, path)) ++ { ++ struct ShortcutsInsertRequest *request; ++ GtkFileSystemHandle *handle; ++ GtkTreePath *p; ++ ++ request = g_new0 (struct ShortcutsInsertRequest, 1); ++ request->impl = g_object_ref (impl); ++ request->path = gtk_file_path_copy (path); ++ request->name_only = TRUE; ++ request->removable = removable; ++ request->pos = pos; ++ request->type = type; ++ if (label) ++ request->label_copy = g_strdup (label); ++ ++ if (pos == -1) ++ gtk_list_store_append (impl->shortcuts_model, &iter); ++ else ++ gtk_list_store_insert (impl->shortcuts_model, &iter, pos); ++ ++ p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter); ++ request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p); ++ gtk_tree_path_free (p); ++ ++ handle = gtk_file_system_get_info (request->impl->file_system, request->path, ++ GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN | GTK_FILE_INFO_ICON, ++ get_file_info_finished, request); ++ ++ gtk_list_store_set (impl->shortcuts_model, &iter, ++ SHORTCUTS_COL_DATA, gtk_file_path_copy (path), ++ SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_PATH, ++ SHORTCUTS_COL_HANDLE, handle, ++ -1); + +- if (pos == -1) +- gtk_list_store_append (impl->shortcuts_model, &iter); ++ shortcuts_update_count (impl, type, 1); ++ ++ return; ++ } + else +- gtk_list_store_insert (impl->shortcuts_model, &iter, pos); ++ { ++ /* Don't call get_info for remote paths to avoid latency and ++ * auth dialogs. ++ */ ++ data = gtk_file_path_copy (path); ++ if (label) ++ label_copy = g_strdup (label); ++ else ++ { ++ gchar *uri; + +- p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter); +- request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->shortcuts_model), p); +- gtk_tree_path_free (p); +- +- handle = gtk_file_system_get_info (request->impl->file_system, request->path, +- GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_IS_HIDDEN | GTK_FILE_INFO_ICON, +- get_file_info_finished, request); +- +- gtk_list_store_set (impl->shortcuts_model, &iter, +- SHORTCUTS_COL_DATA, gtk_file_path_copy (path), +- SHORTCUTS_COL_IS_VOLUME, is_volume, +- SHORTCUTS_COL_HANDLE, handle, +- -1); ++ uri = gtk_file_system_path_to_uri (impl->file_system, path); + +- shortcuts_update_count (impl, type, 1); ++ label_copy = _gtk_file_chooser_label_for_uri (uri); + +- return; ++ g_free (uri); ++ } ++ ++ /* If we switch to a better bookmarks file format (XBEL), we ++ * should use mime info to get a better icon. ++ */ ++ icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); ++ pixbuf = gtk_icon_theme_load_icon (icon_theme, "gnome-fs-directory", ++ impl->icon_size, 0, NULL); ++ } + } +- else ++ else + { +- /* Don't call get_info for remote paths to avoid latency and +- * auth dialogs. +- */ +- data = gtk_file_path_copy (path); +- if (label) +- label_copy = g_strdup (label); +- else +- { +- gchar *uri; +- +- uri = gtk_file_system_path_to_uri (impl->file_system, path); +- +- label_copy = _gtk_file_chooser_label_for_uri (uri); ++ g_assert_not_reached (); + +- g_free (uri); +- } +- +- /* If we switch to a better bookmarks file format (XBEL), we +- * should use mime info to get a better icon. +- */ +- icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); +- pixbuf = gtk_icon_theme_load_icon (icon_theme, "gnome-fs-directory", +- impl->icon_size, 0, NULL); ++ return; + } + + if (pos == -1) +@@ -1604,13 +1679,16 @@ + SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE, + SHORTCUTS_COL_NAME, label_copy, + SHORTCUTS_COL_DATA, data, +- SHORTCUTS_COL_IS_VOLUME, is_volume, ++ SHORTCUTS_COL_TYPE, shortcut_type, + SHORTCUTS_COL_REMOVABLE, removable, + SHORTCUTS_COL_HANDLE, NULL, + -1); + +- if (impl->shortcuts_filter_model) +- gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model)); ++ if (impl->shortcuts_pane_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model)); ++ ++ if (impl->shortcuts_combo_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model)); + + if (type == SHORTCUTS_CURRENT_FOLDER && impl->save_folder_combo != NULL) + { +@@ -1622,7 +1700,8 @@ + g_signal_handlers_block_by_func (impl->save_folder_combo, + G_CALLBACK (save_folder_combo_changed_cb), + impl); +- gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), combo_pos); ++ gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), ++ impl->has_search ? combo_pos - 2 : combo_pos); + g_signal_handlers_unblock_by_func (impl->save_folder_combo, + G_CALLBACK (save_folder_combo_changed_cb), + impl); +@@ -1636,6 +1715,30 @@ + profile_end ("end", NULL); + } + ++static void ++shortcuts_append_search (GtkFileChooserDefault *impl) ++{ ++ GdkPixbuf *pixbuf; ++ GtkTreeIter iter; ++ ++ pixbuf = render_search_icon (impl); ++ ++ gtk_list_store_append (impl->shortcuts_model, &iter); ++ gtk_list_store_set (impl->shortcuts_model, &iter, ++ SHORTCUTS_COL_PIXBUF, pixbuf, ++ SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE, ++ SHORTCUTS_COL_NAME, _("Search"), ++ SHORTCUTS_COL_DATA, NULL, ++ SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH, ++ SHORTCUTS_COL_REMOVABLE, FALSE, ++ -1); ++ ++ if (pixbuf) ++ g_object_unref (pixbuf); ++ ++ impl->has_search = TRUE; ++} ++ + /* Appends an item for the user's home directory to the shortcuts model */ + static void + shortcuts_append_home (GtkFileChooserDefault *impl) +@@ -1654,7 +1757,8 @@ + + home_path = gtk_file_system_filename_to_path (impl->file_system, home); + +- shortcuts_insert_path (impl, -1, FALSE, NULL, home_path, NULL, FALSE, SHORTCUTS_HOME); ++ shortcuts_insert_path (impl, -1, SHORTCUT_TYPE_PATH, NULL, home_path, NULL, FALSE, SHORTCUTS_HOME); ++ impl->has_home = TRUE; + + gtk_file_path_free (home_path); + +@@ -1687,7 +1791,9 @@ + path = gtk_file_system_filename_to_path (impl->file_system, name); + g_free (name); + +- shortcuts_insert_path (impl, -1, FALSE, NULL, path, _("Desktop"), FALSE, SHORTCUTS_DESKTOP); ++ shortcuts_insert_path (impl, -1, SHORTCUT_TYPE_PATH, NULL, path, _("Desktop"), FALSE, SHORTCUTS_DESKTOP); ++ impl->has_desktop = TRUE; ++ + /* We do not actually pop up an error dialog if there is no desktop directory + * because some people may really not want to have one. + */ +@@ -1726,7 +1832,7 @@ + label = gtk_file_system_get_bookmark_label (impl->file_system, path); + + /* NULL GError, but we don't really want to show error boxes here */ +- shortcuts_insert_path (impl, start_row + num_inserted, FALSE, NULL, path, label, TRUE, SHORTCUTS_BOOKMARKS); ++ shortcuts_insert_path (impl, start_row + num_inserted, SHORTCUT_TYPE_PATH, NULL, path, label, TRUE, SHORTCUTS_BOOKMARKS); + num_inserted++; + + g_free (label); +@@ -1745,6 +1851,16 @@ + int n; + + n = 0; ++ ++ if (where == SHORTCUTS_SEARCH) ++ goto out; ++ ++ n += impl->has_search ? 1 : 0; ++ ++ if (where == SHORTCUTS_SEARCH_SEPARATOR) ++ goto out; ++ ++ n += impl->has_search ? 1 : 0; + + if (where == SHORTCUTS_HOME) + goto out; +@@ -1842,15 +1958,18 @@ + } + } + +- shortcuts_insert_path (impl, start_row + n, TRUE, volume, NULL, NULL, FALSE, SHORTCUTS_VOLUMES); ++ shortcuts_insert_path (impl, start_row + n, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_VOLUMES); + n++; + } + + impl->num_volumes = n; + g_slist_free (list); + +- if (impl->shortcuts_filter_model) +- gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model)); ++ if (impl->shortcuts_pane_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model)); ++ ++ if (impl->shortcuts_combo_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model)); + + impl->changing_folder = old_changing_folders; + +@@ -1860,11 +1979,13 @@ + /* Inserts a separator node in the shortcuts list */ + static void + shortcuts_insert_separator (GtkFileChooserDefault *impl, +- ShortcutsIndex where) ++ ShortcutsIndex where) + { + GtkTreeIter iter; + +- g_assert (where == SHORTCUTS_BOOKMARKS_SEPARATOR || where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR); ++ g_assert (where == SHORTCUTS_SEARCH_SEPARATOR || ++ where == SHORTCUTS_BOOKMARKS_SEPARATOR || ++ where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR); + + gtk_list_store_insert (impl->shortcuts_model, &iter, + shortcuts_get_index (impl, where)); +@@ -1873,6 +1994,7 @@ + SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE, + SHORTCUTS_COL_NAME, NULL, + SHORTCUTS_COL_DATA, NULL, ++ SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR, + -1); + } + +@@ -1885,7 +2007,7 @@ + GtkTreeIter iter; + GtkFilePath *list_selected = NULL; + GtkFilePath *combo_selected = NULL; +- gboolean is_volume; ++ ShortcutType shortcut_type; + gpointer col_data; + + profile_start ("start", NULL); +@@ -1898,10 +2020,10 @@ + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), + &iter, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + +- if (col_data && !is_volume) ++ if (col_data && shortcut_type == SHORTCUT_TYPE_PATH) + list_selected = gtk_file_path_copy (col_data); + } + +@@ -1909,13 +2031,18 @@ + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (impl->save_folder_combo), + &iter)) + { ++ GtkTreeIter child_iter; ++ ++ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model), ++ &child_iter, ++ &iter); + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), +- &iter, ++ &child_iter, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + +- if (col_data && !is_volume) ++ if (col_data && shortcut_type == SHORTCUT_TYPE_PATH) + combo_selected = gtk_file_path_copy (col_data); + } + +@@ -1933,8 +2060,11 @@ + if (impl->num_bookmarks > 0) + shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR); + +- if (impl->shortcuts_filter_model) +- gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model)); ++ if (impl->shortcuts_pane_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model)); ++ ++ if (impl->shortcuts_combo_filter_model) ++ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model)); + + if (list_selected) + { +@@ -1948,8 +2078,8 @@ + + pos = shortcut_find_position (impl, combo_selected); + if (pos != -1) +- gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), +- pos); ++ gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), ++ impl->has_search ? pos - 2 : pos); + gtk_file_path_free (combo_selected); + } + +@@ -1994,11 +2124,11 @@ + if (base_path && + strcmp (gtk_file_path_get_string (base_path), gtk_file_path_get_string (impl->current_folder)) == 0) + { +- shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER); ++ shortcuts_insert_path (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER); + } + else + { +- shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER); ++ shortcuts_insert_path (impl, pos, SHORTCUT_TYPE_PATH, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER); + if (volume) + gtk_file_system_volume_free (impl->file_system, volume); + } +@@ -2007,7 +2137,8 @@ + gtk_file_path_free (base_path); + } + else if (impl->save_folder_combo != NULL) +- gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), pos); ++ gtk_combo_box_set_active (GTK_COMBO_BOX (impl->save_folder_combo), ++ impl->has_search ? pos - 2 : pos); + } + + /* Updates the current folder row in the shortcuts model */ +@@ -2029,9 +2160,9 @@ + + /* Filter function used for the shortcuts filter model */ + static gboolean +-shortcuts_filter_cb (GtkTreeModel *model, +- GtkTreeIter *iter, +- gpointer data) ++shortcuts_pane_filter_cb (GtkTreeModel *model, ++ GtkTreeIter *iter, ++ gpointer data) + { + GtkFileChooserDefault *impl; + GtkTreePath *path; +@@ -2058,11 +2189,17 @@ + GDK_TYPE_PIXBUF, /* pixbuf */ + G_TYPE_STRING, /* name */ + G_TYPE_POINTER, /* path or volume */ +- G_TYPE_BOOLEAN, /* is the previous column a volume? */ ++ G_TYPE_INT, /* ShortcutType */ + G_TYPE_BOOLEAN, /* removable */ + G_TYPE_BOOLEAN, /* pixbuf cell visibility */ + G_TYPE_POINTER); /* GtkFileSystemHandle */ + ++ if (search_is_possible (impl)) ++ { ++ shortcuts_append_search (impl); ++ shortcuts_insert_separator (impl, SHORTCUTS_SEARCH_SEPARATOR); ++ } ++ + if (impl->file_system) + { + shortcuts_append_home (impl); +@@ -2070,12 +2207,12 @@ + shortcuts_add_volumes (impl); + } + +- impl->shortcuts_filter_model = shortcuts_model_filter_new (impl, +- GTK_TREE_MODEL (impl->shortcuts_model), +- NULL); ++ impl->shortcuts_pane_filter_model = shortcuts_pane_model_filter_new (impl, ++ GTK_TREE_MODEL (impl->shortcuts_model), ++ NULL); + +- gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model), +- shortcuts_filter_cb, ++ gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model), ++ shortcuts_pane_filter_cb, + impl, + NULL); + } +@@ -2293,16 +2430,16 @@ + for (i = 0; i < current_folder_separator_idx; i++) + { + gpointer col_data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + + if (col_data) + { +- if (is_volume) ++ if (shortcut_type == SHORTCUT_TYPE_VOLUME) + { + GtkFileSystemVolume *volume; + GtkFilePath *base_path; +@@ -2318,7 +2455,7 @@ + if (exists) + return i; + } +- else ++ else if (shortcut_type == SHORTCUT_TYPE_PATH) + { + GtkFilePath *model_path; + +@@ -2424,7 +2561,7 @@ + if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter)) + return FALSE; + +- gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model), ++ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model), + iter, + &parent_iter); + return TRUE; +@@ -2447,11 +2584,12 @@ + SHORTCUTS_COL_DATA, &col_data, + SHORTCUTS_COL_REMOVABLE, &removable, + -1); +- g_assert (col_data != NULL); + + if (!removable) + return; + ++ g_assert (col_data != NULL); ++ + path = col_data; + + error = NULL; +@@ -2608,6 +2746,16 @@ + gboolean active; + gchar *tip; + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ { ++ gtk_widget_set_sensitive (impl->browse_shortcuts_add_button, FALSE); ++ ++ if (impl->browse_files_popup_menu_add_shortcut_item) ++ gtk_widget_set_sensitive (impl->browse_files_popup_menu_add_shortcut_item, FALSE); ++ ++ return; ++ } ++ + selection_check (impl, &num_selected, NULL, &all_folders); + + if (num_selected == 0) +@@ -3121,7 +3269,7 @@ + { + GtkTreeIter iter; + gpointer col_data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + GtkTreePath *path; + int old_position; + int bookmarks_index; +@@ -3146,10 +3294,10 @@ + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, + SHORTCUTS_COL_NAME, &name, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + g_assert (col_data != NULL); +- g_assert (!is_volume); ++ g_assert (shortcut_type == SHORTCUT_TYPE_PATH); + + file_path = col_data; + file_path_copy = gtk_file_path_copy (file_path); /* removal below will free file_path, so we need a copy */ +@@ -3233,17 +3381,11 @@ + GtkTreeIter *iter, + gpointer data) + { +- gint column = GPOINTER_TO_INT (data); +- gchar *text; ++ ShortcutType shortcut_type; + +- gtk_tree_model_get (model, iter, column, &text, -1); ++ gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1); + +- if (!text) +- return TRUE; +- +- g_free (text); +- +- return FALSE; ++ return shortcut_type == SHORTCUT_TYPE_SEPARATOR; + } + + /* Since GtkTreeView has a keybinding attached to '/', we need to catch +@@ -3488,7 +3630,7 @@ + /* Accessible object name for the file chooser's shortcuts pane */ + atk_object_set_name (gtk_widget_get_accessible (impl->browse_shortcuts_tree_view), _("Places")); + +- gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_filter_model); ++ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), impl->shortcuts_pane_filter_model); + + gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), + GDK_BUTTON1_MASK, +@@ -3561,8 +3703,7 @@ + + gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), + shortcuts_row_separator_func, +- GINT_TO_POINTER (SHORTCUTS_COL_NAME), +- NULL); ++ NULL, NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), column); + +@@ -3933,6 +4074,8 @@ + { + file_list_build_popup_menu (impl); + ++ /* FMQ: handle OPERATION_MODE_SEARCH */ ++ + /* The sensitivity of the Add to Bookmarks item is set in + * bookmarks_check_add_sensitivity() + */ +@@ -4032,6 +4175,28 @@ + return TRUE; + } + ++/* Sets the sort column IDs for the file list based on the operation mode */ ++static void ++file_list_set_sort_column_ids (GtkFileChooserDefault *impl) ++{ ++ int name_id, mtime_id; ++ ++ if (impl->operation_mode == OPERATION_MODE_BROWSE) ++ { ++ name_id = FILE_LIST_COL_NAME; ++ mtime_id = FILE_LIST_COL_MTIME; ++ } ++ else ++ { ++ name_id = SEARCH_MODEL_COL_PATH; ++ mtime_id = SEARCH_MODEL_COL_STAT; ++ } ++ ++ gtk_tree_view_column_set_sort_column_id (impl->list_name_column, name_id); ++ gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, mtime_id); ++} ++ ++ + /* Creates the widgets for the file list */ + static GtkWidget * + create_file_list (GtkFileChooserDefault *impl) +@@ -4045,7 +4210,7 @@ + + swin = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin), +- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); ++ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin), + GTK_SHADOW_IN); + +@@ -4135,6 +4300,7 @@ + gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_SIZE); + gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column); + #endif ++ + /* Modification time column */ + + column = gtk_tree_view_column_new (); +@@ -4145,8 +4311,11 @@ + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_cell_data_func (column, renderer, + list_mtime_data_func, impl, NULL); +- gtk_tree_view_column_set_sort_column_id (column, FILE_LIST_COL_MTIME); + gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column); ++ impl->list_mtime_column = column; ++ ++ file_list_set_sort_column_ids (impl); ++ + gtk_widget_show_all (swin); + + return swin; +@@ -4254,9 +4423,59 @@ + return; + + if (gtk_combo_box_get_active_iter (combo, &iter)) +- shortcuts_activate_iter (impl, &iter); ++ { ++ GtkTreeIter child_iter; ++ ++ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model), ++ &child_iter, ++ &iter); ++ shortcuts_activate_iter (impl, &child_iter); ++ } + } + ++/* Filter function used to filter out the Search item and its separator. ++ * Used for the "Save in folder" combo box, so that these items do not appear in it. ++ */ ++static gboolean ++shortcuts_combo_filter_func (GtkTreeModel *model, ++ GtkTreeIter *iter, ++ gpointer data) ++{ ++ GtkFileChooserDefault *impl; ++ GtkTreePath *tree_path; ++ gint *indices; ++ int idx; ++ gboolean retval; ++ ++ impl = GTK_FILE_CHOOSER_DEFAULT (data); ++ ++ g_assert (model == GTK_TREE_MODEL (impl->shortcuts_model)); ++ ++ tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), iter); ++ g_assert (tree_path != NULL); ++ ++ indices = gtk_tree_path_get_indices (tree_path); ++ ++ retval = TRUE; ++ ++ if (impl->has_search) ++ { ++ idx = shortcuts_get_index (impl, SHORTCUTS_SEARCH); ++ if (idx == indices[0]) ++ retval = FALSE; ++ else ++ { ++ idx = shortcuts_get_index (impl, SHORTCUTS_SEARCH_SEPARATOR); ++ if (idx == indices[0]) ++ retval = FALSE; ++ } ++ } ++ ++ gtk_tree_path_free (tree_path); ++ ++ return retval; ++ } ++ + /* Creates the combo box with the save folders */ + static GtkWidget * + save_folder_combo_create (GtkFileChooserDefault *impl) +@@ -4264,8 +4483,14 @@ + GtkWidget *combo; + GtkCellRenderer *cell; + ++ impl->shortcuts_combo_filter_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->shortcuts_model), NULL); ++ gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model), ++ shortcuts_combo_filter_func, ++ impl, ++ NULL); ++ + combo = g_object_new (GTK_TYPE_COMBO_BOX, +- "model", impl->shortcuts_model, ++ "model", impl->shortcuts_combo_filter_model, + "focus-on-click", FALSE, + NULL); + gtk_widget_show (combo); +@@ -4287,8 +4512,7 @@ + + gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo), + shortcuts_row_separator_func, +- GINT_TO_POINTER (SHORTCUTS_COL_NAME), +- NULL); ++ NULL, NULL); + + g_signal_connect (combo, "changed", + G_CALLBACK (save_folder_combo_changed_cb), impl); +@@ -4622,6 +4846,7 @@ + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); ++ impl->browse_path_bar_hbox = hbox; + + location_button_create (impl); + gtk_box_pack_start (GTK_BOX (hbox), impl->location_button, FALSE, FALSE, 0); +@@ -5210,6 +5435,9 @@ + impl->shortcuts_activate_iter_handle = NULL; + } + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ search_stop_searching (impl); ++ + remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl))); + + G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object); +@@ -5487,31 +5715,32 @@ + + GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget); + +- switch (impl->reload_state) +- { +- case RELOAD_EMPTY: +- /* The user didn't explicitly give us a folder to display, so we'll use the cwd */ +- current_working_dir = g_get_current_dir (); +- gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), current_working_dir); +- g_free (current_working_dir); +- break; +- +- case RELOAD_HAS_FOLDER: +- /* Nothing; we are already loading or loaded, so we don't need to reload */ +- break; +- +- case RELOAD_WAS_UNMAPPED: +- /* Just reload the current folder; else continue the pending load. */ +- if (impl->current_folder) +- { +- pending_select_paths_store_selection (impl); +- change_folder_and_display_error (impl, impl->current_folder); +- } +- break; +- +- default: +- g_assert_not_reached (); +- } ++ if (impl->operation_mode == OPERATION_MODE_BROWSE) ++ switch (impl->reload_state) ++ { ++ case RELOAD_EMPTY: ++ /* The user didn't explicitly give us a folder to display, so we'll use the cwd */ ++ current_working_dir = g_get_current_dir (); ++ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), current_working_dir); ++ g_free (current_working_dir); ++ break; ++ ++ case RELOAD_HAS_FOLDER: ++ /* Nothing; we are already loading or loaded, so we don't need to reload */ ++ break; ++ ++ case RELOAD_WAS_UNMAPPED: ++ /* Just reload the current folder; else continue the pending load. */ ++ if (impl->current_folder) ++ { ++ pending_select_paths_store_selection (impl); ++ change_folder_and_display_error (impl, impl->current_folder); ++ } ++ break; ++ ++ default: ++ g_assert_not_reached (); ++ } + + bookmarks_changed_cb (impl->file_system, impl); + +@@ -5982,12 +6211,6 @@ + * but rather on behalf of something else like GtkFileChooserButton. In + * that case, the chooser's selection should be what the caller expects, + * as the user can't see that something else got selected. See bug #165264. +- * +- * Also, we don't select the first file if we are not in OPEN mode. Doing +- * so would change the contents of the filename entry for SAVE or +- * CREATE_FOLDER, which is undesired; in SELECT_FOLDER, we don't want to +- * select a *different* folder from the one into which the user just +- * navigated. + */ + if (GTK_WIDGET_MAPPED (impl) && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN) + browse_files_select_first_row (impl); +@@ -6034,17 +6257,11 @@ + profile_end ("end", NULL); + } + +-/* Gets rid of the old list model and creates a new one for the current folder */ +-static gboolean +-set_list_model (GtkFileChooserDefault *impl, +- GError **error) ++static void ++stop_loading_and_clear_list_model (GtkFileChooserDefault *impl) + { +- g_assert (impl->current_folder != NULL); +- +- profile_start ("start", NULL); +- + load_remove_timer (impl); /* This changes the state to LOAD_EMPTY */ +- ++ + if (impl->browse_files_model) + { + g_object_unref (impl->browse_files_model); +@@ -6056,6 +6273,20 @@ + g_object_unref (impl->sort_model); + impl->sort_model = NULL; + } ++ ++ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); ++} ++ ++/* Gets rid of the old list model and creates a new one for the current folder */ ++static gboolean ++set_list_model (GtkFileChooserDefault *impl, ++ GError **error) ++{ ++ g_assert (impl->current_folder != NULL); ++ ++ profile_start ("start", NULL); ++ ++ stop_loading_and_clear_list_model (impl); + + set_busy_cursor (impl, TRUE); + gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); +@@ -6130,6 +6361,9 @@ + struct update_chooser_entry_selected_foreach_closure closure; + const char *file_part; + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH || !impl->location_entry) ++ return; ++ + if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE + || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER + || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN +@@ -6151,35 +6385,40 @@ + } + else if (closure.num_selected == 1) + { +- GtkTreeIter child_iter; +- const GtkFileInfo *info; +- gboolean change_entry; +- +- gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, +- &child_iter, +- &closure.first_selected_iter); +- +- info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); +- +- /* If the cursor moved to the row of the newly created folder, +- * retrieving info will return NULL. +- */ +- if (!info) +- return; +- +- g_free (impl->browse_files_last_selected_name); +- impl->browse_files_last_selected_name = g_strdup (gtk_file_info_get_display_name (info)); +- +- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN +- || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) +- change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */ +- else +- change_entry = TRUE; /* ... unless we are in one of the folder modes */ +- +- if (change_entry) +- _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name); ++ if (impl->operation_mode == OPERATION_MODE_BROWSE) ++ { ++ GtkTreeIter child_iter; ++ const GtkFileInfo *info; ++ gboolean change_entry; ++ ++ gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, ++ &child_iter, ++ &closure.first_selected_iter); ++ ++ info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); ++ ++ /* If the cursor moved to the row of the newly created folder, ++ * retrieving info will return NULL. ++ */ ++ if (!info) ++ return; + +- return; ++ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ++ || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ++ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ++ change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */ ++ else ++ change_entry = TRUE; /* ... unless we are in CREATE_FOLDER mode */ ++ ++ if (change_entry) ++ file_part = gtk_file_info_get_display_name (info); ++ } ++ else ++ { ++ gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &closure.first_selected_iter, ++ SEARCH_MODEL_COL_DISPLAY_NAME, &file_part, ++ -1); ++ } + } + else + { +@@ -6386,6 +6625,8 @@ + + profile_start ("start", (char *) path); + ++ search_switch_to_browse_mode (impl); ++ + g_assert (path != NULL); + + if (impl->local_only && +@@ -6427,6 +6668,9 @@ + { + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ return NULL; ++ + if (impl->reload_state == RELOAD_EMPTY) + { + char *current_working_dir; +@@ -6487,9 +6731,9 @@ + return FALSE; + + if (!parent_path) +- return _gtk_file_chooser_set_current_folder_path (chooser, path, error); ++ return _gtk_file_chooser_set_current_folder_path (chooser, path, error); + +- if (impl->load_state == LOAD_EMPTY) ++ if (impl->operation_mode == OPERATION_MODE_SEARCH || impl->load_state == LOAD_EMPTY) + same_path = FALSE; + else + { +@@ -6585,6 +6829,16 @@ + gtk_file_chooser_default_select_all (GtkFileChooser *chooser) + { + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); ++ ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ { ++ GtkTreeSelection *selection; ++ ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); ++ gtk_tree_selection_select_all (selection); ++ return; ++ } ++ + if (impl->select_multiple) + gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model), + maybe_select, impl); +@@ -6714,6 +6968,10 @@ + struct get_paths_closure info; + GtkWindow *toplevel; + GtkWidget *current_focus; ++ gboolean file_list_seen; ++ ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ return search_get_selected_paths (impl); + + info.impl = impl; + info.result = NULL; +@@ -6725,12 +6983,14 @@ + else + current_focus = NULL; + ++ file_list_seen = FALSE; + if (current_focus == impl->browse_files_tree_view) + { + GtkTreeSelection *selection; + + file_list: + ++ file_list_seen = TRUE; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); + gtk_tree_selection_selected_foreach (selection, get_paths_foreach, &info); + +@@ -6765,8 +7025,12 @@ + return NULL; + } + +- g_assert (info.path_from_entry != NULL); +- info.result = g_slist_prepend (info.result, info.path_from_entry); ++ if (info.path_from_entry) ++ info.result = g_slist_prepend (info.result, info.path_from_entry); ++ else if (!file_list_seen) ++ goto file_list; ++ else ++ return NULL; + } + else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view) + goto file_list; +@@ -6935,7 +7199,7 @@ + + pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->num_shortcuts); + +- shortcuts_insert_path (data->impl, pos, FALSE, NULL, data->path, NULL, FALSE, SHORTCUTS_SHORTCUTS); ++ shortcuts_insert_path (data->impl, pos, SHORTCUT_TYPE_PATH, NULL, data->path, NULL, FALSE, SHORTCUTS_SHORTCUTS); + + out: + g_object_unref (data->impl); +@@ -7049,15 +7313,15 @@ + for (i = 0; i < impl->num_shortcuts; i++) + { + gpointer col_data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + GtkFilePath *shortcut; + + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + g_assert (col_data != NULL); +- g_assert (!is_volume); ++ g_assert (shortcut_type == SHORTCUT_TYPE_PATH); + + shortcut = col_data; + if (gtk_file_path_compare (shortcut, path) == 0) +@@ -7106,15 +7370,15 @@ + for (i = 0; i < impl->num_shortcuts; i++) + { + gpointer col_data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + GtkFilePath *shortcut; + + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + g_assert (col_data != NULL); +- g_assert (!is_volume); ++ g_assert (shortcut_type == SHORTCUT_TYPE_PATH); + + shortcut = col_data; + list = g_slist_prepend (list, gtk_file_path_copy (shortcut)); +@@ -7449,6 +7713,23 @@ + } + } + ++/* Gives the focus to the browse tree view only if it is visible */ ++static void ++focus_browse_tree_view_if_possible (GtkFileChooserDefault *impl) ++{ ++ gboolean do_focus; ++ ++ if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE ++ || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) ++ && !gtk_expander_get_expanded (GTK_EXPANDER (impl->save_expander))) ++ do_focus = FALSE; ++ else ++ do_focus = TRUE; ++ ++ if (do_focus) ++ gtk_widget_grab_focus (impl->browse_files_tree_view); ++} ++ + static void + action_create_folder_cb (GtkFileSystemHandle *handle, + const GtkFilePath *path, +@@ -7714,6 +7995,9 @@ + + g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER); + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ return search_should_respond (impl); ++ + selection_check (impl, &num_selected, &all_files, &all_folders); + + if (num_selected > 2) +@@ -7850,7 +8134,7 @@ + { + shortcuts_activate_iter (impl, &iter); + +- gtk_widget_grab_focus (impl->browse_files_tree_view); ++ focus_browse_tree_view_if_possible (impl); + } + else + goto file_list; +@@ -7864,6 +8148,11 @@ + */ + goto file_list; + } ++ else if (impl->operation_mode == OPERATION_MODE_SEARCH && impl->toplevel_last_focus_widget == impl->search_entry) ++ { ++ search_entry_activate_cb (GTK_ENTRY (impl->search_entry), impl); ++ return FALSE; ++ } + else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry) + { + /* The focus is on a dialog's action area button, *and* the widget that +@@ -7913,15 +8202,452 @@ + gtk_widget_grab_focus (widget); + } + ++/* Callback used from gtk_tree_selection_selected_foreach(); gets the selected GtkFilePaths */ + static void +-set_current_filter (GtkFileChooserDefault *impl, +- GtkFileFilter *filter) ++search_selected_foreach_get_path_cb (GtkTreeModel *model, ++ GtkTreePath *path, ++ GtkTreeIter *iter, ++ gpointer data) + { +- if (impl->current_filter != filter) +- { +- int filter_index; ++ GSList **list; ++ const GtkFilePath *file_path; ++ GtkFilePath *file_path_copy; + +- /* NULL filters are allowed to reset to non-filtered status ++ list = data; ++ ++ gtk_tree_model_get (model, iter, SEARCH_MODEL_COL_PATH, &file_path, -1); ++ file_path_copy = gtk_file_path_copy (file_path); ++ *list = g_slist_prepend (*list, file_path_copy); ++} ++ ++/* Constructs a list of the selected paths in search mode */ ++static GSList * ++search_get_selected_paths (GtkFileChooserDefault *impl) ++{ ++ GSList *result; ++ GtkTreeSelection *selection; ++ ++ result = NULL; ++ ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); ++ gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_path_cb, &result); ++ result = g_slist_reverse (result); ++ ++ return result; ++} ++ ++/* Called from ::should_respond(). We return whether there are selected files ++ * in the search list. ++ */ ++static gboolean ++search_should_respond (GtkFileChooserDefault *impl) ++{ ++ GtkTreeSelection *selection; ++ ++ g_assert (impl->operation_mode == OPERATION_MODE_SEARCH); ++ ++ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); ++ return (gtk_tree_selection_count_selected_rows (selection) != 0); ++} ++ ++ ++/* Adds one hit from the search engine to the search_model */ ++static void ++search_add_hit (GtkFileChooserDefault *impl, ++ gchar *uri) ++{ ++ GtkFilePath *path; ++ char *filename; ++ char *display_name; ++ char *collation_key; ++ struct stat statbuf; ++ struct stat *statbuf_copy; ++ GtkTreeIter iter; ++ ++ path = gtk_file_system_uri_to_path (impl->file_system, uri); ++ if (!path) ++ return; ++ ++ filename = gtk_file_system_path_to_filename (impl->file_system, path); ++ if (!filename) ++ { ++ gtk_file_path_free (path); ++ return; ++ } ++ ++ if (stat (filename, &statbuf) != 0) ++ { ++ gtk_file_path_free (path); ++ g_free (filename); ++ return; ++ } ++ ++ statbuf_copy = g_new (struct stat, 1); ++ *statbuf_copy = statbuf; ++ ++ display_name = g_filename_display_name (filename); ++ collation_key = g_utf8_collate_key_for_filename (display_name, -1); ++ ++ gtk_list_store_insert_with_values (impl->search_model, &iter, -1, ++ SEARCH_MODEL_COL_PATH, path, ++ SEARCH_MODEL_COL_DISPLAY_NAME, display_name, ++ SEARCH_MODEL_COL_COLLATION_KEY, collation_key, ++ SEARCH_MODEL_COL_STAT, statbuf_copy, ++ -1); ++} ++ ++/* Callback used from GtkSearchEngine when we get new hits */ ++static void ++search_engine_hits_added_cb (GtkSearchEngine *engine, ++ GList *hits, ++ gpointer data) ++{ ++ GtkFileChooserDefault *impl; ++ GList *l; ++ ++ impl = GTK_FILE_CHOOSER_DEFAULT (data); ++ ++ for (l = hits; l; l = l->next) ++ search_add_hit (impl, (gchar*)l->data); ++} ++ ++/* Callback used from GtkSearchEngine when the query is done running */ ++static void ++search_engine_finished_cb (GtkSearchEngine *engine, ++ gpointer data) ++{ ++ GtkFileChooserDefault *impl; ++ ++ impl = GTK_FILE_CHOOSER_DEFAULT (data); ++ ++ /* FMQ: if search was empty, say that we got no hits */ ++ ++ set_busy_cursor (impl, FALSE); ++} ++ ++/* Displays a generic error when we cannot create a GtkSearchEngine. ++ * It would be better if _gtk_search_engine_new() gave us a GError ++ * with a better message, but it doesn't do that right now. ++ */ ++static void ++search_error_could_not_create_client (GtkFileChooserDefault *impl) ++{ ++ error_message (impl, ++ _("Could not start the search process"), ++ _("The program was not able to create a connection to the indexer " ++ "daemon. Please make sure it is running.")); ++} ++ ++static void ++search_engine_error_cb (GtkSearchEngine *engine, ++ const gchar *message, ++ gpointer data) ++{ ++ GtkFileChooserDefault *impl; ++ ++ impl = GTK_FILE_CHOOSER_DEFAULT (data); ++ ++ search_stop_searching (impl); ++ error_message (impl, _("Could not send the search request"), message); ++ ++ set_busy_cursor (impl, FALSE); ++} ++ ++/* Frees the data in the search_model */ ++static void ++search_clear_model (GtkFileChooserDefault *impl, ++ gboolean remove_from_treeview) ++{ ++ GtkTreeIter iter; ++ ++ if (!impl->search_model) ++ return; ++ ++ if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->search_model), &iter)) ++ do ++ { ++ GtkFilePath *path; ++ char *display_name; ++ char *collation_key; ++ struct stat *statbuf; ++ ++ gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &iter, ++ SEARCH_MODEL_COL_PATH, &path, ++ SEARCH_MODEL_COL_DISPLAY_NAME, &display_name, ++ SEARCH_MODEL_COL_COLLATION_KEY, &collation_key, ++ SEARCH_MODEL_COL_STAT, &statbuf, ++ -1); ++ ++ gtk_file_path_free (path); ++ g_free (display_name); ++ g_free (collation_key); ++ g_free (statbuf); ++ } ++ while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->search_model), &iter)); ++ ++ g_object_unref (impl->search_model); ++ impl->search_model = NULL; ++ ++ if (remove_from_treeview) ++ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); ++} ++ ++/* Stops any ongoing searches; does not touch the search_model */ ++static void ++search_stop_searching (GtkFileChooserDefault *impl) ++{ ++ if (impl->search_query) ++ { ++ g_object_unref (impl->search_query); ++ impl->search_query = NULL; ++ } ++ ++ if (impl->search_engine) ++ { ++ g_object_unref (impl->search_engine); ++ impl->search_engine = NULL; ++ } ++} ++ ++/* Stops any pending searches, clears the file list, and switches back to OPERATION_MODE_BROWSE */ ++static void ++search_switch_to_browse_mode (GtkFileChooserDefault *impl) ++{ ++ if (impl->operation_mode == OPERATION_MODE_BROWSE) ++ return; ++ ++ search_stop_searching (impl); ++ search_clear_model (impl, TRUE); ++ ++ gtk_widget_destroy (impl->search_hbox); ++ impl->search_hbox = NULL; ++ impl->search_entry = NULL; ++ ++ gtk_widget_show (impl->browse_path_bar); ++ gtk_widget_show (impl->browse_new_folder_button); ++ ++ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ++ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ++ { ++ gtk_widget_show (impl->location_button); ++ ++ if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY) ++ gtk_widget_show (impl->location_entry_box); ++ } ++ ++ impl->operation_mode = OPERATION_MODE_BROWSE; ++ ++ file_list_set_sort_column_ids (impl); ++} ++ ++/* Sort callback from the path column */ ++static gint ++search_column_path_sort_func (GtkTreeModel *model, ++ GtkTreeIter *a, ++ GtkTreeIter *b, ++ gpointer user_data) ++{ ++ const char *collation_key_a, *collation_key_b; ++ ++ gtk_tree_model_get (model, a, SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_a, -1); ++ gtk_tree_model_get (model, b, SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_b, -1); ++ ++ return strcmp (collation_key_a, collation_key_b); ++} ++ ++/* Sort callback from the modification time column */ ++static gint ++search_column_mtime_sort_func (GtkTreeModel *model, ++ GtkTreeIter *a, ++ GtkTreeIter *b, ++ gpointer user_data) ++{ ++ const struct stat *statbuf_a, *statbuf_b; ++ ++ /* Note that although we store a whole struct stat in the model, we only ++ * compare the mtime here. If we add another column relative to a struct stat ++ * (e.g. a file size column), we'll want another sort callback similar to this ++ * one as well. ++ */ ++ ++ gtk_tree_model_get (model, a, SEARCH_MODEL_COL_STAT, &statbuf_a, -1); ++ gtk_tree_model_get (model, b, SEARCH_MODEL_COL_STAT, &statbuf_b, -1); ++ ++ if (statbuf_a->st_mtime < statbuf_b->st_mtime) ++ return -1; ++ else if (statbuf_a->st_mtime > statbuf_b->st_mtime) ++ return 1; ++ else ++ return 0; ++} ++ ++/* Creates the search_model and puts it in the tree view */ ++static void ++search_setup_model (GtkFileChooserDefault *impl) ++{ ++ g_assert (impl->search_model == NULL); ++ ++ /* We store these columns in the search model: ++ * ++ * SEARCH_MODEL_COL_PATH - a GtkFilePath for the hit's URI (stored as a pointer, not as a GTK_TYPE_FILE_PATH) ++ * SEARCH_MODEL_COL_DISPLAY_NAME - a string with the display name (stored as a pointer, not as a G_TYPE_STRING) ++ * SEARCH_MODEL_COL_COLLATION_KEY - collation key for the filename (stored as a pointer, not as a G_TYPE_STRING) ++ * SEARCH_MODEL_COL_STAT - pointer to a struct stat ++ * ++ * Keep this in sync with the enumeration defined near the beginning of this file. ++ */ ++ impl->search_model = gtk_list_store_new (4, ++ G_TYPE_POINTER, ++ G_TYPE_POINTER, ++ G_TYPE_POINTER, ++ G_TYPE_POINTER); ++ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model), ++ SEARCH_MODEL_COL_PATH, ++ search_column_path_sort_func, ++ impl, ++ NULL); ++ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model), ++ SEARCH_MODEL_COL_STAT, ++ search_column_mtime_sort_func, ++ impl, ++ NULL); ++ ++ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->search_model), ++ SEARCH_MODEL_COL_STAT, ++ GTK_SORT_DESCENDING); ++ ++ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), GTK_TREE_MODEL (impl->search_model)); ++} ++ ++/* Creates a new query with the specified text and launches it */ ++static void ++search_start_query (GtkFileChooserDefault *impl, ++ const gchar *query_text) ++{ ++ search_stop_searching (impl); ++ search_clear_model (impl, TRUE); ++ search_setup_model (impl); ++ set_busy_cursor (impl, TRUE); ++ ++ if (impl->search_engine == NULL) ++ impl->search_engine = _gtk_search_engine_new (); ++ ++ if (!impl->search_engine) ++ { ++ set_busy_cursor (impl, FALSE); ++ search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */ ++ return; ++ } ++ ++ impl->search_query = _gtk_query_new (); ++ _gtk_query_set_text (impl->search_query, query_text); ++ ++ g_signal_connect (impl->search_engine, "hits-added", ++ G_CALLBACK (search_engine_hits_added_cb), impl); ++ g_signal_connect (impl->search_engine, "finished", ++ G_CALLBACK (search_engine_finished_cb), impl); ++ g_signal_connect (impl->search_engine, "error", ++ G_CALLBACK (search_engine_error_cb), impl); ++ ++ _gtk_search_engine_start (impl->search_engine); ++} ++ ++/* Callback used when the user presses Enter while typing on the search entry; starts the query */ ++static void ++search_entry_activate_cb (GtkEntry *entry, ++ gpointer data) ++{ ++ GtkFileChooserDefault *impl; ++ const char *text; ++ ++ impl = GTK_FILE_CHOOSER_DEFAULT (data); ++ ++ text = gtk_entry_get_text (GTK_ENTRY (impl->search_entry)); ++ if (strlen (text) == 0) ++ return; ++ ++ search_start_query (impl, text); ++} ++ ++/* Hides the path bar and creates the search entry */ ++static void ++search_setup_widgets (GtkFileChooserDefault *impl) ++{ ++ GtkWidget *label; ++ gchar *text; ++ ++ impl->search_hbox = gtk_hbox_new (FALSE, 12); ++ ++ /* Label */ ++ ++ label = gtk_label_new (NULL); ++ text = g_strdup_printf ("%s", _("Search:")); ++ gtk_label_set_markup (GTK_LABEL (label), text); ++ g_free (text); ++ gtk_box_pack_start (GTK_BOX (impl->search_hbox), label, FALSE, FALSE, 0); ++ ++ /* Entry */ ++ ++ impl->search_entry = gtk_entry_new (); ++ g_signal_connect (impl->search_entry, "activate", ++ G_CALLBACK (search_entry_activate_cb), ++ impl); ++ gtk_box_pack_start (GTK_BOX (impl->search_hbox), impl->search_entry, TRUE, TRUE, 0); ++ ++ gtk_widget_hide (impl->browse_path_bar); ++ gtk_widget_hide (impl->browse_new_folder_button); ++ ++ /* Box for search widgets */ ++ ++ gtk_box_pack_start (GTK_BOX (impl->browse_path_bar_hbox), impl->search_hbox, TRUE, TRUE, 0); ++ gtk_widget_show_all (impl->search_hbox); ++ ++ /* Hide the location widgets temporarily */ ++ ++ if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ++ || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ++ { ++ gtk_widget_hide (impl->location_button); ++ gtk_widget_hide (impl->location_entry_box); ++ } ++ ++ gtk_widget_grab_focus (impl->search_entry); ++ ++ /* FMQ: hide the filter combo? */ ++} ++ ++/* Main entry point to the searching functions; this gets called when the user ++ * activates the Search shortcut. ++ */ ++static void ++search_activate (GtkFileChooserDefault *impl) ++{ ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ { ++ gtk_widget_grab_focus (impl->search_entry); ++ return; ++ } ++ ++ impl->operation_mode = OPERATION_MODE_SEARCH; ++ ++ g_assert (impl->search_hbox == NULL); ++ g_assert (impl->search_entry == NULL); ++ g_assert (impl->search_model == NULL); ++ ++ stop_loading_and_clear_list_model (impl); ++ search_setup_widgets (impl); ++ file_list_set_sort_column_ids (impl); ++} ++ ++static void ++set_current_filter (GtkFileChooserDefault *impl, ++ GtkFileFilter *filter) ++{ ++ if (impl->current_filter != filter) ++ { ++ int filter_index; ++ ++ /* NULL filters are allowed to reset to non-filtered status + */ + filter_index = g_slist_index (impl->filters, filter); + if (impl->filters && filter && filter_index < 0) +@@ -7961,28 +8687,44 @@ + { + GtkTreePath *cursor_path; + const GtkFilePath *new_path; +- const GtkFileInfo *new_info; ++ const char *new_display_name; + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL); +- if (cursor_path && impl->sort_model) ++ new_path = NULL; ++ new_display_name = NULL; ++ if (cursor_path) + { +- GtkTreeIter iter; +- GtkTreeIter child_iter; +- +- if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path)) +- g_assert_not_reached (); +- +- gtk_tree_path_free (cursor_path); ++ if (impl->operation_mode == OPERATION_MODE_BROWSE) ++ { ++ if (impl->sort_model) ++ { ++ GtkTreeIter iter; ++ GtkTreeIter child_iter; ++ const GtkFileInfo *new_info; ++ ++ gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path); ++ gtk_tree_path_free (cursor_path); ++ ++ gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter); ++ ++ new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter); ++ new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); ++ if (new_info) ++ new_display_name = gtk_file_info_get_display_name (new_info); ++ } ++ } ++ else ++ { ++ GtkTreeIter iter; + +- gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter); ++ gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model), &iter, cursor_path); ++ gtk_tree_path_free (cursor_path); + +- new_path = _gtk_file_system_model_get_path (impl->browse_files_model, &child_iter); +- new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); +- } +- else +- { +- new_path = NULL; +- new_info = NULL; ++ gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &iter, ++ SEARCH_MODEL_COL_PATH, &new_path, ++ SEARCH_MODEL_COL_DISPLAY_NAME, &new_display_name, ++ -1); ++ } + } + + if (new_path != impl->preview_path && +@@ -7998,7 +8740,7 @@ + if (new_path) + { + impl->preview_path = gtk_file_path_copy (new_path); +- impl->preview_display_name = g_strdup (gtk_file_info_get_display_name (new_info)); ++ impl->preview_display_name = g_strdup (new_display_name); + } + else + { +@@ -8067,6 +8809,8 @@ + { + GtkFilePath *path; + ++ search_switch_to_browse_mode (impl); ++ + /* We ref the file chooser since volume_mount() may run a main loop, and the + * user could close the file chooser window in the meantime. + */ +@@ -8087,6 +8831,8 @@ + if (path != NULL) + { + change_folder_and_display_error (impl, path); ++ focus_browse_tree_view_if_possible (impl); ++ + gtk_file_path_free (path); + } + } +@@ -8119,7 +8865,10 @@ + goto out; + + if (!error && gtk_file_info_get_is_folder (info)) +- change_folder_and_display_error (data->impl, data->path); ++ { ++ change_folder_and_display_error (data->impl, data->path); ++ focus_browse_tree_view_if_possible (data->impl); ++ } + else + gtk_file_chooser_default_select_path (GTK_FILE_CHOOSER (data->impl), data->path, NULL); + +@@ -8136,26 +8885,25 @@ + GtkTreeIter *iter) + { + gpointer col_data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + + if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY && impl->action != GTK_FILE_CHOOSER_ACTION_SAVE) + _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), ""); + + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter, + SHORTCUTS_COL_DATA, &col_data, +- SHORTCUTS_COL_IS_VOLUME, &is_volume, ++ SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + +- if (!col_data) +- return; /* We are on a separator */ +- + if (impl->shortcuts_activate_iter_handle) + { + gtk_file_system_cancel_operation (impl->shortcuts_activate_iter_handle); + impl->shortcuts_activate_iter_handle = NULL; + } + +- if (is_volume) ++ if (shortcut_type == SHORTCUT_TYPE_SEPARATOR) ++ return; ++ else if (shortcut_type == SHORTCUT_TYPE_VOLUME) + { + GtkFileSystemVolume *volume; + +@@ -8163,7 +8911,7 @@ + + shortcuts_activate_volume (impl, volume); + } +- else ++ else if (shortcut_type == SHORTCUT_TYPE_PATH) + { + struct ShortcutsActivateData *data; + +@@ -8176,6 +8924,10 @@ + GTK_FILE_INFO_IS_FOLDER, + shortcuts_activate_get_info_cb, data); + } ++ else if (shortcut_type == SHORTCUT_TYPE_SEARCH) ++ { ++ search_activate (impl); ++ } + } + + /* Callback used when a row in the shortcuts list is activated */ +@@ -8188,15 +8940,13 @@ + GtkTreeIter iter; + GtkTreeIter child_iter; + +- if (!gtk_tree_model_get_iter (impl->shortcuts_filter_model, &iter, path)) ++ if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &iter, path)) + return; + +- gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_filter_model), ++ gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_pane_filter_model), + &child_iter, + &iter); + shortcuts_activate_iter (impl, &child_iter); +- +- gtk_widget_grab_focus (impl->browse_files_tree_view); + } + + /* Handler for GtkWidget::key-press-event on the shortcuts list */ +@@ -8236,8 +8986,15 @@ + gpointer data) + { + GtkFileChooserDefault *impl = data; ++ GtkTreeIter filter_iter; ++ ShortcutType shortcut_type; + +- return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR)); ++ if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path)) ++ g_assert_not_reached (); ++ ++ gtk_tree_model_get (impl->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1); ++ ++ return shortcut_type != SHORTCUT_TYPE_SEPARATOR; + } + + static gboolean +@@ -8249,6 +9006,9 @@ + { + GtkFileChooserDefault *impl = data; + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ return TRUE; ++ + if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || + impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + { +@@ -8274,7 +9034,7 @@ + GtkFileChooserDefault *impl) + { + /* See if we are in the new folder editable row for Save mode */ +- if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) ++ if (impl->operation_mode == OPERATION_MODE_BROWSE && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) + { + const GtkFileInfo *info; + gboolean had_selection; +@@ -8289,6 +9049,8 @@ + + out: + ++ /* AQUI: cambiar las siguientes funciones para que acepten SEARCH */ ++ + update_chooser_entry (impl); + check_preview_change (impl); + bookmarks_check_add_sensitivity (impl); +@@ -8306,6 +9068,12 @@ + GtkTreeIter iter, child_iter; + const GtkFileInfo *info; + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ { ++ g_signal_emit_by_name (impl, "file-activated"); ++ return; ++ } ++ + if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path)) + return; + +@@ -8376,6 +9144,15 @@ + const GtkFileInfo *info; + gboolean sensitive = TRUE; + ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ { ++ g_object_set (cell, ++ "pixbuf", NULL, ++ "sensitive", TRUE, ++ NULL); ++ return; ++ } ++ + profile_start ("start", NULL); + + info = get_list_file_info (impl, iter); +@@ -8425,13 +9202,33 @@ + gpointer data) + { + GtkFileChooserDefault *impl = data; +- const GtkFileInfo *info = get_list_file_info (impl, iter); +- gboolean sensitive = TRUE; ++ const GtkFileInfo *info; ++ gboolean sensitive; ++ ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ { ++ char *display_name; ++ ++ gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), iter, ++ SEARCH_MODEL_COL_DISPLAY_NAME, &display_name, ++ -1); ++ g_object_set (cell, ++ "text", display_name, ++ "sensitive", TRUE, ++ "ellipsize", PANGO_ELLIPSIZE_START, ++ NULL); ++ return; ++ } ++ ++ info = get_list_file_info (impl, iter); ++ sensitive = TRUE; + + if (!info) + { + g_object_set (cell, + "text", _("Type name of new folder"), ++ "sensitive", TRUE, ++ "ellipsize", PANGO_ELLIPSIZE_NONE, + NULL); + + return; +@@ -8447,6 +9244,7 @@ + g_object_set (cell, + "text", gtk_file_info_get_display_name (info), + "sensitive", sensitive, ++ "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + } + +@@ -8508,7 +9306,6 @@ + gpointer data) + { + GtkFileChooserDefault *impl; +- const GtkFileInfo *info; + GtkFileTime time_mtime; + GDate mtime, now; + int days_diff; +@@ -8517,17 +9314,35 @@ + + impl = data; + +- info = get_list_file_info (impl, iter); +- if (!info) ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) + { +- g_object_set (cell, +- "text", "", +- "sensitive", TRUE, +- NULL); +- return; ++ struct stat *statbuf; ++ ++ gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), iter, ++ SEARCH_MODEL_COL_STAT, &statbuf, ++ -1); ++ time_mtime = statbuf->st_mtime; + } ++ else ++ { ++ const GtkFileInfo *info; + +- time_mtime = gtk_file_info_get_modification_time (info); ++ info = get_list_file_info (impl, iter); ++ if (!info) ++ { ++ g_object_set (cell, ++ "text", "", ++ "sensitive", TRUE, ++ NULL); ++ return; ++ } ++ ++ time_mtime = gtk_file_info_get_modification_time (info); ++ ++ if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || ++ impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) ++ sensitive = gtk_file_info_get_is_folder (info); ++ } + + if (time_mtime == 0) + strcpy (buf, _("Unknown")); +@@ -8558,10 +9373,6 @@ + } + } + +- if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || +- impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) +- sensitive = gtk_file_info_get_is_folder (info); +- + g_object_set (cell, + "text", buf, + "sensitive", sensitive, +@@ -8587,7 +9398,23 @@ + static void + location_popup_handler (GtkFileChooserDefault *impl, + const gchar *path) +-{ ++{ ++ if (impl->operation_mode == OPERATION_MODE_SEARCH) ++ { ++ GtkWidget *widget_to_focus; ++ ++ search_switch_to_browse_mode (impl); /* This will give us the location widgets back */ ++ if (impl->current_folder) ++ change_folder_and_display_error (impl, impl->current_folder); ++ ++ if (impl->location_mode == LOCATION_MODE_PATH_BAR) ++ widget_to_focus = impl->browse_files_tree_view; ++ else ++ widget_to_focus = impl->location_entry; ++ ++ gtk_widget_grab_focus (widget_to_focus); ++ return; ++ } + if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN + || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + { +@@ -8656,6 +9483,7 @@ + g_assert_not_reached (); + + shortcuts_activate_iter (impl, &iter); ++ focus_browse_tree_view_if_possible (impl); + } + + /* Handler for the "home-folder" keybinding signal */ +@@ -8700,26 +9528,26 @@ + /* Drag and drop interfaces */ + + static void +-_shortcuts_model_filter_class_init (ShortcutsModelFilterClass *class) ++_shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class) + { + } + + static void +-_shortcuts_model_filter_init (ShortcutsModelFilter *model) ++_shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model) + { + model->impl = NULL; + } + + /* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */ + static gboolean +-shortcuts_model_filter_row_draggable (GtkTreeDragSource *drag_source, +- GtkTreePath *path) ++shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source, ++ GtkTreePath *path) + { +- ShortcutsModelFilter *model; ++ ShortcutsPaneModelFilter *model; + int pos; + int bookmarks_pos; + +- model = SHORTCUTS_MODEL_FILTER (drag_source); ++ model = SHORTCUTS_PANE_MODEL_FILTER (drag_source); + + pos = *gtk_tree_path_get_indices (path); + bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS); +@@ -8729,13 +9557,13 @@ + + /* GtkTreeDragSource::drag_data_get implementation for the shortcuts filter model */ + static gboolean +-shortcuts_model_filter_drag_data_get (GtkTreeDragSource *drag_source, +- GtkTreePath *path, +- GtkSelectionData *selection_data) ++shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source, ++ GtkTreePath *path, ++ GtkSelectionData *selection_data) + { +- ShortcutsModelFilter *model; ++ ShortcutsPaneModelFilter *model; + +- model = SHORTCUTS_MODEL_FILTER (drag_source); ++ model = SHORTCUTS_PANE_MODEL_FILTER (drag_source); + + /* FIXME */ + +@@ -8744,30 +9572,30 @@ + + /* Fill the GtkTreeDragSourceIface vtable */ + static void +-shortcuts_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface) ++shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface) + { +- iface->row_draggable = shortcuts_model_filter_row_draggable; +- iface->drag_data_get = shortcuts_model_filter_drag_data_get; ++ iface->row_draggable = shortcuts_pane_model_filter_row_draggable; ++ iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get; + } + + #if 0 + /* Fill the GtkTreeDragDestIface vtable */ + static void +-shortcuts_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface) ++shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface) + { +- iface->drag_data_received = shortcuts_model_filter_drag_data_received; +- iface->row_drop_possible = shortcuts_model_filter_row_drop_possible; ++ iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received; ++ iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible; + } + #endif + + static GtkTreeModel * +-shortcuts_model_filter_new (GtkFileChooserDefault *impl, +- GtkTreeModel *child_model, +- GtkTreePath *root) ++shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl, ++ GtkTreeModel *child_model, ++ GtkTreePath *root) + { +- ShortcutsModelFilter *model; ++ ShortcutsPaneModelFilter *model; + +- model = g_object_new (SHORTCUTS_MODEL_FILTER_TYPE, ++ model = g_object_new (SHORTCUTS_PANE_MODEL_FILTER_TYPE, + "child-model", child_model, + "virtual-root", root, + NULL); +--- gtk+-2.10.8/gtk/gtkfilechooserprivate.h.search 2007-01-16 22:14:43.000000000 -0500 ++++ gtk+-2.10.8/gtk/gtkfilechooserprivate.h 2007-01-17 01:23:26.000000000 -0500 +@@ -25,6 +25,8 @@ + #include "gtkfilesystem.h" + #include "gtkfilesystemmodel.h" + #include "gtkliststore.h" ++#include "gtksearchengine.h" ++#include "gtkquery.h" + #include "gtktooltips.h" + #include "gtktreemodelsort.h" + #include "gtktreestore.h" +@@ -147,6 +149,11 @@ + LOCATION_MODE_FILENAME_ENTRY + } LocationMode; + ++typedef enum { ++ OPERATION_MODE_BROWSE, ++ OPERATION_MODE_SEARCH ++} OperationMode; ++ + struct _GtkFileChooserDefault + { + GtkVBox parent_instance; +@@ -175,11 +182,19 @@ + GtkWidget *browse_files_popup_menu_add_shortcut_item; + GtkWidget *browse_files_popup_menu_hidden_files_item; + GtkWidget *browse_new_folder_button; ++ GtkWidget *browse_path_bar_hbox; + GtkWidget *browse_path_bar; + + GtkFileSystemModel *browse_files_model; + char *browse_files_last_selected_name; + ++ /* Widgets for searching */ ++ GtkWidget *search_hbox; ++ GtkWidget *search_entry; ++ GtkSearchEngine *search_engine; ++ GtkQuery *search_query; ++ GtkListStore *search_model; ++ + GtkWidget *filter_combo_hbox; + GtkWidget *filter_combo; + GtkWidget *preview_box; +@@ -195,7 +210,16 @@ + LocationMode location_mode; + + GtkListStore *shortcuts_model; +- GtkTreeModel *shortcuts_filter_model; ++ ++ /* Filter for the shortcuts pane. We filter out the "current folder" row and ++ * the separator that we use for the "Save in folder" combo. ++ */ ++ GtkTreeModel *shortcuts_pane_filter_model; ++ ++ /* Filter for the "Save in folder" combo. We filter out the Search row and ++ * its separator. ++ */ ++ GtkTreeModel *shortcuts_combo_filter_model; + + GtkTreeModelSort *sort_model; + +@@ -215,6 +239,8 @@ + ReloadState reload_state; + guint load_timeout_id; + ++ OperationMode operation_mode; ++ + GSList *pending_select_paths; + + GtkFileFilter *current_filter; +@@ -222,9 +248,6 @@ + + GtkTooltips *tooltips; + +- gboolean has_home; +- gboolean has_desktop; +- + int num_volumes; + int num_shortcuts; + int num_bookmarks; +@@ -239,6 +262,7 @@ + + GtkTreeViewColumn *list_name_column; + GtkCellRenderer *list_name_renderer; ++ GtkTreeViewColumn *list_mtime_column; + + GSource *edited_idle; + char *edited_new_text; +@@ -265,6 +289,9 @@ + guint list_sort_ascending : 1; + guint changing_folder : 1; + guint shortcuts_current_folder_active : 1; ++ guint has_home : 1; ++ guint has_desktop : 1; ++ guint has_search : 1; + guint expand_folders : 1; + + #if 0 +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchenginesimple.h 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,59 @@ ++/* ++ * Copyright (C) 2005 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Alexander Larsson ++ * ++ * Based on nautilus-search-engine-simple.h ++ */ ++ ++#ifndef __GTK_SEARCH_ENGINE_SIMPLE_H__ ++#define __GTK_SEARCH_ENGINE_SIMPLE_H__ ++ ++#include "gtksearchengine.h" ++ ++G_END_DECLS ++ ++#define GTK_TYPE_SEARCH_ENGINE_SIMPLE (_gtk_search_engine_simple_get_type ()) ++#define GTK_SEARCH_ENGINE_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SEARCH_ENGINE_SIMPLE, GtkSearchEngineSimple)) ++#define GTK_SEARCH_ENGINE_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SEARCH_ENGINE_SIMPLE, GtkSearchEngineSimpleClass)) ++#define GTK_IS_SEARCH_ENGINE_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SEARCH_ENGINE_SIMPLE)) ++#define GTK_IS_SEARCH_ENGINE_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SEARCH_ENGINE_SIMPLE)) ++#define GTK_SEARCH_ENGINE_SIMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SEARCH_ENGINE_SIMPLE, GtkSearchEngineSimpleClass)) ++ ++typedef struct _GtkSearchEngineSimple GtkSearchEngineSimple; ++typedef struct _GtkSearchEngineSimpleClass GtkSearchEngineSimpleClass; ++typedef struct _GtkSearchEngineSimplePrivate GtkSearchEngineSimplePrivate; ++ ++struct _GtkSearchEngineSimple ++{ ++ GtkSearchEngine parent; ++ ++ GtkSearchEngineSimplePrivate *priv; ++}; ++ ++struct _GtkSearchEngineSimpleClass ++{ ++ GtkSearchEngineClass parent_class; ++}; ++ ++GType _gtk_search_engine_simple_get_type (void); ++ ++GtkSearchEngine* _gtk_search_engine_simple_new (void); ++ ++G_END_DECLS ++ ++#endif /* __GTK_SEARCH_ENGINE_SIMPLE_H__ */ +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchenginesimple.c 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,378 @@ ++/* ++ * Copyright (C) 2005 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Alexander Larsson ++ * ++ * Based on nautilus-search-engine-simple.c ++ */ ++ ++#define _XOPEN_SOURCE 500 ++#define _GNU_SOURCE ++ ++#include ++#include "gtksearchenginesimple.h" ++ ++#define XDG_PREFIX _gtk_xdg ++#include "xdgmime/xdgmime.h" ++ ++#include ++#include ++#include ++ ++#define BATCH_SIZE 500 ++ ++typedef struct ++{ ++ GtkSearchEngineSimple *engine; ++ ++ gchar *path; ++ GList *mime_types; ++ gchar **words; ++ GList *found_list; ++ ++ gint n_processed_files; ++ GList *uri_hits; ++ ++ /* accessed on both threads: */ ++ volatile gboolean cancelled; ++} SearchThreadData; ++ ++ ++struct _GtkSearchEngineSimplePrivate ++{ ++ GtkQuery *query; ++ ++ SearchThreadData *active_search; ++ ++ gboolean query_finished; ++}; ++ ++ ++G_DEFINE_TYPE (GtkSearchEngineSimple, _gtk_search_engine_simple, GTK_TYPE_SEARCH_ENGINE); ++ ++static void ++finalize (GObject *object) ++{ ++ GtkSearchEngineSimple *simple; ++ ++ simple = GTK_SEARCH_ENGINE_SIMPLE (object); ++ ++ if (simple->priv->query) ++ { ++ g_object_unref (simple->priv->query); ++ simple->priv->query = NULL; ++ } ++ ++ g_free (simple->priv); ++ ++ G_OBJECT_CLASS (_gtk_search_engine_simple_parent_class)->finalize (object); ++} ++ ++static SearchThreadData * ++search_thread_data_new (GtkSearchEngineSimple *engine, ++ GtkQuery *query) ++{ ++ SearchThreadData *data; ++ char *text, *lower, *uri; ++ ++ data = g_new0 (SearchThreadData, 1); ++ ++ data->engine = engine; ++ uri = _gtk_query_get_location (query); ++ if (uri != NULL) ++ { ++ data->path = g_filename_from_uri (uri, NULL, NULL); ++ g_free (uri); ++ } ++ if (data->path == NULL) ++ data->path = g_strdup (g_get_home_dir ()); ++ ++ text = _gtk_query_get_text (query); ++ lower = g_ascii_strdown (text, -1); ++ data->words = g_strsplit (lower, " ", -1); ++ g_free (text); ++ g_free (lower); ++ ++ data->mime_types = _gtk_query_get_mime_types (query); ++ ++ return data; ++} ++ ++static void ++search_thread_data_free (SearchThreadData *data) ++{ ++ g_free (data->path); ++ g_strfreev (data->words); ++ g_list_foreach (data->mime_types, (GFunc)g_free, NULL); ++ g_list_free (data->mime_types); ++ g_free (data); ++} ++ ++static gboolean ++search_thread_done_idle (gpointer user_data) ++{ ++ SearchThreadData *data; ++ ++ data = user_data; ++ ++ if (!data->cancelled) ++ { ++ _gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine)); ++ data->engine->priv->active_search = NULL; ++ } ++ ++ search_thread_data_free (data); ++ ++ return FALSE; ++} ++ ++typedef struct ++{ ++ GList *uris; ++ SearchThreadData *thread_data; ++} SearchHits; ++ ++ ++static gboolean ++search_thread_add_hits_idle (gpointer user_data) ++{ ++ SearchHits *hits; ++ ++ hits = user_data; ++ ++ if (!hits->thread_data->cancelled) ++ { ++ _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (hits->thread_data->engine), ++ hits->uris); ++ } ++ ++ g_list_foreach (hits->uris, (GFunc)g_free, NULL); ++ g_list_free (hits->uris); ++ g_free (hits); ++ ++ return FALSE; ++} ++ ++static void ++send_batch (SearchThreadData *data) ++{ ++ SearchHits *hits; ++ ++ data->n_processed_files = 0; ++ ++ if (data->uri_hits) ++ { ++ hits = g_new (SearchHits, 1); ++ hits->uris = data->uri_hits; ++ hits->thread_data = data; ++ g_idle_add (search_thread_add_hits_idle, hits); ++ } ++ data->uri_hits = NULL; ++} ++ ++static GStaticPrivate search_thread_data = G_STATIC_PRIVATE_INIT; ++ ++static int ++search_visit_func (const char *fpath, ++ const struct stat *sb, ++ int typeflag, ++ struct FTW *ftwbuf) ++{ ++ SearchThreadData *data; ++ gint i; ++ const gchar *name; ++ gchar *lower_name, *path, *mime_type; ++ gchar *uri; ++ gboolean hit; ++ GList *l; ++ gboolean is_hidden; ++ ++ data = (SearchThreadData*)g_static_private_get (&search_thread_data); ++ ++ if (data->cancelled) ++ return FTW_STOP; ++ ++ name = strrchr (fpath, '/'); ++ if (name) ++ name++; ++ else ++ name = fpath; ++ ++ path = g_build_filename (data->path, fpath, NULL); ++ ++ is_hidden = *name == '.'; ++ ++ hit = FALSE; ++ ++ if (!is_hidden) ++ { ++ lower_name = g_ascii_strdown (name, -1); ++ ++ hit = TRUE; ++ for (i = 0; data->words[i] != NULL; i++) ++ { ++ if (strstr (lower_name, data->words[i]) == NULL) ++ { ++ hit = FALSE; ++ break; ++ } ++ } ++ g_free (lower_name); ++ } ++ ++ if (hit && data->mime_types != NULL) ++ { ++ hit = FALSE; ++ mime_type = xdg_mime_get_mime_type_for_file (path, (struct stat *)sb); ++ for (l = data->mime_types; l != NULL; l = l->next) ++ { ++ if (strcmp (mime_type, l->data) == 0) ++ { ++ hit = TRUE; ++ break; ++ } ++ } ++ ++ g_free (mime_type); ++ } ++ ++ if (hit) ++ { ++ uri = g_filename_to_uri (path, NULL, NULL); ++ data->uri_hits = g_list_prepend (data->uri_hits, uri); ++ } ++ ++ data->n_processed_files++; ++ ++ if (data->n_processed_files > BATCH_SIZE) ++ send_batch (data); ++ ++ if (is_hidden) ++ return FTW_SKIP_SUBTREE; ++ else ++ return FTW_CONTINUE; ++} ++ ++static gpointer ++search_thread_func (gpointer user_data) ++{ ++ SearchThreadData *data; ++ ++ data = user_data; ++ ++ g_static_private_set (&search_thread_data, data, NULL); ++ ++ nftw (data->path, search_visit_func, 20, FTW_ACTIONRETVAL | FTW_PHYS); ++ ++ send_batch (data); ++ ++ g_idle_add (search_thread_done_idle, data); ++ ++ return NULL; ++} ++ ++static void ++gtk_search_engine_simple_start (GtkSearchEngine *engine) ++{ ++ GtkSearchEngineSimple *simple; ++ SearchThreadData *data; ++ ++ simple = GTK_SEARCH_ENGINE_SIMPLE (engine); ++ ++ if (simple->priv->active_search != NULL) ++ return; ++ ++ if (simple->priv->query == NULL) ++ return; ++ ++ data = search_thread_data_new (simple, simple->priv->query); ++ ++ g_thread_create (search_thread_func, data, FALSE, NULL); ++ ++ simple->priv->active_search = data; ++} ++ ++static void ++gtk_search_engine_simple_stop (GtkSearchEngine *engine) ++{ ++ GtkSearchEngineSimple *simple; ++ ++ simple = GTK_SEARCH_ENGINE_SIMPLE (engine); ++ ++ if (simple->priv->active_search != NULL) ++ { ++ simple->priv->active_search->cancelled = TRUE; ++ simple->priv->active_search = NULL; ++ } ++} ++ ++static gboolean ++gtk_search_engine_simple_is_indexed (GtkSearchEngine *engine) ++{ ++ return FALSE; ++} ++ ++static void ++gtk_search_engine_simple_set_query (GtkSearchEngine *engine, ++ GtkQuery *query) ++{ ++ GtkSearchEngineSimple *simple; ++ ++ simple = GTK_SEARCH_ENGINE_SIMPLE (engine); ++ ++ if (query) ++ g_object_ref (query); ++ ++ if (simple->priv->query) ++ g_object_unref (simple->priv->query); ++ ++ simple->priv->query = query; ++} ++ ++static void ++_gtk_search_engine_simple_class_init (GtkSearchEngineSimpleClass *class) ++{ ++ GObjectClass *gobject_class; ++ GtkSearchEngineClass *engine_class; ++ ++ gobject_class = G_OBJECT_CLASS (class); ++ gobject_class->finalize = finalize; ++ ++ engine_class = GTK_SEARCH_ENGINE_CLASS (class); ++ engine_class->set_query = gtk_search_engine_simple_set_query; ++ engine_class->start = gtk_search_engine_simple_start; ++ engine_class->stop = gtk_search_engine_simple_stop; ++ engine_class->is_indexed = gtk_search_engine_simple_is_indexed; ++ ++ g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineSimplePrivate)); ++} ++ ++static void ++_gtk_search_engine_simple_init (GtkSearchEngineSimple *engine) ++{ ++ engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, GTK_TYPE_SEARCH_ENGINE_SIMPLE, GtkSearchEngineSimplePrivate); ++} ++ ++GtkSearchEngine * ++_gtk_search_engine_simple_new (void) ++{ ++ GtkSearchEngine *engine; ++ ++ engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_SIMPLE, NULL); ++ ++ return engine; ++} +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtksearchenginebeagle.c 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,420 @@ ++/* ++ * Copyright (C) 2005 Novell, 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Anders Carlsson ++ * ++ * Based on nautilus-search-engine-beagle.c ++ */ ++ ++#include ++#include ++#include "gtksearchenginebeagle.h" ++#if 0 ++#include ++#endif ++ ++/* We dlopen() all the following from libbeagle at runtime */ ++ ++typedef struct _BeagleHit BeagleHit; ++typedef struct _BeagleQuery BeagleQuery; ++typedef struct _BeagleClient BeagleClient; ++typedef struct _BeagleRequest BeagleRequest; ++typedef struct _BeagleFinishedResponse BeagleFinishedResponse; ++typedef struct _BeagleHitsAddedResponse BeagleHitsAddedResponse; ++typedef struct _BeagleHitsSubtractedResponse BeagleHitsSubtractedResponse; ++typedef struct _BeagleQueryPartProperty BeagleQueryPartProperty; ++typedef struct _BeagleQueryPart BeagleQueryPart; ++ ++#define BEAGLE_HIT(x) ((BeagleHit *)(x)) ++#define BEAGLE_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), beagle_request_get_type(), BeagleRequest)) ++#define BEAGLE_QUERY_PART(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), beagle_query_part_get_type(), BeagleQueryPart)) ++ ++typedef enum ++{ ++ BEAGLE_QUERY_PART_LOGIC_REQUIRED = 1, ++ BEAGLE_QUERY_PART_LOGIC_PROHIBITED = 2 ++} BeagleQueryPartLogic; ++ ++typedef enum ++ { ++ BEAGLE_PROPERTY_TYPE_UNKNOWN = 0, ++ BEAGLE_PROPERTY_TYPE_TEXT = 1, ++ BEAGLE_PROPERTY_TYPE_KEYWORD = 2, ++ BEAGLE_PROPERTY_TYPE_DATE = 3, ++ BEAGLE_PROPERTY_TYPE_LAST = 4 ++} BeaglePropertyType; ++ ++/* *static* wrapper function pointers */ ++static gboolean (*beagle_client_send_request_async) (BeagleClient *client, ++ BeagleRequest *request, ++ GError **err) = NULL; ++static G_CONST_RETURN char *(*beagle_hit_get_uri) (BeagleHit *hit) = NULL; ++static GSList *(*beagle_hits_added_response_get_hits) (BeagleHitsAddedResponse *response) = NULL; ++static GSList *(*beagle_hits_subtracted_response_get_uris) (BeagleHitsSubtractedResponse *response) = NULL; ++static BeagleQuery *(*beagle_query_new) (void) = NULL; ++static void (*beagle_query_add_text) (BeagleQuery *query, ++ const char *str) = NULL; ++static void (*beagle_query_add_hit_type) (BeagleQuery *query, ++ const char *hit_type) = NULL; ++static void (*beagle_query_add_mime_type) (BeagleQuery *query, ++ const char *mime_type) = NULL; ++static void (*beagle_query_set_max_hits) (BeagleQuery *query, ++ gint max_hits) = NULL; ++static BeagleQueryPartProperty *(*beagle_query_part_property_new) (void) = NULL; ++static void (*beagle_query_part_set_logic) (BeagleQueryPart *part, ++ BeagleQueryPartLogic logic) = NULL; ++static void (*beagle_query_part_property_set_key) (BeagleQueryPartProperty *part, ++ const char *key) = NULL; ++static void (*beagle_query_part_property_set_value) (BeagleQueryPartProperty *part, ++ const char * value) = NULL; ++static void (*beagle_query_part_property_set_property_type) (BeagleQueryPartProperty *part, ++ BeaglePropertyType prop_type) = NULL; ++static void (*beagle_query_add_part) (BeagleQuery *query, ++ BeagleQueryPart *part) = NULL; ++static GType (*beagle_request_get_type) (void) = NULL; ++static GType (*beagle_query_part_get_type) (void) = NULL; ++static gboolean (*beagle_util_daemon_is_running) (void) = NULL; ++static BeagleClient *(*beagle_client_new) (const char *client_name) = NULL; ++ ++static struct BeagleDlMapping ++{ ++ const char *fn_name; ++ gpointer *fn_ptr_ref; ++} beagle_dl_mapping[] = ++{ ++#define MAP(a) { #a, (gpointer *)&a } ++ MAP (beagle_client_send_request_async), ++ MAP (beagle_hit_get_uri), ++ MAP (beagle_hits_added_response_get_hits), ++ MAP (beagle_hits_subtracted_response_get_uris), ++ MAP (beagle_query_new), ++ MAP (beagle_query_add_text), ++ MAP (beagle_query_add_hit_type), ++ MAP (beagle_query_add_mime_type), ++ MAP (beagle_query_set_max_hits), ++ MAP (beagle_query_part_property_new), ++ MAP (beagle_query_part_set_logic), ++ MAP (beagle_query_part_property_set_key), ++ MAP (beagle_query_part_property_set_value), ++ MAP (beagle_query_part_property_set_property_type), ++ MAP (beagle_query_add_part), ++ MAP (beagle_request_get_type), ++ MAP (beagle_query_part_get_type), ++ MAP (beagle_util_daemon_is_running), ++ MAP (beagle_client_new) ++#undef MAP ++}; ++ ++static void ++open_libbeagle (void) ++{ ++ static gboolean done = FALSE; ++ ++ if (!done) ++ { ++ int i; ++ GModule *beagle; ++ ++ done = TRUE; ++ ++ beagle = g_module_open ("libbeagle.so.0", G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); ++ if (!beagle) ++ return; ++ ++ for (i = 0; i < G_N_ELEMENTS (beagle_dl_mapping); i++) ++ { ++ if (!g_module_symbol (beagle, beagle_dl_mapping[i].fn_name, ++ beagle_dl_mapping[i].fn_ptr_ref)) ++ { ++ g_warning ("Missing symbol '%s' in libbeagle\n", ++ beagle_dl_mapping[i].fn_name); ++ g_module_close (beagle); ++ ++ for (i = 0; i < G_N_ELEMENTS (beagle_dl_mapping); i++) ++ beagle_dl_mapping[i].fn_ptr_ref = NULL; ++ ++ return; ++ } ++ } ++ } ++} ++ ++ ++struct _GtkSearchEngineBeaglePrivate ++{ ++ BeagleClient *client; ++ GtkQuery *query; ++ ++ BeagleQuery *current_query; ++ char *current_query_uri_prefix; ++ gboolean query_finished; ++}; ++ ++ ++G_DEFINE_TYPE (GtkSearchEngineBeagle, _gtk_search_engine_beagle, GTK_TYPE_SEARCH_ENGINE); ++ ++static void ++finalize (GObject *object) ++{ ++ GtkSearchEngineBeagle *beagle; ++ ++ beagle = GTK_SEARCH_ENGINE_BEAGLE (object); ++ ++ if (beagle->priv->current_query) ++ { ++ g_object_unref (beagle->priv->current_query); ++ beagle->priv->current_query = NULL; ++ g_free (beagle->priv->current_query_uri_prefix); ++ beagle->priv->current_query_uri_prefix = NULL; ++ } ++ ++ if (beagle->priv->query) ++ { ++ g_object_unref (beagle->priv->query); ++ beagle->priv->query = NULL; ++ } ++ ++ if (beagle->priv->client) ++ { ++ g_object_unref (beagle->priv->client); ++ beagle->priv->client = NULL; ++ } ++ ++ G_OBJECT_CLASS (_gtk_search_engine_beagle_parent_class)->finalize (object); ++} ++ ++static void ++beagle_hits_added (BeagleQuery *query, ++ BeagleHitsAddedResponse *response, ++ GtkSearchEngineBeagle *engine) ++{ ++ GSList *hits, *list; ++ GList *hit_uris; ++ const gchar *uri; ++ ++ hit_uris = NULL; ++ ++ hits = beagle_hits_added_response_get_hits (response); ++ ++ for (list = hits; list != NULL; list = list->next) ++ { ++ BeagleHit *hit = BEAGLE_HIT (list->data); ++ ++ uri = beagle_hit_get_uri (hit); ++ ++ if (engine->priv->current_query_uri_prefix && ++ !g_str_has_prefix (uri, engine->priv->current_query_uri_prefix)) ++ continue; ++ ++ hit_uris = g_list_prepend (hit_uris, (char *)uri); ++ } ++ ++ _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (engine), hit_uris); ++ g_list_free (hit_uris); ++} ++ ++static void ++beagle_hits_subtracted (BeagleQuery *query, ++ BeagleHitsSubtractedResponse *response, ++ GtkSearchEngineBeagle *engine) ++{ ++ GSList *uris, *list; ++ GList *hit_uris; ++ ++ hit_uris = NULL; ++ ++ uris = beagle_hits_subtracted_response_get_uris (response); ++ ++ for (list = uris; list != NULL; list = list->next) ++ { ++ hit_uris = g_list_prepend (hit_uris, (char *)list->data); ++ } ++ ++ _gtk_search_engine_hits_subtracted (GTK_SEARCH_ENGINE (engine), hit_uris); ++ g_list_free (hit_uris); ++} ++ ++static void ++beagle_finished (BeagleQuery *query, ++ BeagleFinishedResponse *response, ++ GtkSearchEngineBeagle *engine) ++{ ++ /* For some reason we keep getting finished events, ++ * only emit finished once */ ++ if (engine->priv->query_finished) ++ return; ++ ++ engine->priv->query_finished = TRUE; ++ _gtk_search_engine_finished (GTK_SEARCH_ENGINE (engine)); ++} ++ ++static void ++beagle_error (BeagleQuery *query, ++ GError *error, ++ GtkSearchEngineBeagle *engine) ++{ ++ _gtk_search_engine_error (GTK_SEARCH_ENGINE (engine), error->message); ++} ++ ++static void ++gtk_search_engine_beagle_start (GtkSearchEngine *engine) ++{ ++ GtkSearchEngineBeagle *beagle; ++ GError *error; ++ GList *mimetypes, *l; ++ gchar *text, *mimetype; ++ ++ error = NULL; ++ beagle = GTK_SEARCH_ENGINE_BEAGLE (engine); ++ ++ if (beagle->priv->current_query) ++ return; ++ ++ beagle->priv->query_finished = FALSE; ++ beagle->priv->current_query = beagle_query_new (); ++ g_signal_connect (beagle->priv->current_query, ++ "hits-added", G_CALLBACK (beagle_hits_added), engine); ++ g_signal_connect (beagle->priv->current_query, ++ "hits-subtracted", G_CALLBACK (beagle_hits_subtracted), engine); ++ g_signal_connect (beagle->priv->current_query, ++ "finished", G_CALLBACK (beagle_finished), engine); ++ g_signal_connect (beagle->priv->current_query, ++ "error", G_CALLBACK (beagle_error), engine); ++ ++ /* We only want files */ ++ beagle_query_add_hit_type (beagle->priv->current_query, ++ "File"); ++ beagle_query_set_max_hits (beagle->priv->current_query, ++ 1000); ++ ++ text = _gtk_query_get_text (beagle->priv->query); ++ beagle_query_add_text (beagle->priv->current_query, ++ text); ++ ++ mimetypes = _gtk_query_get_mime_types (beagle->priv->query); ++ for (l = mimetypes; l != NULL; l = l->next) ++ { ++ mimetype = l->data; ++ beagle_query_add_mime_type (beagle->priv->current_query, mimetype); ++ } ++ ++ beagle->priv->current_query_uri_prefix = _gtk_query_get_location (beagle->priv->query); ++ ++ if (!beagle_client_send_request_async (beagle->priv->client, ++ BEAGLE_REQUEST (beagle->priv->current_query), &error)) ++ { ++ _gtk_search_engine_error (engine, error->message); ++ g_error_free (error); ++ } ++ ++ /* These must live during the lifetime of the query */ ++ g_free (text); ++ g_list_foreach (mimetypes, (GFunc)g_free, NULL); ++ g_list_free (mimetypes); ++} ++ ++static void ++gtk_search_engine_beagle_stop (GtkSearchEngine *engine) ++{ ++ GtkSearchEngineBeagle *beagle; ++ ++ beagle = GTK_SEARCH_ENGINE_BEAGLE (engine); ++ ++ if (beagle->priv->current_query) ++ { ++ g_object_unref (beagle->priv->current_query); ++ beagle->priv->current_query = NULL; ++ g_free (beagle->priv->current_query_uri_prefix); ++ beagle->priv->current_query_uri_prefix = NULL; ++ } ++} ++ ++static gboolean ++gtk_search_engine_beagle_is_indexed (GtkSearchEngine *engine) ++{ ++ return TRUE; ++} ++ ++static void ++gtk_search_engine_beagle_set_query (GtkSearchEngine *engine, ++ GtkQuery *query) ++{ ++ GtkSearchEngineBeagle *beagle; ++ ++ beagle = GTK_SEARCH_ENGINE_BEAGLE (engine); ++ ++ if (query) ++ g_object_ref (query); ++ ++ if (beagle->priv->query) ++ g_object_unref (beagle->priv->query); ++ ++ beagle->priv->query = query; ++} ++ ++static void ++_gtk_search_engine_beagle_class_init (GtkSearchEngineBeagleClass *class) ++{ ++ GObjectClass *gobject_class; ++ GtkSearchEngineClass *engine_class; ++ ++ gobject_class = G_OBJECT_CLASS (class); ++ gobject_class->finalize = finalize; ++ ++ engine_class = GTK_SEARCH_ENGINE_CLASS (class); ++ engine_class->set_query = gtk_search_engine_beagle_set_query; ++ engine_class->start = gtk_search_engine_beagle_start; ++ engine_class->stop = gtk_search_engine_beagle_stop; ++ engine_class->is_indexed = gtk_search_engine_beagle_is_indexed; ++ ++ g_type_class_add_private (gobject_class, sizeof (GtkSearchEngineBeaglePrivate)); ++} ++ ++static void ++_gtk_search_engine_beagle_init (GtkSearchEngineBeagle *engine) ++{ ++ engine->priv = G_TYPE_INSTANCE_GET_PRIVATE (engine, GTK_TYPE_SEARCH_ENGINE_BEAGLE, GtkSearchEngineBeaglePrivate); ++} ++ ++ ++GtkSearchEngine * ++_gtk_search_engine_beagle_new (void) ++{ ++ GtkSearchEngineBeagle *engine; ++ BeagleClient *client; ++ ++ open_libbeagle (); ++ ++ if (!beagle_util_daemon_is_running) ++ return NULL; ++ ++ /* check whether daemon is running as beagle_client_new ++ * doesn't fail when a stale socket file exists */ ++ if (!beagle_util_daemon_is_running ()) ++ return NULL; ++ ++ client = beagle_client_new (NULL); ++ ++ if (client == NULL) ++ return NULL; ++ ++ engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_BEAGLE, NULL); ++ ++ engine->priv->client = client; ++ ++ return GTK_SEARCH_ENGINE (engine); ++} +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtkquery.c 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,142 @@ ++/* ++ * Copyright (C) 2005 Novell, 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Anders Carlsson ++ * ++ * Based on nautilus-query.c ++ */ ++ ++#include ++#include ++ ++#include "gtkquery.h" ++ ++struct _GtkQueryPrivate ++{ ++ gchar *text; ++ gchar *location_uri; ++ GList *mime_types; ++}; ++ ++G_DEFINE_TYPE (GtkQuery, _gtk_query, G_TYPE_OBJECT); ++ ++static void ++finalize (GObject *object) ++{ ++ GtkQuery *query; ++ ++ query = GTK_QUERY (object); ++ ++ g_free (query->priv->text); ++ ++ G_OBJECT_CLASS (_gtk_query_parent_class)->finalize (object); ++} ++ ++static void ++_gtk_query_class_init (GtkQueryClass *class) ++{ ++ GObjectClass *gobject_class; ++ ++ gobject_class = G_OBJECT_CLASS (class); ++ gobject_class->finalize = finalize; ++ ++ g_type_class_add_private (gobject_class, sizeof (GtkQueryPrivate)); ++} ++ ++static void ++_gtk_query_init (GtkQuery *query) ++{ ++ query->priv = G_TYPE_INSTANCE_GET_PRIVATE (query, GTK_TYPE_QUERY, GtkQueryPrivate); ++} ++ ++GtkQuery * ++_gtk_query_new (void) ++{ ++ return g_object_new (GTK_TYPE_QUERY, NULL); ++} ++ ++ ++gchar * ++_gtk_query_get_text (GtkQuery *query) ++{ ++ return g_strdup (query->priv->text); ++} ++ ++void ++_gtk_query_set_text (GtkQuery *query, ++ const gchar *text) ++{ ++ g_free (query->priv->text); ++ query->priv->text = g_strdup (text); ++} ++ ++gchar * ++_gtk_query_get_location (GtkQuery *query) ++{ ++ return g_strdup (query->priv->location_uri); ++} ++ ++void ++_gtk_query_set_location (GtkQuery *query, ++ const gchar *uri) ++{ ++ g_free (query->priv->location_uri); ++ query->priv->location_uri = g_strdup (uri); ++} ++ ++GList * ++_gtk_query_get_mime_types (GtkQuery *query) ++{ ++ GList *list, *l; ++ gchar *mime_type; ++ ++ list = NULL; ++ for (l = query->priv->mime_types; l; l = l->next) ++ { ++ mime_type = (gchar*)l->data; ++ list = g_list_prepend (list, g_strdup (mime_type)); ++ } ++ ++ return list; ++} ++ ++void ++_gtk_query_set_mime_types (GtkQuery *query, ++ GList *mime_types) ++{ ++ GList *l; ++ gchar *mime_type; ++ ++ g_list_foreach (query->priv->mime_types, (GFunc)g_free, NULL); ++ g_list_free (query->priv->mime_types); ++ query->priv->mime_types = NULL; ++ ++ for (l = mime_types; l; l = l->next) ++ { ++ mime_type = (gchar*)l->data; ++ query->priv->mime_types = g_list_prepend (query->priv->mime_types, g_strdup (mime_type)); ++ } ++} ++ ++void ++_gtk_query_add_mime_type (GtkQuery *query, ++ const gchar *mime_type) ++{ ++ query->priv->mime_types = g_list_prepend (query->priv->mime_types, ++ g_strdup (mime_type)); ++} ++ +--- /dev/null 2007-01-16 08:16:47.736226048 -0500 ++++ gtk+-2.10.8/gtk/gtkquery.h 2007-01-17 01:21:08.000000000 -0500 +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (C) 2005 Novell, 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, write to the ++ * Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++ * ++ * Author: Anders Carlsson ++ * ++ * Based on nautilus-query.h ++ */ ++ ++#ifndef __GTK_QUERY_H__ ++#define __GTK_QUERY_H__ ++ ++#include ++ ++G_BEGIN_DECLS ++ ++#define GTK_TYPE_QUERY (_gtk_query_get_type ()) ++#define GTK_QUERY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_QUERY, GtkQuery)) ++#define GTK_QUERY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_QUERY, GtkQueryClass)) ++#define GTK_IS_QUERY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_QUERY)) ++#define GTK_IS_QUERY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_QUERY)) ++#define GTK_QUERY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_QUERY, GtkQueryClass)) ++ ++typedef struct _GtkQuery GtkQuery; ++typedef struct _GtkQueryClass GtkQueryClass; ++typedef struct _GtkQueryPrivate GtkQueryPrivate; ++ ++struct _GtkQuery ++{ ++ GObject parent; ++ ++ GtkQueryPrivate *priv; ++}; ++ ++struct _GtkQueryClass ++{ ++ GObjectClass parent_class; ++}; ++ ++GType _gtk_query_get_type (void); ++gboolean _gtk_query_enabled (void); ++ ++GtkQuery* _gtk_query_new (void); ++ ++gchar* _gtk_query_get_text (GtkQuery *query); ++void _gtk_query_set_text (GtkQuery *query, ++ const gchar *text); ++ ++gchar* _gtk_query_get_location (GtkQuery *query); ++void _gtk_query_set_location (GtkQuery *query, ++ const gchar *uri); ++ ++GList* _gtk_query_get_mime_types (GtkQuery *query); ++void _gtk_query_set_mime_types (GtkQuery *query, ++ GList *mime_types); ++void _gtk_query_add_mime_type (GtkQuery *query, ++ const gchar *mime_type); ++ ++G_END_DECLS ++ ++#endif /* __GTK_QUERY_H__ */ diff --git a/gtk2.spec b/gtk2.spec index 214802d..1c22f3a 100644 --- a/gtk2.spec +++ b/gtk2.spec @@ -27,7 +27,7 @@ Patch0: gtk+-2.4.1-lib64.patch # Fedora patch Patch1: gtk+-2.8.10-set-invisible-char-to-bullet.patch # Filechooser search -Patch2: gtk+-2.10.7-search.patch +Patch2: gtk+-2.10.8-search.patch # use fam for recent-files Patch3: gtk+-2.10.3-fam.patch