From 76462f07eec0ffbbec1a4937eb3a1efe822c7325 Mon Sep 17 00:00:00 2001 From: Carlos Soriano Date: Thu, 7 Apr 2016 21:51:52 +0200 Subject: views: Add flow box based view After all the rework on the window slots, views, and splitting the desktop, we can go ahead and use a flow box based view for Nautilus. This is just a prototype. --- .buildconfig | 2 +- org.gnome.Nautilus.json | 1 + src/Makefile.am | 12 + src/nautilus-canvas-view.c | 2 +- src/nautilus-container-max-width.c | 210 +++++++++ src/nautilus-container-max-width.h | 22 + src/nautilus-file.c | 4 +- src/nautilus-files-view.c | 45 +- src/nautilus-files-view.h | 8 +- src/nautilus-icon-info.h | 4 +- src/nautilus-view-icon-controller.c | 894 ++++++++++++++++++++++++++++++++++++ src/nautilus-view-icon-controller.h | 24 + src/nautilus-view-icon-item-ui.c | 265 +++++++++++ src/nautilus-view-icon-item-ui.h | 22 + src/nautilus-view-icon-ui.c | 253 ++++++++++ src/nautilus-view-icon-ui.h | 37 ++ src/nautilus-view-item-model.c | 249 ++++++++++ src/nautilus-view-item-model.h | 41 ++ src/nautilus-view-model.c | 347 ++++++++++++++ src/nautilus-view-model.h | 43 ++ src/resources/css/Adwaita.css | 12 +- 21 files changed, 2466 insertions(+), 31 deletions(-) create mode 100644 src/nautilus-container-max-width.c create mode 100644 src/nautilus-container-max-width.h create mode 100644 src/nautilus-view-icon-controller.c create mode 100644 src/nautilus-view-icon-controller.h create mode 100644 src/nautilus-view-icon-item-ui.c create mode 100644 src/nautilus-view-icon-item-ui.h create mode 100644 src/nautilus-view-icon-ui.c create mode 100644 src/nautilus-view-icon-ui.h create mode 100644 src/nautilus-view-item-model.c create mode 100644 src/nautilus-view-item-model.h create mode 100644 src/nautilus-view-model.c create mode 100644 src/nautilus-view-model.h diff --git a/.buildconfig b/.buildconfig index 78933c61b..72e29cd50 100644 --- a/.buildconfig +++ b/.buildconfig @@ -1,7 +1,7 @@ [default] name=Default device=local -runtime=org.gnome.Nautilus.json@8a0ac05428c64fa88d0296f4a1d2021b991c8bd4 +runtime=org.gnome.Nautilus.json@5f062c5ff39deaebcb5bb889fcd11917a460f71f config-opts=--disable-selinux prefix=/app app-id=org.gnome.Nautilus diff --git a/org.gnome.Nautilus.json b/org.gnome.Nautilus.json index 9e8a08772..1cf4f1f20 100644 --- a/org.gnome.Nautilus.json +++ b/org.gnome.Nautilus.json @@ -3,6 +3,7 @@ "runtime": "org.gnome.Platform", "runtime-version": "master", "sdk": "org.gnome.Sdk", + "sdk-version": "master", "command": "nautilus", "tags": ["devel", "development"], "desktop-file-name-prefix": "(Development) ", diff --git a/src/Makefile.am b/src/Makefile.am index db0a8027d..c79c03064 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -158,6 +158,8 @@ nautilus_no_main_sources = \ nautilus-canvas-view.h \ nautilus-canvas-view-container.c \ nautilus-canvas-view-container.h \ + nautilus-container-max-width.c \ + nautilus-container-max-width.h \ nautilus-dbus-manager.c \ nautilus-dbus-manager.h \ nautilus-desktop-item-properties.c \ @@ -220,6 +222,16 @@ nautilus_no_main_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-icon-ui.c \ + nautilus-view-icon-ui.h \ + nautilus-view-item-model.c \ + nautilus-view-item-model.h \ + nautilus-view-model.c \ + nautilus-view-model.h \ nautilus-window-slot.c \ nautilus-window-slot.h \ nautilus-window-slot-dnd.c \ diff --git a/src/nautilus-canvas-view.c b/src/nautilus-canvas-view.c index f1ed2c58f..0d1d17fa6 100644 --- a/src/nautilus-canvas-view.c +++ b/src/nautilus-canvas-view.c @@ -101,7 +101,7 @@ typedef struct gboolean supports_scaling; gboolean supports_keep_aligned; - /* Needed for async operations. Suposedly we would use cancellable and gtask, + /* FIXME: Needed for async operations. Suposedly we would use cancellable and gtask, * sadly gtkclipboard doesn't support that. * We follow this pattern for checking validity of the object in the views. * Ideally we would connect to a weak reference and do a cancellable. diff --git a/src/nautilus-container-max-width.c b/src/nautilus-container-max-width.c new file mode 100644 index 000000000..690a935f1 --- /dev/null +++ b/src/nautilus-container-max-width.c @@ -0,0 +1,210 @@ +#include "nautilus-container-max-width.h" + +struct _NautilusContainerMaxWidth +{ + GtkBin parent_instance; + guint max_width; +}; + +G_DEFINE_TYPE (NautilusContainerMaxWidth, nautilus_container_max_width, GTK_TYPE_BIN) + +enum +{ + PROP_0, + PROP_MAX_WIDTH, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +void +nautilus_container_max_width_set_max_width (NautilusContainerMaxWidth *self, + guint max_width) +{ + self->max_width = max_width; + gtk_widget_queue_allocate (GTK_WIDGET (self)); +} + +guint +nautilus_container_max_width_get_max_width (NautilusContainerMaxWidth *self) +{ + return self->max_width; +} + +NautilusContainerMaxWidth * +nautilus_container_max_width_new (void) +{ + return g_object_new (NAUTILUS_TYPE_CONTAINER_MAX_WIDTH, NULL); +} + +static void +nautilus_container_max_width_finalize (GObject *object) +{ + NautilusContainerMaxWidth *self = (NautilusContainerMaxWidth *) object; + + G_OBJECT_CLASS (nautilus_container_max_width_parent_class)->finalize (object); +} + +static void +nautilus_container_max_width_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusContainerMaxWidth *self = NAUTILUS_CONTAINER_MAX_WIDTH (object); + + switch (prop_id) + { + case PROP_MAX_WIDTH: + { + g_value_set_int (value, self->max_width); + } + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + break; + } +} + +static void +nautilus_container_max_width_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusContainerMaxWidth *self = NAUTILUS_CONTAINER_MAX_WIDTH (object); + + switch (prop_id) + { + case PROP_MAX_WIDTH: + { + nautilus_container_max_width_set_max_width (self, g_value_get_int (value)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + break; + } +} + +static void +get_preferred_width (GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + GtkWidget *child; + NautilusContainerMaxWidth *self; + GtkStyleContext *style_context; + GtkBorder padding; + + self = NAUTILUS_CONTAINER_MAX_WIDTH (widget); + child = gtk_bin_get_child (GTK_BIN (self)); + + *natural_size = 0; + *minimum_size = 0; + gtk_widget_get_preferred_width (child, minimum_size, natural_size); + + *minimum_size = self->max_width == -1 ? *minimum_size : 96; + *natural_size = self->max_width == -1 ? *natural_size : + MAX (*minimum_size, MIN (self->max_width, *natural_size)); + + style_context = gtk_widget_get_style_context (child); + gtk_style_context_get_padding (style_context, + gtk_widget_get_state_flags (child), + &padding); + *minimum_size += padding.left + padding.right; + *natural_size += padding.left + padding.right; +} + +static void +get_preferred_height (GtkWidget *widget, + gint *minimum_size, + gint *natural_size) +{ + GtkWidget *child; + NautilusContainerMaxWidth *self; + gint minimum_width = 0; + gint natural_width = 0; + GtkStyleContext *style_context; + GtkBorder padding; + + self = NAUTILUS_CONTAINER_MAX_WIDTH (widget); + child = gtk_bin_get_child (GTK_BIN (self)); + + get_preferred_width (widget, &minimum_width, &natural_width); + natural_width = self->max_width == -1 ? natural_width : MIN (self->max_width, natural_width); + + gtk_widget_get_preferred_height_for_width (child, natural_width, minimum_size, natural_size); + + style_context = gtk_widget_get_style_context (child); + gtk_style_context_get_padding (style_context, + gtk_widget_get_state_flags (child), + &padding); + *minimum_size += padding.top + padding.bottom; + *natural_size += padding.top + padding.bottom; +} + +static void +get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum_size, + gint *natural_size) +{ + get_preferred_height (widget, minimum_size, natural_size); +} + +static void +size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GTK_WIDGET_CLASS (nautilus_container_max_width_parent_class)->size_allocate (widget, allocation); +} + +static void +get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum_size, + gint *natural_size) +{ + get_preferred_width (widget, minimum_size, natural_size); +} + +static void +constructed (GObject *obj) +{ + NautilusContainerMaxWidth *self = NAUTILUS_CONTAINER_MAX_WIDTH (obj); + + G_OBJECT_CLASS (nautilus_container_max_width_parent_class)->constructed (obj); + + /* We want our parent to gives our preferred width */ + gtk_widget_set_halign (GTK_WIDGET (self), GTK_ALIGN_CENTER); + self->max_width = -1; +} + +static void +nautilus_container_max_width_class_init (NautilusContainerMaxWidthClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = nautilus_container_max_width_finalize; + object_class->get_property = nautilus_container_max_width_get_property; + object_class->set_property = nautilus_container_max_width_set_property; + object_class->constructed = constructed; + + widget_class->get_preferred_width = get_preferred_width; + widget_class->get_preferred_width_for_height = get_preferred_width_for_height; + widget_class->get_preferred_height = get_preferred_height; + widget_class->get_preferred_height_for_width = get_preferred_height_for_width; + widget_class->size_allocate = size_allocate; +} + +static void +nautilus_container_max_width_init (NautilusContainerMaxWidth *self) +{ +} diff --git a/src/nautilus-container-max-width.h b/src/nautilus-container-max-width.h new file mode 100644 index 000000000..935721d4c --- /dev/null +++ b/src/nautilus-container-max-width.h @@ -0,0 +1,22 @@ +#ifndef NAUTILUS_CONTAINER_MAX_WIDTH_H +#define NAUTILUS_CONTAINER_MAX_WIDTH_H + +#include +#include + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_CONTAINER_MAX_WIDTH (nautilus_container_max_width_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusContainerMaxWidth, nautilus_container_max_width, NAUTILUS, CONTAINER_MAX_WIDTH, GtkBin) + +NautilusContainerMaxWidth *nautilus_container_max_width_new (void); + +void nautilus_container_max_width_set_max_width (NautilusContainerMaxWidth *self, + guint max_width); +guint nautilus_container_max_width_get_max_width (NautilusContainerMaxWidth *self); + +G_END_DECLS + +#endif /* NAUTILUS_CONTAINER_MAX_WIDTH_H */ + diff --git a/src/nautilus-file.c b/src/nautilus-file.c index 919e76376..6df2e7d79 100644 --- a/src/nautilus-file.c +++ b/src/nautilus-file.c @@ -5363,11 +5363,11 @@ nautilus_file_get_thumbnail_icon (NautilusFile *file, { if (nautilus_is_video_file (file)) { - nautilus_ui_frame_video (&pixbuf); + /*nautilus_ui_frame_video (&pixbuf); */ } else { - nautilus_ui_frame_image (&pixbuf); + /*nautilus_ui_frame_image (&pixbuf); */ } } diff --git a/src/nautilus-files-view.c b/src/nautilus-files-view.c index 5ae94bbae..f9701d200 100644 --- a/src/nautilus-files-view.c +++ b/src/nautilus-files-view.c @@ -35,6 +35,7 @@ #include "nautilus-error-reporting.h" #include "nautilus-file-undo-manager.h" #include "nautilus-floating-bar.h" +#include "nautilus-view-icon-controller.h" #include "nautilus-list-view.h" #include "nautilus-canvas-view.h" #include "nautilus-mime-actions.h" @@ -130,10 +131,10 @@ #define MIN_COMMON_FILENAME_PREFIX_LENGTH 4 - enum { ADD_FILE, + ADD_FILES, BEGIN_FILE_CHANGES, BEGIN_LOADING, CLEAR, @@ -4055,6 +4056,7 @@ process_old_files (NautilusFilesView *view) GList *files_added, *files_changed, *node; FileAndDirectory *pending; GList *selection, *files; + g_autoptr (GList) pending_additions = NULL; priv = nautilus_files_view_get_instance_private (view); files_added = priv->old_added_files; @@ -4070,8 +4072,7 @@ process_old_files (NautilusFilesView *view) for (node = files_added; node != NULL; node = node->next) { pending = node->data; - g_signal_emit (view, - signals[ADD_FILE], 0, pending->file, pending->directory); + pending_additions = g_list_prepend (pending_additions, pending->file); /* Acknowledge the files that were pending to be revealed */ if (g_hash_table_contains (priv->pending_reveal, pending->file)) { @@ -4081,6 +4082,12 @@ process_old_files (NautilusFilesView *view) } } + if (files_added != NULL) + { + g_signal_emit (view, + signals[ADD_FILES], 0, pending_additions, pending->directory); + } + for (node = files_changed; node != NULL; node = node->next) { gboolean should_show_file; @@ -8018,9 +8025,11 @@ nautilus_files_view_reset_view_menu (NautilusFilesView *view) gboolean show_sort_trash, show_sort_access, show_sort_modification, sort_available; const gchar *hint; g_autofree gchar *zoom_level_percent = NULL; + NautilusFile *file; view_action_group = nautilus_files_view_get_action_group (view); priv = nautilus_files_view_get_instance_private (view); + file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view)); gtk_widget_set_visible (priv->visible_columns, g_action_group_has_action (view_action_group, "visible-columns")); @@ -8028,6 +8037,8 @@ nautilus_files_view_reset_view_menu (NautilusFilesView *view) sort_available = g_action_group_get_action_enabled (view_action_group, "sort"); show_sort_trash = show_sort_modification = show_sort_access = FALSE; gtk_widget_set_visible (priv->sort_menu, sort_available); + gtk_widget_set_visible (priv->sort_trash_time, + nautilus_file_is_in_trash (file)); /* We want to make insensitive available actions but that are not current * available due to the directory @@ -8037,24 +8048,6 @@ nautilus_files_view_reset_view_menu (NautilusFilesView *view) gtk_widget_set_sensitive (priv->zoom_controls_box, !nautilus_files_view_is_empty (view)); - if (sort_available) - { - variant = g_action_group_get_action_state_hint (view_action_group, "sort"); - g_variant_iter_init (&iter, variant); - - while (g_variant_iter_next (&iter, "&s", &hint)) - { - if (g_strcmp0 (hint, "trash-time") == 0) - { - show_sort_trash = TRUE; - } - } - - g_variant_unref (variant); - } - - gtk_widget_set_visible (priv->sort_trash_time, show_sort_trash); - zoom_level_percent = g_strdup_printf ("%.0f%%", nautilus_files_view_get_zoom_level_percentage (view) * 100.0); gtk_label_set_label (GTK_LABEL (priv->zoom_level_label), zoom_level_percent); } @@ -9339,6 +9332,14 @@ nautilus_files_view_class_init (NautilusFilesViewClass *klass) NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY); + signals[ADD_FILES] = + g_signal_new ("add-files", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusFilesViewClass, add_files), + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, 2, G_TYPE_POINTER, NAUTILUS_TYPE_DIRECTORY); signals[BEGIN_FILE_CHANGES] = g_signal_new ("begin-file-changes", G_TYPE_FROM_CLASS (klass), @@ -9698,7 +9699,7 @@ nautilus_files_view_new (guint id, { case NAUTILUS_VIEW_GRID_ID: { - view = nautilus_canvas_view_new (slot); + view = NAUTILUS_FILES_VIEW (nautilus_view_icon_controller_new (slot)); } break; diff --git a/src/nautilus-files-view.h b/src/nautilus-files-view.h index 6e3cd5df6..bf91bcf9c 100644 --- a/src/nautilus-files-view.h +++ b/src/nautilus-files-view.h @@ -63,6 +63,12 @@ struct _NautilusFilesViewClass { void (* add_file) (NautilusFilesView *view, NautilusFile *file, NautilusDirectory *directory); + /* The 'add_files' signal is emitted to add a set of files to the view. + * It must be replaced by each subclass. + */ + void (* add_files) (NautilusFilesView *view, + GList *files, + NautilusDirectory *directory); void (* remove_file) (NautilusFilesView *view, NautilusFile *file, NautilusDirectory *directory); @@ -324,8 +330,6 @@ char * nautilus_files_view_get_title (NautilusFilesV gboolean nautilus_files_view_supports_zooming (NautilusFilesView *view); void nautilus_files_view_bump_zoom_level (NautilusFilesView *view, int zoom_increment); -void nautilus_files_view_zoom_to_level (NautilusFilesView *view, - gint level); gboolean nautilus_files_view_can_zoom_in (NautilusFilesView *view); gboolean nautilus_files_view_can_zoom_out (NautilusFilesView *view); void nautilus_files_view_update_menus (NautilusFilesView *view); diff --git a/src/nautilus-icon-info.h b/src/nautilus-icon-info.h index f034ba8ae..65e3abb5d 100644 --- a/src/nautilus-icon-info.h +++ b/src/nautilus-icon-info.h @@ -38,10 +38,10 @@ typedef enum { #define NAUTILUS_LIST_ICON_SIZE_LARGE 48 #define NAUTILUS_LIST_ICON_SIZE_LARGER 64 -#define NAUTILUS_CANVAS_ICON_SIZE_SMALL 48 +#define NAUTILUS_CANVAS_ICON_SIZE_SMALL 32 #define NAUTILUS_CANVAS_ICON_SIZE_STANDARD 64 #define NAUTILUS_CANVAS_ICON_SIZE_LARGE 96 -#define NAUTILUS_CANVAS_ICON_SIZE_LARGER 128 +#define NAUTILUS_CANVAS_ICON_SIZE_LARGER 256 /* Maximum size of an icon that the icon factory will ever produce */ #define NAUTILUS_ICON_MAXIMUM_SIZE 320 diff --git a/src/nautilus-view-icon-controller.c b/src/nautilus-view-icon-controller.c new file mode 100644 index 000000000..4f235d50d --- /dev/null +++ b/src/nautilus-view-icon-controller.c @@ -0,0 +1,894 @@ +#include "nautilus-view-icon-controller.h" +#include "nautilus-view-icon-ui.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-global-preferences.h" + +struct _NautilusViewIconController +{ + NautilusFilesView parent_instance; + + NautilusViewIconUi *view_ui; + NautilusViewModel *model; + + GIcon *view_icon; + GActionGroup *action_group; + gint zoom_level; +}; + +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_TRASHED_TIME, + "trashed", + "trash-time", + TRUE, + }, + { + NAUTILUS_FILE_SORT_BY_SEARCH_RELEVANCE, + NULL, + "search-relevance", + TRUE, + } +}; + +static guint get_icon_size_for_zoom_level (NautilusCanvasZoomLevel 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; + NautilusFileSortType default_sort_order; + gboolean reversed; + + default_sort_order = g_settings_get_enum (nautilus_preferences, + NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER); + reversed = g_settings_get_boolean (nautilus_preferences, + NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER); + + /* If this is a special folder (e.g. search or recent), override the sort + * order and reversed flag with values appropriate for the folder */ + sort_type = nautilus_file_get_default_sort_type (file, &reversed); + + if (sort_type == NAUTILUS_FILE_SORT_NONE) + { + sort_type = CLAMP (default_sort_order, + NAUTILUS_FILE_SORT_BY_DISPLAY_NAME, + NAUTILUS_FILE_SORT_BY_ATIME); + } + + 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); + + g_list_store_remove_all (nautilus_view_model_get_g_model (self->model)); +} + + +/* FIXME: ideally this should go into the model so there is not need to + * recreate the model with the new data */ +static void +real_file_changed (NautilusFilesView *files_view, + NautilusFile *file, + NautilusDirectory *directory) +{ + NautilusViewIconController *self; + NautilusViewItemModel *item_model; + NautilusViewItemModel *new_item_model; + + self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); + item_model = nautilus_view_model_get_item_from_file (self->model, file); + nautilus_view_model_remove_item (self->model, item_model); + new_item_model = nautilus_view_item_model_new (file, + get_icon_size_for_zoom_level (self->zoom_level)); + nautilus_view_model_add_item (self->model, item_model); +} + +static GList * +real_get_selection (NautilusFilesView *files_view) +{ + NautilusViewIconController *self; + GList *selected_files = NULL; + GList *l; + g_autoptr (GQueue) selected_items = NULL; + + self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); + selected_items = nautilus_view_model_get_selected (self->model); + for (l = g_queue_peek_head_link (selected_items); l != NULL; l = l->next) + { + selected_files = g_list_prepend (selected_files, l->data); + } + + return selected_files; +} + + +static GList * +real_get_selection_for_file_transfer (NautilusFilesView *files_view) +{ + return NULL; +} + +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 (nautilus_view_model_get_g_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); + NautilusFile *current_file; + NautilusViewItemModel *current_item_model; + guint i = 0; + + while ((current_item_model = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL (nautilus_view_model_get_g_model (self->model)), i)))) + { + current_file = nautilus_view_item_model_get_file (current_item_model); + if (current_file == file) + { + g_list_store_remove (nautilus_view_model_get_g_model (self->model), i); + break; + } + i++; + } +} + +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; + + selection_files = convert_glist_to_queue (selection); + selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files); + nautilus_view_model_set_selected (self->model, selection_item_models); + nautilus_files_view_notify_selection_changed (files_view); +} + +static void +real_select_all (NautilusFilesView *files_view) +{ + NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); + gtk_flow_box_select_all (GTK_FLOW_BOX (self->view_ui)); +} + +static void +real_reveal_selection (NautilusFilesView *files_view) +{ +} + +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); + + /* When we change the sort action state, even using the same value, it triggers + * the sort action changed handler, which reveals the selection, since we expect + * the selection to be visible when the user changes the sort order. But we may + * need to update the actions state for others reason than an actual sort change, + * so we need to prevent to trigger the sort action changed handler for those cases. + * To achieve this, check if the action state value actually changed before setting + * it + */ +#if 0 + sort_state = g_action_group_get_action_state (view_action_group, "sort"); + + if (g_strcmp0 (g_variant_get_string (sort_state, NULL), + NAUTILUS_CANVAS_VIEW (view)->details->sort->action_target_name) != 0) + { + g_action_group_change_action_state (view_action_group, + "sort", + g_variant_new_string (NAUTILUS_CANVAS_VIEW (view)->details->sort->action_target_name)); + } + + g_variant_unref (sort_state); + +#endif + 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); + NautilusCanvasZoomLevel new_level; + + new_level = self->zoom_level + zoom_increment; + + if (new_level >= NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL && + new_level <= NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER) + { + 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 (NautilusCanvasZoomLevel zoom_level) +{ + switch (zoom_level) + { + case NAUTILUS_CANVAS_ZOOM_LEVEL_SMALL: + { + return NAUTILUS_CANVAS_ICON_SIZE_SMALL; + } + break; + + case NAUTILUS_CANVAS_ZOOM_LEVEL_STANDARD: + { + return NAUTILUS_CANVAS_ICON_SIZE_STANDARD; + } + break; + + case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGE: + { + return NAUTILUS_CANVAS_ICON_SIZE_LARGE; + } + break; + + case NAUTILUS_CANVAS_ZOOM_LEVEL_LARGER: + { + return NAUTILUS_CANVAS_ICON_SIZE_LARGER; + } + break; + } + g_return_val_if_reached (NAUTILUS_CANVAS_ICON_SIZE_STANDARD); +} + +static gint +get_default_zoom_level () +{ + NautilusCanvasZoomLevel 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_icon_size (NautilusViewIconController *self, + gint icon_size) +{ + NautilusViewItemModel *current_item_model; + guint i = 0; + + while ((current_item_model = NAUTILUS_VIEW_ITEM_MODEL (g_list_model_get_item (G_LIST_MODEL (nautilus_view_model_get_g_model (self->model)), i)))) + { + nautilus_view_item_model_set_icon_size (current_item_model, + get_icon_size_for_zoom_level (self->zoom_level)); + i++; + } +} + +static void +set_zoom_level (NautilusViewIconController *self, + guint new_level) +{ + self->zoom_level = new_level; + + 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_CANVAS_ZOOM_LEVEL_LARGE)); +} + +static gfloat +real_get_zoom_level_percentage (NautilusFilesView *files_view) +{ + NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); + + return (gfloat) get_icon_size_for_zoom_level (self->zoom_level) / + NAUTILUS_CANVAS_ICON_SIZE_LARGE; +} + +static gboolean +real_can_zoom_in (NautilusFilesView *files_view) +{ + return TRUE; +} + +static gboolean +real_can_zoom_out (NautilusFilesView *files_view) +{ + return TRUE; +} + +static GdkRectangle * +real_compute_rename_popover_pointing_to (NautilusFilesView *files_view) +{ + NautilusViewIconController *self; + GdkRectangle *allocation; + GtkAdjustment *vadjustment; + GtkAdjustment *hadjustment; + GtkWidget *parent_container; + g_autoptr (GQueue) selection_files = NULL; + g_autoptr (GQueue) selection_item_models = NULL; + GList *selection; + NautilusViewIconItemUi *icon_item_ui; + + self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); + allocation = g_new0 (GdkRectangle, 1); + + parent_container = nautilus_files_view_get_content_widget (files_view); + vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (parent_container)); + hadjustment = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (parent_container)); + selection = nautilus_view_get_selection (NAUTILUS_VIEW (files_view)); + selection_files = convert_glist_to_queue (selection); + selection_item_models = nautilus_view_model_get_items_from_files (self->model, selection_files); + /* We only allow one item to be renamed with a popover */ + icon_item_ui = nautilus_view_item_model_get_item_ui (g_queue_peek_head (selection_item_models)); + gtk_widget_get_allocation (GTK_WIDGET (icon_item_ui), allocation); + + allocation->x -= gtk_adjustment_get_value (hadjustment); + allocation->y -= gtk_adjustment_get_value (vadjustment); + + return allocation; +} + +static void +real_click_policy_changed (NautilusFilesView *files_view) +{ +} + +static gboolean +on_button_press_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) +{ + NautilusViewIconController *self; + GList *selection; + + self = NAUTILUS_VIEW_ICON_CONTROLLER (user_data); + + selection = nautilus_view_get_selection (NAUTILUS_VIEW (self)); + nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (self), + (GdkEventButton *) event); + return FALSE; +} + +static int +real_compare_files (NautilusFilesView *files_view, + NautilusFile *file1, + NautilusFile *file2) +{ + if (file1 < file2) + { + return -1; + } + + if (file1 > file2) + { + return +1; + } + + return 0; +} + +static gboolean +real_using_manual_layout (NautilusFilesView *files_view) +{ + return FALSE; +} + +static void +real_end_loading (NautilusFilesView *files_view, + gboolean all_files_seen) +{ +} + +static char * +real_get_first_visible_file (NautilusFilesView *files_view) +{ + return NULL; +} + +static void +real_scroll_to_file (NautilusFilesView *files_view, + const char *uri) +{ +} + +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 *sorts_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); + sorts_constants = get_sorts_constants_from_action_target_name (target_name); + sort_data.sort_type = sorts_constants->sort_type; + sort_data.reversed = sorts_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)), + sorts_constants); + + g_simple_action_set_state (action, value); +} + +static void +real_add_files (NautilusFilesView *files_view, + GList *files, + NautilusDirectory *directory) +{ + NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); + g_autoptr (GQueue) files_queue; + g_autoptr (GQueue) item_models; + + clock_t start = clock (); + + g_print ("add files %d\n", g_list_length (files)); + + files_queue = convert_glist_to_queue (files); + item_models = convert_files_to_item_models (self, files_queue); + nautilus_view_model_set_items (self->model, item_models); + + clock_t end = clock (); + double elapsed_time = (end - start) / (double) CLOCKS_PER_SEC; + g_print ("add file finished %d %f\n", + g_list_model_get_n_items (G_LIST_MODEL (nautilus_view_model_get_g_model (self->model))), + elapsed_time); +} + + +static guint +real_get_view_id (NautilusFilesView *files_view) +{ + return NAUTILUS_VIEW_GRID_ID; +} + +static GIcon * +real_get_icon (NautilusFilesView *files_view) +{ + NautilusViewIconController *self = NAUTILUS_VIEW_ICON_CONTROLLER (files_view); + + return self->view_icon; +} + +static void +real_select_first (NautilusFilesView *files_view) +{ +} + +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 +finalize (GObject *object) +{ + G_OBJECT_CLASS (nautilus_view_icon_controller_parent_class)->finalize (object); +} + + +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; + GActionGroup *view_action_group; + + self->model = nautilus_view_model_new (); + self->view_ui = nautilus_view_icon_ui_new (self); + g_signal_connect_after (GTK_WIDGET (self->view_ui), "button-press-event", + (GCallback) on_button_press_event, self); + gtk_widget_show (GTK_WIDGET (self->view_ui)); + self->view_icon = g_themed_icon_new ("view-grid-symbolic"); + + content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (self)); + gtk_container_add (GTK_CONTAINER (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_all (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->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; + files_view_class->get_selection_for_file_transfer = real_get_selection_for_file_transfer; + 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->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->using_manual_layout = real_using_manual_layout; + 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->get_icon = real_get_icon; + files_view_class->select_first = real_select_first; + files_view_class->restore_standard_zoom_level = real_restore_standard_zoom_level; + files_view_class->get_zoom_level_percentage = real_get_zoom_level_percentage; + files_view_class->compute_rename_popover_pointing_to = real_compute_rename_popover_pointing_to; +} + +static void +nautilus_view_icon_controller_init (NautilusViewIconController *self) +{ +} + +NautilusViewIconController * +nautilus_view_icon_controller_new (NautilusWindowSlot *slot) +{ + return g_object_new (NAUTILUS_TYPE_VIEW_ICON_CONTROLLER, + "window-slot", slot, + NULL); +} + +NautilusViewModel * +nautilus_view_icon_controller_get_model (NautilusViewIconController *self) +{ + g_return_val_if_fail (NAUTILUS_IS_VIEW_ICON_CONTROLLER (self), NULL); + + return self->model; +} diff --git a/src/nautilus-view-icon-controller.h b/src/nautilus-view-icon-controller.h new file mode 100644 index 000000000..814d6d635 --- /dev/null +++ b/src/nautilus-view-icon-controller.h @@ -0,0 +1,24 @@ +#ifndef NAUTILUS_VIEW_ICON_CONTROLLER_H +#define NAUTILUS_VIEW_ICON_CONTROLLER_H + +#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); + +NautilusViewModel * nautilus_view_icon_controller_get_model (NautilusViewIconController *self); + +G_END_DECLS + +#endif /* NAUTILUS_VIEW_ICON_CONTROLLER_H */ + diff --git a/src/nautilus-view-icon-item-ui.c b/src/nautilus-view-icon-item-ui.c new file mode 100644 index 000000000..dab5ac118 --- /dev/null +++ b/src/nautilus-view-icon-item-ui.c @@ -0,0 +1,265 @@ +#include "nautilus-view-icon-item-ui.h" +#include "nautilus-view-item-model.h" +#include "nautilus-container-max-width.h" +#include "nautilus-file.h" +#include "nautilus-thumbnails.h" + +struct _NautilusViewIconItemUi +{ + GtkFlowBoxChild parent_instance; + + NautilusViewItemModel *model; + + NautilusContainerMaxWidth *item_container; + GtkBox *icon; + GtkLabel *label; +}; + +G_DEFINE_TYPE (NautilusViewIconItemUi, nautilus_view_icon_item_ui, GTK_TYPE_FLOW_BOX_CHILD) + +enum { + PROP_0, + PROP_MODEL, + N_PROPS +}; + +static GtkWidget * +create_icon (NautilusViewIconItemUi *self) +{ + NautilusFileIconFlags flags; + g_autoptr (GdkPixbuf) icon_pixbuf; + GtkImage *icon; + GtkWidget *fixed_height_box; + GtkStyleContext *style_context; + NautilusFile *file; + guint icon_size; + + 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_pixbuf = nautilus_file_get_icon_pixbuf (file, icon_size, + TRUE, 1, flags); + icon = gtk_image_new_from_pixbuf (icon_pixbuf); + gtk_widget_set_hexpand (GTK_WIDGET (icon), TRUE); + gtk_widget_set_vexpand (GTK_WIDGET (icon), TRUE); + gtk_widget_set_valign (GTK_WIDGET (icon), GTK_ALIGN_CENTER); + gtk_widget_set_halign (GTK_WIDGET (icon), GTK_ALIGN_CENTER); + + fixed_height_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_valign (GTK_WIDGET (fixed_height_box), GTK_ALIGN_CENTER); + gtk_widget_set_halign (GTK_WIDGET (fixed_height_box), GTK_ALIGN_CENTER); + gtk_widget_set_size_request (GTK_WIDGET (fixed_height_box), icon_size, icon_size); + + if (nautilus_can_thumbnail (file) && + nautilus_file_should_show_thumbnail (file)) + { + style_context = gtk_widget_get_style_context (GTK_WIDGET (fixed_height_box)); + gtk_style_context_add_class (style_context, "icon-background"); + } + + gtk_box_pack_start (fixed_height_box, GTK_WIDGET (icon), FALSE, FALSE, 0); + + return fixed_height_box; +} + +static void +update_icon (NautilusViewIconItemUi *self) +{ + GtkWidget *box; + guint icon_size; + + icon_size = nautilus_view_item_model_get_icon_size (self->model); + nautilus_container_max_width_set_max_width (NAUTILUS_CONTAINER_MAX_WIDTH (self->item_container), + icon_size); + box = gtk_bin_get_child (GTK_BIN (self->item_container)); + if (self->icon) + { + gtk_container_remove (GTK_CONTAINER (box), GTK_WIDGET (self->icon)); + } + self->icon = create_icon (self); + gtk_widget_show_all (GTK_WIDGET (self->icon)); + gtk_box_pack_start (box, GTK_WIDGET (self->icon), FALSE, FALSE, 0); +} + +static void +on_view_item_file_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data); + NautilusFile *file; + + file = nautilus_view_item_model_get_file (self->model); + + if (self->icon) + { + update_icon (self); + } + + if (self->label) + { + gtk_label_set_text (self->label, + nautilus_file_get_display_name (file)); + } +} + +static void +on_view_item_size_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (user_data); + + if (self->icon) + { + update_icon (self); + } +} + +static void +constructed (GObject *object) +{ + NautilusViewIconItemUi *self = NAUTILUS_VIEW_ICON_ITEM_UI (object); + GtkBox *container; + GtkLabel *label; + GtkStyleContext *style_context; + NautilusFile *file; + guint icon_size; + + G_OBJECT_CLASS (nautilus_view_icon_item_ui_parent_class)->constructed (object); + + file = nautilus_view_item_model_get_file (self->model); + icon_size = nautilus_view_item_model_get_icon_size (self->model); + container = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + self->item_container = nautilus_container_max_width_new (); + + self->icon = create_icon (self); + gtk_box_pack_start (container, GTK_WIDGET (self->icon), FALSE, FALSE, 0); + + label = gtk_label_new (nautilus_file_get_display_name (file)); + gtk_widget_show (GTK_WIDGET (label)); + gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END); + gtk_label_set_line_wrap (label, TRUE); + gtk_label_set_line_wrap_mode (label, PANGO_WRAP_WORD_CHAR); + gtk_label_set_lines (label, 4); + gtk_label_set_justify (label, GTK_JUSTIFY_CENTER); + gtk_widget_set_valign (GTK_WIDGET (label), GTK_ALIGN_START); + gtk_box_pack_end (container, GTK_WIDGET (label), TRUE, TRUE, 0); + + style_context = gtk_widget_get_style_context (GTK_WIDGET (container)); + gtk_style_context_add_class (style_context, "icon-item-background"); + + gtk_widget_set_valign (GTK_WIDGET (container), GTK_ALIGN_START); + gtk_widget_set_halign (GTK_WIDGET (container), GTK_ALIGN_CENTER); + + gtk_container_add (GTK_CONTAINER (self->item_container), + GTK_WIDGET (container)); + nautilus_container_max_width_set_max_width (NAUTILUS_CONTAINER_MAX_WIDTH (self->item_container), + icon_size); + + gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (self->item_container)); + gtk_widget_show_all (GTK_WIDGET (self->item_container)); + + g_signal_connect (self->model, "notify::icon-size", + (GCallback) on_view_item_size_changed, self); + g_signal_connect (self->model, "notify::file", + (GCallback) on_view_item_file_changed, self); +} + +static void +finalize (GObject *object) +{ + NautilusViewIconItemUi *self = (NautilusViewIconItemUi *)object; + + g_signal_handlers_disconnect_by_data (self->model, self); + 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) +{ + self->model = g_object_ref (model); +} + +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); + + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->constructed = constructed; + + 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 | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +nautilus_view_icon_item_ui_init (NautilusViewIconItemUi *self) +{ +} + +NautilusViewIconItemUi * +nautilus_view_icon_item_ui_new (NautilusViewItemModel *model) +{ + return g_object_new (NAUTILUS_TYPE_VIEW_ICON_ITEM_UI, + "model", model, + NULL); +} + +NautilusViewItemModel * +nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self) +{ + return self->model; +} diff --git a/src/nautilus-view-icon-item-ui.h b/src/nautilus-view-icon-item-ui.h new file mode 100644 index 000000000..a28eca137 --- /dev/null +++ b/src/nautilus-view-icon-item-ui.h @@ -0,0 +1,22 @@ +#ifndef NAUTILUS_VIEW_ICON_ITEM_UI_H +#define NAUTILUS_VIEW_ICON_ITEM_UI_H + +#include +#include + +#include "nautilus-view-item-model.h" + +G_BEGIN_DECLS + +#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, GtkFlowBoxChild) + +NautilusViewIconItemUi * nautilus_view_icon_item_ui_new (NautilusViewItemModel *item_model); + +NautilusViewItemModel * nautilus_view_icon_item_ui_get_model (NautilusViewIconItemUi *self); + +G_END_DECLS + +#endif /* NAUTILUS_VIEW_ICON_ITEM_UI_H */ + diff --git a/src/nautilus-view-icon-ui.c b/src/nautilus-view-icon-ui.c new file mode 100644 index 000000000..8933e1713 --- /dev/null +++ b/src/nautilus-view-icon-ui.c @@ -0,0 +1,253 @@ +/* nautilus-view-icon-ui.c + * + * Copyright (C) 2016 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 . + */ + +#include +#include + +#include "nautilus-view-icon-ui.h" +#include "nautilus-view-icon-item-ui.h" +#include "nautilus-view-icon-controller.h" +#include "nautilus-files-view.h" +#include "nautilus-file.h" +#include "nautilus-directory.h" +#include "nautilus-global-preferences.h" + +struct _NautilusViewIconUi +{ + GtkFlowBox parent_instance; + + NautilusViewIconController *controller; +} ; + +G_DEFINE_TYPE (NautilusViewIconUi, nautilus_view_icon_ui, GTK_TYPE_FLOW_BOX) + +enum { + PROP_0, + PROP_CONTROLLER, + N_PROPS +}; + +static void +set_controller (NautilusViewIconUi *self, + NautilusViewIconController *controller) +{ + self->controller = controller; + + g_object_notify (G_OBJECT (self), "controller"); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (object); + + switch (prop_id) + { + case PROP_CONTROLLER: + { + g_value_set_object (value, self->controller); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (object); + + switch (prop_id) + { + case PROP_CONTROLLER: + { + set_controller (self, g_value_get_object (value)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +on_view_item_model_selected_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NautilusViewIconUi *self; + NautilusViewItemModel *item_model; + GtkFlowBoxChild *item_ui; + + self = NAUTILUS_VIEW_ICON_UI (user_data); + item_model = NAUTILUS_VIEW_ITEM_MODEL (object); + item_ui = GTK_FLOW_BOX_CHILD (nautilus_view_item_model_get_item_ui (item_model)); + if (nautilus_view_item_model_get_is_selected (item_model) && !gtk_flow_box_child_is_selected (item_ui)) + { + gtk_flow_box_select_child (GTK_FLOW_BOX (self), item_ui); + } + else if (!nautilus_view_item_model_get_is_selected (item_model) && gtk_flow_box_child_is_selected (item_ui)) + { + gtk_flow_box_unselect_child (GTK_FLOW_BOX (self), item_ui); + } +} + + +static GtkWidget * +create_widget_func (gpointer item, + gpointer user_data) +{ + NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (user_data); + NautilusViewItemModel *item_model = NAUTILUS_VIEW_ITEM_MODEL (item); + NautilusViewIconItemUi *child; + + child = nautilus_view_icon_item_ui_new (item_model); + nautilus_view_item_model_set_item_ui (item_model, GTK_WIDGET (child)); + gtk_widget_show (GTK_WIDGET (child)); + + g_signal_connect (item_model, "notify::selected", + (GCallback) on_view_item_model_selected_changed, self); + + return GTK_WIDGET (child); +} + +static void +on_child_activated (GtkFlowBox *flow_box, + GtkFlowBoxChild *child, + gpointer user_data) +{ + NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (user_data); + NautilusViewItemModel *item_model; + NautilusFile *file; + g_autoptr (GList) list = NULL; + + item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (child)); + file = nautilus_view_item_model_get_file (item_model); + list = g_list_append (list, file); + + nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (self->controller), list, 0, TRUE); +} + +static void +on_ui_selected_children_changed (GtkFlowBox *box, + gpointer user_data) +{ + NautilusViewIconUi *self; + GList *selected_children_ui; + GList *l; + GList *files_selection; + + self = NAUTILUS_VIEW_ICON_UI (user_data); + files_selection = NULL; + + selected_children_ui = gtk_flow_box_get_selected_children (GTK_FLOW_BOX (self)); + for (l = selected_children_ui; l != NULL; l = l->next) + { + NautilusViewItemModel *item_model; + NautilusFile *file; + + item_model = nautilus_view_icon_item_ui_get_model (NAUTILUS_VIEW_ICON_ITEM_UI (l->data)); + file = nautilus_view_item_model_get_file (item_model); + files_selection = g_list_prepend (files_selection, file); + } + + g_print ("changed\n"); + nautilus_view_set_selection (NAUTILUS_VIEW (self->controller), files_selection); +} + +static void +finalize (GObject *object) +{ + G_OBJECT_CLASS (nautilus_view_icon_ui_parent_class)->finalize (object); +} + +static void +constructed (GObject *object) +{ + NautilusViewIconUi *self = NAUTILUS_VIEW_ICON_UI (object); + NautilusViewModel *model; + GListStore *gmodel; + + G_OBJECT_CLASS (nautilus_view_icon_ui_parent_class)->constructed (object); + + gtk_flow_box_set_activate_on_single_click (GTK_FLOW_BOX (self), FALSE); + gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX (self), 20); + gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (self), GTK_SELECTION_MULTIPLE); + gtk_flow_box_set_homogeneous (GTK_FLOW_BOX (self), FALSE); + gtk_flow_box_set_row_spacing (GTK_FLOW_BOX (self), 4); + gtk_flow_box_set_column_spacing (GTK_FLOW_BOX (self), 8); + gtk_widget_set_valign (GTK_WIDGET (self), GTK_ALIGN_START); + gtk_widget_set_margin_top (GTK_WIDGET (self), 10); + gtk_widget_set_margin_start (GTK_WIDGET (self), 10); + gtk_widget_set_margin_bottom (GTK_WIDGET (self), 10); + gtk_widget_set_margin_end (GTK_WIDGET (self), 10); + + model = nautilus_view_icon_controller_get_model (self->controller); + gmodel = nautilus_view_model_get_g_model (model); + gtk_flow_box_bind_model (GTK_FLOW_BOX (self), + G_LIST_MODEL (gmodel), + create_widget_func, self, NULL); + + g_signal_connect (self, "child-activated", (GCallback) on_child_activated, self); + g_signal_connect (self, "selected-children-changed", (GCallback) on_ui_selected_children_changed, self); +} + +static void +nautilus_view_icon_ui_class_init (NautilusViewIconUiClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = finalize; + object_class->set_property = set_property; + object_class->get_property = get_property; + object_class->constructed = constructed; + + g_object_class_install_property (object_class, + PROP_CONTROLLER, + g_param_spec_object ("controller", + "Controller", + "The controller of the view", + NAUTILUS_TYPE_VIEW_ICON_CONTROLLER, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); +} + +static void +nautilus_view_icon_ui_init (NautilusViewIconUi *self) +{ +} + +NautilusViewIconUi * +nautilus_view_icon_ui_new (NautilusViewIconController *controller) +{ + return g_object_new (NAUTILUS_TYPE_VIEW_ICON_UI, + "controller", controller, + NULL); +} + diff --git a/src/nautilus-view-icon-ui.h b/src/nautilus-view-icon-ui.h new file mode 100644 index 000000000..5361acc17 --- /dev/null +++ b/src/nautilus-view-icon-ui.h @@ -0,0 +1,37 @@ +/* nautilus-view-icon-ui.h + * + * Copyright (C) 2016 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 . + */ +#ifndef NAUTILUS_VIEW_ICON_UI_H +#define NAUTILUS_VIEW_ICON_UI_H + +#include +#include + +#include "nautilus-view-icon-controller.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_VIEW_ICON_UI (nautilus_view_icon_ui_get_type()) + +G_DECLARE_FINAL_TYPE (NautilusViewIconUi, nautilus_view_icon_ui, NAUTILUS, VIEW_ICON_UI, GtkFlowBox) + +NautilusViewIconUi * nautilus_view_icon_ui_new (NautilusViewIconController *controller); + +G_END_DECLS + +#endif /* NAUTILUS_VIEW_ICON_UI_H */ + diff --git a/src/nautilus-view-item-model.c b/src/nautilus-view-item-model.c new file mode 100644 index 000000000..daf9afc5a --- /dev/null +++ b/src/nautilus-view-item-model.c @@ -0,0 +1,249 @@ +#include "nautilus-view-item-model.h" +#include "nautilus-file.h" + +struct _NautilusViewItemModel +{ + GObject parent_instance; + guint icon_size; + NautilusFile *file; + GtkLabel *label; + gboolean selected; + GtkWidget *item_ui; +}; + +G_DEFINE_TYPE (NautilusViewItemModel, nautilus_view_item_model, G_TYPE_OBJECT) + +enum +{ + PROP_0, + PROP_FILE, + PROP_ICON_SIZE, + PROP_SELECTED, + PROP_ITEM_UI, + N_PROPS +}; + +static void +nautilus_view_item_model_finalize (GObject *object) +{ + 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_SELECTED: + { + g_value_set_boolean (value, self->selected); + } + 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: + { + nautilus_view_item_model_set_file (self, g_value_get_object (value)); + } + break; + + case PROP_ICON_SIZE: + { + nautilus_view_item_model_set_icon_size (self, g_value_get_int (value)); + } + break; + + case PROP_SELECTED: + { + nautilus_view_item_model_set_selected (self, g_value_get_boolean (value)); + } + break; + + case PROP_ITEM_UI: + { + nautilus_view_item_model_set_item_ui (self, 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->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_CANVAS_ICON_SIZE_SMALL, + NAUTILUS_CANVAS_ICON_SIZE_LARGER, + NAUTILUS_CANVAS_ICON_SIZE_LARGE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + 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)); + g_object_class_install_property (object_class, + PROP_SELECTED, + g_param_spec_boolean ("selected", + "Selected", + "Sets the item as selected", + FALSE, + G_PARAM_READWRITE)); + + 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)); +} + +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)); + + self->icon_size = icon_size; + + g_object_notify (G_OBJECT (self), "icon-size"); +} + +NautilusFile * +nautilus_view_item_model_get_file (NautilusViewItemModel *self) +{ + g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), NULL); + + return self->file; +} + +void +nautilus_view_item_model_set_file (NautilusViewItemModel *self, + NautilusFile *file) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self)); + + g_clear_object (&self->file); + self->file = g_object_ref (file); + + g_object_notify (G_OBJECT (self), "file"); +} + +gboolean +nautilus_view_item_model_get_is_selected (NautilusViewItemModel *self) +{ + g_return_val_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self), FALSE); + + return self->selected; +} + +void +nautilus_view_item_model_set_selected (NautilusViewItemModel *self, + gboolean selected) +{ + g_return_if_fail (NAUTILUS_IS_VIEW_ITEM_MODEL (self)); + + if (self->selected != !!selected) + { + self->selected = !!selected; + g_object_notify (G_OBJECT (self), "selected"); + } +} + +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_clear_object (&self->item_ui); + self->item_ui = g_object_ref (item_ui); + + g_object_notify (G_OBJECT (self), "item-ui"); +} diff --git a/src/nautilus-view-item-model.h b/src/nautilus-view-item-model.h new file mode 100644 index 000000000..39a8bc0dd --- /dev/null +++ b/src/nautilus-view-item-model.h @@ -0,0 +1,41 @@ +#ifndef NAUTILUS_VIEW_ITEM_MODEL_H +#define NAUTILUS_VIEW_ITEM_MODEL_H + +#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_file (NautilusViewItemModel *self, + NautilusFile *file); + +NautilusFile * nautilus_view_item_model_get_file (NautilusViewItemModel *self); + +void nautilus_view_item_model_set_selected (NautilusViewItemModel *self, + gboolean selected); + +gboolean nautilus_view_item_model_get_is_selected (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); + +G_END_DECLS + +#endif /* NAUTILUS_VIEW_ITEM_MODEL_H */ + diff --git a/src/nautilus-view-model.c b/src/nautilus-view-model.c new file mode 100644 index 000000000..4df940636 --- /dev/null +++ b/src/nautilus-view-model.c @@ -0,0 +1,347 @@ +#include "nautilus-view-model.h" +#include "nautilus-view-item-model.h" +#include "nautilus-global-preferences.h" + +struct _NautilusViewModel +{ + GObject parent_instance; + + GHashTable *map_files_to_model; + GListStore *internal_model; + NautilusViewModelSortData *sort_data; +}; + +G_DEFINE_TYPE (NautilusViewModel, nautilus_view_model, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_SORT_TYPE, + PROP_G_MODEL, + N_PROPS +}; + +static void +finalize (GObject *object) +{ + NautilusViewModel *self = NAUTILUS_VIEW_MODEL (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_object_unref (self->internal_model); +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object); + + switch (prop_id) + { + case PROP_SORT_TYPE: + { + g_value_set_object (value, self->sort_data); + } + break; + + case PROP_G_MODEL: + { + g_value_set_object (value, self->internal_model); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object); + + switch (prop_id) + { + case PROP_SORT_TYPE: + { + nautilus_view_model_set_sort_type (self, g_value_get_object (value)); + } + break; + + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } + } +} + +static void +constructed (GObject *object) +{ + NautilusViewModel *self = NAUTILUS_VIEW_MODEL (object); + + G_OBJECT_CLASS (nautilus_view_model_parent_class)->constructed (object); + + self->internal_model = g_list_store_new (NAUTILUS_TYPE_VIEW_ITEM_MODEL); + self->map_files_to_model = g_hash_table_new (NULL, NULL); +} + +static void +nautilus_view_model_class_init (NautilusViewModelClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->constructed = constructed; +} + +static void +nautilus_view_model_init (NautilusViewModel *self) +{ +} + +static gint +compare_data_func (gconstpointer a, + gconstpointer b, + 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)); + + return nautilus_file_compare_for_sort (file_a, file_b, + self->sort_data->sort_type, + self->sort_data->directories_first, + self->sort_data->reversed); +} + +NautilusViewModel * +nautilus_view_model_new () +{ + return g_object_new (NAUTILUS_TYPE_VIEW_MODEL, NULL); +} + +void +nautilus_view_model_set_sort_type (NautilusViewModel *self, + NautilusViewModelSortData *sort_data) +{ + if (self->sort_data) + { + g_free (self->sort_data); + } + + 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_list_store_sort (self->internal_model, compare_data_func, self); +} + +NautilusViewModelSortData * +nautilus_view_model_get_sort_type (NautilusViewModel *self) +{ + return self->sort_data; +} + +GListStore * +nautilus_view_model_get_g_model (NautilusViewModel *self) +{ + return self->internal_model; +} + +GQueue * +nautilus_view_model_get_items_from_files (NautilusViewModel *self, + GQueue *files) +{ + GList *l; + NautilusViewItemModel *item_model; + GQueue *item_models; + + item_models = g_queue_new (); + for (l = g_queue_peek_head_link (files); l != NULL; l = l->next) + { + NautilusFile *file1; + gint i = 0; + + file1 = NAUTILUS_FILE (l->data); + while ((item_model = g_list_model_get_item (G_LIST_MODEL(self->internal_model), i))) + { + NautilusFile *file2; + g_autofree gchar *file1_uri; + g_autofree gchar *file2_uri; + + file2 = nautilus_view_item_model_get_file (item_model); + 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); + break; + } + + i++; + } + } + + return item_models; +} + +NautilusViewItemModel * +nautilus_view_model_get_item_from_file (NautilusViewModel *self, + NautilusFile *file) +{ + return g_hash_table_lookup (self->map_files_to_model, file); +} + +void +nautilus_view_model_remove_item (NautilusViewModel *self, + NautilusViewItemModel *item) +{ + NautilusViewItemModel *item_model; + gint i; + + i = 0; + item_model = NULL; + while ((item_model = g_list_model_get_item (G_LIST_MODEL(self->internal_model), i))) + { + if (item_model == item) + { + break; + } + + i++; + } + + if (item_model != NULL) + { + g_list_store_remove (self->internal_model, i); + } +} + +void +nautilus_view_model_add_item (NautilusViewModel *self, + NautilusViewItemModel *item) +{ + g_list_store_insert_sorted (self->internal_model, item, compare_data_func, self); +} + +void +nautilus_view_model_set_selected (NautilusViewModel *self, + GQueue *item_models) +{ + GList *l; + NautilusViewItemModel *item_model; + + gint i = 0; + while ((item_model = g_list_model_get_item (G_LIST_MODEL(self->internal_model), i))) + { + gboolean selected; + + selected = FALSE; + for (l = g_queue_peek_head_link (item_models); l != NULL; l = l->next) + { + NautilusViewItemModel *selected_item_model; + + selected_item_model = NAUTILUS_VIEW_ITEM_MODEL (l->data); + if (item_model == selected_item_model) + { + selected = TRUE; + break; + } + } + i++; + + nautilus_view_item_model_set_selected (item_model, selected); + } +} + +GQueue * +nautilus_view_model_get_selected (NautilusViewModel *self) +{ + NautilusViewItemModel *item_model; + GQueue *selected_items; + gint i; + + i = 0; + selected_items = g_queue_new (); + while ((item_model = g_list_model_get_item (G_LIST_MODEL(self->internal_model), i))) + { + if (nautilus_view_item_model_get_is_selected (item_model)) + { + g_queue_push_tail (selected_items, + g_object_ref (nautilus_view_item_model_get_file (item_model))); + } + i++; + } + + return selected_items; +} + +static gpointer * +convert_gqueue_to_array (NautilusViewModel *self, + GQueue *queue) +{ + gpointer *array; + GList *l; + int i = 0; + + g_return_val_if_fail (queue != NULL, NULL); + + array = g_malloc_n (g_queue_get_length (queue), + sizeof (NautilusViewItemModel *)); + + for (l = g_queue_peek_head_link (queue); l != NULL; l = l->next) + { + array[i] = l->data; + i++; + } + + return array; +} + +void +nautilus_view_model_set_items (NautilusViewModel *self, + GQueue *items) +{ + + g_autofree gpointer *array = NULL; + GList *l; + int i = 0; + + array = g_malloc_n (g_queue_get_length (items), + sizeof (NautilusViewItemModel *)); + + g_hash_table_remove_all (self->map_files_to_model); + 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), + l->data); + i++; + } + + g_list_store_splice (self->internal_model, + g_list_model_get_n_items (G_LIST_MODEL (self->internal_model)), + 0, array, g_queue_get_length (items)); + + g_list_store_sort (self->internal_model, compare_data_func, self); +} diff --git a/src/nautilus-view-model.h b/src/nautilus-view-model.h new file mode 100644 index 000000000..9734032ed --- /dev/null +++ b/src/nautilus-view-model.h @@ -0,0 +1,43 @@ +#ifndef NAUTILUS_VIEW_MODEL_H +#define NAUTILUS_VIEW_MODEL_H + +#include +#include "nautilus-file.h" +#include "nautilus-view-item-model.h" + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_VIEW_MODEL (nautilus_view_model_get_type()) + +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); +GListStore * nautilus_view_model_get_g_model (NautilusViewModel *self); +NautilusViewItemModel * nautilus_view_model_get_item_from_file (NautilusViewModel *self, + NautilusFile *file); +GQueue * nautilus_view_model_get_items_from_files (NautilusViewModel *self, + GQueue *files); +void nautilus_view_model_remove_item (NautilusViewModel *self, + NautilusViewItemModel *item); +void nautilus_view_model_add_item (NautilusViewModel *self, + NautilusViewItemModel *item); +void nautilus_view_model_set_selected (NautilusViewModel *self, + GQueue *item_models); +GQueue * nautilus_view_model_get_selected (NautilusViewModel *self); +void nautilus_view_model_set_items (NautilusViewModel *self, + GQueue *items); +G_END_DECLS + +#endif /* NAUTILUS_VIEW_MODEL_H */ + diff --git a/src/resources/css/Adwaita.css b/src/resources/css/Adwaita.css index 48beeeecf..fc05297b6 100644 --- a/src/resources/css/Adwaita.css +++ b/src/resources/css/Adwaita.css @@ -184,4 +184,14 @@ searchbar { border-top: 1px solid @borders; } .conflict-row:selected { background: @theme_selected_bg_color; color: @theme_selected_fg_color; -} \ No newline at end of file +} + +/* Icon view */ +flowboxchild:selected{background-color:transparent;} + +flowboxchild > widget > box > .icon-background {padding:0px; background-color:black; border-color:#4a90d9; border-style:solid; border-width:0px;} +flowboxchild:selected > widget > box > .icon-background {padding:0px; background-color:black; border-color:#4a90d9; border-style:solid; border-width:0px;} + +flowboxchild > widget > .icon-item-background {padding:4px;} +flowboxchild:selected > widget > .icon-item-background {padding:4px; background-color:#4a90d9; border-color:#4a90d9; border-style:solid; border-width:0px; border-radius:4px 4px 4px 4px;} + -- cgit v1.2.1