diff --git a/data/org.gnome.nautilus.gschema.xml b/data/org.gnome.nautilus.gschema.xml
index 94b17208d..438008f15 100644
--- a/data/org.gnome.nautilus.gschema.xml
+++ b/data/org.gnome.nautilus.gschema.xml
@@ -77,6 +77,11 @@
Always use the location entry, instead of the pathbar
If set to true, then Nautilus browser windows will always use a textual input entry for the location toolbar, instead of the pathbar.
+
+ true
+ Start searching on type ahead
+ If set to true, typing on the files viewer will start searching. Otherwise it select first matching file.
+
'local-only'
Where to perform recursive search
diff --git a/src/nautilus-global-preferences.h b/src/nautilus-global-preferences.h
index 9d39bbc03..27c2e54f1 100644
--- a/src/nautilus-global-preferences.h
+++ b/src/nautilus-global-preferences.h
@@ -116,6 +116,7 @@ typedef enum
/* Search behaviour */
#define NAUTILUS_PREFERENCES_RECURSIVE_SEARCH "recursive-search"
+#define NAUTILUS_PREFERENCES_TYPE_AHEAD_SEARCH "type-ahead-search"
/* Context menu options */
#define NAUTILUS_PREFERENCES_SHOW_DELETE_PERMANENTLY "show-delete-permanently"
diff --git a/src/nautilus-preferences-window.c b/src/nautilus-preferences-window.c
index a1cd95823..b5e167120 100644
--- a/src/nautilus-preferences-window.c
+++ b/src/nautilus-preferences-window.c
@@ -41,6 +41,8 @@
"show_create_link_switch"
#define NAUTILUS_PREFERENCES_DIALOG_LIST_VIEW_USE_TREE_WIDGET \
"use_tree_view_switch"
+#define NAUTILUS_PREFERENCES_DIALOG_TYPE_AHEAD_WIDGET \
+ "type_ahead_search"
/* combo preferences */
#define NAUTILUS_PREFERENCES_DIALOG_OPEN_ACTION_COMBO \
@@ -334,6 +336,9 @@ nautilus_preferences_window_setup (GtkBuilder *builder)
bind_builder_bool (builder, nautilus_preferences,
NAUTILUS_PREFERENCES_DIALOG_DELETE_PERMANENTLY_WIDGET,
NAUTILUS_PREFERENCES_SHOW_DELETE_PERMANENTLY);
+ bind_builder_bool (builder, nautilus_preferences,
+ NAUTILUS_PREFERENCES_DIALOG_TYPE_AHEAD_WIDGET,
+ NAUTILUS_PREFERENCES_TYPE_AHEAD_SEARCH);
bind_builder_combo_row (builder, nautilus_preferences,
NAUTILUS_PREFERENCES_DIALOG_OPEN_ACTION_COMBO,
diff --git a/src/nautilus-query-editor.c b/src/nautilus-query-editor.c
index 95b284b7d..4ca27b001 100644
--- a/src/nautilus-query-editor.c
+++ b/src/nautilus-query-editor.c
@@ -742,6 +742,12 @@ nautilus_query_editor_set_query (NautilusQueryEditor *self,
g_return_if_fail (NAUTILUS_IS_QUERY_EDITOR (self));
+ /* Setting query to NULL causes reentry to set it to an empty query */
+ if (self->change_frozen) {
+ g_set_object (&self->query, query);
+ return;
+ }
+
if (query != NULL)
{
text = nautilus_query_get_text (query);
diff --git a/src/nautilus-window-slot.c b/src/nautilus-window-slot.c
index ac367db12..567df1c88 100644
--- a/src/nautilus-window-slot.c
+++ b/src/nautilus-window-slot.c
@@ -67,6 +67,9 @@ enum
#define FILE_SHARING_SCHEMA_ID "org.gnome.desktop.file-sharing"
+/* In type ahead mode, clear entry if it did not change for a while */
+#define CLEAR_QUERY_EDITOR_TIMEOUT 1000
+
struct _NautilusWindowSlot
{
GtkBox parent_instance;
@@ -105,10 +108,7 @@ struct _NautilusWindowSlot
/* Query editor */
NautilusQueryEditor *query_editor;
NautilusQuery *pending_search_query;
- gulong qe_changed_id;
- gulong qe_cancel_id;
- gulong qe_activated_id;
- gulong qe_focus_view_id;
+ guint clear_query_editor_timeout_id;
GtkLabel *search_info_label;
GtkRevealer *search_info_label_revealer;
@@ -176,6 +176,7 @@ static void real_set_templates_menu (NautilusWindowSlot *self,
GMenuModel *menu);
static GMenuModel *real_get_templates_menu (NautilusWindowSlot *self);
static void nautilus_window_slot_setup_extra_location_widgets (NautilusWindowSlot *self);
+static GFile *nautilus_window_slot_get_current_location (NautilusWindowSlot *self);
void
free_navigation_state (gpointer data)
@@ -426,6 +427,37 @@ query_editor_focus_view_callback (NautilusQueryEditor *editor,
}
}
+static gboolean
+type_ahead_search (void)
+{
+ return g_settings_get_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_TYPE_AHEAD_SEARCH);
+}
+
+static gboolean
+clear_query_editor_timeout_callback (NautilusWindowSlot *self)
+{
+ nautilus_query_editor_set_query (self->query_editor, NULL);
+ self->clear_query_editor_timeout_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+typedef struct {
+ GQuark attribute;
+ gboolean directories_first;
+ gboolean reversed;
+} FileCompareForTypeAheadContext;
+
+static int
+file_compare_for_type_ahead (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ FileCompareForTypeAheadContext *ctx = user_data;
+ return nautilus_file_compare_for_sort_by_attribute_q (NAUTILUS_FILE(a),
+ NAUTILUS_FILE(b),
+ ctx->attribute,
+ ctx->directories_first,
+ ctx->reversed);
+}
+
static void
query_editor_changed_callback (NautilusQueryEditor *editor,
NautilusQuery *query,
@@ -436,8 +468,61 @@ query_editor_changed_callback (NautilusQueryEditor *editor,
view = nautilus_window_slot_get_current_view (self);
- nautilus_view_set_search_query (view, query);
- nautilus_window_slot_set_location (self, nautilus_view_get_location (view));
+ if (nautilus_window_slot_get_search_visible (self))
+ {
+ nautilus_view_set_search_query (view, query);
+ nautilus_window_slot_set_location (self, nautilus_view_get_location (view));
+ }
+ else
+ {
+ /* Find all files with a display name that starts with the query, case insensitive. */
+ GFile *location = nautilus_window_slot_get_current_location (self);
+ g_autoptr (NautilusDirectory) directory = nautilus_directory_get (location);
+ const gchar *text = nautilus_query_get_text (query);
+ g_autofree gchar *text_casefold = g_utf8_casefold (text, -1);
+ g_autofree gchar *text_collate = g_utf8_collate_key_for_filename (text_casefold, -1);
+ gsize text_len = strlen (text);
+ g_autolist (NautilusFile) files = nautilus_directory_get_file_list (directory);
+ g_autolist (NautilusFile) matches = NULL;
+ GList *l;
+
+ for (l = files; l; l = l->next)
+ {
+ NautilusFile *file = NAUTILUS_FILE (l->data);
+ g_autofree const gchar *name = nautilus_file_get_display_name (file);
+ g_autofree const gchar *name_casefold = g_utf8_casefold (name, text_len);
+ g_autofree const gchar *name_collate = g_utf8_collate_key_for_filename (name_casefold, -1);
+
+ if (g_str_equal (name_collate, text_collate))
+ {
+ matches = g_list_prepend (matches, nautilus_file_ref (file));
+ }
+ }
+
+ /* Select the first match */
+ {
+ FileCompareForTypeAheadContext ctx;
+ GActionGroup *action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (view));
+ g_autoptr (GVariant) value = g_action_group_get_action_state (action_group, "sort");
+ const gchar *attribute_name;
+ g_autolist (NautilusFile) selection;
+
+ g_variant_get (value, "(&sb)", &attribute_name, &ctx.reversed);
+ ctx.attribute = g_quark_from_string (attribute_name);
+ ctx.directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (view));
+ matches = g_list_sort_with_data (matches, file_compare_for_type_ahead, &ctx);
+
+ selection = matches;
+ matches = g_list_remove_link (matches, selection);
+ nautilus_view_set_selection (self->content_view, selection);
+ }
+
+ /* Reset timeout that clears type ahead query */
+ g_clear_handle_id (&self->clear_query_editor_timeout_id, g_source_remove);
+ self->clear_query_editor_timeout_id = g_timeout_add (CLEAR_QUERY_EDITOR_TIMEOUT,
+ G_SOURCE_FUNC (clear_query_editor_timeout_callback),
+ self);
+ }
}
static void
@@ -447,11 +532,6 @@ hide_query_editor (NautilusWindowSlot *self)
view = nautilus_window_slot_get_current_view (self);
- g_clear_signal_handler (&self->qe_changed_id, self->query_editor);
- g_clear_signal_handler (&self->qe_cancel_id, self->query_editor);
- g_clear_signal_handler (&self->qe_activated_id, self->query_editor);
- g_clear_signal_handler (&self->qe_focus_view_id, self->query_editor);
-
nautilus_query_editor_set_query (self->query_editor, NULL);
if (nautilus_view_is_searching (view))
@@ -515,31 +595,6 @@ show_query_editor (NautilusWindowSlot *self)
}
gtk_widget_grab_focus (GTK_WIDGET (self->query_editor));
-
- if (self->qe_changed_id == 0)
- {
- self->qe_changed_id =
- g_signal_connect (self->query_editor, "changed",
- G_CALLBACK (query_editor_changed_callback), self);
- }
- if (self->qe_cancel_id == 0)
- {
- self->qe_cancel_id =
- g_signal_connect (self->query_editor, "cancel",
- G_CALLBACK (query_editor_cancel_callback), self);
- }
- if (self->qe_activated_id == 0)
- {
- self->qe_activated_id =
- g_signal_connect (self->query_editor, "activated",
- G_CALLBACK (query_editor_activated_callback), self);
- }
- if (self->qe_focus_view_id == 0)
- {
- self->qe_focus_view_id =
- g_signal_connect (self->query_editor, "focus-view",
- G_CALLBACK (query_editor_focus_view_callback), self);
- }
}
static void
@@ -634,7 +689,7 @@ nautilus_window_slot_handle_event (NautilusWindowSlot *self,
state);
}
- if (retval)
+ if (retval && type_ahead_search ())
{
nautilus_window_slot_set_search_visible (self, TRUE);
}
@@ -886,6 +941,15 @@ nautilus_window_slot_constructed (GObject *object)
gtk_box_append (GTK_BOX (self), extras_vbox);
self->query_editor = NAUTILUS_QUERY_EDITOR (nautilus_query_editor_new ());
+ g_signal_connect (self->query_editor, "changed",
+ G_CALLBACK (query_editor_changed_callback), self);
+ g_signal_connect (self->query_editor, "cancel",
+ G_CALLBACK (query_editor_cancel_callback), self);
+ g_signal_connect (self->query_editor, "activated",
+ G_CALLBACK (query_editor_activated_callback), self);
+ g_signal_connect (self->query_editor, "focus-view",
+ G_CALLBACK (query_editor_focus_view_callback), self);
+
/* We want to keep alive the query editor betwen additions and removals on the
* UI, specifically when the toolbar adds or removes it */
g_object_ref_sink (self->query_editor);
@@ -1972,6 +2036,9 @@ setup_view (NautilusWindowSlot *self,
nautilus_window_slot_disconnect_content_view (self);
+ nautilus_query_editor_set_query (self->query_editor, NULL);
+ g_clear_handle_id (&self->clear_query_editor_timeout_id, g_source_remove);
+
self->new_content_view = view;
nautilus_window_slot_connect_new_content_view (self);
diff --git a/src/nautilus-window.c b/src/nautilus-window.c
index 9429500c0..d9f6003e2 100644
--- a/src/nautilus-window.c
+++ b/src/nautilus-window.c
@@ -1579,6 +1579,7 @@ const GActionEntry win_entries[] =
{ "forward", action_forward },
{ "back-n", action_back_n, "u" },
{ "forward-n", action_forward_n, "u" },
+ { "backspaceup", action_up },
{ "up", action_up },
{ "current-location-menu", action_show_current_location_menu },
{ "open-location", action_open_location, "s" },
@@ -1644,6 +1645,7 @@ nautilus_window_initialize_actions (NautilusWindow *window)
/* Only accesible by shorcuts */
nautilus_application_set_accelerators (app, "win.bookmark-current-location", ACCELS ("d", "AddFavorite"));
nautilus_application_set_accelerator (app, "win.up", "Up");
+ nautilus_application_set_accelerator (app, "win.backspaceup", "BackSpace");
nautilus_application_set_accelerators (app, "win.go-home", ACCELS ("Home", "HomePage", "Start"));
nautilus_application_set_accelerator (app, "win.go-starred", "Favorites");
nautilus_application_set_accelerator (app, "win.tab-move-left", "Page_Up");
diff --git a/src/resources/ui/nautilus-preferences-window.ui b/src/resources/ui/nautilus-preferences-window.ui
index cff1c278e..8ad928908 100644
--- a/src/resources/ui/nautilus-preferences-window.ui
+++ b/src/resources/ui/nautilus-preferences-window.ui
@@ -49,6 +49,21 @@
True
+
+
+