Typeahead Find for Nautilus 3.22
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
data/org.gnome.nautilus.gschema.xml | 5 + | |
src/nautilus-global-preferences.h | 3 + | |
src/nautilus-list-view.c | 19 + | |
src/nautilus-list-view.h | 3 +- | |
src/nautilus-preferences-window.c | 5 + | |
src/nautilus-window-slot.c | 1011 ++++++++++++++++++++++- | |
src/resources/ui/nautilus-preferences-window.ui | 50 ++ | |
7 files changed, 1086 insertions(+), 10 deletions(-) | |
diff --git a/data/org.gnome.nautilus.gschema.xml b/data/org.gnome.nautilus.gschema.xml | |
index c05faf3..62b5e8c 100644 | |
--- a/data/org.gnome.nautilus.gschema.xml | |
+++ b/data/org.gnome.nautilus.gschema.xml | |
@@ -211,6 +211,11 @@ | |
<summary>Bulk rename utility</summary> | |
<description>If set, Nautilus will append URIs of selected files and treat the result as a command line for bulk renaming. Bulk rename applications can register themselves in this key by setting the key to a space-separated string of their executable name and any command line options. If the executable name is not set to a full path, it will be searched for in the search path.</description> | |
</key> | |
+ <key name="enable-interactive-search" type="b"> | |
+ <default>true</default> | |
+ <summary>Enable interactive (type-ahead) search</summary> | |
+ <description>If set to true, enables interactive search, similar to Nautilus 3.4.</description> | |
+ </key> | |
<key type="b" name="open-folder-on-dnd-hover"> | |
<default>true</default> | |
<summary>Whether to open the hovered folder after a timeout when drag and drop operation</summary> | |
diff --git a/src/nautilus-global-preferences.h b/src/nautilus-global-preferences.h | |
index bf1c896..4eaf370 100644 | |
--- a/src/nautilus-global-preferences.h | |
+++ b/src/nautilus-global-preferences.h | |
@@ -161,6 +161,9 @@ typedef enum | |
/* Recent files */ | |
#define NAUTILUS_PREFERENCES_RECENT_FILES_ENABLED "remember-recent-files" | |
+/* Interactive search (typeahead) */ | |
+#define NAUTILUS_PREFERENCES_ENABLE_INTERACTIVE_SEARCH "enable-interactive-search" | |
+ | |
/* Move to trash shorcut changed dialog */ | |
#define NAUTILUS_PREFERENCES_SHOW_MOVE_TO_TRASH_SHORTCUT_CHANGED_DIALOG "show-move-to-trash-shortcut-changed-dialog" | |
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c | |
index f4b1fb0..4162104 100644 | |
--- a/src/nautilus-list-view.c | |
+++ b/src/nautilus-list-view.c | |
@@ -2694,6 +2694,7 @@ nautilus_list_view_set_selection (NautilusFilesView *view, | |
GList *node; | |
GList *iters, *l; | |
NautilusFile *file; | |
+ GtkTreePath *path = NULL; | |
list_view = NAUTILUS_LIST_VIEW (view); | |
tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view); | |
@@ -2710,10 +2711,22 @@ nautilus_list_view_set_selection (NautilusFilesView *view, | |
{ | |
gtk_tree_selection_select_iter (tree_selection, | |
(GtkTreeIter *) l->data); | |
+ if (!path) | |
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), (GtkTreeIter *) l->data); | |
} | |
+ | |
g_list_free_full (iters, g_free); | |
} | |
+ if (path) { | |
+ gtk_tree_view_set_cursor_on_cell (list_view->details->tree_view, | |
+ path, | |
+ list_view->details->file_name_column, | |
+ GTK_CELL_RENDERER (list_view->details->file_name_cell), | |
+ TRUE); | |
+ gtk_tree_path_free (path); | |
+ } | |
+ | |
g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view); | |
nautilus_files_view_notify_selection_changed (view); | |
} | |
@@ -3602,3 +3615,9 @@ nautilus_list_view_new (NautilusWindowSlot *slot) | |
"window-slot", slot, | |
NULL); | |
} | |
+ | |
+GtkTreeView * | |
+nautilus_list_view_get_tree_view (NautilusListView *list_view) | |
+{ | |
+ return list_view->details->tree_view; | |
+} | |
diff --git a/src/nautilus-list-view.h b/src/nautilus-list-view.h | |
index f775431..bbcb565 100644 | |
--- a/src/nautilus-list-view.h | |
+++ b/src/nautilus-list-view.h | |
@@ -3,7 +3,7 @@ | |
Copyright (C) 2000 Eazel, Inc. | |
Copyright (C) 2001 Anders Carlsson <andersca@gnu.org> | |
- | |
+ | |
The Gnome Library is free software; you can redistribute it and/or | |
modify it under the terms of the GNU Library General Public License as | |
published by the Free Software Foundation; either version 2 of the | |
@@ -52,5 +52,6 @@ typedef struct { | |
GType nautilus_list_view_get_type (void); | |
NautilusFilesView * nautilus_list_view_new (NautilusWindowSlot *slot); | |
+GtkTreeView * nautilus_list_view_get_tree_view (NautilusListView *list_view); | |
#endif /* NAUTILUS_LIST_VIEW_H */ | |
diff --git a/src/nautilus-preferences-window.c b/src/nautilus-preferences-window.c | |
index 2df9e4a..5719bf3 100644 | |
--- a/src/nautilus-preferences-window.c | |
+++ b/src/nautilus-preferences-window.c | |
@@ -59,6 +59,8 @@ | |
"trash_confirm_checkbutton" | |
#define NAUTILUS_PREFERENCES_DIALOG_AUTOMATIC_DECOMPRESSION_WIDGET \ | |
"automatic_decompression_checkbutton" | |
+#define NAUTILUS_PREFERENCES_DIALOG_ENABLE_INTERACTIVE_SEARCH_WIDGET \ | |
+ "interactive_search_checkbutton" | |
/* int enums */ | |
#define NAUTILUS_PREFERENCES_DIALOG_THUMBNAIL_LIMIT_WIDGET \ | |
@@ -505,6 +507,9 @@ static void nautilus_preferences_window_setup(GtkBuilder *builder, | |
bind_builder_bool (builder, nautilus_preferences, | |
NAUTILUS_PREFERENCES_DIALOG_AUTOMATIC_DECOMPRESSION_WIDGET, | |
NAUTILUS_PREFERENCES_AUTOMATIC_DECOMPRESSION); | |
+ bind_builder_bool (builder, nautilus_preferences, | |
+ NAUTILUS_PREFERENCES_DIALOG_ENABLE_INTERACTIVE_SEARCH_WIDGET, | |
+ NAUTILUS_PREFERENCES_ENABLE_INTERACTIVE_SEARCH); | |
bind_builder_bool (builder, nautilus_list_view_preferences, | |
NAUTILUS_PREFERENCES_DIALOG_LIST_VIEW_USE_TREE_WIDGET, | |
NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE); | |
diff --git a/src/nautilus-window-slot.c b/src/nautilus-window-slot.c | |
index ba278e7..2e95beb 100644 | |
--- a/src/nautilus-window-slot.c | |
+++ b/src/nautilus-window-slot.c | |
@@ -120,6 +120,17 @@ typedef struct | |
GError *mount_error; | |
gboolean tried_mount; | |
gint view_mode_before_search; | |
+ | |
+ /* Interactive search */ | |
+ gboolean isearch_enable; | |
+ gboolean isearch_imcontext_changed; | |
+ gboolean isearch_disable_hide; | |
+ NautilusFile *isearch_selected_file; | |
+ GtkWidget *isearch_window; | |
+ GtkWidget *isearch_entry; | |
+ gulong isearch_entry_changed_id; | |
+ guint isearch_timeout_id; | |
+ gulong isearch_configure_event_id; | |
} NautilusWindowSlotPrivate; | |
G_DEFINE_TYPE_WITH_PRIVATE (NautilusWindowSlot, nautilus_window_slot, GTK_TYPE_BOX); | |
@@ -147,6 +158,97 @@ static void nautilus_window_slot_set_search_visible (NautilusWindowSlot *self, | |
static gboolean nautilus_window_slot_get_search_visible (NautilusWindowSlot *self); | |
static void nautilus_window_slot_set_location (NautilusWindowSlot *self, | |
GFile *location); | |
+ | |
+/* Interactive search */ | |
+static void isearch_ensure (NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_start (NautilusWindowSlot *slot, | |
+ GdkDevice *device); | |
+ | |
+static void isearch_enable_changed (gpointer callback_data); | |
+ | |
+static void isearch_dispose (NautilusWindowSlot *slot); | |
+ | |
+static void isearch_hide (NautilusWindowSlot *slot, | |
+ GdkDevice *device); | |
+ | |
+static gboolean isearch_timeout (gpointer user_data); | |
+ | |
+static void isearch_timeout_destroy (gpointer user_data); | |
+ | |
+static void isearch_timeout_restart (NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_window_delete_event (GtkWidget *widget, | |
+ GdkEventAny *event, | |
+ NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_window_button_press_event (GtkWidget *widget, | |
+ GdkEventButton *event, | |
+ NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_window_scroll_event (GtkWidget *widget, | |
+ GdkEventScroll *event, | |
+ NautilusWindowSlot *slot); | |
+ | |
+static void isearch_activate_items_alternate (NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_window_key_press_event (GtkWidget *widget, | |
+ GdkEventKey *event, | |
+ NautilusWindowSlot *slot); | |
+ | |
+static void isearch_disable_hide (GtkEntry *entry, | |
+ GtkMenu *menu, | |
+ gpointer data); | |
+ | |
+static void isearch_preedit_changed (GtkEntry *entry, | |
+ gchar *preedit, | |
+ NautilusWindowSlot *slot); | |
+ | |
+static void isearch_activate_event (GtkEntry *entry, | |
+ NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_enable_hide_real (gpointer data); | |
+ | |
+static void isearch_enable_hide (GtkWidget *widget, | |
+ gpointer data); | |
+ | |
+static void send_focus_change (GtkWidget *widget, | |
+ GdkDevice *device, | |
+ gboolean in); | |
+ | |
+static void isearch_entry_changed (GtkWidget *entry, | |
+ NautilusWindowSlot *slot); | |
+ | |
+static void isearch_position (NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_compare_filename (const gchar *f1, | |
+ const gchar *f2, | |
+ guint length); | |
+ | |
+static int compare_files (gconstpointer a, | |
+ gconstpointer b, | |
+ gpointer callback_data); | |
+ | |
+static GList *isearch_get_sorted_files (NautilusWindowSlot *slot); | |
+ | |
+static NautilusFile *isearch_find (NautilusWindowSlot *slot, | |
+ const gchar *text); | |
+ | |
+static NautilusFile *isearch_find_next (NautilusWindowSlot *slot, | |
+ const gchar *text); | |
+ | |
+static NautilusFile *isearch_find_prev (NautilusWindowSlot *slot, | |
+ const gchar *text); | |
+ | |
+static gboolean isearch_move_next (NautilusWindowSlot *slot); | |
+ | |
+static gboolean isearch_move_prev (NautilusWindowSlot *slot); | |
+ | |
+static void isearch_set_selection (NautilusWindowSlot *slot, | |
+ NautilusFile *file); | |
+ | |
+#define ISEARCH_TIMEOUT 5000 | |
+ | |
gboolean | |
nautilus_window_slot_handles_location (NautilusWindowSlot *self, | |
GFile *location) | |
@@ -563,21 +665,86 @@ nautilus_window_slot_handle_event (NautilusWindowSlot *self, | |
action = g_action_map_lookup_action (G_ACTION_MAP (priv->slot_action_group), | |
"search-visible"); | |
- /* If the action is not enabled, don't try to handle search */ | |
- if (g_action_get_enabled (action)) | |
- { | |
- retval = gtk_search_bar_handle_event (GTK_SEARCH_BAR (priv->query_editor), | |
- (GdkEvent *) event); | |
- } | |
+ if (priv->isearch_enable) { | |
+ GdkEvent *new_event; | |
+ gchar *old_text; | |
+ const gchar *new_text; | |
+ GdkScreen *screen; | |
+ gboolean text_modified; | |
+ gulong popup_menu_id; | |
+ | |
+ isearch_ensure (self); | |
+ | |
+ /* Make a copy of the current text */ | |
+ old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->isearch_entry))); | |
+ new_event = gdk_event_copy ((GdkEvent *) event); | |
+ g_object_unref (((GdkEventKey *) new_event)->window); | |
+ | |
+ ((GdkEventKey *) new_event)->window = | |
+ g_object_ref (gtk_widget_get_window (priv->isearch_window)); | |
+ gtk_widget_realize (priv->isearch_window); | |
+ | |
+ popup_menu_id = g_signal_connect (priv->isearch_entry, | |
+ "popup-menu", G_CALLBACK (gtk_true), | |
+ NULL); | |
+ | |
+ /* Move the entry off screen */ | |
+ screen = gtk_widget_get_screen (GTK_WIDGET (self)); | |
+ gtk_window_move (GTK_WINDOW (priv->isearch_window), | |
+ gdk_screen_get_width (screen) + 1, | |
+ gdk_screen_get_height (screen) + 1); | |
+ gtk_widget_show (priv->isearch_window); | |
+ | |
+ /* Send the event to the window. If the preedit_changed signal is emitted during this | |
+ * event, we will set priv->imcontext_changed */ | |
+ priv->isearch_imcontext_changed = FALSE; | |
+ retval = gtk_widget_event (priv->isearch_window, new_event); | |
+ gdk_event_free (new_event); | |
+ gtk_widget_hide (priv->isearch_window); | |
+ | |
+ g_signal_handler_disconnect (priv->isearch_entry, popup_menu_id); | |
+ | |
+ /* We check to make sure that the entry tried to handle the text, and that the text has | |
+ * changed. */ | |
+ new_text = gtk_entry_get_text (GTK_ENTRY (priv->isearch_entry)); | |
+ text_modified = strcmp (old_text, new_text) != 0; | |
+ g_free (old_text); | |
+ | |
+ if (priv->isearch_imcontext_changed || (retval && text_modified)) { | |
+ if (isearch_start (self, gdk_event_get_device ((GdkEvent *) event))) { | |
+ gtk_widget_grab_focus (GTK_WIDGET (self)); | |
+ return TRUE; | |
+ } | |
- if (retval) | |
- { | |
- nautilus_window_slot_set_search_visible (self, TRUE); | |
+ gtk_entry_set_text (GTK_ENTRY (priv->isearch_entry), ""); | |
+ return FALSE; | |
+ } | |
+ } else { | |
+ /* If the action is not enabled, don't try to handle search */ | |
+ if (g_action_get_enabled (action)) | |
+ { | |
+ retval = gtk_search_bar_handle_event (GTK_SEARCH_BAR (priv->query_editor), | |
+ (GdkEvent *) event); | |
+ } | |
+ | |
+ if (retval) | |
+ { | |
+ nautilus_window_slot_set_search_visible (self, TRUE); | |
+ } | |
} | |
return retval; | |
} | |
+/* static gboolean | |
+configure_event_cb (GtkWidget *widget, | |
+ GdkEventConfigure *event, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ isearch_hide (slot, NULL); | |
+ return FALSE; | |
+} */ | |
+ | |
static void | |
real_active (NautilusWindowSlot *self) | |
{ | |
@@ -606,10 +773,19 @@ static void | |
real_inactive (NautilusWindowSlot *self) | |
{ | |
NautilusWindow *window; | |
+ NautilusWindowSlotPrivate *priv; | |
+ priv = nautilus_window_slot_get_instance_private (self); | |
window = nautilus_window_slot_get_window (self); | |
g_assert (self == nautilus_window_get_active_slot (window)); | |
+ isearch_hide (self, NULL); | |
+ if (priv->isearch_configure_event_id != 0) { | |
+ g_signal_handler_disconnect (GTK_WIDGET (priv->window), | |
+ priv->isearch_configure_event_id); | |
+ priv->isearch_configure_event_id = 0; | |
+ } | |
+ | |
gtk_widget_insert_action_group (GTK_WIDGET (window), "slot", NULL); | |
} | |
@@ -898,9 +1074,30 @@ nautilus_window_slot_init (NautilusWindowSlot *self) | |
nautilus_application_set_accelerator (app, "slot.files-view-mode(uint32 0)", "<control>2"); | |
nautilus_application_set_accelerator (app, "slot.search-visible", "<control>f"); | |
+ priv->isearch_enable = g_settings_get_boolean (nautilus_preferences, | |
+ NAUTILUS_PREFERENCES_ENABLE_INTERACTIVE_SEARCH); | |
+ | |
+ g_signal_connect_swapped (nautilus_preferences, | |
+ "changed::" NAUTILUS_PREFERENCES_ENABLE_INTERACTIVE_SEARCH, | |
+ G_CALLBACK (isearch_enable_changed), | |
+ self); | |
+ | |
priv->view_mode_before_search = NAUTILUS_VIEW_INVALID_ID; | |
} | |
+static void | |
+nautilus_window_slot_finalize (GObject *object) | |
+{ | |
+ NautilusWindowSlot *slot; | |
+ slot = NAUTILUS_WINDOW_SLOT (object); | |
+ | |
+ g_signal_handlers_disconnect_by_func (nautilus_preferences, | |
+ isearch_enable_changed, | |
+ slot); | |
+ | |
+ G_OBJECT_CLASS (nautilus_window_slot_parent_class)->finalize (object); | |
+} | |
+ | |
#define DEBUG_FLAG NAUTILUS_DEBUG_WINDOW | |
#include "nautilus-debug.h" | |
@@ -2643,6 +2840,7 @@ nautilus_window_slot_dispose (GObject *object) | |
self = NAUTILUS_WINDOW_SLOT (object); | |
priv = nautilus_window_slot_get_instance_private (self); | |
+ isearch_dispose (self); | |
nautilus_window_slot_clear_forward_list (self); | |
nautilus_window_slot_clear_back_list (self); | |
@@ -2731,6 +2929,7 @@ nautilus_window_slot_class_init (NautilusWindowSlotClass *klass) | |
oclass->constructed = nautilus_window_slot_constructed; | |
oclass->set_property = nautilus_window_slot_set_property; | |
oclass->get_property = nautilus_window_slot_get_property; | |
+ oclass->finalize = nautilus_window_slot_finalize; | |
widget_class->grab_focus = nautilus_window_slot_grab_focus; | |
@@ -3122,3 +3321,797 @@ nautilus_window_slot_get_loading (NautilusWindowSlot *self) | |
return priv->loading; | |
} | |
+ | |
+/* Interactive search */ | |
+static void | |
+isearch_ensure (NautilusWindowSlot *slot) | |
+{ | |
+ GtkWidget *frame; | |
+ GtkWidget *vbox; | |
+ GtkWidget *toplevel; | |
+ GdkScreen *screen; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (slot)); | |
+ screen = gtk_widget_get_screen (GTK_WIDGET (slot)); | |
+ | |
+ if (priv->isearch_window != NULL) | |
+ { | |
+ if (gtk_window_has_group (GTK_WINDOW (toplevel))) | |
+ gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), | |
+ GTK_WINDOW (priv->isearch_window)); | |
+ else if (gtk_window_has_group (GTK_WINDOW (priv->isearch_window))) | |
+ gtk_window_group_remove_window (gtk_window_get_group ( | |
+ GTK_WINDOW (priv->isearch_window)), | |
+ GTK_WINDOW (priv->isearch_window)); | |
+ | |
+ gtk_window_set_screen (GTK_WINDOW (priv->isearch_window), screen); | |
+ return; | |
+ } | |
+ | |
+ priv->isearch_window = gtk_window_new (GTK_WINDOW_POPUP); | |
+ gtk_window_set_screen (GTK_WINDOW (priv->isearch_window), screen); | |
+ | |
+ if (gtk_window_has_group (GTK_WINDOW (toplevel))) | |
+ gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), | |
+ GTK_WINDOW (priv->isearch_window)); | |
+ | |
+ gtk_window_set_type_hint (GTK_WINDOW (priv->isearch_window), | |
+ GDK_WINDOW_TYPE_HINT_UTILITY); | |
+ gtk_window_set_modal (GTK_WINDOW (priv->isearch_window), TRUE); | |
+ g_signal_connect (priv->isearch_window, "delete-event", | |
+ G_CALLBACK (isearch_window_delete_event), slot); | |
+ g_signal_connect (priv->isearch_window, "key-press-event", | |
+ G_CALLBACK (isearch_window_key_press_event), slot); | |
+ g_signal_connect (priv->isearch_window, "button-press-event", | |
+ G_CALLBACK (isearch_window_button_press_event), slot); | |
+ g_signal_connect (priv->isearch_window, "scroll-event", | |
+ G_CALLBACK (isearch_window_scroll_event), slot); | |
+ | |
+ frame = gtk_frame_new (NULL); | |
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); | |
+ gtk_widget_show (frame); | |
+ gtk_container_add (GTK_CONTAINER (priv->isearch_window), frame); | |
+ | |
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); | |
+ gtk_widget_show (vbox); | |
+ gtk_container_add (GTK_CONTAINER (frame), vbox); | |
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); | |
+ | |
+ /* add entry */ | |
+ priv->isearch_entry = gtk_entry_new (); | |
+ gtk_widget_show (priv->isearch_entry); | |
+ g_signal_connect (priv->isearch_entry, "populate-popup", | |
+ G_CALLBACK (isearch_disable_hide), slot); | |
+ g_signal_connect (priv->isearch_entry, "activate", | |
+ G_CALLBACK (isearch_activate_event), slot); | |
+ | |
+ g_signal_connect (G_OBJECT (priv->isearch_entry), "preedit-changed", | |
+ G_CALLBACK (isearch_preedit_changed), slot); | |
+ gtk_container_add (GTK_CONTAINER (vbox), priv->isearch_entry); | |
+ | |
+ gtk_widget_realize (priv->isearch_entry); | |
+} | |
+ | |
+static gboolean | |
+isearch_timeout (gpointer user_data) | |
+{ | |
+ NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (user_data); | |
+ | |
+ if (!g_source_is_destroyed (g_main_current_source ())) | |
+ isearch_hide (slot, NULL); | |
+ | |
+ return FALSE; | |
+} | |
+ | |
+static void | |
+isearch_timeout_destroy (gpointer user_data) | |
+{ | |
+ NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (user_data); | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ priv->isearch_timeout_id = 0; | |
+} | |
+ | |
+static void | |
+isearch_timeout_restart (NautilusWindowSlot *slot) | |
+{ | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ if (priv->isearch_timeout_id != 0) | |
+ { | |
+ g_source_remove (priv->isearch_timeout_id); | |
+ | |
+ priv->isearch_timeout_id = | |
+ gdk_threads_add_timeout_full (G_PRIORITY_LOW, ISEARCH_TIMEOUT, | |
+ isearch_timeout, slot, | |
+ isearch_timeout_destroy); | |
+ } | |
+} | |
+ | |
+static gboolean | |
+isearch_window_delete_event (GtkWidget *widget, | |
+ GdkEventAny *event, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); | |
+ | |
+ isearch_hide (slot, NULL); | |
+ return TRUE; | |
+} | |
+ | |
+static gboolean | |
+isearch_window_button_press_event (GtkWidget *widget, | |
+ GdkEventButton *event, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ GdkDevice *keyb_device; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); | |
+ | |
+ keyb_device = gdk_device_get_associated_device (event->device); | |
+ isearch_hide (slot, keyb_device); | |
+ | |
+ /* A bit of hackery here */ | |
+ if (NAUTILUS_IS_CANVAS_VIEW (priv->content_view)) | |
+ { | |
+ NautilusCanvasContainer *cc = nautilus_canvas_view_get_canvas_container ( | |
+ NAUTILUS_CANVAS_VIEW (priv->content_view)); | |
+ gboolean retval; | |
+ | |
+ if (event->window == gtk_layout_get_bin_window (GTK_LAYOUT (cc))) | |
+ g_signal_emit_by_name (GTK_WIDGET (cc), "button-press-event", event, | |
+ &retval); | |
+ | |
+ return retval; | |
+ } | |
+ else if (NAUTILUS_IS_LIST_VIEW (priv->content_view)) | |
+ { | |
+ gboolean retval; | |
+ // NautilusListView *lv = NAUTILUS_LIST_VIEW (priv->content_view); | |
+ GtkTreeView *tv = | |
+ nautilus_list_view_get_tree_view (NAUTILUS_LIST_VIEW (priv->content_view)); | |
+ | |
+ if (event->window == gtk_tree_view_get_bin_window (tv)) | |
+ g_signal_emit_by_name (GTK_WIDGET (tv), | |
+ "button-press-event", | |
+ event, | |
+ &retval); | |
+ | |
+ return retval; | |
+ } | |
+ | |
+ return TRUE; | |
+} | |
+ | |
+static gboolean | |
+isearch_window_scroll_event (GtkWidget *widget, | |
+ GdkEventScroll *event, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ gboolean retval = FALSE; | |
+ | |
+ if (event->direction == GDK_SCROLL_UP) | |
+ { | |
+ isearch_move_prev (slot); | |
+ retval = TRUE; | |
+ } | |
+ else if (event->direction == GDK_SCROLL_DOWN) | |
+ { | |
+ isearch_move_next (slot); | |
+ retval = TRUE; | |
+ } | |
+ | |
+ if (retval) | |
+ isearch_timeout_restart (slot); | |
+ | |
+ return retval; | |
+} | |
+ | |
+static void | |
+isearch_activate_items_alternate (NautilusWindowSlot *slot) | |
+{ | |
+ GList *file_list; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ file_list = nautilus_view_get_selection (priv->content_view); | |
+ if(file_list != NULL) | |
+ nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (priv->content_view), | |
+ file_list, | |
+ NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB, TRUE); | |
+ nautilus_file_list_free (file_list); | |
+} | |
+ | |
+static gboolean | |
+isearch_window_key_press_event (GtkWidget *widget, | |
+ GdkEventKey *event, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ GdkModifierType default_accel; | |
+ gboolean retval = FALSE; | |
+ | |
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); | |
+ g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot), FALSE); | |
+ | |
+ /* close window and cancel the search */ | |
+ if (event->keyval == GDK_KEY_Escape || event->keyval == GDK_KEY_Tab || | |
+ event->keyval == GDK_KEY_KP_Tab || event->keyval == GDK_KEY_ISO_Left_Tab) | |
+ { | |
+ | |
+ isearch_hide (slot, gdk_event_get_device ((GdkEvent *) event)); | |
+ return TRUE; | |
+ } | |
+ | |
+ default_accel = | |
+ gtk_widget_get_modifier_mask (widget, | |
+ GDK_MODIFIER_INTENT_PRIMARY_ACCELERATOR); | |
+ | |
+ /* select previous matching iter */ | |
+ if (event->keyval == GDK_KEY_Up || event->keyval == GDK_KEY_KP_Up) | |
+ { | |
+ if (!isearch_move_prev (slot)) | |
+ gtk_widget_error_bell (widget); | |
+ | |
+ retval = TRUE; | |
+ } | |
+ if (((event->state & (default_accel | GDK_SHIFT_MASK)) == | |
+ (default_accel | GDK_SHIFT_MASK)) && | |
+ (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G)) | |
+ { | |
+ if (!isearch_move_prev (slot)) | |
+ gtk_widget_error_bell (widget); | |
+ | |
+ retval = TRUE; | |
+ } | |
+ /* select next matching iter */ | |
+ if (event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down) | |
+ { | |
+ if (!isearch_move_next (slot)) | |
+ gtk_widget_error_bell (widget); | |
+ | |
+ retval = TRUE; | |
+ } | |
+ if (((event->state & (default_accel | GDK_SHIFT_MASK)) == default_accel) && | |
+ (event->keyval == GDK_KEY_g || event->keyval == GDK_KEY_G)) | |
+ { | |
+ if (!isearch_move_next (slot)) | |
+ gtk_widget_error_bell (widget); | |
+ | |
+ retval = TRUE; | |
+ } | |
+ | |
+ /* Alternate activation (ShiftEnter). | |
+ * Regular activation (Enter) is handled by the entry activate signal. | |
+ */ | |
+ if ((event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) && | |
+ (event->state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK) | |
+ { | |
+ isearch_activate_items_alternate (slot); | |
+ isearch_hide (slot, gdk_event_get_device ((GdkEvent *) event)); | |
+ retval = TRUE; | |
+ } | |
+ isearch_timeout_restart (slot); | |
+ return retval; | |
+} | |
+ | |
+static void | |
+isearch_disable_hide (GtkEntry *entry, | |
+ GtkMenu *menu, | |
+ gpointer data) | |
+{ | |
+ NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (data); | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ priv->isearch_disable_hide = 1; | |
+ g_signal_connect (menu, "hide", G_CALLBACK (isearch_enable_hide), data); | |
+} | |
+ | |
+static void | |
+isearch_preedit_changed (GtkEntry *entry, | |
+ gchar *preedit, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ priv->isearch_imcontext_changed = 1; | |
+ isearch_timeout_restart (slot); | |
+} | |
+ | |
+static void | |
+isearch_activate_event (GtkEntry *entry, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ // GtkTreePath *path; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ if(priv->content_view != NULL && | |
+ NAUTILUS_IS_FILES_VIEW (priv->content_view) && | |
+ nautilus_view_get_selection (NAUTILUS_VIEW (priv->content_view)) != NULL){ | |
+ isearch_hide (slot, gtk_get_current_event_device ()); | |
+ nautilus_files_view_activate_selection (NAUTILUS_FILES_VIEW (priv->content_view)); | |
+ } | |
+} | |
+ | |
+static gboolean | |
+isearch_enable_hide_real (gpointer data) | |
+{ | |
+ NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (data); | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ priv->isearch_disable_hide = 0; | |
+ return FALSE; | |
+} | |
+ | |
+static void | |
+isearch_enable_hide (GtkWidget *widget, | |
+ gpointer data) | |
+{ | |
+ gdk_threads_add_timeout_full (G_PRIORITY_HIGH, 200, | |
+ isearch_enable_hide_real, | |
+ g_object_ref (data), | |
+ g_object_unref); | |
+} | |
+ | |
+static void | |
+send_focus_change (GtkWidget *widget, | |
+ GdkDevice *device, | |
+ gboolean in) | |
+{ | |
+ GdkDeviceManager *device_manager; | |
+ GList *devices; | |
+ GList *d; | |
+ | |
+ device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget)); | |
+ devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER); | |
+ devices = g_list_concat (devices, | |
+ gdk_device_manager_list_devices (device_manager, | |
+ GDK_DEVICE_TYPE_SLAVE)); | |
+ devices = g_list_concat (devices, | |
+ gdk_device_manager_list_devices (device_manager, | |
+ GDK_DEVICE_TYPE_FLOATING)); | |
+ | |
+ for (d = devices; d; d = d->next) | |
+ { | |
+ GdkDevice *dev = d->data; | |
+ GdkEvent *fevent; | |
+ GdkWindow *window; | |
+ | |
+ if (gdk_device_get_source (dev) != GDK_SOURCE_KEYBOARD) | |
+ continue; | |
+ | |
+ window = gtk_widget_get_window (widget); | |
+ | |
+ /* Skip non-master keyboards that haven't | |
+ * selected for events from this window | |
+ */ | |
+ if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER && | |
+ !gdk_window_get_device_events (window, dev)) | |
+ continue; | |
+ | |
+ fevent = gdk_event_new (GDK_FOCUS_CHANGE); | |
+ | |
+ fevent->focus_change.type = GDK_FOCUS_CHANGE; | |
+ fevent->focus_change.window = g_object_ref (window); | |
+ fevent->focus_change.in = in; | |
+ gdk_event_set_device (fevent, device); | |
+ | |
+ gtk_widget_send_focus_change (widget, fevent); | |
+ | |
+ gdk_event_free (fevent); | |
+ } | |
+ | |
+ g_list_free (devices); | |
+} | |
+ | |
+static void | |
+isearch_hide (NautilusWindowSlot *slot, | |
+ GdkDevice *device) | |
+{ | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ if (priv->isearch_disable_hide) | |
+ return; | |
+ | |
+ if (!priv->isearch_enable) | |
+ return; | |
+ | |
+ if (priv->isearch_entry_changed_id) | |
+ { | |
+ g_signal_handler_disconnect (priv->isearch_entry, | |
+ priv->isearch_entry_changed_id); | |
+ priv->isearch_entry_changed_id = 0; | |
+ } | |
+ if (priv->isearch_timeout_id) | |
+ { | |
+ g_source_remove (priv->isearch_timeout_id); | |
+ priv->isearch_timeout_id = 0; | |
+ } | |
+ if (priv->isearch_window != NULL && | |
+ gtk_widget_get_visible (priv->isearch_window)) | |
+ { | |
+ /* send focus-in event */ | |
+ send_focus_change (GTK_WIDGET (priv->isearch_entry), device, FALSE); | |
+ gtk_widget_hide (priv->isearch_window); | |
+ gtk_entry_set_text (GTK_ENTRY (priv->isearch_entry), ""); | |
+ send_focus_change (GTK_WIDGET (slot), device, TRUE); | |
+ } | |
+} | |
+ | |
+static void | |
+isearch_entry_changed (GtkWidget *entry, | |
+ NautilusWindowSlot *slot) | |
+{ | |
+ // gint ret; | |
+ const gchar *text; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ g_return_if_fail (GTK_IS_ENTRY (entry)); | |
+ g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot)); | |
+ | |
+ text = gtk_entry_get_text (GTK_ENTRY (entry)); | |
+ | |
+ /* unselect all */ | |
+ nautilus_view_set_selection (priv->content_view, NULL); | |
+ | |
+ isearch_timeout_restart (slot); | |
+ | |
+ if (*text == '\0') | |
+ return; | |
+ | |
+ isearch_set_selection (slot, isearch_find (slot, text)); | |
+} | |
+ | |
+static gboolean | |
+isearch_start (NautilusWindowSlot *slot, | |
+ GdkDevice *device) | |
+{ | |
+ // GList * list; | |
+ // gboolean found_focus = FALSE; | |
+ GTypeClass *klass; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ if (!priv->isearch_enable) | |
+ return FALSE; | |
+ | |
+ if (priv->isearch_window != NULL && | |
+ gtk_widget_get_visible (priv->isearch_window)) | |
+ return TRUE; | |
+ | |
+ if (nautilus_files_view_get_loading (NAUTILUS_FILES_VIEW (priv->content_view))) | |
+ return FALSE; | |
+ | |
+ isearch_ensure (slot); | |
+ | |
+ /* done, show it */ | |
+ isearch_position (slot); | |
+ gtk_widget_show (priv->isearch_window); | |
+ | |
+ if (priv->isearch_entry_changed_id == 0) | |
+ { | |
+ priv->isearch_entry_changed_id = | |
+ g_signal_connect (priv->isearch_entry, "changed", | |
+ G_CALLBACK (isearch_entry_changed), slot); | |
+ } | |
+ | |
+ priv->isearch_timeout_id = | |
+ gdk_threads_add_timeout_full (G_PRIORITY_LOW, ISEARCH_TIMEOUT, | |
+ isearch_timeout, slot, | |
+ isearch_timeout_destroy); | |
+ | |
+ /* Grab focus without selecting all the text. */ | |
+ klass = g_type_class_peek_parent (GTK_ENTRY_GET_CLASS (priv->isearch_entry)); | |
+ (*GTK_WIDGET_CLASS (klass)->grab_focus) (priv->isearch_entry); | |
+ | |
+ /* send focus-in event */ | |
+ send_focus_change (priv->isearch_entry, device, TRUE); | |
+ | |
+ /* search first matching iter */ | |
+ isearch_entry_changed (priv->isearch_entry, slot); | |
+ return TRUE; | |
+} | |
+ | |
+static void | |
+isearch_position (NautilusWindowSlot *slot) | |
+{ | |
+ gint x, y; | |
+ gint window_x, window_y; | |
+ gint window_width, window_height; | |
+ GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (slot)); | |
+ GdkScreen *screen = gdk_window_get_screen (window); | |
+ GtkRequisition requisition; | |
+ gint monitor_num; | |
+ GdkRectangle monitor; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ monitor_num = gdk_screen_get_monitor_at_window (screen, window); | |
+ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); | |
+ | |
+ gtk_widget_realize (priv->isearch_window); | |
+ | |
+ gdk_window_get_origin (window, &window_x, &window_y); | |
+ window_width = gdk_window_get_width (window); | |
+ window_height = gdk_window_get_height (window); | |
+ gtk_widget_get_preferred_size (priv->isearch_window, &requisition, NULL); | |
+ | |
+ if (window_x + window_width > gdk_screen_get_width (screen)) | |
+ x = gdk_screen_get_width (screen) - requisition.width; | |
+ else if (window_x + window_width - requisition.width < 0) | |
+ x = 0; | |
+ else | |
+ x = window_x + window_width - requisition.width; | |
+ | |
+ if (window_y + window_height + requisition.height > | |
+ gdk_screen_get_height (screen)) | |
+ y = gdk_screen_get_height (screen) - requisition.height; | |
+ else if (window_y + window_height < 0) /* isn't really possible ... */ | |
+ y = 0; | |
+ else | |
+ y = window_y + window_height; | |
+ | |
+ gtk_window_move (GTK_WINDOW (priv->isearch_window), x, y); | |
+} | |
+ | |
+static gboolean | |
+isearch_compare_filename (const gchar *f1, | |
+ const gchar *f2, | |
+ guint length) | |
+{ | |
+ gchar *normalized_f1; | |
+ gchar *normalized_f2; | |
+ gchar *case_normalized_f1 = NULL; | |
+ gchar *case_normalized_f2 = NULL; | |
+ gboolean retval = FALSE; | |
+ | |
+ normalized_f1 = g_utf8_normalize (f1, -1, G_NORMALIZE_ALL); | |
+ normalized_f2 = g_utf8_normalize (f2, -1, G_NORMALIZE_ALL); | |
+ | |
+ if (G_LIKELY (normalized_f1 != NULL && normalized_f2 != NULL)) | |
+ { | |
+ case_normalized_f1 = g_utf8_casefold (normalized_f1, -1); | |
+ case_normalized_f2 = g_utf8_casefold (normalized_f2, -1); | |
+ | |
+ retval = (strncmp (case_normalized_f1, case_normalized_f2, length) == 0); | |
+ } | |
+ | |
+ g_free (normalized_f1); | |
+ g_free (normalized_f2); | |
+ g_free (case_normalized_f1); | |
+ g_free (case_normalized_f2); | |
+ return retval; | |
+} | |
+ | |
+static int | |
+compare_files (gconstpointer a, | |
+ gconstpointer b, | |
+ gpointer callback_data) | |
+{ | |
+ NautilusFilesView *view = NAUTILUS_FILES_VIEW (callback_data); | |
+ NautilusFile *f1 = NAUTILUS_FILE (a); | |
+ NautilusFile *f2 = NAUTILUS_FILE (b); | |
+ | |
+ return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->compare_files (view, f1, f2); | |
+} | |
+ | |
+static GList * | |
+isearch_get_sorted_files (NautilusWindowSlot *slot) | |
+{ | |
+ NautilusWindowSlotPrivate *priv = nautilus_window_slot_get_instance_private (slot); | |
+ NautilusView *view = priv->content_view; | |
+ NautilusDirectory *dir = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view)); | |
+ GList *list = nautilus_directory_get_file_list (dir); | |
+ GList *sorted_list; | |
+ | |
+ sorted_list = g_list_sort_with_data (list, compare_files, view); | |
+ return sorted_list; | |
+} | |
+ | |
+static NautilusFile * | |
+isearch_find (NautilusWindowSlot *slot, | |
+ const gchar *text) | |
+{ | |
+ GList *files = isearch_get_sorted_files (slot); | |
+ GList *l; | |
+ NautilusFile *found = NULL; | |
+ | |
+ for (l = files; l; l = g_list_next (l)) | |
+ { | |
+ NautilusFile *file = NAUTILUS_FILE (l->data); | |
+ gchar *filename = nautilus_file_get_display_name (file); | |
+ | |
+ if (isearch_compare_filename (filename, text, strlen (text))) | |
+ found = file; | |
+ | |
+ g_free (filename); | |
+ | |
+ if (found) | |
+ break; | |
+ } | |
+ | |
+ return found; | |
+} | |
+ | |
+static NautilusFile * | |
+isearch_find_next (NautilusWindowSlot *slot, | |
+ const gchar *text) | |
+{ | |
+ GList *files = isearch_get_sorted_files (slot); | |
+ NautilusFile *found = NULL; | |
+ GList *current; | |
+ GList *l; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ current = g_list_find (files, priv->isearch_selected_file); | |
+ for (l = g_list_next (current); l; l = g_list_next (l)) | |
+ { | |
+ NautilusFile *file = NAUTILUS_FILE (l->data); | |
+ gchar *display_name = nautilus_file_get_display_name (file); | |
+ | |
+ if (isearch_compare_filename (display_name, text, strlen (text))) | |
+ found = file; | |
+ | |
+ g_free (display_name); | |
+ | |
+ if (found) | |
+ break; | |
+ } | |
+ | |
+ return found; | |
+} | |
+ | |
+static NautilusFile * | |
+isearch_find_prev (NautilusWindowSlot *slot, | |
+ const gchar *text) | |
+{ | |
+ GList *files = isearch_get_sorted_files (slot); | |
+ NautilusFile *found = NULL; | |
+ GList *current; | |
+ GList *l; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ current = g_list_find (files, priv->isearch_selected_file); | |
+ for (l = g_list_previous (current); l; l = g_list_previous (l)) | |
+ { | |
+ NautilusFile *file = NAUTILUS_FILE (l->data); | |
+ gchar *display_name = nautilus_file_get_display_name (file); | |
+ | |
+ if (isearch_compare_filename (display_name, text, strlen (text))) | |
+ found = file; | |
+ | |
+ g_free (display_name); | |
+ | |
+ if (found) | |
+ break; | |
+ } | |
+ | |
+ return found; | |
+} | |
+ | |
+static gboolean | |
+isearch_move_next (NautilusWindowSlot *slot) | |
+{ | |
+ const gchar *text; | |
+ NautilusFile *iter; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ text = gtk_entry_get_text (GTK_ENTRY (priv->isearch_entry)); | |
+ iter = isearch_find_next (slot, text); | |
+ | |
+ if (iter) | |
+ isearch_set_selection (slot, iter); | |
+ | |
+ return iter != NULL; | |
+} | |
+ | |
+static gboolean | |
+isearch_move_prev (NautilusWindowSlot *slot) | |
+{ | |
+ const gchar *text; | |
+ NautilusFile *iter; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ text = gtk_entry_get_text (GTK_ENTRY (priv->isearch_entry)); | |
+ iter = isearch_find_prev (slot, text); | |
+ | |
+ if (iter) | |
+ isearch_set_selection (slot, iter); | |
+ | |
+ return iter != NULL; | |
+} | |
+ | |
+static void | |
+isearch_set_selection (NautilusWindowSlot *slot, | |
+ NautilusFile *file) | |
+{ | |
+ GList *list = NULL; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ list = g_list_append (list, file); | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ priv->isearch_selected_file = file; | |
+ nautilus_view_set_selection (priv->content_view, list); | |
+ g_list_free (list); | |
+} | |
+ | |
+static void | |
+isearch_enable_changed (gpointer callback_data) | |
+{ | |
+ NautilusWindowSlot *slot; | |
+ gboolean enable; | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ slot = NAUTILUS_WINDOW_SLOT (callback_data); | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ enable = | |
+ g_settings_get_boolean (nautilus_preferences, | |
+ NAUTILUS_PREFERENCES_ENABLE_INTERACTIVE_SEARCH); | |
+ | |
+ if (enable != priv->isearch_enable) | |
+ { | |
+ if (!enable) | |
+ isearch_dispose (slot); | |
+ | |
+ priv->isearch_enable = enable; | |
+ } | |
+} | |
+ | |
+static void | |
+isearch_dispose (NautilusWindowSlot *slot) | |
+{ | |
+ NautilusWindowSlotPrivate *priv; | |
+ | |
+ priv = nautilus_window_slot_get_instance_private (slot); | |
+ | |
+ if (!priv->isearch_enable) | |
+ return; | |
+ | |
+ if (priv->isearch_entry_changed_id != 0) | |
+ { | |
+ g_signal_handler_disconnect (G_OBJECT (priv->isearch_entry), | |
+ priv->isearch_entry_changed_id); | |
+ priv->isearch_entry_changed_id = 0; | |
+ } | |
+ if (priv->isearch_timeout_id != 0) | |
+ { | |
+ g_source_remove (priv->isearch_timeout_id); | |
+ priv->isearch_timeout_id = 0; | |
+ } | |
+ if (priv->isearch_window != NULL) | |
+ { | |
+ gtk_widget_destroy (priv->isearch_window); | |
+ priv->isearch_window = NULL; | |
+ priv->isearch_entry = NULL; | |
+ } | |
+} | |
diff --git a/src/resources/ui/nautilus-preferences-window.ui b/src/resources/ui/nautilus-preferences-window.ui | |
index 96a2be8..b1cb7c3 100644 | |
--- a/src/resources/ui/nautilus-preferences-window.ui | |
+++ b/src/resources/ui/nautilus-preferences-window.ui | |
@@ -802,6 +802,56 @@ More information will appear when zooming closer.</property> | |
<property name="position">4</property> | |
</packing> | |
</child> | |
+ | |
+ <!-- Typeahead checkbutton BEGIN --> | |
+ <child> | |
+ <object class="GtkBox" id="vbox100"> | |
+ <property name="visible">True</property> | |
+ <property name="can_focus">False</property> | |
+ <property name="orientation">vertical</property> | |
+ <property name="spacing">6</property> | |
+ <child> | |
+ <object class="GtkLabel" id="label117"> | |
+ <property name="visible">True</property> | |
+ <property name="can_focus">False</property> | |
+ <property name="label" translatable="yes">Interactive search (typeahead)</property> | |
+ <property name="xalign">0</property> | |
+ <attributes> | |
+ <attribute name="weight" value="bold"/> | |
+ </attributes> | |
+ </object> | |
+ <packing> | |
+ <property name="expand">False</property> | |
+ <property name="fill">False</property> | |
+ <property name="position">0</property> | |
+ </packing> | |
+ </child> | |
+ <child> | |
+ <object class="GtkCheckButton" id="interactive_search_checkbutton"> | |
+ <property name="label" translatable="yes">Enable interactive search</property> | |
+ <property name="use_action_appearance">False</property> | |
+ <property name="visible">True</property> | |
+ <property name="can_focus">True</property> | |
+ <property name="receives_default">False</property> | |
+ <property name="use_underline">True</property> | |
+ <property name="xalign">0</property> | |
+ <property name="draw_indicator">True</property> | |
+ </object> | |
+ <packing> | |
+ <property name="expand">False</property> | |
+ <property name="fill">False</property> | |
+ <property name="position">2</property> | |
+ </packing> | |
+ </child> | |
+ </object> | |
+ <packing> | |
+ <property name="expand">False</property> | |
+ <property name="fill">True</property> | |
+ <property name="position">5</property> | |
+ </packing> | |
+ </child> | |
+ <!-- Typeahead checkbutton END --> | |
+ | |
</object> | |
<packing> | |
<property name="position">1</property> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From cfc221d511b0c9416ff9dbf4bbd7123c7bbdc60c Mon Sep 17 00:00:00 2001 | |
From: Thomas Martitz <kugel@rockbox.org> | |
Date: Sat, 29 Aug 2015 00:16:27 +0200 | |
Subject: [PATCH] filechooser: restore pre-3.16 type-ahead-find with setting | |
(off by default) | |
The previous type-ahead-find was loved by many people, so removing it without | |
possibility to restore was not nice to them. This commit re-introduces it again | |
with a default-off seting so that the new default search-as-you-type is | |
untouched, for those who don't consider the new search a viable replacement. | |
--- | |
gtk/gtkfilechooserwidget.c | 35 +++++++++++++++++++++++++--- | |
gtk/org.gtk.Settings.FileChooser.gschema.xml | 7 ++++++ | |
2 files changed, 39 insertions(+), 3 deletions(-) | |
diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c | |
index 94d051f..8d41775 100644 | |
--- a/gtk/gtkfilechooserwidget.c | |
+++ b/gtk/gtkfilechooserwidget.c | |
@@ -354,6 +354,7 @@ struct _GtkFileChooserWidgetPrivate { | |
guint show_size_column : 1; | |
guint create_folders : 1; | |
guint auto_selecting_first_row : 1; | |
+ guint use_type_ahead_find : 1; | |
}; | |
#define MAX_LOADING_TIME 500 | |
@@ -589,6 +590,7 @@ static void clear_model_cache (GtkFileChooserWidget *impl, | |
static void set_model_filter (GtkFileChooserWidget *impl, | |
GtkFileFilter *filter); | |
+static void set_sort_column (GtkFileChooserWidget *impl); | |
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserWidget, gtk_file_chooser_widget, GTK_TYPE_BOX, | |
@@ -2437,7 +2439,11 @@ file_list_set_sort_column_ids (GtkFileChooserWidget *impl) | |
{ | |
GtkFileChooserWidgetPrivate *priv = impl->priv; | |
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), -1); | |
+ fprintf(stderr, "file_list_set_sort_column_ids use-type-ahead: %d\n", priv->use_type_ahead_find); | |
+ if (priv->use_type_ahead_find) | |
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), MODEL_COL_NAME); | |
+ else | |
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), -1); | |
gtk_tree_view_column_set_sort_column_id (priv->list_name_column, MODEL_COL_NAME); | |
gtk_tree_view_column_set_sort_column_id (priv->list_time_column, MODEL_COL_TIME); | |
@@ -3693,6 +3699,16 @@ change_icon_theme (GtkFileChooserWidget *impl) | |
profile_end ("end", NULL); | |
} | |
+static void | |
+change_type_ahead (GtkFileChooserWidget *impl, gboolean value) | |
+{ | |
+ GtkFileChooserWidgetPrivate *priv = impl->priv; | |
+ priv->use_type_ahead_find = value; | |
+ | |
+ file_list_set_sort_column_ids(impl); | |
+ set_sort_column(impl); | |
+} | |
+ | |
/* Callback used when a GtkSettings value changes */ | |
static void | |
settings_notify_cb (GObject *object, | |
@@ -3700,6 +3716,7 @@ settings_notify_cb (GObject *object, | |
GtkFileChooserWidget *impl) | |
{ | |
const char *name; | |
+ gboolean value; | |
profile_start ("start", NULL); | |
@@ -3707,7 +3724,11 @@ settings_notify_cb (GObject *object, | |
if (strcmp (name, "gtk-icon-theme-name") == 0) | |
change_icon_theme (impl); | |
- | |
+ if (strcmp (name, "use-type-ahead") == 0) | |
+ { | |
+ g_object_get (object, name, &value, NULL); | |
+ change_type_ahead (impl, value); | |
+ } | |
profile_end ("end", NULL); | |
} | |
@@ -3806,6 +3827,7 @@ settings_load (GtkFileChooserWidget *impl) | |
gboolean show_hidden; | |
gboolean show_size_column; | |
gboolean sort_directories_first; | |
+ gboolean use_type_ahead_find; | |
DateFormat date_format; | |
gint sort_column; | |
GtkSortType sort_order; | |
@@ -3823,10 +3845,12 @@ settings_load (GtkFileChooserWidget *impl) | |
startup_mode = g_settings_get_enum (settings, SETTINGS_KEY_STARTUP_MODE); | |
sort_directories_first = g_settings_get_boolean (settings, SETTINGS_KEY_SORT_DIRECTORIES_FIRST); | |
date_format = g_settings_get_enum (settings, SETTINGS_KEY_DATE_FORMAT); | |
+ use_type_ahead_find = g_settings_get_boolean (settings, "use-type-ahead"); | |
if (!priv->show_hidden_set) | |
set_show_hidden (impl, show_hidden); | |
priv->show_size_column = show_size_column; | |
+ priv->use_type_ahead_find = use_type_ahead_find; | |
gtk_tree_view_column_set_visible (priv->list_size_column, show_size_column); | |
priv->sort_column = sort_column; | |
@@ -4332,6 +4356,7 @@ load_set_model (GtkFileChooserWidget *impl) | |
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), | |
GTK_TREE_MODEL (priv->browse_files_model)); | |
update_columns (impl, FALSE, _("Modified")); | |
+ g_object_set(priv->browse_files_tree_view, "enable-search", priv->use_type_ahead_find, NULL); | |
file_list_set_sort_column_ids (impl); | |
set_sort_column (impl); | |
profile_msg (" gtk_tree_view_set_model end", NULL); | |
@@ -7412,7 +7437,10 @@ recent_idle_cleanup (gpointer data) | |
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), | |
GTK_TREE_MODEL (priv->recent_model)); | |
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), -1); | |
+ if (priv->use_type_ahead_find) | |
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), MODEL_COL_NAME); | |
+ else | |
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), -1); | |
gtk_tree_view_column_set_sort_column_id (priv->list_name_column, -1); | |
gtk_tree_view_column_set_sort_column_id (priv->list_time_column, -1); | |
@@ -8556,6 +8584,7 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *impl) | |
priv->recent_manager = gtk_recent_manager_get_default (); | |
priv->create_folders = TRUE; | |
priv->auto_selecting_first_row = FALSE; | |
+ priv->use_type_ahead_find = FALSE; | |
/* Ensure GTK+ private types used by the template | |
* definition before calling gtk_widget_init_template() | |
diff --git a/gtk/org.gtk.Settings.FileChooser.gschema.xml b/gtk/org.gtk.Settings.FileChooser.gschema.xml | |
index c3753f2..bdba1b0 100644 | |
--- a/gtk/org.gtk.Settings.FileChooser.gschema.xml | |
+++ b/gtk/org.gtk.Settings.FileChooser.gschema.xml | |
@@ -148,6 +148,13 @@ | |
The amount of detail to show in the Modified column. | |
</description> | |
</key> | |
+ <key name="use-type-ahead" type='b'> | |
+ <default>false</default> | |
+ <summary>Enable type-ahead find</summary> | |
+ <description> | |
+ Whether to use new search method or previous type-ahead find. | |
+ </description> | |
+ </key> | |
</schema> | |
</schemalist> | |
-- | |
2.5.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
as far as I can tell it applies successfully to both (for now)