diff --git a/gtk+-2.10.12-search.patch b/gtk+-2.10.12-search.patch new file mode 100644 index 0000000..912e8e8 --- /dev/null +++ b/gtk+-2.10.12-search.patch @@ -0,0 +1,4436 @@ +--- /dev/null 2007-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchenginetracker.h 2007-05-19 19:51:53.000000000 -0400 +@@ -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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchengine.h 2007-05-19 19:51:53.000000000 -0400 +@@ -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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchenginebeagle.h 2007-05-19 19:51:53.000000000 -0400 +@@ -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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchengine.c 2007-05-19 19:51:53.000000000 -0400 +@@ -0,0 +1,199 @@ ++/* ++ * 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 ++ ++ if (g_thread_supported ()) ++ 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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchenginetracker.c 2007-05-19 19:51:53.000000000 -0400 +@@ -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.12/gtk/Makefile.am.search 2007-05-02 12:41:42.000000000 -0400 ++++ gtk+-2.10.12/gtk/Makefile.am 2007-05-19 19:51:53.000000000 -0400 +@@ -333,6 +333,11 @@ gtk_semi_private_h_sources = \ + + # 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 \ +@@ -376,6 +381,11 @@ gtk_private_h_sources = \ + + # 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.12/gtk/gtkfilechooserdefault.c.search 2007-05-02 12:26:59.000000000 -0400 ++++ gtk+-2.10.12/gtk/gtkfilechooserdefault.c 2007-05-19 20:09:30.000000000 -0400 +@@ -79,6 +79,8 @@ + #include + #include + #include ++#include ++#include + + + #ifdef HAVE_UNISTD_H +@@ -173,13 +175,20 @@ enum { + 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, +@@ -188,6 +197,16 @@ enum { + 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, +@@ -227,9 +246,19 @@ static const GtkTargetEntry file_list_de + 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, +@@ -424,6 +453,14 @@ static void location_button_toggled_cb ( + 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); + + + +@@ -433,26 +470,26 @@ typedef struct { + 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); + + + +@@ -687,6 +724,7 @@ _gtk_file_chooser_default_init (GtkFileC + 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); + +@@ -702,22 +740,24 @@ shortcuts_free_row_data (GtkFileChooserD + 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; + +@@ -728,6 +768,8 @@ shortcuts_free_row_data (GtkFileChooserD + { + GtkFilePath *path; + ++ g_assert (shortcut_type == SHORTCUT_TYPE_PATH); ++ + path = col_data; + gtk_file_path_free (path); + } +@@ -814,8 +856,11 @@ gtk_file_chooser_default_finalize (GObje + 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); + +@@ -852,6 +897,9 @@ gtk_file_chooser_default_finalize (GObje + + if (impl->sort_model) + g_object_unref (impl->sort_model); ++ ++ search_stop_searching (impl); ++ search_clear_model (impl, FALSE); + + g_free (impl->preview_display_name); + +@@ -1105,6 +1153,14 @@ set_preview_widget (GtkFileChooserDefaul + 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 + { +@@ -1175,19 +1231,20 @@ shortcuts_reload_icons (GtkFileChooserDe + 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; + +@@ -1202,46 +1259,53 @@ shortcuts_reload_icons (GtkFileChooserDe + 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); + } + } + } +@@ -1373,7 +1437,6 @@ get_file_info_finished (GtkFileSystemHan + { + gint pos = -1; + gboolean cancelled = handle->cancelled; +- gboolean is_volume = FALSE; + GdkPixbuf *pixbuf; + GtkTreePath *path; + GtkTreeIter iter; +@@ -1438,12 +1501,15 @@ get_file_info_finished (GtkFileSystemHan + 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) +@@ -1455,7 +1521,7 @@ get_file_info_finished (GtkFileSystemHan + 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); +@@ -1529,7 +1595,7 @@ _gtk_file_chooser_label_for_uri (const g + static void + shortcuts_insert_path (GtkFileChooserDefault *impl, + int pos, +- gboolean is_volume, ++ ShortcutType shortcut_type, + GtkFileSystemVolume *volume, + const GtkFilePath *path, + const char *label, +@@ -1542,79 +1608,89 @@ shortcuts_insert_path (GtkFileChooserDef + 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) +@@ -1629,13 +1705,16 @@ shortcuts_insert_path (GtkFileChooserDef + 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) + { +@@ -1647,7 +1726,8 @@ shortcuts_insert_path (GtkFileChooserDef + 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); +@@ -1661,6 +1741,30 @@ shortcuts_insert_path (GtkFileChooserDef + 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) +@@ -1679,7 +1783,8 @@ shortcuts_append_home (GtkFileChooserDef + + 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); + +@@ -1712,7 +1817,9 @@ shortcuts_append_desktop (GtkFileChooser + 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. + */ +@@ -1751,7 +1858,7 @@ shortcuts_append_paths (GtkFileChooserDe + 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); +@@ -1770,6 +1877,16 @@ shortcuts_get_index (GtkFileChooserDefau + 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; +@@ -1867,15 +1984,18 @@ shortcuts_add_volumes (GtkFileChooserDef + } + } + +- 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; + +@@ -1885,11 +2005,13 @@ shortcuts_add_volumes (GtkFileChooserDef + /* 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)); +@@ -1898,6 +2020,7 @@ shortcuts_insert_separator (GtkFileChoos + SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE, + SHORTCUTS_COL_NAME, NULL, + SHORTCUTS_COL_DATA, NULL, ++ SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR, + -1); + } + +@@ -1910,7 +2033,7 @@ shortcuts_add_bookmarks (GtkFileChooserD + GtkTreeIter iter; + GtkFilePath *list_selected = NULL; + GtkFilePath *combo_selected = NULL; +- gboolean is_volume; ++ ShortcutType shortcut_type; + gpointer col_data; + + profile_start ("start", NULL); +@@ -1923,10 +2046,10 @@ shortcuts_add_bookmarks (GtkFileChooserD + 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); + } + +@@ -1934,13 +2057,18 @@ shortcuts_add_bookmarks (GtkFileChooserD + 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); + } + +@@ -1958,8 +2086,11 @@ shortcuts_add_bookmarks (GtkFileChooserD + 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) + { +@@ -1973,8 +2104,8 @@ shortcuts_add_bookmarks (GtkFileChooserD + + 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); + } + +@@ -2019,11 +2150,11 @@ shortcuts_add_current_folder (GtkFileCho + 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); + } +@@ -2032,7 +2163,8 @@ shortcuts_add_current_folder (GtkFileCho + 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 */ +@@ -2054,9 +2186,9 @@ shortcuts_update_current_folder (GtkFile + + /* 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; +@@ -2083,11 +2215,17 @@ shortcuts_model_create (GtkFileChooserDe + 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); +@@ -2095,12 +2233,12 @@ shortcuts_model_create (GtkFileChooserDe + 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); + } +@@ -2318,16 +2456,16 @@ shortcut_find_position (GtkFileChooserDe + 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; +@@ -2343,7 +2481,7 @@ shortcut_find_position (GtkFileChooserDe + if (exists) + return i; + } +- else ++ else if (shortcut_type == SHORTCUT_TYPE_PATH) + { + GtkFilePath *model_path; + +@@ -2449,7 +2587,7 @@ shortcuts_get_selected (GtkFileChooserDe + 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; +@@ -2472,11 +2610,12 @@ remove_selected_bookmarks (GtkFileChoose + 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; +@@ -2633,6 +2772,16 @@ bookmarks_check_add_sensitivity (GtkFile + 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) +@@ -3146,7 +3295,7 @@ shortcuts_reorder (GtkFileChooserDefault + { + GtkTreeIter iter; + gpointer col_data; +- gboolean is_volume; ++ ShortcutType shortcut_type; + GtkTreePath *path; + int old_position; + int bookmarks_index; +@@ -3171,10 +3320,10 @@ shortcuts_reorder (GtkFileChooserDefault + 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 */ +@@ -3258,17 +3407,11 @@ shortcuts_row_separator_func (GtkTreeMod + 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 +@@ -3513,7 +3656,7 @@ shortcuts_list_create (GtkFileChooserDef + /* 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, +@@ -3586,8 +3729,7 @@ shortcuts_list_create (GtkFileChooserDef + + 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); + +@@ -3958,6 +4100,8 @@ file_list_update_popup_menu (GtkFileChoo + { + 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() + */ +@@ -4057,6 +4201,28 @@ list_button_press_event_cb (GtkWidget + 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) +@@ -4070,7 +4236,7 @@ create_file_list (GtkFileChooserDefault + + 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); + +@@ -4160,6 +4326,7 @@ create_file_list (GtkFileChooserDefault + 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 (); +@@ -4170,8 +4337,11 @@ create_file_list (GtkFileChooserDefault + 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; +@@ -4279,9 +4449,59 @@ save_folder_combo_changed_cb (GtkComboBo + 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) +@@ -4289,8 +4509,14 @@ save_folder_combo_create (GtkFileChooser + 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); +@@ -4312,8 +4538,7 @@ save_folder_combo_create (GtkFileChooser + + 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); +@@ -4658,6 +4883,7 @@ browse_widgets_create (GtkFileChooserDef + 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); +@@ -5242,6 +5468,9 @@ gtk_file_chooser_default_dispose (GObjec + 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); +@@ -5519,31 +5748,32 @@ gtk_file_chooser_default_map (GtkWidget + + 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, FALSE); +- } +- break; ++ 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, FALSE); ++ } ++ break; + +- default: +- g_assert_not_reached (); +- } ++ default: ++ g_assert_not_reached (); ++ } + + bookmarks_changed_cb (impl->file_system, impl); + +@@ -6014,12 +6244,6 @@ pending_select_paths_process (GtkFileCho + * 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); +@@ -6066,17 +6290,11 @@ browse_files_model_finished_loading_cb ( + 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); +@@ -6088,6 +6306,20 @@ set_list_model (GtkFileChooserDefault *i + 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); +@@ -6162,6 +6394,9 @@ update_chooser_entry (GtkFileChooserDefa + 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 +@@ -6183,35 +6418,40 @@ update_chooser_entry (GtkFileChooserDefa + } + 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 + { +@@ -6425,6 +6665,8 @@ gtk_file_chooser_default_update_current_ + + profile_start ("start", (char *) path); + ++ search_switch_to_browse_mode (impl); ++ + g_assert (path != NULL); + + if (impl->local_only && +@@ -6467,6 +6709,9 @@ gtk_file_chooser_default_get_current_fol + { + 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; +@@ -6527,9 +6772,9 @@ gtk_file_chooser_default_select_path (Gt + 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 + { +@@ -6625,6 +6870,16 @@ static void + 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); +@@ -6764,6 +7019,10 @@ gtk_file_chooser_default_get_paths (GtkF + 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; +@@ -6775,12 +7034,14 @@ gtk_file_chooser_default_get_paths (GtkF + 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); + +@@ -6815,8 +7076,12 @@ gtk_file_chooser_default_get_paths (GtkF + 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; +@@ -6985,7 +7250,7 @@ add_shortcut_get_info_cb (GtkFileSystemH + + 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); +@@ -7099,15 +7364,15 @@ gtk_file_chooser_default_remove_shortcut + 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) +@@ -7156,15 +7421,15 @@ gtk_file_chooser_default_list_shortcut_f + 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)); +@@ -7511,6 +7776,23 @@ should_respond_after_confirm_overwrite ( + } + } + ++/* 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, +@@ -7776,6 +8058,9 @@ gtk_file_chooser_default_should_respond + + 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) +@@ -7911,7 +8196,7 @@ gtk_file_chooser_default_should_respond + { + 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; +@@ -7925,6 +8210,11 @@ gtk_file_chooser_default_should_respond + */ + 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 +@@ -7974,22 +8264,460 @@ gtk_file_chooser_default_initial_focus ( + 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 +- */ +- filter_index = g_slist_index (impl->filters, filter); +- if (impl->filters && filter && filter_index < 0) +- return; ++ list = data; + +- if (impl->current_filter) +- g_object_unref (impl->current_filter); ++ 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); ++ _gtk_search_engine_set_query (impl->search_engine, impl->search_query); ++ ++ 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) ++ return; ++ ++ if (impl->current_filter) ++ g_object_unref (impl->current_filter); + impl->current_filter = filter; + if (impl->current_filter) + { +@@ -8022,28 +8750,44 @@ check_preview_change (GtkFileChooserDefa + { + 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 && +@@ -8059,7 +8803,7 @@ check_preview_change (GtkFileChooserDefa + 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 + { +@@ -8128,6 +8872,8 @@ shortcuts_activate_volume (GtkFileChoose + { + 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. + */ +@@ -8148,6 +8894,7 @@ shortcuts_activate_volume (GtkFileChoose + if (path != NULL) + { + change_folder_and_display_error (impl, path, FALSE); ++ focus_browse_tree_view_if_possible (impl); + gtk_file_path_free (path); + } + } +@@ -8180,7 +8927,10 @@ shortcuts_activate_get_info_cb (GtkFileS + goto out; + + if (!error && gtk_file_info_get_is_folder (info)) +- change_folder_and_display_error (data->impl, data->path, FALSE); ++ { ++ change_folder_and_display_error (data->impl, data->path, FALSE); ++ focus_browse_tree_view_if_possible (data->impl); ++ } + else + gtk_file_chooser_default_select_path (GTK_FILE_CHOOSER (data->impl), data->path, NULL); + +@@ -8197,26 +8947,25 @@ shortcuts_activate_iter (GtkFileChooserD + 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; + +@@ -8224,7 +8973,7 @@ shortcuts_activate_iter (GtkFileChooserD + + shortcuts_activate_volume (impl, volume); + } +- else ++ else if (shortcut_type == SHORTCUT_TYPE_PATH) + { + struct ShortcutsActivateData *data; + +@@ -8237,6 +8986,10 @@ shortcuts_activate_iter (GtkFileChooserD + 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 */ +@@ -8249,15 +9002,13 @@ shortcuts_row_activated_cb (GtkTreeView + 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 */ +@@ -8297,8 +9048,15 @@ shortcuts_select_func (GtkTreeSelection + gpointer data) + { + GtkFileChooserDefault *impl = data; ++ GtkTreeIter filter_iter; ++ ShortcutType shortcut_type; ++ ++ if (!gtk_tree_model_get_iter (impl->shortcuts_pane_filter_model, &filter_iter, path)) ++ g_assert_not_reached (); + +- return (*gtk_tree_path_get_indices (path) != shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR)); ++ 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 +@@ -8310,6 +9068,9 @@ list_select_func (GtkTreeSelection *se + { + 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) + { +@@ -8335,7 +9096,7 @@ list_selection_changed (GtkTreeSelection + 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; +@@ -8368,6 +9129,12 @@ list_row_activated (GtkTreeView + 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; + +@@ -8438,6 +9205,15 @@ list_icon_data_func (GtkTreeViewColumn * + 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); +@@ -8487,13 +9263,33 @@ list_name_data_func (GtkTreeViewColumn * + 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; +@@ -8509,6 +9305,7 @@ list_name_data_func (GtkTreeViewColumn * + g_object_set (cell, + "text", gtk_file_info_get_display_name (info), + "sensitive", sensitive, ++ "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + } + +@@ -8570,7 +9367,6 @@ list_mtime_data_func (GtkTreeViewColumn + gpointer data) + { + GtkFileChooserDefault *impl; +- const GtkFileInfo *info; + GtkFileTime time_mtime; + GDate mtime, now; + int days_diff; +@@ -8579,17 +9375,35 @@ list_mtime_data_func (GtkTreeViewColumn + + 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")); +@@ -8620,10 +9434,6 @@ list_mtime_data_func (GtkTreeViewColumn + } + } + +- 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, +@@ -8649,7 +9459,23 @@ location_set_user_text (GtkFileChooserDe + 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, FALSE); ++ ++ 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) + { +@@ -8718,6 +9544,7 @@ switch_to_shortcut (GtkFileChooserDefaul + g_assert_not_reached (); + + shortcuts_activate_iter (impl, &iter); ++ focus_browse_tree_view_if_possible (impl); + } + + /* Handler for the "home-folder" keybinding signal */ +@@ -8769,26 +9596,26 @@ show_hidden_handler (GtkFileChooserDefau + /* 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); +@@ -8798,13 +9625,13 @@ shortcuts_model_filter_row_draggable (Gt + + /* 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 */ + +@@ -8813,30 +9640,30 @@ shortcuts_model_filter_drag_data_get (Gt + + /* 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.12/gtk/gtkfilechooserprivate.h.search 2007-05-02 12:26:59.000000000 -0400 ++++ gtk+-2.10.12/gtk/gtkfilechooserprivate.h 2007-05-19 19:51:53.000000000 -0400 +@@ -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 @@ typedef enum { + LOCATION_MODE_FILENAME_ENTRY + } LocationMode; + ++typedef enum { ++ OPERATION_MODE_BROWSE, ++ OPERATION_MODE_SEARCH ++} OperationMode; ++ + struct _GtkFileChooserDefault + { + GtkVBox parent_instance; +@@ -175,11 +182,19 @@ struct _GtkFileChooserDefault + 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 @@ struct _GtkFileChooserDefault + 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 @@ struct _GtkFileChooserDefault + ReloadState reload_state; + guint load_timeout_id; + ++ OperationMode operation_mode; ++ + GSList *pending_select_paths; + + GtkFileFilter *current_filter; +@@ -222,9 +248,6 @@ struct _GtkFileChooserDefault + + GtkTooltips *tooltips; + +- gboolean has_home; +- gboolean has_desktop; +- + int num_volumes; + int num_shortcuts; + int num_bookmarks; +@@ -239,6 +262,7 @@ struct _GtkFileChooserDefault + + GtkTreeViewColumn *list_name_column; + GtkCellRenderer *list_name_renderer; ++ GtkTreeViewColumn *list_mtime_column; + + GSource *edited_idle; + char *edited_new_text; +@@ -265,6 +289,9 @@ struct _GtkFileChooserDefault + 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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchenginesimple.h 2007-05-19 19:51:53.000000000 -0400 +@@ -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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchenginesimple.c 2007-05-19 19:51:53.000000000 -0400 +@@ -0,0 +1,374 @@ ++/* ++ * 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_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, *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; ++ ++ 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 (fpath, (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 (fpath, 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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtksearchenginebeagle.c 2007-05-19 19:51:53.000000000 -0400 +@@ -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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtkquery.c 2007-05-19 19:51:53.000000000 -0400 +@@ -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-05-19 19:05:57.724948798 -0400 ++++ gtk+-2.10.12/gtk/gtkquery.h 2007-05-19 19:51:53.000000000 -0400 +@@ -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/gtk+-2.10.8-search.patch b/gtk+-2.10.8-search.patch deleted file mode 100644 index 720c910..0000000 --- a/gtk+-2.10.8-search.patch +++ /dev/null @@ -1,4434 +0,0 @@ ---- /dev/null 2007-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchenginetracker.h 2007-05-15 01:04:07.000000000 -0400 -@@ -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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchengine.h 2007-05-15 01:04:07.000000000 -0400 -@@ -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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchenginebeagle.h 2007-05-15 01:04:07.000000000 -0400 -@@ -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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchengine.c 2007-05-15 01:05:14.000000000 -0400 -@@ -0,0 +1,199 @@ -+/* -+ * 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 -+ -+ if (g_thread_supported ()) -+ 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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchenginetracker.c 2007-05-15 01:04:07.000000000 -0400 -@@ -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.11/gtk/Makefile.am.search 2007-03-14 00:07:06.000000000 -0400 -+++ gtk+-2.10.11/gtk/Makefile.am 2007-05-15 01:04:07.000000000 -0400 -@@ -332,6 +332,11 @@ gtk_semi_private_h_sources = \ - - # 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_private_h_sources = \ - - # 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.11/gtk/gtkfilechooserdefault.c.search 2007-03-14 00:07:03.000000000 -0400 -+++ gtk+-2.10.11/gtk/gtkfilechooserdefault.c 2007-05-15 01:04:07.000000000 -0400 -@@ -79,6 +79,8 @@ - #include - #include - #include -+#include -+#include - - - -@@ -172,13 +174,20 @@ enum { - 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, -@@ -187,6 +196,16 @@ enum { - 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, -@@ -226,9 +245,19 @@ static const GtkTargetEntry file_list_de - 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, -@@ -420,6 +449,14 @@ static void location_button_toggled_cb ( - 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); - - - -@@ -429,26 +466,26 @@ typedef struct { - 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); - - - -@@ -664,6 +701,7 @@ _gtk_file_chooser_default_init (GtkFileC - 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); - -@@ -679,22 +717,24 @@ shortcuts_free_row_data (GtkFileChooserD - 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; - -@@ -705,6 +745,8 @@ shortcuts_free_row_data (GtkFileChooserD - { - GtkFilePath *path; - -+ g_assert (shortcut_type == SHORTCUT_TYPE_PATH); -+ - path = col_data; - gtk_file_path_free (path); - } -@@ -791,8 +833,11 @@ gtk_file_chooser_default_finalize (GObje - 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,9 @@ gtk_file_chooser_default_finalize (GObje - - if (impl->sort_model) - g_object_unref (impl->sort_model); -+ -+ search_stop_searching (impl); -+ search_clear_model (impl, FALSE); - - g_free (impl->preview_display_name); - -@@ -1081,6 +1129,14 @@ set_preview_widget (GtkFileChooserDefaul - 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 - { -@@ -1151,19 +1207,20 @@ shortcuts_reload_icons (GtkFileChooserDe - 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; - -@@ -1178,46 +1235,53 @@ shortcuts_reload_icons (GtkFileChooserDe - 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); - } - } - } -@@ -1349,7 +1413,6 @@ get_file_info_finished (GtkFileSystemHan - { - gint pos = -1; - gboolean cancelled = handle->cancelled; -- gboolean is_volume = FALSE; - GdkPixbuf *pixbuf; - GtkTreePath *path; - GtkTreeIter iter; -@@ -1414,12 +1477,15 @@ get_file_info_finished (GtkFileSystemHan - 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) -@@ -1431,7 +1497,7 @@ get_file_info_finished (GtkFileSystemHan - 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); -@@ -1505,7 +1571,7 @@ _gtk_file_chooser_label_for_uri (const g - static void - shortcuts_insert_path (GtkFileChooserDefault *impl, - int pos, -- gboolean is_volume, -+ ShortcutType shortcut_type, - GtkFileSystemVolume *volume, - const GtkFilePath *path, - const char *label, -@@ -1518,79 +1584,89 @@ shortcuts_insert_path (GtkFileChooserDef - 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) -@@ -1605,13 +1681,16 @@ shortcuts_insert_path (GtkFileChooserDef - 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) - { -@@ -1623,7 +1702,8 @@ shortcuts_insert_path (GtkFileChooserDef - 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); -@@ -1637,6 +1717,30 @@ shortcuts_insert_path (GtkFileChooserDef - 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) -@@ -1655,7 +1759,8 @@ shortcuts_append_home (GtkFileChooserDef - - 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); - -@@ -1688,7 +1793,9 @@ shortcuts_append_desktop (GtkFileChooser - 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. - */ -@@ -1727,7 +1834,7 @@ shortcuts_append_paths (GtkFileChooserDe - 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); -@@ -1746,6 +1853,16 @@ shortcuts_get_index (GtkFileChooserDefau - 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; -@@ -1843,15 +1960,18 @@ shortcuts_add_volumes (GtkFileChooserDef - } - } - -- 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; - -@@ -1861,11 +1981,13 @@ shortcuts_add_volumes (GtkFileChooserDef - /* 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)); -@@ -1874,6 +1996,7 @@ shortcuts_insert_separator (GtkFileChoos - SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE, - SHORTCUTS_COL_NAME, NULL, - SHORTCUTS_COL_DATA, NULL, -+ SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR, - -1); - } - -@@ -1886,7 +2009,7 @@ shortcuts_add_bookmarks (GtkFileChooserD - GtkTreeIter iter; - GtkFilePath *list_selected = NULL; - GtkFilePath *combo_selected = NULL; -- gboolean is_volume; -+ ShortcutType shortcut_type; - gpointer col_data; - - profile_start ("start", NULL); -@@ -1899,10 +2022,10 @@ shortcuts_add_bookmarks (GtkFileChooserD - 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); - } - -@@ -1910,13 +2033,18 @@ shortcuts_add_bookmarks (GtkFileChooserD - 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); - } - -@@ -1934,8 +2062,11 @@ shortcuts_add_bookmarks (GtkFileChooserD - 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) - { -@@ -1949,8 +2080,8 @@ shortcuts_add_bookmarks (GtkFileChooserD - - 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); - } - -@@ -1995,11 +2126,11 @@ shortcuts_add_current_folder (GtkFileCho - 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); - } -@@ -2008,7 +2139,8 @@ shortcuts_add_current_folder (GtkFileCho - 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 */ -@@ -2030,9 +2162,9 @@ shortcuts_update_current_folder (GtkFile - - /* 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; -@@ -2059,11 +2191,17 @@ shortcuts_model_create (GtkFileChooserDe - 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); -@@ -2071,12 +2209,12 @@ shortcuts_model_create (GtkFileChooserDe - 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); - } -@@ -2294,16 +2432,16 @@ shortcut_find_position (GtkFileChooserDe - 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; -@@ -2319,7 +2457,7 @@ shortcut_find_position (GtkFileChooserDe - if (exists) - return i; - } -- else -+ else if (shortcut_type == SHORTCUT_TYPE_PATH) - { - GtkFilePath *model_path; - -@@ -2425,7 +2563,7 @@ shortcuts_get_selected (GtkFileChooserDe - 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; -@@ -2448,11 +2586,12 @@ remove_selected_bookmarks (GtkFileChoose - 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; -@@ -2609,6 +2748,16 @@ bookmarks_check_add_sensitivity (GtkFile - 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) -@@ -3122,7 +3271,7 @@ shortcuts_reorder (GtkFileChooserDefault - { - GtkTreeIter iter; - gpointer col_data; -- gboolean is_volume; -+ ShortcutType shortcut_type; - GtkTreePath *path; - int old_position; - int bookmarks_index; -@@ -3147,10 +3296,10 @@ shortcuts_reorder (GtkFileChooserDefault - 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 */ -@@ -3234,17 +3383,11 @@ shortcuts_row_separator_func (GtkTreeMod - 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 -@@ -3489,7 +3632,7 @@ shortcuts_list_create (GtkFileChooserDef - /* 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, -@@ -3562,8 +3705,7 @@ shortcuts_list_create (GtkFileChooserDef - - 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); - -@@ -3934,6 +4076,8 @@ file_list_update_popup_menu (GtkFileChoo - { - 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() - */ -@@ -4033,6 +4177,28 @@ list_button_press_event_cb (GtkWidget - 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) -@@ -4046,7 +4212,7 @@ create_file_list (GtkFileChooserDefault - - 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); - -@@ -4136,6 +4302,7 @@ create_file_list (GtkFileChooserDefault - 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 (); -@@ -4146,8 +4313,11 @@ create_file_list (GtkFileChooserDefault - 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; -@@ -4255,9 +4425,59 @@ save_folder_combo_changed_cb (GtkComboBo - 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) -@@ -4265,8 +4485,14 @@ save_folder_combo_create (GtkFileChooser - 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); -@@ -4288,8 +4514,7 @@ save_folder_combo_create (GtkFileChooser - - 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 +4847,7 @@ browse_widgets_create (GtkFileChooserDef - 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); -@@ -5206,6 +5432,9 @@ gtk_file_chooser_default_dispose (GObjec - 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); -@@ -5483,31 +5712,32 @@ gtk_file_chooser_default_map (GtkWidget - - 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); - -@@ -5978,12 +6208,6 @@ pending_select_paths_process (GtkFileCho - * 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); -@@ -6030,17 +6254,11 @@ browse_files_model_finished_loading_cb ( - 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); -@@ -6052,6 +6270,20 @@ set_list_model (GtkFileChooserDefault *i - 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); -@@ -6126,6 +6358,9 @@ update_chooser_entry (GtkFileChooserDefa - 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 -@@ -6147,35 +6382,40 @@ update_chooser_entry (GtkFileChooserDefa - } - 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 - { -@@ -6382,6 +6622,8 @@ gtk_file_chooser_default_update_current_ - - profile_start ("start", (char *) path); - -+ search_switch_to_browse_mode (impl); -+ - g_assert (path != NULL); - - if (impl->local_only && -@@ -6423,6 +6665,9 @@ gtk_file_chooser_default_get_current_fol - { - 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; -@@ -6483,9 +6728,9 @@ gtk_file_chooser_default_select_path (Gt - 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 - { -@@ -6581,6 +6826,16 @@ static void - 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); -@@ -6720,6 +6975,10 @@ gtk_file_chooser_default_get_paths (GtkF - 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; -@@ -6731,12 +6990,14 @@ gtk_file_chooser_default_get_paths (GtkF - 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); - -@@ -6771,8 +7032,12 @@ gtk_file_chooser_default_get_paths (GtkF - 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; -@@ -6941,7 +7206,7 @@ add_shortcut_get_info_cb (GtkFileSystemH - - 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); -@@ -7055,15 +7320,15 @@ gtk_file_chooser_default_remove_shortcut - 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) -@@ -7112,15 +7377,15 @@ gtk_file_chooser_default_list_shortcut_f - 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)); -@@ -7455,6 +7720,23 @@ should_respond_after_confirm_overwrite ( - } - } - -+/* 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, -@@ -7720,6 +8002,9 @@ gtk_file_chooser_default_should_respond - - 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) -@@ -7856,7 +8141,7 @@ gtk_file_chooser_default_should_respond - { - 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; -@@ -7870,6 +8155,11 @@ gtk_file_chooser_default_should_respond - */ - 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 -@@ -7919,15 +8209,453 @@ gtk_file_chooser_default_initial_focus ( - 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); -+ _gtk_search_engine_set_query (impl->search_engine, impl->search_query); -+ -+ 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) -@@ -7967,28 +8695,44 @@ check_preview_change (GtkFileChooserDefa - { - 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 && -@@ -8004,7 +8748,7 @@ check_preview_change (GtkFileChooserDefa - 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 - { -@@ -8073,6 +8817,8 @@ shortcuts_activate_volume (GtkFileChoose - { - 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. - */ -@@ -8093,6 +8839,8 @@ shortcuts_activate_volume (GtkFileChoose - if (path != NULL) - { - change_folder_and_display_error (impl, path); -+ focus_browse_tree_view_if_possible (impl); -+ - gtk_file_path_free (path); - } - } -@@ -8125,7 +8873,10 @@ shortcuts_activate_get_info_cb (GtkFileS - 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); - -@@ -8142,26 +8893,25 @@ shortcuts_activate_iter (GtkFileChooserD - 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; - -@@ -8169,7 +8919,7 @@ shortcuts_activate_iter (GtkFileChooserD - - shortcuts_activate_volume (impl, volume); - } -- else -+ else if (shortcut_type == SHORTCUT_TYPE_PATH) - { - struct ShortcutsActivateData *data; - -@@ -8182,6 +8932,10 @@ shortcuts_activate_iter (GtkFileChooserD - 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 */ -@@ -8194,15 +8948,13 @@ shortcuts_row_activated_cb (GtkTreeView - 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 */ -@@ -8242,8 +8994,15 @@ shortcuts_select_func (GtkTreeSelection - 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 -@@ -8255,6 +9014,9 @@ list_select_func (GtkTreeSelection *se - { - 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) - { -@@ -8280,7 +9042,7 @@ list_selection_changed (GtkTreeSelection - 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; -@@ -8295,6 +9057,8 @@ list_selection_changed (GtkTreeSelection - - out: - -+ /* AQUI: cambiar las siguientes funciones para que acepten SEARCH */ -+ - update_chooser_entry (impl); - check_preview_change (impl); - bookmarks_check_add_sensitivity (impl); -@@ -8312,6 +9076,12 @@ list_row_activated (GtkTreeView - 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; - -@@ -8382,6 +9152,15 @@ list_icon_data_func (GtkTreeViewColumn * - 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); -@@ -8431,13 +9210,33 @@ list_name_data_func (GtkTreeViewColumn * - 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; -@@ -8453,6 +9252,7 @@ list_name_data_func (GtkTreeViewColumn * - g_object_set (cell, - "text", gtk_file_info_get_display_name (info), - "sensitive", sensitive, -+ "ellipsize", PANGO_ELLIPSIZE_END, - NULL); - } - -@@ -8514,7 +9314,6 @@ list_mtime_data_func (GtkTreeViewColumn - gpointer data) - { - GtkFileChooserDefault *impl; -- const GtkFileInfo *info; - GtkFileTime time_mtime; - GDate mtime, now; - int days_diff; -@@ -8523,17 +9322,35 @@ list_mtime_data_func (GtkTreeViewColumn - - 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")); -@@ -8564,10 +9381,6 @@ list_mtime_data_func (GtkTreeViewColumn - } - } - -- 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, -@@ -8593,7 +9406,23 @@ location_set_user_text (GtkFileChooserDe - 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) - { -@@ -8662,6 +9491,7 @@ switch_to_shortcut (GtkFileChooserDefaul - g_assert_not_reached (); - - shortcuts_activate_iter (impl, &iter); -+ focus_browse_tree_view_if_possible (impl); - } - - /* Handler for the "home-folder" keybinding signal */ -@@ -8706,26 +9536,26 @@ quick_bookmark_handler (GtkFileChooserDe - /* 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); -@@ -8735,13 +9565,13 @@ shortcuts_model_filter_row_draggable (Gt - - /* 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 */ - -@@ -8750,30 +9580,30 @@ shortcuts_model_filter_drag_data_get (Gt - - /* 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.11/gtk/gtkfilechooserprivate.h.search 2007-03-14 00:07:03.000000000 -0400 -+++ gtk+-2.10.11/gtk/gtkfilechooserprivate.h 2007-05-15 01:04:07.000000000 -0400 -@@ -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 @@ typedef enum { - LOCATION_MODE_FILENAME_ENTRY - } LocationMode; - -+typedef enum { -+ OPERATION_MODE_BROWSE, -+ OPERATION_MODE_SEARCH -+} OperationMode; -+ - struct _GtkFileChooserDefault - { - GtkVBox parent_instance; -@@ -175,11 +182,19 @@ struct _GtkFileChooserDefault - 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 @@ struct _GtkFileChooserDefault - 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 @@ struct _GtkFileChooserDefault - ReloadState reload_state; - guint load_timeout_id; - -+ OperationMode operation_mode; -+ - GSList *pending_select_paths; - - GtkFileFilter *current_filter; -@@ -222,9 +248,6 @@ struct _GtkFileChooserDefault - - GtkTooltips *tooltips; - -- gboolean has_home; -- gboolean has_desktop; -- - int num_volumes; - int num_shortcuts; - int num_bookmarks; -@@ -239,6 +262,7 @@ struct _GtkFileChooserDefault - - GtkTreeViewColumn *list_name_column; - GtkCellRenderer *list_name_renderer; -+ GtkTreeViewColumn *list_mtime_column; - - GSource *edited_idle; - char *edited_new_text; -@@ -265,6 +289,9 @@ struct _GtkFileChooserDefault - 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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchenginesimple.h 2007-05-15 01:04:07.000000000 -0400 -@@ -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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchenginesimple.c 2007-05-15 01:04:50.000000000 -0400 -@@ -0,0 +1,374 @@ -+/* -+ * 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_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, *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; -+ -+ 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 (fpath, (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 (fpath, 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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtksearchenginebeagle.c 2007-05-15 01:04:07.000000000 -0400 -@@ -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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtkquery.c 2007-05-15 01:04:07.000000000 -0400 -@@ -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-05-14 19:53:51.012713345 -0400 -+++ gtk+-2.10.11/gtk/gtkquery.h 2007-05-15 01:04:07.000000000 -0400 -@@ -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 dfaccaf..406b958 100644 --- a/gtk2.spec +++ b/gtk2.spec @@ -10,16 +10,16 @@ %define cairo_version %{cairo_base_version}-1 %define libpng_version 2:1.2.2-16 -%define base_version 2.10.11 +%define base_version 2.10.12 %define bin_version 2.10.0 Summary: The GIMP ToolKit (GTK+), a library for creating GUIs for X Name: gtk2 Version: %{base_version} -Release: 6%{?dist} +Release: 1%{?dist} License: LGPL Group: System Environment/Libraries -Source: http://ftp.gnome.org/pub/gnome/sources/gtk+/2.10/gtk+-%{version}.tar.bz2 +Source: http://download.gnome.org/sources/gtk+/2.10/gtk+-%{version}.tar.bz2 Source1: update-gdk-pixbuf-loaders Source2: update-gtk-immodules @@ -28,15 +28,13 @@ 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.8-search.patch +Patch2: gtk+-2.10.12-search.patch # use fam for recent-files Patch3: gtk+-2.10.3-fam.patch # backport from HEAD Patch7: gtk+-2.10.7-cursor-blink.patch # fixed in upstream cvs Patch10: gtk+-2.10.4-im-reset.patch -# fixed in upstream cvs -Patch11: raw-printers.patch Patch12: gtk+-2.10.11-user-dirs.patch BuildRequires: atk-devel >= %{atk_version} @@ -123,7 +121,6 @@ docs for the GTK+ widget toolkit. %patch3 -p1 -b .fam %patch7 -p1 -b .cursor-blink %patch10 -p1 -b .im-reset -%patch11 -p1 -b .raw-printers %patch12 -p1 -b .user-dirs for i in config.guess config.sub ; do @@ -291,6 +288,10 @@ rm -rf $RPM_BUILD_ROOT %doc tmpdocs/examples %changelog +* Sat May 19 2007 Matthias Clasen - 1.10.12-1 +- Update to 2.10.12 +- Drop upstreamed patches + * Tue May 15 2007 Matthias Clasen - 1.10.11-6 - Backport some fixes for the ftw()-based search engine diff --git a/raw-printers.patch b/raw-printers.patch deleted file mode 100644 index a1a476a..0000000 --- a/raw-printers.patch +++ /dev/null @@ -1,353 +0,0 @@ ---- gtk+-2.10.8/modules/printbackends/cups/gtkcupsutils.h.raw-printers 2007-01-16 22:15:15.000000000 -0500 -+++ gtk+-2.10.8/modules/printbackends/cups/gtkcupsutils.h 2007-03-28 12:22:59.000000000 -0400 -@@ -33,6 +33,14 @@ - - typedef enum - { -+ GTK_CUPS_ERROR_HTTP, -+ GTK_CUPS_ERROR_IPP, -+ GTK_CUPS_ERROR_IO, -+ GTK_CUPS_ERROR_GENERAL -+} GtkCupsErrorType; -+ -+typedef enum -+{ - GTK_CUPS_POST, - GTK_CUPS_GET - } GtkCupsRequestType; -@@ -126,6 +134,9 @@ - const gchar *value); - gboolean gtk_cups_result_is_error (GtkCupsResult *result); - ipp_t * gtk_cups_result_get_response (GtkCupsResult *result); -+GtkCupsErrorType gtk_cups_result_get_error_type (GtkCupsResult *result); -+int gtk_cups_result_get_error_status (GtkCupsResult *result); -+int gtk_cups_result_get_error_code (GtkCupsResult *result); - const char * gtk_cups_result_get_error_string (GtkCupsResult *result); - - G_END_DECLS ---- gtk+-2.10.8/modules/printbackends/cups/gtkprintbackendcups.c.raw-printers 2007-01-16 22:15:15.000000000 -0500 -+++ gtk+-2.10.8/modules/printbackends/cups/gtkprintbackendcups.c 2007-03-28 12:22:59.000000000 -0400 -@@ -572,10 +572,17 @@ - g_print ("CUPS Backend: %s \n", G_STRFUNC, source)); - - if (gtk_cups_result_is_error (result)) -- g_warning ("Error result: %s", gtk_cups_result_get_error_string (result)); -+ { -+ GTK_NOTE (PRINTING, -+ g_print("Error result: %s (type %i, status %i, code %i)\n", -+ gtk_cups_result_get_error_string (result), -+ gtk_cups_result_get_error_type (result), -+ gtk_cups_result_get_error_status (result), -+ gtk_cups_result_get_error_code (result))); -+ } - - ep_callback (GTK_PRINT_BACKEND (dispatch->backend), result, user_data); -- -+ - return FALSE; - } - -@@ -1223,7 +1230,18 @@ - - if (gtk_cups_result_is_error (result)) - { -- g_signal_emit_by_name (printer, "details-acquired", FALSE); -+ gboolean success = FALSE; -+ -+ /* if we get a 404 then it is just a raw printer without a ppd -+ and not an error */ -+ if ((gtk_cups_result_get_error_type (result) == GTK_CUPS_ERROR_HTTP) && -+ (gtk_cups_result_get_error_status (result) == HTTP_NOT_FOUND)) -+ { -+ gtk_printer_set_has_details (printer, TRUE); -+ success = TRUE; -+ } -+ -+ g_signal_emit_by_name (printer, "details-acquired", success); - return; - } - ---- gtk+-2.10.8/modules/printbackends/cups/gtkcupsutils.c.raw-printers 2007-01-16 22:15:15.000000000 -0500 -+++ gtk+-2.10.8/modules/printbackends/cups/gtkcupsutils.c 2007-03-28 12:23:37.000000000 -0400 -@@ -51,6 +51,11 @@ - { - gchar *error_msg; - ipp_t *ipp_response; -+ GtkCupsErrorType error_type; -+ -+ /* some error types like HTTP_ERROR have a status and a code */ -+ int error_status; -+ int error_code; - - guint is_error : 1; - guint is_ipp_response : 1; -@@ -73,15 +78,20 @@ - _get_read_data}; - - static void --gtk_cups_result_set_error (GtkCupsResult *result, -- const char *error_msg, -+gtk_cups_result_set_error (GtkCupsResult *result, -+ GtkCupsErrorType error_type, -+ int error_status, -+ int error_code, -+ const char *error_msg, - ...) - { - va_list args; - - result->is_ipp_response = FALSE; -- - result->is_error = TRUE; -+ result->error_type = error_type; -+ result->error_status = error_status; -+ result->error_code = error_code; - - va_start (args, error_msg); - result->error_msg = g_strdup_vprintf (error_msg, args); -@@ -206,7 +216,13 @@ - if (request->attempts > _GTK_CUPS_MAX_ATTEMPTS && - request->state != GTK_CUPS_REQUEST_DONE) - { -- gtk_cups_result_set_error (request->result, "Too many failed attempts"); -+ /* TODO: should add a status or error code for too many failed attempts */ -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_GENERAL, -+ 0, -+ 0, -+ "Too many failed attempts"); -+ - request->state = GTK_CUPS_REQUEST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - } -@@ -622,7 +638,12 @@ - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -- gtk_cups_result_set_error (request->result, "Failed Post"); -+ /* TODO: should add a status or error code for failed post */ -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_GENERAL, -+ 0, -+ 0, -+ "Failed Post"); - } - - request->attempts++; -@@ -649,10 +670,16 @@ - - if (ipp_status == IPP_ERROR) - { -+ int cups_error = cupsLastError (); - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -- gtk_cups_result_set_error (request->result, "%s",ippErrorString (cupsLastError ())); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_IPP, -+ ipp_status, -+ cups_error, -+ "%s", -+ ippErrorString (cups_error)); - return; - } - -@@ -708,7 +735,12 @@ - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -- gtk_cups_result_set_error (request->result, "Error reading from cache file: %s", error->message); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_IO, -+ io_status, -+ error->code, -+ "Error reading from cache file: %s", -+ error->message); - - g_error_free (error); - return; -@@ -729,10 +761,19 @@ - if (httpWrite(request->http, buffer, (int) bytes) < bytes) - #endif /* HAVE_CUPS_API_1_2 */ - { -+ int http_errno; -+ -+ http_errno = httpError (request->http); -+ - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -- gtk_cups_result_set_error (request->result, "Error writting to socket in Post %s", strerror (httpError (request->http))); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_HTTP, -+ http_status, -+ http_errno, -+ "Error writing to socket in Post %s", -+ g_strerror (http_errno)); - return; - } - } -@@ -765,7 +806,12 @@ - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -- gtk_cups_result_set_error (request->result, "Can't prompt for authorization"); -+ /* TODO: create a not implemented error code */ -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_GENERAL, -+ 0, -+ 0, -+ "Can't prompt for authorization"); - return; - } - else if (http_status == HTTP_ERROR) -@@ -785,7 +831,12 @@ - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -- gtk_cups_result_set_error (request->result, "Unknown HTTP error"); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_HTTP, -+ http_status, -+ error, -+ "Unknown HTTP error"); -+ - return; - } - } -@@ -817,7 +868,12 @@ - else - { - request->state = GTK_CUPS_POST_DONE; -- gtk_cups_result_set_error (request->result, "HTTP Error in POST %s", strerror (http_errno)); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_HTTP, -+ http_status, -+ http_errno, -+ "HTTP Error in POST %s", -+ g_strerror (http_errno)); - request->poll_state = GTK_CUPS_HTTP_IDLE; - - httpFlush(request->http); -@@ -866,7 +922,13 @@ - - if (ipp_status == IPP_ERROR) - { -- gtk_cups_result_set_error (request->result, "%s", ippErrorString (cupsLastError())); -+ int ipp_error = cupsLastError (); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_IPP, -+ ipp_status, -+ ipp_error, -+ "%s", -+ ippErrorString (ipp_error)); - - ippDelete (request->result->ipp_response); - request->result->ipp_response = NULL; -@@ -891,7 +953,12 @@ - - if (request->data_io == NULL) - { -- gtk_cups_result_set_error (request->result, "Get requires an open io channel"); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_IO, -+ G_IO_STATUS_ERROR, -+ G_IO_CHANNEL_ERROR_FAILED, -+ "Get requires an open io channel"); -+ - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -@@ -909,8 +976,13 @@ - { - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; -- -- gtk_cups_result_set_error (request->result, "Failed Get"); -+ -+ /* TODO: should add a status or error code for failed GET */ -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_GENERAL, -+ 0, -+ 0, -+ "Failed Get"); - } - - request->attempts++; -@@ -947,8 +1019,13 @@ - g_warning ("NOT IMPLEMENTED: We need to prompt for authorization in a non blocking manner"); - request->state = GTK_CUPS_GET_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; -- -- gtk_cups_result_set_error (request->result, "Can't prompt for authorization"); -+ -+ /* TODO: should add a status or error code for not implemented */ -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_GENERAL, -+ 0, -+ 0, -+ "Can't prompt for authorization"); - return; - } - /* TODO: detect ssl in configure.ac */ -@@ -967,7 +1044,7 @@ - request->attempts++; - goto again; - } --#endif -+#endif - else if (http_status != HTTP_OK) - { - int http_errno; -@@ -979,7 +1056,12 @@ - else - { - request->state = GTK_CUPS_GET_DONE; -- gtk_cups_result_set_error (request->result, "HTTP Error in GET %s", strerror (http_errno)); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_HTTP, -+ http_status, -+ http_errno, -+ "HTTP Error in GET %s", -+ g_strerror (http_errno)); - request->poll_state = GTK_CUPS_HTTP_IDLE; - httpFlush(request->http); - -@@ -1055,7 +1137,11 @@ - request->state = GTK_CUPS_POST_DONE; - request->poll_state = GTK_CUPS_HTTP_IDLE; - -- gtk_cups_result_set_error (request->result, error->message); -+ gtk_cups_result_set_error (request->result, -+ GTK_CUPS_ERROR_IO, -+ io_status, -+ error->code, -+ error->message); - g_error_free (error); - } - } -@@ -1078,6 +1164,24 @@ - return result->ipp_response; - } - -+GtkCupsErrorType -+gtk_cups_result_get_error_type (GtkCupsResult *result) -+{ -+ return result->error_type; -+} -+ -+int -+gtk_cups_result_get_error_status (GtkCupsResult *result) -+{ -+ return result->error_status; -+} -+ -+int -+gtk_cups_result_get_error_code (GtkCupsResult *result) -+{ -+ return result->error_code; -+} -+ - const char * - gtk_cups_result_get_error_string (GtkCupsResult *result) - { diff --git a/sources b/sources index 77fb646..13e18d6 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -3b32eab43bf5195d981867d25ba55d66 gtk+-2.10.11.tar.bz2 +cf969c62134c662ff07e64613ed6c11f gtk+-2.10.12.tar.bz2