/* fm-canvas-view.c - implementation of canvas view of directory. Copyright (C) 2000, 2001 Eazel, Inc. The Gnome Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The Gnome Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the Gnome Library; see the file COPYING.LIB. If not, see . Authors: John Sullivan */ #include #include "nautilus-canvas-view.h" #include "nautilus-canvas-view-container.h" #include "nautilus-error-reporting.h" #include "nautilus-files-view-dnd.h" #include "nautilus-toolbar.h" #include #include #include #include #include #include #include "nautilus-clipboard-monitor.h" #include "nautilus-directory.h" #include "nautilus-dnd.h" #include "nautilus-file-utilities.h" #include "nautilus-ui-utilities.h" #include "nautilus-global-preferences.h" #include "nautilus-canvas-container.h" #include "nautilus-canvas-dnd.h" #include "nautilus-link.h" #include "nautilus-metadata.h" #include "nautilus-clipboard.h" #define DEBUG_FLAG NAUTILUS_DEBUG_CANVAS_VIEW #include "nautilus-debug.h" #include #include #include #include #include #include enum { PROP_SUPPORTS_AUTO_LAYOUT = 1, PROP_SUPPORTS_SCALING, PROP_SUPPORTS_KEEP_ALIGNED, PROP_SUPPORTS_MANUAL_LAYOUT, NUM_PROPERTIES }; static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; typedef gboolean (* SortCriterionMatchFunc) (NautilusFile *file); typedef struct { const NautilusFileSortType sort_type; const char *metadata_text; const char *action_target_name; SortCriterionMatchFunc match_func; } SortCriterion; typedef enum { MENU_ITEM_TYPE_STANDARD, MENU_ITEM_TYPE_CHECK, MENU_ITEM_TYPE_RADIO, MENU_ITEM_TYPE_TREE } MenuItemType; struct NautilusCanvasViewDetails { GList *icons_not_positioned; guint react_to_canvas_change_idle_id; const SortCriterion *sort; gboolean sort_reversed; gulong clipboard_handler_id; GtkWidget *canvas_container; gboolean supports_auto_layout; gboolean supports_manual_layout; gboolean supports_scaling; gboolean supports_keep_aligned; GIcon *icon; }; /* Note that the first item in this list is the default sort, * and that the items show up in the menu in the order they * appear in this list. */ static const SortCriterion sort_criteria[] = { { NAUTILUS_FILE_SORT_BY_DISPLAY_NAME, "name", "name" }, { NAUTILUS_FILE_SORT_BY_SIZE, "size", "size" }, { NAUTILUS_FILE_SORT_BY_TYPE, "type", "type" }, { NAUTILUS_FILE_SORT_BY_MTIME, "modification date", "modification-date", }, { NAUTILUS_FILE_SORT_BY_ATIME, "access date", "access-date", }, { NAUTILUS_FILE_SORT_BY_TRASHED_TIME, "trashed", "trash-time", nautilus_file_is_in_trash }, { NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE, NULL, "search-relevance", nautilus_file_is_in_search } }; static void nautilus_canvas_view_set_directory_sort_by (NautilusCanvasView *canvas_view, NautilusFile *file, const char *sort_by); static void nautilus_canvas_view_update_click_mode (NautilusCanvasView *canvas_view); static gboolean nautilus_canvas_view_supports_scaling (NautilusCanvasView *canvas_view); static void nautilus_canvas_view_reveal_selection (NautilusFilesView *view); static const SortCriterion *get_sort_criterion_by_sort_type (NautilusFileSortType sort_type); static gboolean set_sort_reversed (NautilusCanvasView *canvas_view, gboolean new_value, gboolean set_metadata); static void switch_to_manual_layout (NautilusCanvasView *view); static NautilusFileSortType get_default_sort_order (NautilusFile *file, gboolean *reversed); static void nautilus_canvas_view_clear (NautilusFilesView *view); G_DEFINE_TYPE (NautilusCanvasView, nautilus_canvas_view, NAUTILUS_TYPE_FILES_VIEW); static void nautilus_canvas_view_destroy (GtkWidget *object) { NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (object); nautilus_canvas_view_clear (NAUTILUS_FILES_VIEW (object)); if (canvas_view->details->react_to_canvas_change_idle_id != 0) { g_source_remove (canvas_view->details->react_to_canvas_change_idle_id); canvas_view->details->react_to_canvas_change_idle_id = 0; } if (canvas_view->details->clipboard_handler_id != 0) { g_signal_handler_disconnect (nautilus_clipboard_monitor_get (), canvas_view->details->clipboard_handler_id); canvas_view->details->clipboard_handler_id = 0; } if (canvas_view->details->icons_not_positioned) { nautilus_file_list_free (canvas_view->details->icons_not_positioned); canvas_view->details->icons_not_positioned = NULL; } GTK_WIDGET_CLASS (nautilus_canvas_view_parent_class)->destroy (object); } static NautilusCanvasContainer * get_canvas_container (NautilusCanvasView *canvas_view) { return NAUTILUS_CANVAS_CONTAINER (canvas_view->details->canvas_container); } NautilusCanvasContainer * nautilus_canvas_view_get_canvas_container (NautilusCanvasView *canvas_view) { return get_canvas_container (canvas_view); } static gboolean nautilus_canvas_view_supports_manual_layout (NautilusCanvasView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE); return view->details->supports_manual_layout; } static gboolean get_stored_icon_position_callback (NautilusCanvasContainer *container, NautilusFile *file, NautilusCanvasPosition *position, NautilusCanvasView *canvas_view) { char *position_string, *scale_string; gboolean position_good; char c; g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container)); g_assert (NAUTILUS_IS_FILE (file)); g_assert (position != NULL); g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); if (!nautilus_canvas_view_supports_manual_layout (canvas_view)) { return FALSE; } /* Get the current position of this canvas from the metadata. */ position_string = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_ICON_POSITION, ""); position_good = sscanf (position_string, " %d , %d %c", &position->x, &position->y, &c) == 2; g_free (position_string); /* If it is the desktop directory, maybe the gnome-libs metadata has information about it */ /* Disable scaling if not on the desktop */ if (nautilus_canvas_view_supports_scaling (canvas_view)) { /* Get the scale of the canvas from the metadata. */ scale_string = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_ICON_SCALE, "1"); position->scale = g_ascii_strtod (scale_string, NULL); if (errno != 0) { position->scale = 1.0; } g_free (scale_string); } else { position->scale = 1.0; } return position_good; } static void update_sort_criterion (NautilusCanvasView *canvas_view, const SortCriterion *sort, gboolean set_metadata) { NautilusFile *file; const SortCriterion *overrided_sort_criterion; gboolean overrided_reversed; file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view)); /* Make sure we use the default one and not one that the user used previously * of the change to not allow sorting on search and recent, or the * case that the user or some app modified directly the metadata */ if (nautilus_file_is_in_search (file) || nautilus_file_is_in_recent (file)) { overrided_sort_criterion = get_sort_criterion_by_sort_type (get_default_sort_order (file, NULL)); nautilus_file_get_default_sort_attribute (file, &overrided_reversed); set_sort_reversed (canvas_view, overrided_reversed, FALSE); } else if (sort != NULL && canvas_view->details->sort != sort) { overrided_sort_criterion = sort; if (set_metadata) { /* Store the new sort setting. */ nautilus_canvas_view_set_directory_sort_by (canvas_view, file, sort->metadata_text); } } else { return; } canvas_view->details->sort = overrided_sort_criterion; } void nautilus_canvas_view_clean_up_by_name (NautilusCanvasView *canvas_view) { NautilusCanvasContainer *canvas_container; gboolean saved_sort_reversed; canvas_container = get_canvas_container (canvas_view); /* Hardwire Clean Up to always be by name, in forward order */ saved_sort_reversed = canvas_view->details->sort_reversed; set_sort_reversed (canvas_view, FALSE, FALSE); update_sort_criterion (canvas_view, &sort_criteria[0], FALSE); nautilus_canvas_container_sort (canvas_container); nautilus_canvas_container_freeze_icon_positions (canvas_container); set_sort_reversed (canvas_view, saved_sort_reversed, FALSE); } static gboolean nautilus_canvas_view_using_auto_layout (NautilusCanvasView *canvas_view) { return nautilus_canvas_container_is_auto_layout (get_canvas_container (canvas_view)); } static void list_covers (NautilusCanvasIconData *data, gpointer callback_data) { GSList **file_list; file_list = callback_data; *file_list = g_slist_prepend (*file_list, data); } static void unref_cover (NautilusCanvasIconData *data, gpointer callback_data) { nautilus_file_unref (NAUTILUS_FILE (data)); } static void nautilus_canvas_view_clear (NautilusFilesView *view) { NautilusCanvasContainer *canvas_container; GSList *file_list; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view)); if (!canvas_container) return; /* Clear away the existing icons. */ file_list = NULL; nautilus_canvas_container_for_each (canvas_container, list_covers, &file_list); nautilus_canvas_container_clear (canvas_container); g_slist_foreach (file_list, (GFunc)unref_cover, NULL); g_slist_free (file_list); } static void nautilus_canvas_view_remove_file (NautilusFilesView *view, NautilusFile *file, NautilusDirectory *directory) { NautilusCanvasView *canvas_view; /* This used to assert that 'directory == nautilus_files_view_get_model (view)', but that * resulted in a lot of crash reports (bug #352592). I don't see how that trace happens. * It seems that somehow we get a files_changed event sent to the view from a directory * that isn't the model, but the code disables the monitor and signal callback handlers when * changing directories. Maybe we can get some more information when this happens. * Further discussion in bug #368178. */ if (directory != nautilus_files_view_get_model (view)) { char *file_uri, *dir_uri, *model_uri; file_uri = nautilus_file_get_uri (file); dir_uri = nautilus_directory_get_uri (directory); model_uri = nautilus_directory_get_uri (nautilus_files_view_get_model (view)); g_warning ("nautilus_canvas_view_remove_file() - directory not canvas view model, shouldn't happen.\n" "file: %p:%s, dir: %p:%s, model: %p:%s, view loading: %d\n" "If you see this, please add this info to http://bugzilla.gnome.org/show_bug.cgi?id=368178", file, file_uri, directory, dir_uri, nautilus_files_view_get_model (view), model_uri, nautilus_files_view_get_loading (view)); g_free (file_uri); g_free (dir_uri); g_free (model_uri); } canvas_view = NAUTILUS_CANVAS_VIEW (view); if (nautilus_canvas_container_remove (get_canvas_container (canvas_view), NAUTILUS_CANVAS_ICON_DATA (file))) { nautilus_file_unref (file); } } static void nautilus_canvas_view_add_file (NautilusFilesView *view, NautilusFile *file, NautilusDirectory *directory) { NautilusCanvasView *canvas_view; NautilusCanvasContainer *canvas_container; g_assert (directory == nautilus_files_view_get_model (view)); canvas_view = NAUTILUS_CANVAS_VIEW (view); canvas_container = get_canvas_container (canvas_view); /* Reset scroll region for the first canvas added when loading a directory. */ if (nautilus_files_view_get_loading (view) && nautilus_canvas_container_is_empty (canvas_container)) { nautilus_canvas_container_reset_scroll_region (canvas_container); } if (nautilus_canvas_container_add (canvas_container, NAUTILUS_CANVAS_ICON_DATA (file))) { nautilus_file_ref (file); } } static void nautilus_canvas_view_file_changed (NautilusFilesView *view, NautilusFile *file, NautilusDirectory *directory) { NautilusCanvasView *canvas_view; g_assert (directory == nautilus_files_view_get_model (view)); g_return_if_fail (view != NULL); canvas_view = NAUTILUS_CANVAS_VIEW (view); nautilus_canvas_container_request_update (get_canvas_container (canvas_view), NAUTILUS_CANVAS_ICON_DATA (file)); } static gboolean nautilus_canvas_view_supports_auto_layout (NautilusCanvasView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE); return view->details->supports_auto_layout; } static gboolean nautilus_canvas_view_supports_scaling (NautilusCanvasView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE); return view->details->supports_scaling; } static gboolean nautilus_canvas_view_supports_keep_aligned (NautilusCanvasView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE); return view->details->supports_keep_aligned; } static char * nautilus_canvas_view_get_directory_sort_by (NautilusCanvasView *canvas_view, NautilusFile *file) { const SortCriterion *default_sort_criterion; if (!nautilus_canvas_view_supports_auto_layout (canvas_view)) { return g_strdup ("name"); } default_sort_criterion = get_sort_criterion_by_sort_type (get_default_sort_order (file, NULL)); g_return_val_if_fail (default_sort_criterion != NULL, NULL); return nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY, default_sort_criterion->metadata_text); } static NautilusFileSortType get_default_sort_order (NautilusFile *file, gboolean *reversed) { NautilusFileSortType retval, default_sort_order; gboolean default_sort_in_reverse_order; default_sort_order = g_settings_get_enum (nautilus_preferences, NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER); default_sort_in_reverse_order = g_settings_get_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER); retval = nautilus_file_get_default_sort_type (file, reversed); if (retval == NAUTILUS_FILE_SORT_NONE) { if (reversed != NULL) { *reversed = default_sort_in_reverse_order; } retval = CLAMP (default_sort_order, NAUTILUS_FILE_SORT_BY_DISPLAY_NAME, NAUTILUS_FILE_SORT_BY_ATIME); } return retval; } static void nautilus_canvas_view_set_directory_sort_by (NautilusCanvasView *canvas_view, NautilusFile *file, const char *sort_by) { const SortCriterion *default_sort_criterion; if (!nautilus_canvas_view_supports_auto_layout (canvas_view)) { return; } default_sort_criterion = get_sort_criterion_by_sort_type (get_default_sort_order (file, NULL)); g_return_if_fail (default_sort_criterion != NULL); nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY, default_sort_criterion->metadata_text, sort_by); } static gboolean nautilus_canvas_view_get_directory_sort_reversed (NautilusCanvasView *canvas_view, NautilusFile *file) { gboolean reversed; if (!nautilus_canvas_view_supports_auto_layout (canvas_view)) { return FALSE; } get_default_sort_order (file, &reversed); return nautilus_file_get_boolean_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED, reversed); } static void nautilus_canvas_view_set_directory_sort_reversed (NautilusCanvasView *canvas_view, NautilusFile *file, gboolean sort_reversed) { gboolean reversed; if (!nautilus_canvas_view_supports_auto_layout (canvas_view)) { return; } get_default_sort_order (file, &reversed); nautilus_file_set_boolean_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED, reversed, sort_reversed); } static gboolean get_default_directory_keep_aligned (void) { return TRUE; } static gboolean nautilus_canvas_view_get_directory_keep_aligned (NautilusCanvasView *canvas_view, NautilusFile *file) { if (!nautilus_canvas_view_supports_keep_aligned (canvas_view)) { return FALSE; } return nautilus_file_get_boolean_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_KEEP_ALIGNED, get_default_directory_keep_aligned ()); } static void nautilus_canvas_view_set_directory_keep_aligned (NautilusCanvasView *canvas_view, NautilusFile *file, gboolean keep_aligned) { if (!nautilus_canvas_view_supports_keep_aligned (canvas_view)) { return; } nautilus_file_set_boolean_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_KEEP_ALIGNED, get_default_directory_keep_aligned (), keep_aligned); } static gboolean nautilus_canvas_view_get_directory_auto_layout (NautilusCanvasView *canvas_view, NautilusFile *file) { if (!nautilus_canvas_view_supports_auto_layout (canvas_view)) { return FALSE; } if (!nautilus_canvas_view_supports_manual_layout (canvas_view)) { return TRUE; } return nautilus_file_get_boolean_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_AUTO_LAYOUT, TRUE); } static void nautilus_canvas_view_set_directory_auto_layout (NautilusCanvasView *canvas_view, NautilusFile *file, gboolean auto_layout) { if (!nautilus_canvas_view_supports_auto_layout (canvas_view) || !nautilus_canvas_view_supports_manual_layout (canvas_view)) { return; } nautilus_file_set_boolean_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_AUTO_LAYOUT, TRUE, auto_layout); } static gboolean set_sort_reversed (NautilusCanvasView *canvas_view, gboolean new_value, gboolean set_metadata) { if (canvas_view->details->sort_reversed == new_value) { return FALSE; } canvas_view->details->sort_reversed = new_value; if (set_metadata) { /* Store the new sort setting. */ nautilus_canvas_view_set_directory_sort_reversed (canvas_view, nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view)), new_value); } return TRUE; } static const SortCriterion * get_sort_criterion_by_metadata_text (const char *metadata_text) { guint i; /* Figure out what the new sort setting should be. */ for (i = 0; i < G_N_ELEMENTS (sort_criteria); i++) { if (g_strcmp0 (sort_criteria[i].metadata_text, metadata_text) == 0) { return &sort_criteria[i]; } } return NULL; } static const SortCriterion * get_sort_criterion_by_action_target_name (const char *action_target_name) { guint i; /* Figure out what the new sort setting should be. */ for (i = 0; i < G_N_ELEMENTS (sort_criteria); i++) { if (g_strcmp0 (sort_criteria[i].action_target_name, action_target_name) == 0) { return &sort_criteria[i]; } } return NULL; } static const SortCriterion * get_sort_criterion_by_sort_type (NautilusFileSortType sort_type) { guint i; /* Figure out what the new sort setting should be. */ for (i = 0; i < G_N_ELEMENTS (sort_criteria); i++) { if (sort_type == sort_criteria[i].sort_type) { return &sort_criteria[i]; } } return &sort_criteria[0]; } static NautilusCanvasZoomLevel get_default_zoom_level (NautilusCanvasView *canvas_view) { NautilusCanvasZoomLevel default_zoom_level; default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences, NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL); return CLAMP (default_zoom_level, NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL, NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER); } static void nautilus_canvas_view_begin_loading (NautilusFilesView *view) { NautilusCanvasView *canvas_view; GtkWidget *canvas_container; NautilusFile *file; char *sort_name, *uri; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); canvas_view = NAUTILUS_CANVAS_VIEW (view); file = nautilus_files_view_get_directory_as_file (view); uri = nautilus_file_get_uri (file); canvas_container = GTK_WIDGET (get_canvas_container (canvas_view)); nautilus_canvas_container_begin_loading (NAUTILUS_CANVAS_CONTAINER (canvas_container)); g_free (uri); /* Set the sort mode. * It's OK not to resort the icons because the * container doesn't have any icons at this point. */ sort_name = nautilus_canvas_view_get_directory_sort_by (canvas_view, file); update_sort_criterion (canvas_view, get_sort_criterion_by_metadata_text (sort_name), FALSE); g_free (sort_name); /* Set the sort direction from the metadata. */ set_sort_reversed (canvas_view, nautilus_canvas_view_get_directory_sort_reversed (canvas_view, file), FALSE); nautilus_canvas_container_set_keep_aligned (get_canvas_container (canvas_view), nautilus_canvas_view_get_directory_keep_aligned (canvas_view, file)); /* We must set auto-layout last, because it invokes the layout_changed * callback, which works incorrectly if the other layout criteria are * not already set up properly (see bug 6500, e.g.) */ nautilus_canvas_container_set_auto_layout (get_canvas_container (canvas_view), nautilus_canvas_view_get_directory_auto_layout (canvas_view, file)); /* We could have changed to the trash directory or to searching, and then * we need to update the menus */ nautilus_files_view_update_context_menus(view); nautilus_files_view_update_toolbar_menus(view); } static void canvas_view_notify_clipboard_info (NautilusClipboardMonitor *monitor, NautilusClipboardInfo *info, NautilusCanvasView *canvas_view) { GList *icon_data; icon_data = NULL; if (info && info->cut) { icon_data = info->files; } nautilus_canvas_container_set_highlighted_for_clipboard ( get_canvas_container (canvas_view), icon_data); } static void nautilus_canvas_view_end_loading (NautilusFilesView *view, gboolean all_files_seen) { NautilusCanvasView *canvas_view; GtkWidget *canvas_container; NautilusClipboardMonitor *monitor; NautilusClipboardInfo *info; canvas_view = NAUTILUS_CANVAS_VIEW (view); canvas_container = GTK_WIDGET (get_canvas_container (canvas_view)); nautilus_canvas_container_end_loading (NAUTILUS_CANVAS_CONTAINER (canvas_container), all_files_seen); monitor = nautilus_clipboard_monitor_get (); info = nautilus_clipboard_monitor_get_clipboard_info (monitor); canvas_view_notify_clipboard_info (monitor, info, canvas_view); } static NautilusCanvasZoomLevel nautilus_canvas_view_get_zoom_level (NautilusFilesView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD); return nautilus_canvas_container_get_zoom_level (get_canvas_container (NAUTILUS_CANVAS_VIEW (view))); } static void nautilus_canvas_view_zoom_to_level (NautilusFilesView *view, gint new_level) { NautilusCanvasView *canvas_view; NautilusCanvasContainer *canvas_container; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); g_return_if_fail (new_level >= NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL && new_level <= NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER); canvas_view = NAUTILUS_CANVAS_VIEW (view); canvas_container = get_canvas_container (canvas_view); if (nautilus_canvas_container_get_zoom_level (canvas_container) == new_level) return; nautilus_canvas_container_set_zoom_level (canvas_container, new_level); g_action_group_change_action_state (nautilus_files_view_get_action_group (view), "zoom-to-level", g_variant_new_int32 (new_level)); nautilus_files_view_update_toolbar_menus (view); } static void nautilus_canvas_view_bump_zoom_level (NautilusFilesView *view, int zoom_increment) { NautilusCanvasZoomLevel new_level; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); if (!nautilus_files_view_supports_zooming (view)) { return; } new_level = nautilus_canvas_view_get_zoom_level (view) + zoom_increment; if (new_level >= NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL && new_level <= NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER) { nautilus_canvas_view_zoom_to_level (view, new_level); } } static void nautilus_canvas_view_restore_default_zoom_level (NautilusFilesView *view) { NautilusCanvasView *canvas_view; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); canvas_view = NAUTILUS_CANVAS_VIEW (view); nautilus_canvas_view_zoom_to_level (view, get_default_zoom_level (canvas_view)); } static gboolean nautilus_canvas_view_can_zoom_in (NautilusFilesView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE); return nautilus_canvas_view_get_zoom_level (view) < NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER; } static gboolean nautilus_canvas_view_can_zoom_out (NautilusFilesView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE); return nautilus_canvas_view_get_zoom_level (view) > NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL; } static gboolean nautilus_canvas_view_is_empty (NautilusFilesView *view) { g_assert (NAUTILUS_IS_CANVAS_VIEW (view)); return nautilus_canvas_container_is_empty (get_canvas_container (NAUTILUS_CANVAS_VIEW (view))); } static GList * nautilus_canvas_view_get_selection (NautilusFilesView *view) { GList *list; g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NULL); list = nautilus_canvas_container_get_selection (get_canvas_container (NAUTILUS_CANVAS_VIEW (view))); nautilus_file_list_ref (list); return list; } static void action_reversed_order (GSimpleAction *action, GVariant *state, gpointer user_data) { gboolean reversed_order; g_assert (NAUTILUS_IS_CANVAS_VIEW (user_data)); reversed_order = g_variant_get_boolean (state); if (set_sort_reversed (user_data, reversed_order, TRUE)) { nautilus_canvas_container_sort (get_canvas_container (user_data)); nautilus_canvas_view_reveal_selection (NAUTILUS_FILES_VIEW (user_data)); } g_simple_action_set_state (action, state); } static void action_keep_aligned (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusFile *file; NautilusCanvasView *canvas_view; gboolean keep_aligned; canvas_view = NAUTILUS_CANVAS_VIEW (user_data); file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view)); keep_aligned = g_variant_get_boolean (state); nautilus_canvas_view_set_directory_keep_aligned (canvas_view, file, keep_aligned); nautilus_canvas_container_set_keep_aligned (get_canvas_container (canvas_view), keep_aligned); g_simple_action_set_state (action, state); } static void action_sort_order_changed (GSimpleAction *action, GVariant *value, gpointer user_data) { const gchar *target_name; const SortCriterion *sort_criterion; g_assert (NAUTILUS_IS_CANVAS_VIEW (user_data)); target_name = g_variant_get_string (value, NULL); sort_criterion = get_sort_criterion_by_action_target_name (target_name); g_assert (sort_criterion != NULL); /* Note that id might be a toggle item. * Ignore non-sort ids so that they don't cause sorting. */ if (sort_criterion->sort_type == NAUTILUS_FILE_SORT_NONE) { switch_to_manual_layout (user_data); } else { update_sort_criterion (user_data, sort_criterion, TRUE); nautilus_canvas_container_sort (get_canvas_container (user_data)); nautilus_canvas_view_reveal_selection (NAUTILUS_FILES_VIEW (user_data)); } g_simple_action_set_state (action, value); } static void action_zoom_to_level (GSimpleAction *action, GVariant *state, gpointer user_data) { NautilusFilesView *view; NautilusCanvasZoomLevel zoom_level; g_assert (NAUTILUS_IS_FILES_VIEW (user_data)); view = NAUTILUS_FILES_VIEW (user_data); zoom_level = g_variant_get_int32 (state); nautilus_canvas_view_zoom_to_level (view, zoom_level); g_simple_action_set_state (G_SIMPLE_ACTION (action), state); g_settings_set_enum (nautilus_icon_view_preferences, NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL, zoom_level); } static void switch_to_manual_layout (NautilusCanvasView *canvas_view) { if (!nautilus_canvas_view_using_auto_layout (canvas_view) || !nautilus_files_view_is_editable (NAUTILUS_FILES_VIEW (canvas_view))) { return; } canvas_view->details->sort = &sort_criteria[0]; nautilus_canvas_container_set_auto_layout (get_canvas_container (canvas_view), FALSE); } static void layout_changed_callback (NautilusCanvasContainer *container, NautilusCanvasView *canvas_view) { NautilusFile *file; g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view)); if (file != NULL) { nautilus_canvas_view_set_directory_auto_layout (canvas_view, file, nautilus_canvas_view_using_auto_layout (canvas_view)); } } const GActionEntry canvas_view_entries[] = { { "keep-aligned", NULL, NULL, "true", action_keep_aligned }, { "reversed-order", NULL, NULL, "false", action_reversed_order }, { "sort", NULL, "s", "'name'", action_sort_order_changed }, { "zoom-to-level", NULL, NULL, "1", action_zoom_to_level } }; static void update_sort_action_state_hint (NautilusCanvasView *canvas_view) { NautilusFile *file; GVariantBuilder builder; GActionGroup *action_group; GAction *action; GVariant *state_hint; gint idx; file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view)); g_variant_builder_init (&builder, G_VARIANT_TYPE ("as")); for (idx = 0; idx < G_N_ELEMENTS (sort_criteria); idx++) { if (sort_criteria[idx].match_func == NULL || (file != NULL && sort_criteria[idx].match_func (file))) g_variant_builder_add (&builder, "s", sort_criteria[idx].action_target_name); } state_hint = g_variant_builder_end (&builder); action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (canvas_view)); action = g_action_map_lookup_action (G_ACTION_MAP (action_group), "sort"); g_simple_action_set_state_hint (G_SIMPLE_ACTION (action), state_hint); g_variant_unref (state_hint); } static gboolean showing_recent_directory (NautilusFilesView *view) { NautilusFile *file; file = nautilus_files_view_get_directory_as_file (view); if (file != NULL) { return nautilus_file_is_in_recent (file); } return FALSE; } static gboolean showing_search_directory (NautilusFilesView *view) { NautilusFile *file; file = nautilus_files_view_get_directory_as_file (view); if (file != NULL) { return nautilus_file_is_in_search (file); } return FALSE; } static void nautilus_canvas_view_update_actions_state (NautilusFilesView *view) { GActionGroup *view_action_group; GAction *action; gboolean keep_aligned; NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (view); NAUTILUS_FILES_VIEW_CLASS (nautilus_canvas_view_parent_class)->update_actions_state (view); view_action_group = nautilus_files_view_get_action_group (view); if (nautilus_canvas_view_supports_auto_layout (canvas_view)) { GVariant *sort_state; GVariant *reversed_state; /* When we change the sort action state, even using the same value, it triggers * the sort action changed handler, which reveals the selection, since we expect * the selection to be visible when the user changes the sort order. But we may * need to update the actions state for others reason than an actual sort change, * so we need to prevent to trigger the sort action changed handler for those cases. * To achieve this, check if the action state value actually changed before setting * it. The same happens to the reversed-order state. */ sort_state = g_action_group_get_action_state (view_action_group, "sort"); reversed_state = g_action_group_get_action_state (view_action_group, "reversed-order"); if (NAUTILUS_CANVAS_VIEW (view)->details->sort_reversed != g_variant_get_boolean (reversed_state)) { g_action_group_change_action_state (view_action_group, "reversed-order", g_variant_new_boolean (NAUTILUS_CANVAS_VIEW (view)->details->sort_reversed)); } if (g_strcmp0 (g_variant_get_string (sort_state, NULL), NAUTILUS_CANVAS_VIEW (view)->details->sort->action_target_name) != 0) { g_action_group_change_action_state (view_action_group, "sort", g_variant_new_string (NAUTILUS_CANVAS_VIEW (view)->details->sort->action_target_name)); } g_variant_unref (reversed_state); g_variant_unref (sort_state); } action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !showing_recent_directory (view) && !showing_search_directory (view)); action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "keep-aligned"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), canvas_view->details->supports_keep_aligned); if (canvas_view->details->supports_keep_aligned) { keep_aligned = nautilus_canvas_container_is_keep_aligned (get_canvas_container (canvas_view)); g_action_change_state (action, g_variant_new_boolean (keep_aligned)); } update_sort_action_state_hint (canvas_view); } static void nautilus_canvas_view_select_all (NautilusFilesView *view) { NautilusCanvasContainer *canvas_container; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view)); nautilus_canvas_container_select_all (canvas_container); } static void nautilus_canvas_view_select_first (NautilusFilesView *view) { NautilusCanvasContainer *canvas_container; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view)); nautilus_canvas_container_select_first (canvas_container); } static void nautilus_canvas_view_reveal_selection (NautilusFilesView *view) { GList *selection; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); selection = nautilus_view_get_selection (NAUTILUS_VIEW (view)); /* Make sure at least one of the selected items is scrolled into view */ if (selection != NULL) { nautilus_canvas_container_reveal (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)), selection->data); } nautilus_file_list_free (selection); } static GdkRectangle* nautilus_canvas_view_compute_rename_popover_relative_to (NautilusFilesView *view) { GArray *bounding_boxes; GdkRectangle *bounding_box; NautilusCanvasContainer *canvas_container; GtkAdjustment *vadjustment, *hadjustment; GtkWidget *parent_container; canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view)); bounding_boxes = nautilus_canvas_container_get_selected_icons_bounding_box (canvas_container); /* We only allow renaming one item at once */ bounding_box = &g_array_index (bounding_boxes, GdkRectangle, 0); parent_container = nautilus_files_view_get_content_widget (view); vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (parent_container)); hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (parent_container)); bounding_box->x -= gtk_adjustment_get_value (hadjustment); bounding_box->y -= gtk_adjustment_get_value (vadjustment); g_array_free (bounding_boxes, FALSE); return bounding_box; } static void nautilus_canvas_view_set_selection (NautilusFilesView *view, GList *selection) { g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); nautilus_canvas_container_set_selection (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)), selection); } static void nautilus_canvas_view_invert_selection (NautilusFilesView *view) { g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (view)); nautilus_canvas_container_invert_selection (get_canvas_container (NAUTILUS_CANVAS_VIEW (view))); } static gboolean nautilus_canvas_view_using_manual_layout (NautilusFilesView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), FALSE); return !nautilus_canvas_view_using_auto_layout (NAUTILUS_CANVAS_VIEW (view)); } static void nautilus_canvas_view_widget_to_file_operation_position (NautilusFilesView *view, GdkPoint *position) { g_assert (NAUTILUS_IS_CANVAS_VIEW (view)); nautilus_canvas_container_widget_to_file_operation_position (get_canvas_container (NAUTILUS_CANVAS_VIEW (view)), position); } static void canvas_container_activate_callback (NautilusCanvasContainer *container, GList *file_list, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (canvas_view), file_list, 0, TRUE); } static void canvas_container_activate_previewer_callback (NautilusCanvasContainer *container, GList *file_list, GArray *locations, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); nautilus_files_view_preview_files (NAUTILUS_FILES_VIEW (canvas_view), file_list, locations); } /* this is called in one of these cases: * - we activate with enter holding shift * - we activate with space holding shift * - we double click an canvas holding shift * - we middle click an canvas * * If we don't open in new windows by default, the behavior should be * - middle click, shift + activate -> open in new tab * - shift + double click -> open in new window * * If we open in new windows by default, the behaviour should be * - middle click, or shift + activate, or shift + double-click -> close parent */ static void canvas_container_activate_alternate_callback (NautilusCanvasContainer *container, GList *file_list, NautilusCanvasView *canvas_view) { GdkEvent *event; GdkEventButton *button_event; GdkEventKey *key_event; gboolean open_in_tab, open_in_window, close_behind; NautilusWindowOpenFlags flags; g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); flags = 0; event = gtk_get_current_event (); open_in_tab = FALSE; open_in_window = FALSE; close_behind = FALSE; if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE || event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) { button_event = (GdkEventButton *) event; open_in_window = ((button_event->state & GDK_SHIFT_MASK) != 0); open_in_tab = !open_in_window; } else if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) { key_event = (GdkEventKey *) event; open_in_tab = ((key_event->state & GDK_SHIFT_MASK) != 0); } if (open_in_tab) { flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB; flags |= NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE; } if (open_in_window) { flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW; } if (close_behind) { flags |= NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND; } DEBUG ("Activate alternate, open in tab %d, close behind %d, new window %d\n", open_in_tab, close_behind, open_in_window); nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (canvas_view), file_list, flags, TRUE); } static void band_select_started_callback (NautilusCanvasContainer *container, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); nautilus_files_view_start_batching_selection_changes (NAUTILUS_FILES_VIEW (canvas_view)); } static void band_select_ended_callback (NautilusCanvasContainer *container, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); nautilus_files_view_stop_batching_selection_changes (NAUTILUS_FILES_VIEW (canvas_view)); } int nautilus_canvas_view_compare_files (NautilusCanvasView *canvas_view, NautilusFile *a, NautilusFile *b) { return nautilus_file_compare_for_sort (a, b, canvas_view->details->sort->sort_type, /* Use type-unsafe cast for performance */ nautilus_files_view_should_sort_directories_first ((NautilusFilesView *)canvas_view), canvas_view->details->sort_reversed); } static int compare_files (NautilusFilesView *canvas_view, NautilusFile *a, NautilusFile *b) { return nautilus_canvas_view_compare_files ((NautilusCanvasView *)canvas_view, a, b); } static void selection_changed_callback (NautilusCanvasContainer *container, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); nautilus_files_view_notify_selection_changed (NAUTILUS_FILES_VIEW (canvas_view)); } static void canvas_container_context_click_selection_callback (NautilusCanvasContainer *container, GdkEventButton *event, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container)); g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (canvas_view), event); } static void canvas_container_context_click_background_callback (NautilusCanvasContainer *container, GdkEventButton *event, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container)); g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (canvas_view), event); } static void icon_position_changed_callback (NautilusCanvasContainer *container, NautilusFile *file, const NautilusCanvasPosition *position, NautilusCanvasView *canvas_view) { char *position_string; char scale_string[G_ASCII_DTOSTR_BUF_SIZE]; g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); g_assert (container == get_canvas_container (canvas_view)); g_assert (NAUTILUS_IS_FILE (file)); /* Store the new position of the canvas in the metadata. */ if (!nautilus_canvas_view_using_auto_layout (canvas_view)) { position_string = g_strdup_printf ("%d,%d", position->x, position->y); nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_ICON_POSITION, NULL, position_string); g_free (position_string); } g_ascii_dtostr (scale_string, sizeof (scale_string), position->scale); nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_ICON_SCALE, "1.0", scale_string); } static char * get_icon_uri_callback (NautilusCanvasContainer *container, NautilusFile *file, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container)); g_assert (NAUTILUS_IS_FILE (file)); g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); return nautilus_file_get_uri (file); } static char * get_icon_activation_uri_callback (NautilusCanvasContainer *container, NautilusFile *file, NautilusCanvasView *canvas_view) { g_assert (NAUTILUS_IS_CANVAS_CONTAINER (container)); g_assert (NAUTILUS_IS_FILE (file)); g_assert (NAUTILUS_IS_CANVAS_VIEW (canvas_view)); return nautilus_file_get_activation_uri (file); } static char * get_icon_drop_target_uri_callback (NautilusCanvasContainer *container, NautilusFile *file, NautilusCanvasView *canvas_view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL); g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL); g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (canvas_view), NULL); return nautilus_file_get_target_uri (file); } /* Preferences changed callbacks */ static void nautilus_canvas_view_click_policy_changed (NautilusFilesView *directory_view) { g_assert (NAUTILUS_IS_CANVAS_VIEW (directory_view)); nautilus_canvas_view_update_click_mode (NAUTILUS_CANVAS_VIEW (directory_view)); } static void image_display_policy_changed_callback (gpointer callback_data) { NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (callback_data); nautilus_canvas_container_request_update_all (get_canvas_container (canvas_view)); } static void text_attribute_names_changed_callback (gpointer callback_data) { NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (callback_data); nautilus_canvas_container_request_update_all (get_canvas_container (canvas_view)); } static void default_sort_order_changed_callback (gpointer callback_data) { NautilusCanvasView *canvas_view; NautilusFile *file; char *sort_name; NautilusCanvasContainer *canvas_container; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (callback_data)); canvas_view = NAUTILUS_CANVAS_VIEW (callback_data); file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view)); sort_name = nautilus_canvas_view_get_directory_sort_by (canvas_view, file); update_sort_criterion (canvas_view, get_sort_criterion_by_metadata_text (sort_name), FALSE); g_free (sort_name); canvas_container = get_canvas_container (canvas_view); g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (canvas_container)); nautilus_canvas_container_request_update_all (canvas_container); } static void default_sort_in_reverse_order_changed_callback (gpointer callback_data) { NautilusCanvasView *canvas_view; NautilusFile *file; NautilusCanvasContainer *canvas_container; g_return_if_fail (NAUTILUS_IS_CANVAS_VIEW (callback_data)); canvas_view = NAUTILUS_CANVAS_VIEW (callback_data); file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (canvas_view)); set_sort_reversed (canvas_view, nautilus_canvas_view_get_directory_sort_reversed (canvas_view, file), FALSE); canvas_container = get_canvas_container (canvas_view); g_return_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (canvas_container)); nautilus_canvas_container_request_update_all (canvas_container); } static void nautilus_canvas_view_sort_directories_first_changed (NautilusFilesView *directory_view) { NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (directory_view); if (nautilus_canvas_view_using_auto_layout (canvas_view)) { nautilus_canvas_container_sort (get_canvas_container (canvas_view)); } } static gboolean canvas_view_can_accept_item (NautilusCanvasContainer *container, NautilusFile *target_item, const char *item_uri, NautilusFilesView *view) { return nautilus_drag_can_accept_item (target_item, item_uri); } static char * canvas_view_get_container_uri (NautilusCanvasContainer *container, NautilusFilesView *view) { return nautilus_files_view_get_uri (view); } static void canvas_view_move_copy_items (NautilusCanvasContainer *container, const GList *item_uris, GArray *relative_item_points, const char *target_dir, int copy_action, int x, int y, NautilusFilesView *view) { nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view), item_uris, nautilus_files_view_get_copied_files_atom (view)); nautilus_files_view_move_copy_items (view, item_uris, relative_item_points, target_dir, copy_action, x, y); } static void nautilus_canvas_view_update_click_mode (NautilusCanvasView *canvas_view) { NautilusCanvasContainer *canvas_container; int click_mode; canvas_container = get_canvas_container (canvas_view); g_assert (canvas_container != NULL); click_mode = g_settings_get_enum (nautilus_preferences, NAUTILUS_PREFERENCES_CLICK_POLICY); nautilus_canvas_container_set_single_click_mode (canvas_container, click_mode == NAUTILUS_CLICK_POLICY_SINGLE); } static gboolean get_stored_layout_timestamp (NautilusCanvasContainer *container, NautilusCanvasIconData *icon_data, time_t *timestamp, NautilusCanvasView *view) { NautilusFile *file; NautilusDirectory *directory; if (icon_data == NULL) { directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view)); if (directory == NULL) { return FALSE; } file = nautilus_directory_get_corresponding_file (directory); *timestamp = nautilus_file_get_time_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_LAYOUT_TIMESTAMP); nautilus_file_unref (file); } else { *timestamp = nautilus_file_get_time_metadata (NAUTILUS_FILE (icon_data), NAUTILUS_METADATA_KEY_ICON_POSITION_TIMESTAMP); } return TRUE; } static gboolean store_layout_timestamp (NautilusCanvasContainer *container, NautilusCanvasIconData *icon_data, const time_t *timestamp, NautilusCanvasView *view) { NautilusFile *file; NautilusDirectory *directory; if (icon_data == NULL) { directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view)); if (directory == NULL) { return FALSE; } file = nautilus_directory_get_corresponding_file (directory); nautilus_file_set_time_metadata (file, NAUTILUS_METADATA_KEY_ICON_VIEW_LAYOUT_TIMESTAMP, (time_t) *timestamp); nautilus_file_unref (file); } else { nautilus_file_set_time_metadata (NAUTILUS_FILE (icon_data), NAUTILUS_METADATA_KEY_ICON_POSITION_TIMESTAMP, (time_t) *timestamp); } return TRUE; } static NautilusCanvasContainer * create_canvas_container (NautilusCanvasView *canvas_view) { return NAUTILUS_CANVAS_VIEW_CLASS (G_OBJECT_GET_CLASS (canvas_view))->create_canvas_container (canvas_view); } static NautilusCanvasContainer * real_create_canvas_container (NautilusCanvasView *canvas_view) { return nautilus_canvas_view_container_new (canvas_view); } static void initialize_canvas_container (NautilusCanvasView *canvas_view, NautilusCanvasContainer *canvas_container) { GtkWidget *content_widget; content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (canvas_view)); canvas_view->details->canvas_container = GTK_WIDGET (canvas_container); g_object_add_weak_pointer (G_OBJECT (canvas_container), (gpointer *) &canvas_view->details->canvas_container); gtk_widget_set_can_focus (GTK_WIDGET (canvas_container), TRUE); g_signal_connect_object (canvas_container, "activate", G_CALLBACK (canvas_container_activate_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "activate-alternate", G_CALLBACK (canvas_container_activate_alternate_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "activate-previewer", G_CALLBACK (canvas_container_activate_previewer_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "band-select-started", G_CALLBACK (band_select_started_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "band-select-ended", G_CALLBACK (band_select_ended_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "context-click-selection", G_CALLBACK (canvas_container_context_click_selection_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "context-click-background", G_CALLBACK (canvas_container_context_click_background_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "icon-position-changed", G_CALLBACK (icon_position_changed_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "selection-changed", G_CALLBACK (selection_changed_callback), canvas_view, 0); /* FIXME: many of these should move into fm-canvas-container as virtual methods */ g_signal_connect_object (canvas_container, "get-icon-uri", G_CALLBACK (get_icon_uri_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "get-icon-activation-uri", G_CALLBACK (get_icon_activation_uri_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "get-icon-drop-target-uri", G_CALLBACK (get_icon_drop_target_uri_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "move-copy-items", G_CALLBACK (canvas_view_move_copy_items), canvas_view, 0); g_signal_connect_object (canvas_container, "get-container-uri", G_CALLBACK (canvas_view_get_container_uri), canvas_view, 0); g_signal_connect_object (canvas_container, "can-accept-item", G_CALLBACK (canvas_view_can_accept_item), canvas_view, 0); g_signal_connect_object (canvas_container, "get-stored-icon-position", G_CALLBACK (get_stored_icon_position_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "layout-changed", G_CALLBACK (layout_changed_callback), canvas_view, 0); g_signal_connect_object (canvas_container, "icon-stretch-started", G_CALLBACK (nautilus_files_view_update_context_menus), canvas_view, G_CONNECT_SWAPPED); g_signal_connect_object (canvas_container, "icon-stretch-ended", G_CALLBACK (nautilus_files_view_update_context_menus), canvas_view, G_CONNECT_SWAPPED); g_signal_connect_object (canvas_container, "get-stored-layout-timestamp", G_CALLBACK (get_stored_layout_timestamp), canvas_view, 0); g_signal_connect_object (canvas_container, "store-layout-timestamp", G_CALLBACK (store_layout_timestamp), canvas_view, 0); gtk_container_add (GTK_CONTAINER (content_widget), GTK_WIDGET (canvas_container)); nautilus_canvas_view_update_click_mode (canvas_view); nautilus_canvas_container_set_zoom_level (canvas_container, get_default_zoom_level (canvas_view)); gtk_widget_show (GTK_WIDGET (canvas_container)); } /* Handles an URL received from Mozilla */ static void canvas_view_handle_netscape_url (NautilusCanvasContainer *container, const char *encoded_url, const char *target_uri, GdkDragAction action, int x, int y, NautilusCanvasView *view) { nautilus_files_view_handle_netscape_url_drop (NAUTILUS_FILES_VIEW (view), encoded_url, target_uri, action, x, y); } static void canvas_view_handle_uri_list (NautilusCanvasContainer *container, const char *item_uris, const char *target_uri, GdkDragAction action, int x, int y, NautilusCanvasView *view) { nautilus_files_view_handle_uri_list_drop (NAUTILUS_FILES_VIEW (view), item_uris, target_uri, action, x, y); } static void canvas_view_handle_text (NautilusCanvasContainer *container, const char *text, const char *target_uri, GdkDragAction action, int x, int y, NautilusCanvasView *view) { nautilus_files_view_handle_text_drop (NAUTILUS_FILES_VIEW (view), text, target_uri, action, x, y); } static void canvas_view_handle_raw (NautilusCanvasContainer *container, const char *raw_data, int length, const char *target_uri, const char *direct_save_uri, GdkDragAction action, int x, int y, NautilusCanvasView *view) { nautilus_files_view_handle_raw_drop (NAUTILUS_FILES_VIEW (view), raw_data, length, target_uri, direct_save_uri, action, x, y); } static void canvas_view_handle_hover (NautilusCanvasContainer *container, const char *target_uri, NautilusCanvasView *view) { nautilus_files_view_handle_hover (NAUTILUS_FILES_VIEW (view), target_uri); } static char * canvas_view_get_first_visible_file (NautilusFilesView *view) { NautilusFile *file; NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (view); file = NAUTILUS_FILE (nautilus_canvas_container_get_first_visible_icon (get_canvas_container (canvas_view))); if (file) { return nautilus_file_get_uri (file); } return NULL; } static void canvas_view_scroll_to_file (NautilusFilesView *view, const char *uri) { NautilusFile *file; NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (view); if (uri != NULL) { /* Only if existing, since we don't want to add the file to the directory if it has been removed since then */ file = nautilus_file_get_existing_by_uri (uri); if (file != NULL) { nautilus_canvas_container_scroll_to_canvas (get_canvas_container (canvas_view), NAUTILUS_CANVAS_ICON_DATA (file)); nautilus_file_unref (file); } } } static guint nautilus_canvas_view_get_id (NautilusFilesView *view) { return NAUTILUS_VIEW_GRID_ID; } static void nautilus_canvas_view_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (object); switch (prop_id) { case PROP_SUPPORTS_AUTO_LAYOUT: canvas_view->details->supports_auto_layout = g_value_get_boolean (value); break; case PROP_SUPPORTS_MANUAL_LAYOUT: canvas_view->details->supports_manual_layout = g_value_get_boolean (value); break; case PROP_SUPPORTS_SCALING: canvas_view->details->supports_scaling = g_value_get_boolean (value); break; case PROP_SUPPORTS_KEEP_ALIGNED: canvas_view->details->supports_keep_aligned = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void nautilus_canvas_view_finalize (GObject *object) { NautilusCanvasView *canvas_view; canvas_view = NAUTILUS_CANVAS_VIEW (object); g_clear_object (&canvas_view->details->icon); g_free (canvas_view->details); g_signal_handlers_disconnect_by_func (nautilus_preferences, default_sort_order_changed_callback, canvas_view); g_signal_handlers_disconnect_by_func (nautilus_preferences, default_sort_in_reverse_order_changed_callback, canvas_view); g_signal_handlers_disconnect_by_func (nautilus_preferences, image_display_policy_changed_callback, canvas_view); g_signal_handlers_disconnect_by_func (nautilus_icon_view_preferences, text_attribute_names_changed_callback, canvas_view); G_OBJECT_CLASS (nautilus_canvas_view_parent_class)->finalize (object); } static GIcon* nautilus_canvas_view_get_icon (NautilusFilesView *view) { g_return_val_if_fail (NAUTILUS_IS_CANVAS_VIEW (view), NULL); return NAUTILUS_CANVAS_VIEW (view)->details->icon; } static void nautilus_canvas_view_class_init (NautilusCanvasViewClass *klass) { NautilusFilesViewClass *nautilus_files_view_class; GObjectClass *oclass; nautilus_files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass); oclass = G_OBJECT_CLASS (klass); oclass->set_property = nautilus_canvas_view_set_property; oclass->finalize = nautilus_canvas_view_finalize; GTK_WIDGET_CLASS (klass)->destroy = nautilus_canvas_view_destroy; klass->create_canvas_container = real_create_canvas_container; nautilus_files_view_class->add_file = nautilus_canvas_view_add_file; nautilus_files_view_class->begin_loading = nautilus_canvas_view_begin_loading; nautilus_files_view_class->bump_zoom_level = nautilus_canvas_view_bump_zoom_level; nautilus_files_view_class->can_zoom_in = nautilus_canvas_view_can_zoom_in; nautilus_files_view_class->can_zoom_out = nautilus_canvas_view_can_zoom_out; nautilus_files_view_class->clear = nautilus_canvas_view_clear; nautilus_files_view_class->end_loading = nautilus_canvas_view_end_loading; nautilus_files_view_class->file_changed = nautilus_canvas_view_file_changed; nautilus_files_view_class->compute_rename_popover_relative_to = nautilus_canvas_view_compute_rename_popover_relative_to; nautilus_files_view_class->get_selection = nautilus_canvas_view_get_selection; nautilus_files_view_class->get_selection_for_file_transfer = nautilus_canvas_view_get_selection; nautilus_files_view_class->is_empty = nautilus_canvas_view_is_empty; nautilus_files_view_class->remove_file = nautilus_canvas_view_remove_file; nautilus_files_view_class->restore_default_zoom_level = nautilus_canvas_view_restore_default_zoom_level; nautilus_files_view_class->reveal_selection = nautilus_canvas_view_reveal_selection; nautilus_files_view_class->select_all = nautilus_canvas_view_select_all; nautilus_files_view_class->select_first = nautilus_canvas_view_select_first; nautilus_files_view_class->set_selection = nautilus_canvas_view_set_selection; nautilus_files_view_class->invert_selection = nautilus_canvas_view_invert_selection; nautilus_files_view_class->compare_files = compare_files; nautilus_files_view_class->click_policy_changed = nautilus_canvas_view_click_policy_changed; nautilus_files_view_class->update_actions_state = nautilus_canvas_view_update_actions_state; nautilus_files_view_class->sort_directories_first_changed = nautilus_canvas_view_sort_directories_first_changed; nautilus_files_view_class->using_manual_layout = nautilus_canvas_view_using_manual_layout; nautilus_files_view_class->widget_to_file_operation_position = nautilus_canvas_view_widget_to_file_operation_position; nautilus_files_view_class->get_view_id = nautilus_canvas_view_get_id; nautilus_files_view_class->get_first_visible_file = canvas_view_get_first_visible_file; nautilus_files_view_class->scroll_to_file = canvas_view_scroll_to_file; nautilus_files_view_class->get_icon = nautilus_canvas_view_get_icon; properties[PROP_SUPPORTS_AUTO_LAYOUT] = g_param_spec_boolean ("supports-auto-layout", "Supports auto layout", "Whether this view supports auto layout", TRUE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_SUPPORTS_MANUAL_LAYOUT] = g_param_spec_boolean ("supports-manual-layout", "Supports manual layout", "Whether this view supports manual layout", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_SUPPORTS_SCALING] = g_param_spec_boolean ("supports-scaling", "Supports scaling", "Whether this view supports scaling", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); properties[PROP_SUPPORTS_KEEP_ALIGNED] = g_param_spec_boolean ("supports-keep-aligned", "Supports keep aligned", "Whether this view supports keep aligned", FALSE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); } static void nautilus_canvas_view_init (NautilusCanvasView *canvas_view) { NautilusCanvasContainer *canvas_container; GActionGroup *view_action_group; canvas_view->details = g_new0 (NautilusCanvasViewDetails, 1); canvas_view->details->sort = &sort_criteria[0]; canvas_view->details->icon = g_themed_icon_new ("view-grid-symbolic"); canvas_container = create_canvas_container (canvas_view); initialize_canvas_container (canvas_view, canvas_container); g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER, G_CALLBACK (default_sort_order_changed_callback), canvas_view); g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER, G_CALLBACK (default_sort_in_reverse_order_changed_callback), canvas_view); g_signal_connect_swapped (nautilus_preferences, "changed::" NAUTILUS_PREFERENCES_SHOW_FILE_THUMBNAILS, G_CALLBACK (image_display_policy_changed_callback), canvas_view); g_signal_connect_swapped (nautilus_icon_view_preferences, "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS, G_CALLBACK (text_attribute_names_changed_callback), canvas_view); g_signal_connect_object (canvas_container, "handle-netscape-url", G_CALLBACK (canvas_view_handle_netscape_url), canvas_view, 0); g_signal_connect_object (canvas_container, "handle-uri-list", G_CALLBACK (canvas_view_handle_uri_list), canvas_view, 0); g_signal_connect_object (canvas_container, "handle-text", G_CALLBACK (canvas_view_handle_text), canvas_view, 0); g_signal_connect_object (canvas_container, "handle-raw", G_CALLBACK (canvas_view_handle_raw), canvas_view, 0); g_signal_connect_object (canvas_container, "handle-hover", G_CALLBACK (canvas_view_handle_hover), canvas_view, 0); canvas_view->details->clipboard_handler_id = g_signal_connect (nautilus_clipboard_monitor_get (), "clipboard-info", G_CALLBACK (canvas_view_notify_clipboard_info), canvas_view); view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (canvas_view)); g_action_map_add_action_entries (G_ACTION_MAP (view_action_group), canvas_view_entries, G_N_ELEMENTS (canvas_view_entries), canvas_view); /* Keep the action synced with the actual value, so the toolbar can poll it */ g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (canvas_view)), "zoom-to-level", g_variant_new_int32 (get_default_zoom_level (canvas_view))); } NautilusFilesView * nautilus_canvas_view_new (NautilusWindowSlot *slot) { return g_object_new (NAUTILUS_TYPE_CANVAS_VIEW, "window-slot", slot, NULL); }