/* * Nautilus * * Copyright (C) 1999, 2000, 2004 Red Hat, Inc. * Copyright (C) 1999, 2000, 2001 Eazel, Inc. * * Nautilus is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * Nautilus is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Authors: Elliot Lee * John Sullivan * Alexander Larsson */ /* nautilus-window.c: Implementation of the main window object */ #include "nautilus-window.h" #include #include #include #include #include #include #include #include #include #ifdef GDK_WINDOWING_WAYLAND #include #endif #ifdef GDK_WINDOWING_X11 #include #endif #define DEBUG_FLAG NAUTILUS_DEBUG_WINDOW #include "nautilus-debug.h" #include "nautilus-application.h" #include "nautilus-bookmark-list.h" #include "nautilus-clipboard.h" #include "nautilus-dnd.h" #include "nautilus-enums.h" #include "nautilus-file-operations.h" #include "nautilus-file-undo-manager.h" #include "nautilus-file-utilities.h" #include "nautilus-global-preferences.h" #include "nautilus-list-view.h" #include "nautilus-location-entry.h" #include "nautilus-metadata.h" #include "nautilus-mime-actions.h" #include "nautilus-notebook.h" #include "nautilus-other-locations-window-slot.h" #include "nautilus-pathbar.h" #include "nautilus-profile.h" #include "nautilus-properties-window.h" #include "nautilus-signaller.h" #include "nautilus-toolbar.h" #include "nautilus-trash-monitor.h" #include "nautilus-ui-utilities.h" #include "nautilus-window-slot.h" /* Forward and back buttons on the mouse */ static gboolean mouse_extra_buttons = TRUE; static int mouse_forward_button = 9; static int mouse_back_button = 8; static void mouse_back_button_changed (gpointer callback_data); static void mouse_forward_button_changed (gpointer callback_data); static void use_extra_mouse_buttons_changed (gpointer callback_data); static void nautilus_window_initialize_actions (NautilusWindow *window); static GtkWidget *nautilus_window_ensure_location_entry (NautilusWindow *window); static void close_slot (NautilusWindow *window, NautilusWindowSlot *slot, gboolean remove_from_notebook); /* Sanity check: highest mouse button value I could find was 14. 5 is our * lower threshold (well-documented to be the one of the button events for the * scrollwheel), so it's hardcoded in the functions below. However, if you have * a button that registers higher and want to map it, file a bug and * we'll move the bar. Makes you wonder why the X guys don't have * defined values for these like the XKB stuff, huh? */ #define UPPER_MOUSE_LIMIT 14 #define NOTIFICATION_TIMEOUT 6 /*s */ struct _NautilusWindow { GtkApplicationWindow parent_instance; GtkWidget *notebook; /* available slots, and active slot. * Both of them may never be NULL. */ GList *slots; NautilusWindowSlot *active_slot; GtkWidget *content_paned; /* Side Pane */ int side_pane_width; GtkWidget *sidebar; /* container for the GtkPlacesSidebar */ GtkWidget *places_sidebar; /* the actual GtkPlacesSidebar */ GVolume *selected_volume; /* the selected volume in the sidebar popup callback */ GFile *selected_file; /* the selected file in the sidebar popup callback */ /* Main view */ GtkWidget *main_view; /* Notifications */ GtkWidget *in_app_notification_undo; GtkWidget *in_app_notification_undo_label; GtkWidget *in_app_notification_undo_close_button; GtkWidget *in_app_notification_undo_undo_button; guint in_app_notification_undo_timeout_id; GtkWidget *notification_operation; GtkWidget *notification_operation_label; GtkWidget *notification_operation_close; GtkWidget *notification_operation_open; guint notification_operation_timeout_id; GFile *folder_to_open; /* Toolbar */ GtkWidget *toolbar; gboolean temporary_navigation_bar; /* focus widget before the location bar has been shown temporarily */ GtkWidget *last_focus_widget; /* Handle when exported */ gchar *export_handle; guint sidebar_width_handler_id; gulong bookmarks_id; GQueue *tab_data_queue; GtkPadController *pad_controller; GtkGesture *multi_press_gesture; GtkGesture *notebook_multi_press_gesture; }; enum { SLOT_ADDED, SLOT_REMOVED, ACTIVE_SELECTION_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (NautilusWindow, nautilus_window, GTK_TYPE_APPLICATION_WINDOW); static const struct { unsigned int keyval; const char *action; } extra_window_keybindings [] = { /* Window actions */ { GDK_KEY_AddFavorite, "bookmark-current-location" }, { GDK_KEY_Favorites, "bookmarks" }, { GDK_KEY_Go, "enter-location" }, { GDK_KEY_HomePage, "go-home" }, { GDK_KEY_OpenURL, "enter-location" }, { GDK_KEY_Refresh, "reload" }, { GDK_KEY_Reload, "reload" }, { GDK_KEY_Search, "search" }, { GDK_KEY_Start, "go-home" }, { GDK_KEY_Stop, "stop" }, { GDK_KEY_Back, "back" }, { GDK_KEY_Forward, "forward" }, }; static const GtkPadActionEntry pad_actions[] = { { GTK_PAD_ACTION_BUTTON, 0, -1, N_("Parent folder"), "up" }, { GTK_PAD_ACTION_BUTTON, 1, -1, N_("Home"), "go-home" }, { GTK_PAD_ACTION_BUTTON, 2, -1, N_("New tab"), "new-tab" }, { GTK_PAD_ACTION_BUTTON, 3, -1, N_("Close current view"), "close-current-view" }, { GTK_PAD_ACTION_BUTTON, 4, -1, N_("Back"), "back" }, { GTK_PAD_ACTION_BUTTON, 5, -1, N_("Forward"), "forward" }, }; static void action_close_current_view (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window; NautilusWindowSlot *slot; window = NAUTILUS_WINDOW (user_data); slot = nautilus_window_get_active_slot (window); nautilus_window_slot_close (window, slot); } static void action_go_home (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window; GFile *home; window = NAUTILUS_WINDOW (user_data); home = g_file_new_for_path (g_get_home_dir ()); nautilus_window_open_location_full (window, home, nautilus_event_get_window_open_flags (), NULL, NULL); g_object_unref (home); } static void action_reload (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindowSlot *slot; slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (user_data)); nautilus_window_slot_queue_reload (slot); } static void action_stop (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window; NautilusWindowSlot *slot; window = NAUTILUS_WINDOW (user_data); slot = nautilus_window_get_active_slot (window); nautilus_window_slot_stop_loading (slot); } static void action_up (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindowSlot *slot; GFile *parent, *location; slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (user_data)); location = nautilus_window_slot_get_location (slot); if (location != NULL) { parent = g_file_get_parent (location); if (parent != NULL) { nautilus_window_open_location_full (NAUTILUS_WINDOW (user_data), parent, nautilus_event_get_window_open_flags (), NULL, NULL); } g_clear_object (&parent); } } static void action_back (GSimpleAction *action, GVariant *state, gpointer user_data) { nautilus_window_back_or_forward (NAUTILUS_WINDOW (user_data), TRUE, 0, nautilus_event_get_window_open_flags ()); } static void action_forward (GSimpleAction *action, GVariant *state, gpointer user_data) { nautilus_window_back_or_forward (NAUTILUS_WINDOW (user_data), FALSE, 0, nautilus_event_get_window_open_flags ()); } static void action_bookmark_current_location (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; NautilusApplication *app = NAUTILUS_APPLICATION (g_application_get_default ()); NautilusWindowSlot *slot; slot = nautilus_window_get_active_slot (window); nautilus_bookmark_list_append (nautilus_application_get_bookmarks (app), nautilus_window_slot_get_bookmark (slot)); } static void action_new_tab (GSimpleAction *action, GVariant *state, gpointer user_data) { nautilus_window_new_tab (NAUTILUS_WINDOW (user_data)); } static void action_enter_location (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; nautilus_window_ensure_location_entry (window); } static void action_tab_previous (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; nautilus_notebook_prev_page (NAUTILUS_NOTEBOOK (window->notebook)); } static void action_tab_next (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; nautilus_notebook_next_page (NAUTILUS_NOTEBOOK (window->notebook)); } static void action_tab_move_left (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; nautilus_notebook_reorder_current_child_relative (NAUTILUS_NOTEBOOK (window->notebook), -1); } static void action_tab_move_right (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; nautilus_notebook_reorder_current_child_relative (NAUTILUS_NOTEBOOK (window->notebook), 1); } static void action_go_to_tab (GSimpleAction *action, GVariant *value, gpointer user_data) { NautilusWindow *window = NAUTILUS_WINDOW (user_data); GtkNotebook *notebook; gint16 num; notebook = GTK_NOTEBOOK (window->notebook); num = g_variant_get_int32 (value); if (num < gtk_notebook_get_n_pages (notebook)) { gtk_notebook_set_current_page (notebook, num); } } static void action_prompt_for_location_root (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; GFile *location; GtkWidget *entry; location = g_file_new_for_path ("/"); entry = nautilus_window_ensure_location_entry (window); nautilus_location_entry_set_location (NAUTILUS_LOCATION_ENTRY (entry), location); g_object_unref (location); } static void action_prompt_for_location_home (GSimpleAction *action, GVariant *state, gpointer user_data) { GtkWidget *entry; entry = nautilus_window_ensure_location_entry (NAUTILUS_WINDOW (user_data)); nautilus_location_entry_set_special_text (NAUTILUS_LOCATION_ENTRY (entry), "~"); gtk_editable_set_position (GTK_EDITABLE (entry), -1); } static void action_redo (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; nautilus_file_undo_manager_redo (GTK_WINDOW (window), NULL); } static void action_undo (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = user_data; nautilus_file_undo_manager_undo (GTK_WINDOW (window), NULL); } static void action_toggle_state_view_button (GSimpleAction *action, GVariant *state, gpointer user_data) { GVariant *current_state; current_state = g_action_get_state (G_ACTION (action)); g_action_change_state (G_ACTION (action), g_variant_new_boolean (!g_variant_get_boolean (current_state))); g_variant_unref (current_state); } static void on_location_changed (NautilusWindow *window) { gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (window->places_sidebar), nautilus_window_slot_get_location (nautilus_window_get_active_slot (window))); } static void on_slot_location_changed (NautilusWindowSlot *slot, GParamSpec *pspec, NautilusWindow *window) { if (nautilus_window_get_active_slot (window) == slot) { on_location_changed (window); } } static void on_slot_selection_changed (NautilusWindowSlot *slot, GParamSpec *pspec, NautilusWindow *window) { if (nautilus_window_get_active_slot (window) == slot) { g_signal_emit (window, signals[ACTIVE_SELECTION_CHANGED], 0); } } static void notebook_switch_page_cb (GtkNotebook *notebook, GtkWidget *page, unsigned int page_num, NautilusWindow *window) { NautilusWindowSlot *slot; GtkWidget *widget; widget = gtk_notebook_get_nth_page (GTK_NOTEBOOK (window->notebook), page_num); g_assert (widget != NULL); /* find slot corresponding to the target page */ slot = NAUTILUS_WINDOW_SLOT (widget); g_assert (slot != NULL); nautilus_window_set_active_slot (nautilus_window_slot_get_window (slot), slot); } static void connect_slot (NautilusWindow *window, NautilusWindowSlot *slot) { g_signal_connect (slot, "notify::location", G_CALLBACK (on_slot_location_changed), window); g_signal_connect (slot, "notify::selection", G_CALLBACK (on_slot_selection_changed), window); } static void disconnect_slot (NautilusWindow *window, NautilusWindowSlot *slot) { g_signal_handlers_disconnect_by_data (slot, window); } static NautilusWindowSlot * nautilus_window_create_slot (NautilusWindow *window, GFile *location) { NautilusFile *file = NULL; NautilusWindowSlot *slot; if (location) { file = nautilus_file_get (location); } /* If not file, assume we open the home directory. We will switch eventually * to a different location if not. */ if (file && nautilus_file_is_other_locations (file)) { slot = NAUTILUS_WINDOW_SLOT (nautilus_other_locations_window_slot_new (window)); } else { slot = nautilus_window_slot_new (window); } nautilus_file_unref (file); return slot; } static NautilusWindowSlot * nautilus_window_create_and_init_slot (NautilusWindow *window, GFile *location, NautilusWindowOpenFlags flags) { NautilusWindowSlot *slot; slot = nautilus_window_create_slot (window, location); nautilus_window_initialize_slot (window, slot, flags); return slot; } static NautilusWindowSlot * replace_active_slot (NautilusWindow *window, GFile *location, NautilusWindowOpenFlags flags) { NautilusWindowSlot *new_slot; NautilusWindowSlot *active_slot; new_slot = nautilus_window_create_and_init_slot (window, location, flags); active_slot = nautilus_window_get_active_slot (window); if (active_slot) { close_slot (window, active_slot, TRUE); } return new_slot; } void nautilus_window_initialize_slot (NautilusWindow *window, NautilusWindowSlot *slot, NautilusWindowOpenFlags flags) { g_assert (NAUTILUS_IS_WINDOW (window)); g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); connect_slot (window, slot); g_signal_handlers_block_by_func (window->notebook, G_CALLBACK (notebook_switch_page_cb), window); nautilus_notebook_add_tab (NAUTILUS_NOTEBOOK (window->notebook), slot, (flags & NAUTILUS_WINDOW_OPEN_SLOT_APPEND) != 0 ? -1 : gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook)) + 1, FALSE); g_signal_handlers_unblock_by_func (window->notebook, G_CALLBACK (notebook_switch_page_cb), window); window->slots = g_list_append (window->slots, slot); g_signal_emit (window, signals[SLOT_ADDED], 0, slot); } void nautilus_window_open_location_full (NautilusWindow *window, GFile *location, NautilusWindowOpenFlags flags, GList *selection, NautilusWindowSlot *target_slot) { NautilusWindowSlot *active_slot; gboolean new_tab_at_end; NautilusNavigationState *navigation_state = NULL; /* The location owner can be one of the slots requesting to handle an * unhandled location. But this slot can be destroyed when switching to * a new slot. So keep the location alive. */ g_object_ref (location); /* Assert that we are not managing new windows */ g_assert (!(flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW)); /* if the flags say we want a new tab, open a slot in the current window */ if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) != 0) { new_tab_at_end = g_settings_get_enum (nautilus_preferences, NAUTILUS_PREFERENCES_NEW_TAB_POSITION) == NAUTILUS_NEW_TAB_POSITION_END; if (new_tab_at_end) { flags |= NAUTILUS_WINDOW_OPEN_SLOT_APPEND; } } active_slot = nautilus_window_get_active_slot (window); if (!target_slot) { target_slot = active_slot; } if (target_slot == NULL || (flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) != 0) { target_slot = nautilus_window_create_and_init_slot (window, location, flags); } else if (!nautilus_window_slot_handles_location (target_slot, location)) { navigation_state = nautilus_window_slot_get_navigation_state (active_slot); target_slot = replace_active_slot (window, location, flags); } /* Make the opened location the one active if we weren't ask for the * oposite, since it's the most usual use case */ if (!(flags & NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE)) { gtk_window_present (GTK_WINDOW (window)); nautilus_window_set_active_slot (window, target_slot); } if (navigation_state != NULL) { nautilus_window_slot_open_location_set_navigation_state (target_slot, location, flags, selection, NAUTILUS_LOCATION_CHANGE_STANDARD, navigation_state, 0); free_navigation_state (navigation_state); } else { nautilus_window_slot_open_location_full (target_slot, location, flags, selection); } g_object_unref (location); } static void unset_focus_widget (NautilusWindow *window) { if (window->last_focus_widget != NULL) { g_object_remove_weak_pointer (G_OBJECT (window->last_focus_widget), (gpointer *) &window->last_focus_widget); window->last_focus_widget = NULL; } } static void remember_focus_widget (NautilusWindow *window) { GtkWidget *focus_widget; focus_widget = gtk_window_get_focus (GTK_WINDOW (window)); if (focus_widget != NULL) { unset_focus_widget (window); window->last_focus_widget = focus_widget; g_object_add_weak_pointer (G_OBJECT (focus_widget), (gpointer *) &(window->last_focus_widget)); } } static void nautilus_window_grab_focus (GtkWidget *widget) { NautilusWindowSlot *slot; slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (widget)); GTK_WIDGET_CLASS (nautilus_window_parent_class)->grab_focus (widget); if (slot) { gtk_widget_grab_focus (GTK_WIDGET (slot)); } } static void restore_focus_widget (NautilusWindow *window) { if (window->last_focus_widget != NULL) { gtk_widget_grab_focus (window->last_focus_widget); unset_focus_widget (window); } } static void location_entry_cancel_callback (GtkWidget *widget, NautilusWindow *window) { nautilus_toolbar_set_show_location_entry (NAUTILUS_TOOLBAR (window->toolbar), FALSE); restore_focus_widget (window); } static void location_entry_location_changed_callback (GtkWidget *widget, GFile *location, NautilusWindow *window) { nautilus_toolbar_set_show_location_entry (NAUTILUS_TOOLBAR (window->toolbar), FALSE); restore_focus_widget (window); nautilus_window_open_location_full (window, location, 0, NULL, NULL); } static void close_slot (NautilusWindow *window, NautilusWindowSlot *slot, gboolean remove_from_notebook) { int page_num; GtkNotebook *notebook; g_assert (NAUTILUS_IS_WINDOW_SLOT (slot)); DEBUG ("Closing slot %p", slot); disconnect_slot (window, slot); window->slots = g_list_remove (window->slots, slot); g_signal_emit (window, signals[SLOT_REMOVED], 0, slot); notebook = GTK_NOTEBOOK (window->notebook); if (remove_from_notebook) { page_num = gtk_notebook_page_num (notebook, GTK_WIDGET (slot)); g_assert (page_num >= 0); /* this will call gtk_widget_destroy on the slot */ gtk_notebook_remove_page (notebook, page_num); } } void nautilus_window_new_tab (NautilusWindow *window) { NautilusWindowSlot *current_slot; GFile *location; g_autofree gchar *uri = NULL; current_slot = nautilus_window_get_active_slot (window); location = nautilus_window_slot_get_location (current_slot); if (location != NULL) { uri = g_file_get_uri (location); if (eel_uri_is_search (uri)) { location = g_file_new_for_path (g_get_home_dir ()); } else { g_object_ref (location); } nautilus_window_open_location_full (window, location, NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB, NULL, NULL); g_object_unref (location); } } static void update_cursor (NautilusWindow *window) { NautilusWindowSlot *slot; slot = nautilus_window_get_active_slot (window); if (slot != NULL && nautilus_window_slot_get_allow_stop (slot)) { GdkDisplay *display; g_autoptr (GdkCursor) cursor = NULL; display = gtk_widget_get_display (GTK_WIDGET (window)); cursor = gdk_cursor_new_from_name (display, "progress"); gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), cursor); } else { gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL); } } void nautilus_window_reset_menus (NautilusWindow *window) { nautilus_window_sync_allow_stop (window, nautilus_window_get_active_slot (window)); } void nautilus_window_sync_allow_stop (NautilusWindow *window, NautilusWindowSlot *slot) { GAction *stop_action; GAction *reload_action; gboolean allow_stop, slot_is_active, slot_allow_stop; stop_action = g_action_map_lookup_action (G_ACTION_MAP (window), "stop"); reload_action = g_action_map_lookup_action (G_ACTION_MAP (window), "reload"); allow_stop = g_action_get_enabled (stop_action); slot_allow_stop = nautilus_window_slot_get_allow_stop (slot); slot_is_active = (slot == nautilus_window_get_active_slot (window)); if (!slot_is_active || allow_stop != slot_allow_stop) { if (slot_is_active) { g_simple_action_set_enabled (G_SIMPLE_ACTION (stop_action), slot_allow_stop); g_simple_action_set_enabled (G_SIMPLE_ACTION (reload_action), !slot_allow_stop); } if (gtk_widget_get_realized (GTK_WIDGET (window))) { update_cursor (window); } /* Avoid updating the notebook if we are calling on dispose or * on removal of a notebook tab */ if (nautilus_notebook_contains_slot (NAUTILUS_NOTEBOOK (window->notebook), slot)) { nautilus_notebook_sync_loading (NAUTILUS_NOTEBOOK (window->notebook), slot); } } } GtkWidget * nautilus_window_get_notebook (NautilusWindow *window) { g_return_val_if_fail (NAUTILUS_IS_WINDOW (window), NULL); return window->notebook; } static gboolean save_sidebar_width_cb (gpointer user_data) { NautilusWindow *window = user_data; window->sidebar_width_handler_id = 0; DEBUG ("Saving sidebar width: %d", window->side_pane_width); g_settings_set_int (nautilus_window_state, NAUTILUS_WINDOW_STATE_SIDEBAR_WIDTH, window->side_pane_width); return FALSE; } /* side pane helpers */ static void side_pane_size_allocate_callback (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { NautilusWindow *window = user_data; if (window->sidebar_width_handler_id != 0) { g_source_remove (window->sidebar_width_handler_id); window->sidebar_width_handler_id = 0; } if (allocation->width != window->side_pane_width && allocation->width > 1) { window->side_pane_width = allocation->width; window->sidebar_width_handler_id = g_idle_add (save_sidebar_width_cb, window); } } static void setup_side_pane_width (NautilusWindow *window) { g_return_if_fail (window->sidebar != NULL); window->side_pane_width = g_settings_get_int (nautilus_window_state, NAUTILUS_WINDOW_STATE_SIDEBAR_WIDTH); gtk_paned_set_position (GTK_PANED (window->content_paned), window->side_pane_width); } /* Callback used when the places sidebar changes location; we need to change the displayed folder */ static void open_location_cb (NautilusWindow *window, GFile *location, GtkPlacesOpenFlags open_flags) { NautilusWindowOpenFlags flags; NautilusApplication *application; switch (open_flags) { case GTK_PLACES_OPEN_NEW_TAB: { flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB | NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE; } break; case GTK_PLACES_OPEN_NEW_WINDOW: { flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW; } break; case GTK_PLACES_OPEN_NORMAL: /* fall-through */ default: { flags = 0; } break; } application = NAUTILUS_APPLICATION (g_application_get_default ()); /* FIXME: We shouldn't need to provide the window, but seems gtk_application_get_active_window * is not working properly in GtkApplication, so we cannot rely on that... */ nautilus_application_open_location_full (application, location, flags, NULL, window, NULL); } static void places_sidebar_unmount_operation_cb (NautilusWindow *window, GMountOperation *mount_operation) { g_signal_connect (mount_operation, "show-unmount-progress", G_CALLBACK (show_unmount_progress_cb), NULL); g_signal_connect (mount_operation, "aborted", G_CALLBACK (show_unmount_progress_aborted_cb), NULL); } /* Callback used when the places sidebar needs us to present an error message */ static void places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar, const char *primary, const char *secondary, gpointer user_data) { NautilusWindow *window = NAUTILUS_WINDOW (user_data); show_dialog (primary, secondary, GTK_WINDOW (window), GTK_MESSAGE_ERROR); } static void places_sidebar_show_other_locations_with_flags (NautilusWindow *window, GtkPlacesOpenFlags open_flags) { GFile *location; location = g_file_new_for_uri ("other-locations:///"); open_location_cb (window, location, open_flags); g_object_unref (location); } static void places_sidebar_show_starred_location (NautilusWindow *window, GtkPlacesOpenFlags open_flags) { GFile *location; location = g_file_new_for_uri ("starred:///"); open_location_cb (window, location, open_flags); g_object_unref (location); } static GList * build_selection_list_from_gfile_list (GList *gfile_list) { GList *result; GList *l; result = NULL; for (l = gfile_list; l; l = l->next) { GFile *file; NautilusDragSelectionItem *item; file = l->data; item = nautilus_drag_selection_item_new (); item->uri = g_file_get_uri (file); item->file = nautilus_file_get_existing (file); item->got_icon_position = FALSE; result = g_list_prepend (result, item); } return g_list_reverse (result); } void nautilus_window_start_dnd (NautilusWindow *window, GdkDragContext *context) { g_return_if_fail (NAUTILUS_IS_WINDOW (window)); gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (window->places_sidebar), TRUE, context); } void nautilus_window_end_dnd (NautilusWindow *window, GdkDragContext *context) { g_return_if_fail (NAUTILUS_IS_WINDOW (window)); gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (window->places_sidebar), FALSE, context); } /* Callback used when the places sidebar needs to know the drag action to suggest */ static GdkDragAction places_sidebar_drag_action_requested_cb (GtkPlacesSidebar *sidebar, GdkDragContext *context, GFile *dest_file, GList *source_file_list, gpointer user_data) { GList *items; char *uri; int action = 0; NautilusDragInfo *info; guint32 source_actions; info = nautilus_drag_get_source_data (context); if (info != NULL) { items = info->selection_cache; source_actions = info->source_actions; } else { items = build_selection_list_from_gfile_list (source_file_list); source_actions = 0; } uri = g_file_get_uri (dest_file); if (items == NULL) { goto out; } nautilus_drag_default_drop_action_for_icons (context, uri, items, source_actions, &action); out: if (info == NULL) { nautilus_drag_destroy_selection_list (items); } g_free (uri); return action; } /* Callback used when the places sidebar needs us to pop up a menu with possible drag actions */ static GdkDragAction places_sidebar_drag_action_ask_cb (GtkPlacesSidebar *sidebar, GdkDragAction actions, gpointer user_data) { return nautilus_drag_drop_action_ask (GTK_WIDGET (sidebar), actions); } static GList * build_uri_list_from_gfile_list (GList *file_list) { GList *result; GList *l; result = NULL; for (l = file_list; l; l = l->next) { GFile *file = l->data; char *uri; uri = g_file_get_uri (file); result = g_list_prepend (result, uri); } return g_list_reverse (result); } /* Callback used when the places sidebar has URIs dropped into it. We do a normal file operation for them. */ static void places_sidebar_drag_perform_drop_cb (GtkPlacesSidebar *sidebar, GFile *dest_file, GList *source_file_list, GdkDragAction action, gpointer user_data) { char *dest_uri; GList *source_uri_list; dest_uri = g_file_get_uri (dest_file); source_uri_list = build_uri_list_from_gfile_list (source_file_list); nautilus_file_operations_copy_move (source_uri_list, dest_uri, action, GTK_WIDGET (sidebar), NULL, NULL, NULL); g_free (dest_uri); g_list_free_full (source_uri_list, g_free); } /* Callback used in the "empty trash" menu item from the places sidebar */ static void action_empty_trash (GSimpleAction *action, GVariant *variant, gpointer user_data) { NautilusWindow *window = NAUTILUS_WINDOW (user_data); nautilus_file_operations_empty_trash (GTK_WIDGET (window), TRUE, NULL); } /* Callback used for the "properties" menu item from the places sidebar */ static void action_properties (GSimpleAction *action, GVariant *variant, gpointer user_data) { NautilusWindow *window = NAUTILUS_WINDOW (user_data); GList *list; NautilusFile *file; file = nautilus_file_get (window->selected_file); list = g_list_append (NULL, file); nautilus_properties_window_present (list, GTK_WIDGET (window), NULL, NULL, NULL); nautilus_file_list_free (list); g_clear_object (&window->selected_file); } static gboolean check_have_gnome_disks (void) { gchar *disks_path; gboolean res; disks_path = g_find_program_in_path ("gnome-disks"); res = (disks_path != NULL); g_free (disks_path); return res; } static gboolean should_show_format_command (GVolume *volume) { gchar *unix_device_id; gboolean show_format; unix_device_id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); show_format = (unix_device_id != NULL) && check_have_gnome_disks (); g_free (unix_device_id); return show_format; } static void action_restore_tab (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusWindow *window = NAUTILUS_WINDOW (user_data); NautilusWindowOpenFlags flags; g_autoptr (GFile) location = NULL; NautilusWindowSlot *slot; NautilusNavigationState *data; if (g_queue_get_length (window->tab_data_queue) == 0) { return; } flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB | NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE; data = g_queue_pop_head (window->tab_data_queue); location = nautilus_file_get_location (data->file); slot = nautilus_window_create_and_init_slot (window, location, flags); nautilus_window_slot_open_location_full (slot, location, flags, NULL); nautilus_window_slot_restore_navigation_state (slot, data); free_navigation_state (data); } static guint get_window_xid (NautilusWindow *window) { #ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) { GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); return (guint) gdk_x11_window_get_xid (gdk_window); } #endif return 0; } static void action_format (GSimpleAction *action, GVariant *variant, gpointer user_data) { NautilusWindow *window = NAUTILUS_WINDOW (user_data); GAppInfo *app_info; gchar *cmdline, *device_identifier, *xid_string; device_identifier = g_volume_get_identifier (window->selected_volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); xid_string = g_strdup_printf ("%x", get_window_xid (window)); cmdline = g_strconcat ("gnome-disks ", "--block-device ", device_identifier, " ", "--format-device ", "--xid ", xid_string, NULL); app_info = g_app_info_create_from_commandline (cmdline, NULL, 0, NULL); g_app_info_launch (app_info, NULL, NULL, NULL); g_free (cmdline); g_free (device_identifier); g_free (xid_string); g_clear_object (&app_info); g_clear_object (&window->selected_volume); } static void add_menu_separator (GtkWidget *menu) { GtkWidget *separator; separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); gtk_container_add (GTK_CONTAINER (menu), separator); gtk_widget_show (separator); } static void places_sidebar_populate_popup_cb (GtkPlacesSidebar *sidebar, GtkWidget *menu, GFile *selected_file, GVolume *selected_volume, gpointer user_data) { NautilusWindow *window = NAUTILUS_WINDOW (user_data); GFile *trash; GtkWidget *menu_item; GAction *action; g_clear_object (&window->selected_file); g_clear_object (&window->selected_volume); if (selected_file) { trash = g_file_new_for_uri ("trash:///"); if (g_file_equal (trash, selected_file)) { add_menu_separator (menu); menu_item = gtk_model_button_new (); gtk_actionable_set_action_name (GTK_ACTIONABLE (menu_item), "win.empty-trash"); g_object_set (menu_item, "text", _("Empty _Trash"), NULL); gtk_container_add (GTK_CONTAINER (menu), menu_item); gtk_widget_show (menu_item); action = g_action_map_lookup_action (G_ACTION_MAP (window), "empty-trash"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !nautilus_trash_monitor_is_empty ()); } g_object_unref (trash); if (g_file_is_native (selected_file)) { window->selected_file = g_object_ref (selected_file); add_menu_separator (menu); menu_item = gtk_model_button_new (); gtk_actionable_set_action_name (GTK_ACTIONABLE (menu_item), "win.properties"); g_object_set (menu_item, "text", _("_Properties"), NULL); gtk_container_add (GTK_CONTAINER (menu), menu_item); gtk_widget_show (menu_item); } } if (selected_volume) { if (should_show_format_command (selected_volume)) { menu_item = gtk_model_button_new (); gtk_actionable_set_action_name (GTK_ACTIONABLE (menu_item), "win.format"); g_object_set (menu_item, "text", _("_Format…"), NULL); if (selected_volume != NULL && G_IS_VOLUME (selected_volume)) { window->selected_volume = g_object_ref (selected_volume); } gtk_container_add (GTK_CONTAINER (menu), menu_item); gtk_widget_show (menu_item); action = g_action_map_lookup_action (G_ACTION_MAP (window), "format"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selected_volume != NULL && G_IS_VOLUME (selected_volume)); } } } static void nautilus_window_set_up_sidebar (NautilusWindow *window) { setup_side_pane_width (window); g_signal_connect (window->sidebar, "size-allocate", G_CALLBACK (side_pane_size_allocate_callback), window); gtk_places_sidebar_set_open_flags (GTK_PLACES_SIDEBAR (window->places_sidebar), (GTK_PLACES_OPEN_NORMAL | GTK_PLACES_OPEN_NEW_TAB | GTK_PLACES_OPEN_NEW_WINDOW)); g_signal_connect_swapped (window->places_sidebar, "open-location", G_CALLBACK (open_location_cb), window); g_signal_connect (window->places_sidebar, "show-error-message", G_CALLBACK (places_sidebar_show_error_message_cb), window); g_signal_connect (window->places_sidebar, "drag-action-requested", G_CALLBACK (places_sidebar_drag_action_requested_cb), window); g_signal_connect (window->places_sidebar, "drag-action-ask", G_CALLBACK (places_sidebar_drag_action_ask_cb), window); g_signal_connect (window->places_sidebar, "drag-perform-drop", G_CALLBACK (places_sidebar_drag_perform_drop_cb), window); g_signal_connect (window->places_sidebar, "populate-popup", G_CALLBACK (places_sidebar_populate_popup_cb), window); g_signal_connect (window->places_sidebar, "unmount", G_CALLBACK (places_sidebar_unmount_operation_cb), window); } void nautilus_window_hide_sidebar (NautilusWindow *window) { DEBUG ("Called hide_sidebar()"); g_return_if_fail (NAUTILUS_IS_WINDOW (window)); gtk_widget_hide (window->sidebar); } void nautilus_window_show_sidebar (NautilusWindow *window) { DEBUG ("Called show_sidebar()"); g_return_if_fail (NAUTILUS_IS_WINDOW (window)); gtk_widget_show (window->sidebar); setup_side_pane_width (window); } void nautilus_window_slot_close (NautilusWindow *window, NautilusWindowSlot *slot) { NautilusNavigationState *data; DEBUG ("Requesting to remove slot %p from window %p", slot, window); if (window == NULL) { return; } data = nautilus_window_slot_get_navigation_state (slot); if (data != NULL) { g_queue_push_head (window->tab_data_queue, data); } close_slot (window, slot, TRUE); /* If that was the last slot in the window, close the window. */ if (window->slots == NULL) { DEBUG ("Last slot removed, closing the window"); nautilus_window_close (window); } } static void nautilus_window_sync_bookmarks (NautilusWindow *window) { gboolean can_bookmark = FALSE; NautilusWindowSlot *slot; NautilusBookmarkList *bookmarks; GAction *action; GFile *location; slot = window->active_slot; location = nautilus_window_slot_get_location (slot); if (location != NULL) { bookmarks = nautilus_application_get_bookmarks (NAUTILUS_APPLICATION (gtk_window_get_application (GTK_WINDOW (window)))); can_bookmark = nautilus_bookmark_list_can_bookmark_location (bookmarks, location); } action = g_action_map_lookup_action (G_ACTION_MAP (window), "bookmark-current-location"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_bookmark); } void nautilus_window_sync_location_widgets (NautilusWindow *window) { NautilusWindowSlot *slot; GFile *location; GAction *action; gboolean enabled; slot = window->active_slot; location = nautilus_window_slot_get_location (slot); /* Change the location bar and path bar to match the current location. */ if (location != NULL) { GtkWidget *location_entry; GtkWidget *path_bar; location_entry = nautilus_toolbar_get_location_entry (NAUTILUS_TOOLBAR (window->toolbar)); nautilus_location_entry_set_location (NAUTILUS_LOCATION_ENTRY (location_entry), location); path_bar = nautilus_toolbar_get_path_bar (NAUTILUS_TOOLBAR (window->toolbar)); nautilus_path_bar_set_path (NAUTILUS_PATH_BAR (path_bar), location); } action = g_action_map_lookup_action (G_ACTION_MAP (window), "back"); enabled = nautilus_window_slot_get_back_history (slot) != NULL; g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled); action = g_action_map_lookup_action (G_ACTION_MAP (window), "forward"); enabled = nautilus_window_slot_get_forward_history (slot) != NULL; g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled); nautilus_window_sync_bookmarks (window); } static GtkWidget * nautilus_window_ensure_location_entry (NautilusWindow *window) { GtkWidget *location_entry; remember_focus_widget (window); nautilus_toolbar_set_show_location_entry (NAUTILUS_TOOLBAR (window->toolbar), TRUE); location_entry = nautilus_toolbar_get_location_entry (NAUTILUS_TOOLBAR (window->toolbar)); gtk_widget_grab_focus (location_entry); return location_entry; } static void remove_notifications (NautilusWindow *window) { GtkRevealerTransitionType transition_type; /* Hide it inmediatily so we can animate the new notification. */ transition_type = gtk_revealer_get_transition_type (GTK_REVEALER (window->in_app_notification_undo)); gtk_revealer_set_transition_type (GTK_REVEALER (window->in_app_notification_undo), GTK_REVEALER_TRANSITION_TYPE_NONE); gtk_revealer_set_reveal_child (GTK_REVEALER (window->in_app_notification_undo), FALSE); gtk_revealer_set_transition_type (GTK_REVEALER (window->in_app_notification_undo), transition_type); if (window->in_app_notification_undo_timeout_id != 0) { g_source_remove (window->in_app_notification_undo_timeout_id); window->in_app_notification_undo_timeout_id = 0; } transition_type = gtk_revealer_get_transition_type (GTK_REVEALER (window->notification_operation)); gtk_revealer_set_transition_type (GTK_REVEALER (window->notification_operation), GTK_REVEALER_TRANSITION_TYPE_NONE); gtk_revealer_set_reveal_child (GTK_REVEALER (window->notification_operation), FALSE); gtk_revealer_set_transition_type (GTK_REVEALER (window->notification_operation), transition_type); if (window->notification_operation_timeout_id != 0) { g_source_remove (window->notification_operation_timeout_id); window->notification_operation_timeout_id = 0; } } static void hide_in_app_notification_undo (NautilusWindow *window) { if (window->in_app_notification_undo_timeout_id != 0) { g_source_remove (window->in_app_notification_undo_timeout_id); window->in_app_notification_undo_timeout_id = 0; } gtk_revealer_set_reveal_child (GTK_REVEALER (window->in_app_notification_undo), FALSE); } static void on_in_app_notification_undo_undo_button_clicked (GtkWidget *notification, NautilusWindow *window) { hide_in_app_notification_undo (window); nautilus_file_undo_manager_undo (GTK_WINDOW (window), NULL); } static void on_in_app_notification_undo_close_button_clicked (GtkWidget *notification, NautilusWindow *window) { hide_in_app_notification_undo (window); } static gboolean on_in_app_notification_undo_timeout (NautilusWindow *window) { hide_in_app_notification_undo (window); return G_SOURCE_REMOVE; } static gchar * in_app_notification_undo_deleted_get_label (NautilusFileUndoInfo *undo_info) { GList *files; gchar *file_label; gchar *label; gint length; files = nautilus_file_undo_info_trash_get_files (NAUTILUS_FILE_UNDO_INFO_TRASH (undo_info)); length = g_list_length (files); if (length == 1) { file_label = g_file_get_basename (files->data); /* Translators: only one item has been deleted and %s is its name. */ label = g_markup_printf_escaped (_("“%s” deleted"), file_label); g_free (file_label); } else { /* Translators: one or more items might have been deleted, and %d * is the count. */ label = g_markup_printf_escaped (ngettext ("%d file deleted", "%d files deleted", length), length); } return label; } static gchar * in_app_notification_undo_unstar_get_label (NautilusFileUndoInfo *undo_info) { GList *files; gchar *label; gint length; files = nautilus_file_undo_info_starred_get_files (NAUTILUS_FILE_UNDO_INFO_STARRED (undo_info)); length = g_list_length (files); if (length == 1) { g_autofree gchar *file_label = NULL; file_label = nautilus_file_get_display_name (files->data); /* Translators: one item has been unstarred and %s is its name. */ label = g_markup_printf_escaped (_("“%s” unstarred"), file_label); } else { /* Translators: one or more items have been unstarred, and %d * is the count. */ label = g_markup_printf_escaped (ngettext ("%d file unstarred", "%d files unstarred", length), length); } return label; } static void nautilus_window_on_undo_changed (NautilusFileUndoManager *manager, NautilusWindow *window) { NautilusFileUndoInfo *undo_info; NautilusFileUndoManagerState state; undo_info = nautilus_file_undo_manager_get_action (); state = nautilus_file_undo_manager_get_state (); if (undo_info != NULL && state == NAUTILUS_FILE_UNDO_MANAGER_STATE_UNDO) { gboolean popup_notification = FALSE; g_autofree gchar *label = NULL; if (nautilus_file_undo_info_get_op_type (undo_info) == NAUTILUS_FILE_UNDO_OP_MOVE_TO_TRASH) { g_autoptr (GList) files = NULL; files = nautilus_file_undo_info_trash_get_files (NAUTILUS_FILE_UNDO_INFO_TRASH (undo_info)); /* Don't pop up a notification if user canceled the operation or the focus * is not in the this window. This is an easy way to know from which window * was the delete operation made */ if (files != NULL && gtk_window_has_toplevel_focus (GTK_WINDOW (window))) { popup_notification = TRUE; label = in_app_notification_undo_deleted_get_label (undo_info); } } else if (nautilus_file_undo_info_get_op_type (undo_info) == NAUTILUS_FILE_UNDO_OP_STARRED) { NautilusWindowSlot *active_slot; GFile *location; active_slot = nautilus_window_get_active_slot (window); location = nautilus_window_slot_get_location (active_slot); /* Don't pop up a notification if the focus is not in the this * window. This is an easy way to know from which window was the * unstart operation made */ if (eel_uri_is_starred (g_file_get_uri (location)) && gtk_window_has_toplevel_focus (GTK_WINDOW (window)) && !nautilus_file_undo_info_starred_is_starred (NAUTILUS_FILE_UNDO_INFO_STARRED (undo_info))) { popup_notification = TRUE; label = in_app_notification_undo_unstar_get_label (undo_info); } } if (popup_notification) { remove_notifications (window); gtk_label_set_markup (GTK_LABEL (window->in_app_notification_undo_label), label); gtk_revealer_set_reveal_child (GTK_REVEALER (window->in_app_notification_undo), TRUE); window->in_app_notification_undo_timeout_id = g_timeout_add_seconds (NOTIFICATION_TIMEOUT, (GSourceFunc) on_in_app_notification_undo_timeout, window); } } else { hide_in_app_notification_undo (window); } } static void hide_notification_operation (NautilusWindow *window) { if (window->notification_operation_timeout_id != 0) { g_source_remove (window->notification_operation_timeout_id); window->notification_operation_timeout_id = 0; } gtk_revealer_set_reveal_child (GTK_REVEALER (window->notification_operation), FALSE); g_clear_object (&window->folder_to_open); } static void on_notification_operation_open_clicked (GtkWidget *notification, NautilusWindow *window) { nautilus_window_open_location_full (window, window->folder_to_open, 0, NULL, NULL); hide_notification_operation (window); } static void on_notification_operation_close_clicked (GtkWidget *notification, NautilusWindow *window) { hide_notification_operation (window); } static gboolean on_notification_operation_timeout (NautilusWindow *window) { hide_notification_operation (window); return FALSE; } void nautilus_window_show_operation_notification (NautilusWindow *window, gchar *main_label, GFile *folder_to_open) { gchar *button_label; gchar *folder_name; NautilusFile *folder; GFile *current_location; current_location = nautilus_window_slot_get_location (window->active_slot); if (gtk_window_has_toplevel_focus (GTK_WINDOW (window))) { remove_notifications (window); gtk_label_set_text (GTK_LABEL (window->notification_operation_label), main_label); if (g_file_equal (folder_to_open, current_location)) { gtk_widget_hide (window->notification_operation_open); } else { gtk_widget_show (window->notification_operation_open); window->folder_to_open = g_object_ref (folder_to_open); folder = nautilus_file_get (folder_to_open); folder_name = nautilus_file_get_display_name (folder); button_label = g_strdup_printf (_("Open %s"), folder_name); gtk_button_set_label (GTK_BUTTON (window->notification_operation_open), button_label); nautilus_file_unref (folder); g_free (folder_name); g_free (button_label); } gtk_revealer_set_reveal_child (GTK_REVEALER (window->notification_operation), TRUE); window->notification_operation_timeout_id = g_timeout_add_seconds (NOTIFICATION_TIMEOUT, (GSourceFunc) on_notification_operation_timeout, window); } } static void path_bar_location_changed_callback (GtkWidget *widget, GFile *location, NautilusWindow *window) { nautilus_window_open_location_full (window, location, 0, NULL, NULL); } static void notebook_popup_menu_new_tab_cb (GtkMenuItem *menuitem, gpointer user_data) { NautilusWindow *window = user_data; nautilus_window_new_tab (window); } static void notebook_popup_menu_move_left_cb (GtkMenuItem *menuitem, gpointer user_data) { NautilusWindow *window = user_data; nautilus_notebook_reorder_current_child_relative (NAUTILUS_NOTEBOOK (window->notebook), -1); } static void notebook_popup_menu_move_right_cb (GtkMenuItem *menuitem, gpointer user_data) { NautilusWindow *window = user_data; nautilus_notebook_reorder_current_child_relative (NAUTILUS_NOTEBOOK (window->notebook), 1); } static void notebook_popup_menu_close_cb (GtkMenuItem *menuitem, gpointer user_data) { NautilusWindow *window = user_data; NautilusWindowSlot *slot; slot = window->active_slot; nautilus_window_slot_close (window, slot); } static void notebook_popup_menu_show (NautilusWindow *window, const GdkEvent *event) { GtkWidget *popup; GtkWidget *item; gboolean can_move_left, can_move_right; NautilusNotebook *notebook; notebook = NAUTILUS_NOTEBOOK (window->notebook); can_move_left = nautilus_notebook_can_reorder_current_child_relative (notebook, -1); can_move_right = nautilus_notebook_can_reorder_current_child_relative (notebook, 1); popup = gtk_menu_new (); item = gtk_menu_item_new_with_mnemonic (_("_New Tab")); g_signal_connect (item, "activate", G_CALLBACK (notebook_popup_menu_new_tab_cb), window); gtk_menu_shell_append (GTK_MENU_SHELL (popup), item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), gtk_separator_menu_item_new ()); item = gtk_menu_item_new_with_mnemonic (_("Move Tab _Left")); g_signal_connect (item, "activate", G_CALLBACK (notebook_popup_menu_move_left_cb), window); gtk_menu_shell_append (GTK_MENU_SHELL (popup), item); gtk_widget_set_sensitive (item, can_move_left); item = gtk_menu_item_new_with_mnemonic (_("Move Tab _Right")); g_signal_connect (item, "activate", G_CALLBACK (notebook_popup_menu_move_right_cb), window); gtk_menu_shell_append (GTK_MENU_SHELL (popup), item); gtk_widget_set_sensitive (item, can_move_right); gtk_menu_shell_append (GTK_MENU_SHELL (popup), gtk_separator_menu_item_new ()); item = gtk_menu_item_new_with_mnemonic (_("_Close Tab")); g_signal_connect (item, "activate", G_CALLBACK (notebook_popup_menu_close_cb), window); gtk_menu_shell_append (GTK_MENU_SHELL (popup), item); gtk_widget_show_all (popup); gtk_menu_popup_at_pointer (GTK_MENU (popup), event); } /* emitted when the user clicks the "close" button of tabs */ static void notebook_tab_close_requested (NautilusNotebook *notebook, NautilusWindowSlot *slot, NautilusWindow *window) { nautilus_window_slot_close (window, slot); } static void notebook_button_press_cb (GtkGestureMultiPress *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) { NautilusWindow *window; GdkEventSequence *sequence; const GdkEvent *event; window = NAUTILUS_WINDOW (user_data); if (nautilus_notebook_content_area_hit (NAUTILUS_NOTEBOOK (window->notebook), x, y)) { return; } sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); notebook_popup_menu_show (window, event); } static gboolean notebook_popup_menu_cb (GtkWidget *widget, gpointer user_data) { NautilusWindow *window = user_data; notebook_popup_menu_show (window, NULL); return TRUE; } GtkWidget * nautilus_window_get_toolbar (NautilusWindow *window) { g_return_val_if_fail (NAUTILUS_IS_WINDOW (window), NULL); return window->toolbar; } static void setup_toolbar (NautilusWindow *window) { GtkWidget *path_bar; GtkWidget *location_entry; g_object_set (window->toolbar, "window", window, NULL); /* connect to the pathbar signals */ path_bar = nautilus_toolbar_get_path_bar (NAUTILUS_TOOLBAR (window->toolbar)); g_signal_connect_object (path_bar, "path-clicked", G_CALLBACK (path_bar_location_changed_callback), window, 0); g_signal_connect_swapped (path_bar, "open-location", G_CALLBACK (open_location_cb), window); /* connect to the location entry signals */ location_entry = nautilus_toolbar_get_location_entry (NAUTILUS_TOOLBAR (window->toolbar)); g_signal_connect_object (location_entry, "location-changed", G_CALLBACK (location_entry_location_changed_callback), window, 0); g_signal_connect_object (location_entry, "cancel", G_CALLBACK (location_entry_cancel_callback), window, 0); gtk_window_set_titlebar (GTK_WINDOW (window), window->toolbar); } static void notebook_page_removed_cb (GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data) { NautilusWindow *window = user_data; NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (page); gboolean dnd_slot; dnd_slot = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (slot), "dnd-window-slot")); if (!dnd_slot) { return; } close_slot (window, slot, FALSE); } static void notebook_page_added_cb (GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data) { NautilusWindow *window = user_data; NautilusWindowSlot *slot = NAUTILUS_WINDOW_SLOT (page); NautilusWindowSlot *dummy_slot; gboolean dnd_slot; dnd_slot = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (slot), "dnd-window-slot")); if (!dnd_slot) { return; } g_object_set_data (G_OBJECT (page), "dnd-window-slot", GINT_TO_POINTER (FALSE)); nautilus_window_slot_set_window (slot, window); window->slots = g_list_append (window->slots, slot); g_signal_emit (window, signals[SLOT_ADDED], 0, slot); nautilus_window_set_active_slot (window, slot); dummy_slot = g_list_nth_data (window->slots, 0); if (dummy_slot != NULL) { close_slot (window, dummy_slot, TRUE); } gtk_widget_show (GTK_WIDGET (window)); } static GtkNotebook * notebook_create_window_cb (GtkNotebook *notebook, GtkWidget *page, gint x, gint y, gpointer user_data) { NautilusApplication *app; NautilusWindow *new_window; NautilusWindowSlot *slot; if (!NAUTILUS_IS_WINDOW_SLOT (page)) { return NULL; } app = NAUTILUS_APPLICATION (g_application_get_default ()); new_window = nautilus_application_create_window (app, gtk_widget_get_screen (GTK_WIDGET (notebook))); slot = NAUTILUS_WINDOW_SLOT (page); g_object_set_data (G_OBJECT (slot), "dnd-window-slot", GINT_TO_POINTER (TRUE)); gtk_window_set_position (GTK_WINDOW (new_window), GTK_WIN_POS_MOUSE); return GTK_NOTEBOOK (new_window->notebook); } static void setup_notebook (NautilusWindow *window) { g_signal_connect (window->notebook, "tab-close-request", G_CALLBACK (notebook_tab_close_requested), window); g_signal_connect (window->notebook, "popup-menu", G_CALLBACK (notebook_popup_menu_cb), window); g_signal_connect (window->notebook, "switch-page", G_CALLBACK (notebook_switch_page_cb), window); g_signal_connect (window->notebook, "create-window", G_CALLBACK (notebook_create_window_cb), window); g_signal_connect (window->notebook, "page-added", G_CALLBACK (notebook_page_added_cb), window); g_signal_connect (window->notebook, "page-removed", G_CALLBACK (notebook_page_removed_cb), window); g_signal_connect (window->notebook_multi_press_gesture, "pressed", G_CALLBACK (notebook_button_press_cb), window); } const GActionEntry win_entries[] = { { "back", action_back }, { "forward", action_forward }, { "up", action_up }, { "view-menu", action_toggle_state_view_button, NULL, "false", NULL }, { "reload", action_reload }, { "stop", action_stop }, { "new-tab", action_new_tab }, { "enter-location", action_enter_location }, { "bookmark-current-location", action_bookmark_current_location }, { "undo", action_undo }, { "redo", action_redo }, /* Only accesible by shorcuts */ { "close-current-view", action_close_current_view }, { "go-home", action_go_home }, { "tab-previous", action_tab_previous }, { "tab-next", action_tab_next }, { "tab-move-left", action_tab_move_left }, { "tab-move-right", action_tab_move_right }, { "prompt-root-location", action_prompt_for_location_root }, { "prompt-home-location", action_prompt_for_location_home }, { "go-to-tab", NULL, "i", "0", action_go_to_tab }, { "empty-trash", action_empty_trash }, { "properties", action_properties }, { "format", action_format }, { "restore-tab", action_restore_tab }, }; static void nautilus_window_initialize_actions (NautilusWindow *window) { GApplication *app; GAction *action; GVariant *state; gchar detailed_action[80]; gchar accel[80]; gint i; const gchar *reload_accels[] = { "F5", "r", NULL }; const gchar *prompt_root_location_accels[] = { "slash", "KP_Divide", NULL }; const gchar *prompt_home_location_accels[] = { "asciitilde", "dead_tilde", NULL }; g_action_map_add_action_entries (G_ACTION_MAP (window), win_entries, G_N_ELEMENTS (win_entries), window); app = g_application_get_default (); nautilus_application_set_accelerator (app, "win.back", "Left"); nautilus_application_set_accelerator (app, "win.forward", "Right"); nautilus_application_set_accelerator (app, "win.enter-location", "l"); nautilus_application_set_accelerator (app, "win.new-tab", "t"); nautilus_application_set_accelerator (app, "win.close-current-view", "w"); /* Special case reload, since users are used to use two shortcuts instead of one */ nautilus_application_set_accelerators (app, "win.reload", reload_accels); nautilus_application_set_accelerator (app, "win.undo", "z"); nautilus_application_set_accelerator (app, "win.redo", "z"); /* Only accesible by shorcuts */ nautilus_application_set_accelerator (app, "win.bookmark-current-location", "d"); nautilus_application_set_accelerator (app, "win.up", "Up"); nautilus_application_set_accelerator (app, "win.go-home", "Home"); nautilus_application_set_accelerator (app, "win.tab-previous", "Page_Up"); nautilus_application_set_accelerator (app, "win.tab-next", "Page_Down"); nautilus_application_set_accelerator (app, "win.tab-move-left", "Page_Up"); nautilus_application_set_accelerator (app, "win.tab-move-right", "Page_Down"); nautilus_application_set_accelerators (app, "win.prompt-root-location", prompt_root_location_accels); /* Support keyboard layouts which have a dead tilde key but not a tilde key. */ nautilus_application_set_accelerators (app, "win.prompt-home-location", prompt_home_location_accels); nautilus_application_set_accelerator (app, "win.view-menu", "F10"); nautilus_application_set_accelerator (app, "win.restore-tab", "t"); /* Alt+N for the first 9 tabs */ for (i = 0; i < 9; ++i) { g_snprintf (detailed_action, sizeof (detailed_action), "win.go-to-tab(%i)", i); g_snprintf (accel, sizeof (accel), "%i", i + 1); nautilus_application_set_accelerator (app, detailed_action, accel); } action = g_action_map_lookup_action (G_ACTION_MAP (app), "show-hide-sidebar"); state = g_action_get_state (action); if (g_variant_get_boolean (state)) { nautilus_window_show_sidebar (window); } g_variant_unref (state); } static void nautilus_window_constructed (GObject *self) { NautilusWindow *window; NautilusWindowSlot *slot; NautilusApplication *application; window = NAUTILUS_WINDOW (self); nautilus_profile_start (NULL); G_OBJECT_CLASS (nautilus_window_parent_class)->constructed (self); application = NAUTILUS_APPLICATION (g_application_get_default ()); gtk_window_set_application (GTK_WINDOW (window), GTK_APPLICATION (application)); setup_toolbar (window); gtk_window_set_default_size (GTK_WINDOW (window), NAUTILUS_WINDOW_DEFAULT_WIDTH, NAUTILUS_WINDOW_DEFAULT_HEIGHT); setup_notebook (window); nautilus_window_set_up_sidebar (window); g_signal_connect_object (nautilus_file_undo_manager_get (), "undo-changed", G_CALLBACK (nautilus_window_on_undo_changed), self, G_CONNECT_AFTER); /* Is required that the UI is constructed before initializating the actions, since * some actions trigger UI widgets to show/hide. */ nautilus_window_initialize_actions (window); slot = nautilus_window_create_and_init_slot (window, NULL, 0); nautilus_window_set_active_slot (window, slot); window->bookmarks_id = g_signal_connect_swapped (nautilus_application_get_bookmarks (application), "changed", G_CALLBACK (nautilus_window_sync_bookmarks), window); nautilus_toolbar_on_window_constructed (NAUTILUS_TOOLBAR (window->toolbar)); nautilus_profile_end (NULL); } static gint sort_slots_active_last (NautilusWindowSlot *a, NautilusWindowSlot *b, NautilusWindow *window) { if (window->active_slot == a) { return 1; } if (window->active_slot == b) { return -1; } return 0; } static void destroy_slots_foreach (gpointer data, gpointer user_data) { NautilusWindowSlot *slot = data; NautilusWindow *window = user_data; close_slot (window, slot, TRUE); } static void nautilus_window_destroy (GtkWidget *object) { NautilusWindow *window; NautilusApplication *application; GList *slots_copy; window = NAUTILUS_WINDOW (object); application = NAUTILUS_APPLICATION (gtk_window_get_application (GTK_WINDOW (window))); DEBUG ("Destroying window"); /* close all slots safely */ slots_copy = g_list_copy (window->slots); if (window->active_slot != NULL) { /* Make sure active slot is last one to be closed, to avoid default activation * of others slots when closing the active one, see bug #741952 */ slots_copy = g_list_sort_with_data (slots_copy, (GCompareDataFunc) sort_slots_active_last, window); } g_list_foreach (slots_copy, (GFunc) destroy_slots_foreach, window); g_list_free (slots_copy); /* the slots list should now be empty */ g_assert (window->slots == NULL); window->active_slot = NULL; g_clear_signal_handler (&window->bookmarks_id, nautilus_application_get_bookmarks (application)); g_clear_handle_id (&window->in_app_notification_undo_timeout_id, g_source_remove); nautilus_window_unexport_handle (window); GTK_WIDGET_CLASS (nautilus_window_parent_class)->destroy (object); } static void nautilus_window_dispose (GObject *object) { NautilusWindow *window; window = NAUTILUS_WINDOW (object); g_clear_object (&window->notebook_multi_press_gesture); G_OBJECT_CLASS (nautilus_window_parent_class)->dispose (object); } static void nautilus_window_finalize (GObject *object) { NautilusWindow *window; window = NAUTILUS_WINDOW (object); if (window->sidebar_width_handler_id != 0) { g_source_remove (window->sidebar_width_handler_id); window->sidebar_width_handler_id = 0; } if (window->in_app_notification_undo_timeout_id != 0) { g_source_remove (window->in_app_notification_undo_timeout_id); window->in_app_notification_undo_timeout_id = 0; } if (window->notification_operation_timeout_id != 0) { g_source_remove (window->notification_operation_timeout_id); window->notification_operation_timeout_id = 0; } g_clear_object (&window->selected_file); g_clear_object (&window->selected_volume); g_signal_handlers_disconnect_by_func (nautilus_file_undo_manager_get (), G_CALLBACK (nautilus_window_on_undo_changed), window); g_queue_free_full (window->tab_data_queue, free_navigation_state); g_object_unref (window->pad_controller); /* nautilus_window_close() should have run */ g_assert (window->slots == NULL); G_OBJECT_CLASS (nautilus_window_parent_class)->finalize (object); } static void nautilus_window_save_geometry (NautilusWindow *window) { GdkWindow *gdk_window; GdkWindowState window_state; gboolean is_maximized; g_assert (NAUTILUS_IS_WINDOW (window)); gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); if (!gdk_window) { return; } window_state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window))); /* Don't save the window state for tiled windows. This is a special case, * where the geometry only makes sense in combination with other tiled * windows, that we can't possibly restore. */ if (window_state & GDK_WINDOW_STATE_TILED) { return; } is_maximized = window_state & GDK_WINDOW_STATE_MAXIMIZED; /* Only save the initial size when the window is not maximized. If the * window is maximized, a previously stored initial size will be more * appropriate when unmaximizing the window in the future. */ if (!is_maximized) { gint width; gint height; GVariant *initial_size; gtk_window_get_size (GTK_WINDOW (window), &width, &height); initial_size = g_variant_new_parsed ("(%i, %i)", width, height); g_settings_set_value (nautilus_window_state, NAUTILUS_WINDOW_STATE_INITIAL_SIZE, initial_size); } g_settings_set_boolean (nautilus_window_state, NAUTILUS_WINDOW_STATE_MAXIMIZED, is_maximized); } void nautilus_window_close (NautilusWindow *window) { g_return_if_fail (NAUTILUS_IS_WINDOW (window)); nautilus_window_save_geometry (window); nautilus_window_set_active_slot (window, NULL); gtk_widget_destroy (GTK_WIDGET (window)); } void nautilus_window_set_active_slot (NautilusWindow *window, NautilusWindowSlot *new_slot) { NautilusWindowSlot *old_slot; g_assert (NAUTILUS_IS_WINDOW (window)); if (new_slot) { g_assert ((window == nautilus_window_slot_get_window (new_slot))); } old_slot = nautilus_window_get_active_slot (window); if (old_slot == new_slot) { return; } DEBUG ("Setting new slot %p as active, old slot inactive %p", new_slot, old_slot); /* make old slot inactive if it exists (may be NULL after init, for example) */ if (old_slot != NULL) { /* inform slot & view */ nautilus_window_slot_set_active (old_slot, FALSE); nautilus_toolbar_set_window_slot (NAUTILUS_TOOLBAR (window->toolbar), NULL); } window->active_slot = new_slot; /* make new slot active, if it exists */ if (new_slot) { /* inform slot & view */ nautilus_window_slot_set_active (new_slot, TRUE); nautilus_toolbar_set_window_slot (NAUTILUS_TOOLBAR (window->toolbar), new_slot); on_location_changed (window); g_signal_emit (window, signals[ACTIVE_SELECTION_CHANGED], 0); } } static void nautilus_window_realize (GtkWidget *widget) { GTK_WIDGET_CLASS (nautilus_window_parent_class)->realize (widget); update_cursor (NAUTILUS_WINDOW (widget)); } static gboolean nautilus_window_key_press_event (GtkWidget *widget, GdkEventKey *event) { NautilusWindow *window; guint keyval; GtkWidget *focus_widget; window = NAUTILUS_WINDOW (widget); if (G_UNLIKELY (!gdk_event_get_keyval ((GdkEvent *) event, &keyval))) { g_return_val_if_reached (GDK_EVENT_PROPAGATE); } focus_widget = gtk_window_get_focus (GTK_WINDOW (window)); if (focus_widget != NULL && GTK_IS_EDITABLE (focus_widget)) { /* if we have input focus on a GtkEditable (e.g. a GtkEntry), forward * the event to it before activating accelerator bindings too. */ if (gtk_window_propagate_key_event (GTK_WINDOW (window), (GdkEventKey *) event)) { return GDK_EVENT_STOP; } } for (int i = 0; i < G_N_ELEMENTS (extra_window_keybindings); i++) { if (extra_window_keybindings[i].keyval == keyval) { GAction *action; action = g_action_map_lookup_action (G_ACTION_MAP (window), extra_window_keybindings[i].action); g_assert (action != NULL); if (g_action_get_enabled (action)) { g_action_activate (action, NULL); return GDK_EVENT_STOP; } break; } } if (GTK_WIDGET_CLASS (nautilus_window_parent_class)->key_press_event (widget, event)) { return GDK_EVENT_STOP; } if (nautilus_window_slot_handle_event (window->active_slot, (GdkEvent *) event)) { return GDK_EVENT_STOP; } return GDK_EVENT_PROPAGATE; } void nautilus_window_sync_title (NautilusWindow *window, NautilusWindowSlot *slot) { g_return_if_fail (NAUTILUS_IS_WINDOW (window)); g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot)); if (slot == nautilus_window_get_active_slot (window)) { gtk_window_set_title (GTK_WINDOW (window), nautilus_window_slot_get_title (slot)); } nautilus_notebook_sync_tab_label (NAUTILUS_NOTEBOOK (window->notebook), slot); } #ifdef GDK_WINDOWING_WAYLAND typedef struct { NautilusWindow *window; NautilusWindowHandleExported callback; gpointer user_data; } WaylandWindowHandleExportedData; static void wayland_window_handle_exported (GdkWindow *window, const char *wayland_handle_str, gpointer user_data) { WaylandWindowHandleExportedData *data = user_data; data->window->export_handle = g_strdup_printf ("wayland:%s", wayland_handle_str); data->callback (data->window, data->window->export_handle, 0, data->user_data); } #endif gboolean nautilus_window_export_handle (NautilusWindow *window, NautilusWindowHandleExported callback, gpointer user_data) { guint xid = get_window_xid (window); if (window->export_handle != NULL) { callback (window, window->export_handle, xid, user_data); return TRUE; } #ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) { window->export_handle = g_strdup_printf ("x11:%x", xid); callback (window, window->export_handle, xid, user_data); return TRUE; } #endif #ifdef GDK_WINDOWING_WAYLAND if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) { GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); WaylandWindowHandleExportedData *data; data = g_new0 (WaylandWindowHandleExportedData, 1); data->window = window; data->callback = callback; data->user_data = user_data; if (!gdk_wayland_window_export_handle (gdk_window, wayland_window_handle_exported, data, g_free)) { g_free (data); return FALSE; } else { return TRUE; } } #endif g_warning ("Couldn't export handle, unsupported windowing system"); return FALSE; } void nautilus_window_unexport_handle (NautilusWindow *window) { if (window->export_handle == NULL) { return; } #ifdef GDK_WINDOWING_WAYLAND if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window)))) { GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); if (gdk_window != NULL) { gdk_wayland_window_unexport_handle (gdk_window); } } #endif g_clear_pointer (&window->export_handle, g_free); } /** * nautilus_window_show: * @widget: GtkWidget * * Call parent and then show/hide window items * base on user prefs. */ static void nautilus_window_show (GtkWidget *widget) { GTK_WIDGET_CLASS (nautilus_window_parent_class)->show (widget); } NautilusWindowSlot * nautilus_window_get_active_slot (NautilusWindow *window) { g_assert (NAUTILUS_IS_WINDOW (window)); return window->active_slot; } GList * nautilus_window_get_slots (NautilusWindow *window) { g_assert (NAUTILUS_IS_WINDOW (window)); return window->slots; } static void on_is_maximized_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { gboolean is_maximized; is_maximized = gtk_window_is_maximized (GTK_WINDOW (object)); g_settings_set_boolean (nautilus_window_state, NAUTILUS_WINDOW_STATE_MAXIMIZED, is_maximized); } static gboolean nautilus_window_delete_event (GtkWidget *widget, GdkEventAny *event) { nautilus_window_close (NAUTILUS_WINDOW (widget)); return FALSE; } static void on_multi_press_gesture_pressed (GtkGestureMultiPress *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) { GtkWidget *widget; NautilusWindow *window; guint button; widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); window = NAUTILUS_WINDOW (widget); button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); if (mouse_extra_buttons && (button == mouse_back_button)) { nautilus_window_back_or_forward (window, TRUE, 0, 0); } else if (mouse_extra_buttons && (button == mouse_forward_button)) { nautilus_window_back_or_forward (window, FALSE, 0, 0); } } static void mouse_back_button_changed (gpointer callback_data) { int new_back_button; new_back_button = g_settings_get_int (nautilus_preferences, NAUTILUS_PREFERENCES_MOUSE_BACK_BUTTON); /* Bounds checking */ if (new_back_button < 6 || new_back_button > UPPER_MOUSE_LIMIT) { return; } mouse_back_button = new_back_button; } static void mouse_forward_button_changed (gpointer callback_data) { int new_forward_button; new_forward_button = g_settings_get_int (nautilus_preferences, NAUTILUS_PREFERENCES_MOUSE_FORWARD_BUTTON); /* Bounds checking */ if (new_forward_button < 6 || new_forward_button > UPPER_MOUSE_LIMIT) { return; } mouse_forward_button = new_forward_button; } static void use_extra_mouse_buttons_changed (gpointer callback_data) { mouse_extra_buttons = g_settings_get_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_MOUSE_USE_EXTRA_BUTTONS); } static void nautilus_window_init (NautilusWindow *window) { GtkWindowGroup *window_group; g_type_ensure (NAUTILUS_TYPE_TOOLBAR); g_type_ensure (NAUTILUS_TYPE_NOTEBOOK); gtk_widget_init_template (GTK_WIDGET (window)); g_signal_connect (window, "notify::is-maximized", G_CALLBACK (on_is_maximized_changed), NULL); g_signal_connect_object (window->in_app_notification_undo_close_button, "clicked", G_CALLBACK (on_in_app_notification_undo_close_button_clicked), window, 0); g_signal_connect_object (window->in_app_notification_undo_undo_button, "clicked", G_CALLBACK (on_in_app_notification_undo_undo_button_clicked), window, 0); window->slots = NULL; window->active_slot = NULL; gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (window)), "nautilus-window"); window->toolbar = nautilus_toolbar_new (); window_group = gtk_window_group_new (); gtk_window_group_add_window (window_group, GTK_WINDOW (window)); g_object_unref (window_group); window->tab_data_queue = g_queue_new (); window->pad_controller = gtk_pad_controller_new (GTK_WINDOW (window), G_ACTION_GROUP (window), NULL); gtk_pad_controller_set_action_entries (window->pad_controller, pad_actions, G_N_ELEMENTS (pad_actions)); window->multi_press_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (window)); gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (window->multi_press_gesture), GTK_PHASE_CAPTURE); gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (window->multi_press_gesture), 0); g_signal_connect (window->multi_press_gesture, "pressed", G_CALLBACK (on_multi_press_gesture_pressed), NULL); window->notebook_multi_press_gesture = gtk_gesture_multi_press_new (window->notebook); gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (window->notebook_multi_press_gesture), GTK_PHASE_CAPTURE); gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (window->notebook_multi_press_gesture), GDK_BUTTON_SECONDARY); } static void nautilus_window_class_init (NautilusWindowClass *class) { GObjectClass *oclass = G_OBJECT_CLASS (class); GtkWidgetClass *wclass = GTK_WIDGET_CLASS (class); oclass->dispose = nautilus_window_dispose; oclass->finalize = nautilus_window_finalize; oclass->constructed = nautilus_window_constructed; wclass->destroy = nautilus_window_destroy; wclass->show = nautilus_window_show; wclass->realize = nautilus_window_realize; wclass->key_press_event = nautilus_window_key_press_event; wclass->delete_event = nautilus_window_delete_event; wclass->grab_focus = nautilus_window_grab_focus; gtk_widget_class_set_template_from_resource (wclass, "/org/gnome/nautilus/ui/nautilus-window.ui"); gtk_widget_class_bind_template_child (wclass, NautilusWindow, content_paned); gtk_widget_class_bind_template_child (wclass, NautilusWindow, sidebar); gtk_widget_class_bind_template_child (wclass, NautilusWindow, places_sidebar); gtk_widget_class_bind_template_child (wclass, NautilusWindow, main_view); gtk_widget_class_bind_template_child (wclass, NautilusWindow, notebook); gtk_widget_class_bind_template_child (wclass, NautilusWindow, in_app_notification_undo); gtk_widget_class_bind_template_child (wclass, NautilusWindow, in_app_notification_undo_label); gtk_widget_class_bind_template_child (wclass, NautilusWindow, in_app_notification_undo_undo_button); gtk_widget_class_bind_template_child (wclass, NautilusWindow, in_app_notification_undo_close_button); gtk_widget_class_bind_template_child (wclass, NautilusWindow, notification_operation); gtk_widget_class_bind_template_child (wclass, NautilusWindow, notification_operation_label); gtk_widget_class_bind_template_child (wclass, NautilusWindow, notification_operation_open); gtk_widget_class_bind_template_child (wclass, NautilusWindow, notification_operation_close); gtk_widget_class_bind_template_callback (wclass, places_sidebar_show_other_locations_with_flags); gtk_widget_class_bind_template_callback (wclass, places_sidebar_show_starred_location); signals[SLOT_ADDED] = g_signal_new ("slot-added", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, NAUTILUS_TYPE_WINDOW_SLOT); signals[SLOT_REMOVED] = g_signal_new ("slot-removed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, NAUTILUS_TYPE_WINDOW_SLOT); signals[ACTIVE_SELECTION_CHANGED] = g_signal_new ("active-selection-changed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_MOUSE_BACK_BUTTON, G_CALLBACK (mouse_back_button_changed), NULL); g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_MOUSE_FORWARD_BUTTON, G_CALLBACK (mouse_forward_button_changed), NULL); g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_MOUSE_USE_EXTRA_BUTTONS, G_CALLBACK (use_extra_mouse_buttons_changed), NULL); gtk_widget_class_bind_template_callback (wclass, on_notification_operation_open_clicked); gtk_widget_class_bind_template_callback (wclass, on_notification_operation_close_clicked); } NautilusWindow * nautilus_window_new (GdkScreen *screen) { return g_object_new (NAUTILUS_TYPE_WINDOW, "icon-name", APPLICATION_ID, "screen", screen, NULL); } NautilusWindowOpenFlags nautilus_event_get_window_open_flags (void) { NautilusWindowOpenFlags flags = 0; GdkEvent *event; event = gtk_get_current_event (); if (event == NULL) { return flags; } if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) && (event->button.button == GDK_BUTTON_MIDDLE)) { flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB; } gdk_event_free (event); return flags; } void nautilus_window_show_about_dialog (NautilusWindow *window) { const gchar *artists[] = { "The GNOME Project", NULL }; const gchar *authors[] = { "The contributors to the Nautilus project", NULL }; const gchar *documenters[] = { "GNOME Documentation Team", "Sun Microsystems", NULL }; g_autofree gchar *program_name = NULL; /* “Files” is the generic application name and the suffix is * an arbitrary and deliberately unlocalized string only shown * in development builds. */ program_name = g_strconcat (_("Files"), NAME_SUFFIX, NULL); gtk_show_about_dialog (window ? GTK_WINDOW (window) : NULL, "program-name", program_name, "version", VERSION, "comments", _("Access and organize your files"), "website", "https://wiki.gnome.org/action/show/Apps/Files", "copyright", "© 1999 The Files Authors", "license-type", GTK_LICENSE_GPL_3_0, "artists", artists, "authors", authors, "documenters", documenters, /* Translators should localize the following string * which will be displayed at the bottom of the about * box to give credit to the translator(s). */ "translator-credits", _("translator-credits"), "logo-icon-name", APPLICATION_ID, NULL); } void nautilus_window_search (NautilusWindow *window, NautilusQuery *query) { NautilusWindowSlot *active_slot; active_slot = nautilus_window_get_active_slot (window); if (active_slot) { nautilus_window_slot_search (active_slot, query); } else { g_warning ("Trying search on a slot but no active slot present"); } } /* Ideally, this method should be a simple wrapper for the slot method. However, * going back or forward can result in a new slot (or another subclass), so we * workaround that by duplicating part of nautilus_window_slot_back_or_forward() */ void nautilus_window_back_or_forward (NautilusWindow *window, gboolean back, guint distance, NautilusWindowOpenFlags flags) { NautilusWindowSlot *slot; GList *next_location_list, *back_list, *forward_list; g_autoptr (GFile) next_location = NULL; guint len; NautilusBookmark *next_location_bookmark; gboolean active_slot_handles_location; slot = nautilus_window_get_active_slot (window); back_list = nautilus_window_slot_get_back_history (slot); forward_list = nautilus_window_slot_get_forward_history (slot); next_location_list = back ? back_list : forward_list; len = (guint) g_list_length (next_location_list); /* If we can't move in the direction at all, just return. */ if (len == 0) { return; } /* If the distance to move is off the end of the list, go to the end * of the list. */ if (distance >= len) { distance = len - 1; } next_location_bookmark = g_list_nth_data (next_location_list, distance); next_location = nautilus_bookmark_get_location (next_location_bookmark); active_slot_handles_location = nautilus_window_slot_handles_location (slot, next_location); if (!active_slot_handles_location) { NautilusNavigationState *navigation_state; NautilusLocationChangeType location_change_type; navigation_state = nautilus_window_slot_get_navigation_state (slot); location_change_type = back ? NAUTILUS_LOCATION_CHANGE_BACK : NAUTILUS_LOCATION_CHANGE_FORWARD; slot = replace_active_slot (window, next_location, flags); nautilus_window_slot_open_location_set_navigation_state (slot, next_location, flags, NULL, location_change_type, navigation_state, distance); free_navigation_state (navigation_state); } else { nautilus_window_slot_back_or_forward (slot, back, distance, flags); } }