diff -Naur nautilus-3.38.2/debian/changelog nautilus-3.38.2.patched/debian/changelog --- nautilus-3.38.2/debian/changelog 2021-01-11 17:23:10.000000000 +0100 +++ nautilus-3.38.2.patched/debian/changelog 2021-02-07 23:40:31.116806724 +0100 @@ -1,3 +1,9 @@ +nautilus (1:3.38.2-1ubuntu2) hirsute; urgency=medium + + * restore type-ahead with a patch borrowed from Arch. LP: #1754069 + + -- Rolf Leggewie Sun, 07 Feb 2021 23:39:30 +0100 + nautilus (1:3.38.2-1ubuntu1) hirsute; urgency=medium * Resynchronize on Debian: diff -Naur nautilus-3.38.2/debian/patches/nautilus-restore-typeahead.patch nautilus-3.38.2.patched/debian/patches/nautilus-restore-typeahead.patch --- nautilus-3.38.2/debian/patches/nautilus-restore-typeahead.patch 1970-01-01 01:00:00.000000000 +0100 +++ nautilus-3.38.2.patched/debian/patches/nautilus-restore-typeahead.patch 2021-02-07 23:38:19.134087896 +0100 @@ -0,0 +1,1136 @@ +diff --git a/data/org.gnome.nautilus.gschema.xml b/data/org.gnome.nautilus.gschema.xml +index 4b393f83f..2c96e9c0c 100644 +--- a/data/org.gnome.nautilus.gschema.xml ++++ b/data/org.gnome.nautilus.gschema.xml +@@ -192,6 +192,11 @@ + What viewer should be used when searching + When searching Nautilus will switch to the type of view in this setting. + ++ ++ true ++ Enable interactive (type-ahead) search ++ If set to true, enables interactive search, similar to Nautilus 3.4. ++ + + true + Whether to open the hovered folder after a timeout when drag and drop operation +diff --git a/src/nautilus-global-preferences.h b/src/nautilus-global-preferences.h +index 87a79e706..109c2e6e2 100644 +--- a/src/nautilus-global-preferences.h ++++ b/src/nautilus-global-preferences.h +@@ -140,6 +140,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" ++ + /* Default view when searching */ + #define NAUTILUS_PREFERENCES_SEARCH_VIEW "search-view" + +diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c +index 6b78eded4..594d8fcde 100644 +--- a/src/nautilus-list-view.c ++++ b/src/nautilus-list-view.c +@@ -3080,6 +3080,7 @@ nautilus_list_view_set_selection (NautilusFilesView *view, + gboolean cursor_is_set_on_selection = FALSE; + GList *iters, *l; + NautilusFile *file; ++ GtkTreePath *path2 = NULL; + + list_view = NAUTILUS_LIST_VIEW (view); + model = list_view->details->model; +@@ -3111,10 +3112,22 @@ nautilus_list_view_set_selection (NautilusFilesView *view, + + gtk_tree_selection_select_iter (tree_selection, + (GtkTreeIter *) l->data); ++ if (!path2) ++ path2 = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), (GtkTreeIter *) l->data); + } ++ + g_list_free_full (iters, g_free); + } + ++ if (path2) { ++ gtk_tree_view_set_cursor_on_cell (list_view->details->tree_view, ++ path2, ++ list_view->details->file_name_column, ++ GTK_CELL_RENDERER (list_view->details->file_name_cell), ++ TRUE); ++ gtk_tree_path_free (path2); ++ } ++ + g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view); + nautilus_files_view_notify_selection_changed (view); + } +@@ -4077,3 +4090,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 7e19621e8..b568b9f71 100644 +@@ -37,4 +37,5 @@ struct _NautilusListView + NautilusListViewDetails *details; + }; + +-NautilusFilesView * nautilus_list_view_new (NautilusWindowSlot *slot); +\ No newline at end of file ++NautilusFilesView * nautilus_list_view_new (NautilusWindowSlot *slot); ++GtkTreeView * nautilus_list_view_get_tree_view (NautilusListView *list_view); +diff --git a/src/nautilus-window-slot.c b/src/nautilus-window-slot.c +index b3a7bc211..0e126a563 100644 +--- a/src/nautilus-window-slot.c ++++ b/src/nautilus-window-slot.c +@@ -23,6 +23,9 @@ + + #include "nautilus-window-slot.h" + ++#include "nautilus-canvas-view.h" ++#include "nautilus-list-view.h" ++ + #include "nautilus-application.h" + #include "nautilus-bookmark.h" + #include "nautilus-bookmark-list.h" +@@ -126,6 +129,17 @@ typedef struct + 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; ++ + /* Menus */ + GMenu *extensions_background_menu; + GMenu *templates_menu; +@@ -227,6 +241,98 @@ nautilus_window_slot_get_restore_tab_data (NautilusWindowSlot *self) + return data; + } + ++ ++/* 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) +@@ -658,15 +764,70 @@ nautilus_window_slot_handle_event (NautilusWindowSlot *self, + } + } + +- /* If the action is not enabled, don't try to handle search */ +- if (g_action_get_enabled (action)) +- { +- retval = nautilus_query_editor_handle_event (priv->query_editor, 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; ++ } ++ 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 = nautilus_query_editor_handle_event (priv->query_editor, event); ++ } + +- if (retval) +- { +- nautilus_window_slot_set_search_visible (self, TRUE); ++ if (retval) ++ { ++ nautilus_window_slot_set_search_visible (self, TRUE); ++ } + } + + return retval; +@@ -1206,6 +1367,14 @@ nautilus_window_slot_init (NautilusWindowSlot *self) + nautilus_application_set_accelerator (app, "slot.files-view-mode(uint32 0)", "2"); + nautilus_application_set_accelerator (app, "slot.search-visible", "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; + } + +@@ -3042,6 +3211,7 @@ nautilus_window_slot_dispose (GObject *object) + + self = NAUTILUS_WINDOW_SLOT (object); + priv = nautilus_window_slot_get_instance_private (self); ++ isearch_dispose (self); + + g_signal_handlers_disconnect_by_data (nautilus_trash_monitor_get (), self); + +@@ -3102,6 +3272,10 @@ nautilus_window_slot_finalize (GObject *object) + self = NAUTILUS_WINDOW_SLOT (object); + priv = nautilus_window_slot_get_instance_private (self); + ++ g_signal_handlers_disconnect_by_func (nautilus_preferences, ++ isearch_enable_changed, ++ self); ++ + g_clear_pointer (&priv->title, g_free); + + G_OBJECT_CLASS (nautilus_window_slot_parent_class)->finalize (object); +@@ -3566,6 +3740,13 @@ nautilus_window_slot_set_active (NautilusWindowSlot *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); + } + +@@ -3610,3 +3791,792 @@ nautilus_window_slot_get_query_editor (NautilusWindowSlot *self) + + return priv->query_editor; + } ++ ++/* 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 = FALSE; ++ ++ 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 = FALSE; ++ // 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); ++ 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); ++ ++ 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 -Naur nautilus-3.38.2/debian/patches/series nautilus-3.38.2.patched/debian/patches/series --- nautilus-3.38.2/debian/patches/series 2021-01-11 17:23:10.000000000 +0100 +++ nautilus-3.38.2.patched/debian/patches/series 2021-02-07 23:38:19.154088305 +0100 @@ -10,3 +10,4 @@ nautilusgtkplacesview-show-error-if-volume-is-not-mo.patch ubuntu/shell-search-provider-implement-XUbuntuCancel-to-request-.patch revert_tracker_update.patch +nautilus-restore-typeahead.patch