diff --git a/data/org.gnome.nautilus.gschema.xml b/data/org.gnome.nautilus.gschema.xml index 90f63c488d2fd292fa69be8c1956f8fdc4f0340c..39a8ed753a256b412eec8f3e3d4e08f16183c91b 100644 --- a/data/org.gnome.nautilus.gschema.xml +++ b/data/org.gnome.nautilus.gschema.xml @@ -226,7 +226,7 @@ Default list view zoom level - [ 'name', 'size', 'date_modified', 'starred' ] + [ 'name', 'size', 'date_modified' ] Columns visible in list view diff --git a/meson.build b/meson.build index 154c7b76326d45b2e9be22e58f6e0b6acd54503c..7af5a3ef0bfc288ca6f8174f68f028b95c4173f2 100644 --- a/meson.build +++ b/meson.build @@ -92,7 +92,7 @@ pkgconfig = import('pkgconfig') ################ # Dependencies # ################ -glib_ver = '>= 2.67.1' +glib_ver = '>= 2.72.0' libm = cc.find_library('m') diff --git a/po/POTFILES.in b/po/POTFILES.in index cea948da64cc60f6ce270f4028d519fdca6088fb..19a277eebbe20e93ef836ff09db45ef0e53c316c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -61,7 +61,6 @@ src/nautilus-shell-search-provider.c src/nautilus-special-location-bar.c src/nautilus-toolbar.c src/nautilus-trash-bar.c -src/nautilus-tree-view-drag-dest.c src/nautilus-ui-utilities.c src/nautilus-vfs-file.c src/nautilus-view.c @@ -91,7 +90,7 @@ src/resources/ui/nautilus-starred-is-empty.ui src/resources/ui/nautilus-toolbar.ui src/resources/ui/nautilus-toolbar-view-menu.ui src/resources/ui/nautilus-trash-is-empty.ui -src/resources/ui/nautilus-view-icon-item-ui.ui +src/resources/ui/nautilus-grid-cell.ui src/resources/ui/nautilus-window.ui src/gtk/nautilusgtkplacesview.c src/gtk/nautilusgtkplacesviewrow.c diff --git a/src/meson.build b/src/meson.build index 35a0dce98e426b75cbdcf4e07d0af0c959819d7c..edcfe0d91f842870e7acd96ddfcce10056bfc8e7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -83,17 +83,22 @@ libnautilus_sources = [ 'nautilus-floating-bar.h', 'nautilus-freedesktop-dbus.c', 'nautilus-freedesktop-dbus.h', - 'nautilus-list-model.c', - 'nautilus-list-model.h', + 'nautilus-grid-cell.c', + 'nautilus-grid-cell.h', + 'nautilus-grid-view.c', + 'nautilus-grid-view.h', + 'nautilus-label-cell.c', + 'nautilus-label-cell.h', + 'nautilus-list-base.c', + 'nautilus-list-base.h', 'nautilus-list-view.c', 'nautilus-list-view.h', - 'nautilus-list-view-private.h', - 'nautilus-list-view-dnd.c', - 'nautilus-list-view-dnd.h', 'nautilus-location-entry.c', 'nautilus-location-entry.h', 'nautilus-mime-actions.c', 'nautilus-mime-actions.h', + 'nautilus-name-cell.c', + 'nautilus-name-cell.h', 'nautilus-notebook.c', 'nautilus-notebook.h', 'nautilus-pathbar.c', @@ -115,6 +120,8 @@ libnautilus_sources = [ 'nautilus-self-check-functions.h', 'nautilus-shell-search-provider.c', 'nautilus-special-location-bar.c', + 'nautilus-star-cell.c', + 'nautilus-star-cell.h', 'nautilus-toolbar.c', 'nautilus-toolbar.h', 'nautilus-toolbar-menu-sections.h', @@ -122,12 +129,10 @@ libnautilus_sources = [ 'nautilus-trash-bar.h', 'nautilus-view.c', 'nautilus-view.h', - 'nautilus-view-icon-controller.c', - 'nautilus-view-icon-controller.h', - 'nautilus-view-icon-item-ui.c', - 'nautilus-view-icon-item-ui.h', - 'nautilus-view-item-model.c', - 'nautilus-view-item-model.h', + 'nautilus-view-cell.c', + 'nautilus-view-cell.h', + 'nautilus-view-item.c', + 'nautilus-view-item.h', 'nautilus-view-model.c', 'nautilus-view-model.h', 'nautilus-window-slot.c', @@ -227,8 +232,6 @@ libnautilus_sources = [ 'nautilus-thumbnails.h', 'nautilus-trash-monitor.c', 'nautilus-trash-monitor.h', - 'nautilus-tree-view-drag-dest.c', - 'nautilus-tree-view-drag-dest.h', 'nautilus-ui-utilities.c', 'nautilus-ui-utilities.h', 'nautilus-video-mime-types.h', diff --git a/src/nautilus-column-chooser.c b/src/nautilus-column-chooser.c index 3412fb1bdfa5594ce5409dfa386ff537aa0dd5d4..23aa5af5c5bab14f07ccb36d4ea0f73fc337d223 100644 --- a/src/nautilus-column-chooser.c +++ b/src/nautilus-column-chooser.c @@ -298,6 +298,12 @@ populate_tree (NautilusColumnChooser *chooser) visible = TRUE; sensitive = FALSE; } + if (strcmp (name, "starred") == 0) + { + g_free (name); + g_free (label); + continue; + } gtk_list_store_append (chooser->store, &iter); gtk_list_store_set (chooser->store, &iter, diff --git a/src/nautilus-directory-async.c b/src/nautilus-directory-async.c index 5fa29ae91c2f1b1218f22d91ba260fda208a3dde..4899ea7b627e5a84ba9c016efd1e31b3aa41e167 100644 --- a/src/nautilus-directory-async.c +++ b/src/nautilus-directory-async.c @@ -3704,7 +3704,7 @@ thumbnail_loader_size_prepared (GdkPixbufLoader *loader, aspect_ratio = ((double) width) / height; /* cf. nautilus_file_get_icon() */ - max_thumbnail_size = NAUTILUS_GRID_ICON_SIZE_LARGEST * NAUTILUS_GRID_ICON_SIZE_STANDARD / NAUTILUS_GRID_ICON_SIZE_SMALL; + max_thumbnail_size = NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE * NAUTILUS_GRID_ICON_SIZE_MEDIUM / NAUTILUS_GRID_ICON_SIZE_SMALL; if (MAX (width, height) > max_thumbnail_size) { if (width > height) diff --git a/src/nautilus-dnd.c b/src/nautilus-dnd.c index a1e9ff128d983ef50fd28d940fe330b049371542..b43e758b827d50c30528340e460adb6c3a12c022 100644 --- a/src/nautilus-dnd.c +++ b/src/nautilus-dnd.c @@ -33,7 +33,6 @@ #include #include #include "nautilus-file-utilities.h" -#include #include #include diff --git a/src/nautilus-enums.h b/src/nautilus-enums.h index 4145ed3869b9122a946cdbc5318043882e52f9ae..2cb9b4c80f0e63d33e65a5e317dac2319ce1c4f3 100644 --- a/src/nautilus-enums.h +++ b/src/nautilus-enums.h @@ -30,36 +30,32 @@ typedef enum { - NAUTILUS_GRID_ICON_SIZE_SMALL = 48, - NAUTILUS_GRID_ICON_SIZE_STANDARD = 64, - NAUTILUS_GRID_ICON_SIZE_LARGE = 96, - NAUTILUS_GRID_ICON_SIZE_LARGER = 128, - NAUTILUS_GRID_ICON_SIZE_LARGEST = 256, + NAUTILUS_GRID_ICON_SIZE_SMALL = 48, + NAUTILUS_GRID_ICON_SIZE_MEDIUM = 96, + NAUTILUS_GRID_ICON_SIZE_LARGE = 168, + NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE = 256, } NautilusGridIconSize; typedef enum { NAUTILUS_GRID_ZOOM_LEVEL_SMALL, - NAUTILUS_GRID_ZOOM_LEVEL_STANDARD, + NAUTILUS_GRID_ZOOM_LEVEL_MEDIUM, NAUTILUS_GRID_ZOOM_LEVEL_LARGE, - NAUTILUS_GRID_ZOOM_LEVEL_LARGER, - NAUTILUS_GRID_ZOOM_LEVEL_LARGEST, + NAUTILUS_GRID_ZOOM_LEVEL_EXTRA_LARGE, } NautilusGridZoomLevel; typedef enum { - NAUTILUS_LIST_ICON_SIZE_SMALL = 16, - NAUTILUS_LIST_ICON_SIZE_STANDARD = 32, - NAUTILUS_LIST_ICON_SIZE_LARGE = 48, - NAUTILUS_LIST_ICON_SIZE_LARGER = 64, + NAUTILUS_LIST_ICON_SIZE_SMALL = 16, + NAUTILUS_LIST_ICON_SIZE_MEDIUM = 32, + NAUTILUS_LIST_ICON_SIZE_LARGE = 64, } NautilusListIconSize; typedef enum { NAUTILUS_LIST_ZOOM_LEVEL_SMALL, - NAUTILUS_LIST_ZOOM_LEVEL_STANDARD, + NAUTILUS_LIST_ZOOM_LEVEL_MEDIUM, NAUTILUS_LIST_ZOOM_LEVEL_LARGE, - NAUTILUS_LIST_ZOOM_LEVEL_LARGER, } NautilusListZoomLevel; typedef enum diff --git a/src/nautilus-file.c b/src/nautilus-file.c index 4d3d4a74bd1e4f1ffd2542adb3457a6fcafa53f6..5bdc0e125f73292b2f7ffe5b1adf9c2c05d6d8b5 100644 --- a/src/nautilus-file.c +++ b/src/nautilus-file.c @@ -5216,7 +5216,7 @@ nautilus_file_get_thumbnail_icon (NautilusFile *file, } else { - modified_size = size * scale * NAUTILUS_GRID_ICON_SIZE_STANDARD / NAUTILUS_GRID_ICON_SIZE_SMALL; + modified_size = size * scale * NAUTILUS_GRID_ICON_SIZE_MEDIUM / NAUTILUS_GRID_ICON_SIZE_SMALL; } if (file->details->thumbnail) @@ -5226,7 +5226,7 @@ nautilus_file_get_thumbnail_icon (NautilusFile *file, s = MAX (w, h); /* Don't scale up small thumbnails in the standard view */ - if (s <= NAUTILUS_GRID_ICON_SIZE_STANDARD) + if (s <= NAUTILUS_GRID_ICON_SIZE_MEDIUM) { thumb_scale = (double) size / NAUTILUS_GRID_ICON_SIZE_SMALL; } diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index ba08f22546f440eb45869da6689170b14714df27..5c8729c307e0f7bba0bdb669801d5f4949050ebe 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -86,7 +86,7 @@ #include "nautilus-trash-monitor.h" #include "nautilus-ui-utilities.h" #include "nautilus-view.h" -#include "nautilus-view-icon-controller.h" +#include "nautilus-grid-view.h" #include "nautilus-window.h" #include "nautilus-tracker-utilities.h" @@ -3718,7 +3718,7 @@ done_loading (NautilusFilesView *view, if (do_reveal) { - if (NAUTILUS_IS_LIST_VIEW (view) || NAUTILUS_IS_VIEW_ICON_CONTROLLER (view)) + if (NAUTILUS_IS_LIST_VIEW (view) || NAUTILUS_IS_GRID_VIEW (view)) { /* HACK: We should be able to directly call reveal_selection here, * but at this point the GtkTreeView hasn't allocated the new nodes @@ -8052,20 +8052,26 @@ nautilus_files_view_reset_view_menu (NautilusFilesView *view) NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view); NautilusFile *file; GMenuModel *sort_section = priv->toolbar_menu_sections->sort_section; - const gchar *trashed_action; + const gchar *action; gint i; file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view)); - /* When not in Trash, set an inexistant action to hide the menu item. This - * works under the assumptiont that the menu item has its "hidden-when" - * attribute set to "action-disabled", and that an inexistant action is - * treated as a disabled action. */ - trashed_action = nautilus_file_is_in_trash (file) ? "view.sort" : "doesnt-exist"; - i = nautilus_g_menu_model_find_by_string (sort_section, "target", "trash-time"); - g_return_if_fail (i != -1); - nautilus_g_menu_replace_string_in_item (G_MENU (sort_section), i, - "action", trashed_action); + /* When not in the special location, set an inexistant action to hide the + * menu item. This works under the assumptiont that the menu item has its + * "hidden-when" attribute set to "action-disabled", and that an inexistant + * action is treated as a disabled action. */ + action = nautilus_file_is_in_trash (file) ? "view.sort" : "doesnt-exist"; + i = nautilus_g_menu_model_find_by_string (sort_section, "nautilus-menu-item", "last_trashed"); + nautilus_g_menu_replace_string_in_item (G_MENU (sort_section), i, "action", action); + + action = nautilus_file_is_in_recent (file) ? "view.sort" : "doesnt-exist"; + i = nautilus_g_menu_model_find_by_string (sort_section, "nautilus-menu-item", "recency"); + nautilus_g_menu_replace_string_in_item (G_MENU (sort_section), i, "action", action); + + action = nautilus_file_is_in_search (file) ? "view.sort" : "doesnt-exist"; + i = nautilus_g_menu_model_find_by_string (sort_section, "nautilus-menu-item", "relevance"); + nautilus_g_menu_replace_string_in_item (G_MENU (sort_section), i, "action", action); } /* Convenience function to reset the menus owned by the view but managed on @@ -9605,13 +9611,13 @@ nautilus_files_view_new (guint id, { case NAUTILUS_VIEW_GRID_ID: { - view = NAUTILUS_FILES_VIEW (nautilus_view_icon_controller_new (slot)); + view = NAUTILUS_FILES_VIEW (nautilus_grid_view_new (slot)); } break; case NAUTILUS_VIEW_LIST_ID: { - view = nautilus_list_view_new (slot); + view = NAUTILUS_FILES_VIEW (nautilus_list_view_new (slot)); } break; } diff --git a/src/nautilus-grid-cell.c b/src/nautilus-grid-cell.c new file mode 100644 index 0000000000000000000000000000000000000000..426a2c2a8459a88ed2fc4c626ee1bd2583138839 --- /dev/null +++ b/src/nautilus-grid-cell.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "nautilus-grid-cell.h" + +struct _NautilusGridCell +{ + NautilusViewCell parent_instance; + + GSignalGroup *item_signal_group; + + GQuark *caption_attributes; + + GtkWidget *fixed_height_box; + GtkWidget *icon; + GtkWidget *label; + GtkWidget *first_caption; + GtkWidget *second_caption; + GtkWidget *third_caption; +}; + +G_DEFINE_TYPE (NautilusGridCell, nautilus_grid_cell, NAUTILUS_TYPE_VIEW_CELL) + +#define EXTRA_WIDTH_FOR_TEXT 36 + +static void +update_icon (NautilusGridCell *self) +{ + NautilusViewItem *item; + NautilusFileIconFlags flags; + g_autoptr (GdkPaintable) icon_paintable = NULL; + GtkStyleContext *style_context; + NautilusFile *file; + guint icon_size; + g_autofree gchar *thumbnail_path = NULL; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + file = nautilus_view_item_get_file (item); + icon_size = nautilus_view_item_get_icon_size (item); + flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | + NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE | + NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS | + NAUTILUS_FILE_ICON_FLAGS_USE_ONE_EMBLEM; + + icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, 1, flags); + gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); + + /* Set the same height and width for all icons regardless of aspect ratio. + */ + gtk_widget_set_size_request (self->fixed_height_box, icon_size, icon_size); + if (icon_size < NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE) + { + int extra_margins = 0.5 * EXTRA_WIDTH_FOR_TEXT; + gtk_widget_set_margin_start (self->fixed_height_box, extra_margins); + gtk_widget_set_margin_end (self->fixed_height_box, extra_margins); + } + style_context = gtk_widget_get_style_context (self->icon); + thumbnail_path = nautilus_file_get_thumbnail_path (file); + if (thumbnail_path != NULL && + nautilus_file_should_show_thumbnail (file)) + { + gtk_style_context_add_class (style_context, "thumbnail"); + } + else + { + gtk_style_context_remove_class (style_context, "thumbnail"); + } +} + +static void +update_captions (NautilusGridCell *self) +{ + NautilusViewItem *item; + NautilusFile *file; + GtkWidget * const caption_labels[] = + { + self->first_caption, + self->second_caption, + self->third_caption + }; + G_STATIC_ASSERT (G_N_ELEMENTS (caption_labels) == NAUTILUS_GRID_CELL_N_CAPTIONS); + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + file = nautilus_view_item_get_file (item); + for (guint i = 0; i < NAUTILUS_GRID_CELL_N_CAPTIONS; i++) + { + GQuark attribute_q = self->caption_attributes[i]; + gboolean show_caption; + + show_caption = (attribute_q != 0); + gtk_widget_set_visible (caption_labels[i], show_caption); + if (show_caption) + { + g_autofree gchar *string = NULL; + string = nautilus_file_get_string_attribute_q (file, attribute_q); + gtk_label_set_text (GTK_LABEL (caption_labels[i]), string); + } + } +} + +static void +on_file_changed (NautilusGridCell *self) +{ + NautilusViewItem *item; + NautilusFile *file; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + file = nautilus_view_item_get_file (item); + + update_icon (self); + + gtk_label_set_text (GTK_LABEL (self->label), + nautilus_file_get_display_name (file)); + update_captions (self); +} + +static void +on_item_size_changed (NautilusGridCell *self) +{ + update_icon (self); + update_captions (self); +} + +static void +on_item_is_cut_changed (NautilusGridCell *self) +{ + gboolean is_cut; + + g_object_get (nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)), + "is-cut", &is_cut, + NULL); + if (is_cut) + { + gtk_widget_add_css_class (self->icon, "cut"); + } + else + { + gtk_widget_remove_css_class (self->icon, "cut"); + } +} + +static void +finalize (GObject *object) +{ + NautilusGridCell *self = (NautilusGridCell *) object; + + g_object_unref (self->item_signal_group); + G_OBJECT_CLASS (nautilus_grid_cell_parent_class)->finalize (object); +} + +static void +nautilus_grid_cell_class_init (NautilusGridCellClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-grid-cell.ui"); + + gtk_widget_class_bind_template_child (widget_class, NautilusGridCell, fixed_height_box); + gtk_widget_class_bind_template_child (widget_class, NautilusGridCell, icon); + gtk_widget_class_bind_template_child (widget_class, NautilusGridCell, label); + gtk_widget_class_bind_template_child (widget_class, NautilusGridCell, first_caption); + gtk_widget_class_bind_template_child (widget_class, NautilusGridCell, second_caption); + gtk_widget_class_bind_template_child (widget_class, NautilusGridCell, third_caption); +} + +static void +nautilus_grid_cell_init (NautilusGridCell *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + /* Connect automatically to an item. */ + self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM); + g_signal_group_connect_swapped (self->item_signal_group, "notify::icon-size", + (GCallback) on_item_size_changed, self); + g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut", + (GCallback) on_item_is_cut_changed, self); + g_signal_group_connect_swapped (self->item_signal_group, "file-changed", + (GCallback) on_file_changed, self); + g_signal_connect_object (self->item_signal_group, "bind", + (GCallback) on_file_changed, self, + G_CONNECT_SWAPPED); + + g_object_bind_property (self, "item", + self->item_signal_group, "target", + G_BINDING_SYNC_CREATE); + +#if PANGO_VERSION_CHECK (1, 44, 4) + { + PangoAttrList *attr_list; + + /* GTK4 TODO: This attribute is set in the UI file but GTK 3 ignores it. + * Remove this block after the switch to GTK 4. */ + attr_list = pango_attr_list_new (); + pango_attr_list_insert (attr_list, pango_attr_insert_hyphens_new (FALSE)); + gtk_label_set_attributes (GTK_LABEL (self->label), attr_list); + pango_attr_list_unref (attr_list); + } +#endif +} + +NautilusGridCell * +nautilus_grid_cell_new (NautilusListBase *view) +{ + return g_object_new (NAUTILUS_TYPE_GRID_CELL, + "view", view, + NULL); +} + +void +nautilus_grid_cell_set_caption_attributes (NautilusGridCell *self, + GQuark *attrs) +{ + self->caption_attributes = attrs; +} diff --git a/src/nautilus-grid-cell.h b/src/nautilus-grid-cell.h new file mode 100644 index 0000000000000000000000000000000000000000..52da1f81c38e533ed6f8bb65ba4bac2548edf217 --- /dev/null +++ b/src/nautilus-grid-cell.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "nautilus-view-cell.h" + +G_BEGIN_DECLS + +enum +{ + NAUTILUS_GRID_CELL_FIRST_CAPTION, + NAUTILUS_GRID_CELL_SECOND_CAPTION, + NAUTILUS_GRID_CELL_THIRD_CAPTION, + NAUTILUS_GRID_CELL_N_CAPTIONS +}; + +#define NAUTILUS_TYPE_GRID_CELL (nautilus_grid_cell_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusGridCell, nautilus_grid_cell, NAUTILUS, GRID_CELL, NautilusViewCell) + +NautilusGridCell * nautilus_grid_cell_new (NautilusListBase *view); +void nautilus_grid_cell_set_caption_attributes (NautilusGridCell *self, + GQuark *attrs); + +G_END_DECLS diff --git a/src/nautilus-grid-view.c b/src/nautilus-grid-view.c new file mode 100644 index 0000000000000000000000000000000000000000..926f5042e3c35c9039743b211573d4a7ec1178b5 --- /dev/null +++ b/src/nautilus-grid-view.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "nautilus-list-base-private.h" +#include "nautilus-grid-view.h" + +#include "nautilus-grid-cell.h" +#include "nautilus-global-preferences.h" + +struct _NautilusGridView +{ + NautilusListBase parent_instance; + + GtkGridView *view_ui; + + GActionGroup *action_group; + gint zoom_level; + + gboolean directories_first; + + GQuark caption_attributes[NAUTILUS_GRID_CELL_N_CAPTIONS]; + + NautilusFileSortType sort_type; + gboolean reversed; +}; + +G_DEFINE_TYPE (NautilusGridView, nautilus_grid_view, NAUTILUS_TYPE_LIST_BASE) + +static guint get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level); + +static gint +nautilus_grid_view_sort (gconstpointer a, + gconstpointer b, + gpointer user_data) +{ + NautilusGridView *self = user_data; + NautilusFile *file_a; + NautilusFile *file_b; + + file_a = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) a)); + file_b = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) b)); + + return nautilus_file_compare_for_sort (file_a, file_b, + self->sort_type, + self->directories_first, + self->reversed); +} + +static void +real_bump_zoom_level (NautilusFilesView *files_view, + int zoom_increment) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (files_view); + NautilusGridZoomLevel new_level; + + new_level = self->zoom_level + zoom_increment; + + if (new_level >= NAUTILUS_GRID_ZOOM_LEVEL_SMALL && + new_level <= NAUTILUS_GRID_ZOOM_LEVEL_EXTRA_LARGE) + { + g_action_group_change_action_state (self->action_group, + "zoom-to-level", + g_variant_new_int32 (new_level)); + } +} + +static guint +get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level) +{ + switch (zoom_level) + { + case NAUTILUS_GRID_ZOOM_LEVEL_SMALL: + { + return NAUTILUS_GRID_ICON_SIZE_SMALL; + } + break; + + case NAUTILUS_GRID_ZOOM_LEVEL_MEDIUM: + { + return NAUTILUS_GRID_ICON_SIZE_MEDIUM; + } + break; + + case NAUTILUS_GRID_ZOOM_LEVEL_LARGE: + { + return NAUTILUS_GRID_ICON_SIZE_LARGE; + } + break; + + case NAUTILUS_GRID_ZOOM_LEVEL_EXTRA_LARGE: + { + return NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE; + } + break; + } + g_return_val_if_reached (NAUTILUS_GRID_ICON_SIZE_MEDIUM); +} + +static gint +get_default_zoom_level (void) +{ + NautilusGridZoomLevel default_zoom_level; + + default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences, + NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL); + + /* Sanitize preference value */ + return CLAMP (default_zoom_level, + NAUTILUS_GRID_ZOOM_LEVEL_SMALL, + NAUTILUS_GRID_ZOOM_LEVEL_EXTRA_LARGE); +} + +static void +set_captions_from_preferences (NautilusGridView *self) +{ + g_auto (GStrv) value = NULL; + gint n_captions_for_zoom_level; + + value = g_settings_get_strv (nautilus_icon_view_preferences, + NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS); + + /* Set a celling on the number of captions depending on the zoom level. */ + n_captions_for_zoom_level = MIN (self->zoom_level, + G_N_ELEMENTS (self->caption_attributes)); + + /* Reset array to zeros beforehand, as we may not refill all elements. */ + memset (&self->caption_attributes, 0, sizeof (self->caption_attributes)); + for (gint i = 0, quark_i = 0; + value[i] != NULL && quark_i < n_captions_for_zoom_level; + i++) + { + if (g_strcmp0 (value[i], "none") == 0) + { + continue; + } + + /* Convert to quarks in advance, otherwise each NautilusFile attribute + * getter would call g_quark_from_string() once for each file. */ + self->caption_attributes[quark_i] = g_quark_from_string (value[i]); + quark_i++; + } +} + +static void +set_zoom_level (NautilusGridView *self, + guint new_level) +{ + self->zoom_level = new_level; + + /* The zoom level may change how many captions are allowed. Update it before + * setting the icon size, under the assumption that NautilusGridCell + * updates captions whenever the icon size is set*/ + set_captions_from_preferences (self); + + nautilus_list_base_set_icon_size (NAUTILUS_LIST_BASE (self), + get_icon_size_for_zoom_level (new_level)); + + nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self)); +} + +static void +real_restore_standard_zoom_level (NautilusFilesView *files_view) +{ + NautilusGridView *self; + + self = NAUTILUS_GRID_VIEW (files_view); + g_action_group_change_action_state (self->action_group, + "zoom-to-level", + g_variant_new_int32 (NAUTILUS_GRID_ZOOM_LEVEL_MEDIUM)); +} + +static gboolean +real_is_zoom_level_default (NautilusFilesView *files_view) +{ + NautilusGridView *self; + guint icon_size; + + self = NAUTILUS_GRID_VIEW (files_view); + icon_size = get_icon_size_for_zoom_level (self->zoom_level); + + return icon_size == NAUTILUS_GRID_ICON_SIZE_MEDIUM; +} + +static gboolean +real_can_zoom_in (NautilusFilesView *files_view) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (files_view); + + return self->zoom_level < NAUTILUS_GRID_ZOOM_LEVEL_EXTRA_LARGE; +} + +static gboolean +real_can_zoom_out (NautilusFilesView *files_view) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (files_view); + + return self->zoom_level > NAUTILUS_GRID_ZOOM_LEVEL_SMALL; +} + +/* We only care about the keyboard activation part that GtkGridView provides, + * but we don't need any special filtering here. Indeed, we ask GtkGridView + * to not activate on single click, and we get to handle double clicks before + * GtkGridView does (as one of widget subclassing's goal is to modify the parent + * class's behavior), while claiming the click gestures, so it means GtkGridView + * will never react to a click event to emit this signal. So we should be pretty + * safe here with regards to our custom item click handling. + */ +static void +on_grid_view_item_activated (GtkGridView *grid_view, + guint position, + gpointer user_data) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (user_data); + + nautilus_files_view_activate_selection (NAUTILUS_FILES_VIEW (self)); +} + +static guint +real_get_icon_size (NautilusListBase *files_model_view) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (files_model_view); + + return get_icon_size_for_zoom_level (self->zoom_level); +} + +static GtkWidget * +real_get_view_ui (NautilusListBase *files_model_view) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (files_model_view); + + return GTK_WIDGET (self->view_ui); +} + +static void +real_scroll_to_item (NautilusListBase *files_model_view, + guint position) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (files_model_view); + + gtk_widget_activate_action (GTK_WIDGET (self->view_ui), + "list.scroll-to-item", + "u", + position); +} + +static void +real_sort_directories_first_changed (NautilusFilesView *files_view) +{ + NautilusGridView *self; + NautilusViewModel *model; + g_autoptr (GtkCustomSorter) sorter = NULL; + + self = NAUTILUS_GRID_VIEW (files_view); + self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self)); + + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + sorter = gtk_custom_sorter_new (nautilus_grid_view_sort, self, NULL); + nautilus_view_model_set_sorter (model, GTK_SORTER (sorter)); +} + +static void +action_sort_order_changed (GSimpleAction *action, + GVariant *value, + gpointer user_data) +{ + const gchar *target_name; + NautilusGridView *self = NAUTILUS_GRID_VIEW (user_data); + NautilusViewModel *model; + g_autoptr (GtkCustomSorter) sorter = NULL; + + /* Don't resort if the action is in the same state as before */ + if (g_variant_equal (value, g_action_get_state (G_ACTION (action)))) + { + return; + } + + g_variant_get (value, "(&sb)", &target_name, &self->reversed); + self->sort_type = get_sorts_type_from_metadata_text (target_name); + + sorter = gtk_custom_sorter_new (nautilus_grid_view_sort, self, NULL); + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + nautilus_view_model_set_sorter (model, GTK_SORTER (sorter)); + set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)), + target_name, + self->reversed); + + g_simple_action_set_state (action, value); +} + +static guint +real_get_view_id (NautilusFilesView *files_view) +{ + return NAUTILUS_VIEW_GRID_ID; +} + +static void +action_zoom_to_level (GSimpleAction *action, + GVariant *state, + gpointer user_data) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (user_data); + int zoom_level; + + zoom_level = g_variant_get_int32 (state); + set_zoom_level (self, zoom_level); + g_simple_action_set_state (G_SIMPLE_ACTION (action), state); + + if (g_settings_get_enum (nautilus_icon_view_preferences, + NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level) + { + g_settings_set_enum (nautilus_icon_view_preferences, + NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL, + zoom_level); + } +} + +static void +on_captions_preferences_changed (NautilusGridView *self) +{ + set_captions_from_preferences (self); + + /* Hack: this relies on the assumption that NautilusGridCell updates + * captions whenever the icon size is set (even if it's the same value). */ + nautilus_list_base_set_icon_size (NAUTILUS_LIST_BASE (self), + get_icon_size_for_zoom_level (self->zoom_level)); +} + +static void +dispose (GObject *object) +{ + G_OBJECT_CLASS (nautilus_grid_view_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + G_OBJECT_CLASS (nautilus_grid_view_parent_class)->finalize (object); +} + +static void +bind_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) +{ + GtkWidget *cell; + NautilusViewItem *item; + + cell = gtk_list_item_get_child (listitem); + item = NAUTILUS_VIEW_ITEM (gtk_list_item_get_item (listitem)); + + nautilus_view_item_set_item_ui (item, cell); + + if (nautilus_view_cell_once (NAUTILUS_VIEW_CELL (cell))) + { + GtkWidget *parent; + + /* At the time of ::setup emission, the item ui has got no parent yet, + * that's why we need to complete the widget setup process here, on the + * first time ::bind is emitted. */ + parent = gtk_widget_get_parent (cell); + gtk_widget_set_halign (parent, GTK_ALIGN_CENTER); + gtk_widget_set_valign (parent, GTK_ALIGN_START); + gtk_widget_set_margin_top (parent, 3); + gtk_widget_set_margin_bottom (parent, 3); + gtk_widget_set_margin_start (parent, 3); + gtk_widget_set_margin_end (parent, 3); + } +} + +static void +unbind_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) +{ + NautilusViewItem *item; + + item = NAUTILUS_VIEW_ITEM (gtk_list_item_get_item (listitem)); + + nautilus_view_item_set_item_ui (item, NULL); +} + +static void +setup_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) +{ + NautilusGridView *self = NAUTILUS_GRID_VIEW (user_data); + NautilusGridCell *cell; + + cell = nautilus_grid_cell_new (NAUTILUS_LIST_BASE (self)); + setup_cell_common (listitem, NAUTILUS_VIEW_CELL (cell)); + + nautilus_grid_cell_set_caption_attributes (cell, self->caption_attributes); +} + +static GtkGridView * +create_view_ui (NautilusGridView *self) +{ + NautilusViewModel *model; + GtkListItemFactory *factory; + GtkWidget *widget; + + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + + factory = gtk_signal_list_item_factory_new (); + g_signal_connect (factory, "setup", G_CALLBACK (setup_cell), self); + g_signal_connect (factory, "bind", G_CALLBACK (bind_cell), self); + g_signal_connect (factory, "unbind", G_CALLBACK (unbind_cell), self); + + widget = gtk_grid_view_new (GTK_SELECTION_MODEL (model), factory); + gtk_widget_set_focusable (widget, TRUE); + gtk_widget_set_valign (widget, GTK_ALIGN_START); + + /* We don't use the built-in child activation feature for clicks because it + * doesn't fill all our needs nor does it match our expected behavior. + * Instead, we roll our own event handling and double/single click mode. + * However, GtkGridView:single-click-activate has other effects besides + * activation, as it affects the selection behavior as well (e.g. selects on + * hover). Setting it to FALSE gives us the expected behavior. */ + gtk_grid_view_set_single_click_activate (GTK_GRID_VIEW (widget), FALSE); + gtk_grid_view_set_max_columns (GTK_GRID_VIEW (widget), 20); + gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (widget), TRUE); + + /* While we don't want to use GTK's click activation, we'll let it handle + * the key activation part (with Enter). + */ + g_signal_connect (widget, "activate", G_CALLBACK (on_grid_view_item_activated), self); + + return GTK_GRID_VIEW (widget); +} + +const GActionEntry view_icon_actions[] = +{ + { "sort", NULL, "(sb)", "('invalid',false)", action_sort_order_changed }, + { "zoom-to-level", NULL, NULL, "100", action_zoom_to_level } +}; + +static void +nautilus_grid_view_class_init (NautilusGridViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass); + NautilusListBaseClass *files_model_view_class = NAUTILUS_LIST_BASE_CLASS (klass); + + object_class->dispose = dispose; + object_class->finalize = finalize; + + files_view_class->bump_zoom_level = real_bump_zoom_level; + files_view_class->can_zoom_in = real_can_zoom_in; + files_view_class->can_zoom_out = real_can_zoom_out; + files_view_class->sort_directories_first_changed = real_sort_directories_first_changed; + files_view_class->get_view_id = real_get_view_id; + files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level; + files_view_class->is_zoom_level_default = real_is_zoom_level_default; + + files_model_view_class->get_icon_size = real_get_icon_size; + files_model_view_class->get_view_ui = real_get_view_ui; + files_model_view_class->scroll_to_item = real_scroll_to_item; +} + +static void +nautilus_grid_view_init (NautilusGridView *self) +{ + GtkWidget *content_widget; + + gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-grid-view"); + + set_captions_from_preferences (self); + g_signal_connect_object (nautilus_icon_view_preferences, + "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS, + G_CALLBACK (on_captions_preferences_changed), + self, + G_CONNECT_SWAPPED); + + content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self)); + + self->view_ui = create_view_ui (self); + nautilus_list_base_setup_gestures (NAUTILUS_LIST_BASE (self)); + + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget), + GTK_WIDGET (self->view_ui)); + + self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)); + g_action_map_add_action_entries (G_ACTION_MAP (self->action_group), + view_icon_actions, + G_N_ELEMENTS (view_icon_actions), + self); + + self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self)); + + self->zoom_level = get_default_zoom_level (); + /* 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 (self)), + "zoom-to-level", g_variant_new_int32 (self->zoom_level)); +} + +NautilusGridView * +nautilus_grid_view_new (NautilusWindowSlot *slot) +{ + return g_object_new (NAUTILUS_TYPE_GRID_VIEW, + "window-slot", slot, + NULL); +} diff --git a/src/nautilus-grid-view.h b/src/nautilus-grid-view.h new file mode 100644 index 0000000000000000000000000000000000000000..4c8835fcb1a7516aa571a1447a98713362917091 --- /dev/null +++ b/src/nautilus-grid-view.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "nautilus-list-base.h" +#include "nautilus-window-slot.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_GRID_VIEW (nautilus_grid_view_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusGridView, nautilus_grid_view, NAUTILUS, GRID_VIEW, NautilusListBase) + +NautilusGridView *nautilus_grid_view_new (NautilusWindowSlot *slot); + +G_END_DECLS diff --git a/src/nautilus-icon-info.h b/src/nautilus-icon-info.h index e5fab7a6bf0f9db30d4ee5215240d20542c437f9..074789fda864d5c43e28eb24a6e8b0bdc1b9dea6 100644 --- a/src/nautilus-icon-info.h +++ b/src/nautilus-icon-info.h @@ -8,9 +8,6 @@ G_BEGIN_DECLS -#define NAUTILUS_LIST_ZOOM_LEVEL_N_ENTRIES (NAUTILUS_LIST_ZOOM_LEVEL_LARGER + 1) -#define NAUTILUS_GRID_ZOOM_LEVEL_N_ENTRIES (NAUTILUS_GRID_ZOOM_LEVEL_LARGEST + 1) - /* Maximum size of an icon that the icon factory will ever produce */ #define NAUTILUS_ICON_MAXIMUM_SIZE 320 diff --git a/src/nautilus-label-cell.c b/src/nautilus-label-cell.c new file mode 100644 index 0000000000000000000000000000000000000000..7d3319dba11d971ae6e87cf1646b530f240cdc96 --- /dev/null +++ b/src/nautilus-label-cell.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2022 António Fernandes + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* Needed for NautilusColumn (full GType). */ +#include + +#include "nautilus-label-cell.h" + +struct _NautilusLabelCell +{ + NautilusViewCell parent_instance; + + GSignalGroup *item_signal_group; + + NautilusColumn *column; + GQuark attribute_q; + + GtkLabel *label; + + gboolean show_snippet; +}; + +G_DEFINE_TYPE (NautilusLabelCell, nautilus_label_cell, NAUTILUS_TYPE_VIEW_CELL) + +enum +{ + PROP_0, + PROP_COLUMN, + N_PROPS +}; + +static GParamSpec *properties[N_PROPS] = { NULL, }; + +static void +on_file_changed (NautilusLabelCell *self) +{ + NautilusViewItem *item; + NautilusFile *file; + g_autofree gchar *string = NULL; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + file = nautilus_view_item_get_file (item); + + string = nautilus_file_get_string_attribute_q (file, self->attribute_q); + gtk_label_set_text (self->label, string); +} + +static void +nautilus_label_cell_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusLabelCell *self = NAUTILUS_LABEL_CELL (object); + + switch (prop_id) + { + case PROP_COLUMN: + { + self->column = g_value_get_object (value); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +nautilus_label_cell_init (NautilusLabelCell *self) +{ + GtkWidget *child; + + child = gtk_label_new (NULL); + adw_bin_set_child (ADW_BIN (self), child); + gtk_widget_set_valign (child, GTK_ALIGN_CENTER); + gtk_widget_add_css_class (child, "dim-label"); + self->label = GTK_LABEL (child); + + /* Connect automatically to an item. */ + self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM); + g_signal_group_connect_swapped (self->item_signal_group, "file-changed", + (GCallback) on_file_changed, self); + g_signal_connect_object (self->item_signal_group, "bind", + (GCallback) on_file_changed, self, + G_CONNECT_SWAPPED); + g_object_bind_property (self, "item", + self->item_signal_group, "target", + G_BINDING_SYNC_CREATE); +} + +static void +nautilus_label_cell_constructed (GObject *object) +{ + NautilusLabelCell *self = NAUTILUS_LABEL_CELL (object); + g_autofree gchar *column_name = NULL; + gfloat xalign; + + G_OBJECT_CLASS (nautilus_label_cell_parent_class)->constructed (object); + + g_object_get (self->column, + "attribute_q", &self->attribute_q, + "name", &column_name, + "xalign", &xalign, + NULL); + gtk_label_set_xalign (self->label, xalign); + + if (g_strcmp0 (column_name, "permissions") == 0) + { + gtk_widget_add_css_class (GTK_WIDGET (self->label), "monospace"); + } + else + { + gtk_widget_add_css_class (GTK_WIDGET (self->label), "numeric"); + } +} + +static void +nautilus_label_cell_finalize (GObject *object) +{ + NautilusLabelCell *self = (NautilusLabelCell *) object; + + g_object_unref (self->item_signal_group); + G_OBJECT_CLASS (nautilus_label_cell_parent_class)->finalize (object); +} + +static void +nautilus_label_cell_class_init (NautilusLabelCellClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = nautilus_label_cell_set_property; + object_class->constructed = nautilus_label_cell_constructed; + object_class->finalize = nautilus_label_cell_finalize; + + properties[PROP_COLUMN] = g_param_spec_object ("column", + "", "", + NAUTILUS_TYPE_COLUMN, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +NautilusViewCell * +nautilus_label_cell_new (NautilusListBase *view, + NautilusColumn *column) +{ + return NAUTILUS_VIEW_CELL (g_object_new (NAUTILUS_TYPE_LABEL_CELL, + "view", view, + "column", column, + NULL)); +} diff --git a/src/nautilus-label-cell.h b/src/nautilus-label-cell.h new file mode 100644 index 0000000000000000000000000000000000000000..b476ef9a0bddb03f3aa79c1b1488cbe11afffdba --- /dev/null +++ b/src/nautilus-label-cell.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2022 António Fernandes + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +/* Needed for NautilusColumn (typedef only). */ +#include "nautilus-types.h" + +#include "nautilus-view-cell.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_LABEL_CELL (nautilus_label_cell_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusLabelCell, nautilus_label_cell, NAUTILUS, LABEL_CELL, NautilusViewCell) + +NautilusViewCell * nautilus_label_cell_new (NautilusListBase *view, + NautilusColumn *column); + +G_END_DECLS diff --git a/src/nautilus-list-base-private.h b/src/nautilus-list-base-private.h new file mode 100644 index 0000000000000000000000000000000000000000..96944d52e96251559181b08c151d8ea0491a500a --- /dev/null +++ b/src/nautilus-list-base-private.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "nautilus-list-base.h" +#include "nautilus-view-model.h" +#include "nautilus-view-cell.h" + +/* + * Private header to be included only by subclasses. + */ + +G_BEGIN_DECLS + +/* Methods */ +NautilusViewModel *nautilus_list_base_get_model (NautilusListBase *self); +void nautilus_list_base_set_icon_size (NautilusListBase *self, + gint icon_size); +void nautilus_list_base_setup_gestures (NautilusListBase *self); + +/* Shareable helpers */ +void set_directory_sort_metadata (NautilusFile *file, + const gchar *metadata_name, + gboolean reversed); +const NautilusFileSortType get_sorts_type_from_metadata_text (const char *metadata_name); +void setup_cell_common (GtkListItem *listitem, + NautilusViewCell *cell); + +G_END_DECLS diff --git a/src/nautilus-list-base.c b/src/nautilus-list-base.c new file mode 100644 index 0000000000000000000000000000000000000000..355961e94297978dd4ebddac6ec70b99a528b768 --- /dev/null +++ b/src/nautilus-list-base.c @@ -0,0 +1,1291 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "nautilus-list-base-private.h" + +#include "nautilus-clipboard.h" +#include "nautilus-view-cell.h" +#include "nautilus-view-item.h" +#include "nautilus-view-model.h" +#include "nautilus-files-view.h" +#include "nautilus-file.h" +#include "nautilus-metadata.h" +#include "nautilus-global-preferences.h" +#include "nautilus-thumbnails.h" + +/** + * NautilusListBase: + * + * Abstract class containing shared code for #NautilusFilesView implementations + * using a #GtkListBase-derived widget (e.g. GtkGridView, GtkColumnView) which + * takes a #NautilusViewModel instance as its model and and a #NautilusViewCell + * instance as #GtkListItem:child. + * + * It has been has been created to avoid code duplication in implementations, + * while keeping #NautilusFilesView implementation-agnostic (should the need for + * non-#GtkListBase views arise). + */ + +typedef struct _NautilusListBasePrivate NautilusListBasePrivate; +struct _NautilusListBasePrivate +{ + NautilusViewModel *model; + + GList *cut_files; + + guint scroll_to_file_handle_id; + guint prioritize_thumbnailing_handle_id; + GtkAdjustment *vadjustment; + + gboolean single_click_mode; + gboolean activate_on_release; + gboolean deny_background_click; +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusListBase, nautilus_list_base, NAUTILUS_TYPE_FILES_VIEW) + +typedef struct +{ + const NautilusFileSortType sort_type; + const gchar *metadata_name; +} SortConstants; + +static const SortConstants sorts_constants[] = +{ + { + NAUTILUS_FILE_SORT_BY_DISPLAY_NAME, + "name", + }, + { + NAUTILUS_FILE_SORT_BY_SIZE, + "size", + }, + { + NAUTILUS_FILE_SORT_BY_TYPE, + "type", + }, + { + NAUTILUS_FILE_SORT_BY_MTIME, + "modification date", + }, + { + NAUTILUS_FILE_SORT_BY_ATIME, + "access date", + }, + { + NAUTILUS_FILE_SORT_BY_BTIME, + "creation date", + }, + { + NAUTILUS_FILE_SORT_BY_TRASHED_TIME, + "trashed", + }, + { + NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE, + "search_relevance", + }, + { + NAUTILUS_FILE_SORT_BY_RECENCY, + "recency", + }, +}; + +static inline NautilusViewItem * +get_view_item (GListModel *model, + guint position) +{ + return NAUTILUS_VIEW_ITEM (g_list_model_get_item (model, position)); +} + +static const SortConstants * +get_sorts_constants_from_sort_type (NautilusFileSortType sort_type) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++) + { + if (sort_type == sorts_constants[i].sort_type) + { + return &sorts_constants[i]; + } + } + + return &sorts_constants[0]; +} + +static const SortConstants * +get_sorts_constants_from_metadata_text (const char *metadata_name) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++) + { + if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0) + { + return &sorts_constants[i]; + } + } + + return &sorts_constants[0]; +} + +const NautilusFileSortType +get_sorts_type_from_metadata_text (const char *metadata_name) +{ + return get_sorts_constants_from_metadata_text (metadata_name)->sort_type; +} + +static const SortConstants * +get_default_sort_order (NautilusFile *file, + gboolean *reversed) +{ + NautilusFileSortType sort_type; + + sort_type = nautilus_file_get_default_sort_type (file, reversed); + + return get_sorts_constants_from_sort_type (sort_type); +} + +static const SortConstants * +get_directory_sort_by (NautilusFile *file, + gboolean *reversed) +{ + const SortConstants *default_sort; + g_autofree char *sort_by = NULL; + + default_sort = get_default_sort_order (file, reversed); + g_return_val_if_fail (default_sort != NULL, NULL); + + sort_by = nautilus_file_get_metadata (file, + NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY, + default_sort->metadata_name); + + *reversed = nautilus_file_get_boolean_metadata (file, + NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED, + *reversed); + + return get_sorts_constants_from_metadata_text (sort_by); +} + +void +set_directory_sort_metadata (NautilusFile *file, + const gchar *metadata_name, + gboolean reversed) +{ + const SortConstants *default_sort; + gboolean default_reversed; + + default_sort = get_default_sort_order (file, &default_reversed); + + nautilus_file_set_metadata (file, + NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY, + default_sort->metadata_name, + metadata_name); + nautilus_file_set_boolean_metadata (file, + NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED, + default_reversed, + reversed); +} + +static void +update_sort_order_from_metadata_and_preferences (NautilusListBase *self) +{ + const SortConstants *default_directory_sort; + GActionGroup *view_action_group; + gboolean reversed; + + default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)), + &reversed); + view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)); + g_action_group_change_action_state (view_action_group, + "sort", + g_variant_new ("(sb)", + default_directory_sort->metadata_name, + reversed)); +} + +void +nautilus_list_base_set_icon_size (NautilusListBase *self, + gint icon_size) +{ + GListModel *model; + guint n_items; + + model = G_LIST_MODEL (nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self))); + + n_items = g_list_model_get_n_items (model); + for (guint i = 0; i < n_items; i++) + { + g_autoptr (NautilusViewItem) current_item = NULL; + + current_item = get_view_item (model, i); + nautilus_view_item_set_icon_size (current_item, icon_size); + } +} + +/* GtkListBase changes selection only with the primary button, and only after + * release. But we need to antecipate selection earlier if we are to activate it + * or open its context menu. This helper should be used in these situations if + * it's desirable to act on a multi-item selection, because it preserves it. */ +static void +select_single_item_if_not_selected (NautilusListBase *self, + NautilusViewItem *item) +{ + NautilusViewModel *model; + guint position; + + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + position = nautilus_view_model_get_index (model, item); + if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), position)) + { + gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), position, TRUE); + } +} + +static void +activate_selection_on_click (NautilusListBase *self, + gboolean open_in_new_tab) +{ + g_autolist (NautilusFile) selection = NULL; + NautilusOpenFlags flags = 0; + NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (self); + + selection = nautilus_view_get_selection (NAUTILUS_VIEW (self)); + if (open_in_new_tab) + { + flags |= NAUTILUS_OPEN_FLAG_NEW_TAB; + flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE; + } + nautilus_files_view_activate_files (files_view, selection, flags, TRUE); +} + +static void +on_item_click_pressed (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell)); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + NautilusViewItem *item; + guint button; + GdkModifierType modifiers; + gboolean selection_mode; + + item = nautilus_view_cell_get_item (cell); + g_return_if_fail (item != NULL); + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture)); + selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)); + + /* Before anything else, store event state to be read by other handlers. */ + priv->deny_background_click = TRUE; + priv->activate_on_release = (priv->single_click_mode && + button == GDK_BUTTON_PRIMARY && + n_press == 1 && + !selection_mode); + + /* It's safe to claim event sequence on press in the following cases because + * they don't interfere with touch scrolling. */ + if (button == GDK_BUTTON_PRIMARY && n_press == 2 && !priv->single_click_mode) + { + activate_selection_on_click (self, modifiers & GDK_SHIFT_MASK); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + } + else if (button == GDK_BUTTON_MIDDLE && n_press == 1) + { + /* Antecipate selection, if necessary, to activate it. */ + select_single_item_if_not_selected (self, item); + activate_selection_on_click (self, TRUE); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + } + else if (button == GDK_BUTTON_SECONDARY && n_press == 1) + { + gdouble view_x, view_y; + + /* Antecipate selection, if necessary, for the context menu. */ + select_single_item_if_not_selected (self, item); + + gtk_widget_translate_coordinates (GTK_WIDGET (cell), GTK_WIDGET (self), + x, y, + &view_x, &view_y); + nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self), + view_x, view_y); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); + } +} + +static void +on_item_click_released (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell)); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + if (priv->activate_on_release) + { + NautilusViewModel *model; + NautilusViewItem *item; + guint i; + + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + item = nautilus_view_cell_get_item (cell); + g_return_if_fail (item != NULL); + i = nautilus_view_model_get_index (model, item); + + /* Antecipate selection, enforcing single selection of target item. */ + gtk_selection_model_select_item (GTK_SELECTION_MODEL (model), i, TRUE); + + activate_selection_on_click (self, FALSE); + } + + priv->activate_on_release = FALSE; + priv->deny_background_click = FALSE; +} + +static void +on_item_click_stopped (GtkGestureClick *gesture, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell)); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + priv->activate_on_release = FALSE; + priv->deny_background_click = FALSE; +} + +static void +on_view_click_pressed (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) +{ + NautilusListBase *self = user_data; + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + guint button; + GdkModifierType modifiers; + gboolean selection_mode; + + if (priv->deny_background_click) + { + /* Item was clicked. */ + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); + return; + } + + /* Don't interfere with GtkListBase default selection handling when + * holding Ctrl and Shift. */ + modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture)); + selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)); + if (!selection_mode) + { + nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL); + } + + button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); + if (button == GDK_BUTTON_SECONDARY) + { + GtkWidget *event_widget; + gdouble view_x; + gdouble view_y; + + event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); + gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self), + x, y, + &view_x, &view_y); + nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self), + view_x, view_y); + } +} + +static void +on_item_longpress_pressed (GtkGestureLongPress *gesture, + gdouble x, + gdouble y, + gpointer user_data) +{ + NautilusViewCell *cell = user_data; + NautilusListBase *self = NAUTILUS_LIST_BASE (nautilus_view_cell_get_view (cell)); + GtkWidget *event_widget; + gdouble view_x; + gdouble view_y; + + event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); + + gtk_widget_translate_coordinates (event_widget, + GTK_WIDGET (self), + x, y, &view_x, &view_y); + + nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self), + view_x, view_y); + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); +} + +static void +on_view_longpress_pressed (GtkGestureLongPress *gesture, + gdouble x, + gdouble y, + gpointer user_data) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (user_data); + GtkWidget *event_widget; + gdouble view_x; + gdouble view_y; + + event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); + + gtk_widget_translate_coordinates (event_widget, + GTK_WIDGET (self), + x, y, &view_x, &view_y); + + nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL); + nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self), + view_x, view_y); +} + +void +setup_cell_common (GtkListItem *listitem, + NautilusViewCell *cell) +{ + GtkEventController *controller; + + g_object_bind_property (listitem, "item", + cell, "item", + G_BINDING_SYNC_CREATE); + gtk_list_item_set_child (listitem, GTK_WIDGET (cell)); + + controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); + gtk_widget_add_controller (GTK_WIDGET (cell), controller); + gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0); + g_signal_connect (controller, "pressed", G_CALLBACK (on_item_click_pressed), cell); + g_signal_connect (controller, "stopped", G_CALLBACK (on_item_click_stopped), cell); + g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released), cell); + + controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ()); + gtk_widget_add_controller (GTK_WIDGET (cell), controller); + gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE); + g_signal_connect (controller, "pressed", G_CALLBACK (on_item_longpress_pressed), cell); +} + +static void +nautilus_list_base_scroll_to_item (NautilusListBase *self, + guint position) +{ + NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->scroll_to_item (self, position); +} + +static guint +nautilus_list_base_get_icon_size (NautilusListBase *self) +{ + return NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->get_icon_size (self); +} + +static GtkWidget * +nautilus_list_base_get_view_ui (NautilusListBase *self) +{ + return NAUTILUS_LIST_BASE_CLASS (G_OBJECT_GET_CLASS (self))->get_view_ui (self); +} + +typedef struct +{ + NautilusListBase *self; + GQuark attribute_q; +} NautilusListBaseSortData; + +static void +real_begin_loading (NautilusFilesView *files_view) +{ + /*TODO move this to the files view class begin_loading and hook up? */ + + + /* TODO: This calls sort once, and update_context_menus calls update_actions + * which calls the action again + */ + update_sort_order_from_metadata_and_preferences (NAUTILUS_LIST_BASE (files_view)); + + /* 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 (files_view); + nautilus_files_view_update_toolbar_menus (files_view); +} + +static void +real_clear (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + nautilus_view_model_remove_all_items (priv->model); +} + +static void +set_click_mode_from_settings (NautilusListBase *self) +{ + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + int click_policy; + + click_policy = g_settings_get_enum (nautilus_preferences, + NAUTILUS_PREFERENCES_CLICK_POLICY); + + priv->single_click_mode = (click_policy == NAUTILUS_CLICK_POLICY_SINGLE); +} + +static void +real_click_policy_changed (NautilusFilesView *files_view) +{ + set_click_mode_from_settings (NAUTILUS_LIST_BASE (files_view)); +} + +static void +real_file_changed (NautilusFilesView *files_view, + NautilusFile *file, + NautilusDirectory *directory) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + NautilusViewItem *item; + + item = nautilus_view_model_get_item_from_file (priv->model, file); + nautilus_view_item_file_changed (item); +} + +static GList * +real_get_selection (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (GtkSelectionFilterModel) selection = NULL; + guint n_selected; + GList *selected_files = NULL; + + selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (priv->model)); + n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection)); + for (guint i = 0; i < n_selected; i++) + { + g_autoptr (NautilusViewItem) item = NULL; + + item = get_view_item (G_LIST_MODEL (selection), i); + selected_files = g_list_prepend (selected_files, + g_object_ref (nautilus_view_item_get_file (item))); + } + + return selected_files; +} + +static gboolean +real_is_empty (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + return g_list_model_get_n_items (G_LIST_MODEL (priv->model)) == 0; +} + +static void +real_end_file_changes (NautilusFilesView *files_view) +{ +} + +static void +real_remove_file (NautilusFilesView *files_view, + NautilusFile *file, + NautilusDirectory *directory) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + NautilusViewItem *item; + + item = nautilus_view_model_get_item_from_file (priv->model, file); + if (item != NULL) + { + nautilus_view_model_remove_item (priv->model, item); + } +} + +static GQueue * +convert_glist_to_queue (GList *list) +{ + GList *l; + GQueue *queue; + + queue = g_queue_new (); + for (l = list; l != NULL; l = l->next) + { + g_queue_push_tail (queue, l->data); + } + + return queue; +} + +static GQueue * +convert_files_to_items (NautilusListBase *self, + GQueue *files) +{ + GList *l; + GQueue *models; + + models = g_queue_new (); + for (l = g_queue_peek_head_link (files); l != NULL; l = l->next) + { + NautilusViewItem *item; + + item = nautilus_view_item_new (NAUTILUS_FILE (l->data), + nautilus_list_base_get_icon_size (self)); + g_queue_push_tail (models, item); + } + + return models; +} + +static void +real_set_selection (NautilusFilesView *files_view, + GList *selection) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (GQueue) selection_files = NULL; + g_autoptr (GQueue) selection_items = NULL; + g_autoptr (GtkBitset) update_set = NULL; + g_autoptr (GtkBitset) selection_set = NULL; + + update_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (priv->model)); + selection_set = gtk_bitset_new_empty (); + + /* Convert file list into set of model indices */ + selection_files = convert_glist_to_queue (selection); + selection_items = nautilus_view_model_get_items_from_files (priv->model, selection_files); + for (GList *l = g_queue_peek_head_link (selection_items); l != NULL; l = l->next) + { + gtk_bitset_add (selection_set, + nautilus_view_model_get_index (priv->model, l->data)); + } + + /* Set focus on the first selected row. */ + if (!g_queue_is_empty (selection_items)) + { + NautilusViewItem *item = g_queue_peek_head (selection_items); + GtkWidget *parent = gtk_widget_get_parent (nautilus_view_item_get_item_ui (item)); + + if (!gtk_widget_grab_focus (parent)) + { + /* In GtkColumnView, the parent is a cell; its parent is the row. */ + gtk_widget_grab_focus (gtk_widget_get_parent (parent)); + } + } + + gtk_bitset_union (update_set, selection_set); + gtk_selection_model_set_selection (GTK_SELECTION_MODEL (priv->model), + selection_set, + update_set); +} + +static void +real_select_all (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + gtk_selection_model_select_all (GTK_SELECTION_MODEL (priv->model)); +} + +static void +real_invert_selection (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (priv->model); + g_autoptr (GtkBitset) selected = NULL; + g_autoptr (GtkBitset) all = NULL; + g_autoptr (GtkBitset) new_selected = NULL; + + selected = gtk_selection_model_get_selection (selection_model); + + /* We are going to flip the selection state of every item in the model. */ + all = gtk_bitset_new_range (0, g_list_model_get_n_items (G_LIST_MODEL (priv->model))); + + /* The new selection is all items minus the ones currently selected. */ + new_selected = gtk_bitset_copy (all); + gtk_bitset_subtract (new_selected, selected); + + gtk_selection_model_set_selection (selection_model, new_selected, all); +} + +static guint +get_first_selected_item (NautilusListBase *self) +{ + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autolist (NautilusFile) selection = NULL; + NautilusFile *file; + NautilusViewItem *item; + + selection = nautilus_view_get_selection (NAUTILUS_VIEW (self)); + if (selection == NULL) + { + return G_MAXUINT; + } + + file = NAUTILUS_FILE (selection->data); + item = nautilus_view_model_get_item_from_file (priv->model, file); + + return nautilus_view_model_get_index (priv->model, item); +} + +static void +real_reveal_selection (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + + nautilus_list_base_scroll_to_item (self, get_first_selected_item (self)); +} + +static int +real_compare_files (NautilusFilesView *files_view, + NautilusFile *file1, + NautilusFile *file2) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + GtkSorter *sorter; + g_autoptr (NautilusViewItem) item1 = NULL; + g_autoptr (NautilusViewItem) item2 = NULL; + + sorter = nautilus_view_model_get_sorter (priv->model); + if (sorter == NULL) + { + return 0; + } + + /* Generate fake model items for sorter use only. */ + item1 = nautilus_view_item_new (file1, NAUTILUS_GRID_ICON_SIZE_SMALL); + item2 = nautilus_view_item_new (file2, NAUTILUS_GRID_ICON_SIZE_SMALL); + + return gtk_sorter_compare (sorter, item1, item2); +} + +static void +on_clipboard_contents_received (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object); + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + NautilusClipboard *clip; + NautilusViewItem *item; + + for (GList *l = priv->cut_files; l != NULL; l = l->next) + { + item = nautilus_view_model_get_item_from_file (priv->model, l->data); + if (item != NULL) + { + nautilus_view_item_set_cut (item, FALSE); + } + } + g_clear_list (&priv->cut_files, g_object_unref); + + clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL); + if (clip != NULL && nautilus_clipboard_is_cut (clip)) + { + priv->cut_files = g_list_copy_deep (nautilus_clipboard_peek_files (clip), + (GCopyFunc) g_object_ref, + NULL); + } + + for (GList *l = priv->cut_files; l != NULL; l = l->next) + { + item = nautilus_view_model_get_item_from_file (priv->model, l->data); + if (item != NULL) + { + nautilus_view_item_set_cut (item, TRUE); + } + } +} + +static void +update_clipboard_status (NautilusFilesView *view) +{ + nautilus_files_view_get_clipboard_async (view, + on_clipboard_contents_received, + NULL); +} + +static void +on_clipboard_owner_changed (GdkClipboard *clipboard, + gpointer user_data) +{ + update_clipboard_status (NAUTILUS_FILES_VIEW (user_data)); +} + +static void +real_end_loading (NautilusFilesView *files_view, + gboolean all_files_seen) +{ + update_clipboard_status (files_view); +} + +static guint +get_first_visible_item (NautilusListBase *self) +{ + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + guint n_items; + gdouble scrolled_y; + GtkWidget *view_ui; + + n_items = g_list_model_get_n_items (G_LIST_MODEL (priv->model)); + scrolled_y = gtk_adjustment_get_value (priv->vadjustment); + view_ui = nautilus_list_base_get_view_ui (self); + for (guint i = 0; i < n_items; i++) + { + g_autoptr (NautilusViewItem) item = NULL; + GtkWidget *item_ui; + + item = get_view_item (G_LIST_MODEL (priv->model), i); + item_ui = nautilus_view_item_get_item_ui (item); + if (item_ui != NULL) + { + gdouble y; + + gtk_widget_translate_coordinates (item_ui, view_ui, + 0, 0, NULL, &y); + if (gtk_widget_is_visible (item_ui) && y >= scrolled_y) + { + return i; + } + } + } + + return G_MAXUINT; +} + +static char * +real_get_first_visible_file (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + guint i; + g_autoptr (NautilusViewItem) item = NULL; + gchar *uri = NULL; + + i = get_first_visible_item (self); + if (i < G_MAXUINT) + { + item = get_view_item (G_LIST_MODEL (priv->model), i); + uri = nautilus_file_get_uri (nautilus_view_item_get_file (item)); + } + return uri; +} + +typedef struct +{ + NautilusListBase *view; + char *uri; +} ScrollToFileData; + +static void +scroll_to_file_data_free (ScrollToFileData *data) +{ + g_free (data->uri); + g_free (data); +} + +static gboolean +scroll_to_file_on_idle (ScrollToFileData *data) +{ + NautilusListBase *self = data->view; + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (NautilusFile) file = NULL; + NautilusViewItem *item; + guint i; + + file = nautilus_file_get_existing_by_uri (data->uri); + item = nautilus_view_model_get_item_from_file (priv->model, file); + i = nautilus_view_model_get_index (priv->model, item); + + nautilus_list_base_scroll_to_item (self, i); + + priv->scroll_to_file_handle_id = 0; + return G_SOURCE_REMOVE; +} + +static void +real_scroll_to_file (NautilusFilesView *files_view, + const char *uri) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + ScrollToFileData *data; + guint handle_id; + + data = g_new (ScrollToFileData, 1); + data->view = self; + data->uri = g_strdup (uri); + handle_id = g_idle_add_full (G_PRIORITY_LOW, + (GSourceFunc) scroll_to_file_on_idle, + data, + (GDestroyNotify) scroll_to_file_data_free); + priv->scroll_to_file_handle_id = handle_id; +} + +static void +real_add_files (NautilusFilesView *files_view, + GList *files) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (GQueue) files_queue = NULL; + g_autoptr (GQueue) items = NULL; + gdouble adjustment_value; + + files_queue = convert_glist_to_queue (files); + items = convert_files_to_items (self, files_queue); + nautilus_view_model_add_items (priv->model, items); + + /* GtkListBase anchoring doesn't cope well with our lazy loading. + * Assuming that GtkListBase|list.scroll-to-item resets the anchor to 0, use + * that as a workaround to prevent scrolling while we are at the top. */ + adjustment_value = gtk_adjustment_get_value (priv->vadjustment); + if (G_APPROX_VALUE (adjustment_value, 0.0, DBL_EPSILON)) + { + nautilus_list_base_scroll_to_item (self, 0); + } +} + +static void +real_select_first (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (NautilusViewItem) item = NULL; + NautilusFile *file; + g_autoptr (GList) selection = NULL; + + item = get_view_item (G_LIST_MODEL (priv->model), 0); + if (item == NULL) + { + return; + } + file = nautilus_view_item_get_file (item); + selection = g_list_prepend (selection, file); + nautilus_view_set_selection (NAUTILUS_VIEW (files_view), selection); +} + +static GdkRectangle * +get_rectangle_for_item_ui (NautilusListBase *self, + GtkWidget *item_ui) +{ + GdkRectangle *rectangle; + GtkWidget *content_widget; + gdouble view_x; + gdouble view_y; + + rectangle = g_new0 (GdkRectangle, 1); + gtk_widget_get_allocation (item_ui, rectangle); + + content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self)); + gtk_widget_translate_coordinates (item_ui, content_widget, + rectangle->x, rectangle->y, + &view_x, &view_y); + rectangle->x = view_x; + rectangle->y = view_y; + + return rectangle; +} + +static GdkRectangle * +real_compute_rename_popover_pointing_to (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (NautilusViewItem) item = NULL; + GtkWidget *item_ui; + + /* We only allow one item to be renamed with a popover */ + item = get_view_item (G_LIST_MODEL (priv->model), get_first_selected_item (self)); + item_ui = nautilus_view_item_get_item_ui (item); + g_return_val_if_fail (item_ui != NULL, NULL); + + return get_rectangle_for_item_ui (self, item_ui); +} + +static GdkRectangle * +real_reveal_for_selection_context_menu (NautilusFilesView *files_view) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + g_autoptr (GtkSelectionFilterModel) selection = NULL; + guint n_selected; + GtkWidget *focus_child; + guint i; + GtkWidget *item_ui; + + selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (priv->model)); + n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection)); + g_return_val_if_fail (n_selected > 0, NULL); + + /* Get the focused item_ui, if selected. + * Otherwise, get the selected item_ui which is sorted the lowest.*/ + focus_child = gtk_widget_get_focus_child (nautilus_list_base_get_view_ui (self)); + for (i = 0; i < n_selected; i++) + { + g_autoptr (NautilusViewItem) item = NULL; + + item = get_view_item (G_LIST_MODEL (selection), i); + item_ui = nautilus_view_item_get_item_ui (item); + if (item_ui != NULL && gtk_widget_get_parent (item_ui) == focus_child) + { + break; + } + } + nautilus_list_base_scroll_to_item (self, i); + + return get_rectangle_for_item_ui (self, item_ui); +} + +static void +real_preview_selection_event (NautilusFilesView *files_view, + GtkDirectionType direction) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (files_view); + GtkMovementStep step; + gint count; + gboolean handled; + + step = (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) ? + GTK_MOVEMENT_DISPLAY_LINES : GTK_MOVEMENT_VISUAL_POSITIONS; + count = (direction == GTK_DIR_RIGHT || direction == GTK_DIR_DOWN) ? + 1 : -1; + + g_signal_emit_by_name (nautilus_list_base_get_view_ui (self), + "move-cursor", step, count, &handled); +} + +static void +default_sort_order_changed_callback (NautilusListBase *self) +{ + update_sort_order_from_metadata_and_preferences (self); +} + +static void +nautilus_list_base_dispose (GObject *object) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (object); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + g_clear_handle_id (&priv->scroll_to_file_handle_id, g_source_remove); + g_clear_handle_id (&priv->prioritize_thumbnailing_handle_id, g_source_remove); + + G_OBJECT_CLASS (nautilus_list_base_parent_class)->dispose (object); +} + +static void +nautilus_list_base_finalize (GObject *object) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (object); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + g_clear_list (&priv->cut_files, g_object_unref); + + G_OBJECT_CLASS (nautilus_list_base_parent_class)->finalize (object); +} + +static gboolean +prioritize_thumbnailing_on_idle (NautilusListBase *self) +{ + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + gdouble page_size; + GtkWidget *first_visible_child; + GtkWidget *next_child; + guint first_index; + guint next_index; + gdouble y; + guint last_index; + g_autoptr (NautilusViewItem) first_item = NULL; + NautilusFile *file; + + priv->prioritize_thumbnailing_handle_id = 0; + + page_size = gtk_adjustment_get_page_size (priv->vadjustment); + first_index = get_first_visible_item (self); + if (first_index == G_MAXUINT) + { + return G_SOURCE_REMOVE; + } + + first_item = get_view_item (G_LIST_MODEL (priv->model), first_index); + + first_visible_child = nautilus_view_item_get_item_ui (first_item); + + for (next_index = first_index + 1; next_index < g_list_model_get_n_items (G_LIST_MODEL (priv->model)); next_index++) + { + g_autoptr (NautilusViewItem) next_item = NULL; + + next_item = get_view_item (G_LIST_MODEL (priv->model), next_index); + next_child = nautilus_view_item_get_item_ui (next_item); + if (next_child == NULL) + { + break; + } + if (gtk_widget_translate_coordinates (next_child, first_visible_child, + 0, 0, NULL, &y)) + { + if (y > page_size) + { + break; + } + } + } + last_index = next_index - 1; + + /* Do the iteration in reverse to give higher priority to the top */ + for (gint i = 0; i <= last_index - first_index; i++) + { + g_autoptr (NautilusViewItem) item = NULL; + + item = get_view_item (G_LIST_MODEL (priv->model), last_index - i); + g_return_val_if_fail (item != NULL, G_SOURCE_REMOVE); + + file = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM (item)); + if (file != NULL && nautilus_file_is_thumbnailing (file)) + { + g_autofree gchar *uri = nautilus_file_get_uri (file); + nautilus_thumbnail_prioritize (uri); + } + } + + return G_SOURCE_REMOVE; +} + +static void +on_vadjustment_changed (GtkAdjustment *adjustment, + gpointer user_data) +{ + NautilusListBase *self = NAUTILUS_LIST_BASE (user_data); + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + guint handle_id; + + /* Schedule on idle to rate limit and to avoid delaying scrolling. */ + if (priv->prioritize_thumbnailing_handle_id == 0) + { + handle_id = g_idle_add ((GSourceFunc) prioritize_thumbnailing_on_idle, self); + priv->prioritize_thumbnailing_handle_id = handle_id; + } +} + +static void +nautilus_list_base_class_init (NautilusListBaseClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass); + + object_class->dispose = nautilus_list_base_dispose; + object_class->finalize = nautilus_list_base_finalize; + + files_view_class->add_files = real_add_files; + files_view_class->begin_loading = real_begin_loading; + files_view_class->clear = real_clear; + files_view_class->click_policy_changed = real_click_policy_changed; + files_view_class->file_changed = real_file_changed; + files_view_class->get_selection = real_get_selection; + /* TODO: remove this get_selection_for_file_transfer, this doesn't even + * take into account we could us the view for recursive search :/ + * CanvasView has the same issue. */ + files_view_class->get_selection_for_file_transfer = real_get_selection; + files_view_class->is_empty = real_is_empty; + files_view_class->remove_file = real_remove_file; + files_view_class->select_all = real_select_all; + files_view_class->set_selection = real_set_selection; + files_view_class->invert_selection = real_invert_selection; + files_view_class->compare_files = real_compare_files; + files_view_class->end_file_changes = real_end_file_changes; + files_view_class->end_loading = real_end_loading; + files_view_class->get_first_visible_file = real_get_first_visible_file; + files_view_class->reveal_selection = real_reveal_selection; + files_view_class->scroll_to_file = real_scroll_to_file; + files_view_class->select_first = real_select_first; + files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to; + files_view_class->reveal_for_selection_context_menu = real_reveal_for_selection_context_menu; + files_view_class->preview_selection_event = real_preview_selection_event; +} + +static void +nautilus_list_base_init (NautilusListBase *self) +{ + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + GtkWidget *content_widget; + GtkAdjustment *vadjustment; + + gtk_widget_add_css_class (GTK_WIDGET (self), "view"); + + g_signal_connect_object (nautilus_preferences, + "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER, + G_CALLBACK (default_sort_order_changed_callback), + self, + G_CONNECT_SWAPPED); + g_signal_connect_object (nautilus_preferences, + "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER, + G_CALLBACK (default_sort_order_changed_callback), + self, + G_CONNECT_SWAPPED); + + /* React to clipboard changes */ + g_signal_connect_object (gdk_display_get_clipboard (gdk_display_get_default ()), + "changed", + G_CALLBACK (on_clipboard_owner_changed), self, 0); + + content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self)); + vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget)); + + priv->vadjustment = vadjustment; + g_signal_connect (vadjustment, "changed", (GCallback) on_vadjustment_changed, self); + g_signal_connect (vadjustment, "value-changed", (GCallback) on_vadjustment_changed, self); + + priv->model = nautilus_view_model_new (); + + g_signal_connect_object (GTK_SELECTION_MODEL (priv->model), + "selection-changed", + G_CALLBACK (nautilus_files_view_notify_selection_changed), + NAUTILUS_FILES_VIEW (self), + G_CONNECT_SWAPPED); + + set_click_mode_from_settings (self); +} + +NautilusViewModel * +nautilus_list_base_get_model (NautilusListBase *self) +{ + NautilusListBasePrivate *priv = nautilus_list_base_get_instance_private (self); + + return priv->model; +} + +void +nautilus_list_base_setup_gestures (NautilusListBase *self) +{ + GtkWidget *view_ui = nautilus_list_base_get_view_ui (self); + GtkEventController *controller; + + controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); + gtk_widget_add_controller (view_ui, controller); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0); + g_signal_connect (controller, "pressed", + G_CALLBACK (on_view_click_pressed), self); + + controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ()); + gtk_widget_add_controller (view_ui, controller); + gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE); + g_signal_connect (controller, "pressed", + G_CALLBACK (on_view_longpress_pressed), self); +} diff --git a/src/nautilus-list-base.h b/src/nautilus-list-base.h new file mode 100644 index 0000000000000000000000000000000000000000..007ab07a621c59c062f3de07c3ea931e19c0b7e7 --- /dev/null +++ b/src/nautilus-list-base.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "nautilus-files-view.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_LIST_BASE (nautilus_list_base_get_type()) + +G_DECLARE_DERIVABLE_TYPE (NautilusListBase, nautilus_list_base, NAUTILUS, LIST_BASE, NautilusFilesView) + +struct _NautilusListBaseClass +{ + NautilusFilesViewClass parent_class; + + guint (*get_icon_size) (NautilusListBase *self); + GtkWidget *(*get_view_ui) (NautilusListBase *self); + void (*scroll_to_item) (NautilusListBase *self, + guint position); +}; + +G_END_DECLS diff --git a/src/nautilus-list-model.c b/src/nautilus-list-model.c deleted file mode 100644 index dba6b99e4c6dff1cbefc6649c4f707f9a6042b0a..0000000000000000000000000000000000000000 --- a/src/nautilus-list-model.c +++ /dev/null @@ -1,1883 +0,0 @@ -/* fm-list-model.h - a GtkTreeModel for file lists. - * - * Copyright (C) 2001, 2002 Anders Carlsson - * Copyright (C) 2003, Soeren Sandmann - * Copyright (C) 2004, Novell, 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: Anders Carlsson , Soeren Sandmann (sandmann@daimi.au.dk), Dave Camp - */ - -#include - -#include "nautilus-list-model.h" - -#include -#include -#include -#include -#include - -#include -#include "nautilus-dnd.h" - -enum -{ - SUBDIRECTORY_UNLOADED, - GET_ICON_SCALE, - LAST_SIGNAL -}; - -static GQuark attribute_name_q, - attribute_modification_date_q, - attribute_date_modified_q; - -/* msec delay after Loading... dummy row turns into (empty) */ -#define LOADING_TO_EMPTY_DELAY 100 - -static guint list_model_signals[LAST_SIGNAL] = { 0 }; - -static int nautilus_list_model_file_entry_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data); -static void nautilus_list_model_tree_model_init (GtkTreeModelIface *iface); -static void nautilus_list_model_sortable_init (GtkTreeSortableIface *iface); - -typedef struct -{ - GSequence *files; - GHashTable *directory_reverse_map; /* map from directory to GSequenceIter's */ - GHashTable *top_reverse_map; /* map from files in top dir to GSequenceIter's */ - - int stamp; - - GQuark sort_attribute; - GtkSortType order; - - gboolean sort_directories_first; - - GtkTreeView *drag_view; - int drag_begin_x; - int drag_begin_y; - - GPtrArray *columns; - - GList *highlight_files; -} NautilusListModelPrivate; - -typedef struct -{ - NautilusListModel *model; - - GList *path_list; -} DragDataGetInfo; - -typedef struct FileEntry FileEntry; - -struct FileEntry -{ - NautilusFile *file; - GHashTable *reverse_map; /* map from files to GSequenceIter's */ - NautilusDirectory *subdirectory; - FileEntry *parent; - GSequence *files; - GSequenceIter *ptr; - guint loaded : 1; -}; - -G_DEFINE_TYPE_WITH_CODE (NautilusListModel, nautilus_list_model, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - nautilus_list_model_tree_model_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, - nautilus_list_model_sortable_init) - G_ADD_PRIVATE (NautilusListModel)); - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -static const GtkTargetEntry drag_types [] = -{ - { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST }, - { NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST }, -}; -#endif - -static void -file_entry_free (FileEntry *file_entry) -{ - nautilus_file_unref (file_entry->file); - if (file_entry->reverse_map) - { - g_hash_table_destroy (file_entry->reverse_map); - file_entry->reverse_map = NULL; - } - if (file_entry->subdirectory != NULL) - { - nautilus_directory_unref (file_entry->subdirectory); - } - if (file_entry->files != NULL) - { - g_sequence_free (file_entry->files); - } - g_free (file_entry); -} - -static GtkTreeModelFlags -nautilus_list_model_get_flags (GtkTreeModel *tree_model) -{ - return GTK_TREE_MODEL_ITERS_PERSIST; -} - -static int -nautilus_list_model_get_n_columns (GtkTreeModel *tree_model) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (NAUTILUS_LIST_MODEL (tree_model)); - - return NAUTILUS_LIST_MODEL_NUM_COLUMNS + priv->columns->len; -} - -static GType -nautilus_list_model_get_column_type (GtkTreeModel *tree_model, - int index) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - switch (index) - { - case NAUTILUS_LIST_MODEL_FILE_COLUMN: - { - return NAUTILUS_TYPE_FILE; - } - - case NAUTILUS_LIST_MODEL_SUBDIRECTORY_COLUMN: - { - return NAUTILUS_TYPE_DIRECTORY; - } - - case NAUTILUS_LIST_MODEL_SMALL_ICON_COLUMN: - case NAUTILUS_LIST_MODEL_STANDARD_ICON_COLUMN: - case NAUTILUS_LIST_MODEL_LARGE_ICON_COLUMN: - case NAUTILUS_LIST_MODEL_LARGER_ICON_COLUMN: - { - return GDK_TYPE_TEXTURE; - } - - case NAUTILUS_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN: - { - return G_TYPE_BOOLEAN; - } - - default: - { - if (index < NAUTILUS_LIST_MODEL_NUM_COLUMNS + priv->columns->len) - { - return G_TYPE_STRING; - } - else - { - return G_TYPE_INVALID; - } - } - } -} - -static void -nautilus_list_model_ptr_to_iter (NautilusListModel *model, - GSequenceIter *ptr, - GtkTreeIter *iter) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (model); - - g_assert (!g_sequence_iter_is_end (ptr)); - - if (iter != NULL) - { - iter->stamp = priv->stamp; - iter->user_data = ptr; - } -} - -static gboolean -nautilus_list_model_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - GSequence *files; - GSequenceIter *ptr; - FileEntry *file_entry; - int i, d; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - ptr = NULL; - - files = priv->files; - for (d = 0; d < gtk_tree_path_get_depth (path); d++) - { - i = gtk_tree_path_get_indices (path)[d]; - - if (files == NULL || i >= g_sequence_get_length (files)) - { - return FALSE; - } - - ptr = g_sequence_get_iter_at_pos (files, i); - file_entry = g_sequence_get (ptr); - files = file_entry->files; - } - - nautilus_list_model_ptr_to_iter (model, ptr, iter); - - return TRUE; -} - -static GtkTreePath * -nautilus_list_model_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - GtkTreePath *path; - NautilusListModel *model; - NautilusListModelPrivate *priv; - GSequenceIter *ptr; - FileEntry *file_entry; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - g_return_val_if_fail (iter->stamp == priv->stamp, NULL); - - if (g_sequence_iter_is_end (iter->user_data)) - { - /* FIXME is this right? */ - return NULL; - } - - path = gtk_tree_path_new (); - ptr = iter->user_data; - while (ptr != NULL) - { - gtk_tree_path_prepend_index (path, g_sequence_iter_get_position (ptr)); - file_entry = g_sequence_get (ptr); - if (file_entry->parent != NULL) - { - ptr = file_entry->parent->ptr; - } - else - { - ptr = NULL; - } - } - - return path; -} - -static gint -nautilus_list_model_get_icon_scale (NautilusListModel *model) -{ - gint retval = -1; - - g_signal_emit (model, list_model_signals[GET_ICON_SCALE], 0, - &retval); - - if (retval == -1) - { - retval = gdk_monitor_get_scale_factor (g_list_model_get_item (gdk_display_get_monitors (gdk_display_get_default ()), 0)); - } - - /* FIXME: Temporary regression: HiDPI icons not supported, ignore scale. */ - retval = 1; - - return retval; -} - -guint -nautilus_list_model_get_icon_size_for_zoom_level (NautilusListZoomLevel zoom_level) -{ - switch (zoom_level) - { - case NAUTILUS_LIST_ZOOM_LEVEL_SMALL: - { - return NAUTILUS_LIST_ICON_SIZE_SMALL; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_STANDARD: - { - return NAUTILUS_LIST_ICON_SIZE_STANDARD; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_LARGE: - { - return NAUTILUS_LIST_ICON_SIZE_LARGE; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_LARGER: - { - return NAUTILUS_LIST_ICON_SIZE_LARGER; - } - } - g_return_val_if_reached (NAUTILUS_LIST_ICON_SIZE_STANDARD); -} - -static void -nautilus_list_model_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - int column, - GValue *value) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - FileEntry *file_entry; - NautilusFile *file; - char *str; - GdkTexture *icon; - int icon_size, icon_scale; - NautilusListZoomLevel zoom_level; - NautilusFileIconFlags flags; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - g_return_if_fail (priv->stamp == iter->stamp); - g_return_if_fail (!g_sequence_iter_is_end (iter->user_data)); - - file_entry = g_sequence_get (iter->user_data); - file = file_entry->file; - - switch (column) - { - case NAUTILUS_LIST_MODEL_FILE_COLUMN: - { - g_value_init (value, NAUTILUS_TYPE_FILE); - - g_value_set_object (value, file); - } - break; - - case NAUTILUS_LIST_MODEL_SUBDIRECTORY_COLUMN: - { - g_value_init (value, NAUTILUS_TYPE_DIRECTORY); - - g_value_set_object (value, file_entry->subdirectory); - } - break; - - case NAUTILUS_LIST_MODEL_SMALL_ICON_COLUMN: - case NAUTILUS_LIST_MODEL_STANDARD_ICON_COLUMN: - case NAUTILUS_LIST_MODEL_LARGE_ICON_COLUMN: - case NAUTILUS_LIST_MODEL_LARGER_ICON_COLUMN: - { - g_value_init (value, GDK_TYPE_TEXTURE); - - if (file != NULL) - { - zoom_level = nautilus_list_model_get_zoom_level_from_column_id (column); - icon_size = nautilus_list_model_get_icon_size_for_zoom_level (zoom_level); - icon_scale = nautilus_list_model_get_icon_scale (model); - - flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | - NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE | - NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS | - NAUTILUS_FILE_ICON_FLAGS_USE_ONE_EMBLEM; - - if (priv->drag_view != NULL) - { - GtkTreePath *path_a, *path_b; - - gtk_tree_view_get_drag_dest_row (priv->drag_view, - &path_a, - NULL); - if (path_a != NULL) - { - path_b = gtk_tree_model_get_path (tree_model, iter); - - if (gtk_tree_path_compare (path_a, path_b) == 0) - { - flags |= NAUTILUS_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT; - } - - gtk_tree_path_free (path_a); - gtk_tree_path_free (path_b); - } - } - - icon = nautilus_file_get_icon_texture (file, icon_size, icon_scale, flags); - -#if 0 && NAUTILUS_CLIPBOARD_NEEDS_GTK4_REIMPLEMENTATION - if (priv->highlight_files != NULL && - g_list_find_custom (priv->highlight_files, - file, (GCompareFunc) nautilus_file_compare_location)) - { - rendered_icon = eel_create_spotlight_pixbuf (icon); - - if (rendered_icon != NULL) - { - g_object_unref (icon); - icon = rendered_icon; - } - } -#endif - - g_value_set_object (value, icon); - g_object_unref (icon); - } - } - break; - - case NAUTILUS_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN: - { - g_value_init (value, G_TYPE_BOOLEAN); - - g_value_set_boolean (value, file != NULL && nautilus_file_can_rename (file)); - } - break; - - default: - { - if (column >= NAUTILUS_LIST_MODEL_NUM_COLUMNS && column < NAUTILUS_LIST_MODEL_NUM_COLUMNS + priv->columns->len) - { - NautilusColumn *nautilus_column; - GQuark attribute; - nautilus_column = priv->columns->pdata[column - NAUTILUS_LIST_MODEL_NUM_COLUMNS]; - - g_value_init (value, G_TYPE_STRING); - g_object_get (nautilus_column, - "attribute_q", &attribute, - NULL); - if (file != NULL) - { - str = nautilus_file_get_string_attribute_with_default_q (file, - attribute); - g_value_take_string (value, str); - } - else if (attribute == attribute_name_q) - { - if (file_entry->parent->loaded) - { - g_value_set_string (value, _("(Empty)")); - } - else - { - g_value_set_string (value, _("Loading…")); - } - } - } - else - { - g_assert_not_reached (); - } - } - } -} - -static gboolean -nautilus_list_model_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - g_return_val_if_fail (priv->stamp == iter->stamp, FALSE); - - iter->user_data = g_sequence_iter_next (iter->user_data); - - return !g_sequence_iter_is_end (iter->user_data); -} - -static gboolean -nautilus_list_model_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - GSequence *files; - FileEntry *file_entry; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - if (parent == NULL) - { - files = priv->files; - } - else - { - file_entry = g_sequence_get (parent->user_data); - files = file_entry->files; - } - - if (files == NULL || g_sequence_get_length (files) == 0) - { - return FALSE; - } - - iter->stamp = priv->stamp; - iter->user_data = g_sequence_get_begin_iter (files); - - return TRUE; -} - -static gboolean -nautilus_list_model_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - FileEntry *file_entry; - - if (iter == NULL) - { - return !nautilus_list_model_is_empty (NAUTILUS_LIST_MODEL (tree_model)); - } - - file_entry = g_sequence_get (iter->user_data); - - return (file_entry->files != NULL && g_sequence_get_length (file_entry->files) > 0); -} - -static int -nautilus_list_model_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - GSequence *files; - FileEntry *file_entry; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - if (iter == NULL) - { - files = priv->files; - } - else - { - file_entry = g_sequence_get (iter->user_data); - files = file_entry->files; - } - - return g_sequence_get_length (files); -} - -static gboolean -nautilus_list_model_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - int n) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - GSequenceIter *child; - GSequence *files; - FileEntry *file_entry; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - if (parent != NULL) - { - file_entry = g_sequence_get (parent->user_data); - files = file_entry->files; - } - else - { - files = priv->files; - } - - child = g_sequence_get_iter_at_pos (files, n); - - if (g_sequence_iter_is_end (child)) - { - return FALSE; - } - - iter->stamp = priv->stamp; - iter->user_data = child; - - return TRUE; -} - -static gboolean -nautilus_list_model_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - FileEntry *file_entry; - - model = NAUTILUS_LIST_MODEL (tree_model); - priv = nautilus_list_model_get_instance_private (model); - - file_entry = g_sequence_get (child->user_data); - - if (file_entry->parent == NULL) - { - return FALSE; - } - - iter->stamp = priv->stamp; - iter->user_data = file_entry->parent->ptr; - - return TRUE; -} - -static GSequenceIter * -lookup_file (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory) -{ - NautilusListModelPrivate *priv; - FileEntry *file_entry; - GSequenceIter *ptr, *parent_ptr; - - priv = nautilus_list_model_get_instance_private (model); - - parent_ptr = NULL; - if (directory) - { - parent_ptr = g_hash_table_lookup (priv->directory_reverse_map, - directory); - } - - if (parent_ptr) - { - file_entry = g_sequence_get (parent_ptr); - ptr = g_hash_table_lookup (file_entry->reverse_map, file); - } - else - { - ptr = g_hash_table_lookup (priv->top_reverse_map, file); - } - - if (ptr) - { - g_assert (((FileEntry *) g_sequence_get (ptr))->file == file); - } - - return ptr; -} - - -struct GetIters -{ - NautilusListModel *model; - NautilusFile *file; - GList *iters; -}; - -static void -dir_to_iters (struct GetIters *data, - GHashTable *reverse_map) -{ - GSequenceIter *ptr; - - ptr = g_hash_table_lookup (reverse_map, data->file); - if (ptr) - { - GtkTreeIter *iter; - iter = g_new0 (GtkTreeIter, 1); - nautilus_list_model_ptr_to_iter (data->model, ptr, iter); - data->iters = g_list_prepend (data->iters, iter); - } -} - -static void -file_to_iter_cb (gpointer key, - gpointer value, - gpointer user_data) -{ - struct GetIters *data; - FileEntry *dir_file_entry; - - data = user_data; - dir_file_entry = g_sequence_get ((GSequenceIter *) value); - dir_to_iters (data, dir_file_entry->reverse_map); -} - -GList * -nautilus_list_model_get_all_iters_for_file (NautilusListModel *model, - NautilusFile *file) -{ - struct GetIters data; - NautilusListModelPrivate *priv; - data.file = file; - data.model = model; - data.iters = NULL; - - priv = nautilus_list_model_get_instance_private (model); - - dir_to_iters (&data, priv->top_reverse_map); - g_hash_table_foreach (priv->directory_reverse_map, - file_to_iter_cb, &data); - - return g_list_reverse (data.iters); -} - -gboolean -nautilus_list_model_get_first_iter_for_file (NautilusListModel *model, - NautilusFile *file, - GtkTreeIter *iter) -{ - GList *list; - gboolean res; - - res = FALSE; - - list = nautilus_list_model_get_all_iters_for_file (model, file); - if (list != NULL) - { - res = TRUE; - *iter = *(GtkTreeIter *) list->data; - } - g_list_free_full (list, g_free); - - return res; -} - - -gboolean -nautilus_list_model_get_tree_iter_from_file (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory, - GtkTreeIter *iter) -{ - GSequenceIter *ptr; - - ptr = lookup_file (model, file, directory); - if (!ptr) - { - return FALSE; - } - - nautilus_list_model_ptr_to_iter (model, ptr, iter); - - return TRUE; -} - -static int -nautilus_list_model_file_entry_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data) -{ - FileEntry *file_entry1; - FileEntry *file_entry2; - NautilusListModel *model; - NautilusListModelPrivate *priv; - int result; - - model = NAUTILUS_LIST_MODEL (user_data); - priv = nautilus_list_model_get_instance_private (model); - - file_entry1 = (FileEntry *) a; - file_entry2 = (FileEntry *) b; - - if (file_entry1->file != NULL && file_entry2->file != NULL) - { - result = nautilus_file_compare_for_sort_by_attribute_q (file_entry1->file, file_entry2->file, - priv->sort_attribute, - priv->sort_directories_first, - (priv->order == GTK_SORT_DESCENDING)); - } - else if (file_entry1->file == NULL) - { - return -1; - } - else - { - return 1; - } - - return result; -} - -int -nautilus_list_model_compare_func (NautilusListModel *model, - NautilusFile *file1, - NautilusFile *file2) -{ - NautilusListModelPrivate *priv; - int result; - - priv = nautilus_list_model_get_instance_private (model); - result = nautilus_file_compare_for_sort_by_attribute_q (file1, file2, - priv->sort_attribute, - priv->sort_directories_first, - (priv->order == GTK_SORT_DESCENDING)); - - return result; -} - -static void -nautilus_list_model_sort_file_entries (NautilusListModel *model, - GSequence *files, - GtkTreePath *path) -{ - GSequenceIter **old_order; - GtkTreeIter iter; - int *new_order; - int length; - int i; - FileEntry *file_entry; - gboolean has_iter; - - length = g_sequence_get_length (files); - - if (length <= 1) - { - return; - } - - /* generate old order of GSequenceIter's */ - old_order = g_new (GSequenceIter *, length); - for (i = 0; i < length; ++i) - { - GSequenceIter *ptr = g_sequence_get_iter_at_pos (files, i); - - file_entry = g_sequence_get (ptr); - if (file_entry->files != NULL) - { - gtk_tree_path_append_index (path, i); - nautilus_list_model_sort_file_entries (model, file_entry->files, path); - gtk_tree_path_up (path); - } - - old_order[i] = ptr; - } - - /* sort */ - g_sequence_sort (files, nautilus_list_model_file_entry_compare_func, model); - - /* generate new order */ - new_order = g_new (int, length); - /* Note: new_order[newpos] = oldpos */ - for (i = 0; i < length; ++i) - { - new_order[g_sequence_iter_get_position (old_order[i])] = i; - } - - /* Let the world know about our new order */ - - g_assert (new_order != NULL); - - has_iter = FALSE; - if (gtk_tree_path_get_depth (path) != 0) - { - gboolean get_iter_result; - has_iter = TRUE; - get_iter_result = gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); - g_assert (get_iter_result); - } - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), - path, has_iter ? &iter : NULL, new_order); - - g_free (old_order); - g_free (new_order); -} - -static void -nautilus_list_model_sort (NautilusListModel *model) -{ - GtkTreePath *path; - NautilusListModelPrivate *priv; - - path = gtk_tree_path_new (); - priv = nautilus_list_model_get_instance_private (model); - - nautilus_list_model_sort_file_entries (model, priv->files, path); - - gtk_tree_path_free (path); -} - -static gboolean -nautilus_list_model_get_sort_column_id (GtkTreeSortable *sortable, - gint *sort_column_id, - GtkSortType *order) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - int id; - - model = NAUTILUS_LIST_MODEL (sortable); - priv = nautilus_list_model_get_instance_private (model); - id = nautilus_list_model_get_sort_column_id_from_attribute (model, priv->sort_attribute); - - if (id == -1) - { - return FALSE; - } - - if (sort_column_id != NULL) - { - *sort_column_id = id; - } - - if (order != NULL) - { - *order = priv->order; - } - - return TRUE; -} - -static void -nautilus_list_model_set_sort_column_id (GtkTreeSortable *sortable, - gint sort_column_id, - GtkSortType order) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - - model = NAUTILUS_LIST_MODEL (sortable); - priv = nautilus_list_model_get_instance_private (model); - - priv->sort_attribute = nautilus_list_model_get_attribute_from_sort_column_id (model, sort_column_id); - - priv->order = order; - - nautilus_list_model_sort (model); - gtk_tree_sortable_sort_column_changed (sortable); -} - -static gboolean -nautilus_list_model_has_default_sort_func (GtkTreeSortable *sortable) -{ - return FALSE; -} - -static void -add_dummy_row (NautilusListModel *model, - FileEntry *parent_entry) -{ - NautilusListModelPrivate *priv; - FileEntry *dummy_file_entry; - GtkTreeIter iter; - GtkTreePath *path; - - priv = nautilus_list_model_get_instance_private (model); - dummy_file_entry = g_new0 (FileEntry, 1); - dummy_file_entry->parent = parent_entry; - dummy_file_entry->ptr = g_sequence_insert_sorted (parent_entry->files, dummy_file_entry, - nautilus_list_model_file_entry_compare_func, model); - iter.stamp = priv->stamp; - iter.user_data = dummy_file_entry->ptr; - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); -} - -gboolean -nautilus_list_model_add_file (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory) -{ - NautilusListModelPrivate *priv; - GtkTreeIter iter; - GtkTreePath *path; - FileEntry *file_entry; - GSequenceIter *ptr, *parent_ptr; - GSequence *files; - gboolean replace_dummy; - GHashTable *parent_hash; - - priv = nautilus_list_model_get_instance_private (model); - - parent_ptr = g_hash_table_lookup (priv->directory_reverse_map, - directory); - if (parent_ptr) - { - file_entry = g_sequence_get (parent_ptr); - ptr = g_hash_table_lookup (file_entry->reverse_map, file); - } - else - { - file_entry = NULL; - ptr = g_hash_table_lookup (priv->top_reverse_map, file); - } - - if (ptr != NULL) - { - g_warning ("file already in tree (parent_ptr: %p)!!!\n", parent_ptr); - return FALSE; - } - - file_entry = g_new0 (FileEntry, 1); - file_entry->file = nautilus_file_ref (file); - file_entry->parent = NULL; - file_entry->subdirectory = NULL; - file_entry->files = NULL; - - files = priv->files; - parent_hash = priv->top_reverse_map; - - replace_dummy = FALSE; - - if (parent_ptr != NULL) - { - file_entry->parent = g_sequence_get (parent_ptr); - /* At this point we set loaded. Either we saw - * "done" and ignored it waiting for this, or we do this - * earlier, but then we replace the dummy row anyway, - * so it doesn't matter */ - file_entry->parent->loaded = 1; - parent_hash = file_entry->parent->reverse_map; - files = file_entry->parent->files; - if (g_sequence_get_length (files) == 1) - { - GSequenceIter *dummy_ptr = g_sequence_get_iter_at_pos (files, 0); - FileEntry *dummy_entry = g_sequence_get (dummy_ptr); - if (dummy_entry->file == NULL) - { - /* replace the dummy loading entry */ - priv->stamp++; - g_sequence_remove (dummy_ptr); - - replace_dummy = TRUE; - } - } - } - - - file_entry->ptr = g_sequence_insert_sorted (files, file_entry, - nautilus_list_model_file_entry_compare_func, model); - - g_hash_table_insert (parent_hash, file, file_entry->ptr); - - iter.stamp = priv->stamp; - iter.user_data = file_entry->ptr; - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - if (replace_dummy) - { - gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); - } - else - { - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); - } - - if (nautilus_file_is_directory (file)) - { - file_entry->files = g_sequence_new ((GDestroyNotify) file_entry_free); - - add_dummy_row (model, file_entry); - - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model), - path, &iter); - } - gtk_tree_path_free (path); - - return TRUE; -} - -void -nautilus_list_model_file_changed (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory) -{ - NautilusListModelPrivate *priv; - FileEntry *parent_file_entry; - GtkTreeIter iter; - GtkTreePath *path, *parent_path; - GSequenceIter *ptr; - int pos_before, pos_after, length, i, old; - int *new_order; - gboolean has_iter; - GSequence *files; - - priv = nautilus_list_model_get_instance_private (model); - - ptr = lookup_file (model, file, directory); - if (!ptr) - { - return; - } - - - pos_before = g_sequence_iter_get_position (ptr); - - g_sequence_sort_changed (ptr, nautilus_list_model_file_entry_compare_func, model); - - pos_after = g_sequence_iter_get_position (ptr); - - if (pos_before != pos_after) - { - /* The file moved, we need to send rows_reordered */ - - parent_file_entry = ((FileEntry *) g_sequence_get (ptr))->parent; - - if (parent_file_entry == NULL) - { - has_iter = FALSE; - parent_path = gtk_tree_path_new (); - files = priv->files; - } - else - { - has_iter = TRUE; - nautilus_list_model_ptr_to_iter (model, parent_file_entry->ptr, &iter); - parent_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - files = parent_file_entry->files; - } - - length = g_sequence_get_length (files); - new_order = g_new (int, length); - /* Note: new_order[newpos] = oldpos */ - for (i = 0, old = 0; i < length; ++i) - { - if (i == pos_after) - { - new_order[i] = pos_before; - } - else - { - if (old == pos_before) - { - old++; - } - new_order[i] = old++; - } - } - - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), - parent_path, has_iter ? &iter : NULL, new_order); - - gtk_tree_path_free (parent_path); - g_free (new_order); - } - - nautilus_list_model_ptr_to_iter (model, ptr, &iter); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); -} - -gboolean -nautilus_list_model_is_empty (NautilusListModel *model) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (model); - - return (g_sequence_get_length (priv->files) == 0); -} - -static void -nautilus_list_model_remove (NautilusListModel *model, - GtkTreeIter *iter) -{ - NautilusListModelPrivate *priv; - GSequenceIter *ptr, *child_ptr; - FileEntry *file_entry, *child_file_entry, *parent_file_entry; - GtkTreePath *path; - GtkTreeIter parent_iter; - - priv = nautilus_list_model_get_instance_private (model); - ptr = iter->user_data; - file_entry = g_sequence_get (ptr); - - if (file_entry->files != NULL) - { - while (g_sequence_get_length (file_entry->files) > 0) - { - child_ptr = g_sequence_get_begin_iter (file_entry->files); - child_file_entry = g_sequence_get (child_ptr); - if (child_file_entry->file != NULL) - { - nautilus_list_model_remove_file (model, - child_file_entry->file, - file_entry->subdirectory); - } - else - { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); - gtk_tree_path_append_index (path, 0); - priv->stamp++; - g_sequence_remove (child_ptr); - gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); - gtk_tree_path_free (path); - } - - /* the parent iter didn't actually change */ - iter->stamp = priv->stamp; - } - } - - if (file_entry->file != NULL) /* Don't try to remove dummy row */ - { - if (file_entry->parent != NULL) - { - g_hash_table_remove (file_entry->parent->reverse_map, file_entry->file); - } - else - { - g_hash_table_remove (priv->top_reverse_map, file_entry->file); - } - } - - parent_file_entry = file_entry->parent; - if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 1 && - file_entry->file != NULL) - { - /* this is the last non-dummy child, add a dummy node */ - /* We need to do this before removing the last file to avoid - * collapsing the row. - */ - add_dummy_row (model, parent_file_entry); - } - - if (file_entry->subdirectory != NULL) - { - g_signal_emit (model, - list_model_signals[SUBDIRECTORY_UNLOADED], 0, - file_entry->subdirectory); - g_hash_table_remove (priv->directory_reverse_map, - file_entry->subdirectory); - } - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); - - g_sequence_remove (ptr); - priv->stamp++; - gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); - - gtk_tree_path_free (path); - - if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 0) - { - parent_iter.stamp = priv->stamp; - parent_iter.user_data = parent_file_entry->ptr; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &parent_iter); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model), - path, &parent_iter); - gtk_tree_path_free (path); - } -} - -void -nautilus_list_model_remove_file (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory) -{ - GtkTreeIter iter; - - if (nautilus_list_model_get_tree_iter_from_file (model, file, directory, &iter)) - { - nautilus_list_model_remove (model, &iter); - } -} - -static void -nautilus_list_model_clear_directory (NautilusListModel *model, - GSequence *files) -{ - NautilusListModelPrivate *priv; - GtkTreeIter iter; - FileEntry *file_entry; - - priv = nautilus_list_model_get_instance_private (model); - - while (g_sequence_get_length (files) > 0) - { - iter.user_data = g_sequence_get_begin_iter (files); - - file_entry = g_sequence_get (iter.user_data); - if (file_entry->files != NULL) - { - nautilus_list_model_clear_directory (model, file_entry->files); - } - - iter.stamp = priv->stamp; - nautilus_list_model_remove (model, &iter); - } -} - -void -nautilus_list_model_clear (NautilusListModel *model) -{ - NautilusListModelPrivate *priv; - - g_return_if_fail (model != NULL); - - priv = nautilus_list_model_get_instance_private (model); - - nautilus_list_model_clear_directory (model, priv->files); -} - -NautilusFile * -nautilus_list_model_file_for_path (NautilusListModel *model, - GtkTreePath *path) -{ - NautilusFile *file; - GtkTreeIter iter; - - file = NULL; - if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), - &iter, path)) - { - gtk_tree_model_get (GTK_TREE_MODEL (model), - &iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - } - return file; -} - -gboolean -nautilus_list_model_load_subdirectory (NautilusListModel *model, - GtkTreePath *path, - NautilusDirectory **directory) -{ - NautilusListModelPrivate *priv; - GtkTreeIter iter; - FileEntry *file_entry; - NautilusDirectory *subdirectory; - - priv = nautilus_list_model_get_instance_private (model); - - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) - { - return FALSE; - } - - file_entry = g_sequence_get (iter.user_data); - if (file_entry->file == NULL || - file_entry->subdirectory != NULL) - { - return FALSE; - } - - subdirectory = nautilus_directory_get_for_file (file_entry->file); - - if (g_hash_table_lookup (priv->directory_reverse_map, subdirectory) != NULL) - { - nautilus_directory_unref (subdirectory); - g_warning ("Already in directory_reverse_map, failing\n"); - return FALSE; - } - - file_entry->subdirectory = subdirectory, - g_hash_table_insert (priv->directory_reverse_map, - subdirectory, file_entry->ptr); - file_entry->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal); - - /* Return a ref too */ - nautilus_directory_ref (subdirectory); - *directory = subdirectory; - - return TRUE; -} - -/* removes all children of the subfolder and unloads the subdirectory */ -void -nautilus_list_model_unload_subdirectory (NautilusListModel *model, - GtkTreeIter *iter) -{ - NautilusListModelPrivate *priv; - GSequenceIter *child_ptr; - FileEntry *file_entry, *child_file_entry; - GtkTreeIter child_iter; - - priv = nautilus_list_model_get_instance_private (model); - - file_entry = g_sequence_get (iter->user_data); - if (file_entry->file == NULL || - file_entry->subdirectory == NULL) - { - return; - } - - file_entry->loaded = 0; - - /* Remove all children */ - while (g_sequence_get_length (file_entry->files) > 0) - { - child_ptr = g_sequence_get_begin_iter (file_entry->files); - child_file_entry = g_sequence_get (child_ptr); - if (child_file_entry->file == NULL) - { - /* Don't delete the dummy node */ - break; - } - else - { - nautilus_list_model_ptr_to_iter (model, child_ptr, &child_iter); - nautilus_list_model_remove (model, &child_iter); - } - } - - /* Emit unload signal */ - g_signal_emit (model, - list_model_signals[SUBDIRECTORY_UNLOADED], 0, - file_entry->subdirectory); - - /* actually unload */ - g_hash_table_remove (priv->directory_reverse_map, - file_entry->subdirectory); - nautilus_directory_unref (file_entry->subdirectory); - file_entry->subdirectory = NULL; - - g_assert (g_hash_table_size (file_entry->reverse_map) == 0); - g_hash_table_destroy (file_entry->reverse_map); - file_entry->reverse_map = NULL; -} - - - -void -nautilus_list_model_set_should_sort_directories_first (NautilusListModel *model, - gboolean sort_directories_first) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (model); - - if (priv->sort_directories_first == sort_directories_first) - { - return; - } - - priv->sort_directories_first = sort_directories_first; - nautilus_list_model_sort (model); -} - -int -nautilus_list_model_get_sort_column_id_from_attribute (NautilusListModel *model, - GQuark attribute) -{ - NautilusListModelPrivate *priv; - guint i; - - if (attribute == 0) - { - return -1; - } - - priv = nautilus_list_model_get_instance_private (model); - - /* Hack - the preferences dialog sets modification_date for some - * rather than date_modified for some reason. Make sure that - * works. */ - if (attribute == attribute_modification_date_q) - { - attribute = attribute_date_modified_q; - } - - for (i = 0; i < priv->columns->len; i++) - { - NautilusColumn *column; - GQuark column_attribute; - - column = NAUTILUS_COLUMN (priv->columns->pdata[i]); - g_object_get (G_OBJECT (column), - "attribute_q", &column_attribute, - NULL); - if (column_attribute == attribute) - { - return NAUTILUS_LIST_MODEL_NUM_COLUMNS + i; - } - } - - return -1; -} - -GQuark -nautilus_list_model_get_attribute_from_sort_column_id (NautilusListModel *model, - int sort_column_id) -{ - NautilusListModelPrivate *priv; - NautilusColumn *column; - int index; - GQuark attribute; - - priv = nautilus_list_model_get_instance_private (model); - index = sort_column_id - NAUTILUS_LIST_MODEL_NUM_COLUMNS; - - if (index < 0 || index >= priv->columns->len) - { - g_warning ("unknown sort column id: %d", sort_column_id); - return 0; - } - - column = NAUTILUS_COLUMN (priv->columns->pdata[index]); - g_object_get (G_OBJECT (column), "attribute_q", &attribute, NULL); - - return attribute; -} - -NautilusListZoomLevel -nautilus_list_model_get_zoom_level_from_column_id (int column) -{ - switch (column) - { - case NAUTILUS_LIST_MODEL_SMALL_ICON_COLUMN: - { - return NAUTILUS_LIST_ZOOM_LEVEL_SMALL; - } - - case NAUTILUS_LIST_MODEL_STANDARD_ICON_COLUMN: - { - return NAUTILUS_LIST_ZOOM_LEVEL_STANDARD; - } - - case NAUTILUS_LIST_MODEL_LARGE_ICON_COLUMN: - { - return NAUTILUS_LIST_ZOOM_LEVEL_LARGE; - } - - case NAUTILUS_LIST_MODEL_LARGER_ICON_COLUMN: - { - return NAUTILUS_LIST_ZOOM_LEVEL_LARGER; - } - } - - g_return_val_if_reached (NAUTILUS_LIST_ZOOM_LEVEL_STANDARD); -} - -int -nautilus_list_model_get_column_id_from_zoom_level (NautilusListZoomLevel zoom_level) -{ - switch (zoom_level) - { - case NAUTILUS_LIST_ZOOM_LEVEL_SMALL: - { - return NAUTILUS_LIST_MODEL_SMALL_ICON_COLUMN; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_STANDARD: - { - return NAUTILUS_LIST_MODEL_STANDARD_ICON_COLUMN; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_LARGE: - { - return NAUTILUS_LIST_MODEL_LARGE_ICON_COLUMN; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_LARGER: - { - return NAUTILUS_LIST_MODEL_LARGER_ICON_COLUMN; - } - } - - g_return_val_if_reached (NAUTILUS_LIST_MODEL_STANDARD_ICON_COLUMN); -} - -void -nautilus_list_model_set_drag_view (NautilusListModel *model, - GtkTreeView *view, - int drag_begin_x, - int drag_begin_y) -{ - NautilusListModelPrivate *priv; - - g_return_if_fail (model != NULL); - g_return_if_fail (NAUTILUS_IS_LIST_MODEL (model)); - g_return_if_fail (!view || GTK_IS_TREE_VIEW (view)); - - priv = nautilus_list_model_get_instance_private (model); - - priv->drag_view = view; - priv->drag_begin_x = drag_begin_x; - priv->drag_begin_y = drag_begin_y; -} - -GtkTreeView * -nautilus_list_model_get_drag_view (NautilusListModel *model, - int *drag_begin_x, - int *drag_begin_y) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (model); - - if (drag_begin_x != NULL) - { - *drag_begin_x = priv->drag_begin_x; - } - - if (drag_begin_y != NULL) - { - *drag_begin_y = priv->drag_begin_y; - } - - return priv->drag_view; -} - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -GtkTargetList * -nautilus_list_model_get_drag_target_list () -{ - GtkTargetList *target_list; - - target_list = gtk_target_list_new (drag_types, G_N_ELEMENTS (drag_types)); - gtk_target_list_add_text_targets (target_list, NAUTILUS_ICON_DND_TEXT); - - return target_list; -} -#endif - -int -nautilus_list_model_add_column (NautilusListModel *model, - NautilusColumn *column) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (model); - - g_ptr_array_add (priv->columns, column); - g_object_ref (column); - - return NAUTILUS_LIST_MODEL_NUM_COLUMNS + (priv->columns->len - 1); -} - -static void -nautilus_list_model_dispose (GObject *object) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - int i; - - model = NAUTILUS_LIST_MODEL (object); - priv = nautilus_list_model_get_instance_private (model); - - if (priv->columns) - { - for (i = 0; i < priv->columns->len; i++) - { - g_object_unref (priv->columns->pdata[i]); - } - g_ptr_array_free (priv->columns, TRUE); - priv->columns = NULL; - } - - if (priv->files) - { - g_sequence_free (priv->files); - priv->files = NULL; - } - - if (priv->top_reverse_map) - { - g_hash_table_destroy (priv->top_reverse_map); - priv->top_reverse_map = NULL; - } - if (priv->directory_reverse_map) - { - g_hash_table_destroy (priv->directory_reverse_map); - priv->directory_reverse_map = NULL; - } - - G_OBJECT_CLASS (nautilus_list_model_parent_class)->dispose (object); -} - -static void -nautilus_list_model_finalize (GObject *object) -{ - NautilusListModel *model; - NautilusListModelPrivate *priv; - - model = NAUTILUS_LIST_MODEL (object); - priv = nautilus_list_model_get_instance_private (model); - - if (priv->highlight_files != NULL) - { - nautilus_file_list_free (priv->highlight_files); - priv->highlight_files = NULL; - } - - G_OBJECT_CLASS (nautilus_list_model_parent_class)->finalize (object); -} - -static void -nautilus_list_model_init (NautilusListModel *model) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (model); - - priv->files = g_sequence_new ((GDestroyNotify) file_entry_free); - priv->top_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal); - priv->directory_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal); - priv->stamp = g_random_int (); - priv->sort_attribute = 0; - priv->columns = g_ptr_array_new (); -} - -static void -nautilus_list_model_class_init (NautilusListModelClass *klass) -{ - GObjectClass *object_class; - - attribute_name_q = g_quark_from_static_string ("name"); - attribute_modification_date_q = g_quark_from_static_string ("modification_date"); - attribute_date_modified_q = g_quark_from_static_string ("date_modified"); - - object_class = (GObjectClass *) klass; - object_class->finalize = nautilus_list_model_finalize; - object_class->dispose = nautilus_list_model_dispose; - - list_model_signals[SUBDIRECTORY_UNLOADED] = - g_signal_new ("subdirectory-unloaded", - NAUTILUS_TYPE_LIST_MODEL, - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (NautilusListModelClass, subdirectory_unloaded), - NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, - NAUTILUS_TYPE_DIRECTORY); - - list_model_signals[GET_ICON_SCALE] = - g_signal_new ("get-icon-scale", - NAUTILUS_TYPE_LIST_MODEL, - G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - NULL, - G_TYPE_INT, 0); -} - -static void -nautilus_list_model_tree_model_init (GtkTreeModelIface *iface) -{ - iface->get_flags = nautilus_list_model_get_flags; - iface->get_n_columns = nautilus_list_model_get_n_columns; - iface->get_column_type = nautilus_list_model_get_column_type; - iface->get_iter = nautilus_list_model_get_iter; - iface->get_path = nautilus_list_model_get_path; - iface->get_value = nautilus_list_model_get_value; - iface->iter_next = nautilus_list_model_iter_next; - iface->iter_children = nautilus_list_model_iter_children; - iface->iter_has_child = nautilus_list_model_iter_has_child; - iface->iter_n_children = nautilus_list_model_iter_n_children; - iface->iter_nth_child = nautilus_list_model_iter_nth_child; - iface->iter_parent = nautilus_list_model_iter_parent; -} - -static void -nautilus_list_model_sortable_init (GtkTreeSortableIface *iface) -{ - iface->get_sort_column_id = nautilus_list_model_get_sort_column_id; - iface->set_sort_column_id = nautilus_list_model_set_sort_column_id; - iface->has_default_sort_func = nautilus_list_model_has_default_sort_func; -} - -void -nautilus_list_model_subdirectory_done_loading (NautilusListModel *model, - NautilusDirectory *directory) -{ - NautilusListModelPrivate *priv; - GtkTreeIter iter; - GtkTreePath *path; - FileEntry *file_entry, *dummy_entry; - GSequenceIter *parent_ptr, *dummy_ptr; - GSequence *files; - - priv = nautilus_list_model_get_instance_private (model); - - if (model == NULL || priv->directory_reverse_map == NULL) - { - return; - } - parent_ptr = g_hash_table_lookup (priv->directory_reverse_map, - directory); - if (parent_ptr == NULL) - { - return; - } - - file_entry = g_sequence_get (parent_ptr); - files = file_entry->files; - - /* Only swap loading -> empty if we saw no files yet at "done", - * otherwise, toggle loading at first added file to the model. - */ - if (!nautilus_directory_is_not_empty (directory) && - g_sequence_get_length (files) == 1) - { - dummy_ptr = g_sequence_get_iter_at_pos (file_entry->files, 0); - dummy_entry = g_sequence_get (dummy_ptr); - if (dummy_entry->file == NULL) - { - /* was the dummy file */ - file_entry->loaded = 1; - - iter.stamp = priv->stamp; - iter.user_data = dummy_ptr; - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); - gtk_tree_path_free (path); - } - } -} - -static void -refresh_row (gpointer data, - gpointer user_data) -{ - NautilusFile *file; - NautilusListModel *model; - GList *iters, *l; - GtkTreePath *path; - - model = user_data; - file = data; - - iters = nautilus_list_model_get_all_iters_for_file (model, file); - for (l = iters; l != NULL; l = l->next) - { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), l->data); - gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, l->data); - - gtk_tree_path_free (path); - } - - g_list_free_full (iters, g_free); -} - -void -nautilus_list_model_set_highlight_for_files (NautilusListModel *model, - GList *files) -{ - NautilusListModelPrivate *priv; - - priv = nautilus_list_model_get_instance_private (model); - - if (priv->highlight_files != NULL) - { - g_list_foreach (priv->highlight_files, refresh_row, model); - nautilus_file_list_free (priv->highlight_files); - priv->highlight_files = NULL; - } - - if (files != NULL) - { - priv->highlight_files = nautilus_file_list_copy (files); - g_list_foreach (priv->highlight_files, refresh_row, model); - } -} diff --git a/src/nautilus-list-model.h b/src/nautilus-list-model.h deleted file mode 100644 index cb430cfc1cacebbdc7926288ae22ce178f3d6d41..0000000000000000000000000000000000000000 --- a/src/nautilus-list-model.h +++ /dev/null @@ -1,115 +0,0 @@ - -/* fm-list-model.h - a GtkTreeModel for file lists. - - Copyright (C) 2001, 2002 Anders Carlsson - - 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: Anders Carlsson -*/ - -#pragma once - -#include -#include -#include "nautilus-file.h" -#include "nautilus-directory.h" -#include - -#define NAUTILUS_TYPE_LIST_MODEL nautilus_list_model_get_type() -G_DECLARE_DERIVABLE_TYPE (NautilusListModel, nautilus_list_model, NAUTILUS, LIST_MODEL, GObject); - -enum { - NAUTILUS_LIST_MODEL_FILE_COLUMN, - NAUTILUS_LIST_MODEL_SUBDIRECTORY_COLUMN, - NAUTILUS_LIST_MODEL_SMALL_ICON_COLUMN, - NAUTILUS_LIST_MODEL_STANDARD_ICON_COLUMN, - NAUTILUS_LIST_MODEL_LARGE_ICON_COLUMN, - NAUTILUS_LIST_MODEL_LARGER_ICON_COLUMN, - NAUTILUS_LIST_MODEL_FILE_NAME_IS_EDITABLE_COLUMN, - NAUTILUS_LIST_MODEL_NUM_COLUMNS -}; - -struct _NautilusListModelClass -{ - GObjectClass parent_class; - - void (* subdirectory_unloaded)(NautilusListModel *model, - NautilusDirectory *subdirectory); -}; - -gboolean nautilus_list_model_add_file (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory); -void nautilus_list_model_file_changed (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory); -gboolean nautilus_list_model_is_empty (NautilusListModel *model); -void nautilus_list_model_remove_file (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory); -void nautilus_list_model_clear (NautilusListModel *model); -gboolean nautilus_list_model_get_tree_iter_from_file (NautilusListModel *model, - NautilusFile *file, - NautilusDirectory *directory, - GtkTreeIter *iter); -GList * nautilus_list_model_get_all_iters_for_file (NautilusListModel *model, - NautilusFile *file); -gboolean nautilus_list_model_get_first_iter_for_file (NautilusListModel *model, - NautilusFile *file, - GtkTreeIter *iter); -void nautilus_list_model_set_should_sort_directories_first (NautilusListModel *model, - gboolean sort_directories_first); - -int nautilus_list_model_get_sort_column_id_from_attribute (NautilusListModel *model, - GQuark attribute); -GQuark nautilus_list_model_get_attribute_from_sort_column_id (NautilusListModel *model, - int sort_column_id); -void nautilus_list_model_sort_files (NautilusListModel *model, - GList **files); - -NautilusListZoomLevel nautilus_list_model_get_zoom_level_from_column_id (int column); -int nautilus_list_model_get_column_id_from_zoom_level (NautilusListZoomLevel zoom_level); -guint nautilus_list_model_get_icon_size_for_zoom_level (NautilusListZoomLevel zoom_level); - -NautilusFile * nautilus_list_model_file_for_path (NautilusListModel *model, GtkTreePath *path); -gboolean nautilus_list_model_load_subdirectory (NautilusListModel *model, GtkTreePath *path, NautilusDirectory **directory); -void nautilus_list_model_unload_subdirectory (NautilusListModel *model, GtkTreeIter *iter); - -void nautilus_list_model_set_drag_view (NautilusListModel *model, - GtkTreeView *view, - int begin_x, - int begin_y); -GtkTreeView * nautilus_list_model_get_drag_view (NautilusListModel *model, - int *drag_begin_x, - int *drag_begin_y); - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -GtkTargetList * nautilus_list_model_get_drag_target_list (void); -#endif - -int nautilus_list_model_compare_func (NautilusListModel *model, - NautilusFile *file1, - NautilusFile *file2); - - -int nautilus_list_model_add_column (NautilusListModel *model, - NautilusColumn *column); - -void nautilus_list_model_subdirectory_done_loading (NautilusListModel *model, - NautilusDirectory *directory); - -void nautilus_list_model_set_highlight_for_files (NautilusListModel *model, - GList *files); diff --git a/src/nautilus-list-view-dnd.c b/src/nautilus-list-view-dnd.c deleted file mode 100644 index cef6192c50b580e810e9ea0b6636b43eecf611e9..0000000000000000000000000000000000000000 --- a/src/nautilus-list-view-dnd.c +++ /dev/null @@ -1,307 +0,0 @@ -/* nautilus-list-view-dnd.c - * - * Copyright (C) 2015 Carlos Soriano - * - * This program 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 3 of the License, or - * (at your option) any later version. - * - * This program 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 . - */ - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -#include - -#include "nautilus-list-view-dnd.h" -#include "nautilus-list-view-private.h" - -static GtkTargetList *source_target_list = NULL; - -static void -drag_info_data_free (NautilusListView *list_view); - -static void -drag_data_get_callback (GtkWidget *widget, - GdkDragContext *context, - GtkSelectionData *selection_data, - guint info, - guint time, - gpointer user_data) -{ - GtkTreeView *tree_view; - GtkTreeModel *model; - NautilusListView *list_view; - - tree_view = GTK_TREE_VIEW (widget); - list_view = NAUTILUS_LIST_VIEW (user_data); - - model = gtk_tree_view_get_model (tree_view); - - if (model == NULL) - { - return; - } - - if (list_view->details->drag_source_info == NULL || - list_view->details->drag_source_info->selection_cache == NULL) - { - return; - } - - nautilus_drag_drag_data_get_from_cache (list_view->details->drag_source_info->selection_cache, - context, selection_data, info, time); -} - -static cairo_surface_t * -get_drag_surface (NautilusListView *view) -{ - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; - cairo_surface_t *ret; - GdkRectangle cell_area; - - ret = NULL; - - if (gtk_tree_view_get_path_at_pos (view->details->tree_view, - view->details->drag_x, - view->details->drag_y, - &path, NULL, NULL, NULL)) - { - model = gtk_tree_view_get_model (view->details->tree_view); - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_model_get (model, &iter, - nautilus_list_model_get_column_id_from_zoom_level (view->details->zoom_level), - &ret, - -1); - } - - gtk_tree_view_get_cell_area (view->details->tree_view, - path, - view->details->file_name_column, - &cell_area); - - gtk_tree_path_free (path); - - return ret; -} - -/* iteration glue struct */ -typedef struct -{ - NautilusListView *view; - NautilusDragEachSelectedItemDataGet iteratee; - gpointer iteratee_data; -} ListGetDataBinderContext; - -static void -item_get_data_binder (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - ListGetDataBinderContext *context = data; - NautilusFile *file; - GtkTreeView *treeview; - GtkTreeViewColumn *column; - GdkRectangle cell_area; - int drag_begin_y = 0; - char *uri; - - treeview = nautilus_list_model_get_drag_view (context->view->details->model, - NULL, - &drag_begin_y); - column = gtk_tree_view_get_column (treeview, 0); - - file = nautilus_list_model_file_for_path (NAUTILUS_LIST_MODEL (model), path); - if (file == NULL) - { - return; - } - - gtk_tree_view_get_cell_area (treeview, - path, - column, - &cell_area); - - uri = nautilus_file_get_activation_uri (file); - - nautilus_file_unref (file); - - /* pass the uri, mouse-relative x/y and icon width/height */ - context->iteratee (uri, - 0, - cell_area.y - drag_begin_y, - cell_area.width, - cell_area.height, - context->iteratee_data); - - g_free (uri); -} - -static void -each_item_get_data_binder (NautilusDragEachSelectedItemDataGet iteratee, - gpointer iterator_context, - gpointer data) -{ - NautilusListView *view = NAUTILUS_LIST_VIEW (iterator_context); - ListGetDataBinderContext context; - GtkTreeSelection *selection; - - context.view = view; - context.iteratee = iteratee; - context.iteratee_data = data; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_view)); - gtk_tree_selection_selected_foreach (selection, item_get_data_binder, &context); -} - -static void -drag_begin_callback (GtkWidget *widget, - GdkDragContext *context, - NautilusListView *view) -{ - cairo_surface_t *surface; - NautilusWindow *window; - GList *dragged_files; - - window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (view)); - surface = get_drag_surface (view); - if (surface) - { - gtk_drag_set_icon_surface (context, surface); - cairo_surface_destroy (surface); - } - else - { - gtk_drag_set_icon_default (context); - } - - view->details->drag_button = 0; - view->details->drag_started = TRUE; - - view->details->drag_source_info->selection_cache = nautilus_drag_create_selection_cache (view, - each_item_get_data_binder); - - dragged_files = nautilus_drag_file_list_from_selection_list (view->details->drag_source_info->selection_cache); - if (nautilus_file_list_are_all_folders (dragged_files)) - { - nautilus_window_start_dnd (window, context); - } - g_list_free_full (dragged_files, g_object_unref); -} - -static void -drag_end_callback (GtkWidget *widget, - GdkDragContext *context, - NautilusListView *list_view) -{ - NautilusWindow *window; - - window = nautilus_files_view_get_window (NAUTILUS_FILES_VIEW (list_view)); - - nautilus_window_end_dnd (window, context); - - drag_info_data_free (list_view); -} - -static void -drag_info_data_free (NautilusListView *list_view) -{ - nautilus_drag_destroy_selection_list (list_view->details->drag_source_info->selection_cache); - list_view->details->drag_source_info->selection_cache = NULL; - - g_free (list_view->details->drag_source_info); - list_view->details->drag_source_info = NULL; - - g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_begin_callback, list_view); - g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_data_get_callback, list_view); - g_signal_handlers_disconnect_by_func (list_view->details->tree_view, drag_end_callback, list_view); -} - -NautilusDragInfo * -nautilus_list_view_dnd_get_drag_source_data (NautilusListView *list_view, - GdkDragContext *context) -{ - GtkTreeView *tree_view; - GtkTreeModel *model; - - tree_view = GTK_TREE_VIEW (list_view->details->tree_view); - - model = gtk_tree_view_get_model (tree_view); - - if (model == NULL) - { - return NULL; - } - - if (list_view->details->drag_source_info == NULL || - list_view->details->drag_source_info->selection_cache == NULL) - { - return NULL; - } - - return list_view->details->drag_source_info; -} - -void -nautilus_list_view_dnd_init (NautilusListView *list_view) -{ - if (list_view->details->drag_source_info != NULL) - { - return; - } - - list_view->details->drag_source_info = g_new0 (NautilusDragInfo, 1); - - g_signal_connect_object (list_view->details->tree_view, "drag-begin", - G_CALLBACK (drag_begin_callback), list_view, 0); - g_signal_connect_object (list_view->details->tree_view, "drag-end", - G_CALLBACK (drag_end_callback), list_view, 0); - g_signal_connect_object (list_view->details->tree_view, "drag-data-get", - G_CALLBACK (drag_data_get_callback), list_view, 0); -} - -void -nautilus_list_view_dnd_drag_begin (NautilusListView *list_view, - gdouble offset_x, - gdouble offset_y, - GdkEvent *event) -{ - if (list_view->details->drag_button == 0) - { - return; - } - - if (!source_target_list) - { - source_target_list = nautilus_list_model_get_drag_target_list (); - } - - if (gtk_drag_check_threshold (GTK_WIDGET (list_view->details->tree_view), - list_view->details->drag_x, - list_view->details->drag_y, - list_view->details->drag_x + offset_x, - list_view->details->drag_y + offset_y)) - { - guint32 actions; - - actions = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK; - list_view->details->drag_source_info->source_actions = actions; - gtk_drag_begin_with_coordinates (GTK_WIDGET (list_view->details->tree_view), - source_target_list, - actions, - list_view->details->drag_button, - (GdkEvent *) event, - -1, - -1); - } -} -#endif diff --git a/src/nautilus-list-view-dnd.h b/src/nautilus-list-view-dnd.h deleted file mode 100644 index 88ade6770de5cba8baaeae49fa117e6d9e42bf11..0000000000000000000000000000000000000000 --- a/src/nautilus-list-view-dnd.h +++ /dev/null @@ -1,36 +0,0 @@ -/* nautilus-list-view-dnd.h - * - * Copyright (C) 2015 Carlos Soriano - * - * This program 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 3 of the License, or - * (at your option) any later version. - * - * This program 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 . - */ - -#pragma once -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - -#include - -#include "nautilus-list-view.h" - -#include "nautilus-dnd.h" - -void nautilus_list_view_dnd_init (NautilusListView *list_view); -void nautilus_list_view_dnd_drag_begin (NautilusListView *list_view, - gdouble offset_x, - gdouble offset_y, - GdkEvent *event); -NautilusDragInfo * -nautilus_list_view_dnd_get_drag_source_data (NautilusListView *list_view, - GdkDragContext *context); -#endif diff --git a/src/nautilus-list-view-private.h b/src/nautilus-list-view-private.h deleted file mode 100644 index 4c0e3e1c86a5bf2747aed220036fd64fe8228eb9..0000000000000000000000000000000000000000 --- a/src/nautilus-list-view-private.h +++ /dev/null @@ -1,80 +0,0 @@ -/* nautilus-list-view-private.h - * - * Copyright (C) 2015 Carlos Soriano - * - * This program 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 3 of the License, or - * (at your option) any later version. - * - * This program 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 . - */ - -/* Data and functions shared between list view and list view dnd */ - -#pragma once - -#include "nautilus-list-model.h" -#include "nautilus-tree-view-drag-dest.h" -#include "nautilus-dnd.h" -#include "nautilus-tag-manager.h" - -struct NautilusListViewDetails { - GtkTreeView *tree_view; - NautilusListModel *model; - - GtkTreeViewColumn *file_name_column; - int file_name_column_num; - - GtkCellRendererPixbuf *pixbuf_cell; - GtkCellRendererText *file_name_cell; - GList *cells; - - NautilusListZoomLevel zoom_level; - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - NautilusTreeViewDragDest *drag_dest; -#endif - - GtkTreePath *first_click_path; /* Both clicks in a double click need to be on the same row */ - - GtkTreePath *new_selection_path; /* Path of the new selection after removing a file */ - - GtkTreePath *hover_path; - - gint last_event_button_x; - gint last_event_button_y; - - guint drag_button; - int drag_x; - int drag_y; - - gboolean drag_started; - gboolean row_selected_on_button_down; - gboolean active; -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - NautilusDragInfo *drag_source_info; -#endif - - GHashTable *columns; - GtkWidget *column_editor; - GtkWidget *columns_popover; - GtkWidget *columns_popover_box; - - char *original_name; - - gulong clipboard_handler_id; - - GQuark last_sort_attr; - - GRegex *regex; - - GCancellable *starred_cancellable; -}; - diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c index 0aeb4b14645689b2afb3df78ab6cf2679773eb9c..ab13aaae42cf8501bca5538c8fb9bec9870f8d69 100644 --- a/src/nautilus-list-view.c +++ b/src/nautilus-list-view.c @@ -1,3107 +1,447 @@ -/* fm-list-view.c - implementation of list view of directory. - * - * Copyright (C) 2000 Eazel, Inc. - * Copyright (C) 2001, 2002 Anders Carlsson - * - * 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 . +/* + * Copyright (C) 2000 Eazel, Inc. + * Copyright (C) 2001, 2002 Anders Carlsson + * Copyright (C) 2022 GNOME project contributors * - * Authors: John Sullivan - * Anders Carlsson - * David Emory Watson + * SPDX-License-Identifier: GPL-3.0-or-later */ -#include "nautilus-list-view.h" -#include "nautilus-list-view-private.h" - -#include -#include -#include #include -#include -#include -#define DEBUG_FLAG NAUTILUS_DEBUG_LIST_VIEW -#include "nautilus-debug.h" +/* Needed for NautilusColumn. */ +#include + +#include "nautilus-list-base-private.h" +#include "nautilus-list-view.h" -#include "nautilus-clipboard.h" #include "nautilus-column-chooser.h" #include "nautilus-column-utilities.h" -#include "nautilus-dnd.h" -#include "nautilus-enums.h" -#include "nautilus-error-reporting.h" +#include "nautilus-directory.h" +#include "nautilus-file.h" #include "nautilus-file-utilities.h" -#include "nautilus-files-view-dnd.h" #include "nautilus-global-preferences.h" -#include "nautilus-list-model.h" -#include "nautilus-list-view-dnd.h" +#include "nautilus-label-cell.h" #include "nautilus-metadata.h" +#include "nautilus-name-cell.h" #include "nautilus-search-directory.h" +#include "nautilus-star-cell.h" #include "nautilus-tag-manager.h" -#include "nautilus-toolbar.h" -#include "nautilus-tree-view-drag-dest.h" -#include "nautilus-ui-utilities.h" -#include "nautilus-view.h" -#include "nautilus-tracker-utilities.h" -struct SelectionForeachData +struct _NautilusListView { - GList *list; - GtkTreeSelection *selection; -}; + NautilusListBase parent_instance; -/* - * The row height should be large enough to not clip emblems. - * Computing this would be costly, so we just choose a number - * that works well with the set of emblems we've designed. - */ -#define LIST_VIEW_MINIMUM_ROW_HEIGHT 28 + GtkColumnView *view_ui; -/* The star icon itself is 16px, which leaves an empty 16px gutter on each side, - * which is necessary to avoid the overlay scrollbar. - */ -#define STAR_COLUMN_WIDTH 48 - -/* We wait two seconds after row is collapsed to unload the subdirectory */ -#define COLLAPSE_TO_UNLOAD_DELAY 2 - -/* According to Pango docs, alpha is a guint16 value between 0 and 65535. */ -#define ALPHA_55_PERCENT ((guint16) (0.55 * 0xffff)) - -static GdkCursor *hand_cursor = NULL; - -static GList *nautilus_list_view_get_selection (NautilusFilesView *view); -static GList *nautilus_list_view_get_selection_for_file_transfer (NautilusFilesView *view); -static void nautilus_list_view_set_zoom_level (NautilusListView *view, - NautilusListZoomLevel new_level); -static void nautilus_list_view_scroll_to_file (NautilusListView *view, - NautilusFile *file); -static void nautilus_list_view_sort_directories_first_changed (NautilusFilesView *view); - -static void apply_columns_settings (NautilusListView *list_view, - char **column_order, - char **visible_columns); -static char **get_visible_columns (NautilusListView *list_view); -static char **get_default_visible_columns (NautilusListView *list_view); -static char **get_column_order (NautilusListView *list_view); -static char **get_default_column_order (NautilusListView *list_view); -static void popup_column_header_menu (NautilusListView *list_view, - gdouble x, - gdouble y); - -G_DEFINE_TYPE (NautilusListView, nautilus_list_view, NAUTILUS_TYPE_FILES_VIEW); - -static const char *default_search_visible_columns[] = -{ - "name", "size", "where", NULL -}; + GActionGroup *action_group; + gint zoom_level; -static const char *default_search_columns_order[] = -{ - "name", "size", "where", NULL -}; + gboolean directories_first; -static const char *default_recent_visible_columns[] = -{ - "name", "where", "recency", NULL -}; + GQuark path_attribute_q; + GFile *file_path_base_location; -static const char *default_recent_columns_order[] = -{ - "name", "where", "recency", NULL + GtkColumnViewColumn *star_column; + GtkWidget *column_editor; + GHashTable *factory_to_column_map; }; -static const char *default_trash_visible_columns[] = +G_DEFINE_TYPE (NautilusListView, nautilus_list_view, NAUTILUS_TYPE_LIST_BASE) + + +static void on_sorter_changed (GtkSorter *sorter, + GtkSorterChange change, + gpointer user_data); + +static const char *default_columns_for_recent[] = { - "name", "size", "trash_orig_path", "trashed_on", NULL + "name", "size", "recency", NULL }; -static const char *default_trash_columns_order[] = +static const char *default_columns_for_trash[] = { - "name", "size", "trash_orig_path", "trashed_on", NULL + "name", "size", "trashed_on", NULL }; -static const gchar * -get_default_sort_order (NautilusFile *file, - gboolean *reversed) +static guint +get_icon_size_for_zoom_level (NautilusListZoomLevel zoom_level) { - NautilusFileSortType sort_type; - - /* This array makes the #NautilusFileSortType values correspond to the - * respective column attribute. - */ - const char *attributes[] = + switch (zoom_level) { - "name", - "size", - "type", - "date_modified", - "date_accessed", - "date_created", - "starred", - "trashed_on", - "search_relevance", - "recency", - NULL - }; + case NAUTILUS_LIST_ZOOM_LEVEL_SMALL: + { + return NAUTILUS_LIST_ICON_SIZE_SMALL; + } + break; - sort_type = nautilus_file_get_default_sort_type (file, reversed); + case NAUTILUS_LIST_ZOOM_LEVEL_MEDIUM: + { + return NAUTILUS_LIST_ICON_SIZE_MEDIUM; + } + break; - return attributes[sort_type]; + case NAUTILUS_LIST_ZOOM_LEVEL_LARGE: + { + return NAUTILUS_LIST_ICON_SIZE_LARGE; + } + break; + } + g_return_val_if_reached (NAUTILUS_LIST_ICON_SIZE_MEDIUM); } -static void -list_selection_changed_callback (GtkTreeSelection *selection, - gpointer user_data) +static guint +real_get_icon_size (NautilusListBase *files_model_view) { - NautilusFilesView *view; + NautilusListView *self = NAUTILUS_LIST_VIEW (files_model_view); - view = NAUTILUS_FILES_VIEW (user_data); - - nautilus_files_view_notify_selection_changed (view); + return get_icon_size_for_zoom_level (self->zoom_level); } -static void -activate_selected_items (NautilusListView *view) +static GtkWidget * +real_get_view_ui (NautilusListBase *files_model_view) { - GList *file_list; + NautilusListView *self = NAUTILUS_LIST_VIEW (files_model_view); - file_list = nautilus_list_view_get_selection (NAUTILUS_FILES_VIEW (view)); - if (file_list != NULL) - { - nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (view), - file_list, - 0, TRUE); - nautilus_file_list_free (file_list); - } + return GTK_WIDGET (self->view_ui); } static void -activate_selected_items_alternate (NautilusListView *view, - NautilusFile *file, - gboolean open_in_tab) +apply_columns_settings (NautilusListView *self, + char **column_order, + char **visible_columns) { - GList *file_list; - NautilusOpenFlags flags; - - flags = 0; - - if (open_in_tab) + g_autolist (NautilusColumn) all_columns = NULL; + NautilusFile *file; + NautilusDirectory *directory; + g_autoptr (GFile) location = NULL; + g_autoptr (GList) view_columns = NULL; + GListModel *old_view_columns; + g_autoptr (GHashTable) visible_columns_hash = NULL; + g_autoptr (GHashTable) old_view_columns_hash = NULL; + int column_i = 0; + + file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); + directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (self)); + if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { - flags |= NAUTILUS_OPEN_FLAG_NEW_TAB; - flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE; + g_autoptr (NautilusQuery) query = NULL; + + query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory)); + location = nautilus_query_get_location (query); } else { - flags |= NAUTILUS_OPEN_FLAG_NEW_WINDOW; + location = nautilus_file_get_location (file); } - if (file != NULL) + all_columns = nautilus_get_columns_for_file (file); + all_columns = nautilus_sort_columns (all_columns, column_order); + + /* hash table to lookup if a given column should be visible */ + visible_columns_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + /* always show name column */ + g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name")); + + /* always show star column if supported */ + if (nautilus_tag_manager_can_star_contents (nautilus_tag_manager_get (), location) || + nautilus_is_starred_directory (location)) { - nautilus_file_ref (file); - file_list = g_list_prepend (NULL, file); + g_hash_table_insert (visible_columns_hash, g_strdup ("starred"), g_strdup ("starred")); } - else + + if (visible_columns != NULL) { - file_list = nautilus_list_view_get_selection (NAUTILUS_FILES_VIEW (view)); + for (int i = 0; visible_columns[i] != NULL; ++i) + { + g_hash_table_insert (visible_columns_hash, + g_ascii_strdown (visible_columns[i], -1), + g_ascii_strdown (visible_columns[i], -1)); + } } - nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (view), - file_list, - flags, - TRUE); - nautilus_file_list_free (file_list); -} -static gboolean -button_event_modifies_selection (GdkEvent *event) -{ - GdkModifierType state; + old_view_columns_hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + NULL); + old_view_columns = gtk_column_view_get_columns (self->view_ui); + for (guint i = 0; i < g_list_model_get_n_items (old_view_columns); i++) + { + g_autoptr (GtkColumnViewColumn) view_column = NULL; + GtkListItemFactory *factory; + NautilusColumn *nautilus_column; + gchar *name; - state = gdk_event_get_modifier_state (event); + view_column = g_list_model_get_item (old_view_columns, i); + factory = gtk_column_view_column_get_factory (view_column); + nautilus_column = g_hash_table_lookup (self->factory_to_column_map, factory); + g_object_get (nautilus_column, "name", &name, NULL); + g_hash_table_insert (old_view_columns_hash, name, view_column); + } - return (state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0; -} + for (GList *l = all_columns; l != NULL; l = l->next) + { + g_autofree char *name = NULL; + g_autofree char *lowercase = NULL; -static int -get_click_policy (void) -{ - return g_settings_get_enum (nautilus_preferences, - NAUTILUS_PREFERENCES_CLICK_POLICY); -} + g_object_get (G_OBJECT (l->data), "name", &name, NULL); + lowercase = g_ascii_strdown (name, -1); -static void -on_event_controller_motion_motion (GtkEventControllerMotion *controller, - double x, - double y, - gpointer user_data) -{ - NautilusListView *view; - GtkWidget *widget; - GtkTreePath *old_hover_path; - int x_in_bin; - int y_in_bin; + if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL) + { + GtkColumnViewColumn *view_column; - if (get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE) - { - return; + view_column = g_hash_table_lookup (old_view_columns_hash, name); + if (view_column != NULL) + { + view_columns = g_list_prepend (view_columns, view_column); + } + } } - view = user_data; - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller)); - old_hover_path = view->details->hover_path; - - gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (widget), - x, y, - &x_in_bin, &y_in_bin); - gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), - x_in_bin, y_in_bin, - &view->details->hover_path, - NULL, NULL, NULL); + view_columns = g_list_reverse (view_columns); - if ((old_hover_path != NULL) != (view->details->hover_path != NULL)) + /* hide columns that are not present in the configuration */ + for (guint i = 0; i < g_list_model_get_n_items (old_view_columns); i++) { - if (view->details->hover_path != NULL) + g_autoptr (GtkColumnViewColumn) view_column = NULL; + + view_column = g_list_model_get_item (old_view_columns, i); + if (g_list_find (view_columns, view_column) == NULL) { - gtk_widget_set_cursor (widget, hand_cursor); + gtk_column_view_column_set_visible (view_column, FALSE); } else { - gtk_widget_set_cursor (widget, NULL); + gtk_column_view_column_set_visible (view_column, TRUE); } } - if (old_hover_path != NULL) - { - gtk_tree_path_free (old_hover_path); - } -} - -static void -on_event_controller_motion_leave (GtkEventControllerMotion *controller, - gpointer user_data) -{ - NautilusListView *view; - - view = user_data; - - if (get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE || - view->details->hover_path == NULL) + /* place columns in the correct order */ + for (GList *l = view_columns; l != NULL; l = l->next, column_i++) { - return; + gtk_column_view_insert_column (self->view_ui, column_i, l->data); } - - gtk_tree_path_free (view->details->hover_path); - view->details->hover_path = NULL; } static void -on_event_controller_motion_enter (GtkEventControllerMotion *controller, - double x, - double y, - gpointer user_data) +real_scroll_to_item (NautilusListBase *files_model_view, + guint position) { - NautilusListView *view; - GtkWidget *widget; - int x_in_bin; - int y_in_bin; + NautilusListView *self = NAUTILUS_LIST_VIEW (files_model_view); + GtkWidget *child; - if (get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE) - { - return; - } + child = gtk_widget_get_last_child (GTK_WIDGET (self->view_ui)); - view = user_data; - if (view->details->hover_path != NULL) + while (child != NULL && !GTK_IS_LIST_VIEW (child)) { - gtk_tree_path_free (view->details->hover_path); + child = gtk_widget_get_prev_sibling (child); } - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller)); - gtk_tree_view_convert_widget_to_bin_window_coords (GTK_TREE_VIEW (widget), - x, y, - &x_in_bin, &y_in_bin); - gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), - x_in_bin, y_in_bin, - &view->details->hover_path, - NULL, NULL, NULL); - - if (view->details->hover_path != NULL) + if (child != NULL) { - gtk_widget_set_cursor (widget, hand_cursor); + gtk_widget_activate_action (child, "list.scroll-to-item", "u", position); } } -static void -row_activated_callback (GtkTreeView *treeview, - GtkTreePath *path, - GtkTreeViewColumn *column, - NautilusListView *view) +static gint +nautilus_list_view_sort (gconstpointer a, + gconstpointer b, + gpointer user_data) { - activate_selected_items (view); + GQuark attribute_q = GPOINTER_TO_UINT (user_data); + NautilusFile *file_a = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) a)); + NautilusFile *file_b = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) b)); + + /* The reversed argument is FALSE because the columnview sorter handles that + * itself and if we don't want to reverse the reverse. The directories_first + * argument is also FALSE for the same reason: we don't want the columnview + * sorter to reverse it (it would display directories last!); instead we + * handle directories_first in a separate sorter. */ + return nautilus_file_compare_for_sort_by_attribute_q (file_a, file_b, + attribute_q, + FALSE /* directories_first */, + FALSE /* reversed */); } -static gboolean -check_starred_status (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) +static gint +sort_directories_func (gconstpointer a, + gconstpointer b, + gpointer user_data) { - NautilusFile *file; - GList *l; - GList *changed_files; - - changed_files = data; + gboolean *directories_first = user_data; - gtk_tree_model_get (GTK_TREE_MODEL (model), - iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - - if (!file) + if (*directories_first) { - return FALSE; - } + NautilusFile *file_a = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) a)); + NautilusFile *file_b = nautilus_view_item_get_file (NAUTILUS_VIEW_ITEM ((gpointer) b)); + gboolean a_is_directory = nautilus_file_is_directory (file_a); + gboolean b_is_directory = nautilus_file_is_directory (file_b); - for (l = changed_files; l != NULL; l = l->next) - { - if (nautilus_file_compare_location (NAUTILUS_FILE (l->data), file) == 0) + if (a_is_directory && !b_is_directory) + { + return GTK_ORDERING_SMALLER; + } + if (b_is_directory && !a_is_directory) { - gtk_tree_model_row_changed (model, path, iter); + return GTK_ORDERING_LARGER; } } - - nautilus_file_unref (file); - - return FALSE; -} - -static void -on_starred_files_changed (NautilusTagManager *tag_manager, - GList *changed_files, - gpointer user_data) -{ - NautilusListView *list_view; - - list_view = NAUTILUS_LIST_VIEW (user_data); - - gtk_tree_model_foreach (GTK_TREE_MODEL (list_view->details->model), - check_starred_status, - changed_files); + return GTK_ORDERING_EQUAL; } -static void -on_star_cell_renderer_clicked (GtkTreePath *path, - NautilusListView *list_view) +static char ** +get_default_visible_columns (NautilusListView *self) { - NautilusListModel *list_model; NautilusFile *file; - g_autofree gchar *uri = NULL; - GList *selection; - NautilusTagManager *tag_manager = nautilus_tag_manager_get (); - - list_model = list_view->details->model; - file = nautilus_list_model_file_for_path (list_model, path); + file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); - if (file == NULL) + if (nautilus_file_is_in_trash (file)) { - /* This row is a label, not a file */ - return; + return g_strdupv ((gchar **) default_columns_for_trash); } - uri = nautilus_file_get_uri (file); - selection = g_list_prepend (NULL, file); - - if (nautilus_tag_manager_file_is_starred (tag_manager, uri)) - { - nautilus_tag_manager_unstar_files (tag_manager, - G_OBJECT (list_view), - selection, - NULL, - list_view->details->starred_cancellable); - } - else + if (nautilus_file_is_in_recent (file)) { - nautilus_tag_manager_star_files (tag_manager, - G_OBJECT (list_view), - selection, - NULL, - list_view->details->starred_cancellable); + return g_strdupv ((gchar **) default_columns_for_recent); } - nautilus_file_list_free (selection); + return g_settings_get_strv (nautilus_list_view_preferences, + NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS); } -static void -on_tree_view_click_gesture_pressed (GtkGestureClick *gesture, - gint n_press, - gdouble x, - gdouble y, - gpointer callback_data) +static char ** +get_visible_columns (NautilusListView *self) { - NautilusListView *view; - GtkWidget *widget; - GtkTreeView *tree_view; - g_autoptr (GtkTreePath) path = NULL; - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - guint button; - gint bin_x; - gint bin_y; - GdkEventSequence *sequence; - GdkEvent *event; - gboolean on_expander, show_expanders; - gboolean is_simple_click, path_selected; NautilusFile *file; - gboolean on_star; - - view = NAUTILUS_LIST_VIEW (callback_data); - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); - tree_view = GTK_TREE_VIEW (widget); - selection = gtk_tree_view_get_selection (tree_view); - button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &bin_x, &bin_y); + g_autofree gchar **visible_columns = NULL; - view->details->last_event_button_x = bin_x; - view->details->last_event_button_y = bin_y; + file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); - /* Don't handle extra mouse buttons here */ - if (button > 5) + visible_columns = nautilus_file_get_metadata_list (file, + NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS); + if (visible_columns == NULL || visible_columns[0] == NULL) { - return; + return get_default_visible_columns (self); } - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); + return g_steal_pointer (&visible_columns); +} - /* Column headers lie above bin_window, hence negative y coordinate. */ - if (bin_y < 0) - { - if (button == GDK_BUTTON_SECONDARY) - { - popup_column_header_menu (view, x, y); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - } - else - { - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - } - return; - } +static char ** +get_default_column_order (NautilusListView *self) +{ + NautilusFile *file; - nautilus_list_model_set_drag_view - (NAUTILUS_LIST_MODEL (gtk_tree_view_get_model (tree_view)), - tree_view, - bin_x, bin_y); + file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); - /* Ignore double click if we are in single click mode */ - if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE && n_press >= 2) + if (nautilus_file_is_in_trash (file)) { - return; + return g_strdupv ((gchar **) default_columns_for_trash); } - is_simple_click = ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE) && (n_press == 1)); - - /* No item at this position */ - if (!gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y, - &path, &column, NULL, NULL)) + if (nautilus_file_is_in_recent (file)) { - if (is_simple_click) - { - g_clear_pointer (&view->details->first_click_path, gtk_tree_path_free); - } + return g_strdupv ((gchar **) default_columns_for_recent); + } - gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view)); + return g_settings_get_strv (nautilus_list_view_preferences, + NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER); +} - if (button == GDK_BUTTON_SECONDARY) - { - nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (view), - x, y); - } +static char ** +get_column_order (NautilusListView *self) +{ + NautilusFile *file; + g_autofree gchar **column_order = NULL; - return; - } + file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)); - on_expander = FALSE; - path_selected = gtk_tree_selection_path_is_selected (selection, path); - show_expanders = g_settings_get_boolean (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE); + column_order = nautilus_file_get_metadata_list (file, + NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER); - if (show_expanders) + if (column_order != NULL && column_order[0] != NULL) { - GdkRectangle cell_area; + return g_steal_pointer (&column_order); + } - gtk_tree_view_get_cell_area (tree_view, path, column, &cell_area); + return get_default_column_order (self); +} +static void +update_columns_settings_from_metadata_and_preferences (NautilusListView *self) +{ + g_auto (GStrv) column_order = get_column_order (self); + g_auto (GStrv) visible_columns = get_visible_columns (self); - /* We assume that the cell area excludes the expander itself. - * Explanatory link for future reference: - * https://gitlab.gnome.org/GNOME/nautilus/merge_requests/97#note_58649 */ + apply_columns_settings (self, column_order, visible_columns); +} - if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) - { - on_expander = bin_x > (cell_area.x + cell_area.width); - } - else - { - on_expander = bin_x < cell_area.x; - } - } +static GFile * +get_base_location (NautilusListView *self) +{ + NautilusDirectory *directory; + GFile *base_location = NULL; - /* Keep track of path of last click so double clicks only happen - * on the same item */ - if (is_simple_click) + directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (self)); + if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { - g_clear_pointer (&view->details->first_click_path, gtk_tree_path_free); - view->details->first_click_path = gtk_tree_path_copy (path); - } + g_autoptr (NautilusQuery) query = NULL; + g_autoptr (GFile) location = NULL; - on_star = (g_hash_table_lookup (view->details->columns, "starred") == column && - !gtk_tree_view_is_blank_at_pos (tree_view, - bin_x, - bin_y, - NULL, - NULL, - NULL, - NULL)); - - if (is_simple_click && on_star) - { - on_star_cell_renderer_clicked (path, view); - } - else if (n_press == 2 && !on_star) - { - /* Double clicking does not trigger a D&D action. */ - view->details->drag_button = 0; + query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory)); + location = nautilus_query_get_location (query); - /* NOTE: Activation can actually destroy the view if we're switching */ - if (!on_expander && - view->details->first_click_path && - gtk_tree_path_compare (path, view->details->first_click_path) == 0) + if (!nautilus_is_recent_directory (location) && + !nautilus_is_starred_directory (location) && + !nautilus_is_trash_directory (location)) { - if ((button == GDK_BUTTON_PRIMARY) && button_event_modifies_selection (event)) - { - file = nautilus_list_model_file_for_path (view->details->model, path); - if (file != NULL) - { - activate_selected_items_alternate (view, file, TRUE); - nautilus_file_unref (file); - } - } - else if ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_SECONDARY)) - { - activate_selected_items (view); - } - } - else - { - return; + base_location = g_steal_pointer (&location); } } - else - { - GdkModifierType state; - g_autoptr (GtkTreePath) cursor = NULL; - GList *selected_rows = NULL; - - state = gdk_event_get_modifier_state (event); - - /* We cannot easily match the expected behavior of Shift+click, so we - * must fall back to GtkTreeView's default event handling. - * - * If Shift and Ctrl are held simultateously, GtkTreeView ignores Shift, - * so we implement a more useful behavior ourselves. - */ - if ((state & GDK_SHIFT_MASK) != 0 && (state & GDK_CONTROL_MASK) == 0) - { - return; - } - /* Let GtkTreeView handle tree expanding/collapsing. */ - if (is_simple_click && on_expander) - { - return; - } + return base_location; +} - /* As we don't let GtkTreeView default event handling go through, so we - * must grab the focus ourselves. */ - gtk_widget_grab_focus (widget); +static void +on_column_view_item_activated (GtkGridView *grid_view, + guint position, + gpointer user_data) +{ + NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); - if (!path_selected) - { - if ((state & GDK_CONTROL_MASK) != 0) - { - if ((state & GDK_SHIFT_MASK) != 0) - { - gtk_tree_view_get_cursor (tree_view, &cursor, NULL); - if (cursor != NULL) - { - gtk_tree_selection_select_range (selection, cursor, path); - } - else - { - gtk_tree_selection_select_path (selection, path); - } - } - else - { - gtk_tree_selection_select_path (selection, path); - } - selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL); - - /* This unselects everything */ - gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE); - - /* So select it again */ - for (GList *l = selected_rows; l != NULL; l = l->next) - { - gtk_tree_selection_select_path (selection, l->data); - } - g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free); - } - else - { - gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE); - } - } + nautilus_files_view_activate_selection (NAUTILUS_FILES_VIEW (self)); +} - if (is_simple_click) - { - view->details->drag_started = FALSE; - view->details->drag_button = button; - view->details->drag_x = bin_x; - view->details->drag_y = bin_y; - view->details->row_selected_on_button_down = path_selected; - } +static GtkColumnView * +create_view_ui (NautilusListView *self) +{ + NautilusViewModel *model; + GtkWidget *widget; - if (button == GDK_BUTTON_SECONDARY) - { - nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (view), - x, y); - } + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + widget = gtk_column_view_new (GTK_SELECTION_MODEL (model)); - /* Don't open a new tab if we are in single click mode (this would open 2 tabs), - * or if CTRL or SHIFT is pressed. - */ - if (button == GDK_BUTTON_MIDDLE && - get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE && - !button_event_modifies_selection (event)) - { - gtk_tree_selection_unselect_all (selection); - gtk_tree_selection_select_path (selection, path); + gtk_widget_set_hexpand (widget, TRUE); - activate_selected_items_alternate (view, NULL, TRUE); - } - } - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); -} - -static void -on_tree_view_click_gesture_released (GtkGestureClick *gesture, - gint n_press, - gdouble x, - gdouble y, - gpointer callback_data) -{ - NautilusListView *view; - guint button; - GdkEventSequence *sequence; - GdkEvent *event; - GtkTreeView *tree_view; - GtkTreeSelection *selection; - gint x_in_bin; - gint y_in_bin; - GtkTreePath *path; - GdkModifierType state; - - view = NAUTILUS_LIST_VIEW (callback_data); - button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - if (button != view->details->drag_button) - { - return; - } - - view->details->drag_button = 0; - - if (view->details->drag_started) - { - return; - } - - /* Did not drag. */ - - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); - /* Typically will only happen with GTK+ <= 3.22.30 and <= 3.93.0, - * where ::released is emitted after ::cancel, but can’t hurt to guard - * against it anyway. - */ - if (event == NULL) - { - return; - } - - tree_view = view->details->tree_view; - selection = gtk_tree_view_get_selection (tree_view); - - - gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, - x, y, - &x_in_bin, &y_in_bin); - - if (!gtk_tree_view_get_path_at_pos (tree_view, x_in_bin, y_in_bin, &path, NULL, NULL, NULL)) - { - return; - } - - state = gdk_event_get_modifier_state (event); - - if ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE) - && ((state & GDK_CONTROL_MASK) != 0 || - (state & GDK_SHIFT_MASK) == 0) - && view->details->row_selected_on_button_down) - { - if (!button_event_modifies_selection (event)) - { - gtk_tree_selection_unselect_all (selection); - gtk_tree_selection_select_path (selection, path); - } - else - { - gtk_tree_selection_unselect_path (selection, path); - } - } - - if ((get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE) - && !button_event_modifies_selection (event)) - { - if (button == GDK_BUTTON_PRIMARY) - { - activate_selected_items (view); - } - else if (button == GDK_BUTTON_MIDDLE) - { - activate_selected_items_alternate (view, NULL, TRUE); - } - } - gtk_tree_path_free (path); -} - -static gboolean -on_event_controller_key_key_pressed (GtkEventControllerKey *controller, - unsigned int keyval, - unsigned int keycode, - GdkModifierType state, - gpointer user_data) -{ - GtkWidget *widget; - NautilusFilesView *view; - GtkTreeView *tree_view; - - widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (controller)); - view = NAUTILUS_FILES_VIEW (user_data); - tree_view = GTK_TREE_VIEW (widget); - - NAUTILUS_LIST_VIEW (view)->details->last_event_button_x = -1; - NAUTILUS_LIST_VIEW (view)->details->last_event_button_y = -1; - - if (keyval == GDK_KEY_Right) - { - g_autoptr (GtkTreePath) path = NULL; - - gtk_tree_view_get_cursor (tree_view, &path, NULL); - - if (path != NULL) - { - gtk_tree_view_expand_row (tree_view, path, FALSE); - } - - return GDK_EVENT_STOP; - } - - if (keyval == GDK_KEY_Left) - { - g_autoptr (GtkTreePath) path = NULL; - - gtk_tree_view_get_cursor (tree_view, &path, NULL); - - if (path != NULL && !gtk_tree_view_collapse_row (tree_view, path)) - { - /* if the row is already collapsed or doesn't have any children, - * jump to the parent row instead. - */ - if ((gtk_tree_path_get_depth (path) > 1) && gtk_tree_path_up (path)) - { - gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE); - } - } - - return GDK_EVENT_STOP; - } - - if (keyval == GDK_KEY_space) - { - if ((state & GDK_CONTROL_MASK) != 0) - { - return GDK_EVENT_PROPAGATE; - } - - if (!gtk_widget_has_focus (GTK_WIDGET (NAUTILUS_LIST_VIEW (view)->details->tree_view))) - { - return GDK_EVENT_PROPAGATE; - } - - if ((state & GDK_SHIFT_MASK) != 0) - { - activate_selected_items_alternate (NAUTILUS_LIST_VIEW (view), NULL, TRUE); - } - - return GDK_EVENT_STOP; - } - - if (keyval == GDK_KEY_v) - { - /* Eat Control + v to not enable type ahead */ - if ((state & GDK_CONTROL_MASK) != 0) - { - return GDK_EVENT_STOP; - } - } - - return GDK_EVENT_PROPAGATE; -} - -static void -subdirectory_done_loading_callback (NautilusDirectory *directory, - NautilusListView *view) -{ - nautilus_list_model_subdirectory_done_loading (view->details->model, directory); -} - -static void -row_expanded_callback (GtkTreeView *treeview, - GtkTreeIter *iter, - GtkTreePath *path, - gpointer callback_data) -{ - NautilusListView *view; - NautilusDirectory *directory; - char *uri; - - view = NAUTILUS_LIST_VIEW (callback_data); - - if (!nautilus_list_model_load_subdirectory (view->details->model, path, &directory)) - { - return; - } - - uri = nautilus_directory_get_uri (directory); - DEBUG ("Row expanded callback for URI %s", uri); - g_free (uri); - - nautilus_files_view_add_subdirectory (NAUTILUS_FILES_VIEW (view), directory); - - if (nautilus_directory_are_all_files_seen (directory)) - { - nautilus_list_model_subdirectory_done_loading (view->details->model, - directory); - } - else - { - g_signal_connect_object (directory, "done-loading", - G_CALLBACK (subdirectory_done_loading_callback), - view, 0); - } - - nautilus_directory_unref (directory); -} - -typedef struct -{ - NautilusFile *file; - NautilusDirectory *directory; - NautilusListView *view; -} UnloadDelayData; - -static void -unload_delay_data_free (UnloadDelayData *unload_data) -{ - if (unload_data->view != NULL) - { - g_object_remove_weak_pointer (G_OBJECT (unload_data->view), - (gpointer *) &unload_data->view); - } - - nautilus_directory_unref (unload_data->directory); - nautilus_file_unref (unload_data->file); - - g_slice_free (UnloadDelayData, unload_data); -} - -static UnloadDelayData * -unload_delay_data_new (NautilusFile *file, - NautilusDirectory *parent_directory, - NautilusListView *view) -{ - UnloadDelayData *unload_data; - - unload_data = g_slice_new0 (UnloadDelayData); - unload_data->view = view; - unload_data->file = nautilus_file_ref (file); - unload_data->directory = nautilus_directory_ref (parent_directory); - - g_object_add_weak_pointer (G_OBJECT (unload_data->view), - (gpointer *) &unload_data->view); - - return unload_data; -} - -static gboolean -unload_file_timeout (gpointer data) -{ - UnloadDelayData *unload_data = data; - GtkTreeIter iter; - NautilusListModel *model; - GtkTreePath *path; - - if (unload_data->view == NULL) - { - goto out; - } - - model = unload_data->view->details->model; - if (nautilus_list_model_get_tree_iter_from_file (model, - unload_data->file, - unload_data->directory, - &iter)) - { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); - if (!gtk_tree_view_row_expanded (unload_data->view->details->tree_view, - path)) - { - nautilus_list_model_unload_subdirectory (model, &iter); - } - gtk_tree_path_free (path); - } - -out: - unload_delay_data_free (unload_data); - return FALSE; -} - -static void -row_collapsed_callback (GtkTreeView *treeview, - GtkTreeIter *iter, - GtkTreePath *path, - gpointer callback_data) -{ - NautilusListView *view; - NautilusFile *file; - NautilusDirectory *directory; - GtkTreeIter parent; - UnloadDelayData *unload_data; - GtkTreeModel *model; - char *uri; - - view = NAUTILUS_LIST_VIEW (callback_data); - model = GTK_TREE_MODEL (view->details->model); - - gtk_tree_model_get (model, iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - - uri = nautilus_file_get_uri (file); - DEBUG ("Row collapsed callback for uri %s", uri); - g_free (uri); - - directory = NULL; - if (gtk_tree_model_iter_parent (model, &parent, iter)) - { - gtk_tree_model_get (model, &parent, - NAUTILUS_LIST_MODEL_SUBDIRECTORY_COLUMN, &directory, - -1); - } - - unload_data = unload_delay_data_new (file, directory, view); - g_timeout_add_seconds (COLLAPSE_TO_UNLOAD_DELAY, - unload_file_timeout, - unload_data); - - nautilus_file_unref (file); - nautilus_directory_unref (directory); -} - -static void -subdirectory_unloaded_callback (NautilusListModel *model, - NautilusDirectory *directory, - gpointer callback_data) -{ - NautilusListView *view; - - g_return_if_fail (NAUTILUS_IS_LIST_MODEL (model)); - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); - - view = NAUTILUS_LIST_VIEW (callback_data); - - g_signal_handlers_disconnect_by_func (directory, - G_CALLBACK (subdirectory_done_loading_callback), - view); - nautilus_files_view_remove_subdirectory (NAUTILUS_FILES_VIEW (view), directory); -} - -static gboolean -test_expand_row_callback (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path, - gboolean user_data) -{ - return !g_settings_get_boolean (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE); -} - -static void -nautilus_list_view_reveal_selection (NautilusFilesView *view) -{ - g_autolist (NautilusFile) selection = NULL; - - g_return_if_fail (NAUTILUS_IS_LIST_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) - { - NautilusListView *list_view; - NautilusFile *file; - GtkTreeIter iter; - GtkTreePath *path; - - list_view = NAUTILUS_LIST_VIEW (view); - file = selection->data; - if (nautilus_list_model_get_first_iter_for_file (list_view->details->model, file, &iter)) - { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter); - - gtk_tree_view_scroll_to_cell (list_view->details->tree_view, path, NULL, FALSE, 0.0, 0.0); - - gtk_tree_path_free (path); - } - } -} - -static gboolean -sort_criterion_changes_due_to_user (GtkTreeView *tree_view) -{ - GList *columns, *p; - GtkTreeViewColumn *column; - GSignalInvocationHint *ihint; - gboolean ret; - - ret = FALSE; - - columns = gtk_tree_view_get_columns (tree_view); - for (p = columns; p != NULL; p = p->next) - { - column = p->data; - ihint = g_signal_get_invocation_hint (column); - if (ihint != NULL) - { - ret = TRUE; - break; - } - } - g_list_free (columns); - - return ret; -} - -static void -sort_column_changed_callback (GtkTreeSortable *sortable, - NautilusListView *view) -{ - NautilusFile *file; - gint sort_column_id, default_sort_column_id; - GtkSortType reversed; - GQuark sort_attr, default_sort_attr; - char *reversed_attr, *default_reversed_attr; - gboolean default_sort_reversed; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view)); - - gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &reversed); - sort_attr = nautilus_list_model_get_attribute_from_sort_column_id (view->details->model, sort_column_id); - - default_sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (view->details->model, - g_quark_from_string (get_default_sort_order (file, &default_sort_reversed))); - default_sort_attr = nautilus_list_model_get_attribute_from_sort_column_id (view->details->model, default_sort_column_id); - nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN, - g_quark_to_string (default_sort_attr), g_quark_to_string (sort_attr)); - - default_reversed_attr = (default_sort_reversed ? "true" : "false"); - - if (view->details->last_sort_attr != sort_attr && - sort_criterion_changes_due_to_user (view->details->tree_view)) - { - /* at this point, the sort order is always GTK_SORT_ASCENDING, if the sort column ID - * switched. Invert the sort order, if it's the default criterion with a reversed preference, - * or if it makes sense for the attribute (i.e. date). */ - if (sort_attr == default_sort_attr) - { - /* use value from preferences */ - reversed = g_settings_get_boolean (nautilus_preferences, - NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER); - } - else - { - reversed = nautilus_file_is_date_sort_attribute_q (sort_attr); - } - - if (reversed) - { - g_signal_handlers_block_by_func (sortable, sort_column_changed_callback, view); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->details->model), - sort_column_id, - GTK_SORT_DESCENDING); - g_signal_handlers_unblock_by_func (sortable, sort_column_changed_callback, view); - } - } - - - reversed_attr = (reversed ? "true" : "false"); - nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED, - default_reversed_attr, reversed_attr); - - /* Make sure selected item(s) is visible after sort */ - nautilus_list_view_reveal_selection (NAUTILUS_FILES_VIEW (view)); - - view->details->last_sort_attr = sort_attr; -} - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -static char * -get_root_uri_callback (NautilusTreeViewDragDest *dest, - gpointer user_data) -{ - NautilusListView *view; - - view = NAUTILUS_LIST_VIEW (user_data); - - return nautilus_files_view_get_uri (NAUTILUS_FILES_VIEW (view)); -} - -static NautilusFile * -get_file_for_path_callback (NautilusTreeViewDragDest *dest, - GtkTreePath *path, - gpointer user_data) -{ - NautilusListView *view; - - view = NAUTILUS_LIST_VIEW (user_data); - - return nautilus_list_model_file_for_path (view->details->model, path); -} - - -static void -list_view_handle_uri_list (NautilusTreeViewDragDest *dest, - const char *item_uris, - const char *target_uri, - GdkDragAction action, - NautilusListView *view) -{ - nautilus_files_view_handle_uri_list_drop (NAUTILUS_FILES_VIEW (view), - item_uris, target_uri, action); -} - -static void -list_view_handle_text (NautilusTreeViewDragDest *dest, - const char *text, - const char *target_uri, - GdkDragAction action, - NautilusListView *view) -{ - nautilus_files_view_handle_text_drop (NAUTILUS_FILES_VIEW (view), - text, target_uri, action); -} - -static void -list_view_handle_raw (NautilusTreeViewDragDest *dest, - const char *raw_data, - int length, - const char *target_uri, - const char *direct_save_uri, - GdkDragAction action, - NautilusListView *view) -{ - nautilus_files_view_handle_raw_drop (NAUTILUS_FILES_VIEW (view), - raw_data, length, target_uri, direct_save_uri, - action); -} - -static void -list_view_handle_hover (NautilusTreeViewDragDest *dest, - const char *target_uri, - NautilusListView *view) -{ - nautilus_files_view_handle_hover (NAUTILUS_FILES_VIEW (view), target_uri); -} - -static void -move_copy_items_callback (NautilusTreeViewDragDest *dest, - const GList *item_uris, - const char *target_uri, - guint action, - gpointer user_data) -{ - NautilusFilesView *view = user_data; - - nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view), - item_uris); - nautilus_files_view_move_copy_items (view, - item_uris, - target_uri, - action); -} -#endif - -static void -column_header_menu_toggled (GtkCheckButton *menu_item, - NautilusListView *list_view) -{ - NautilusFile *file; - char **visible_columns; - char **column_order; - const char *column; - gboolean active; - GPtrArray *ptr_array; - int i; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - visible_columns = get_visible_columns (list_view); - column_order = get_column_order (list_view); - column = g_object_get_data (G_OBJECT (menu_item), "column-name"); - active = gtk_check_button_get_active (menu_item); - - /* Rebuild visible_columns to add or remove the toggled column. */ - ptr_array = g_ptr_array_sized_new (g_strv_length (visible_columns)); - for (i = 0; visible_columns[i] != NULL; ++i) - { - if (!active && g_strcmp0 (visible_columns[i], column)) - { - continue; - } - g_ptr_array_add (ptr_array, visible_columns[i]); - } - - if (active) - { - g_ptr_array_add (ptr_array, g_strdup (column)); - } - g_ptr_array_add (ptr_array, NULL); - - g_free (visible_columns); - visible_columns = (gchar **) g_ptr_array_free (ptr_array, FALSE); - - nautilus_file_set_metadata_list (file, - NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, - visible_columns); - - /* set view values ourselves, as new metadata could not have been - * updated yet. - */ - apply_columns_settings (list_view, column_order, visible_columns); - - g_strfreev (column_order); - g_strfreev (visible_columns); -} - -static void -column_header_menu_use_default (GtkButton *menu_item, - NautilusListView *list_view) -{ - NautilusFile *file; - char **default_columns; - char **default_order; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - - nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NULL); - nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL); - - default_columns = get_default_visible_columns (list_view); - default_order = get_default_column_order (list_view); - - /* set view values ourselves, as new metadata could not have been - * updated yet. - */ - apply_columns_settings (list_view, default_order, default_columns); - /* Popdown the popover because the checkboxes are not updated. */ - gtk_popover_popdown (GTK_POPOVER (list_view->details->columns_popover)); - - g_strfreev (default_columns); - g_strfreev (default_order); -} - -static void -popup_column_header_menu (NautilusListView *list_view, - gdouble x, - gdouble y) -{ - NautilusFile *file; - char **visible_columns; - char **column_order; - GList *all_columns; - GHashTable *visible_columns_hash; - int i; - GList *l; - GtkPopover *popover; - GtkWidget *menu; - GtkWidget *menu_item; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - - visible_columns = get_visible_columns (list_view); - column_order = get_column_order (list_view); - - all_columns = nautilus_get_columns_for_file (file); - all_columns = nautilus_sort_columns (all_columns, column_order); - - /* hash table to lookup if a given column should be visible */ - visible_columns_hash = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) g_free); - /* always show name column */ - g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name")); - if (visible_columns != NULL) - { - for (i = 0; visible_columns[i] != NULL; ++i) - { - g_hash_table_insert (visible_columns_hash, - g_ascii_strdown (visible_columns[i], -1), - g_ascii_strdown (visible_columns[i], -1)); - } - } - - popover = GTK_POPOVER (list_view->details->columns_popover); - menu = list_view->details->columns_popover_box; - /* Remove all old items before repopulating. */ - while ((menu_item = gtk_widget_get_first_child (menu)) != NULL) - { - gtk_box_remove (GTK_BOX (menu), menu_item); - } - - for (l = all_columns; l != NULL; l = l->next) - { - char *name; - char *label; - char *lowercase; - - g_object_get (G_OBJECT (l->data), - "name", &name, - "label", &label, - NULL); - lowercase = g_ascii_strdown (name, -1); - - menu_item = gtk_check_button_new_with_label (label); - gtk_box_append (GTK_BOX (menu), menu_item); - - g_object_set_data_full (G_OBJECT (menu_item), - "column-name", name, g_free); - - /* name is always visible */ - if (strcmp (lowercase, "name") == 0) - { - gtk_widget_set_sensitive (menu_item, FALSE); - } - - if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL) - { - gtk_check_button_set_active (GTK_CHECK_BUTTON (menu_item), TRUE); - } - - g_signal_connect (menu_item, - "toggled", - G_CALLBACK (column_header_menu_toggled), - list_view); - - g_free (lowercase); - g_free (label); - } - - menu_item = gtk_button_new_with_label (_("Use Default")); - gtk_box_append (GTK_BOX (menu), menu_item); - - g_signal_connect (menu_item, - "clicked", - G_CALLBACK (column_header_menu_use_default), - list_view); - - gtk_widget_show (menu); - gtk_popover_set_pointing_to (popover, &(GdkRectangle){x, y, 0, 0}); - gtk_popover_popup (popover); - - g_hash_table_destroy (visible_columns_hash); - nautilus_column_list_free (all_columns); - g_strfreev (column_order); - g_strfreev (visible_columns); -} - -static void -apply_columns_settings (NautilusListView *list_view, - char **column_order, - char **visible_columns) -{ - GList *all_columns; - NautilusFile *file; - GList *old_view_columns, *view_columns; - GHashTable *visible_columns_hash; - GtkTreeViewColumn *prev_view_column; - GList *l; - int i; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - - /* prepare ordered list of view columns using column_order and visible_columns */ - view_columns = NULL; - - all_columns = nautilus_get_columns_for_file (file); - all_columns = nautilus_sort_columns (all_columns, column_order); - - /* hash table to lookup if a given column should be visible */ - visible_columns_hash = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) g_free); - /* always show name column */ - g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name")); - if (visible_columns != NULL) - { - for (i = 0; visible_columns[i] != NULL; ++i) - { - g_hash_table_insert (visible_columns_hash, - g_ascii_strdown (visible_columns[i], -1), - g_ascii_strdown (visible_columns[i], -1)); - } - } - - for (l = all_columns; l != NULL; l = l->next) - { - char *name; - char *lowercase; - - g_object_get (G_OBJECT (l->data), "name", &name, NULL); - lowercase = g_ascii_strdown (name, -1); - - if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL) - { - GtkTreeViewColumn *view_column; - - view_column = g_hash_table_lookup (list_view->details->columns, name); - if (view_column != NULL) - { - view_columns = g_list_prepend (view_columns, view_column); - } - } - - g_free (name); - g_free (lowercase); - } - - g_hash_table_destroy (visible_columns_hash); - nautilus_column_list_free (all_columns); - - view_columns = g_list_reverse (view_columns); - - /* hide columns that are not present in the configuration */ - old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view); - for (l = old_view_columns; l != NULL; l = l->next) - { - if (g_list_find (view_columns, l->data) == NULL) - { - gtk_tree_view_column_set_visible (l->data, FALSE); - } - } - g_list_free (old_view_columns); - - /* show new columns from the configuration */ - for (l = view_columns; l != NULL; l = l->next) - { - gtk_tree_view_column_set_visible (l->data, TRUE); - } - - /* place columns in the correct order */ - prev_view_column = NULL; - for (l = view_columns; l != NULL; l = l->next) - { - gtk_tree_view_move_column_after (list_view->details->tree_view, l->data, prev_view_column); - prev_view_column = l->data; - } - g_list_free (view_columns); -} - -static void -starred_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - NautilusListView *view) -{ - g_autofree gchar *text = NULL; - g_autofree gchar *uri = NULL; - NautilusFile *file; - - gtk_tree_model_get (model, iter, - view->details->file_name_column_num, &text, - -1); - - gtk_tree_model_get (GTK_TREE_MODEL (model), - iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - - if (file == NULL) - { - /* This row is a label, not a file */ - g_object_set (renderer, - "icon-name", NULL, - "mode", GTK_CELL_RENDERER_MODE_INERT, - NULL); - return; - } - - uri = nautilus_file_get_uri (file); - - if (nautilus_tag_manager_file_is_starred (nautilus_tag_manager_get (), uri)) - { - g_object_set (renderer, - "icon-name", "starred-symbolic", - NULL); - } - else - { - g_object_set (renderer, - "icon-name", "non-starred-symbolic", - NULL); - } - - nautilus_file_unref (file); -} - -static void -filename_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - NautilusListView *view) -{ - char *text; - g_autofree gchar *escaped_text = NULL; - g_autofree gchar *escaped_name = NULL; - g_autofree gchar *replaced_text = NULL; - GtkTreePath *path; - PangoUnderline underline; - GString *display_text; - NautilusDirectory *directory; - NautilusQuery *query = NULL; - NautilusFile *file; - const gchar *snippet; - - gtk_tree_model_get (model, iter, - view->details->file_name_column_num, &text, - -1); - - escaped_name = g_markup_escape_text (text, -1); - display_text = g_string_new (escaped_name); - - directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view)); - - if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) - { - query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory)); - } - - if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE) - { - path = gtk_tree_model_get_path (model, iter); - - if (view->details->hover_path == NULL || - gtk_tree_path_compare (path, view->details->hover_path)) - { - underline = PANGO_UNDERLINE_NONE; - } - else - { - underline = PANGO_UNDERLINE_SINGLE; - } - - gtk_tree_path_free (path); - } - else - { - underline = PANGO_UNDERLINE_NONE; - } - - if (query && - nautilus_query_get_search_content (query) == NAUTILUS_QUERY_SEARCH_CONTENT_FULL_TEXT) - { - gtk_tree_model_get (model, iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - - /* Rule out dummy row */ - if (file != NULL) - { - snippet = nautilus_file_get_search_fts_snippet (file); - if (snippet) - { - replaced_text = g_regex_replace (view->details->regex, - snippet, - -1, - 0, - " ", - G_REGEX_MATCH_NEWLINE_ANY, - NULL); - - escaped_text = g_markup_escape_text (replaced_text, -1); - - g_string_append_printf (display_text, - " %s", - escaped_text); - } - } - nautilus_file_unref (file); - } - - g_object_set (G_OBJECT (renderer), - "markup", display_text->str, - "underline", underline, - NULL); - - g_free (text); - g_string_free (display_text, TRUE); -} - -static void -location_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - NautilusListView *view, - gboolean show_trash_orig) -{ - NautilusDirectory *directory; - GFile *home_location; - NautilusFile *file; - GFile *dir_location; - GFile *base_location; - gchar *where = NULL; - - directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view)); - - home_location = g_file_new_for_path (g_get_home_dir ()); - - gtk_tree_model_get (model, iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - - /* The file might be NULL if we just toggled an expander - * and we're still loading the subdirectory. - */ - if (file == NULL) - { - return; - } - - if (show_trash_orig && nautilus_file_is_in_trash (file)) - { - NautilusFile *orig_file; - - orig_file = nautilus_file_get_trash_original_file (file); - - if (orig_file != NULL) - { - nautilus_file_unref (file); - file = orig_file; - } - } - - if (!nautilus_file_is_in_recent (file)) - { - dir_location = nautilus_file_get_parent_location (file); - } - else - { - GFile *activation_location; - - activation_location = nautilus_file_get_activation_location (file); - dir_location = g_file_get_parent (activation_location); - - g_object_unref (activation_location); - } - - if (!NAUTILUS_IS_SEARCH_DIRECTORY (directory)) - { - base_location = g_object_ref (home_location); - } - else - { - NautilusQuery *query; - NautilusFile *base; - GFile *location; - - query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory)); - location = nautilus_query_get_location (query); - base = nautilus_file_get (location); - - if (!nautilus_file_is_in_recent (base)) - { - base_location = nautilus_file_get_location (base); - } - else - { - base_location = g_object_ref (home_location); - } - - nautilus_file_unref (base); - g_object_unref (location); - g_object_unref (query); - } - - if (g_file_equal (base_location, dir_location)) - { - /* Only occurs when search result is - * a direct child of the base location - */ - where = g_strdup (""); - } - else if (g_file_equal (home_location, dir_location)) - { - where = g_strdup (_("Home")); - } - else if (g_file_has_prefix (dir_location, base_location)) - { - gchar *relative_path; - - relative_path = g_file_get_relative_path (base_location, dir_location); - where = g_filename_display_name (relative_path); - - g_free (relative_path); - } - else - { - where = g_file_get_path (dir_location); - } - - g_object_set (G_OBJECT (renderer), - "text", where, - NULL); - - g_free (where); - - g_object_unref (base_location); - g_object_unref (dir_location); - nautilus_file_unref (file); - g_object_unref (home_location); -} - - -static void -where_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - NautilusListView *view) -{ - location_cell_data_func (column, renderer, model, iter, view, FALSE); -} - -static void -trash_orig_path_cell_data_func (GtkTreeViewColumn *column, - GtkCellRenderer *renderer, - GtkTreeModel *model, - GtkTreeIter *iter, - NautilusListView *view) -{ - location_cell_data_func (column, renderer, model, iter, view, TRUE); -} - -#define SMALL_ZOOM_ICON_PADDING 0 -#define STANDARD_ZOOM_ICON_PADDING 6 -#define LARGE_ZOOM_ICON_PADDING 6 -#define LARGER_ZOOM_ICON_PADDING 6 - -static gint -nautilus_list_view_get_icon_padding_for_zoom_level (NautilusListZoomLevel zoom_level) -{ - switch (zoom_level) - { - case NAUTILUS_LIST_ZOOM_LEVEL_SMALL: - { - return SMALL_ZOOM_ICON_PADDING; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_STANDARD: - { - return STANDARD_ZOOM_ICON_PADDING; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_LARGE: - { - return LARGE_ZOOM_ICON_PADDING; - } - - case NAUTILUS_LIST_ZOOM_LEVEL_LARGER: - { - return LARGER_ZOOM_ICON_PADDING; - } - - default: - { - g_assert_not_reached (); - } - } -} - -static void -set_up_pixbuf_size (NautilusListView *view) -{ - int icon_size, icon_padding; - - /* Make all rows the same size. */ - icon_size = nautilus_list_model_get_icon_size_for_zoom_level (view->details->zoom_level); - icon_padding = nautilus_list_view_get_icon_padding_for_zoom_level (view->details->zoom_level); - gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (view->details->pixbuf_cell), - -1, icon_size + 2 * icon_padding); - - /* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=641518 */ - gtk_tree_view_columns_autosize (view->details->tree_view); -} - -static gint -get_icon_scale_callback (NautilusListModel *model, - NautilusListView *view) -{ - /* FIXME: Temporary regression: HiDPI icons not supported, ignore scale. */ - return 1; -} - -static void -on_longpress_gesture_pressed_event (GtkGestureLongPress *gesture, - gdouble x, - gdouble y, - gpointer user_data) -{ - NautilusListView *view = user_data; - g_autolist (NautilusFile) selection = NULL; - - selection = nautilus_view_get_selection (NAUTILUS_VIEW (view)); - if (selection != NULL) - { - nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (view), x, y); - } - else - { - nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (view), x, y); - } -} - -static void -on_tree_view_drag_gesture_drag_begin (GtkGestureDrag *gesture, - gdouble start_x, - gdouble start_y, - gpointer user_data) -{ -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - nautilus_list_view_dnd_init (NAUTILUS_LIST_VIEW (user_data)); -#endif -} - -static void -on_tree_view_drag_gesture_drag_update (GtkGestureDrag *gesture, - gdouble offset_x, - gdouble offset_y, - gpointer user_data) -{ -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - GdkEventSequence *sequence; - GdkEvent *event; - NautilusListView *list_view; - - sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); - event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); - list_view = NAUTILUS_LIST_VIEW (user_data); - - nautilus_list_view_dnd_drag_begin (list_view, offset_x, offset_y, event); -#endif -} - -static void -list_view_use_tree_changed_callback (gpointer callback_data) -{ - GtkTreeView *tree_view; - - tree_view = GTK_TREE_VIEW (callback_data); - - gtk_tree_view_collapse_all (tree_view); -} - - -static void -create_and_set_up_tree_view (NautilusListView *view) -{ - GtkCellRenderer *cell; - GtkTreeViewColumn *column; -#if 0 && NAUTILUS_A11Y_NEEDS_GTK4_REIMPLEMENTATION - AtkObject *atk_obj; -#endif - GList *nautilus_columns; - GList *l; - gchar **default_column_order, **default_visible_columns; - GtkWidget *content_widget; - GtkEventController *controller; - - content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (view)); - view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); - view->details->columns = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify) g_free, - NULL); - gtk_tree_view_set_enable_search (view->details->tree_view, FALSE); - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - view->details->drag_dest = - nautilus_tree_view_drag_dest_new (view->details->tree_view); -#endif - - /* Stop the tree view from performing select-all actions. - * It is desireable that the action is disabled while directory - * is loading. - */ - g_signal_connect (view->details->tree_view, "select-all", - G_CALLBACK (g_signal_stop_emission_by_name), "select-all"); - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - g_signal_connect_object (view->details->drag_dest, - "get-root-uri", - G_CALLBACK (get_root_uri_callback), - view, 0); - g_signal_connect_object (view->details->drag_dest, - "get-file-for-path", - G_CALLBACK (get_file_for_path_callback), - view, 0); - g_signal_connect_object (view->details->drag_dest, - "move-copy-items", - G_CALLBACK (move_copy_items_callback), - view, 0); - g_signal_connect_object (view->details->drag_dest, "handle-uri-list", - G_CALLBACK (list_view_handle_uri_list), view, 0); - g_signal_connect_object (view->details->drag_dest, "handle-text", - G_CALLBACK (list_view_handle_text), view, 0); - g_signal_connect_object (view->details->drag_dest, "handle-raw", - G_CALLBACK (list_view_handle_raw), view, 0); - g_signal_connect_object (view->details->drag_dest, "handle-hover", - G_CALLBACK (list_view_handle_hover), view, 0); -#endif - - g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view), - "changed", - G_CALLBACK (list_selection_changed_callback), view, 0); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ()); - gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0); - - g_signal_connect (controller, "drag-begin", - G_CALLBACK (on_tree_view_drag_gesture_drag_begin), view); - g_signal_connect (controller, "drag-update", - G_CALLBACK (on_tree_view_drag_gesture_drag_update), view); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); - gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller); - - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0); - - g_signal_connect (controller, "pressed", - G_CALLBACK (on_tree_view_click_gesture_pressed), view); - g_signal_connect (controller, "released", - G_CALLBACK (on_tree_view_click_gesture_released), view); - - controller = gtk_event_controller_motion_new (); - gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - - g_signal_connect (controller, "enter", - G_CALLBACK (on_event_controller_motion_enter), view); - g_signal_connect (controller, "leave", - G_CALLBACK (on_event_controller_motion_leave), view); - g_signal_connect (controller, "motion", - G_CALLBACK (on_event_controller_motion_motion), view); - - controller = gtk_event_controller_key_new (); - gtk_widget_add_controller (GTK_WIDGET (view->details->tree_view), controller); - - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); - - g_signal_connect (controller, "key-pressed", - G_CALLBACK (on_event_controller_key_key_pressed), view); - - g_signal_connect_object (view->details->tree_view, "test-expand-row", - G_CALLBACK (test_expand_row_callback), view, 0); - g_signal_connect_object (view->details->tree_view, "row-expanded", - G_CALLBACK (row_expanded_callback), view, 0); - g_signal_connect_object (view->details->tree_view, "row-collapsed", - G_CALLBACK (row_collapsed_callback), view, 0); - g_signal_connect_object (view->details->tree_view, "row-activated", - G_CALLBACK (row_activated_callback), view, 0); - - g_signal_connect_object (nautilus_list_view_preferences, - "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE, - G_CALLBACK (list_view_use_tree_changed_callback), - view->details->tree_view, - G_CONNECT_SWAPPED); - - view->details->model = g_object_new (NAUTILUS_TYPE_LIST_MODEL, NULL); - gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model)); - /* Need the model for the dnd drop icon "accept" change */ - nautilus_list_model_set_drag_view (NAUTILUS_LIST_MODEL (view->details->model), - view->details->tree_view, 0, 0); - - g_signal_connect_object (view->details->model, "sort-column-changed", - G_CALLBACK (sort_column_changed_callback), view, 0); - - g_signal_connect_object (view->details->model, "subdirectory-unloaded", - G_CALLBACK (subdirectory_unloaded_callback), view, 0); - - g_signal_connect_object (view->details->model, "get-icon-scale", - G_CALLBACK (get_icon_scale_callback), view, 0); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ()); - gtk_widget_add_controller (GTK_WIDGET (content_widget), controller); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE); - g_signal_connect (controller, "pressed", - (GCallback) on_longpress_gesture_pressed_event, view); - - gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view->details->tree_view), GTK_SELECTION_MULTIPLE); - - g_settings_bind (nautilus_list_view_preferences, NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE, - view->details->tree_view, "show-expanders", - G_SETTINGS_BIND_DEFAULT); - - nautilus_columns = nautilus_get_all_columns (); - - for (l = nautilus_columns; l != NULL; l = l->next) - { - NautilusColumn *nautilus_column; - int column_num; - char *name; - char *label; - float xalign; - GtkSortType sort_order; - - nautilus_column = NAUTILUS_COLUMN (l->data); - - g_object_get (nautilus_column, - "name", &name, - "label", &label, - "xalign", &xalign, - "default-sort-order", &sort_order, - NULL); - - column_num = nautilus_list_model_add_column (view->details->model, - nautilus_column); - - /* Created the name column specially, because it - * has the icon in it.*/ - if (!strcmp (name, "name")) - { - /* Create the file name column */ - view->details->file_name_column = gtk_tree_view_column_new (); - gtk_tree_view_append_column (view->details->tree_view, - view->details->file_name_column); - view->details->file_name_column_num = column_num; - - g_hash_table_insert (view->details->columns, - g_strdup ("name"), - view->details->file_name_column); - - gtk_tree_view_set_search_column (view->details->tree_view, column_num); - - gtk_tree_view_column_set_sort_column_id (view->details->file_name_column, column_num); - gtk_tree_view_column_set_title (view->details->file_name_column, _("Name")); - gtk_tree_view_column_set_resizable (view->details->file_name_column, TRUE); - gtk_tree_view_column_set_expand (view->details->file_name_column, TRUE); - - /* Initial padding */ - cell = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE); - g_object_set (cell, "xpad", 6, NULL); - g_settings_bind (nautilus_list_view_preferences, NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE, - cell, "visible", - G_SETTINGS_BIND_INVERT_BOOLEAN | G_SETTINGS_BIND_GET); - - /* File icon */ - cell = gtk_cell_renderer_pixbuf_new (); - view->details->pixbuf_cell = (GtkCellRendererPixbuf *) cell; - set_up_pixbuf_size (view); - - gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE); - gtk_tree_view_column_set_attributes (view->details->file_name_column, - cell, - "texture", nautilus_list_model_get_column_id_from_zoom_level (view->details->zoom_level), - NULL); - - cell = gtk_cell_renderer_text_new (); - view->details->file_name_cell = (GtkCellRendererText *) cell; - g_object_set (cell, - "ellipsize", PANGO_ELLIPSIZE_END, - "single-paragraph-mode", FALSE, - "width-chars", 30, - "xpad", 5, - NULL); - - gtk_tree_view_column_pack_start (view->details->file_name_column, cell, TRUE); - gtk_tree_view_column_set_cell_data_func (view->details->file_name_column, cell, - (GtkTreeCellDataFunc) filename_cell_data_func, - view, NULL); - } - else - { - if (g_strcmp0 (name, "starred") == 0) - { - cell = gtk_cell_renderer_pixbuf_new (); - g_object_set (cell, - "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, - NULL); - - column = gtk_tree_view_column_new_with_attributes ("", - cell, - NULL); - gtk_tree_view_column_set_fixed_width (column, STAR_COLUMN_WIDTH); - } - else - { - PangoAttrList *attr_list = pango_attr_list_new (); - - cell = gtk_cell_renderer_text_new (); - - column = gtk_tree_view_column_new_with_attributes (label, - cell, - "text", column_num, - NULL); - - pango_attr_list_insert (attr_list, pango_attr_foreground_alpha_new (ALPHA_55_PERCENT)); - g_object_set (cell, "attributes", attr_list, NULL); - pango_attr_list_unref (attr_list); - } - - gtk_tree_view_column_set_alignment (column, xalign); - g_object_set (cell, - "xalign", xalign, - "xpad", 5, - NULL); - if (!strcmp (name, "permissions")) - { - g_object_set (cell, - "family", "Monospace", - NULL); - } - view->details->cells = g_list_append (view->details->cells, - cell); - - gtk_tree_view_append_column (view->details->tree_view, column); - gtk_tree_view_column_set_sort_column_id (column, column_num); - g_hash_table_insert (view->details->columns, - g_strdup (name), - column); - - gtk_tree_view_column_set_resizable (column, TRUE); - gtk_tree_view_column_set_sort_order (column, sort_order); - - if (!strcmp (name, "where")) - { - gtk_tree_view_column_set_cell_data_func (column, cell, - (GtkTreeCellDataFunc) where_cell_data_func, - view, NULL); - } - else if (!strcmp (name, "trash_orig_path")) - { - gtk_tree_view_column_set_cell_data_func (column, cell, - (GtkTreeCellDataFunc) trash_orig_path_cell_data_func, - view, NULL); - } - else if (!strcmp (name, "starred")) - { - gtk_tree_view_column_set_cell_data_func (column, cell, - (GtkTreeCellDataFunc) starred_cell_data_func, - view, NULL); - } - } - g_free (name); - g_free (label); - } - nautilus_column_list_free (nautilus_columns); - - default_visible_columns = g_settings_get_strv (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS); - default_column_order = g_settings_get_strv (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER); - - /* Apply the default column order and visible columns, to get it - * right most of the time. The metadata will be checked when a - * folder is loaded */ - apply_columns_settings (view, - default_column_order, - default_visible_columns); - - gtk_widget_show (GTK_WIDGET (view->details->tree_view)); - gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget), - GTK_WIDGET (view->details->tree_view)); - -#if 0 && NAUTILUS_A11Y_NEEDS_GTK4_REIMPLEMENTATION - atk_obj = gtk_widget_get_accessible (GTK_WIDGET (view->details->tree_view)); - atk_object_set_name (atk_obj, _("List View")); -#endif - - g_strfreev (default_visible_columns); - g_strfreev (default_column_order); -} - -static void -nautilus_list_view_add_files (NautilusFilesView *view, - GList *files) -{ - NautilusListModel *model; - GList *l; - - model = NAUTILUS_LIST_VIEW (view)->details->model; - for (l = files; l != NULL; l = l->next) - { - NautilusFile *parent; - NautilusDirectory *directory; - - parent = nautilus_file_get_parent (NAUTILUS_FILE (l->data)); - directory = nautilus_directory_get_for_file (parent); - nautilus_list_model_add_file (model, NAUTILUS_FILE (l->data), directory); - - nautilus_file_unref (parent); - nautilus_directory_unref (directory); - } -} - -static char ** -get_default_visible_columns (NautilusListView *list_view) -{ - NautilusFile *file; - NautilusDirectory *directory; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - - if (nautilus_file_is_in_trash (file)) - { - return g_strdupv ((gchar **) default_trash_visible_columns); - } - - if (nautilus_file_is_in_recent (file)) - { - return g_strdupv ((gchar **) default_recent_visible_columns); - } - - directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (list_view)); - if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) - { - return g_strdupv ((gchar **) default_search_visible_columns); - } - - return g_settings_get_strv (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS); -} - -static char ** -get_visible_columns (NautilusListView *list_view) -{ - NautilusFile *file; - g_autofree gchar **visible_columns = NULL; - g_autoptr (GFile) location = NULL; - GPtrArray *res; - g_autofree gchar *uri = NULL; - gboolean can_star_current_directory; - gboolean is_starred; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - uri = nautilus_file_get_uri (file); - - location = g_file_new_for_uri (uri); - can_star_current_directory = nautilus_tag_manager_can_star_contents (nautilus_tag_manager_get (), - location); - is_starred = eel_uri_is_starred (uri); - - visible_columns = nautilus_file_get_metadata_list (file, - NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS); - if (visible_columns == NULL) - { - visible_columns = get_default_visible_columns (list_view); - } - - res = g_ptr_array_new (); - for (gint i = 0; visible_columns[i] != NULL; i++) - { - if (g_strcmp0 (visible_columns[i], "starred") != 0 || - (g_strcmp0 (visible_columns[i], "starred") == 0 && (can_star_current_directory || is_starred))) - { - g_ptr_array_add (res, visible_columns[i]); - } - } - - g_ptr_array_add (res, NULL); - - return (char **) g_ptr_array_free (res, FALSE); -} - -static char ** -get_default_column_order (NautilusListView *list_view) -{ - NautilusFile *file; - NautilusDirectory *directory; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - - if (nautilus_file_is_in_trash (file)) - { - return g_strdupv ((gchar **) default_trash_columns_order); - } - - if (nautilus_file_is_in_recent (file)) - { - return g_strdupv ((gchar **) default_recent_columns_order); - } - - directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (list_view)); - if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) - { - return g_strdupv ((gchar **) default_search_columns_order); - } - - return g_settings_get_strv (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER); -} - -static char ** -get_column_order (NautilusListView *list_view) -{ - NautilusFile *file; - gchar **column_order; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - - column_order = nautilus_file_get_metadata_list - (file, - NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER); - - if (column_order != NULL) - { - return column_order; - } - - return get_default_column_order (list_view); -} - -static void -check_allow_sort (NautilusListView *list_view) -{ - GList *column_names; - GList *l; - NautilusFile *file; - GtkTreeViewColumn *column; - gboolean allow_sorting; - int sort_column_id; - - column_names = g_hash_table_get_keys (list_view->details->columns); - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - allow_sorting = !(nautilus_file_is_in_recent (file) || nautilus_file_is_in_search (file)); - - for (l = column_names; l != NULL; l = l->next) - { - column = g_hash_table_lookup (list_view->details->columns, l->data); - if (allow_sorting) - { - sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model, - g_quark_from_string (l->data)); - /* Restore its original sorting id. We rely on that the keys of the hashmap - * use the same string than the sort criterias */ - gtk_tree_view_column_set_sort_column_id (column, sort_column_id); - } - else - { - /* This disables the header and any sorting capability (like shortcuts), - * but leaving them interactionable so the user can still resize them */ - gtk_tree_view_column_set_sort_column_id (column, -1); - } - } - - g_list_free (column_names); -} - -static void -set_columns_settings_from_metadata_and_preferences (NautilusListView *list_view) -{ - char **column_order; - char **visible_columns; - - column_order = get_column_order (list_view); - visible_columns = get_visible_columns (list_view); + /* We don't use the built-in child activation feature for click because it + * doesn't fill all our needs nor does it match our expected behavior. + * Instead, we roll our own event handling and double/single click mode. + * However, GtkColumnView:single-click-activate has other effects besides + * activation, as it affects the selection behavior as well (e.g. selects on + * hover). Setting it to FALSE gives us the expected behavior. */ + gtk_column_view_set_single_click_activate (GTK_COLUMN_VIEW (widget), FALSE); + gtk_column_view_set_enable_rubberband (GTK_COLUMN_VIEW (widget), TRUE); - apply_columns_settings (list_view, column_order, visible_columns); - - g_strfreev (column_order); - g_strfreev (visible_columns); -} - -static void -set_sort_order_from_metadata_and_preferences (NautilusListView *list_view) -{ - char *sort_attribute; - int sort_column_id; - NautilusFile *file; - gboolean sort_reversed, default_sort_reversed; - const gchar *default_sort_order; - - file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view)); - default_sort_order = get_default_sort_order (file, &default_sort_reversed); - if (!(nautilus_file_is_in_recent (file) || nautilus_file_is_in_search (file))) - { - sort_attribute = nautilus_file_get_metadata (file, - NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN, - NULL); - sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model, - g_quark_from_string (sort_attribute)); - g_free (sort_attribute); - - if (sort_column_id == -1) - { - sort_column_id = - nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model, - g_quark_from_string (default_sort_order)); - } - - sort_reversed = nautilus_file_get_boolean_metadata (file, - NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED, - default_sort_reversed); - } - else - { - /* 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 */ - sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model, - g_quark_from_string (default_sort_order)); - sort_reversed = default_sort_reversed; - } - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_view->details->model), - sort_column_id, - sort_reversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING); -} - -static NautilusListZoomLevel -get_default_zoom_level (void) -{ - NautilusListZoomLevel default_zoom_level; - - default_zoom_level = g_settings_get_enum (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL); - - if (default_zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_SMALL - || default_zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_LARGER) - { - default_zoom_level = NAUTILUS_LIST_ZOOM_LEVEL_STANDARD; - } - - return default_zoom_level; -} - -static void -nautilus_list_view_begin_loading (NautilusFilesView *view) -{ - NautilusListView *list_view; - - list_view = NAUTILUS_LIST_VIEW (view); - - nautilus_list_view_sort_directories_first_changed (NAUTILUS_FILES_VIEW (list_view)); - set_sort_order_from_metadata_and_preferences (list_view); - set_columns_settings_from_metadata_and_preferences (list_view); - check_allow_sort (list_view); -} - -static void -nautilus_list_view_clear (NautilusFilesView *view) -{ - NautilusListView *list_view; - GtkTreeView *tree_view; - GtkTreeSelection *tree_selection; - GtkTreePath *path; - - list_view = NAUTILUS_LIST_VIEW (view); - - if (list_view->details->model != NULL) - { - tree_view = list_view->details->tree_view; - - /* When the current cursor's row gets deleted, GTK will move the cursor to - * the next row, and when setting the cursor it also selects the new - * cursor's row, thereby triggering selection signals. The new cursor will - * soon be deleted again and the loop repeats. - * - * Since clear() removes all entries, those selections are useless but they - * take up most of the time in clear(). For example, when a search returns - * a large list, exiting from the search view would make nautilus hang. - * - * At the time the code is written simply removing the cursor solves the - * problem, but to be future-proof in case GTK does anything fancy with - * the current selection, we also remove the selection. - * - * Because GTK internally seeking the cursor takes time, only blocking the - * selection signal like everywhere else will not remove that overhead. - */ - - /* Clear the current selection */ - tree_selection = gtk_tree_view_get_selection (tree_view); - gtk_tree_selection_unselect_all (tree_selection); - - /* Clear the current cursor */ - path = gtk_tree_path_new (); - gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE); - gtk_tree_path_free (path); - - nautilus_list_model_clear (list_view->details->model); - } -} - -static void -nautilus_list_view_file_changed (NautilusFilesView *view, - NautilusFile *file, - NautilusDirectory *directory) -{ - NautilusListView *listview; - - listview = NAUTILUS_LIST_VIEW (view); - - nautilus_list_model_file_changed (listview->details->model, file, directory); -} - -typedef struct -{ - GtkTreePath *path; - gboolean is_common; - gboolean is_root; -} HasCommonParentData; - -static void -tree_selection_has_common_parent_foreach_func (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - HasCommonParentData *data; - GtkTreePath *parent_path; - gboolean has_parent; - - data = (HasCommonParentData *) user_data; - - parent_path = gtk_tree_path_copy (path); - gtk_tree_path_up (parent_path); - - has_parent = (gtk_tree_path_get_depth (parent_path) > 0) ? TRUE : FALSE; - - if (!has_parent) - { - data->is_root = TRUE; - } - - if (data->is_common && !data->is_root) - { - if (data->path == NULL) - { - data->path = gtk_tree_path_copy (parent_path); - } - else if (gtk_tree_path_compare (data->path, parent_path) != 0) - { - data->is_common = FALSE; - } - } - - gtk_tree_path_free (parent_path); -} - -static void -tree_selection_has_common_parent (GtkTreeSelection *selection, - gboolean *is_common, - gboolean *is_root) -{ - HasCommonParentData data; - - g_assert (is_common != NULL); - g_assert (is_root != NULL); - - data.path = NULL; - data.is_common = *is_common = TRUE; - data.is_root = *is_root = FALSE; - - gtk_tree_selection_selected_foreach (selection, - tree_selection_has_common_parent_foreach_func, - &data); - - *is_common = data.is_common; - *is_root = data.is_root; - - if (data.path != NULL) - { - gtk_tree_path_free (data.path); - } -} - -static char * -nautilus_list_view_get_backing_uri (NautilusFilesView *view) -{ - NautilusListView *list_view; - NautilusListModel *list_model; - NautilusFile *file; - GtkTreeView *tree_view; - GtkTreeSelection *selection; - GtkTreePath *path; - GList *paths; - guint length; - char *uri; - - g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), NULL); - - list_view = NAUTILUS_LIST_VIEW (view); - list_model = list_view->details->model; - tree_view = list_view->details->tree_view; - - g_assert (list_model); - - /* We currently handle three common cases here: - * (a) if the selection contains non-filesystem items (i.e., the - * "(Empty)" label), we return the uri of the parent. - * (b) if the selection consists of exactly one _expanded_ directory, we - * return its URI. - * (c) if the selection consists of either exactly one item which is not - * an expanded directory) or multiple items in the same directory, - * we return the URI of the common parent. + /* While we don't want to use GTK's click activation, we'll let it handle + * the key activation part (with Enter). */ + g_signal_connect (widget, "activate", G_CALLBACK (on_column_view_item_activated), self); - uri = NULL; - - selection = gtk_tree_view_get_selection (tree_view); - length = gtk_tree_selection_count_selected_rows (selection); - - if (length == 1) - { - paths = gtk_tree_selection_get_selected_rows (selection, NULL); - path = (GtkTreePath *) paths->data; - - file = nautilus_list_model_file_for_path (list_model, path); - if (file == NULL) - { - /* The selected item is a label, not a file */ - gtk_tree_path_up (path); - file = nautilus_list_model_file_for_path (list_model, path); - } - - if (file != NULL) - { - if (nautilus_file_is_directory (file) && - gtk_tree_view_row_expanded (tree_view, path)) - { - uri = nautilus_file_get_uri (file); - } - nautilus_file_unref (file); - } - - gtk_tree_path_free (path); - g_list_free (paths); - } - - if (uri == NULL && length > 0) - { - gboolean is_common, is_root; - - /* Check that all the selected items belong to the same - * directory and that directory is not the root directory (which - * is handled by NautilusFilesView::get_backing_directory.) */ - - tree_selection_has_common_parent (selection, &is_common, &is_root); - - if (is_common && !is_root) - { - paths = gtk_tree_selection_get_selected_rows (selection, NULL); - path = (GtkTreePath *) paths->data; - - file = nautilus_list_model_file_for_path (list_model, path); - g_assert (file != NULL); - uri = nautilus_file_get_parent_uri (file); - nautilus_file_unref (file); - - g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free); - } - } - - if (uri != NULL) - { - return uri; - } - - return NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->get_backing_uri (view); -} - -static void -nautilus_list_view_get_selection_foreach_func (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GList **list; - NautilusFile *file; - - list = data; - - gtk_tree_model_get (model, iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - - if (file != NULL) - { - (*list) = g_list_prepend ((*list), file); - } -} - -static GList * -nautilus_list_view_get_selection (NautilusFilesView *view) -{ - GList *list; - - list = NULL; - - gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view), - nautilus_list_view_get_selection_foreach_func, &list); - - return g_list_reverse (list); -} - -static void -nautilus_list_view_get_selection_for_file_transfer_foreach_func (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - NautilusFile *file; - struct SelectionForeachData *selection_data; - GtkTreeIter parent, child; - - selection_data = data; - - gtk_tree_model_get (model, iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - - if (file != NULL) - { - /* If the parent folder is also selected, don't include this file in the - * file operation, since that would copy it to the toplevel target instead - * of keeping it as a child of the copied folder - */ - child = *iter; - while (gtk_tree_model_iter_parent (model, &parent, &child)) - { - if (gtk_tree_selection_iter_is_selected (selection_data->selection, - &parent)) - { - return; - } - child = parent; - } - - nautilus_file_ref (file); - selection_data->list = g_list_prepend (selection_data->list, file); - } -} - - -static GList * -nautilus_list_view_get_selection_for_file_transfer (NautilusFilesView *view) -{ - struct SelectionForeachData selection_data; - - selection_data.list = NULL; - selection_data.selection = gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view); - - gtk_tree_selection_selected_foreach (selection_data.selection, - nautilus_list_view_get_selection_for_file_transfer_foreach_func, &selection_data); - - return g_list_reverse (selection_data.list); -} - -static gboolean -nautilus_list_view_is_empty (NautilusFilesView *view) -{ - return nautilus_list_model_is_empty (NAUTILUS_LIST_VIEW (view)->details->model); -} - -static void -nautilus_list_view_end_file_changes (NautilusFilesView *view) -{ - NautilusListView *list_view; - - list_view = NAUTILUS_LIST_VIEW (view); - - if (list_view->details->new_selection_path) - { - gtk_tree_view_set_cursor (list_view->details->tree_view, - list_view->details->new_selection_path, - NULL, FALSE); - gtk_tree_path_free (list_view->details->new_selection_path); - list_view->details->new_selection_path = NULL; - } -} - -static void -nautilus_list_view_remove_file (NautilusFilesView *view, - NautilusFile *file, - NautilusDirectory *directory) -{ - GtkTreePath *path; - GtkTreePath *file_path; - GtkTreeIter iter; - GtkTreeIter temp_iter; - GtkTreeRowReference *row_reference; - NautilusListView *list_view; - GtkTreeModel *tree_model; - GtkTreeSelection *selection; - - path = NULL; - row_reference = NULL; - list_view = NAUTILUS_LIST_VIEW (view); - tree_model = GTK_TREE_MODEL (list_view->details->model); - - if (nautilus_list_model_get_tree_iter_from_file (list_view->details->model, file, directory, &iter)) - { - selection = gtk_tree_view_get_selection (list_view->details->tree_view); - file_path = gtk_tree_model_get_path (tree_model, &iter); - - if (gtk_tree_selection_path_is_selected (selection, file_path)) - { - /* get reference for next element in the list view. If the element to be deleted is the - * last one, get reference to previous element. If there is only one element in view - * no need to select anything. - */ - temp_iter = iter; - - if (gtk_tree_model_iter_next (tree_model, &iter)) - { - path = gtk_tree_model_get_path (tree_model, &iter); - row_reference = gtk_tree_row_reference_new (tree_model, path); - } - else - { - path = gtk_tree_model_get_path (tree_model, &temp_iter); - if (gtk_tree_path_prev (path)) - { - row_reference = gtk_tree_row_reference_new (tree_model, path); - } - } - gtk_tree_path_free (path); - } - - gtk_tree_path_free (file_path); - - nautilus_list_model_remove_file (list_view->details->model, file, directory); - - if (gtk_tree_row_reference_valid (row_reference)) - { - if (list_view->details->new_selection_path) - { - gtk_tree_path_free (list_view->details->new_selection_path); - } - list_view->details->new_selection_path = gtk_tree_row_reference_get_path (row_reference); - } - - if (row_reference) - { - gtk_tree_row_reference_free (row_reference); - } - } -} - -static void -nautilus_list_view_set_selection (NautilusFilesView *view, - GList *selection) -{ - NautilusListView *list_view; - NautilusListModel *model; - GtkTreeView *tree_view; - GtkTreeSelection *tree_selection; - GList *node; - gboolean cursor_is_set_on_selection = FALSE; - GList *iters, *l; - NautilusFile *file; - - list_view = NAUTILUS_LIST_VIEW (view); - model = list_view->details->model; - tree_view = list_view->details->tree_view; - tree_selection = gtk_tree_view_get_selection (tree_view); - - g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view); - - gtk_tree_selection_unselect_all (tree_selection); - for (node = selection; node != NULL; node = node->next) - { - file = node->data; - iters = nautilus_list_model_get_all_iters_for_file (model, file); - - for (l = iters; l != NULL; l = l->next) - { - if (!cursor_is_set_on_selection) - { - GtkTreePath *path; - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), - (GtkTreeIter *) l->data); - gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE); - gtk_tree_path_free (path); - - cursor_is_set_on_selection = TRUE; - continue; - } - - gtk_tree_selection_select_iter (tree_selection, - (GtkTreeIter *) l->data); - } - g_list_free_full (iters, g_free); - } - - g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view); - nautilus_files_view_notify_selection_changed (view); -} - -static void -nautilus_list_view_invert_selection (NautilusFilesView *view) -{ - NautilusListView *list_view; - GtkTreeSelection *tree_selection; - GList *node; - GList *iters, *l; - NautilusFile *file; - GList *selection = NULL; - - list_view = NAUTILUS_LIST_VIEW (view); - tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view); - - g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view); - - gtk_tree_selection_selected_foreach (tree_selection, - nautilus_list_view_get_selection_foreach_func, &selection); - - gtk_tree_selection_select_all (tree_selection); - - for (node = selection; node != NULL; node = node->next) - { - file = node->data; - iters = nautilus_list_model_get_all_iters_for_file (list_view->details->model, file); - - for (l = iters; l != NULL; l = l->next) - { - gtk_tree_selection_unselect_iter (tree_selection, - (GtkTreeIter *) l->data); - } - g_list_free_full (iters, g_free); - } - - g_list_free (selection); - - g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view); - nautilus_files_view_notify_selection_changed (view); -} - -static void -nautilus_list_view_select_all (NautilusFilesView *view) -{ - gtk_tree_selection_select_all (gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view)); -} - -static void -nautilus_list_view_select_first (NautilusFilesView *view) -{ - GtkTreeSelection *selection; - GtkTreeIter iter; - - if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (NAUTILUS_LIST_VIEW (view)->details->model), &iter)) - { - return; - } - selection = gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view); - gtk_tree_selection_unselect_all (selection); - gtk_tree_selection_select_iter (selection, &iter); -} - -static void -nautilus_list_view_zoom_to_level (NautilusFilesView *view, - gint zoom_level) -{ - NautilusListView *list_view; - - g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view)); - - list_view = NAUTILUS_LIST_VIEW (view); - - if (list_view->details->zoom_level == zoom_level) - { - return; - } - - nautilus_list_view_set_zoom_level (list_view, zoom_level); - g_action_group_change_action_state (nautilus_files_view_get_action_group (view), - "zoom-to-level", g_variant_new_int32 (zoom_level)); - - nautilus_files_view_update_toolbar_menus (view); -} - -static void -action_zoom_to_level (GSimpleAction *action, - GVariant *state, - gpointer user_data) -{ - NautilusFilesView *view; - NautilusListZoomLevel zoom_level; - - g_assert (NAUTILUS_IS_FILES_VIEW (user_data)); - - view = NAUTILUS_FILES_VIEW (user_data); - zoom_level = g_variant_get_int32 (state); - nautilus_list_view_zoom_to_level (view, zoom_level); - - g_simple_action_set_state (G_SIMPLE_ACTION (action), state); - if (g_settings_get_enum (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level) - { - g_settings_set_enum (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL, - zoom_level); - } + return GTK_COLUMN_VIEW (widget); } static void @@ -3241,708 +581,585 @@ action_visible_columns (GSimpleAction *action, GVariant *state, gpointer user_data) { - NautilusListView *list_view; + NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); - list_view = NAUTILUS_LIST_VIEW (user_data); - - if (list_view->details->column_editor) + if (self->column_editor) { - gtk_window_present (GTK_WINDOW (list_view->details->column_editor)); + gtk_widget_show (self->column_editor); } else { - list_view->details->column_editor = create_column_editor (list_view); - g_object_add_weak_pointer (G_OBJECT (list_view->details->column_editor), - (gpointer *) &list_view->details->column_editor); + self->column_editor = create_column_editor (self); + g_object_add_weak_pointer (G_OBJECT (self->column_editor), + (gpointer *) &self->column_editor); - gtk_widget_show (list_view->details->column_editor); + gtk_widget_show (self->column_editor); } } -const GActionEntry list_view_entries[] = -{ - { "visible-columns", action_visible_columns }, - { "zoom-to-level", NULL, NULL, "1", action_zoom_to_level } -}; - static void -nautilus_list_view_set_zoom_level (NautilusListView *view, - NautilusListZoomLevel new_level) +action_sort_order_changed (GSimpleAction *action, + GVariant *value, + gpointer user_data) { - int column; + const gchar *target_name; + gboolean reversed; + NautilusFileSortType sort_type; + NautilusListView *self; + GListModel *view_columns; + g_autoptr (GtkColumnViewColumn) sort_column = NULL; + GtkSorter *sorter; - g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view)); - g_return_if_fail (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL && - new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGER); + /* This array makes the #NautilusFileSortType values correspond to the + * respective column attribute. + */ + const char *attributes[] = + { + "name", + "size", + "type", + "date_modified", + "date_accessed", + "date_created", + "starred", + "trashed_on", + "search_relevance", + "recency", + NULL + }; - if (view->details->zoom_level == new_level) + /* Don't resort if the action is in the same state as before */ + if (g_variant_equal (value, g_action_get_state (G_ACTION (action)))) { return; } - view->details->zoom_level = new_level; - - /* Select correctly scaled icons. */ - column = nautilus_list_model_get_column_id_from_zoom_level (new_level); - gtk_tree_view_column_set_attributes (view->details->file_name_column, - GTK_CELL_RENDERER (view->details->pixbuf_cell), - "texture", column, - NULL); - set_up_pixbuf_size (view); -} - -static void -nautilus_list_view_bump_zoom_level (NautilusFilesView *view, - int zoom_increment) -{ - NautilusListView *list_view; - gint new_level; + self = NAUTILUS_LIST_VIEW (user_data); + g_variant_get (value, "(&sb)", &target_name, &reversed); - g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view)); + if (g_strcmp0 (target_name, "unknown") == 0) + { + /* Sort order has been changed without using this action. */ + g_simple_action_set_state (action, value); + return; + } - list_view = NAUTILUS_LIST_VIEW (view); - new_level = list_view->details->zoom_level + zoom_increment; + sort_type = get_sorts_type_from_metadata_text (target_name); - if (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL && - new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGER) + view_columns = gtk_column_view_get_columns (self->view_ui); + for (guint i = 0; i < g_list_model_get_n_items (view_columns); i++) { - nautilus_list_view_zoom_to_level (view, new_level); - } -} + g_autoptr (GtkColumnViewColumn) view_column = NULL; + GtkListItemFactory *factory; + NautilusColumn *nautilus_column; + gchar *attribute; -static void -nautilus_list_view_restore_standard_zoom_level (NautilusFilesView *view) -{ - nautilus_list_view_zoom_to_level (view, NAUTILUS_LIST_ZOOM_LEVEL_STANDARD); -} + view_column = g_list_model_get_item (view_columns, i); + factory = gtk_column_view_column_get_factory (view_column); + nautilus_column = g_hash_table_lookup (self->factory_to_column_map, factory); + if (nautilus_column == NULL) + { + continue; + } + g_object_get (nautilus_column, "attribute", &attribute, NULL); + if (g_strcmp0 (attributes[sort_type], attribute) == 0) + { + sort_column = g_steal_pointer (&view_column); + break; + } + } -static gboolean -nautilus_list_view_can_zoom_in (NautilusFilesView *view) -{ - g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), FALSE); + sorter = gtk_column_view_get_sorter (self->view_ui); - return NAUTILUS_LIST_VIEW (view)->details->zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_LARGER; -} + g_signal_handlers_block_by_func (sorter, on_sorter_changed, self); + /* FIXME: Set NULL to stop drawing the arrow on previous sort column + * to workaround https://gitlab.gnome.org/GNOME/gtk/-/issues/4696 */ + gtk_column_view_sort_by_column (self->view_ui, NULL, FALSE); + gtk_column_view_sort_by_column (self->view_ui, sort_column, reversed); + g_signal_handlers_unblock_by_func (sorter, on_sorter_changed, self); -static gboolean -nautilus_list_view_can_zoom_out (NautilusFilesView *view) -{ - g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), FALSE); + set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)), + target_name, + reversed); - return NAUTILUS_LIST_VIEW (view)->details->zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_SMALL; + g_simple_action_set_state (action, value); } -static gboolean -nautilus_list_view_is_zoom_level_default (NautilusFilesView *view) +static void +set_zoom_level (NautilusListView *self, + guint new_level) { - NautilusListView *list_view; - guint icon_size; + self->zoom_level = new_level; + + nautilus_list_base_set_icon_size (NAUTILUS_LIST_BASE (self), + get_icon_size_for_zoom_level (new_level)); - list_view = NAUTILUS_LIST_VIEW (view); - icon_size = nautilus_list_model_get_icon_size_for_zoom_level (list_view->details->zoom_level); + if (self->zoom_level == NAUTILUS_LIST_ZOOM_LEVEL_SMALL) + { + gtk_widget_add_css_class (GTK_WIDGET (self), "compact"); + } + else + { + gtk_widget_remove_css_class (GTK_WIDGET (self), "compact"); + } - return icon_size == NAUTILUS_LIST_ICON_SIZE_STANDARD; + nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self)); } static void -nautilus_list_view_click_policy_changed (NautilusFilesView *directory_view) +action_zoom_to_level (GSimpleAction *action, + GVariant *state, + gpointer user_data) { - GdkDisplay *display; - NautilusListView *view; - GtkTreeIter iter; - GtkTreeView *tree; + NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); + int zoom_level; - view = NAUTILUS_LIST_VIEW (directory_view); - display = gtk_widget_get_display (GTK_WIDGET (view)); + zoom_level = g_variant_get_int32 (state); + set_zoom_level (self, zoom_level); + g_simple_action_set_state (G_SIMPLE_ACTION (action), state); - /* ensure that we unset the hand cursor and refresh underlined rows */ - if (get_click_policy () == NAUTILUS_CLICK_POLICY_DOUBLE) + if (g_settings_get_enum (nautilus_list_view_preferences, + NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level) { - if (view->details->hover_path != NULL) - { - if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model), - &iter, view->details->hover_path)) - { - gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model), - view->details->hover_path, &iter); - } + g_settings_set_enum (nautilus_list_view_preferences, + NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL, + zoom_level); + } +} - gtk_tree_path_free (view->details->hover_path); - view->details->hover_path = NULL; - } +const GActionEntry list_view_entries[] = +{ + { "visible-columns", action_visible_columns }, + { "sort", NULL, "(sb)", "('invalid',false)", action_sort_order_changed }, + { "zoom-to-level", NULL, NULL, "1", action_zoom_to_level } +}; - tree = view->details->tree_view; - if (gtk_widget_get_realized (GTK_WIDGET (tree))) - { - gtk_widget_set_cursor (GTK_WIDGET (tree), NULL); +static void +real_begin_loading (NautilusFilesView *files_view) +{ + NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); + NautilusFile *file; - if (display != NULL) - { - gdk_display_flush (display); - } - } + NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->begin_loading (files_view); + + update_columns_settings_from_metadata_and_preferences (self); - g_clear_object (&hand_cursor); + self->path_attribute_q = 0; + g_clear_object (&self->file_path_base_location); + file = nautilus_files_view_get_directory_as_file (files_view); + if (nautilus_file_is_in_trash (file)) + { + self->path_attribute_q = g_quark_from_string ("trash_orig_path"); + self->file_path_base_location = get_base_location (self); } - else if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE) + else if (nautilus_file_is_in_search (file) || + nautilus_file_is_in_recent (file) || + nautilus_file_is_in_starred (file)) { - if (hand_cursor == NULL) - { - hand_cursor = gdk_cursor_new_from_name ("pointer", NULL); - } + self->path_attribute_q = g_quark_from_string ("where"); + self->file_path_base_location = get_base_location (self); } } static void -default_sort_order_changed_callback (gpointer callback_data) +real_bump_zoom_level (NautilusFilesView *files_view, + int zoom_increment) { - NautilusListView *list_view; + NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); + NautilusListZoomLevel new_level; - list_view = NAUTILUS_LIST_VIEW (callback_data); + new_level = self->zoom_level + zoom_increment; - set_sort_order_from_metadata_and_preferences (list_view); + if (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL && + new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGE) + { + g_action_group_change_action_state (self->action_group, + "zoom-to-level", + g_variant_new_int32 (new_level)); + } } -static void -default_visible_columns_changed_callback (gpointer callback_data) +static gint +get_default_zoom_level (void) { - NautilusListView *list_view; + NautilusListZoomLevel default_zoom_level; - list_view = NAUTILUS_LIST_VIEW (callback_data); + default_zoom_level = g_settings_get_enum (nautilus_list_view_preferences, + NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL); - set_columns_settings_from_metadata_and_preferences (list_view); + /* Sanitize preference value. */ + return CLAMP (default_zoom_level, + NAUTILUS_LIST_ZOOM_LEVEL_SMALL, + NAUTILUS_LIST_ZOOM_LEVEL_LARGE); } static void -default_column_order_changed_callback (gpointer callback_data) +real_restore_standard_zoom_level (NautilusFilesView *files_view) { - NautilusListView *list_view; - - list_view = NAUTILUS_LIST_VIEW (callback_data); + NautilusListView *self; - set_columns_settings_from_metadata_and_preferences (list_view); + self = NAUTILUS_LIST_VIEW (files_view); + g_action_group_change_action_state (self->action_group, + "zoom-to-level", + g_variant_new_int32 (NAUTILUS_LIST_ZOOM_LEVEL_MEDIUM)); } -static void -nautilus_list_view_sort_directories_first_changed (NautilusFilesView *view) +static gboolean +real_can_zoom_in (NautilusFilesView *files_view) { - NautilusListView *list_view; + NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); - list_view = NAUTILUS_LIST_VIEW (view); - - nautilus_list_model_set_should_sort_directories_first (list_view->details->model, - nautilus_files_view_should_sort_directories_first (view)); + return self->zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_LARGE; } -static int -nautilus_list_view_compare_files (NautilusFilesView *view, - NautilusFile *file1, - NautilusFile *file2) +static gboolean +real_can_zoom_out (NautilusFilesView *files_view) { - NautilusListView *list_view; + NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); - list_view = NAUTILUS_LIST_VIEW (view); - return nautilus_list_model_compare_func (list_view->details->model, file1, file2); + return self->zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_SMALL; } -static void -nautilus_list_view_dispose (GObject *object) +static gboolean +real_is_zoom_level_default (NautilusFilesView *files_view) { - NautilusListView *list_view; - - list_view = NAUTILUS_LIST_VIEW (object); - - if (list_view->details->model) - { - g_object_unref (list_view->details->model); - list_view->details->model = NULL; - } - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION - if (list_view->details->drag_dest) - { - g_object_unref (list_view->details->drag_dest); - list_view->details->drag_dest = NULL; - } -#endif - - g_signal_handlers_disconnect_by_func (nautilus_preferences, - default_sort_order_changed_callback, - list_view); - g_signal_handlers_disconnect_by_func (nautilus_list_view_preferences, - default_visible_columns_changed_callback, - list_view); - g_signal_handlers_disconnect_by_func (nautilus_list_view_preferences, - default_column_order_changed_callback, - list_view); + NautilusListView *self; + guint icon_size; - g_clear_pointer (&list_view->details->columns_popover, gtk_widget_unparent); + self = NAUTILUS_LIST_VIEW (files_view); + icon_size = get_icon_size_for_zoom_level (self->zoom_level); - G_OBJECT_CLASS (nautilus_list_view_parent_class)->dispose (object); + return icon_size == NAUTILUS_LIST_ICON_SIZE_MEDIUM; } static void -nautilus_list_view_finalize (GObject *object) +real_sort_directories_first_changed (NautilusFilesView *files_view) { - NautilusListView *list_view; - - list_view = NAUTILUS_LIST_VIEW (object); - - g_free (list_view->details->original_name); - list_view->details->original_name = NULL; - - if (list_view->details->first_click_path) - { - gtk_tree_path_free (list_view->details->first_click_path); - } - if (list_view->details->new_selection_path) - { - gtk_tree_path_free (list_view->details->new_selection_path); - } - - g_list_free (list_view->details->cells); - g_hash_table_destroy (list_view->details->columns); - - if (list_view->details->hover_path != NULL) - { - gtk_tree_path_free (list_view->details->hover_path); - } - - if (list_view->details->column_editor != NULL) - { - gtk_window_destroy (GTK_WINDOW (list_view->details->column_editor)); - } + NautilusListView *self = NAUTILUS_LIST_VIEW (files_view); + NautilusViewModel *model; - g_regex_unref (list_view->details->regex); + self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self)); - g_cancellable_cancel (list_view->details->starred_cancellable); - g_clear_object (&list_view->details->starred_cancellable); - - g_signal_handlers_disconnect_by_func (nautilus_tag_manager_get (), - on_starred_files_changed, - list_view); - - g_free (list_view->details); - - G_OBJECT_CLASS (nautilus_list_view_parent_class)->finalize (object); + /* Reset the sorter to trigger ressorting */ + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + nautilus_view_model_set_sorter (model, nautilus_view_model_get_sorter (model)); } -static char * -nautilus_list_view_get_first_visible_file (NautilusFilesView *view) +static void +on_sorter_changed (GtkSorter *sorter, + GtkSorterChange change, + gpointer user_data) { - NautilusFile *file; - GtkTreePath *path; - GtkTreeIter iter; - NautilusListView *list_view; - - list_view = NAUTILUS_LIST_VIEW (view); - - if (gtk_tree_view_get_path_at_pos (list_view->details->tree_view, - 0, 0, - &path, NULL, NULL, NULL)) - { - gtk_tree_model_get_iter (GTK_TREE_MODEL (list_view->details->model), - &iter, path); - - gtk_tree_path_free (path); - - gtk_tree_model_get (GTK_TREE_MODEL (list_view->details->model), - &iter, - NAUTILUS_LIST_MODEL_FILE_COLUMN, &file, - -1); - if (file) - { - char *uri; + NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); - uri = nautilus_file_get_uri (file); - - nautilus_file_unref (file); - - return uri; - } - } + /* When user clicks a header to change sort order, we don't know what the + * new sort order is. Make sure the sort menu doesn't indicate a outdated + * action state. */ + g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)), + "sort", g_variant_new ("(sb)", "unknown", FALSE)); +} - return NULL; +static guint +real_get_view_id (NautilusFilesView *files_view) +{ + return NAUTILUS_VIEW_LIST_ID; } static void -nautilus_list_view_scroll_to_file (NautilusListView *view, - NautilusFile *file) +on_item_click_released_workaround (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) { - GtkTreePath *path; - GtkTreeIter iter; + NautilusViewCell *cell = user_data; + NautilusListView *self = NAUTILUS_LIST_VIEW (nautilus_view_cell_get_view (cell)); + GdkModifierType modifiers; - if (!nautilus_list_model_get_first_iter_for_file (view->details->model, file, &iter)) + modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture)); + if (n_press == 1 && + modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) { - return; - } + NautilusViewModel *model; + NautilusViewItem *item; + guint i; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->model), &iter); + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + item = nautilus_view_cell_get_item (cell); + g_return_if_fail (item != NULL); + i = nautilus_view_model_get_index (model, item); - gtk_tree_view_scroll_to_cell (view->details->tree_view, - path, NULL, - TRUE, 0.0, 0.0); - - gtk_tree_path_free (path); + gtk_widget_activate_action (GTK_WIDGET (cell), + "list.select-item", + "(ubb)", + i, + modifiers & GDK_CONTROL_MASK, + modifiers & GDK_SHIFT_MASK); + } } +/* This whole event handler is a workaround to a GtkColumnView bug: it + * activates the list|select-item action twice, which may cause the + * second activation to reverse the effects of the first: + * https://gitlab.gnome.org/GNOME/gtk/-/issues/4819 + * + * As a workaround, we are going to activate the action a 3rd time. + * The third time is the charm, as the saying goes. */ static void -list_view_scroll_to_file (NautilusFilesView *view, - const char *uri) +setup_selection_click_workaround (NautilusViewCell *cell) { - NautilusFile *file; + GtkEventController *controller; - 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_list_view_scroll_to_file (NAUTILUS_LIST_VIEW (view), file); - nautilus_file_unref (file); - } - } + controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); + gtk_widget_add_controller (GTK_WIDGET (cell), controller); + gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), GDK_BUTTON_PRIMARY); + g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released_workaround), cell); } static void -on_clipboard_contents_received (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +setup_name_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) { - NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object); - NautilusListViewDetails *details = NAUTILUS_LIST_VIEW (files_view)->details; - NautilusClipboard *clip; + NautilusListView *self = NAUTILUS_LIST_VIEW (user_data); + NautilusViewCell *cell; - clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL); - if (clip != NULL && nautilus_clipboard_is_cut (clip)) - { - GList *files; + cell = nautilus_name_cell_new (NAUTILUS_LIST_BASE (self)); + setup_cell_common (listitem, cell); - files = nautilus_clipboard_peek_files (clip); - nautilus_list_model_set_highlight_for_files (details->model, files); - } - else + nautilus_name_cell_set_path (NAUTILUS_NAME_CELL (cell), + self->path_attribute_q, + self->file_path_base_location); + if (NAUTILUS_IS_SEARCH_DIRECTORY (nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (self)))) { - nautilus_list_model_set_highlight_for_files (details->model, NULL); + nautilus_name_cell_show_snippet (NAUTILUS_NAME_CELL (cell)); } + + gtk_list_item_set_child (listitem, GTK_WIDGET (cell)); + + setup_selection_click_workaround (cell); } static void -update_clipboard_status (NautilusFilesView *view) +bind_name_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) { - nautilus_files_view_get_clipboard_async (view, - on_clipboard_contents_received, - NULL); + NautilusViewItem *item; + + item = NAUTILUS_VIEW_ITEM (gtk_list_item_get_item (listitem)); + + nautilus_view_item_set_item_ui (item, gtk_list_item_get_child (listitem)); } static void -on_clipboard_owner_changed (GdkClipboard *clipboard, - gpointer user_data) +unbind_name_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) { - update_clipboard_status (NAUTILUS_FILES_VIEW (user_data)); + NautilusViewItem *item; + + item = NAUTILUS_VIEW_ITEM (gtk_list_item_get_item (listitem)); + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (item)); + + nautilus_view_item_set_item_ui (item, NULL); } static void -nautilus_list_view_end_loading (NautilusFilesView *view, - gboolean all_files_seen) +setup_star_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) { - update_clipboard_status (view); -} + NautilusViewCell *cell; -static guint -nautilus_list_view_get_id (NautilusFilesView *view) -{ - return NAUTILUS_VIEW_LIST_ID; + cell = nautilus_star_cell_new (NAUTILUS_LIST_BASE (user_data)); + setup_cell_common (listitem, cell); + setup_selection_click_workaround (cell); } -static GdkRectangle * -get_rectangle_for_path (NautilusListView *list_view, - GtkTreePath *path) +static void +setup_label_cell (GtkSignalListItemFactory *factory, + GtkListItem *listitem, + gpointer user_data) { - GtkTreeView *tree_view = list_view->details->tree_view; - GdkRectangle *rect = g_malloc0 (sizeof (GdkRectangle)); - int header_height; - - gtk_tree_view_get_cell_area (tree_view, - path, - list_view->details->file_name_column, - rect); - gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, - rect->x, rect->y, - &rect->x, &rect->y); - - /* FIXME Due to smooth scrolling, we may get the cell area while the view is - * still scrolling (and still outside the view), not at the final position - * of the cell after scrolling. - * https://bugzilla.gnome.org/show_bug.cgi?id=746773 - * The following workaround guesses the final "y" coordinate by clamping it - * to the widget edge. Note that the top edge has got columns header, which - * is private, so first guess the header height from the difference between - * widget coordinates and bin cooridinates. - */ - gtk_tree_view_convert_bin_window_to_widget_coords (tree_view, - 0, 0, - NULL, &header_height); + NautilusListView *self = user_data; + NautilusColumn *nautilus_column; + NautilusViewCell *cell; - rect->y = CLAMP (rect->y, - header_height, - gtk_widget_get_allocated_height (GTK_WIDGET (list_view)) - rect->height); - /* End of workaround */ + nautilus_column = g_hash_table_lookup (self->factory_to_column_map, factory); - return rect; + cell = nautilus_label_cell_new (NAUTILUS_LIST_BASE (user_data), nautilus_column); + setup_cell_common (listitem, cell); + setup_selection_click_workaround (cell); } -static GdkRectangle * -nautilus_list_view_compute_rename_popover_pointing_to (NautilusFilesView *view) +static void +setup_view_columns (NautilusListView *self) { - NautilusListView *list_view; - GtkTreeView *tree_view; - GtkTreeSelection *selection; - GList *list; - GtkTreePath *path; - GdkRectangle *rect; - - list_view = NAUTILUS_LIST_VIEW (view); - tree_view = list_view->details->tree_view; - selection = gtk_tree_view_get_selection (tree_view); - list = gtk_tree_selection_get_selected_rows (selection, NULL); - path = list->data; - rect = get_rectangle_for_path (list_view, path); - - if (list_view->details->last_event_button_x > 0) - { - /* Point to the position in the row where it was clicked. */ - rect->x = list_view->details->last_event_button_x; - /* Make it zero width to point exactly at rect->x.*/ - rect->width = 0; - } + GtkListItemFactory *factory; + g_autolist (NautilusColumn) nautilus_columns = NULL; - g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); + nautilus_columns = nautilus_get_all_columns (); - return rect; -} + self->factory_to_column_map = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + g_object_unref); -static GdkRectangle * -nautilus_list_view_reveal_for_selection_context_menu (NautilusFilesView *view) -{ - NautilusListView *list_view; - GtkTreeView *tree_view; - GtkTreeSelection *tree_selection; - GtkTreePath *path; - GdkRectangle *rect; + for (GList *l = nautilus_columns; l != NULL; l = l->next) + { + NautilusColumn *nautilus_column = NAUTILUS_COLUMN (l->data); + g_autofree gchar *name = NULL; + g_autofree gchar *label = NULL; + GQuark attribute_q = 0; + GtkSortType sort_order; + g_autoptr (GtkCustomSorter) sorter = NULL; + g_autoptr (GtkColumnViewColumn) view_column = NULL; - g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), NULL); + g_object_get (nautilus_column, + "name", &name, + "label", &label, + "attribute_q", &attribute_q, + "default-sort-order", &sort_order, + NULL); - list_view = NAUTILUS_LIST_VIEW (view); - tree_view = list_view->details->tree_view; - tree_selection = gtk_tree_view_get_selection (tree_view); - g_return_val_if_fail (tree_selection != NULL, NULL); + sorter = gtk_custom_sorter_new (nautilus_list_view_sort, + GUINT_TO_POINTER (attribute_q), + NULL); - /* Get the path to the last focused item, if selected. Otherwise, get - * the path to the selected item which is sorted the lowest. - */ - gtk_tree_view_get_cursor (tree_view, &path, NULL); - if (path == NULL || !gtk_tree_selection_path_is_selected (tree_selection, path)) - { - GList *list; + factory = gtk_signal_list_item_factory_new (); + view_column = gtk_column_view_column_new (NULL, factory); + gtk_column_view_column_set_expand (view_column, FALSE); + gtk_column_view_column_set_resizable (view_column, TRUE); + gtk_column_view_column_set_title (view_column, label); + gtk_column_view_column_set_sorter (view_column, GTK_SORTER (sorter)); - list = gtk_tree_selection_get_selected_rows (tree_selection, NULL); - list = g_list_last (list); - path = g_steal_pointer (&list->data); + if (!strcmp (name, "name")) + { + g_signal_connect (factory, "setup", G_CALLBACK (setup_name_cell), self); + g_signal_connect (factory, "bind", G_CALLBACK (bind_name_cell), self); + g_signal_connect (factory, "unbind", G_CALLBACK (unbind_name_cell), self); - g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); - } + gtk_column_view_column_set_expand (view_column, TRUE); + } + else if (g_strcmp0 (name, "starred") == 0) + { + g_signal_connect (factory, "setup", G_CALLBACK (setup_star_cell), self); - gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0); + gtk_column_view_column_set_title (view_column, ""); + gtk_column_view_column_set_resizable (view_column, FALSE); - rect = get_rectangle_for_path (list_view, path); - rect->width = rect->height; + self->star_column = view_column; + } + else + { + g_signal_connect (factory, "setup", G_CALLBACK (setup_label_cell), self); + } - gtk_tree_path_free (path); + gtk_column_view_append_column (self->view_ui, view_column); - return rect; + g_hash_table_insert (self->factory_to_column_map, + factory, + g_object_ref (nautilus_column)); + } } static void -nautilus_list_view_preview_selection_event (NautilusFilesView *view, - GtkDirectionType direction) +nautilus_list_view_init (NautilusListView *self) { - NautilusListView *list_view; - GtkTreeView *tree_view; - GtkTreeSelection *selection; - GList *list; - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeModel *tree_model; - gboolean moved; - - /* We only support up and down movements for the list view */ - if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN) - { - return; - } + NautilusViewModel *model; + GtkWidget *content_widget; + g_autoptr (GtkCustomSorter) directories_sorter = NULL; + g_autoptr (GtkMultiSorter) sorter = NULL; - list_view = NAUTILUS_LIST_VIEW (view); - tree_view = list_view->details->tree_view; - selection = gtk_tree_view_get_selection (tree_view); - list = gtk_tree_selection_get_selected_rows (selection, &tree_model); + gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-list-view"); - if (list == NULL) - { - return; - } + g_signal_connect_object (nautilus_list_view_preferences, + "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS, + G_CALLBACK (update_columns_settings_from_metadata_and_preferences), + self, + G_CONNECT_SWAPPED); + g_signal_connect_object (nautilus_list_view_preferences, + "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER, + G_CALLBACK (update_columns_settings_from_metadata_and_preferences), + self, + G_CONNECT_SWAPPED); - /* Advance the first selected item, since that's what we use for - * the previewer */ - path = list->data; - moved = FALSE; - if (gtk_tree_model_get_iter (tree_model, &iter, path)) - { - if (direction == GTK_DIR_UP) - { - moved = gtk_tree_model_iter_previous (tree_model, &iter); - } - else - { - moved = gtk_tree_model_iter_next (tree_model, &iter); - } - } + content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self)); - if (moved) - { - g_signal_handlers_block_by_func (selection, list_selection_changed_callback, view); + self->view_ui = create_view_ui (self); + nautilus_list_base_setup_gestures (NAUTILUS_LIST_BASE (self)); - gtk_tree_selection_unselect_all (selection); - gtk_tree_selection_select_iter (selection, &iter); + setup_view_columns (self); - g_signal_handlers_unblock_by_func (selection, list_selection_changed_callback, view); - nautilus_files_view_notify_selection_changed (view); - } + self->directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self)); + directories_sorter = gtk_custom_sorter_new (sort_directories_func, &self->directories_first, NULL); + + sorter = gtk_multi_sorter_new (); + gtk_multi_sorter_append (sorter, g_object_ref (GTK_SORTER (directories_sorter))); + gtk_multi_sorter_append (sorter, g_object_ref (gtk_column_view_get_sorter (self->view_ui))); + g_signal_connect_object (sorter, "changed", G_CALLBACK (on_sorter_changed), self, 0); + + model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self)); + nautilus_view_model_set_sorter (model, GTK_SORTER (sorter)); + + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget), + GTK_WIDGET (self->view_ui)); + + self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)); + g_action_map_add_action_entries (G_ACTION_MAP (self->action_group), + list_view_entries, + G_N_ELEMENTS (list_view_entries), + self); + + self->zoom_level = get_default_zoom_level (); + g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)), + "zoom-to-level", g_variant_new_int32 (self->zoom_level)); +} + +static void +nautilus_list_view_dispose (GObject *object) +{ + NautilusListView *self = NAUTILUS_LIST_VIEW (object); - g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free); + g_clear_object (&self->file_path_base_location); + g_clear_pointer (&self->factory_to_column_map, g_hash_table_destroy); + + G_OBJECT_CLASS (nautilus_list_view_parent_class)->dispose (object); } static void -nautilus_list_view_class_init (NautilusListViewClass *class) +nautilus_list_view_finalize (GObject *object) { - NautilusFilesViewClass *nautilus_files_view_class; - - nautilus_files_view_class = NAUTILUS_FILES_VIEW_CLASS (class); - - G_OBJECT_CLASS (class)->dispose = nautilus_list_view_dispose; - G_OBJECT_CLASS (class)->finalize = nautilus_list_view_finalize; - - nautilus_files_view_class->add_files = nautilus_list_view_add_files; - nautilus_files_view_class->begin_loading = nautilus_list_view_begin_loading; - nautilus_files_view_class->end_loading = nautilus_list_view_end_loading; - nautilus_files_view_class->bump_zoom_level = nautilus_list_view_bump_zoom_level; - nautilus_files_view_class->can_zoom_in = nautilus_list_view_can_zoom_in; - nautilus_files_view_class->can_zoom_out = nautilus_list_view_can_zoom_out; - nautilus_files_view_class->is_zoom_level_default = nautilus_list_view_is_zoom_level_default; - nautilus_files_view_class->click_policy_changed = nautilus_list_view_click_policy_changed; - nautilus_files_view_class->clear = nautilus_list_view_clear; - nautilus_files_view_class->file_changed = nautilus_list_view_file_changed; - nautilus_files_view_class->get_backing_uri = nautilus_list_view_get_backing_uri; - nautilus_files_view_class->get_selection = nautilus_list_view_get_selection; - nautilus_files_view_class->get_selection_for_file_transfer = nautilus_list_view_get_selection_for_file_transfer; - nautilus_files_view_class->is_empty = nautilus_list_view_is_empty; - nautilus_files_view_class->remove_file = nautilus_list_view_remove_file; - nautilus_files_view_class->restore_standard_zoom_level = nautilus_list_view_restore_standard_zoom_level; - nautilus_files_view_class->reveal_selection = nautilus_list_view_reveal_selection; - nautilus_files_view_class->select_all = nautilus_list_view_select_all; - nautilus_files_view_class->select_first = nautilus_list_view_select_first; - nautilus_files_view_class->set_selection = nautilus_list_view_set_selection; - nautilus_files_view_class->invert_selection = nautilus_list_view_invert_selection; - nautilus_files_view_class->compare_files = nautilus_list_view_compare_files; - nautilus_files_view_class->sort_directories_first_changed = nautilus_list_view_sort_directories_first_changed; - nautilus_files_view_class->end_file_changes = nautilus_list_view_end_file_changes; - nautilus_files_view_class->get_view_id = nautilus_list_view_get_id; - nautilus_files_view_class->get_first_visible_file = nautilus_list_view_get_first_visible_file; - nautilus_files_view_class->scroll_to_file = list_view_scroll_to_file; - nautilus_files_view_class->compute_rename_popover_pointing_to = nautilus_list_view_compute_rename_popover_pointing_to; - nautilus_files_view_class->reveal_for_selection_context_menu = nautilus_list_view_reveal_for_selection_context_menu; - nautilus_files_view_class->preview_selection_event = nautilus_list_view_preview_selection_event; + G_OBJECT_CLASS (nautilus_list_view_parent_class)->finalize (object); } static void -nautilus_list_view_init (NautilusListView *list_view) +nautilus_list_view_class_init (NautilusListViewClass *klass) { - GActionGroup *view_action_group; - GdkClipboard *clipboard; - - list_view->details = g_new0 (NautilusListViewDetails, 1); - - /* ensure that the zoom level is always set before settings up the tree view columns */ - list_view->details->zoom_level = get_default_zoom_level (); - - create_and_set_up_tree_view (list_view); - - gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (list_view)), - "nautilus-list-view"); - - list_view->details->columns_popover = gtk_popover_new (); - gtk_widget_set_parent (list_view->details->columns_popover, - GTK_WIDGET (list_view)); - g_signal_connect (list_view->details->columns_popover, "destroy", G_CALLBACK (gtk_widget_unparent), NULL); - - list_view->details->columns_popover_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - gtk_widget_set_margin_top (list_view->details->columns_popover_box, 6); - gtk_widget_set_margin_bottom (list_view->details->columns_popover_box, 6); - gtk_widget_set_margin_start (list_view->details->columns_popover_box, 6); - gtk_widget_set_margin_end (list_view->details->columns_popover_box, 6); - gtk_popover_set_child (GTK_POPOVER (list_view->details->columns_popover), - list_view->details->columns_popover_box); - - g_signal_connect_swapped (nautilus_preferences, - "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER, - G_CALLBACK (default_sort_order_changed_callback), - list_view); - g_signal_connect_swapped (nautilus_preferences, - "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER, - G_CALLBACK (default_sort_order_changed_callback), - list_view); - g_signal_connect_swapped (nautilus_list_view_preferences, - "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS, - G_CALLBACK (default_visible_columns_changed_callback), - list_view); - g_signal_connect_swapped (nautilus_list_view_preferences, - "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER, - G_CALLBACK (default_column_order_changed_callback), - list_view); - - /* React to clipboard changes */ - clipboard = gdk_display_get_clipboard (gdk_display_get_default ()); - g_signal_connect_object (clipboard, "changed", - G_CALLBACK (on_clipboard_owner_changed), list_view, - 0); - - nautilus_list_view_click_policy_changed (NAUTILUS_FILES_VIEW (list_view)); - - nautilus_list_view_set_zoom_level (list_view, get_default_zoom_level ()); - - list_view->details->hover_path = NULL; - - view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (list_view)); - g_action_map_add_action_entries (G_ACTION_MAP (view_action_group), - list_view_entries, - G_N_ELEMENTS (list_view_entries), - list_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 (list_view)), - "zoom-to-level", g_variant_new_int32 (get_default_zoom_level ())); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass); + NautilusListBaseClass *files_model_view_class = NAUTILUS_LIST_BASE_CLASS (klass); - list_view->details->regex = g_regex_new ("\\R+", 0, G_REGEX_MATCH_NEWLINE_ANY, NULL); + object_class->dispose = nautilus_list_view_dispose; + object_class->finalize = nautilus_list_view_finalize; - list_view->details->starred_cancellable = g_cancellable_new (); + files_view_class->begin_loading = real_begin_loading; + files_view_class->bump_zoom_level = real_bump_zoom_level; + files_view_class->can_zoom_in = real_can_zoom_in; + files_view_class->can_zoom_out = real_can_zoom_out; + files_view_class->sort_directories_first_changed = real_sort_directories_first_changed; + files_view_class->get_view_id = real_get_view_id; + files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level; + files_view_class->is_zoom_level_default = real_is_zoom_level_default; - g_signal_connect (nautilus_tag_manager_get (), - "starred-changed", - (GCallback) on_starred_files_changed, - list_view); + files_model_view_class->get_icon_size = real_get_icon_size; + files_model_view_class->get_view_ui = real_get_view_ui; + files_model_view_class->scroll_to_item = real_scroll_to_item; } -NautilusFilesView * +NautilusListView * nautilus_list_view_new (NautilusWindowSlot *slot) { return g_object_new (NAUTILUS_TYPE_LIST_VIEW, diff --git a/src/nautilus-list-view.h b/src/nautilus-list-view.h index 7e19621e81d41f856210bb8e2ab4708d27c0b9db..8c2133648ed8625ce20b85e2f9fa24c9738ff732 100644 --- a/src/nautilus-list-view.h +++ b/src/nautilus-list-view.h @@ -1,40 +1,22 @@ - -/* fm-list-view.h - interface for list view of directory. - - Copyright (C) 2000 Eazel, Inc. - Copyright (C) 2001 Anders Carlsson - - 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 - Anders Carlsson -*/ +/* + * Copyright (C) 2000 Eazel, Inc. + * Copyright (C) 2001, 2002 Anders Carlsson + * Copyright (C) 2022 GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ #pragma once -#include "nautilus-files-view.h" +#include "nautilus-list-base.h" +#include "nautilus-window-slot.h" + +G_BEGIN_DECLS -#define NAUTILUS_TYPE_LIST_VIEW (nautilus_list_view_get_type ()) -G_DECLARE_FINAL_TYPE (NautilusListView, nautilus_list_view, NAUTILUS, LIST_VIEW, NautilusFilesView) +#define NAUTILUS_TYPE_LIST_VIEW (nautilus_list_view_get_type()) -typedef struct NautilusListViewDetails NautilusListViewDetails; +G_DECLARE_FINAL_TYPE (NautilusListView, nautilus_list_view, NAUTILUS, LIST_VIEW, NautilusListBase) -struct _NautilusListView -{ - NautilusFilesView parent_instance; - NautilusListViewDetails *details; -}; +NautilusListView *nautilus_list_view_new (NautilusWindowSlot *slot); -NautilusFilesView * nautilus_list_view_new (NautilusWindowSlot *slot); \ No newline at end of file +G_END_DECLS diff --git a/src/nautilus-name-cell.c b/src/nautilus-name-cell.c new file mode 100644 index 0000000000000000000000000000000000000000..daa6308a7deaf8df1e46412d29f9321c1ecdcebf --- /dev/null +++ b/src/nautilus-name-cell.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "nautilus-name-cell.h" +#include "nautilus-file-utilities.h" + +struct _NautilusNameCell +{ + NautilusViewCell parent_instance; + + GSignalGroup *item_signal_group; + + GQuark path_attribute_q; + GFile *file_path_base_location; + + GtkWidget *fixed_height_box; + GtkWidget *icon; + GtkWidget *label; + GtkWidget *snippet; + GtkWidget *path; + + gboolean show_snippet; +}; + +G_DEFINE_TYPE (NautilusNameCell, nautilus_name_cell, NAUTILUS_TYPE_VIEW_CELL) + +static gchar * +get_path_text (NautilusFile *file, + GQuark path_attribute_q, + GFile *base_location) +{ + g_autofree gchar *path = NULL; + g_autoptr (GFile) dir_location = NULL; + g_autoptr (GFile) home_location = g_file_new_for_path (g_get_home_dir ()); + GFile *relative_location_base; + + if (path_attribute_q == 0) + { + return NULL; + } + + path = nautilus_file_get_string_attribute_q (file, path_attribute_q); + dir_location = g_file_new_for_commandline_arg (path); + + if (base_location != NULL && g_file_equal (base_location, dir_location)) + { + /* Only occurs when search result is + * a direct child of the base location + */ + return NULL; + } + + if (g_file_equal (dir_location, home_location)) + { + return nautilus_compute_title_for_location (home_location); + } + + relative_location_base = base_location; + if (relative_location_base == NULL) + { + /* Only occurs in Recent, Starred and Trash. */ + relative_location_base = home_location; + } + + if (g_file_has_prefix (dir_location, relative_location_base)) + { + g_autofree gchar *relative_path = NULL; + + relative_path = g_file_get_relative_path (relative_location_base, dir_location); + return g_filename_display_name (relative_path); + } + + return g_file_get_path (dir_location); +} + +static gchar * +get_fts_snippet (NautilusFile *file) +{ + const gchar *snippet; + g_autoptr (GRegex) regex = NULL; + + snippet = nautilus_file_get_search_fts_snippet (file); + if (snippet == NULL) + { + return NULL; + } + + /* Flatten the text by replacing new lines with spaces */ + regex = g_regex_new ("\\R+", 0, G_REGEX_MATCH_NEWLINE_ANY, NULL); + return g_regex_replace (regex, + snippet, + -1, + 0, + " ", + G_REGEX_MATCH_NEWLINE_ANY, + NULL); +} + +static void +update_labels (NautilusNameCell *self) +{ + NautilusViewItem *item; + NautilusFile *file; + g_autofree gchar *display_name = NULL; + g_autofree gchar *path_text = NULL; + g_autofree gchar *fts_snippet = NULL; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + file = nautilus_view_item_get_file (item); + + display_name = nautilus_file_get_display_name (file); + path_text = get_path_text (file, + self->path_attribute_q, + self->file_path_base_location); + if (self->show_snippet) + { + fts_snippet = get_fts_snippet (file); + } + + gtk_label_set_text (GTK_LABEL (self->label), display_name); + gtk_label_set_text (GTK_LABEL (self->path), path_text); + gtk_label_set_text (GTK_LABEL (self->snippet), fts_snippet); + + gtk_widget_set_visible (self->path, (path_text != NULL)); + gtk_widget_set_visible (self->snippet, (fts_snippet != NULL)); +} + +static void +update_icon (NautilusNameCell *self) +{ + NautilusFileIconFlags flags; + g_autoptr (GdkPaintable) icon_paintable = NULL; + GtkStyleContext *style_context; + NautilusViewItem *item; + NautilusFile *file; + guint icon_size; + int icon_height; + int extra_margin; + g_autofree gchar *thumbnail_path = NULL; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + + file = nautilus_view_item_get_file (item); + icon_size = nautilus_view_item_get_icon_size (item); + flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | + NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE | + NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS | + NAUTILUS_FILE_ICON_FLAGS_USE_ONE_EMBLEM; + + icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, 1, flags); + gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); + + /* Set the same width for all icons regardless of aspect ratio. + * Don't set the width here because it would get GtkPicture w4h confused. + */ + gtk_widget_set_size_request (self->fixed_height_box, icon_size, -1); + + /* Give all items the same minimum width. This cannot be done by setting the + * width request directly, as above, because it would get mess up with + * height for width calculations. + * + * Instead we must add margins on both sides of the icon which, summed up + * with the icon's actual width, equal the desired item width. */ + icon_height = gdk_paintable_get_intrinsic_height (icon_paintable); + extra_margin = (icon_size - icon_height) / 2; + gtk_widget_set_margin_top (self->fixed_height_box, extra_margin); + gtk_widget_set_margin_bottom (self->fixed_height_box, extra_margin); + + style_context = gtk_widget_get_style_context (self->icon); + thumbnail_path = nautilus_file_get_thumbnail_path (file); + if (icon_size >= NAUTILUS_THUMBNAIL_MINIMUM_ICON_SIZE && + thumbnail_path != NULL && + nautilus_file_should_show_thumbnail (file)) + { + gtk_style_context_add_class (style_context, "thumbnail"); + } + else + { + gtk_style_context_remove_class (style_context, "thumbnail"); + } +} + +static void +on_file_changed (NautilusNameCell *self) +{ + update_icon (self); + update_labels (self); +} + +static void +on_item_size_changed (NautilusNameCell *self) +{ + update_icon (self); +} + +static void +on_item_is_cut_changed (NautilusNameCell *self) +{ + gboolean is_cut; + + g_object_get (nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)), + "is-cut", &is_cut, + NULL); + if (is_cut) + { + gtk_widget_add_css_class (self->icon, "cut"); + } + else + { + gtk_widget_remove_css_class (self->icon, "cut"); + } +} + +static void +nautilus_name_cell_init (NautilusNameCell *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + /* Connect automatically to an item. */ + self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM); + g_signal_group_connect_swapped (self->item_signal_group, "notify::icon-size", + (GCallback) on_item_size_changed, self); + g_signal_group_connect_swapped (self->item_signal_group, "notify::is-cut", + (GCallback) on_item_is_cut_changed, self); + g_signal_group_connect_swapped (self->item_signal_group, "file-changed", + (GCallback) on_file_changed, self); + g_signal_connect_object (self->item_signal_group, "bind", + (GCallback) on_file_changed, self, + G_CONNECT_SWAPPED); + + g_object_bind_property (self, "item", + self->item_signal_group, "target", + G_BINDING_SYNC_CREATE); +} + +static void +nautilus_name_cell_finalize (GObject *object) +{ + NautilusNameCell *self = (NautilusNameCell *) object; + + g_clear_object (&self->item_signal_group); + g_clear_object (&self->file_path_base_location); + G_OBJECT_CLASS (nautilus_name_cell_parent_class)->finalize (object); +} + +static void +nautilus_name_cell_class_init (NautilusNameCellClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = nautilus_name_cell_finalize; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-name-cell.ui"); + + gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, fixed_height_box); + gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, icon); + gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, label); + gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, snippet); + gtk_widget_class_bind_template_child (widget_class, NautilusNameCell, path); +} + +NautilusViewCell * +nautilus_name_cell_new (NautilusListBase *view) +{ + return NAUTILUS_VIEW_CELL (g_object_new (NAUTILUS_TYPE_NAME_CELL, + "view", view, + NULL)); +} + +void +nautilus_name_cell_set_path (NautilusNameCell *self, + GQuark path_attribute_q, + GFile *base_location) +{ + self->path_attribute_q = path_attribute_q; + g_set_object (&self->file_path_base_location, base_location); +} + +void +nautilus_name_cell_show_snippet (NautilusNameCell *self) +{ + self->show_snippet = TRUE; +} diff --git a/src/nautilus-name-cell.h b/src/nautilus-name-cell.h new file mode 100644 index 0000000000000000000000000000000000000000..62862cdf564c774671ae9ab9c0963d4f531f83e5 --- /dev/null +++ b/src/nautilus-name-cell.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2022 António Fernandes + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "nautilus-view-cell.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_NAME_CELL (nautilus_name_cell_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusNameCell, nautilus_name_cell, NAUTILUS, NAME_CELL, NautilusViewCell) + +NautilusViewCell * nautilus_name_cell_new (NautilusListBase *view); +void nautilus_name_cell_set_path (NautilusNameCell *self, + GQuark path_attribute_q, + GFile *base_location); +void nautilus_name_cell_show_snippet (NautilusNameCell *self); + +G_END_DECLS diff --git a/src/nautilus-properties-window.c b/src/nautilus-properties-window.c index 6050683f2816a79ed4751d3d488b79125893d063..b3bfa1991dd5ab7aeddf0b4e7f9c9261fb822511 100644 --- a/src/nautilus-properties-window.c +++ b/src/nautilus-properties-window.c @@ -414,13 +414,13 @@ get_image_for_properties_window (NautilusPropertiesWindow *self, if (!icon) { - icon = nautilus_file_get_icon (file, NAUTILUS_GRID_ICON_SIZE_STANDARD, icon_scale, + icon = nautilus_file_get_icon (file, NAUTILUS_GRID_ICON_SIZE_MEDIUM, icon_scale, NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | NAUTILUS_FILE_ICON_FLAGS_IGNORE_VISITING); } else { - new_icon = nautilus_file_get_icon (file, NAUTILUS_GRID_ICON_SIZE_STANDARD, icon_scale, + new_icon = nautilus_file_get_icon (file, NAUTILUS_GRID_ICON_SIZE_MEDIUM, icon_scale, NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | NAUTILUS_FILE_ICON_FLAGS_IGNORE_VISITING); if (!new_icon || new_icon != icon) @@ -436,7 +436,7 @@ get_image_for_properties_window (NautilusPropertiesWindow *self, { g_autoptr (GIcon) gicon = g_themed_icon_new ("text-x-generic"); - icon = nautilus_icon_info_lookup (gicon, NAUTILUS_GRID_ICON_SIZE_STANDARD, icon_scale); + icon = nautilus_icon_info_lookup (gicon, NAUTILUS_GRID_ICON_SIZE_MEDIUM, icon_scale); } if (icon_name != NULL) diff --git a/src/nautilus-star-cell.c b/src/nautilus-star-cell.c new file mode 100644 index 0000000000000000000000000000000000000000..a47a6b2ffeb9d2350331170b0aa652c2727e18d8 --- /dev/null +++ b/src/nautilus-star-cell.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2022 António Fernandes + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "nautilus-star-cell.h" +#include "nautilus-tag-manager.h" + +struct _NautilusStarCell +{ + NautilusViewCell parent_instance; + + GSignalGroup *item_signal_group; + + GtkImage *star; +}; + +G_DEFINE_TYPE (NautilusStarCell, nautilus_star_cell, NAUTILUS_TYPE_VIEW_CELL) + +static void +on_star_click_released (GtkGestureClick *gesture, + gint n_press, + gdouble x, + gdouble y, + gpointer user_data) +{ + NautilusStarCell *self = user_data; + NautilusTagManager *tag_manager = nautilus_tag_manager_get (); + NautilusViewItem *item; + NautilusFile *file; + g_autofree gchar *uri = NULL; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + file = nautilus_view_item_get_file (item); + uri = nautilus_file_get_uri (file); + + if (nautilus_tag_manager_file_is_starred (tag_manager, uri)) + { + nautilus_tag_manager_unstar_files (tag_manager, + G_OBJECT (item), + &(GList){ file, NULL }, + NULL, + NULL); + gtk_widget_remove_css_class (GTK_WIDGET (self->star), "added"); + } + else + { + nautilus_tag_manager_star_files (tag_manager, + G_OBJECT (item), + &(GList){ file, NULL }, + NULL, + NULL); + gtk_widget_add_css_class (GTK_WIDGET (self->star), "added"); + } + + gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); +} + +static void +update_star (GtkImage *star, + NautilusFile *file) +{ + gboolean is_starred; + g_autofree gchar *file_uri = NULL; + + g_return_if_fail (NAUTILUS_IS_FILE (file)); + + file_uri = nautilus_file_get_uri (file); + is_starred = nautilus_tag_manager_file_is_starred (nautilus_tag_manager_get (), + file_uri); + + gtk_image_set_from_icon_name (star, is_starred ? "starred-symbolic" : "non-starred-symbolic"); +} + +static void +on_file_changed (NautilusStarCell *self) +{ + NautilusViewItem *item; + NautilusFile *file; + g_autofree gchar *string = NULL; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + g_return_if_fail (item != NULL); + file = nautilus_view_item_get_file (item); + + update_star (self->star, file); +} + +static void +on_starred_changed (NautilusTagManager *tag_manager, + GList *changed_files, + gpointer user_data) +{ + NautilusStarCell *self = user_data; + NautilusViewItem *item; + NautilusFile *file; + + item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self)); + if (item == NULL) + { + return; + } + + file = nautilus_view_item_get_file (item); + if (g_list_find (changed_files, file)) + { + update_star (self->star, file); + } +} + +static void +nautilus_star_cell_init (NautilusStarCell *self) +{ + GtkWidget *star; + GtkGesture *gesture; + + /* Create star icon */ + star = gtk_image_new (); + gtk_widget_set_halign (star, GTK_ALIGN_END); + gtk_widget_set_valign (star, GTK_ALIGN_CENTER); + gtk_widget_add_css_class (star, "dim-label"); + gtk_widget_add_css_class (star, "star"); + adw_bin_set_child (ADW_BIN (self), star); + self->star = GTK_IMAGE (star); + + /* Make it clickable */ + gesture = gtk_gesture_click_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_PRIMARY); + g_signal_connect (gesture, "released", G_CALLBACK (on_star_click_released), self); + gtk_widget_add_controller (star, GTK_EVENT_CONTROLLER (gesture)); + + /* Update on tag changes */ + g_signal_connect_object (nautilus_tag_manager_get (), "starred-changed", + G_CALLBACK (on_starred_changed), self, 0); + + /* Connect automatically to an item. */ + self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM); + g_signal_group_connect_swapped (self->item_signal_group, "file-changed", + (GCallback) on_file_changed, self); + g_signal_connect_object (self->item_signal_group, "bind", + (GCallback) on_file_changed, self, + G_CONNECT_SWAPPED); + g_object_bind_property (self, "item", + self->item_signal_group, "target", + G_BINDING_SYNC_CREATE); +} + +static void +nautilus_star_cell_finalize (GObject *object) +{ + NautilusStarCell *self = (NautilusStarCell *) object; + + g_object_unref (self->item_signal_group); + G_OBJECT_CLASS (nautilus_star_cell_parent_class)->finalize (object); +} + +static void +nautilus_star_cell_class_init (NautilusStarCellClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = nautilus_star_cell_finalize; +} + +NautilusViewCell * +nautilus_star_cell_new (NautilusListBase *view) +{ + return NAUTILUS_VIEW_CELL (g_object_new (NAUTILUS_TYPE_STAR_CELL, + "view", view, + NULL)); +} diff --git a/src/nautilus-star-cell.h b/src/nautilus-star-cell.h new file mode 100644 index 0000000000000000000000000000000000000000..415a745143fc1ad723e36d599133af3d8ec482b9 --- /dev/null +++ b/src/nautilus-star-cell.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2022 António Fernandes + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "nautilus-view-cell.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_STAR_CELL (nautilus_star_cell_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusStarCell, nautilus_star_cell, NAUTILUS, STAR_CELL, NautilusViewCell) + +NautilusViewCell * nautilus_star_cell_new (NautilusListBase *view); + +G_END_DECLS diff --git a/src/nautilus-tree-view-drag-dest.c b/src/nautilus-tree-view-drag-dest.c deleted file mode 100644 index ffe89748008ffe5a627316f22db83d4b447bd7d6..0000000000000000000000000000000000000000 --- a/src/nautilus-tree-view-drag-dest.c +++ /dev/null @@ -1,1305 +0,0 @@ -/* - * Nautilus - * - * Copyright (C) 2002 Sun Microsystems, 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 . - * - * Author: Dave Camp - * XDS support: Benedikt Meurer (adapted by Amos Brocco ) - */ - -/* nautilus-tree-view-drag-dest.c: Handles drag and drop for treeviews which - * contain a hierarchy of files - */ - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -#include - -#include "nautilus-tree-view-drag-dest.h" - -#include "nautilus-dnd.h" -#include "nautilus-file-changes-queue.h" -#include "nautilus-global-preferences.h" - -#include - -#include -#include - -#define DEBUG_FLAG NAUTILUS_DEBUG_LIST_VIEW -#include "nautilus-debug.h" - -#define AUTO_SCROLL_MARGIN 20 -#define HOVER_EXPAND_TIMEOUT 1 - -struct _NautilusTreeViewDragDestDetails -{ - GtkTreeView *tree_view; - - gboolean drop_occurred; - - gboolean have_drag_data; - guint drag_type; - GtkSelectionData *drag_data; - GList *drag_list; - - guint hover_id; - gulong highlight_id; - guint scroll_id; - guint expand_id; - - char *direct_save_uri; - char *target_uri; -}; - -enum -{ - GET_ROOT_URI, - GET_FILE_FOR_PATH, - MOVE_COPY_ITEMS, - HANDLE_NETSCAPE_URL, - HANDLE_URI_LIST, - HANDLE_TEXT, - HANDLE_RAW, - HANDLE_HOVER, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL]; - -G_DEFINE_TYPE (NautilusTreeViewDragDest, nautilus_tree_view_drag_dest, - G_TYPE_OBJECT); - -static const GtkTargetEntry drag_types [] = -{ - { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST }, - /* prefer "_NETSCAPE_URL" over "text/uri-list" to satisfy web browsers. */ - { NAUTILUS_ICON_DND_NETSCAPE_URL_TYPE, 0, NAUTILUS_ICON_DND_NETSCAPE_URL }, - { NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST }, - { NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */ - { NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW } -}; - - -static void -gtk_tree_view_vertical_autoscroll (GtkTreeView *tree_view) -{ - GdkRectangle visible_rect; - GtkAdjustment *vadjustment; - GdkDisplay *display; - GdkSeat *seat; - GdkDevice *pointer; - GdkWindow *window; - int y; - int offset; - float value; - - window = gtk_tree_view_get_bin_window (tree_view); - vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (tree_view)); - - display = gtk_widget_get_display (GTK_WIDGET (tree_view)); - seat = gdk_display_get_default_seat (display); - pointer = gdk_seat_get_pointer (seat); - gdk_window_get_device_position (window, pointer, - NULL, &y, NULL); - - y += gtk_adjustment_get_value (vadjustment); - - gtk_tree_view_get_visible_rect (tree_view, &visible_rect); - - offset = y - (visible_rect.y + 2 * AUTO_SCROLL_MARGIN); - if (offset > 0) - { - offset = y - (visible_rect.y + visible_rect.height - 2 * AUTO_SCROLL_MARGIN); - if (offset < 0) - { - return; - } - } - - value = CLAMP (gtk_adjustment_get_value (vadjustment) + offset, 0.0, - gtk_adjustment_get_upper (vadjustment) - gtk_adjustment_get_page_size (vadjustment)); - gtk_adjustment_set_value (vadjustment, value); -} - -static int -scroll_timeout (gpointer data) -{ - GtkTreeView *tree_view = GTK_TREE_VIEW (data); - - gtk_tree_view_vertical_autoscroll (tree_view); - - return TRUE; -} - -static void -remove_scroll_timeout (NautilusTreeViewDragDest *dest) -{ - if (dest->details->scroll_id) - { - g_source_remove (dest->details->scroll_id); - dest->details->scroll_id = 0; - } -} - -static int -expand_timeout (gpointer data) -{ - GtkTreeView *tree_view; - GtkTreePath *drop_path; - - tree_view = GTK_TREE_VIEW (data); - - gtk_tree_view_get_drag_dest_row (tree_view, &drop_path, NULL); - - if (drop_path) - { - gtk_tree_view_expand_row (tree_view, drop_path, FALSE); - gtk_tree_path_free (drop_path); - } - - return FALSE; -} - -static void -remove_expand_timer (NautilusTreeViewDragDest *dest) -{ - if (dest->details->expand_id) - { - g_source_remove (dest->details->expand_id); - dest->details->expand_id = 0; - } -} - -static void -set_drag_dest_row (NautilusTreeViewDragDest *dest, - GtkTreePath *path) -{ - if (path) - { - gtk_tree_view_set_drag_dest_row - (dest->details->tree_view, - path, - GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); - } - else - { - gtk_tree_view_set_drag_dest_row (dest->details->tree_view, - NULL, - 0); - } -} - -static void -clear_drag_dest_row (NautilusTreeViewDragDest *dest) -{ - gtk_tree_view_set_drag_dest_row (dest->details->tree_view, NULL, 0); -} - -static gboolean -get_drag_data (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - guint32 time) -{ - GdkAtom target; - - target = gtk_drag_dest_find_target (GTK_WIDGET (dest->details->tree_view), - context, - NULL); - - if (target == GDK_NONE) - { - return FALSE; - } - - if (target == gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE) && - !dest->details->drop_occurred) - { - dest->details->drag_type = NAUTILUS_ICON_DND_XDNDDIRECTSAVE; - dest->details->have_drag_data = TRUE; - return TRUE; - } - - gtk_drag_get_data (GTK_WIDGET (dest->details->tree_view), - context, target, time); - - return TRUE; -} - -static void -remove_hover_timer (NautilusTreeViewDragDest *dest) -{ - if (dest->details->hover_id != 0) - { - g_source_remove (dest->details->hover_id); - dest->details->hover_id = 0; - } -} - -static void -free_drag_data (NautilusTreeViewDragDest *dest) -{ - dest->details->have_drag_data = FALSE; - - if (dest->details->drag_data) - { - gtk_selection_data_free (dest->details->drag_data); - dest->details->drag_data = NULL; - } - - if (dest->details->drag_list) - { - nautilus_drag_destroy_selection_list (dest->details->drag_list); - dest->details->drag_list = NULL; - } - - g_free (dest->details->direct_save_uri); - dest->details->direct_save_uri = NULL; - - g_free (dest->details->target_uri); - dest->details->target_uri = NULL; - - remove_hover_timer (dest); - remove_expand_timer (dest); -} - -static gboolean -hover_timer (gpointer user_data) -{ - NautilusTreeViewDragDest *dest = user_data; - - dest->details->hover_id = 0; - - g_signal_emit (dest, signals[HANDLE_HOVER], 0, dest->details->target_uri); - - return FALSE; -} - -static void -check_hover_timer (NautilusTreeViewDragDest *dest, - const char *uri) -{ - if (g_strcmp0 (uri, dest->details->target_uri) == 0) - { - return; - } - remove_hover_timer (dest); - - g_clear_pointer (&dest->details->target_uri, g_free); - - if (uri != NULL) - { - dest->details->target_uri = g_strdup (uri); - dest->details->hover_id = g_timeout_add (HOVER_TIMEOUT, hover_timer, dest); - } -} - -static void -check_expand_timer (NautilusTreeViewDragDest *dest, - GtkTreePath *drop_path, - GtkTreePath *old_drop_path) -{ - GtkTreeModel *model; - GtkTreeIter drop_iter; - - model = gtk_tree_view_get_model (dest->details->tree_view); - - if (drop_path == NULL || - (old_drop_path != NULL && gtk_tree_path_compare (old_drop_path, drop_path) != 0)) - { - remove_expand_timer (dest); - } - - if (dest->details->expand_id == 0 && - drop_path != NULL) - { - gtk_tree_model_get_iter (model, &drop_iter, drop_path); - if (gtk_tree_model_iter_has_child (model, &drop_iter)) - { - dest->details->expand_id = - g_timeout_add_seconds (HOVER_EXPAND_TIMEOUT, - expand_timeout, - dest->details->tree_view); - } - } -} - -static char * -get_root_uri (NautilusTreeViewDragDest *dest) -{ - char *uri; - - g_signal_emit (dest, signals[GET_ROOT_URI], 0, &uri); - - return uri; -} - -static NautilusFile * -file_for_path (NautilusTreeViewDragDest *dest, - GtkTreePath *path) -{ - NautilusFile *file; - char *uri; - - if (path) - { - g_signal_emit (dest, signals[GET_FILE_FOR_PATH], 0, path, &file); - } - else - { - uri = get_root_uri (dest); - - file = NULL; - if (uri != NULL) - { - file = nautilus_file_get_by_uri (uri); - } - - g_free (uri); - } - - return file; -} - -static char * -get_drop_target_uri_for_path (NautilusTreeViewDragDest *dest, - GtkTreePath *path) -{ - NautilusFile *file; - char *target = NULL; - gboolean can; - - file = file_for_path (dest, path); - if (file == NULL) - { - return NULL; - } - can = nautilus_drag_can_accept_info (file, - dest->details->drag_type, - dest->details->drag_list); - if (can) - { - target = nautilus_file_get_uri (file); - } - nautilus_file_unref (file); - - return target; -} - -static void -check_hover_expand_timer (NautilusTreeViewDragDest *dest, - GtkTreePath *path, - GtkTreePath *drop_path, - GtkTreePath *old_drop_path) -{ - gboolean use_tree = g_settings_get_boolean (nautilus_list_view_preferences, - NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE); - - if (use_tree) - { - check_expand_timer (dest, drop_path, old_drop_path); - } - else - { - char *uri; - uri = get_drop_target_uri_for_path (dest, path); - check_hover_timer (dest, uri); - g_free (uri); - } -} - -static GtkTreePath * -get_drop_path (NautilusTreeViewDragDest *dest, - GtkTreePath *path) -{ - NautilusFile *file; - GtkTreePath *ret; - - if (!path || !dest->details->have_drag_data) - { - return NULL; - } - - ret = gtk_tree_path_copy (path); - file = file_for_path (dest, ret); - - /* Go up the tree until we find a file that can accept a drop */ - while (file == NULL /* dummy row */ || - !nautilus_drag_can_accept_info (file, - dest->details->drag_type, - dest->details->drag_list)) - { - if (gtk_tree_path_get_depth (ret) == 1) - { - gtk_tree_path_free (ret); - ret = NULL; - break; - } - else - { - gtk_tree_path_up (ret); - - nautilus_file_unref (file); - file = file_for_path (dest, ret); - } - } - nautilus_file_unref (file); - - return ret; -} - -static guint -get_drop_action (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - GtkTreePath *path) -{ - char *drop_target; - int action; - - if (!dest->details->have_drag_data || - (dest->details->drag_type == NAUTILUS_ICON_DND_GNOME_ICON_LIST && - dest->details->drag_list == NULL)) - { - return 0; - } - - drop_target = get_drop_target_uri_for_path (dest, path); - if (drop_target == NULL) - { - return 0; - } - - action = 0; - switch (dest->details->drag_type) - { - case NAUTILUS_ICON_DND_GNOME_ICON_LIST: - { - nautilus_drag_default_drop_action_for_icons - (context, - drop_target, - dest->details->drag_list, - 0, - &action); - } - break; - - case NAUTILUS_ICON_DND_NETSCAPE_URL: - { - action = nautilus_drag_default_drop_action_for_netscape_url (context); - } - break; - - case NAUTILUS_ICON_DND_URI_LIST: - { - action = gdk_drag_context_get_suggested_action (context); - } - break; - - case NAUTILUS_ICON_DND_TEXT: - case NAUTILUS_ICON_DND_RAW: - case NAUTILUS_ICON_DND_XDNDDIRECTSAVE: - { - action = GDK_ACTION_COPY; - } - break; - } - - g_free (drop_target); - - return action; -} - -static gboolean -drag_motion_callback (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - guint32 time, - gpointer data) -{ - NautilusTreeViewDragDest *dest; - GtkTreePath *path; - GtkTreePath *drop_path, *old_drop_path; - GtkTreeViewDropPosition pos; - int bin_y; - guint action; - gboolean res = TRUE; - - dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data); - - gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), - x, y, &path, &pos); - if (pos == GTK_TREE_VIEW_DROP_BEFORE || - pos == GTK_TREE_VIEW_DROP_AFTER) - { - gtk_tree_path_free (path); - path = NULL; - } - - if (!dest->details->have_drag_data) - { - res = get_drag_data (dest, context, time); - } - - if (!res) - { - return FALSE; - } - - drop_path = get_drop_path (dest, path); - - action = 0; - gtk_tree_view_convert_bin_window_to_widget_coords (GTK_TREE_VIEW (widget), - 0, 0, - NULL, &bin_y); - if (bin_y <= y) - { - /* ignore drags on the header */ - action = get_drop_action (dest, context, drop_path); - } - - gtk_tree_view_get_drag_dest_row (GTK_TREE_VIEW (widget), &old_drop_path, - NULL); - - if (action) - { - set_drag_dest_row (dest, drop_path); - check_hover_expand_timer (dest, path, drop_path, old_drop_path); - } - else - { - clear_drag_dest_row (dest); - remove_hover_timer (dest); - remove_expand_timer (dest); - } - - if (path) - { - gtk_tree_path_free (path); - } - - if (drop_path) - { - gtk_tree_path_free (drop_path); - } - - if (old_drop_path) - { - gtk_tree_path_free (old_drop_path); - } - - if (dest->details->scroll_id == 0) - { - dest->details->scroll_id = - g_timeout_add (150, - scroll_timeout, - dest->details->tree_view); - } - - gdk_drag_status (context, action, time); - - return TRUE; -} - -static void -drag_leave_callback (GtkWidget *widget, - GdkDragContext *context, - guint32 time, - gpointer data) -{ - NautilusTreeViewDragDest *dest; - - dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data); - - clear_drag_dest_row (dest); - - free_drag_data (dest); - - remove_scroll_timeout (dest); -} - -static char * -get_drop_target_uri_at_pos (NautilusTreeViewDragDest *dest, - int x, - int y) -{ - char *drop_target = NULL; - GtkTreePath *path; - GtkTreePath *drop_path; - GtkTreeViewDropPosition pos; - - gtk_tree_view_get_dest_row_at_pos (dest->details->tree_view, x, y, - &path, &pos); - if (pos == GTK_TREE_VIEW_DROP_BEFORE || - pos == GTK_TREE_VIEW_DROP_AFTER) - { - gtk_tree_path_free (path); - path = NULL; - } - - drop_path = get_drop_path (dest, path); - - drop_target = get_drop_target_uri_for_path (dest, drop_path); - - if (path != NULL) - { - gtk_tree_path_free (path); - } - - if (drop_path != NULL) - { - gtk_tree_path_free (drop_path); - } - - return drop_target; -} - -static void -receive_uris (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - GList *source_uris, - int x, - int y) -{ - char *drop_target; - GdkDragAction action, real_action; - - drop_target = get_drop_target_uri_at_pos (dest, x, y); - g_assert (drop_target != NULL); - - real_action = gdk_drag_context_get_selected_action (context); - - if (real_action == GDK_ACTION_ASK) - { - action = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK; - real_action = nautilus_drag_drop_action_ask (GTK_WIDGET (dest->details->tree_view), action); - } - - /* We only want to copy external uris */ - if (dest->details->drag_type == NAUTILUS_ICON_DND_URI_LIST) - { - real_action = GDK_ACTION_COPY; - } - - if (real_action > 0) - { - if (!nautilus_drag_uris_local (drop_target, source_uris) - || real_action != GDK_ACTION_MOVE) - { - g_signal_emit (dest, signals[MOVE_COPY_ITEMS], 0, - source_uris, - drop_target, - real_action, - x, y); - } - } - - g_free (drop_target); -} - -static void -receive_dropped_icons (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - int x, - int y) -{ - GList *source_uris; - GList *l; - - /* FIXME: ignore local only moves */ - - if (!dest->details->drag_list) - { - return; - } - - source_uris = NULL; - for (l = dest->details->drag_list; l != NULL; l = l->next) - { - source_uris = g_list_prepend (source_uris, - ((NautilusDragSelectionItem *) l->data)->uri); - } - - source_uris = g_list_reverse (source_uris); - - receive_uris (dest, context, source_uris, x, y); - - g_list_free (source_uris); -} - -static void -receive_dropped_uri_list (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - int x, - int y) -{ - char *drop_target; - - if (!dest->details->drag_data) - { - return; - } - - drop_target = get_drop_target_uri_at_pos (dest, x, y); - g_assert (drop_target != NULL); - - g_signal_emit (dest, signals[HANDLE_URI_LIST], 0, - (char *) gtk_selection_data_get_data (dest->details->drag_data), - drop_target, - gdk_drag_context_get_selected_action (context), - x, y); - - g_free (drop_target); -} - -static void -receive_dropped_text (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - int x, - int y) -{ - char *drop_target; - guchar *text; - - if (!dest->details->drag_data) - { - return; - } - - drop_target = get_drop_target_uri_at_pos (dest, x, y); - g_assert (drop_target != NULL); - - text = gtk_selection_data_get_text (dest->details->drag_data); - g_signal_emit (dest, signals[HANDLE_TEXT], 0, - (char *) text, drop_target, - gdk_drag_context_get_selected_action (context), - x, y); - - g_free (text); - g_free (drop_target); -} - -static void -receive_dropped_raw (NautilusTreeViewDragDest *dest, - const char *raw_data, - int length, - GdkDragContext *context, - int x, - int y) -{ - char *drop_target; - - if (!dest->details->drag_data) - { - return; - } - - drop_target = get_drop_target_uri_at_pos (dest, x, y); - g_assert (drop_target != NULL); - - g_signal_emit (dest, signals[HANDLE_RAW], 0, - raw_data, length, drop_target, - dest->details->direct_save_uri, - gdk_drag_context_get_selected_action (context)); - - g_free (drop_target); -} - -static void -receive_dropped_netscape_url (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - int x, - int y) -{ - char *drop_target; - - if (!dest->details->drag_data) - { - return; - } - - drop_target = get_drop_target_uri_at_pos (dest, x, y); - g_assert (drop_target != NULL); - - g_signal_emit (dest, signals[HANDLE_NETSCAPE_URL], 0, - (char *) gtk_selection_data_get_data (dest->details->drag_data), - drop_target, - gdk_drag_context_get_selected_action (context), - x, y); - - g_free (drop_target); -} - -static gboolean -receive_xds (NautilusTreeViewDragDest *dest, - GtkWidget *widget, - guint32 time, - GdkDragContext *context, - int x, - int y) -{ - GFile *location; - const guchar *selection_data; - gint selection_format; - gint selection_length; - - selection_data = gtk_selection_data_get_data (dest->details->drag_data); - selection_format = gtk_selection_data_get_format (dest->details->drag_data); - selection_length = gtk_selection_data_get_length (dest->details->drag_data); - - if (selection_format == 8 - && selection_length == 1 - && selection_data[0] == 'F') - { - gtk_drag_get_data (widget, context, - gdk_atom_intern (NAUTILUS_ICON_DND_RAW_TYPE, - FALSE), - time); - return FALSE; - } - else if (selection_format == 8 - && selection_length == 1 - && selection_data[0] == 'S') - { - g_assert (dest->details->direct_save_uri != NULL); - location = g_file_new_for_uri (dest->details->direct_save_uri); - - nautilus_file_changes_queue_file_added (location); - nautilus_file_changes_consume_changes (TRUE); - - g_object_unref (location); - } - return TRUE; -} - - -static gboolean -drag_data_received_callback (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - GtkSelectionData *selection_data, - guint info, - guint32 time, - gpointer data) -{ - NautilusTreeViewDragDest *dest; - const gchar *tmp; - int length; - gboolean success, finished; - - dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data); - - if (!dest->details->have_drag_data) - { - dest->details->have_drag_data = TRUE; - dest->details->drag_type = info; - dest->details->drag_data = - gtk_selection_data_copy (selection_data); - if (info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) - { - dest->details->drag_list = - nautilus_drag_build_selection_list (selection_data); - } - } - - if (dest->details->drop_occurred) - { - success = FALSE; - finished = TRUE; - switch (info) - { - case NAUTILUS_ICON_DND_GNOME_ICON_LIST: - { - receive_dropped_icons (dest, context, x, y); - success = TRUE; - } - break; - - case NAUTILUS_ICON_DND_NETSCAPE_URL: - { - receive_dropped_netscape_url (dest, context, x, y); - success = TRUE; - } - break; - - case NAUTILUS_ICON_DND_URI_LIST: - { - receive_dropped_uri_list (dest, context, x, y); - success = TRUE; - } - break; - - case NAUTILUS_ICON_DND_TEXT: - { - receive_dropped_text (dest, context, x, y); - success = TRUE; - } - break; - - case NAUTILUS_ICON_DND_RAW: - { - length = gtk_selection_data_get_length (selection_data); - tmp = (const gchar *) gtk_selection_data_get_data (selection_data); - receive_dropped_raw (dest, tmp, length, context, x, y); - success = TRUE; - } - break; - - case NAUTILUS_ICON_DND_XDNDDIRECTSAVE: - { - finished = receive_xds (dest, widget, time, context, x, y); - success = TRUE; - } - break; - } - - if (finished) - { - dest->details->drop_occurred = FALSE; - free_drag_data (dest); - gtk_drag_finish (context, success, FALSE, time); - } - } - - /* appease GtkTreeView by preventing its drag_data_receive - * from being called */ - g_signal_stop_emission_by_name (dest->details->tree_view, - "drag-data-received"); - - return TRUE; -} - -static char * -get_direct_save_filename (GdkDragContext *context) -{ - guchar *prop_text; - gint prop_len; - - if (!gdk_property_get (gdk_drag_context_get_source_window (context), gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE), - gdk_atom_intern ("text/plain", FALSE), 0, 1024, FALSE, NULL, NULL, - &prop_len, &prop_text)) - { - return NULL; - } - - /* Zero-terminate the string */ - prop_text = g_realloc (prop_text, prop_len + 1); - prop_text[prop_len] = '\0'; - - /* Verify that the file name provided by the source is valid */ - if (*prop_text == '\0' || - strchr ((const gchar *) prop_text, G_DIR_SEPARATOR) != NULL) - { - DEBUG ("Invalid filename provided by XDS drag site"); - g_free (prop_text); - return NULL; - } - - return (gchar *) prop_text; -} - -static gboolean -set_direct_save_uri (NautilusTreeViewDragDest *dest, - GdkDragContext *context, - int x, - int y) -{ - GFile *base, *child; - char *drop_uri; - char *filename, *uri; - - g_assert (dest->details->direct_save_uri == NULL); - - uri = NULL; - - drop_uri = get_drop_target_uri_at_pos (dest, x, y); - if (drop_uri != NULL) - { - filename = get_direct_save_filename (context); - if (filename != NULL) - { - /* Resolve relative path */ - base = g_file_new_for_uri (drop_uri); - child = g_file_get_child (base, filename); - uri = g_file_get_uri (child); - - g_object_unref (base); - g_object_unref (child); - - /* Change the property */ - gdk_property_change (gdk_drag_context_get_source_window (context), - gdk_atom_intern (NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, FALSE), - gdk_atom_intern ("text/plain", FALSE), 8, - GDK_PROP_MODE_REPLACE, (const guchar *) uri, - strlen (uri)); - - dest->details->direct_save_uri = uri; - } - else - { - DEBUG ("Invalid filename provided by XDS drag site"); - } - } - else - { - DEBUG ("Could not retrieve XDS drop destination"); - } - - return uri != NULL; -} - -static gboolean -drag_drop_callback (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - guint32 time, - gpointer data) -{ - NautilusTreeViewDragDest *dest; - guint info; - GdkAtom target; - - dest = NAUTILUS_TREE_VIEW_DRAG_DEST (data); - - target = gtk_drag_dest_find_target (GTK_WIDGET (dest->details->tree_view), - context, - NULL); - if (target == GDK_NONE) - { - return FALSE; - } - - info = dest->details->drag_type; - - if (info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE) - { - /* We need to set this or get_drop_path will fail, and it - * was unset by drag_leave_callback */ - dest->details->have_drag_data = TRUE; - if (!set_direct_save_uri (dest, context, x, y)) - { - return FALSE; - } - dest->details->have_drag_data = FALSE; - } - - dest->details->drop_occurred = TRUE; - - get_drag_data (dest, context, time); - remove_scroll_timeout (dest); - clear_drag_dest_row (dest); - - return TRUE; -} - -static void -tree_view_weak_notify (gpointer user_data, - GObject *object) -{ - NautilusTreeViewDragDest *dest; - - dest = NAUTILUS_TREE_VIEW_DRAG_DEST (user_data); - - remove_scroll_timeout (dest); - - dest->details->tree_view = NULL; -} - -static void -nautilus_tree_view_drag_dest_dispose (GObject *object) -{ - NautilusTreeViewDragDest *dest; - - dest = NAUTILUS_TREE_VIEW_DRAG_DEST (object); - - if (dest->details->tree_view) - { - g_object_weak_unref (G_OBJECT (dest->details->tree_view), - tree_view_weak_notify, - dest); - } - - remove_scroll_timeout (dest); - - G_OBJECT_CLASS (nautilus_tree_view_drag_dest_parent_class)->dispose (object); -} - -static void -nautilus_tree_view_drag_dest_finalize (GObject *object) -{ - NautilusTreeViewDragDest *dest; - - dest = NAUTILUS_TREE_VIEW_DRAG_DEST (object); - free_drag_data (dest); - - G_OBJECT_CLASS (nautilus_tree_view_drag_dest_parent_class)->finalize (object); -} - -static void -nautilus_tree_view_drag_dest_init (NautilusTreeViewDragDest *dest) -{ - dest->details = G_TYPE_INSTANCE_GET_PRIVATE (dest, NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST, - NautilusTreeViewDragDestDetails); -} - -static void -nautilus_tree_view_drag_dest_class_init (NautilusTreeViewDragDestClass *class) -{ - GObjectClass *gobject_class; - - gobject_class = G_OBJECT_CLASS (class); - - gobject_class->dispose = nautilus_tree_view_drag_dest_dispose; - gobject_class->finalize = nautilus_tree_view_drag_dest_finalize; - - g_type_class_add_private (class, sizeof (NautilusTreeViewDragDestDetails)); - - signals[GET_ROOT_URI] = - g_signal_new ("get-root-uri", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - get_root_uri), - NULL, NULL, - g_cclosure_marshal_generic, - G_TYPE_STRING, 0); - signals[GET_FILE_FOR_PATH] = - g_signal_new ("get-file-for-path", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - get_file_for_path), - NULL, NULL, - g_cclosure_marshal_generic, - NAUTILUS_TYPE_FILE, 1, - GTK_TYPE_TREE_PATH); - signals[MOVE_COPY_ITEMS] = - g_signal_new ("move-copy-items", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - move_copy_items), - NULL, NULL, - - g_cclosure_marshal_generic, - G_TYPE_NONE, 3, - G_TYPE_POINTER, - G_TYPE_STRING, - GDK_TYPE_DRAG_ACTION); - signals[HANDLE_NETSCAPE_URL] = - g_signal_new ("handle-netscape-url", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - handle_netscape_url), - NULL, NULL, - g_cclosure_marshal_generic, - G_TYPE_NONE, 3, - G_TYPE_STRING, - G_TYPE_STRING, - GDK_TYPE_DRAG_ACTION); - signals[HANDLE_URI_LIST] = - g_signal_new ("handle-uri-list", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - handle_uri_list), - NULL, NULL, - g_cclosure_marshal_generic, - G_TYPE_NONE, 3, - G_TYPE_STRING, - G_TYPE_STRING, - GDK_TYPE_DRAG_ACTION); - signals[HANDLE_TEXT] = - g_signal_new ("handle-text", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - handle_text), - NULL, NULL, - g_cclosure_marshal_generic, - G_TYPE_NONE, 3, - G_TYPE_STRING, - G_TYPE_STRING, - GDK_TYPE_DRAG_ACTION); - signals[HANDLE_RAW] = - g_signal_new ("handle-raw", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - handle_raw), - NULL, NULL, - g_cclosure_marshal_generic, - G_TYPE_NONE, 5, - G_TYPE_POINTER, - G_TYPE_INT, - G_TYPE_STRING, - G_TYPE_STRING, - GDK_TYPE_DRAG_ACTION); - signals[HANDLE_HOVER] = - g_signal_new ("handle-hover", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (NautilusTreeViewDragDestClass, - handle_hover), - NULL, NULL, - g_cclosure_marshal_generic, - G_TYPE_NONE, 1, - G_TYPE_STRING); -} - - - -NautilusTreeViewDragDest * -nautilus_tree_view_drag_dest_new (GtkTreeView *tree_view) -{ - NautilusTreeViewDragDest *dest; - GtkTargetList *targets; - - dest = g_object_new (NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST, NULL); - - dest->details->tree_view = tree_view; - g_object_weak_ref (G_OBJECT (dest->details->tree_view), - tree_view_weak_notify, dest); - - gtk_drag_dest_set (GTK_WIDGET (tree_view), - 0, drag_types, G_N_ELEMENTS (drag_types), - GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK); - - targets = gtk_drag_dest_get_target_list (GTK_WIDGET (tree_view)); - gtk_target_list_add_text_targets (targets, NAUTILUS_ICON_DND_TEXT); - - g_signal_connect_object (tree_view, - "drag-motion", - G_CALLBACK (drag_motion_callback), - dest, 0); - g_signal_connect_object (tree_view, - "drag-leave", - G_CALLBACK (drag_leave_callback), - dest, 0); - g_signal_connect_object (tree_view, - "drag-drop", - G_CALLBACK (drag_drop_callback), - dest, 0); - g_signal_connect_object (tree_view, - "drag-data-received", - G_CALLBACK (drag_data_received_callback), - dest, 0); - - return dest; -} -#endif diff --git a/src/nautilus-tree-view-drag-dest.h b/src/nautilus-tree-view-drag-dest.h deleted file mode 100644 index 288eec34836ce66b5dda58d9828de3343127f57c..0000000000000000000000000000000000000000 --- a/src/nautilus-tree-view-drag-dest.h +++ /dev/null @@ -1,98 +0,0 @@ - -/* - * Nautilus - * - * Copyright (C) 2002 Sun Microsystems, 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 . - * - * Author: Dave Camp - */ - -/* nautilus-tree-view-drag-dest.h: Handles drag and drop for treeviews which - * contain a hierarchy of files - */ - -#pragma once - -#if 0 && NAUTILUS_DND_NEEDS_GTK4_REIMPLEMENTATION -#include - -#include "nautilus-file.h" - -G_BEGIN_DECLS - -#define NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST (nautilus_tree_view_drag_dest_get_type ()) -#define NAUTILUS_TREE_VIEW_DRAG_DEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST, NautilusTreeViewDragDest)) -#define NAUTILUS_TREE_VIEW_DRAG_DEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST, NautilusTreeViewDragDestClass)) -#define NAUTILUS_IS_TREE_VIEW_DRAG_DEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST)) -#define NAUTILUS_IS_TREE_VIEW_DRAG_DEST_CLASS(klass) (G_TYPE_CLASS_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_TREE_VIEW_DRAG_DEST)) - -typedef struct _NautilusTreeViewDragDest NautilusTreeViewDragDest; -typedef struct _NautilusTreeViewDragDestClass NautilusTreeViewDragDestClass; -typedef struct _NautilusTreeViewDragDestDetails NautilusTreeViewDragDestDetails; - -struct _NautilusTreeViewDragDest { - GObject parent; - - NautilusTreeViewDragDestDetails *details; -}; - -struct _NautilusTreeViewDragDestClass { - GObjectClass parent; - - char *(*get_root_uri) (NautilusTreeViewDragDest *dest); - NautilusFile *(*get_file_for_path) (NautilusTreeViewDragDest *dest, - GtkTreePath *path); - void (*move_copy_items) (NautilusTreeViewDragDest *dest, - const GList *item_uris, - const char *target_uri, - GdkDragAction action, - int x, - int y); - void (* handle_netscape_url) (NautilusTreeViewDragDest *dest, - const char *url, - const char *target_uri, - GdkDragAction action, - int x, - int y); - void (* handle_uri_list) (NautilusTreeViewDragDest *dest, - const char *uri_list, - const char *target_uri, - GdkDragAction action, - int x, - int y); - void (* handle_text) (NautilusTreeViewDragDest *dest, - const char *text, - const char *target_uri, - GdkDragAction action, - int x, - int y); - void (* handle_raw) (NautilusTreeViewDragDest *dest, - char *raw_data, - int length, - const char *target_uri, - const char *direct_save_uri, - GdkDragAction action, - int x, - int y); - void (* handle_hover) (NautilusTreeViewDragDest *dest, - const char *target_uri); -}; - -GType nautilus_tree_view_drag_dest_get_type (void); -NautilusTreeViewDragDest *nautilus_tree_view_drag_dest_new (GtkTreeView *tree_view); - -G_END_DECLS -#endif diff --git a/src/nautilus-types.h b/src/nautilus-types.h index 073a1576d5c781a609709760e8ff198715b8b4e5..1fe93544916ba1de1f17225ab2b3e6f29c712d8d 100644 --- a/src/nautilus-types.h +++ b/src/nautilus-types.h @@ -36,8 +36,9 @@ typedef struct _NautilusClipboard NautilusClipboard; typedef struct _NautilusDirectory NautilusDirectory; typedef struct NautilusFile NautilusFile; typedef struct NautilusFileQueue NautilusFileQueue; -typedef struct _NautilusFilesView NautilusFilesView; +typedef struct _NautilusFilesModelView NautilusFilesModelView; typedef struct _NautilusIconInfo NautilusIconInfo; +typedef struct _NautilusListBase NautilusListBase; typedef struct NautilusMonitor NautilusMonitor; typedef struct _NautilusQuery NautilusQuery; typedef struct _NautilusQueryEditor NautilusQueryEditor; diff --git a/src/nautilus-ui-utilities.c b/src/nautilus-ui-utilities.c index 1498488df5535d5816923bd31cdc36a93171dc2f..8e5933ee84c1c99882a3d4445603615845278879 100644 --- a/src/nautilus-ui-utilities.c +++ b/src/nautilus-ui-utilities.c @@ -127,7 +127,9 @@ nautilus_g_menu_replace_string_in_item (GMenu *menu, { g_autoptr (GMenuItem) item = NULL; + g_return_if_fail (i != -1); item = g_menu_item_new_from_model (G_MENU_MODEL (menu), i); + g_return_if_fail (item != NULL); g_menu_item_set_attribute (item, attribute, "s", string); g_menu_remove (menu, i); g_menu_insert_item (menu, i, item); diff --git a/src/nautilus-view-cell.c b/src/nautilus-view-cell.c new file mode 100644 index 0000000000000000000000000000000000000000..6f28fd8ff70f20ada9065913244bfc78f6f24a9a --- /dev/null +++ b/src/nautilus-view-cell.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2022 2022 António Fernandes + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "nautilus-view-cell.h" +#include "nautilus-list-base.h" + +/** + * NautilusViewCell: + * + * Abstract class of widgets tailored to be set as #GtkListItem:child in a view + * which subclasses #NautilusListBase. + * + * Subclass constructors should take a pointer to the #NautilusListBase view. + * + * The view is responsible for setting #NautilusViewCell:item. This can be done + * using a GBinding from #GtkListItem:item to #NautilusViewCell:item. + */ + +typedef struct _NautilusViewCellPrivate NautilusViewCellPrivate; +struct _NautilusViewCellPrivate +{ + AdwBin parent_instance; + + NautilusListBase *view; /* Unowned */ + NautilusViewItem *item; /* Owned reference */ + + gboolean called_once; +}; + + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusViewCell, nautilus_view_cell, ADW_TYPE_BIN) + +enum +{ + PROP_0, + PROP_VIEW, + PROP_ITEM, + N_PROPS +}; + +static GParamSpec *properties[N_PROPS] = { NULL, }; + +static void +nautilus_view_cell_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusViewCell *self = NAUTILUS_VIEW_CELL (object); + NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self); + + switch (prop_id) + { + case PROP_VIEW: + { + g_value_set_object (value, priv->view); + } + break; + + case PROP_ITEM: + { + g_value_set_object (value, priv->item); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +nautilus_view_cell_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusViewCell *self = NAUTILUS_VIEW_CELL (object); + NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self); + + switch (prop_id) + { + case PROP_VIEW: + { + priv->view = g_value_get_object (value); + } + break; + + case PROP_ITEM: + { + g_set_object (&priv->item, g_value_get_object (value)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +nautilus_view_cell_init (NautilusViewCell *self) +{ + gtk_widget_set_name (GTK_WIDGET (self), "NautilusViewCell"); +} + +static void +nautilus_view_cell_finalize (GObject *object) +{ + NautilusViewCell *self = NAUTILUS_VIEW_CELL (object); + NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self); + + g_clear_object (&priv->item); + + G_OBJECT_CLASS (nautilus_view_cell_parent_class)->finalize (object); +} + +static void +nautilus_view_cell_class_init (NautilusViewCellClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = nautilus_view_cell_finalize; + object_class->get_property = nautilus_view_cell_get_property; + object_class->set_property = nautilus_view_cell_set_property; + + properties[PROP_VIEW] = g_param_spec_object ("view", + "", "", + NAUTILUS_TYPE_LIST_BASE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + properties[PROP_ITEM] = g_param_spec_object ("item", + "", "", + NAUTILUS_TYPE_VIEW_ITEM, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +gboolean +nautilus_view_cell_once (NautilusViewCell *self) +{ + NautilusViewCellPrivate *priv = nautilus_view_cell_get_instance_private (self); + + if (priv->called_once) + { + return FALSE; + } + priv->called_once = TRUE; + + return TRUE; +} + +NautilusListBase * +nautilus_view_cell_get_view (NautilusViewCell *self) +{ + NautilusListBase *view; + + g_return_val_if_fail (NAUTILUS_IS_VIEW_CELL (self), NULL); + + g_object_get (self, "view", &view, NULL); + + return view; +} + +void +nautilus_view_cell_set_item (NautilusViewCell *self, + NautilusViewItem *item) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_CELL (self)); + g_return_if_fail (item == NULL || NAUTILUS_IS_VIEW_ITEM (item)); + + g_object_set (self, "item", item, NULL); +} + +NautilusViewItem * +nautilus_view_cell_get_item (NautilusViewCell *self) +{ + NautilusViewItem *item; + + g_return_val_if_fail (NAUTILUS_IS_VIEW_CELL (self), NULL); + + g_object_get (self, "item", &item, NULL); + + return item; +} diff --git a/src/nautilus-view-cell.h b/src/nautilus-view-cell.h new file mode 100644 index 0000000000000000000000000000000000000000..78297b82d22c09d15874af53102da72c0881fb3b --- /dev/null +++ b/src/nautilus-view-cell.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 António Fernandes + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "nautilus-types.h" +#include "nautilus-view-item.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_VIEW_CELL (nautilus_view_cell_get_type()) + +G_DECLARE_DERIVABLE_TYPE (NautilusViewCell, nautilus_view_cell, NAUTILUS, VIEW_CELL, AdwBin) + +struct _NautilusViewCellClass +{ + AdwBinClass parent_class; +}; + +NautilusListBase *nautilus_view_cell_get_view (NautilusViewCell *self); +void nautilus_view_cell_set_item (NautilusViewCell *self, + NautilusViewItem *item); +NautilusViewItem *nautilus_view_cell_get_item (NautilusViewCell *self); +gboolean nautilus_view_cell_once (NautilusViewCell *self); + +G_END_DECLS diff --git a/src/nautilus-view-icon-controller.c b/src/nautilus-view-icon-controller.c deleted file mode 100644 index 94feef1bc4e025aa409be27a2fa0654968c7f8f0..0000000000000000000000000000000000000000 --- a/src/nautilus-view-icon-controller.c +++ /dev/null @@ -1,1677 +0,0 @@ -#include "nautilus-view-icon-controller.h" -#include "nautilus-view-item-model.h" -#include "nautilus-view-icon-item-ui.h" -#include "nautilus-view-model.h" -#include "nautilus-files-view.h" -#include "nautilus-file.h" -#include "nautilus-metadata.h" -#include "nautilus-window-slot.h" -#include "nautilus-directory.h" -#include "nautilus-clipboard.h" -#include "nautilus-global-preferences.h" -#include "nautilus-thumbnails.h" - -struct _NautilusViewIconController -{ - NautilusFilesView parent_instance; - - GtkGridView *view_ui; - NautilusViewModel *model; - - GList *cut_files; - - GActionGroup *action_group; - gint zoom_level; - GQuark caption_attributes[NAUTILUS_VIEW_ICON_N_CAPTIONS]; - - gboolean single_click_mode; - gboolean activate_on_release; - gboolean deny_background_click; - - guint scroll_to_file_handle_id; - guint prioritize_thumbnailing_handle_id; - GtkAdjustment *vadjustment; -}; - -G_DEFINE_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS_TYPE_FILES_VIEW) - -typedef struct -{ - const NautilusFileSortType sort_type; - const gchar *metadata_name; - const gchar *action_target_name; - gboolean reversed; -} SortConstants; - -static const SortConstants sorts_constants[] = -{ - { - NAUTILUS_FILE_SORT_BY_DISPLAY_NAME, - "name", - "name", - FALSE, - }, - { - NAUTILUS_FILE_SORT_BY_DISPLAY_NAME, - "name", - "name-desc", - TRUE, - }, - { - NAUTILUS_FILE_SORT_BY_SIZE, - "size", - "size", - TRUE, - }, - { - NAUTILUS_FILE_SORT_BY_TYPE, - "type", - "type", - FALSE, - }, - { - NAUTILUS_FILE_SORT_BY_MTIME, - "modification date", - "modification-date", - FALSE, - }, - { - NAUTILUS_FILE_SORT_BY_MTIME, - "modification date", - "modification-date-desc", - TRUE, - }, - { - NAUTILUS_FILE_SORT_BY_ATIME, - "access date", - "access-date", - FALSE, - }, - { - NAUTILUS_FILE_SORT_BY_ATIME, - "access date", - "access-date-desc", - TRUE, - }, - { - NAUTILUS_FILE_SORT_BY_BTIME, - "creation date", - "creation-date", - FALSE, - }, - { - NAUTILUS_FILE_SORT_BY_BTIME, - "creation date", - "creation-date-desc", - TRUE, - }, - { - NAUTILUS_FILE_SORT_BY_TRASHED_TIME, - "trashed", - "trash-time", - TRUE, - }, - { - NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE, - "search_relevance", - "search-relevance", - TRUE, - } -}; - -static guint get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level); - -static const SortConstants * -get_sorts_constants_from_action_target_name (const gchar *action_target_name) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++) - { - if (g_strcmp0 (sorts_constants[i].action_target_name, action_target_name) == 0) - { - return &sorts_constants[i]; - } - } - - return &sorts_constants[0]; -} - -static const SortConstants * -get_sorts_constants_from_sort_type (NautilusFileSortType sort_type, - gboolean reversed) -{ - guint i; - - for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++) - { - if (sort_type == sorts_constants[i].sort_type - && reversed == sorts_constants[i].reversed) - { - return &sorts_constants[i]; - } - } - - return &sorts_constants[0]; -} - -static const SortConstants * -get_sorts_constants_from_metadata_text (const char *metadata_name, - gboolean reversed) -{ - guint i; - - for (i = 0; i < G_N_ELEMENTS (sorts_constants); i++) - { - if (g_strcmp0 (sorts_constants[i].metadata_name, metadata_name) == 0 - && reversed == sorts_constants[i].reversed) - { - return &sorts_constants[i]; - } - } - - return &sorts_constants[0]; -} - -static const SortConstants * -get_default_sort_order (NautilusFile *file) -{ - NautilusFileSortType sort_type; - gboolean reversed; - - sort_type = nautilus_file_get_default_sort_type (file, &reversed); - - return get_sorts_constants_from_sort_type (sort_type, reversed); -} - -static const SortConstants * -get_directory_sort_by (NautilusFile *file) -{ - const SortConstants *default_sort; - g_autofree char *sort_by = NULL; - gboolean reversed; - - default_sort = get_default_sort_order (file); - g_return_val_if_fail (default_sort != NULL, NULL); - - sort_by = nautilus_file_get_metadata (file, - NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY, - default_sort->metadata_name); - - reversed = nautilus_file_get_boolean_metadata (file, - NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED, - default_sort->reversed); - - return get_sorts_constants_from_metadata_text (sort_by, reversed); -} - -static void -set_directory_sort_metadata (NautilusFile *file, - const SortConstants *sort) -{ - const SortConstants *default_sort; - - default_sort = get_default_sort_order (file); - - nautilus_file_set_metadata (file, - NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY, - default_sort->metadata_name, - sort->metadata_name); - nautilus_file_set_boolean_metadata (file, - NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED, - default_sort->reversed, - sort->reversed); -} - -static void -update_sort_order_from_metadata_and_preferences (NautilusViewIconController *self) -{ - const SortConstants *default_directory_sort; - GActionGroup *view_action_group; - - default_directory_sort = get_directory_sort_by (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self))); - view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)); - g_action_group_change_action_state (view_action_group, - "sort", - g_variant_new_string (get_sorts_constants_from_sort_type (default_directory_sort->sort_type, default_directory_sort->reversed)->action_target_name)); -} - -static void -real_begin_loading (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - - /* TODO: This calls sort once, and update_context_menus calls update_actions which calls - * the action again - */ - update_sort_order_from_metadata_and_preferences (self); - - /*TODO move this to the files view class begin_loading and hook up? */ - - /* 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 (files_view); - nautilus_files_view_update_toolbar_menus (files_view); -} - -static void -real_clear (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - - nautilus_view_model_remove_all_items (self->model); -} - -static void -real_file_changed (NautilusFilesView *files_view, - NautilusFile *file, - NautilusDirectory *directory) -{ - NautilusViewIconController *self; - NautilusViewItemModel *item_model; - - self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - item_model = nautilus_view_model_get_item_from_file (self->model, file); - nautilus_view_item_model_file_changed (item_model); -} - -static GList * -real_get_selection (NautilusFilesView *files_view) -{ - NautilusViewIconController *self; - g_autoptr (GtkSelectionFilterModel) selection = NULL; - guint n_selected; - GList *selected_files = NULL; - - self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (self->model)); - n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection)); - for (guint i = 0; i < n_selected; i++) - { - g_autoptr (NautilusViewItemModel) item_model = NULL; - - item_model = g_list_model_get_item (G_LIST_MODEL (selection), i); - selected_files = g_list_prepend (selected_files, - g_object_ref (nautilus_view_item_model_get_file (item_model))); - } - - return selected_files; -} - -static gboolean -real_is_empty (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - - return g_list_model_get_n_items (G_LIST_MODEL (self->model)) == 0; -} - -static void -real_end_file_changes (NautilusFilesView *files_view) -{ -} - -static void -real_remove_file (NautilusFilesView *files_view, - NautilusFile *file, - NautilusDirectory *directory) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - NautilusViewItemModel *item_model; - - item_model = nautilus_view_model_get_item_from_file (self->model, file); - if (item_model != NULL) - { - nautilus_view_model_remove_item (self->model, item_model); - } -} - -static GQueue * -convert_glist_to_queue (GList *list) -{ - GList *l; - GQueue *queue; - - queue = g_queue_new (); - for (l = list; l != NULL; l = l->next) - { - g_queue_push_tail (queue, l->data); - } - - return queue; -} - -static GQueue * -convert_files_to_item_models (NautilusViewIconController *self, - GQueue *files) -{ - GList *l; - GQueue *models; - - models = g_queue_new (); - for (l = g_queue_peek_head_link (files); l != NULL; l = l->next) - { - NautilusViewItemModel *item_model; - - item_model = nautilus_view_item_model_new (NAUTILUS_FILE (l->data), - get_icon_size_for_zoom_level (self->zoom_level)); - g_queue_push_tail (models, item_model); - } - - return models; -} - -static void -real_set_selection (NautilusFilesView *files_view, - GList *selection) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - g_autoptr (GQueue) selection_files = NULL; - g_autoptr (GQueue) selection_item_models = NULL; - g_autoptr (GtkBitset) update_set = NULL; - g_autoptr (GtkBitset) selection_set = NULL; - - update_set = gtk_selection_model_get_selection (GTK_SELECTION_MODEL (self->model)); - selection_set = gtk_bitset_new_empty (); - - /* Convert file list into set of model indices */ - selection_files = convert_glist_to_queue (selection); - selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files); - for (GList *l = g_queue_peek_head_link (selection_item_models); l != NULL ; l = l->next) - { - gtk_bitset_add (selection_set, - nautilus_view_model_get_index (self->model, l->data)); - } - - gtk_bitset_union (update_set, selection_set); - gtk_selection_model_set_selection (GTK_SELECTION_MODEL (self->model), - selection_set, - update_set); -} - -static void -real_select_all (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - gtk_selection_model_select_all (GTK_SELECTION_MODEL (self->model)); -} - -static void -real_invert_selection (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->model); - g_autoptr (GtkBitset) selected = NULL; - g_autoptr (GtkBitset) all = NULL; - g_autoptr (GtkBitset) new_selected = NULL; - - selected = gtk_selection_model_get_selection (selection_model); - - /* We are going to flip the selection state of every item in the model. */ - all = gtk_bitset_new_range (0, g_list_model_get_n_items (G_LIST_MODEL (self->model))); - - /* The new selection is all items minus the ones currently selected. */ - new_selected = gtk_bitset_copy (all); - gtk_bitset_subtract (new_selected, selected); - - gtk_selection_model_set_selection (selection_model, new_selected, all); -} - -static guint -get_first_selected_item (NautilusViewIconController *self) -{ - g_autolist (NautilusFile) selection = NULL; - NautilusFile *file; - NautilusViewItemModel *item_model; - - selection = nautilus_view_get_selection (NAUTILUS_VIEW (self)); - if (selection == NULL) - { - return G_MAXUINT; - } - - file = NAUTILUS_FILE (selection->data); - item_model = nautilus_view_model_get_item_from_file (self->model, file); - - return nautilus_view_model_get_index (self->model, item_model); -} - -static void -real_reveal_selection (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - - gtk_widget_activate_action (GTK_WIDGET (self->view_ui), - "list.scroll-to-item", - "u", - get_first_selected_item (self)); -} - -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 -real_update_actions_state (NautilusFilesView *files_view) -{ - GAction *action; - GActionGroup *view_action_group; - - NAUTILUS_FILES_VIEW_CLASS (nautilus_view_icon_controller_parent_class)->update_actions_state (files_view); - - view_action_group = nautilus_files_view_get_action_group (files_view); - 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 (files_view) && - !showing_search_directory (files_view)); -} - -static void -real_bump_zoom_level (NautilusFilesView *files_view, - int zoom_increment) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - NautilusGridZoomLevel new_level; - - new_level = self->zoom_level + zoom_increment; - - if (new_level >= NAUTILUS_GRID_ZOOM_LEVEL_SMALL && - new_level <= NAUTILUS_GRID_ZOOM_LEVEL_LARGEST) - { - g_action_group_change_action_state (self->action_group, - "zoom-to-level", - g_variant_new_int32 (new_level)); - } -} - -static guint -get_icon_size_for_zoom_level (NautilusGridZoomLevel zoom_level) -{ - switch (zoom_level) - { - case NAUTILUS_GRID_ZOOM_LEVEL_SMALL: - { - return NAUTILUS_GRID_ICON_SIZE_SMALL; - } - break; - - case NAUTILUS_GRID_ZOOM_LEVEL_STANDARD: - { - return NAUTILUS_GRID_ICON_SIZE_STANDARD; - } - break; - - case NAUTILUS_GRID_ZOOM_LEVEL_LARGE: - { - return NAUTILUS_GRID_ICON_SIZE_LARGE; - } - break; - - case NAUTILUS_GRID_ZOOM_LEVEL_LARGER: - { - return NAUTILUS_GRID_ICON_SIZE_LARGER; - } - break; - - case NAUTILUS_GRID_ZOOM_LEVEL_LARGEST: - { - return NAUTILUS_GRID_ICON_SIZE_LARGEST; - } - break; - } - g_return_val_if_reached (NAUTILUS_GRID_ICON_SIZE_STANDARD); -} - -static gint -get_default_zoom_level (void) -{ - NautilusGridZoomLevel default_zoom_level; - - default_zoom_level = g_settings_get_enum (nautilus_icon_view_preferences, - NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL); - - return default_zoom_level; -} - -static void -set_captions_from_preferences (NautilusViewIconController *self) -{ - g_auto (GStrv) value = NULL; - gint n_captions_for_zoom_level; - - value = g_settings_get_strv (nautilus_icon_view_preferences, - NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS); - - /* Set a celling on the number of captions depending on the zoom level. */ - n_captions_for_zoom_level = MIN (self->zoom_level, - G_N_ELEMENTS (self->caption_attributes)); - - /* Reset array to zeros beforehand, as we may not refill all elements. */ - memset (&self->caption_attributes, 0, sizeof (self->caption_attributes)); - for (gint i = 0, quark_i = 0; - value[i] != NULL && quark_i < n_captions_for_zoom_level; - i++) - { - if (g_strcmp0 (value[i], "none") == 0) - { - continue; - } - - /* Convert to quarks in advance, otherwise each NautilusFile attribute - * getter would call g_quark_from_string() once for each file. */ - self->caption_attributes[quark_i] = g_quark_from_string (value[i]); - quark_i++; - } -} - -static void -set_icon_size (NautilusViewIconController *self, - gint icon_size) -{ - guint n_items; - - n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model)); - for (guint i = 0; i < n_items; i++) - { - g_autoptr (NautilusViewItemModel) current_item_model = NULL; - - current_item_model = g_list_model_get_item (G_LIST_MODEL (self->model), i); - nautilus_view_item_model_set_icon_size (current_item_model, - get_icon_size_for_zoom_level (self->zoom_level)); - } -} - -static void -set_zoom_level (NautilusViewIconController *self, - guint new_level) -{ - self->zoom_level = new_level; - - /* The zoom level may change how many captions are allowed. Update it before - * setting the icon size, under the assumption that NautilusViewIconItemUi - * updates captions whenever the icon size is set*/ - set_captions_from_preferences (self); - - set_icon_size (self, get_icon_size_for_zoom_level (new_level)); - - nautilus_files_view_update_toolbar_menus (NAUTILUS_FILES_VIEW (self)); -} - -static void -real_restore_standard_zoom_level (NautilusFilesView *files_view) -{ - NautilusViewIconController *self; - - self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - g_action_group_change_action_state (self->action_group, - "zoom-to-level", - g_variant_new_int32 (NAUTILUS_GRID_ZOOM_LEVEL_LARGE)); -} - -static gboolean -real_is_zoom_level_default (NautilusFilesView *files_view) -{ - NautilusViewIconController *self; - guint icon_size; - - self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - icon_size = get_icon_size_for_zoom_level (self->zoom_level); - - return icon_size == NAUTILUS_GRID_ICON_SIZE_LARGE; -} - -static gboolean -real_can_zoom_in (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - - return self->zoom_level < NAUTILUS_GRID_ZOOM_LEVEL_LARGEST; -} - -static gboolean -real_can_zoom_out (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - - return self->zoom_level > NAUTILUS_GRID_ZOOM_LEVEL_SMALL; -} - -static GdkRectangle * -get_rectangle_for_item_ui (NautilusViewIconController *self, - GtkWidget *item_ui) -{ - GdkRectangle *rectangle; - GtkWidget *content_widget; - gdouble view_x; - gdouble view_y; - - rectangle = g_new0 (GdkRectangle, 1); - gtk_widget_get_allocation (item_ui, rectangle); - - content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self)); - gtk_widget_translate_coordinates (item_ui, content_widget, - rectangle->x, rectangle->y, - &view_x, &view_y); - rectangle->x = view_x; - rectangle->y = view_y; - - return rectangle; -} - -static GdkRectangle * -real_compute_rename_popover_pointing_to (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - g_autoptr (NautilusViewItemModel) item = NULL; - GtkWidget *item_ui; - - /* We only allow one item to be renamed with a popover */ - item = g_list_model_get_item (G_LIST_MODEL (self->model), - get_first_selected_item (self)); - item_ui = nautilus_view_item_model_get_item_ui (item); - g_return_val_if_fail (item_ui != NULL, NULL); - - return get_rectangle_for_item_ui (self, item_ui); -} - -static GdkRectangle * -real_reveal_for_selection_context_menu (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - g_autoptr (GtkSelectionFilterModel) selection = NULL; - guint n_selected; - GtkWidget *focus_child; - guint i; - GtkWidget *item_ui; - - selection = gtk_selection_filter_model_new (GTK_SELECTION_MODEL (self->model)); - n_selected = g_list_model_get_n_items (G_LIST_MODEL (selection)); - g_return_val_if_fail (n_selected > 0, NULL); - - /* Get the focused item_ui, if selected. - * Otherwise, get the selected item_ui which is sorted the lowest.*/ - focus_child = gtk_widget_get_focus_child (GTK_WIDGET (self->view_ui)); - for (i = 0; i < n_selected; i++) - { - g_autoptr (NautilusViewItemModel) item = NULL; - - item = g_list_model_get_item (G_LIST_MODEL (selection), i); - item_ui = nautilus_view_item_model_get_item_ui (item); - if (item_ui != NULL && gtk_widget_get_parent (item_ui) == focus_child) - { - break; - } - } - - gtk_widget_activate_action (GTK_WIDGET (self->view_ui), - "list.scroll-to-item", - "u", - i); - - return get_rectangle_for_item_ui (self, item_ui); -} - -static void -set_click_mode_from_settings (NautilusViewIconController *self) -{ - int click_policy; - - click_policy = g_settings_get_enum (nautilus_preferences, - NAUTILUS_PREFERENCES_CLICK_POLICY); - - self->single_click_mode = (click_policy == NAUTILUS_CLICK_POLICY_SINGLE); -} - -static void -real_click_policy_changed (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - set_click_mode_from_settings (self); -} - -/* GtkListBase changes selection only with the primary button, and only after - * release. But we need to antecipate selection earlier if we are to activate it - * or open its context menu. This helper should be used in these situations if - * it's desirable to act on a multi-item selection, because it preserves it. */ -static void -select_single_item_if_not_selected (NautilusViewIconController *self, - NautilusViewItemModel *item) -{ - GtkSelectionModel *selection_model = GTK_SELECTION_MODEL (self->model); - guint position; - - position = nautilus_view_model_get_index (self->model, item); - if (!gtk_selection_model_is_selected (selection_model, position)) - { - gtk_selection_model_select_item (selection_model, position, TRUE); - } -} - -static void -activate_selection_on_click (NautilusViewIconController *self, - gboolean open_in_new_tab) -{ - g_autolist (NautilusFile) selection = NULL; - NautilusOpenFlags flags = 0; - NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (self); - - selection = nautilus_view_get_selection (NAUTILUS_VIEW (self)); - if (open_in_new_tab) - { - flags |= NAUTILUS_OPEN_FLAG_NEW_TAB; - flags |= NAUTILUS_OPEN_FLAG_DONT_MAKE_ACTIVE; - } - nautilus_files_view_activate_files (files_view, selection, flags, TRUE); -} - -/* We only care about the keyboard activation part that GtkGridView provides, - * but we don't need any special filtering here. Indeed, we ask GtkGridView - * to not activate on single click, and we get to handle double clicks before - * GtkGridView does (as one of widget subclassing's goal is to modify the parent - * class's behavior), while claiming the click gestures, so it means GtkGridView - * will never react to a click event to emit this signal. So we should be pretty - * safe here with regards to our custom item click handling. - */ -static void -on_grid_view_item_activated (GtkGridView *grid_view, - guint position, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - - nautilus_files_view_activate_selection (NAUTILUS_FILES_VIEW (self)); -} - -static void -on_item_click_pressed (GtkGestureClick *gesture, - gint n_press, - gdouble x, - gdouble y, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - GtkWidget *event_widget; - NautilusViewItemModel *item_model; - guint button; - GdkModifierType modifiers; - gboolean selection_mode; - - event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); - item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (event_widget)); - button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture)); - selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)); - - /* Before anything else, store event state to be read by other handlers. */ - self->deny_background_click = TRUE; - self->activate_on_release = (self->single_click_mode && - button == GDK_BUTTON_PRIMARY && - n_press == 1 && - !selection_mode); - - /* It's safe to claim event sequence on press in the following cases because - * they don't interfere with touch scrolling. */ - if (button == GDK_BUTTON_PRIMARY && n_press == 2 && !self->single_click_mode) - { - activate_selection_on_click (self, modifiers & GDK_SHIFT_MASK); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - } - else if (button == GDK_BUTTON_MIDDLE && n_press == 1) - { - /* Antecipate selection, if necessary, to activate it. */ - select_single_item_if_not_selected (self, item_model); - activate_selection_on_click (self, TRUE); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - } - else if (button == GDK_BUTTON_SECONDARY && n_press == 1) - { - gdouble view_x, view_y; - - /* Antecipate selection, if necessary, for the context menu. */ - select_single_item_if_not_selected (self, item_model); - - gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self), - x, y, - &view_x, &view_y); - nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self), - view_x, view_y); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - } -} - -static void -on_item_click_released (GtkGestureClick *gesture, - gint n_press, - gdouble x, - gdouble y, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - - if (self->activate_on_release) - { - GtkWidget *event_widget; - NautilusViewItemModel *item_model; - guint i; - - event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); - item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (event_widget)); - i = nautilus_view_model_get_index (self->model, item_model); - - /* Antecipate selection, enforcing single selection of target item. */ - gtk_selection_model_select_item (GTK_SELECTION_MODEL (self->model), i, TRUE); - - activate_selection_on_click (self, FALSE); - } - - self->activate_on_release = FALSE; - self->deny_background_click = FALSE; -} - -static void -on_item_click_stopped (GtkGestureClick *gesture, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - - self->activate_on_release = FALSE; - self->deny_background_click = FALSE; -} - -static void -on_view_click_pressed (GtkGestureClick *gesture, - gint n_press, - gdouble x, - gdouble y, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - guint button; - GdkModifierType modifiers; - gboolean selection_mode; - - if (self->deny_background_click) - { - /* Item was clicked. */ - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED); - return; - } - - /* Don't interfere with GtkGridView default selection handling when - * holding Ctrl and Shift. */ - modifiers = gtk_event_controller_get_current_event_state (GTK_EVENT_CONTROLLER (gesture)); - selection_mode = (modifiers & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)); - if (!selection_mode) - { - nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL); - } - - button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture)); - if (button == GDK_BUTTON_SECONDARY) - { - GtkWidget *event_widget; - gdouble view_x; - gdouble view_y; - - event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); - gtk_widget_translate_coordinates (event_widget, GTK_WIDGET (self), - x, y, - &view_x, &view_y); - nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self), - view_x, view_y); - } -} - -static void -on_longpress_gesture_pressed_callback (GtkGestureLongPress *gesture, - gdouble x, - gdouble y, - gpointer user_data) -{ - NautilusViewIconController *self; - GtkWidget *event_widget; - gdouble view_x; - gdouble view_y; - - self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - event_widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); - - gtk_widget_translate_coordinates (event_widget, - GTK_WIDGET (self), - x, y, &view_x, &view_y); - if (NAUTILUS_IS_VIEW_ICON_ITEM_UI (event_widget)) - { - nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self), - view_x, view_y); - gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED); - } - else - { - nautilus_view_set_selection (NAUTILUS_VIEW (self), NULL); - nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (self), - view_x, view_y); - } -} - -static int -real_compare_files (NautilusFilesView *files_view, - NautilusFile *file1, - NautilusFile *file2) -{ - GActionGroup *view_action_group; - GAction *action; - const gchar *target_name; - const SortConstants *sort_constants; - gboolean directories_first; - - view_action_group = nautilus_files_view_get_action_group (files_view); - action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group), "sort"); - target_name = g_variant_get_string (g_action_get_state (action), NULL); - sort_constants = get_sorts_constants_from_action_target_name (target_name); - directories_first = nautilus_files_view_should_sort_directories_first (files_view); - - return nautilus_file_compare_for_sort (file1, file2, - sort_constants->sort_type, - directories_first, - sort_constants->reversed); -} - -static void -on_clipboard_contents_received (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - NautilusFilesView *files_view = NAUTILUS_FILES_VIEW (source_object); - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - NautilusClipboard *clip; - NautilusViewItemModel *item; - - for (GList *l = self->cut_files; l != NULL; l = l->next) - { - item = nautilus_view_model_get_item_from_file (self->model, l->data); - if (item != NULL) - { - nautilus_view_item_model_set_cut (item, FALSE); - } - } - g_clear_list (&self->cut_files, g_object_unref); - - clip = nautilus_files_view_get_clipboard_finish (files_view, res, NULL); - if (clip != NULL && nautilus_clipboard_is_cut (clip)) - { - self->cut_files = g_list_copy_deep (nautilus_clipboard_peek_files (clip), - (GCopyFunc) g_object_ref, - NULL); - } - - for (GList *l = self->cut_files; l != NULL; l = l->next) - { - item = nautilus_view_model_get_item_from_file (self->model, l->data); - if (item != NULL) - { - nautilus_view_item_model_set_cut (item, TRUE); - } - } -} - -static void -update_clipboard_status (NautilusFilesView *files_view) -{ - nautilus_files_view_get_clipboard_async (files_view, - on_clipboard_contents_received, - NULL); -} - -static void -on_clipboard_owner_changed (GdkClipboard *clipboard, - gpointer user_data) -{ - update_clipboard_status (NAUTILUS_FILES_VIEW (user_data)); -} - - -static void -real_end_loading (NautilusFilesView *files_view, - gboolean all_files_seen) -{ - update_clipboard_status (files_view); -} - -static guint -get_first_visible_item (NautilusViewIconController *self) -{ - guint n_items; - gdouble scrolled_y; - - n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model)); - scrolled_y = gtk_adjustment_get_value (self->vadjustment); - for (guint i = 0; i < n_items; i++) - { - g_autoptr (NautilusViewItemModel) item = NULL; - GtkWidget *item_ui; - - item = g_list_model_get_item (G_LIST_MODEL (self->model), i); - item_ui = nautilus_view_item_model_get_item_ui (item); - if (item_ui != NULL) - { - gdouble y; - - gtk_widget_translate_coordinates (item_ui, GTK_WIDGET (self->view_ui), - 0, 0, NULL, &y); - if (gtk_widget_is_visible (item_ui) && y >= scrolled_y) - { - return i; - } - } - } - - return G_MAXUINT; -} - -static char * -real_get_first_visible_file (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - guint i; - g_autoptr (NautilusViewItemModel) item = NULL; - gchar *uri = NULL; - - i = get_first_visible_item (self); - if (i < G_MAXUINT) - { - item = g_list_model_get_item (G_LIST_MODEL (self->model), i); - uri = nautilus_file_get_uri (nautilus_view_item_model_get_file (item)); - } - return uri; -} - -typedef struct -{ - NautilusViewIconController *view; - char *uri; -} ScrollToFileData; - -static void -scroll_to_file_data_free (ScrollToFileData *data) -{ - g_free (data->uri); - g_free (data); -} - -static gboolean -scroll_to_file_on_idle (ScrollToFileData *data) -{ - NautilusViewIconController *self = data->view; - g_autoptr (NautilusFile) file = NULL; - NautilusViewItemModel *item; - guint i; - - file = nautilus_file_get_existing_by_uri (data->uri); - item = nautilus_view_model_get_item_from_file (self->model, file); - i = nautilus_view_model_get_index (self->model, item); - - gtk_widget_activate_action (GTK_WIDGET (self->view_ui), - "list.scroll-to-item", - "u", - i); - - self->scroll_to_file_handle_id = 0; - return G_SOURCE_REMOVE; -} - -static void -real_scroll_to_file (NautilusFilesView *files_view, - const char *uri) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - ScrollToFileData *data; - guint handle_id; - - /* Not exactly sure why, but the child widgets are not yet realized when - * this is usually called (which is when view finishes loading. Maybe - * because GtkFlowBox only generates children at the next GMainContext - * iteration? Anyway, doing it on idle as low priority works. */ - - data = g_new (ScrollToFileData, 1); - data->view = self; - data->uri = g_strdup (uri); - handle_id = g_idle_add_full (G_PRIORITY_LOW, - (GSourceFunc) scroll_to_file_on_idle, - data, - (GDestroyNotify) scroll_to_file_data_free); - self->scroll_to_file_handle_id = handle_id; -} - -static void -real_sort_directories_first_changed (NautilusFilesView *files_view) -{ - NautilusViewModelSortData sort_data; - NautilusViewModelSortData *current_sort_data; - NautilusViewIconController *self; - - self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - current_sort_data = nautilus_view_model_get_sort_type (self->model); - sort_data.sort_type = current_sort_data->sort_type; - sort_data.reversed = current_sort_data->reversed; - sort_data.directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self)); - - nautilus_view_model_set_sort_type (self->model, &sort_data); -} - -static void -action_sort_order_changed (GSimpleAction *action, - GVariant *value, - gpointer user_data) -{ - const gchar *target_name; - const SortConstants *sort_constants; - NautilusViewModelSortData sort_data; - NautilusViewIconController *self; - - /* Don't resort if the action is in the same state as before */ - if (g_strcmp0 (g_variant_get_string (value, NULL), g_variant_get_string (g_action_get_state (G_ACTION (action)), NULL)) == 0) - { - return; - } - - self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - target_name = g_variant_get_string (value, NULL); - sort_constants = get_sorts_constants_from_action_target_name (target_name); - sort_data.sort_type = sort_constants->sort_type; - sort_data.reversed = sort_constants->reversed; - sort_data.directories_first = nautilus_files_view_should_sort_directories_first (NAUTILUS_FILES_VIEW (self)); - - nautilus_view_model_set_sort_type (self->model, &sort_data); - set_directory_sort_metadata (nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (self)), - sort_constants); - - g_simple_action_set_state (action, value); -} - -static void -real_add_files (NautilusFilesView *files_view, - GList *files) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - g_autoptr (GQueue) files_queue = NULL; - g_autoptr (GQueue) item_models = NULL; - gdouble adjustment_value; - - files_queue = convert_glist_to_queue (files); - item_models = convert_files_to_item_models (self, files_queue); - nautilus_view_model_add_items (self->model, item_models); - - /* GtkListBase anchoring doesn't cope well with our lazy loading. - * Assuming that GtkListBase|list.scroll-to-item resets the anchor to 0, use - * that as a workaround to prevent scrolling while we are at the top. */ - adjustment_value = gtk_adjustment_get_value (self->vadjustment); - if (G_APPROX_VALUE (adjustment_value, 0.0, DBL_EPSILON)) - { - gtk_widget_activate_action (GTK_WIDGET (self->view_ui), "list.scroll-to-item", "u", 0); - } -} - - -static guint -real_get_view_id (NautilusFilesView *files_view) -{ - return NAUTILUS_VIEW_GRID_ID; -} - -static void -real_select_first (NautilusFilesView *files_view) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - g_autoptr (NautilusViewItemModel) item = NULL; - NautilusFile *file; - g_autoptr (GList) selection = NULL; - - item = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL (self->model), 0)); - if (item == NULL) - { - return; - } - file = nautilus_view_item_model_get_file (item); - selection = g_list_prepend (selection, file); - nautilus_view_set_selection (NAUTILUS_VIEW (files_view), selection); -} - -static void -real_preview_selection_event (NautilusFilesView *files_view, - GtkDirectionType direction) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); - GtkMovementStep step; - gint count; - gboolean handled; - - step = (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN) ? - GTK_MOVEMENT_DISPLAY_LINES : GTK_MOVEMENT_VISUAL_POSITIONS; - count = (direction == GTK_DIR_RIGHT || direction == GTK_DIR_DOWN) ? - 1 : -1; - - g_signal_emit_by_name (self->view_ui, "move-cursor", step, count, &handled); -} - -static void -action_zoom_to_level (GSimpleAction *action, - GVariant *state, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - int zoom_level; - - zoom_level = g_variant_get_int32 (state); - set_zoom_level (self, zoom_level); - g_simple_action_set_state (G_SIMPLE_ACTION (action), state); - - if (g_settings_get_enum (nautilus_icon_view_preferences, - NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level) - { - g_settings_set_enum (nautilus_icon_view_preferences, - NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_ZOOM_LEVEL, - zoom_level); - } -} - -static void -on_captions_preferences_changed (NautilusViewIconController *self) -{ - set_captions_from_preferences (self); - - /* Hack: this relies on the assumption that NautilusViewIconItemUi updates - * captions whenever the icon size is set (even if it's the same value). */ - set_icon_size (self, get_icon_size_for_zoom_level (self->zoom_level)); -} - -static void -on_default_sort_order_changed (NautilusViewIconController *self) -{ - update_sort_order_from_metadata_and_preferences (self); -} - -static void -dispose (GObject *object) -{ - NautilusViewIconController *self; - - self = NAUTILUS_VIEW_ICON_CONTROLLER (object); - - g_clear_handle_id (&self->scroll_to_file_handle_id, g_source_remove); - g_clear_handle_id (&self->prioritize_thumbnailing_handle_id, g_source_remove); - - g_signal_handlers_disconnect_by_data (nautilus_preferences, self); - - G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object); - - g_clear_list (&self->cut_files, g_object_unref); - - G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->finalize (object); -} - -static gboolean -prioritize_thumbnailing_on_idle (NautilusViewIconController *self) -{ - gdouble page_size; - GtkWidget *first_visible_child; - GtkWidget *next_child; - guint first_index; - guint next_index; - gdouble y; - guint last_index; - g_autoptr (NautilusViewItemModel) first_item = NULL; - NautilusFile *file; - - self->prioritize_thumbnailing_handle_id = 0; - - page_size = gtk_adjustment_get_page_size (self->vadjustment); - first_index = get_first_visible_item (self); - if (first_index == G_MAXUINT) - { - return G_SOURCE_REMOVE; - } - - first_item = g_list_model_get_item (G_LIST_MODEL (self->model), first_index); - - first_visible_child = nautilus_view_item_model_get_item_ui (first_item); - - for (next_index = first_index + 1; next_index < g_list_model_get_n_items (G_LIST_MODEL (self->model)); next_index++) - { - g_autoptr (NautilusViewItemModel) next_item = NULL; - - next_item = g_list_model_get_item (G_LIST_MODEL (self->model), next_index); - next_child = nautilus_view_item_model_get_item_ui (next_item); - if (next_child == NULL) - { - break; - } - if (gtk_widget_translate_coordinates (next_child, first_visible_child, - 0, 0, NULL, &y)) - { - if (y > page_size) - { - break; - } - } - } - last_index = next_index - 1; - - /* Do the iteration in reverse to give higher priority to the top */ - for (gint i = 0; i <= last_index - first_index; i++) - { - g_autoptr (NautilusViewItemModel) item = NULL; - - item = g_list_model_get_item (G_LIST_MODEL (self->model), last_index - i); - g_return_val_if_fail (item != NULL, G_SOURCE_REMOVE); - - file = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL (item)); - if (file != NULL && nautilus_file_is_thumbnailing (file)) - { - g_autofree gchar *uri = nautilus_file_get_uri (file); - nautilus_thumbnail_prioritize (uri); - } - } - - return G_SOURCE_REMOVE; -} - -static void -on_vadjustment_changed (GtkAdjustment *adjustment, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - guint handle_id; - - /* Schedule on idle to rate limit and to avoid delaying scrolling. */ - if (self->prioritize_thumbnailing_handle_id == 0) - { - handle_id = g_idle_add ((GSourceFunc) prioritize_thumbnailing_on_idle, self); - self->prioritize_thumbnailing_handle_id = handle_id; - } -} - -static void -bind_item_ui (GtkSignalListItemFactory *factory, - GtkListItem *listitem, - gpointer user_data) -{ - GtkWidget *item_ui; - NautilusViewItemModel *item_model; - - item_ui = gtk_list_item_get_child (listitem); - item_model = NAUTILUS_VIEW_ITEM_MODEL (gtk_list_item_get_item (listitem)); - - nautilus_view_icon_item_ui_set_model (NAUTILUS_VIEW_ICON_ITEM_UI (item_ui), - item_model); - nautilus_view_item_model_set_item_ui (item_model, item_ui); - - if (nautilus_view_icon_item_ui_once (NAUTILUS_VIEW_ICON_ITEM_UI (item_ui))) - { - GtkWidget *parent; - - /* At the time of ::setup emission, the item ui has got no parent yet, - * that's why we need to complete the widget setup process here, on the - * first time ::bind is emitted. */ - parent = gtk_widget_get_parent (item_ui); - gtk_widget_set_halign (parent, GTK_ALIGN_CENTER); - gtk_widget_set_valign (parent, GTK_ALIGN_START); - gtk_widget_set_margin_top (parent, 3); - gtk_widget_set_margin_bottom (parent, 3); - gtk_widget_set_margin_start (parent, 3); - gtk_widget_set_margin_end (parent, 3); - } -} - -static void -unbind_item_ui (GtkSignalListItemFactory *factory, - GtkListItem *listitem, - gpointer user_data) -{ - NautilusViewIconItemUi *item_ui; - NautilusViewItemModel *item_model; - - item_ui = NAUTILUS_VIEW_ICON_ITEM_UI (gtk_list_item_get_child (listitem)); - item_model = NAUTILUS_VIEW_ITEM_MODEL (gtk_list_item_get_item (listitem)); - - nautilus_view_icon_item_ui_set_model (item_ui, NULL); - nautilus_view_item_model_set_item_ui (item_model, NULL); -} - -static void -setup_item_ui (GtkSignalListItemFactory *factory, - GtkListItem *listitem, - gpointer user_data) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); - NautilusViewIconItemUi *item_ui; - GtkEventController *controller; - - item_ui = nautilus_view_icon_item_ui_new (); - nautilus_view_item_ui_set_caption_attributes (item_ui, self->caption_attributes); - gtk_list_item_set_child (listitem, GTK_WIDGET (item_ui)); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); - gtk_widget_add_controller (GTK_WIDGET (item_ui), controller); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0); - g_signal_connect (controller, "pressed", G_CALLBACK (on_item_click_pressed), self); - g_signal_connect (controller, "released", G_CALLBACK (on_item_click_released), self); - g_signal_connect (controller, "stopped", G_CALLBACK (on_item_click_stopped), self); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ()); - gtk_widget_add_controller (GTK_WIDGET (item_ui), controller); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE); - g_signal_connect (controller, "pressed", G_CALLBACK (on_longpress_gesture_pressed_callback), self); -} - -static GtkGridView * -create_view_ui (NautilusViewIconController *self) -{ - GtkListItemFactory *factory; - GtkWidget *widget; - - factory = gtk_signal_list_item_factory_new (); - g_signal_connect (factory, "setup", G_CALLBACK (setup_item_ui), self); - g_signal_connect (factory, "bind", G_CALLBACK (bind_item_ui), self); - g_signal_connect (factory, "unbind", G_CALLBACK (unbind_item_ui), self); - - widget = gtk_grid_view_new (GTK_SELECTION_MODEL (self->model), factory); - gtk_widget_set_focusable (widget, TRUE); - gtk_widget_set_valign (widget, GTK_ALIGN_START); - - /* We don't use the built-in child activation feature for clicks because it - * doesn't fill all our needs nor does it match our expected behavior. - * Instead, we roll our own event handling and double/single click mode. - * However, GtkGridView:single-click-activate has other effects besides - * activation, as it affects the selection behavior as well (e.g. selects on - * hover). Setting it to FALSE gives us the expected behavior. */ - gtk_grid_view_set_single_click_activate (GTK_GRID_VIEW (widget), FALSE); - gtk_grid_view_set_max_columns (GTK_GRID_VIEW (widget), 20); - gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (widget), TRUE); - - /* While we don't want to use GTK's click activation, we'll let it handle - * the key activation part (with Enter). - */ - g_signal_connect (widget, "activate", G_CALLBACK (on_grid_view_item_activated), self); - - return GTK_GRID_VIEW (widget); -} - -const GActionEntry view_icon_actions[] = -{ - { "sort", NULL, "s", "'invalid'", action_sort_order_changed }, - { "zoom-to-level", NULL, NULL, "100", action_zoom_to_level } -}; - -static void -constructed (GObject *object) -{ - NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (object); - GtkWidget *content_widget; - GtkAdjustment *vadjustment; - GActionGroup *view_action_group; - GtkEventController *controller; - - content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self)); - vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (content_widget)); - - self->vadjustment = vadjustment; - g_signal_connect (vadjustment, "changed", (GCallback) on_vadjustment_changed, self); - g_signal_connect (vadjustment, "value-changed", (GCallback) on_vadjustment_changed, self); - - self->model = nautilus_view_model_new (); - - self->view_ui = create_view_ui (self); - gtk_widget_show (GTK_WIDGET (self->view_ui)); - - g_signal_connect_swapped (GTK_SELECTION_MODEL (self->model), - "selection-changed", - G_CALLBACK (nautilus_files_view_notify_selection_changed), - NAUTILUS_FILES_VIEW (self)); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ()); - gtk_widget_add_controller (GTK_WIDGET (content_widget), controller); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); - gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0); - g_signal_connect (controller, "pressed", - G_CALLBACK (on_view_click_pressed), self); - - controller = GTK_EVENT_CONTROLLER (gtk_gesture_long_press_new ()); - gtk_widget_add_controller (GTK_WIDGET (self->view_ui), controller); - gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE); - gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), TRUE); - g_signal_connect (controller, "pressed", - (GCallback) on_longpress_gesture_pressed_callback, self); - - gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (content_widget), - GTK_WIDGET (self->view_ui)); - - self->action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)); - g_action_map_add_action_entries (G_ACTION_MAP (self->action_group), - view_icon_actions, - G_N_ELEMENTS (view_icon_actions), - self); - - gtk_widget_show (GTK_WIDGET (self)); - - view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (self)); - g_action_map_add_action_entries (G_ACTION_MAP (view_action_group), - view_icon_actions, - G_N_ELEMENTS (view_icon_actions), - self); - self->zoom_level = get_default_zoom_level (); - /* 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 (self)), - "zoom-to-level", g_variant_new_int32 (self->zoom_level)); -} - -static void -nautilus_view_icon_controller_class_init (NautilusViewIconControllerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - NautilusFilesViewClass *files_view_class = NAUTILUS_FILES_VIEW_CLASS (klass); - - object_class->dispose = dispose; - object_class->finalize = finalize; - object_class->constructed = constructed; - - files_view_class->add_files = real_add_files; - files_view_class->begin_loading = real_begin_loading; - files_view_class->bump_zoom_level = real_bump_zoom_level; - files_view_class->can_zoom_in = real_can_zoom_in; - files_view_class->can_zoom_out = real_can_zoom_out; - files_view_class->click_policy_changed = real_click_policy_changed; - files_view_class->clear = real_clear; - files_view_class->file_changed = real_file_changed; - files_view_class->get_selection = real_get_selection; - /* TODO: remove this get_selection_for_file_transfer, this doesn't even - * take into account we could us the view for recursive search :/ - * CanvasView has the same issue. */ - files_view_class->get_selection_for_file_transfer = real_get_selection; - files_view_class->is_empty = real_is_empty; - files_view_class->remove_file = real_remove_file; - files_view_class->update_actions_state = real_update_actions_state; - files_view_class->reveal_selection = real_reveal_selection; - files_view_class->select_all = real_select_all; - files_view_class->invert_selection = real_invert_selection; - files_view_class->set_selection = real_set_selection; - files_view_class->compare_files = real_compare_files; - files_view_class->sort_directories_first_changed = real_sort_directories_first_changed; - files_view_class->end_file_changes = real_end_file_changes; - files_view_class->end_loading = real_end_loading; - files_view_class->get_view_id = real_get_view_id; - files_view_class->get_first_visible_file = real_get_first_visible_file; - files_view_class->scroll_to_file = real_scroll_to_file; - files_view_class->select_first = real_select_first; - files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level; - files_view_class->is_zoom_level_default = real_is_zoom_level_default; - files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to; - files_view_class->reveal_for_selection_context_menu = real_reveal_for_selection_context_menu; - files_view_class->preview_selection_event = real_preview_selection_event; -} - -static void -nautilus_view_icon_controller_init (NautilusViewIconController *self) -{ - GdkClipboard *clipboard; - - gtk_widget_add_css_class (GTK_WIDGET (self), "view"); - gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-grid-view"); - set_click_mode_from_settings (self); - - g_signal_connect_swapped (nautilus_preferences, - "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER, - G_CALLBACK (on_default_sort_order_changed), - self); - g_signal_connect_swapped (nautilus_preferences, - "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER, - G_CALLBACK (on_default_sort_order_changed), - self); - - set_captions_from_preferences (self); - g_signal_connect_swapped (nautilus_icon_view_preferences, - "changed::" NAUTILUS_PREFERENCES_ICON_VIEW_CAPTIONS, - G_CALLBACK (on_captions_preferences_changed), - self); - - clipboard = gdk_display_get_clipboard (gdk_display_get_default ()); - g_signal_connect_object (clipboard, "changed", - G_CALLBACK (on_clipboard_owner_changed), self, 0); -} - -NautilusViewIconController * -nautilus_view_icon_controller_new (NautilusWindowSlot *slot) -{ - return g_object_new (NAUTILUS_TYPE_VIEW_ICON_CONTROLLER, - "window-slot", slot, - NULL); -} diff --git a/src/nautilus-view-icon-controller.h b/src/nautilus-view-icon-controller.h deleted file mode 100644 index be70c94bd8ceac4bdac0e1ea25016e1d4c5d15c6..0000000000000000000000000000000000000000 --- a/src/nautilus-view-icon-controller.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include -#include - -#include "nautilus-files-view.h" -#include "nautilus-window-slot.h" -#include "nautilus-view-model.h" - -G_BEGIN_DECLS - -#define NAUTILUS_TYPE_VIEW_ICON_CONTROLLER (nautilus_view_icon_controller_get_type()) - -G_DECLARE_FINAL_TYPE (NautilusViewIconController, nautilus_view_icon_controller, NAUTILUS, VIEW_ICON_CONTROLLER, NautilusFilesView) - -NautilusViewIconController *nautilus_view_icon_controller_new (NautilusWindowSlot *slot); - -G_END_DECLS diff --git a/src/nautilus-view-icon-item-ui.c b/src/nautilus-view-icon-item-ui.c deleted file mode 100644 index d0ebff7b4b6e0df745e1f35bfcf78480d216c77b..0000000000000000000000000000000000000000 --- a/src/nautilus-view-icon-item-ui.c +++ /dev/null @@ -1,330 +0,0 @@ -#include "nautilus-view-icon-item-ui.h" -#include "nautilus-view-item-model.h" -#include "nautilus-file.h" -#include "nautilus-thumbnails.h" - -struct _NautilusViewIconItemUi -{ - GtkBox parent_instance; - - NautilusViewItemModel *model; - GQuark *caption_attributes; - - GtkWidget *fixed_height_box; - GtkWidget *icon; - GtkWidget *label; - GtkWidget *first_caption; - GtkWidget *second_caption; - GtkWidget *third_caption; - - gboolean called_once; -}; - -G_DEFINE_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, GTK_TYPE_BOX) - -enum -{ - PROP_0, - PROP_MODEL, - N_PROPS -}; - -#define EXTRA_WIDTH_FOR_TEXT 36 - -static void -update_icon (NautilusViewIconItemUi *self) -{ - NautilusFileIconFlags flags; - g_autoptr (GdkPaintable) icon_paintable = NULL; - GtkStyleContext *style_context; - NautilusFile *file; - guint icon_size; - g_autofree gchar *thumbnail_path = NULL; - - file = nautilus_view_item_model_get_file (self->model); - icon_size = nautilus_view_item_model_get_icon_size (self->model); - flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | - NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE | - NAUTILUS_FILE_ICON_FLAGS_USE_EMBLEMS | - NAUTILUS_FILE_ICON_FLAGS_USE_ONE_EMBLEM; - - icon_paintable = nautilus_file_get_icon_paintable (file, icon_size, 1, flags); - gtk_picture_set_paintable (GTK_PICTURE (self->icon), icon_paintable); - - /* Set the same height and width for all icons regardless of aspect ratio. - */ - gtk_widget_set_size_request (self->fixed_height_box, icon_size, icon_size); - if (icon_size < NAUTILUS_GRID_ICON_SIZE_LARGEST) - { - int extra_margins = 0.5 * EXTRA_WIDTH_FOR_TEXT; - gtk_widget_set_margin_start (self->fixed_height_box, extra_margins); - gtk_widget_set_margin_end (self->fixed_height_box, extra_margins); - } - style_context = gtk_widget_get_style_context (self->icon); - thumbnail_path = nautilus_file_get_thumbnail_path (file); - if (thumbnail_path != NULL && - nautilus_file_should_show_thumbnail (file)) - { - gtk_style_context_add_class (style_context, "thumbnail"); - } - else - { - gtk_style_context_remove_class (style_context, "thumbnail"); - } -} - -static void -update_captions (NautilusViewIconItemUi *self) -{ - NautilusFile *file; - GtkWidget * const caption_labels[] = - { - self->first_caption, - self->second_caption, - self->third_caption - }; - G_STATIC_ASSERT (G_N_ELEMENTS (caption_labels) == NAUTILUS_VIEW_ICON_N_CAPTIONS); - - file = nautilus_view_item_model_get_file (self->model); - for (guint i = 0; i < NAUTILUS_VIEW_ICON_N_CAPTIONS; i++) - { - GQuark attribute_q = self->caption_attributes[i]; - gboolean show_caption; - - show_caption = (attribute_q != 0); - gtk_widget_set_visible (caption_labels[i], show_caption); - if (show_caption) - { - g_autofree gchar *string = NULL; - string = nautilus_file_get_string_attribute_q (file, attribute_q); - gtk_label_set_text (GTK_LABEL (caption_labels[i]), string); - } - } -} - -static void -on_file_changed (NautilusViewIconItemUi *self) -{ - NautilusFile *file; - - file = nautilus_view_item_model_get_file (self->model); - - update_icon (self); - - gtk_label_set_text (GTK_LABEL (self->label), - nautilus_file_get_display_name (file)); - update_captions (self); -} - -static void -on_view_item_size_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data); - - update_icon (self); - update_captions (self); -} - -static void -on_view_item_is_cut_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) -{ - NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data); - gboolean is_cut; - - g_object_get (object, "is-cut", &is_cut, NULL); - if (is_cut) - { - gtk_widget_add_css_class (self->icon, "cut"); - } - else - { - gtk_widget_remove_css_class (self->icon, "cut"); - } -} - -static void -set_model (NautilusViewIconItemUi *self, - NautilusViewItemModel *model); - -static void -finalize (GObject *object) -{ - NautilusViewIconItemUi *self = (NautilusViewIconItemUi *) object; - - set_model (self, NULL); - G_OBJECT_CLASS (nautilus_view_icon_item_ui_parent_class)->finalize (object); -} - -static void -get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object); - - switch (prop_id) - { - case PROP_MODEL: - { - g_value_set_object (value, self->model); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -set_model (NautilusViewIconItemUi *self, - NautilusViewItemModel *model) -{ - NautilusFile *file; - - if (self->model == model) - { - return; - } - - if (self->model != NULL) - { - g_signal_handlers_disconnect_by_data (self->model, self); - g_clear_object (&self->model); - } - - if (model == NULL) - { - return; - } - - self->model = g_object_ref (model); - - file = nautilus_view_item_model_get_file (self->model); - - update_icon (self); - gtk_label_set_text (GTK_LABEL (self->label), - nautilus_file_get_display_name (file)); - update_captions (self); - - g_signal_connect (self->model, "notify::icon-size", - (GCallback) on_view_item_size_changed, self); - g_signal_connect (self->model, "notify::is-cut", - (GCallback) on_view_item_is_cut_changed, self); - g_signal_connect_swapped (self->model, "file-changed", - (GCallback) on_file_changed, self); -} - -static void -set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object); - - switch (prop_id) - { - case PROP_MODEL: - { - set_model (self, g_value_get_object (value)); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -nautilus_view_icon_item_ui_class_init (NautilusViewIconItemUiClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = finalize; - object_class->get_property = get_property; - object_class->set_property = set_property; - - g_object_class_install_property (object_class, - PROP_MODEL, - g_param_spec_object ("model", - "Item model", - "The item model that this UI reprensents", - NAUTILUS_TYPE_VIEW_ITEM_MODEL, - G_PARAM_READWRITE)); - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-view-icon-item-ui.ui"); - - gtk_widget_class_bind_template_child (widget_class, NautilusViewIconItemUi, fixed_height_box); - gtk_widget_class_bind_template_child (widget_class, NautilusViewIconItemUi, icon); - gtk_widget_class_bind_template_child (widget_class, NautilusViewIconItemUi, label); - gtk_widget_class_bind_template_child (widget_class, NautilusViewIconItemUi, first_caption); - gtk_widget_class_bind_template_child (widget_class, NautilusViewIconItemUi, second_caption); - gtk_widget_class_bind_template_child (widget_class, NautilusViewIconItemUi, third_caption); -} - -static void -nautilus_view_icon_item_ui_init (NautilusViewIconItemUi *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); - -#if PANGO_VERSION_CHECK (1, 44, 4) - { - PangoAttrList *attr_list; - - /* GTK4 TODO: This attribute is set in the UI file but GTK 3 ignores it. - * Remove this block after the switch to GTK 4. */ - attr_list = pango_attr_list_new (); - pango_attr_list_insert (attr_list, pango_attr_insert_hyphens_new (FALSE)); - gtk_label_set_attributes (GTK_LABEL (self->label), attr_list); - pango_attr_list_unref (attr_list); - } -#endif -} - -NautilusViewIconItemUi * -nautilus_view_icon_item_ui_new (void) -{ - return g_object_new (NAUTILUS_TYPE_VIEW_ICON_ITEM_UI, NULL); -} - -void -nautilus_view_icon_item_ui_set_model (NautilusViewIconItemUi *self, - NautilusViewItemModel *model) -{ - g_object_set (self, "model", model, NULL); -} - -NautilusViewItemModel * -nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self) -{ - NautilusViewItemModel *model = NULL; - - g_object_get (self, "model", &model, NULL); - - return model; -} - -void -nautilus_view_item_ui_set_caption_attributes (NautilusViewIconItemUi *self, - GQuark *attrs) -{ - self->caption_attributes = attrs; -} - -gboolean -nautilus_view_icon_item_ui_once (NautilusViewIconItemUi *self) -{ - if (self->called_once) - { - return FALSE; - } - - self->called_once = TRUE; - return TRUE; -} diff --git a/src/nautilus-view-icon-item-ui.h b/src/nautilus-view-icon-item-ui.h deleted file mode 100644 index edae77dc8537b721be0be0daabc20bfef6cbc10c..0000000000000000000000000000000000000000 --- a/src/nautilus-view-icon-item-ui.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include - -#include "nautilus-view-item-model.h" - -G_BEGIN_DECLS - -enum -{ - NAUTILUS_VIEW_ICON_FIRST_CAPTION, - NAUTILUS_VIEW_ICON_SECOND_CAPTION, - NAUTILUS_VIEW_ICON_THIRD_CAPTION, - NAUTILUS_VIEW_ICON_N_CAPTIONS -}; - -#define NAUTILUS_TYPE_VIEW_ICON_ITEM_UI (nautilus_view_icon_item_ui_get_type()) - -G_DECLARE_FINAL_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, NAUTILUS, VIEW_ICON_ITEM_UI, GtkBox) - -NautilusViewIconItemUi * nautilus_view_icon_item_ui_new (void); -void nautilus_view_icon_item_ui_set_model (NautilusViewIconItemUi *self, - NautilusViewItemModel *model); -NautilusViewItemModel *nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self); -void nautilus_view_item_ui_set_caption_attributes (NautilusViewIconItemUi *self, - GQuark *attrs); -gboolean nautilus_view_icon_item_ui_once (NautilusViewIconItemUi *self); - -G_END_DECLS diff --git a/src/nautilus-view-item-model.c b/src/nautilus-view-item-model.c deleted file mode 100644 index 6cc3f54f5c1e5d41c22a6183f967835bc8172d54..0000000000000000000000000000000000000000 --- a/src/nautilus-view-item-model.c +++ /dev/null @@ -1,257 +0,0 @@ -#include "nautilus-view-item-model.h" -#include "nautilus-file.h" - -struct _NautilusViewItemModel -{ - GObject parent_instance; - guint icon_size; - gboolean is_cut; - NautilusFile *file; - GtkWidget *item_ui; -}; - -G_DEFINE_TYPE (NautilusViewItemModel, nautilus_view_item_model, G_TYPE_OBJECT) - -enum -{ - PROP_0, - PROP_FILE, - PROP_ICON_SIZE, - PROP_IS_CUT, - PROP_ITEM_UI, - N_PROPS -}; - -enum -{ - FILE_CHANGED, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL]; - -static void -nautilus_view_item_model_dispose (GObject *object) -{ - NautilusViewItemModel *self = NAUTILUS_VIEW_ITEM_MODEL (object); - - g_clear_object (&self->item_ui); - - G_OBJECT_CLASS (nautilus_view_item_model_parent_class)->dispose (object); -} - -static void -nautilus_view_item_model_finalize (GObject *object) -{ - NautilusViewItemModel *self = NAUTILUS_VIEW_ITEM_MODEL (object); - - g_clear_object (&self->file); - - G_OBJECT_CLASS (nautilus_view_item_model_parent_class)->finalize (object); -} - -static void -nautilus_view_item_model_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - NautilusViewItemModel *self = NAUTILUS_VIEW_ITEM_MODEL (object); - - switch (prop_id) - { - case PROP_FILE: - { - g_value_set_object (value, self->file); - } - break; - - case PROP_ICON_SIZE: - { - g_value_set_int (value, self->icon_size); - } - break; - - case PROP_IS_CUT: - { - g_value_set_boolean (value, self->is_cut); - } - break; - - case PROP_ITEM_UI: - { - g_value_set_object (value, self->item_ui); - } - break; - - default: - { - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } - } -} - -static void -nautilus_view_item_model_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - NautilusViewItemModel *self = NAUTILUS_VIEW_ITEM_MODEL (object); - - switch (prop_id) - { - case PROP_FILE: - { - self->file = g_value_dup_object (value); - } - break; - - case PROP_ICON_SIZE: - { - self->icon_size = g_value_get_int (value); - } - break; - - case PROP_IS_CUT: - { - self->is_cut = g_value_get_boolean (value); - } - break; - - case PROP_ITEM_UI: - { - g_set_object (&self->item_ui, g_value_get_object (value)); - } - break; - - default: - { - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } - } -} - -static void -nautilus_view_item_model_init (NautilusViewItemModel *self) -{ -} - -static void -nautilus_view_item_model_class_init (NautilusViewItemModelClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->dispose = nautilus_view_item_model_dispose; - object_class->finalize = nautilus_view_item_model_finalize; - object_class->get_property = nautilus_view_item_model_get_property; - object_class->set_property = nautilus_view_item_model_set_property; - - g_object_class_install_property (object_class, - PROP_ICON_SIZE, - g_param_spec_int ("icon-size", - "Icon size", - "The size in pixels of the icon", - NAUTILUS_GRID_ICON_SIZE_SMALL, - NAUTILUS_GRID_ICON_SIZE_LARGEST, - NAUTILUS_GRID_ICON_SIZE_LARGE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); - g_object_class_install_property (object_class, - PROP_IS_CUT, - g_param_spec_boolean ("is-cut", - "", "", - FALSE, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_FILE, - g_param_spec_object ("file", - "File", - "The file the icon item represents", - NAUTILUS_TYPE_FILE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_ITEM_UI, - g_param_spec_object ("item-ui", - "Item ui", - "The UI that reprensents the item model", - GTK_TYPE_WIDGET, - G_PARAM_READWRITE)); - - signals[FILE_CHANGED] = g_signal_new ("file-changed", - G_TYPE_FROM_CLASS (klass), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -NautilusViewItemModel * -nautilus_view_item_model_new (NautilusFile *file, - guint icon_size) -{ - return g_object_new (NAUTILUS_TYPE_VIEW_ITEM_MODEL, - "file", file, - "icon-size", icon_size, - NULL); -} - -guint -nautilus_view_item_model_get_icon_size (NautilusViewItemModel *self) -{ - g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), -1); - - return self->icon_size; -} - -void -nautilus_view_item_model_set_icon_size (NautilusViewItemModel *self, - guint icon_size) -{ - g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self)); - - g_object_set (self, "icon-size", icon_size, NULL); -} - -void -nautilus_view_item_model_set_cut (NautilusViewItemModel *self, - gboolean is_cut) -{ - g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self)); - - g_object_set (self, "is-cut", is_cut, NULL); -} - -NautilusFile * -nautilus_view_item_model_get_file (NautilusViewItemModel *self) -{ - g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), NULL); - - return self->file; -} - -GtkWidget * -nautilus_view_item_model_get_item_ui (NautilusViewItemModel *self) -{ - g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), NULL); - - return self->item_ui; -} - -void -nautilus_view_item_model_set_item_ui (NautilusViewItemModel *self, - GtkWidget *item_ui) -{ - g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self)); - - g_object_set (self, "item-ui", item_ui, NULL); -} - -void -nautilus_view_item_model_file_changed (NautilusViewItemModel *self) -{ - g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self)); - - g_signal_emit (self, signals[FILE_CHANGED], 0); -} diff --git a/src/nautilus-view-item-model.h b/src/nautilus-view-item-model.h deleted file mode 100644 index 27c4a8fd9d63e3ec6be4def3937cd79c8bacd2ff..0000000000000000000000000000000000000000 --- a/src/nautilus-view-item-model.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -#include "nautilus-file.h" - -G_BEGIN_DECLS - -#define NAUTILUS_TYPE_VIEW_ITEM_MODEL (nautilus_view_item_model_get_type()) - -G_DECLARE_FINAL_TYPE (NautilusViewItemModel, nautilus_view_item_model, NAUTILUS, VIEW_ITEM_MODEL, GObject) - -NautilusViewItemModel * nautilus_view_item_model_new (NautilusFile *file, - guint icon_size); - -void nautilus_view_item_model_set_icon_size (NautilusViewItemModel *self, - guint icon_size); - -guint nautilus_view_item_model_get_icon_size (NautilusViewItemModel *self); -void nautilus_view_item_model_set_cut (NautilusViewItemModel *self, - gboolean is_cut); - -NautilusFile * nautilus_view_item_model_get_file (NautilusViewItemModel *self); - -void nautilus_view_item_model_set_item_ui (NautilusViewItemModel *self, - GtkWidget *item_ui); - -GtkWidget * nautilus_view_item_model_get_item_ui (NautilusViewItemModel *self); -void nautilus_view_item_model_file_changed (NautilusViewItemModel *self); - -G_END_DECLS diff --git a/src/nautilus-view-item.c b/src/nautilus-view-item.c new file mode 100644 index 0000000000000000000000000000000000000000..560cfec42f73ee0b8eeb0b6e7217f84f5159ba98 --- /dev/null +++ b/src/nautilus-view-item.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "nautilus-view-item.h" + +struct _NautilusViewItem +{ + GObject parent_instance; + guint icon_size; + gboolean is_cut; + NautilusFile *file; + GtkWidget *item_ui; +}; + +G_DEFINE_TYPE (NautilusViewItem, nautilus_view_item, G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_FILE, + PROP_ICON_SIZE, + PROP_IS_CUT, + PROP_ITEM_UI, + N_PROPS +}; + +static GParamSpec *properties[N_PROPS] = { NULL, }; + +enum +{ + FILE_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +static void +nautilus_view_item_dispose (GObject *object) +{ + NautilusViewItem *self = NAUTILUS_VIEW_ITEM (object); + + g_clear_object (&self->item_ui); + + G_OBJECT_CLASS (nautilus_view_item_parent_class)->dispose (object); +} + +static void +nautilus_view_item_finalize (GObject *object) +{ + NautilusViewItem *self = NAUTILUS_VIEW_ITEM (object); + + g_clear_object (&self->file); + + G_OBJECT_CLASS (nautilus_view_item_parent_class)->finalize (object); +} + +static void +nautilus_view_item_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusViewItem *self = NAUTILUS_VIEW_ITEM (object); + + switch (prop_id) + { + case PROP_FILE: + { + g_value_set_object (value, self->file); + } + break; + + case PROP_ICON_SIZE: + { + g_value_set_int (value, self->icon_size); + } + break; + + case PROP_IS_CUT: + { + g_value_set_boolean (value, self->is_cut); + } + break; + + case PROP_ITEM_UI: + { + g_value_set_object (value, self->item_ui); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +nautilus_view_item_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusViewItem *self = NAUTILUS_VIEW_ITEM (object); + + switch (prop_id) + { + case PROP_FILE: + { + self->file = g_value_dup_object (value); + } + break; + + case PROP_ICON_SIZE: + { + self->icon_size = g_value_get_int (value); + } + break; + + case PROP_IS_CUT: + { + self->is_cut = g_value_get_boolean (value); + } + break; + + case PROP_ITEM_UI: + { + g_set_object (&self->item_ui, g_value_get_object (value)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +nautilus_view_item_init (NautilusViewItem *self) +{ +} + +static void +nautilus_view_item_class_init (NautilusViewItemClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = nautilus_view_item_dispose; + object_class->finalize = nautilus_view_item_finalize; + object_class->get_property = nautilus_view_item_get_property; + object_class->set_property = nautilus_view_item_set_property; + + properties[PROP_ICON_SIZE] = g_param_spec_int ("icon-size", + "", "", + NAUTILUS_LIST_ICON_SIZE_SMALL, + NAUTILUS_GRID_ICON_SIZE_EXTRA_LARGE, + NAUTILUS_GRID_ICON_SIZE_LARGE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + properties[PROP_IS_CUT] = g_param_spec_boolean ("is-cut", + "", "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + properties[PROP_FILE] = g_param_spec_object ("file", + "", "", + NAUTILUS_TYPE_FILE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + properties[PROP_ITEM_UI] = g_param_spec_object ("item-ui", + "", "", + GTK_TYPE_WIDGET, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, N_PROPS, properties); + + signals[FILE_CHANGED] = g_signal_new ("file-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +NautilusViewItem * +nautilus_view_item_new (NautilusFile *file, + guint icon_size) +{ + return g_object_new (NAUTILUS_TYPE_VIEW_ITEM, + "file", file, + "icon-size", icon_size, + NULL); +} + +guint +nautilus_view_item_get_icon_size (NautilusViewItem *self) +{ + g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM (self), -1); + + return self->icon_size; +} + +void +nautilus_view_item_set_icon_size (NautilusViewItem *self, + guint icon_size) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (self)); + + g_object_set (self, "icon-size", icon_size, NULL); +} + +void +nautilus_view_item_set_cut (NautilusViewItem *self, + gboolean is_cut) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (self)); + + g_object_set (self, "is-cut", is_cut, NULL); +} + +NautilusFile * +nautilus_view_item_get_file (NautilusViewItem *self) +{ + g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM (self), NULL); + + return self->file; +} + +GtkWidget * +nautilus_view_item_get_item_ui (NautilusViewItem *self) +{ + g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM (self), NULL); + + return self->item_ui; +} + +void +nautilus_view_item_set_item_ui (NautilusViewItem *self, + GtkWidget *item_ui) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (self)); + + g_object_set (self, "item-ui", item_ui, NULL); +} + +void +nautilus_view_item_file_changed (NautilusViewItem *self) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM (self)); + + g_signal_emit (self, signals[FILE_CHANGED], 0); +} diff --git a/src/nautilus-view-item.h b/src/nautilus-view-item.h new file mode 100644 index 0000000000000000000000000000000000000000..5e620520aa042d546eac4e45d6abfa55b39ad207 --- /dev/null +++ b/src/nautilus-view-item.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The GNOME project contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +#include "nautilus-file.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_VIEW_ITEM (nautilus_view_item_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusViewItem, nautilus_view_item, NAUTILUS, VIEW_ITEM, GObject) + +NautilusViewItem * nautilus_view_item_new (NautilusFile *file, + guint icon_size); + +void nautilus_view_item_set_icon_size (NautilusViewItem *self, + guint icon_size); + +guint nautilus_view_item_get_icon_size (NautilusViewItem *self); +void nautilus_view_item_set_cut (NautilusViewItem *self, + gboolean is_cut); + +NautilusFile * nautilus_view_item_get_file (NautilusViewItem *self); + +void nautilus_view_item_set_item_ui (NautilusViewItem *self, + GtkWidget *item_ui); + +GtkWidget * nautilus_view_item_get_item_ui (NautilusViewItem *self); +void nautilus_view_item_file_changed (NautilusViewItem *self); + +G_END_DECLS diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c index 69cdeb0f8fdd74e04985bd3b4662160affc785f3..72fd8a79eda54cd74a56ce8561ba7679e1416659 100644 --- a/src/nautilus-view-model.c +++ b/src/nautilus-view-model.c @@ -1,5 +1,5 @@ #include "nautilus-view-model.h" -#include "nautilus-view-item-model.h" +#include "nautilus-view-item.h" #include "nautilus-global-preferences.h" struct _NautilusViewModel @@ -9,13 +9,14 @@ struct _NautilusViewModel GHashTable *map_files_to_model; GListStore *internal_model; GtkMultiSelection *selection_model; - NautilusViewModelSortData *sort_data; + GtkSorter *sorter; + gulong sorter_changed_id; }; static GType nautilus_view_model_get_item_type (GListModel *list) { - return NAUTILUS_TYPE_VIEW_ITEM_MODEL; + return NAUTILUS_TYPE_VIEW_ITEM; } static guint @@ -116,10 +117,12 @@ G_DEFINE_TYPE_WITH_CODE (NautilusViewModel, nautilus_view_model, G_TYPE_OBJECT, enum { PROP_0, - PROP_SORT_TYPE, + PROP_SORTER, N_PROPS }; +static GParamSpec *properties[N_PROPS] = { NULL, }; + static void dispose (GObject *object) { @@ -143,6 +146,8 @@ dispose (GObject *object) self->internal_model = NULL; } + g_clear_signal_handler (&self->sorter_changed_id, self->sorter); + G_OBJECT_CLASS (nautilus_view_model_parent_class)->dispose (object); } @@ -154,10 +159,7 @@ finalize (GObject *object) G_OBJECT_CLASS (nautilus_view_model_parent_class)->finalize (object); g_hash_table_destroy (self->map_files_to_model); - if (self->sort_data) - { - g_free (self->sort_data); - } + g_clear_object (&self->sorter); } static void @@ -170,9 +172,9 @@ get_property (GObject *object, switch (prop_id) { - case PROP_SORT_TYPE: + case PROP_SORTER: { - g_value_set_object (value, self->sort_data); + g_value_set_object (value, nautilus_view_model_get_sorter (self)); } break; @@ -193,9 +195,9 @@ set_property (GObject *object, switch (prop_id) { - case PROP_SORT_TYPE: + case PROP_SORTER: { - nautilus_view_model_set_sort_type (self, g_value_get_object (value)); + nautilus_view_model_set_sorter (self, g_value_get_object (value)); } break; @@ -213,7 +215,7 @@ constructed (GObject *object) G_OBJECT_CLASS (nautilus_view_model_parent_class)->constructed (object); - self->internal_model = g_list_store_new (NAUTILUS_TYPE_VIEW_ITEM_MODEL); + self->internal_model = g_list_store_new (NAUTILUS_TYPE_VIEW_ITEM); self->selection_model = gtk_multi_selection_new (g_object_ref (G_LIST_MODEL (self->internal_model))); self->map_files_to_model = g_hash_table_new (NULL, NULL); @@ -233,6 +235,14 @@ nautilus_view_model_class_init (NautilusViewModelClass *klass) object_class->get_property = get_property; object_class->set_property = set_property; object_class->constructed = constructed; + + properties[PROP_SORTER] = + g_param_spec_object ("sorter", + "", "", + GTK_TYPE_SORTER, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void @@ -246,16 +256,20 @@ compare_data_func (gconstpointer a, gpointer user_data) { NautilusViewModel *self = NAUTILUS_VIEW_MODEL (user_data); - NautilusFile *file_a; - NautilusFile *file_b; - file_a = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL ((gpointer) a)); - file_b = nautilus_view_item_model_get_file (NAUTILUS_VIEW_ITEM_MODEL ((gpointer) b)); + g_return_val_if_fail (self->sorter != NULL, GTK_ORDERING_EQUAL); - return nautilus_file_compare_for_sort (file_a, file_b, - self->sort_data->sort_type, - self->sort_data->directories_first, - self->sort_data->reversed); + return gtk_sorter_compare (self->sorter, (gpointer) a, (gpointer) b); +} + +static void +on_sorter_changed (GtkSorter *sorter, + GtkSorterChange change, + gpointer user_data) +{ + NautilusViewModel *self = NAUTILUS_VIEW_MODEL (user_data); + + g_list_store_sort (self->internal_model, compare_data_func, self); } NautilusViewModel * @@ -264,39 +278,38 @@ nautilus_view_model_new () return g_object_new (NAUTILUS_TYPE_VIEW_MODEL, NULL); } +GtkSorter * +nautilus_view_model_get_sorter (NautilusViewModel *self) +{ + return self->sorter; +} + void -nautilus_view_model_set_sort_type (NautilusViewModel *self, - NautilusViewModelSortData *sort_data) +nautilus_view_model_set_sorter (NautilusViewModel *self, + GtkSorter *sorter) { - if (self->sort_data) + if (g_set_object (&self->sorter, sorter)) { - g_free (self->sort_data); - } + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]); - self->sort_data = g_new (NautilusViewModelSortData, 1); - self->sort_data->sort_type = sort_data->sort_type; - self->sort_data->reversed = sort_data->reversed; - self->sort_data->directories_first = sort_data->directories_first; + g_clear_signal_handler (&self->sorter_changed_id, self->sorter); + self->sorter_changed_id = g_signal_connect (self->sorter, "changed", + G_CALLBACK (on_sorter_changed), self); + } g_list_store_sort (self->internal_model, compare_data_func, self); } -NautilusViewModelSortData * -nautilus_view_model_get_sort_type (NautilusViewModel *self) -{ - return self->sort_data; -} - GQueue * nautilus_view_model_get_items_from_files (NautilusViewModel *self, GQueue *files) { GList *l; guint n_items; - GQueue *item_models; + GQueue *items; n_items = g_list_model_get_n_items (G_LIST_MODEL (self->internal_model)); - item_models = g_queue_new (); + items = g_queue_new (); for (l = g_queue_peek_head_link (files); l != NULL; l = l->next) { NautilusFile *file1; @@ -304,27 +317,27 @@ nautilus_view_model_get_items_from_files (NautilusViewModel *self, file1 = NAUTILUS_FILE (l->data); for (guint i = 0; i < n_items; i++) { - g_autoptr (NautilusViewItemModel) item_model = NULL; + g_autoptr (NautilusViewItem) item = NULL; NautilusFile *file2; g_autofree gchar *file1_uri = NULL; g_autofree gchar *file2_uri = NULL; - item_model = g_list_model_get_item (G_LIST_MODEL (self->internal_model), i); - file2 = nautilus_view_item_model_get_file (item_model); + item = g_list_model_get_item (G_LIST_MODEL (self->internal_model), i); + file2 = nautilus_view_item_get_file (item); file1_uri = nautilus_file_get_uri (file1); file2_uri = nautilus_file_get_uri (file2); if (g_strcmp0 (file1_uri, file2_uri) == 0) { - g_queue_push_tail (item_models, item_model); + g_queue_push_tail (items, item); break; } } } - return item_models; + return items; } -NautilusViewItemModel * +NautilusViewItem * nautilus_view_model_get_item_from_file (NautilusViewModel *self, NautilusFile *file) { @@ -332,8 +345,8 @@ nautilus_view_model_get_item_from_file (NautilusViewModel *self, } void -nautilus_view_model_remove_item (NautilusViewModel *self, - NautilusViewItemModel *item) +nautilus_view_model_remove_item (NautilusViewModel *self, + NautilusViewItem *item) { guint i; @@ -341,7 +354,7 @@ nautilus_view_model_remove_item (NautilusViewModel *self, { NautilusFile *file; - file = nautilus_view_item_model_get_file (item); + file = nautilus_view_item_get_file (item); g_list_store_remove (self->internal_model, i); g_hash_table_remove (self->map_files_to_model, file); } @@ -355,11 +368,11 @@ nautilus_view_model_remove_all_items (NautilusViewModel *self) } void -nautilus_view_model_add_item (NautilusViewModel *self, - NautilusViewItemModel *item) +nautilus_view_model_add_item (NautilusViewModel *self, + NautilusViewItem *item) { g_hash_table_insert (self->map_files_to_model, - nautilus_view_item_model_get_file (item), + nautilus_view_item_get_file (item), item); g_list_store_insert_sorted (self->internal_model, item, compare_data_func, self); } @@ -373,13 +386,13 @@ nautilus_view_model_add_items (NautilusViewModel *self, int i = 0; array = g_malloc_n (g_queue_get_length (items), - sizeof (NautilusViewItemModel *)); + sizeof (NautilusViewItem *)); for (l = g_queue_peek_head_link (items); l != NULL; l = l->next) { array[i] = l->data; g_hash_table_insert (self->map_files_to_model, - nautilus_view_item_model_get_file (l->data), + nautilus_view_item_get_file (l->data), l->data); i++; } @@ -392,8 +405,8 @@ nautilus_view_model_add_items (NautilusViewModel *self, } guint -nautilus_view_model_get_index (NautilusViewModel *self, - NautilusViewItemModel *item) +nautilus_view_model_get_index (NautilusViewModel *self, + NautilusViewItem *item) { guint i = G_MAXUINT; gboolean found; diff --git a/src/nautilus-view-model.h b/src/nautilus-view-model.h index c32e2656e5b009dede69fd1606b1006bd802efe1..839c6d2c70555e4f7fb184152df36692282b7e54 100644 --- a/src/nautilus-view-model.h +++ b/src/nautilus-view-model.h @@ -2,7 +2,7 @@ #include #include "nautilus-file.h" -#include "nautilus-view-item-model.h" +#include "nautilus-view-item.h" G_BEGIN_DECLS @@ -10,32 +10,25 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (NautilusViewModel, nautilus_view_model, NAUTILUS, VIEW_MODEL, GObject) -typedef struct -{ - NautilusFileSortType sort_type; - gboolean reversed; - gboolean directories_first; -} NautilusViewModelSortData; - NautilusViewModel * nautilus_view_model_new (void); -void nautilus_view_model_set_sort_type (NautilusViewModel *self, - NautilusViewModelSortData *sort_data); -NautilusViewModelSortData * nautilus_view_model_get_sort_type (NautilusViewModel *self); -NautilusViewItemModel * nautilus_view_model_get_item_from_file (NautilusViewModel *self, +GtkSorter *nautilus_view_model_get_sorter (NautilusViewModel *self); +void nautilus_view_model_set_sorter (NautilusViewModel *self, + GtkSorter *sorter); +NautilusViewItem * nautilus_view_model_get_item_from_file (NautilusViewModel *self, NautilusFile *file); GQueue * nautilus_view_model_get_items_from_files (NautilusViewModel *self, GQueue *files); /* Don't use inside a loop, use nautilus_view_model_remove_all_items instead. */ void nautilus_view_model_remove_item (NautilusViewModel *self, - NautilusViewItemModel *item); + NautilusViewItem *item); void nautilus_view_model_remove_all_items (NautilusViewModel *self); /* Don't use inside a loop, use nautilus_view_model_add_items instead. */ void nautilus_view_model_add_item (NautilusViewModel *self, - NautilusViewItemModel *item); + NautilusViewItem *item); void nautilus_view_model_add_items (NautilusViewModel *self, GQueue *items); guint nautilus_view_model_get_index (NautilusViewModel *self, - NautilusViewItemModel *item); + NautilusViewItem *item); G_END_DECLS diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css index 830eeed069d369df39c7f42573820c2c43d8eea8..47110f38e22e093e27a0e677c65ac42a67dbdd59 100644 --- a/src/resources/css/Adwaita.css +++ b/src/resources/css/Adwaita.css @@ -138,13 +138,6 @@ color: shade(@disk_space_free, 0.7); } -/* As a workaround for GtkTreeView showing thick border above first row when - * horizontal grid lines are enabled, we add them using CSS instead. Exclude the - * drop targets to let through the default feedback style. */ -.nautilus-list-view .view:not(:drop(active)) { - border-bottom: 1px solid @window_bg_color; -} - .search-information { background-color: @accent_bg_color; color:white; @@ -165,7 +158,63 @@ padding: 6px; } -.nautilus-grid-view .thumbnail { +/* Column view */ + +/* Setup padding on the list. Horizontal padding must be set on the columnview + * for it to calculate column widths correctly. */ +.nautilus-list-view columnview { + padding-left: 24px; + padding-right: 24px; +} +.nautilus-list-view columnview > listview { + padding-top: 12px; + padding-bottom: 24px; +} + +/* Use negative margins to extend rubberbanding area into the columnview's + * padding, then apply positive margin on rows to reestablish positioning. */ +.nautilus-list-view columnview > listview { + margin-left: -24px; + margin-right: -24px; +} +.nautilus-list-view columnview > listview > row { + margin-left: 24px; + margin-right: 24px; +} + +.nautilus-list-view columnview > listview > row { + border-radius: 6px; + margin-top: 4px; + margin-bottom: 4px; +} + +.nautilus-list-view.compact columnview > listview > row { + margin-top: 2px; + margin-bottom: 2px; +} + +/* GTK unconditionally sets padding on GtkColumnViewCell, even with .data-table. + * We don't want this to hpappen because we have event controllers on the child, + * which should thus cover the whole area of the row. */ +.nautilus-list-view columnview > listview > row > cell { + padding: 0px; +} + +.nautilus-list-view #NautilusViewCell { + padding: 6px; +} + +.nautilus-list-view.compact #NautilusViewCell { + padding-top: 3px; + padding-bottom: 3px; +} + +.nautilus-list-view:not(.compact) image.star { + padding: 6px; +} + +/* Both views */ +.view .thumbnail { background: url('/org/gnome/nautilus/Checkerboard.png') repeat; box-shadow: 0px 1px 2px 0px @shade_color, 0px 0px 0px 1px @shade_color, @@ -173,6 +222,19 @@ border-radius: 2px; } -.nautilus-grid-view .cut { +.view .cut { opacity: 0.55; } + +.view image.star:hover { + opacity: 1; +} + +@keyframes rotate_star { + from { -gtk-icon-transform: rotate(-72deg); } + to {} +} + +.view image.star.added { + animation: rotate_star 0.4s ease; +} diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml index 304814f75a40cb9453d7d42d90307e1e96c6e20c..d6ae0a20a45418ed37a5b38933120f074e96cfd5 100644 --- a/src/resources/nautilus.gresource.xml +++ b/src/resources/nautilus.gresource.xml @@ -25,7 +25,8 @@ ui/nautilus-file-conflict-dialog.ui ui/nautilus-files-view-select-items.ui ui/nautilus-operations-ui-manager-request-passphrase.ui - ui/nautilus-view-icon-item-ui.ui + ui/nautilus-grid-cell.ui + ui/nautilus-name-cell.ui ../gtk/nautilusgtksidebarrow.ui ../gtk/nautilusgtkplacesview.ui ../gtk/nautilusgtkplacesviewrow.ui diff --git a/src/resources/ui/nautilus-view-icon-item-ui.ui b/src/resources/ui/nautilus-grid-cell.ui similarity index 98% rename from src/resources/ui/nautilus-view-icon-item-ui.ui rename to src/resources/ui/nautilus-grid-cell.ui index 0b0f18d26eb25bda7d06e5ab39b4c40dbc008352..2a9417aa86f62f83bc8b642ca2abfb318e170413 100644 --- a/src/resources/ui/nautilus-view-icon-item-ui.ui +++ b/src/resources/ui/nautilus-grid-cell.ui @@ -1,7 +1,7 @@ -