summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2022-10-21 19:42:24 +0000
committerMatthias Clasen <mclasen@redhat.com>2022-10-21 19:42:24 +0000
commitc09ba28b061cd285dc9882a7da03db3ae6fbcaf3 (patch)
tree34ef1105536297f25dacc5b23af35971cb2d7749
parent81de43eba5458a55b06e586dde12da5bf0d1369a (diff)
parent788be8b945719da0f6f5d7e71d9cb9da78c7c3f1 (diff)
downloadgtk+-c09ba28b061cd285dc9882a7da03db3ae6fbcaf3.tar.gz
Merge branch 'gbsneto/filechooser-column-view' into 'main'
Port filechooser to GtkColumnView See merge request GNOME/gtk!5108
-rw-r--r--gtk/gtkactionmuxer.c12
-rw-r--r--gtk/gtkfilechoosercell.c297
-rw-r--r--gtk/gtkfilechoosercellprivate.h37
-rw-r--r--gtk/gtkfilechooserentry.c149
-rw-r--r--gtk/gtkfilechooserutils.c9
-rw-r--r--gtk/gtkfilechooserutils.h2
-rw-r--r--gtk/gtkfilechooserwidget.c2845
-rw-r--r--gtk/gtkfilechooserwidgetprivate.h7
-rw-r--r--gtk/gtkfilesystemmodel.c1044
-rw-r--r--gtk/gtkfilesystemmodel.h37
-rw-r--r--gtk/gtkfilethumbnail.c255
-rw-r--r--gtk/gtkfilethumbnail.h46
-rw-r--r--gtk/gtksearchenginemodel.c45
-rw-r--r--gtk/gtksearchenginetracker3.c9
-rw-r--r--gtk/meson.build2
-rw-r--r--gtk/theme/Default/_common.scss24
-rw-r--r--gtk/ui/gtkfilechooserwidget.ui309
17 files changed, 2162 insertions, 2967 deletions
diff --git a/gtk/gtkactionmuxer.c b/gtk/gtkactionmuxer.c
index d118fdae39..7af7bb413b 100644
--- a/gtk/gtkactionmuxer.c
+++ b/gtk/gtkactionmuxer.c
@@ -27,6 +27,8 @@
#include "gtkmarshalers.h"
#include "gtkwidgetprivate.h"
#include "gsettings-mapping.h"
+#include "gtkdebug.h"
+#include "gtkprivate.h"
#include <string.h>
@@ -846,9 +848,15 @@ gtk_action_muxer_activate_action (GtkActionMuxer *muxer,
if (!_gtk_bitmask_get (muxer->widget_actions_disabled, position))
{
if (action->activate)
- action->activate (muxer->widget, action->name, parameter);
+ {
+ GTK_DEBUG (ACTIONS, "%s: activate action", action->name);
+ action->activate (muxer->widget, action->name, parameter);
+ }
else if (action->pspec)
- prop_action_activate (muxer->widget, action, parameter);
+ {
+ GTK_DEBUG (ACTIONS, "%s: activate prop action", action->pspec->name);
+ prop_action_activate (muxer->widget, action, parameter);
+ }
}
return;
diff --git a/gtk/gtkfilechoosercell.c b/gtk/gtkfilechoosercell.c
new file mode 100644
index 0000000000..655f54c46f
--- /dev/null
+++ b/gtk/gtkfilechoosercell.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "config.h"
+
+#include "gtkfilechoosercellprivate.h"
+
+#include "gtkprivate.h"
+#include "gtkbinlayout.h"
+#include "gtkdragsource.h"
+#include "gtkgestureclick.h"
+#include "gtkgesturelongpress.h"
+#include "gtkicontheme.h"
+#include "gtklistitem.h"
+#include "gtkselectionmodel.h"
+#include "gtkfilechooserutils.h"
+#include "gtkfilechooserwidget.h"
+#include "gtkfilechooserwidgetprivate.h"
+
+struct _GtkFileChooserCell
+{
+ GtkWidget parent_instance;
+
+ GFileInfo *item;
+ gboolean selected;
+ guint position;
+
+ gboolean show_time;
+};
+
+struct _GtkFileChooserCellClass
+{
+ GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE (GtkFileChooserCell, gtk_file_chooser_cell, GTK_TYPE_WIDGET)
+
+enum
+{
+ PROP_POSITION = 1,
+ PROP_SELECTED,
+ PROP_ITEM,
+ PROP_SHOW_TIME,
+};
+
+#define ICON_SIZE 16
+
+static void
+popup_menu (GtkFileChooserCell *self,
+ double x,
+ double y)
+{
+ GtkWidget *widget = GTK_WIDGET (self);
+ GtkWidget *impl;
+ double xx, yy;
+
+ impl = gtk_widget_get_ancestor (widget, GTK_TYPE_FILE_CHOOSER_WIDGET);
+
+ gtk_widget_translate_coordinates (widget, GTK_WIDGET (impl),
+ x, y, &xx, &yy);
+
+ gtk_widget_activate_action (widget, "item.popup-file-list-menu",
+ "(udd)", self->position, xx, yy);
+}
+
+static void
+file_chooser_cell_clicked (GtkEventController *controller,
+ int n_press,
+ double x,
+ double y)
+{
+ GtkWidget *widget = gtk_event_controller_get_widget (controller);
+ GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (widget);
+
+ popup_menu (self, x, y);
+}
+
+static void
+file_chooser_cell_long_pressed (GtkEventController *controller,
+ double x,
+ double y)
+{
+ GtkWidget *widget = gtk_event_controller_get_widget (controller);
+ GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (widget);
+
+ popup_menu (self, x, y);
+}
+
+static GdkContentProvider *
+drag_prepare_cb (GtkDragSource *source,
+ double x,
+ double y,
+ gpointer user_data)
+{
+ GdkContentProvider *provider;
+ GSList *selection;
+ GtkFileChooserWidget *impl;
+ GtkIconTheme *icon_theme;
+ GIcon *icon;
+ int scale;
+ GtkIconPaintable *paintable;
+ GtkFileChooserCell *self = user_data;
+
+ impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (GTK_WIDGET (self),
+ GTK_TYPE_FILE_CHOOSER_WIDGET));
+
+ if (!self->selected)
+ {
+ gtk_selection_model_select_item (gtk_file_chooser_widget_get_selection_model (impl),
+ self->position, TRUE);
+ }
+
+ selection = gtk_file_chooser_widget_get_selected_files (impl);
+ if (!selection)
+ return NULL;
+
+ scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (self)));
+
+ icon = _gtk_file_info_get_icon (self->item, ICON_SIZE, scale, icon_theme);
+
+ paintable = gtk_icon_theme_lookup_by_gicon (icon_theme,icon, ICON_SIZE, scale, GTK_TEXT_DIR_NONE, 0);
+
+ gtk_drag_source_set_icon (source, GDK_PAINTABLE (paintable), x, y);
+
+ provider = gdk_content_provider_new_typed (GDK_TYPE_FILE_LIST, selection);
+ g_slist_free_full (selection, g_object_unref);
+ g_object_unref (paintable);
+ g_object_unref (icon);
+
+ return provider;
+}
+
+static void
+gtk_file_chooser_cell_realize (GtkWidget *widget)
+{
+ GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (widget);
+ GtkFileChooserWidget *impl;
+
+ impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (GTK_WIDGET (self),
+ GTK_TYPE_FILE_CHOOSER_WIDGET));
+
+ g_object_bind_property (impl, "show-time", self, "show-time", G_BINDING_SYNC_CREATE);
+}
+
+static void
+gtk_file_chooser_cell_init (GtkFileChooserCell *self)
+{
+ GtkGesture *gesture;
+ GtkDragSource *drag_source;
+
+ gesture = gtk_gesture_click_new ();
+ gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
+ g_signal_connect (gesture, "pressed", G_CALLBACK (file_chooser_cell_clicked), NULL);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
+
+ gesture = gtk_gesture_long_press_new ();
+ g_signal_connect (gesture, "pressed", G_CALLBACK (file_chooser_cell_long_pressed), NULL);
+
+ drag_source = gtk_drag_source_new ();
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (drag_source));
+ g_signal_connect (drag_source, "prepare", G_CALLBACK (drag_prepare_cb), self);
+
+ g_signal_connect (self, "realize", G_CALLBACK (gtk_file_chooser_cell_realize), NULL);
+}
+
+static void
+gtk_file_chooser_cell_dispose (GObject *object)
+{
+ GtkWidget *child;
+
+ while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
+ gtk_widget_unparent (child);
+
+ G_OBJECT_CLASS (gtk_file_chooser_cell_parent_class)->dispose (object);
+}
+
+static void
+gtk_file_chooser_cell_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (object);
+
+ switch (prop_id)
+ {
+ case PROP_POSITION:
+ self->position = g_value_get_uint (value);
+ break;
+
+ case PROP_SELECTED:
+ self->selected = g_value_get_boolean (value);
+ break;
+
+ case PROP_ITEM:
+ self->item = g_value_get_object (value);
+ break;
+
+ case PROP_SHOW_TIME:
+ self->show_time = g_value_get_boolean (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_file_chooser_cell_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileChooserCell *self = GTK_FILE_CHOOSER_CELL (object);
+
+ switch (prop_id)
+ {
+ case PROP_POSITION:
+ g_value_set_uint (value, self->position);
+ break;
+
+ case PROP_SELECTED:
+ g_value_set_boolean (value, self->selected);
+ break;
+
+ case PROP_ITEM:
+ g_value_set_object (value, self->item);
+ break;
+
+ case PROP_SHOW_TIME:
+ g_value_set_boolean (value, self->show_time);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gtk_file_chooser_cell_class_init (GtkFileChooserCellClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = gtk_file_chooser_cell_dispose;
+ object_class->set_property = gtk_file_chooser_cell_set_property;
+ object_class->get_property = gtk_file_chooser_cell_get_property;
+
+ g_object_class_install_property (object_class, PROP_POSITION,
+ g_param_spec_uint ("position", NULL, NULL,
+ 0, G_MAXUINT, 0,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_SELECTED,
+ g_param_spec_boolean ("selected", NULL, NULL,
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_ITEM,
+ g_param_spec_object ("item", NULL, NULL,
+ G_TYPE_FILE_INFO,
+ GTK_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class, PROP_SHOW_TIME,
+ g_param_spec_boolean ("show-time", NULL, NULL,
+ FALSE,
+ GTK_PARAM_READWRITE));
+
+ gtk_widget_class_set_css_name (widget_class, I_("filelistcell"));
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+}
+
+GtkFileChooserCell *
+gtk_file_chooser_cell_new (void)
+{
+ return g_object_new (GTK_TYPE_FILE_CHOOSER_CELL, NULL);
+}
diff --git a/gtk/gtkfilechoosercellprivate.h b/gtk/gtkfilechoosercellprivate.h
new file mode 100644
index 0000000000..b70371abec
--- /dev/null
+++ b/gtk/gtkfilechoosercellprivate.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Matthias Clasen
+ */
+
+#ifndef __GTK_FILE_CHOOSER_CELL_PRIVATE_H__
+#define __GTK_FILE_CHOOSER_CELL_PRIVATE_H__
+
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkexpression.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_CHOOSER_CELL (gtk_file_chooser_cell_get_type ())
+
+GDK_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (GtkFileChooserCell, gtk_file_chooser_cell, GTK, FILE_CHOOSER_CELL, GtkWidget)
+
+GtkFileChooserCell * gtk_file_chooser_cell_new (void);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_CHOOSER_CELL_PRIVATE_H__ */
diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c
index 39f7c3dceb..4f3d2f8c7f 100644
--- a/gtk/gtkfilechooserentry.c
+++ b/gtk/gtkfilechooserentry.c
@@ -60,7 +60,8 @@ struct _GtkFileChooserEntry
char *dir_part;
char *file_part;
- GtkTreeModel *completion_store;
+ GtkTreeStore *completion_store;
+ GtkFileSystemModel *model;
GtkFileFilter *current_filter;
guint current_folder_loaded : 1;
@@ -71,6 +72,7 @@ struct _GtkFileChooserEntry
enum
{
+ FILE_INFO_COLUMN,
DISPLAY_NAME_COLUMN,
FULL_PATH_COLUMN,
N_COLUMNS
@@ -197,20 +199,21 @@ match_func (GtkEntryCompletion *compl,
* current file filter (e.g. just jpg files) here. */
if (chooser_entry->current_filter != NULL)
{
- GFile *file;
GFileInfo *info;
- file = _gtk_file_system_model_get_file (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
- iter);
- info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
- iter);
+ gtk_tree_model_get (GTK_TREE_MODEL (chooser_entry->completion_store),
+ iter,
+ FILE_INFO_COLUMN, &info,
+ -1);
+
+ g_assert (info != NULL);
+ g_object_unref (info);
/* We always allow navigating into subfolders, so don't ever filter directories */
if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
return TRUE;
- if (!g_file_info_has_attribute (info, "standard::file"))
- g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
+ g_assert (g_file_info_has_attribute (info, "standard::file"));
return gtk_filter_match (GTK_FILTER (chooser_entry->current_filter), info);
}
@@ -428,7 +431,7 @@ explicitly_complete (GtkFileChooserEntry *chooser_entry)
{
chooser_entry->complete_on_load = FALSE;
- if (chooser_entry->completion_store)
+ if (chooser_entry->model)
{
char *completion, *text;
gsize completion_len, text_len;
@@ -539,77 +542,93 @@ update_inline_completion (GtkFileChooserEntry *chooser_entry)
static void
discard_completion_store (GtkFileChooserEntry *chooser_entry)
{
- if (!chooser_entry->completion_store)
+ if (!chooser_entry->model)
return;
+ g_assert (chooser_entry->completion_store != NULL);
+
gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)), NULL);
update_inline_completion (chooser_entry);
- g_object_unref (chooser_entry->completion_store);
- chooser_entry->completion_store = NULL;
+ g_clear_object (&chooser_entry->completion_store);
+ g_clear_object (&chooser_entry->model);
}
-static gboolean
-completion_store_set (GtkFileSystemModel *model,
- GFile *file,
- GFileInfo *info,
- int column,
- GValue *value,
- gpointer data)
+static void
+model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkFileChooserEntry *self)
{
- GtkFileChooserEntry *chooser_entry = data;
+ if (removed > 0)
+ {
+ GtkTreeIter iter;
- const char *prefix = "";
- const char *suffix = "";
+ if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (self->completion_store),
+ &iter,
+ NULL,
+ position))
+ {
+ while (removed--)
+ gtk_tree_store_remove (self->completion_store, &iter);
+ }
+ }
- switch (column)
+ while (added-- > 0)
{
- case FULL_PATH_COLUMN:
- prefix = chooser_entry->dir_part;
- G_GNUC_FALLTHROUGH;
- case DISPLAY_NAME_COLUMN:
+ GtkTreeIter iter;
+ GFileInfo *info;
+ const char *suffix = NULL;
+ char *full_path;
+ char *display_name;
+
+ info = g_list_model_get_item (model, position);
+
if (_gtk_file_info_consider_as_directory (info))
suffix = G_DIR_SEPARATOR_S;
- g_value_take_string (value,
- g_strconcat (prefix,
- g_file_info_get_display_name (info),
- suffix,
- NULL));
- break;
- default:
- g_assert_not_reached ();
- break;
- }
+ display_name = g_strconcat (g_file_info_get_display_name (info), suffix, NULL);
+ full_path = g_strconcat (self->dir_part, display_name, NULL);
- return TRUE;
+ gtk_tree_store_insert_with_values (self->completion_store,
+ &iter, NULL,
+ position,
+ FILE_INFO_COLUMN, info,
+ FULL_PATH_COLUMN, full_path,
+ DISPLAY_NAME_COLUMN, display_name,
+ -1);
+
+ g_clear_object (&info);
+
+ position++;
+ }
}
/* Fills the completion store from the contents of the current folder */
static void
populate_completion_store (GtkFileChooserEntry *chooser_entry)
{
- chooser_entry->completion_store = GTK_TREE_MODEL (
+ chooser_entry->completion_store = gtk_tree_store_new (N_COLUMNS,
+ G_TYPE_FILE_INFO,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ chooser_entry->model =
_gtk_file_system_model_new_for_directory (chooser_entry->current_folder_file,
"standard::name,standard::display-name,standard::type,"
- "standard::content-type",
- completion_store_set,
- chooser_entry,
- N_COLUMNS,
- G_TYPE_STRING,
- G_TYPE_STRING));
- g_signal_connect (chooser_entry->completion_store, "finished-loading",
+ "standard::content-type");
+ g_signal_connect (chooser_entry->model, "items-changed",
+ G_CALLBACK (model_items_changed_cb), chooser_entry);
+ g_signal_connect (chooser_entry->model, "finished-loading",
G_CALLBACK (finished_loading_cb), chooser_entry);
- _gtk_file_system_model_set_filter_folders (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
- TRUE);
- _gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
+ _gtk_file_system_model_set_filter_folders (chooser_entry->model, TRUE);
+ _gtk_file_system_model_set_show_files (chooser_entry->model,
chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SAVE);
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (chooser_entry->completion_store),
- DISPLAY_NAME_COLUMN, GTK_SORT_ASCENDING);
gtk_entry_completion_set_model (gtk_entry_get_completion (GTK_ENTRY (chooser_entry)),
- chooser_entry->completion_store);
+ GTK_TREE_MODEL (chooser_entry->completion_store));
}
/* Callback when the current folder finishes loading */
@@ -658,11 +677,7 @@ set_completion_folder (GtkFileChooserEntry *chooser_entry,
return;
}
- if (chooser_entry->current_folder_file)
- {
- g_object_unref (chooser_entry->current_folder_file);
- chooser_entry->current_folder_file = NULL;
- }
+ g_clear_object (&chooser_entry->current_folder_file);
g_free (chooser_entry->dir_part);
chooser_entry->dir_part = g_strdup (dir_part);
@@ -710,7 +725,7 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry)
g_free (dir_part);
- if (chooser_entry->completion_store &&
+ if (chooser_entry->model &&
(g_strcmp0 (old_file_part, chooser_entry->file_part) != 0))
{
GtkFileFilter *filter;
@@ -720,8 +735,7 @@ refresh_current_folder_and_file_part (GtkFileChooserEntry *chooser_entry)
pattern = g_strconcat (chooser_entry->file_part, "*", NULL);
gtk_file_filter_add_pattern (filter, pattern);
- _gtk_file_system_model_set_filter (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
- filter);
+ _gtk_file_system_model_set_filter (chooser_entry->model, filter);
g_free (pattern);
g_object_unref (filter);
@@ -940,8 +954,8 @@ _gtk_file_chooser_entry_set_action (GtkFileChooserEntry *chooser_entry,
break;
}
- if (chooser_entry->completion_store)
- _gtk_file_system_model_set_show_files (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
+ if (chooser_entry->model)
+ _gtk_file_system_model_set_show_files (chooser_entry->model,
action == GTK_FILE_CHOOSER_ACTION_OPEN ||
action == GTK_FILE_CHOOSER_ACTION_SAVE);
@@ -971,17 +985,14 @@ gboolean
_gtk_file_chooser_entry_get_is_folder (GtkFileChooserEntry *chooser_entry,
GFile *file)
{
- GtkTreeIter iter;
GFileInfo *info;
- if (chooser_entry->completion_store == NULL ||
- !_gtk_file_system_model_get_iter_for_file (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
- &iter,
- file))
+ if (chooser_entry->model == NULL)
return FALSE;
- info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (chooser_entry->completion_store),
- &iter);
+ info = _gtk_file_system_model_get_info_for_file (chooser_entry->model, file);
+ if (!info)
+ return FALSE;
return _gtk_file_info_consider_as_directory (info);
}
diff --git a/gtk/gtkfilechooserutils.c b/gtk/gtkfilechooserutils.c
index 1722ced87b..d75a12b0b4 100644
--- a/gtk/gtkfilechooserutils.c
+++ b/gtk/gtkfilechooserutils.c
@@ -479,3 +479,12 @@ _gtk_file_info_get_icon (GFileInfo *info,
icon = g_themed_icon_new ("text-x-generic");
return icon;
}
+
+GFile *
+_gtk_file_info_get_file (GFileInfo *info)
+{
+ g_assert (G_IS_FILE_INFO (info));
+ g_assert (g_file_info_has_attribute (info, "standard::file"));
+
+ return G_FILE (g_file_info_get_attribute_object (info, "standard::file"));
+}
diff --git a/gtk/gtkfilechooserutils.h b/gtk/gtkfilechooserutils.h
index 9481704ad9..3a41403802 100644
--- a/gtk/gtkfilechooserutils.h
+++ b/gtk/gtkfilechooserutils.h
@@ -58,6 +58,8 @@ GIcon * _gtk_file_info_get_icon (GFileInfo *info,
int scale,
GtkIconTheme *icon_theme);
+GFile * _gtk_file_info_get_file (GFileInfo *info);
+
G_END_DECLS
#endif /* __GTK_FILE_CHOOSER_UTILS_H__ */
diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c
index d4eb2537c9..7d18ea03df 100644
--- a/gtk/gtkfilechooserwidget.c
+++ b/gtk/gtkfilechooserwidget.c
@@ -22,13 +22,13 @@
#include "gtkfilechooserwidget.h"
#include "gtkfilechooserwidgetprivate.h"
+#include "gtkbitset.h"
#include "gtkbookmarksmanagerprivate.h"
#include "gtkbutton.h"
-#include "deprecated/gtkcellrendererpixbuf.h"
-#include "deprecated/gtkcellrenderertext.h"
#include "gtkdropdown.h"
+#include "gtkcolumnview.h"
+#include "gtkcolumnviewcolumn.h"
#include "gtkcssnumbervalueprivate.h"
-#include "gtkdragsource.h"
#include "gtkdroptarget.h"
#include "gtkentry.h"
#include "gtkfilechooserprivate.h"
@@ -37,12 +37,14 @@
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
#include "gtkfilesystemmodel.h"
+#include "gtkfilethumbnail.h"
#include "gtkgrid.h"
-#include "gtkicontheme.h"
#include "gtklabel.h"
+#include "gtklistitem.h"
#include "gtkmarshalers.h"
#include "gtkmessagedialog.h"
#include "gtkmountoperation.h"
+#include "gtkmultiselection.h"
#include "gtkpaned.h"
#include "gtkpathbarprivate.h"
#include "gtkplacessidebarprivate.h"
@@ -51,13 +53,11 @@
#include "gtkrecentmanager.h"
#include "gtksearchentryprivate.h"
#include "gtksettings.h"
+#include "gtksingleselection.h"
#include "gtksizegroup.h"
#include "gtksizerequest.h"
#include "gtkstack.h"
#include "gtktooltip.h"
-#include "deprecated/gtktreednd.h"
-#include "deprecated/gtktreeprivate.h"
-#include "deprecated/gtktreeselection.h"
#include "gtkbox.h"
#include "gtkcheckbutton.h"
#include "gtkwindowgroup.h"
@@ -70,8 +70,6 @@
#include "gtkspinner.h"
#include "gtkseparator.h"
#include "gtkmodelbuttonprivate.h"
-#include "gtkgesturelongpress.h"
-#include "gtkgestureclick.h"
#include "gtkeventcontrollerkey.h"
#include "gtkdebug.h"
#include "gtkfilechoosererrorstackprivate.h"
@@ -86,6 +84,13 @@
#include "gtkshortcutaction.h"
#include "gtkshortcut.h"
#include "gtkstringlist.h"
+#include "gtkfilterlistmodel.h"
+#include "gtkcustomfilter.h"
+#include "gtkcustomsorter.h"
+#include "gtkstringsorter.h"
+#include "gtkmultisorter.h"
+#include "gtkcolumnviewsorter.h"
+#include "gtkexpression.h"
#ifndef G_OS_WIN32
#include "gopenuriportal.h"
@@ -121,7 +126,8 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
enum {
PROP_SEARCH_MODE = 1,
- PROP_SUBTITLE
+ PROP_SUBTITLE,
+ PROP_SHOW_TIME,
};
typedef enum {
@@ -186,16 +192,27 @@ struct _GtkFileChooserWidget
GtkWidget *save_widgets;
GtkWidget *save_widgets_table;
+ /* This is the model that the column view always show. Changing
+ * between browsing, search, and recent, sets the model of the
+ * selection model.
+ */
+ GtkSelectionModel *selection_model;
+ GtkSortListModel *sort_model;
+ GtkFilterListModel *filter_model;
+
/* The file browsing widgets */
GtkWidget *browse_widgets_hpaned;
GtkWidget *browse_header_revealer;
GtkWidget *browse_header_stack;
GtkWidget *browse_files_stack;
GtkWidget *browse_files_swin;
- GtkWidget *browse_files_tree_view;
+ GtkWidget *browse_files_column_view;
GtkWidget *remote_warning_bar;
GtkWidget *browse_files_popover;
+ guint browse_files_popover_item;
+ double browse_files_popover_x;
+ double browse_files_popover_y;
GtkWidget *browse_new_folder_button;
GtkSizeGroup *browse_path_bar_size_group;
@@ -268,18 +285,11 @@ struct _GtkFileChooserWidget
GFile *current_folder;
GFile *renamed_file;
- GtkTreeViewColumn *list_name_column;
- GtkCellRenderer *list_name_renderer;
- GtkCellRenderer *list_pixbuf_renderer;
- GtkTreeViewColumn *list_time_column;
- GtkCellRenderer *list_date_renderer;
- GtkCellRenderer *list_time_renderer;
- GtkTreeViewColumn *list_size_column;
- GtkCellRenderer *list_size_renderer;
- GtkTreeViewColumn *list_type_column;
- GtkCellRenderer *list_type_renderer;
- GtkTreeViewColumn *list_location_column;
- GtkCellRenderer *list_location_renderer;
+ GtkColumnViewColumn *column_view_name_column;
+ GtkColumnViewColumn *column_view_location_column;
+ GtkColumnViewColumn *column_view_size_column;
+ GtkColumnViewColumn *column_view_time_column;
+ GtkColumnViewColumn *column_view_type_column;
guint location_changed_id;
@@ -304,7 +314,6 @@ struct _GtkFileChooserWidget
guint show_hidden : 1;
guint sort_directories_first : 1;
guint show_time : 1;
- guint list_sort_ascending : 1;
guint shortcuts_current_folder_active : 1;
guint show_size_column : 1;
guint show_type_column : 1;
@@ -348,47 +357,9 @@ static guint signals[LAST_SIGNAL] = { 0 };
"standard::content-type,standard::fast-content-type,time::modified,time::access," \
"access::can-rename,access::can-delete,access::can-trash," \
"standard::target-uri"
-enum {
- /* the first 4 must be these due to settings caching sort column */
- MODEL_COL_NAME,
- MODEL_COL_SIZE,
- MODEL_COL_TYPE,
- MODEL_COL_TIME,
- MODEL_COL_FILE,
- MODEL_COL_NAME_COLLATED,
- MODEL_COL_IS_FOLDER,
- MODEL_COL_IS_SENSITIVE,
- MODEL_COL_ICON,
- MODEL_COL_SIZE_TEXT,
- MODEL_COL_DATE_TEXT,
- MODEL_COL_TIME_TEXT,
- MODEL_COL_LOCATION_TEXT,
- MODEL_COL_ELLIPSIZE,
- MODEL_COL_NUM_COLUMNS
-};
-
-/* This list of types is passed to _gtk_file_system_model_new*() */
-#define MODEL_COLUMN_TYPES \
- MODEL_COL_NUM_COLUMNS, \
- G_TYPE_STRING, /* MODEL_COL_NAME */ \
- G_TYPE_INT64, /* MODEL_COL_SIZE */ \
- G_TYPE_STRING, /* MODEL_COL_TYPE */ \
- G_TYPE_LONG, /* MODEL_COL_TIME */ \
- G_TYPE_FILE, /* MODEL_COL_FILE */ \
- G_TYPE_STRING, /* MODEL_COL_NAME_COLLATED */ \
- G_TYPE_BOOLEAN, /* MODEL_COL_IS_FOLDER */ \
- G_TYPE_BOOLEAN, /* MODEL_COL_IS_SENSITIVE */ \
- G_TYPE_ICON, /* MODEL_COL_ICON */ \
- G_TYPE_STRING, /* MODEL_COL_SIZE_TEXT */ \
- G_TYPE_STRING, /* MODEL_COL_DATE_TEXT */ \
- G_TYPE_STRING, /* MODEL_COL_TIME_TEXT */ \
- G_TYPE_STRING, /* MODEL_COL_LOCATION_TEXT */ \
- PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */
#define DEFAULT_RECENT_FILES_LIMIT 50
-#define ICON_SIZE 16
-
static void gtk_file_chooser_widget_iface_init (GtkFileChooserIface *iface);
static void gtk_file_chooser_widget_constructed (GObject *object);
@@ -406,8 +377,6 @@ static void gtk_file_chooser_widget_map (GtkWidget *w
static void gtk_file_chooser_widget_unmap (GtkWidget *widget);
static void gtk_file_chooser_widget_root (GtkWidget *widget);
static void gtk_file_chooser_widget_unroot (GtkWidget *widget);
-static void gtk_file_chooser_widget_css_changed (GtkWidget *widget,
- GtkCssStyleChange *change);
static gboolean gtk_file_chooser_widget_set_current_folder (GtkFileChooser *chooser,
GFile *folder,
@@ -473,7 +442,6 @@ static void search_shortcut_handler (GtkFileChooserWidget *impl);
static void recent_shortcut_handler (GtkFileChooserWidget *impl);
static void places_shortcut_handler (GtkFileChooserWidget *impl);
static void update_appearance (GtkFileChooserWidget *impl);
-static void check_icon_theme (GtkFileChooserWidget *impl);
static void operation_mode_set (GtkFileChooserWidget *impl, OperationMode mode);
static void location_mode_set (GtkFileChooserWidget *impl, LocationMode new_mode);
@@ -484,39 +452,28 @@ static void set_current_filter (GtkFileChooserWidget *impl,
static void filter_combo_changed (GtkDropDown *dropdown,
GParamSpec *pspec,
GtkFileChooserWidget *impl);
-
-static gboolean list_select_func (GtkTreeSelection *selection,
- GtkTreeModel *model,
- GtkTreePath *path,
- gboolean path_currently_selected,
- gpointer data);
-
-static void list_selection_changed (GtkTreeSelection *tree_selection,
- GtkFileChooserWidget *impl);
-static void list_row_activated (GtkTreeView *tree_view,
- GtkTreePath *path,
- GtkTreeViewColumn *column,
- GtkFileChooserWidget *impl);
-
+static void list_selection_changed (GtkSelectionModel *selection_model,
+ guint position,
+ guint n_items,
+ GtkFileChooserWidget *impl);
+static void list_items_changed (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkFileChooserWidget *impl);
static void path_bar_clicked (GtkPathBar *path_bar,
GFile *file,
GFile *child,
gboolean child_is_hidden,
GtkFileChooserWidget *impl);
-static void update_cell_renderer_attributes (GtkFileChooserWidget *impl);
-
static void load_remove_timer (GtkFileChooserWidget *impl, LoadState new_load_state);
-static void browse_files_center_selected_row (GtkFileChooserWidget *impl);
static void location_switch_to_path_bar (GtkFileChooserWidget *impl);
static void stop_loading_and_clear_list_model (GtkFileChooserWidget *impl,
gboolean remove_from_treeview);
-static GSList *get_selected_files (GtkFileChooserWidget *impl);
-static GSList *get_selected_infos (GtkFileChooserWidget *impl);
-
static void search_setup_widgets (GtkFileChooserWidget *impl);
static void search_stop_searching (GtkFileChooserWidget *impl,
gboolean remove_query);
@@ -533,15 +490,17 @@ static gboolean recent_files_setting_is_enabled (GtkFileChooserWidget *impl);
static void recent_start_loading (GtkFileChooserWidget *impl);
static void recent_clear_model (GtkFileChooserWidget *impl,
gboolean remove_from_treeview);
-static gboolean recent_should_respond (GtkFileChooserWidget *impl);
-static void clear_model_cache (GtkFileChooserWidget *impl,
- int column);
static void set_model_filter (GtkFileChooserWidget *impl,
GtkFileFilter *filter);
static void switch_to_home_dir (GtkFileChooserWidget *impl);
static void set_show_hidden (GtkFileChooserWidget *impl,
gboolean show_hidden);
-
+static char * get_type_information (GtkFileChooserWidget *impl,
+ GFileInfo *info);
+static char * my_g_format_date_for_display (GtkFileChooserWidget *impl,
+ glong secs);
+static char * my_g_format_time_for_display (GtkFileChooserWidget *impl,
+ glong secs);
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserWidget, gtk_file_chooser_widget, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER,
@@ -612,6 +571,10 @@ gtk_file_chooser_widget_finalize (GObject *object)
recent_clear_model (impl, FALSE);
g_clear_object (&impl->model_for_search);
+ g_clear_object (&impl->selection_model);
+ g_clear_object (&impl->sort_model);
+ g_clear_object (&impl->filter_model);
+
/* stopping the load above should have cleared this */
g_assert (impl->load_timeout_id == 0);
@@ -631,6 +594,24 @@ get_toplevel (GtkWidget *widget)
return NULL;
}
+static void setup_sorting (GtkFileChooserWidget *impl);
+
+static GListModel *
+get_current_model (GtkFileChooserWidget *self)
+{
+ return gtk_filter_list_model_get_model (self->filter_model);
+}
+
+static void
+set_current_model (GtkFileChooserWidget *self,
+ GListModel *model)
+{
+ gtk_filter_list_model_set_model (self->filter_model, model);
+ gtk_filter_changed (gtk_filter_list_model_get_filter (self->filter_model),
+ GTK_FILTER_CHANGE_DIFFERENT);
+ setup_sorting (self);
+}
+
/* Extracts the parent folders out of the supplied list of GtkRecentInfo* items, and returns
* a list of GFile* for those unique parents.
*/
@@ -996,71 +977,52 @@ new_folder_create_clicked (GtkButton *button,
error_creating_folder_dialog (impl, file, error);
}
-struct selection_check_closure {
- GtkFileChooserWidget *impl;
- int num_selected;
- gboolean all_files;
- gboolean all_folders;
-};
-
-/* Used from gtk_tree_selection_selected_foreach() */
+/* Checks whether the selected items in the file list are all files or all folders */
static void
-selection_check_foreach_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
+selection_check (GtkFileChooserWidget *impl,
+ int *out_num_selected,
+ gboolean *out_all_files,
+ gboolean *out_all_folders)
{
- struct selection_check_closure *closure;
- gboolean is_folder;
- GFile *file;
-
- gtk_tree_model_get (model, iter,
- MODEL_COL_FILE, &file,
- MODEL_COL_IS_FOLDER, &is_folder,
- -1);
-
- if (file == NULL)
- return;
-
- g_object_unref (file);
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ gboolean all_folders;
+ gboolean all_files;
+ guint i;
+ int n_selected;
- closure = data;
- closure->num_selected++;
+ all_folders = TRUE;
+ all_files = TRUE;
+ n_selected = 0;
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
- closure->all_folders = closure->all_folders && is_folder;
- closure->all_files = closure->all_files && !is_folder;
-}
+ for (gtk_bitset_iter_init_first (&iter, bitset, &i);
+ gtk_bitset_iter_is_valid (&iter);
+ gtk_bitset_iter_next (&iter, &i))
+ {
+ GFileInfo *info;
+ gboolean is_folder;
-/* Checks whether the selected items in the file list are all files or all folders */
-static void
-selection_check (GtkFileChooserWidget *impl,
- int *num_selected,
- gboolean *all_files,
- gboolean *all_folders)
-{
- struct selection_check_closure closure;
- GtkTreeSelection *selection;
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ is_folder = _gtk_file_info_consider_as_directory (info);
- closure.impl = impl;
- closure.num_selected = 0;
- closure.all_files = TRUE;
- closure.all_folders = TRUE;
+ all_folders &= is_folder;
+ all_files &= !is_folder;
+ n_selected++;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection,
- selection_check_foreach_cb,
- &closure);
+ g_clear_object (&info);
+ }
- g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
+ g_assert (n_selected == 0 || !(all_files && all_folders));
- if (num_selected)
- *num_selected = closure.num_selected;
+ if (out_num_selected)
+ *out_num_selected = n_selected;
- if (all_files)
- *all_files = closure.all_files;
+ if (out_all_files)
+ *out_all_files = all_files;
- if (all_folders)
- *all_folders = closure.all_folders;
+ if (out_all_folders)
+ *out_all_folders = all_folders;
}
static gboolean
@@ -1128,27 +1090,6 @@ trigger_location_entry (GtkWidget *widget,
return TRUE;
}
-/* Callback used from gtk_tree_selection_selected_foreach(); adds a bookmark for
- * each selected item in the file list.
- */
-static void
-add_bookmark_foreach_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- GtkFileChooserWidget *impl = (GtkFileChooserWidget *) data;
- GFile *file;
-
- gtk_tree_model_get (model, iter,
- MODEL_COL_FILE, &file,
- -1);
-
- _gtk_bookmarks_manager_insert_bookmark (impl->bookmarks_manager, file, 0, NULL); /* NULL-GError */
-
- g_object_unref (file);
-}
-
/* Callback used when the "Add to Bookmarks" menu item is activated */
static void
add_to_shortcuts_cb (GSimpleAction *action,
@@ -1156,13 +1097,26 @@ add_to_shortcuts_cb (GSimpleAction *action,
gpointer data)
{
GtkFileChooserWidget *impl = data;
- GtkTreeSelection *selection;
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ guint i;
+
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
+
+ for (gtk_bitset_iter_init_first (&iter, bitset, &i);
+ gtk_bitset_iter_is_valid (&iter);
+ gtk_bitset_iter_next (&iter, &i))
+ {
+ GFileInfo *info;
+ GFile *file;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ file = _gtk_file_info_get_file (info);
- gtk_tree_selection_selected_foreach (selection,
- add_bookmark_foreach_cb,
- impl);
+ _gtk_bookmarks_manager_insert_bookmark (impl->bookmarks_manager, file, 0, NULL);
+
+ g_clear_object (&info);
+ }
}
typedef struct {
@@ -1231,49 +1185,32 @@ confirm_delete (GtkFileChooserWidget *impl,
}
static void
-delete_selected_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- GtkFileChooserWidget *impl = data;
- GFile *file;
- GFileInfo *info;
-
- file = _gtk_file_system_model_get_file (GTK_FILE_SYSTEM_MODEL (model), iter);
- info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (model), iter);
-
- confirm_delete (impl, file, info);
-}
-
-static void
delete_file_cb (GSimpleAction *action,
GVariant *parameter,
gpointer data)
{
GtkFileChooserWidget *impl = data;
- GtkTreeSelection *selection;
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ guint i;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, delete_selected_cb, impl);
-}
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
-static void
-trash_selected_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- GtkFileChooserWidget *impl = data;
- GFile *file;
- GError *error = NULL;
+ for (gtk_bitset_iter_init_first (&iter, bitset, &i);
+ gtk_bitset_iter_is_valid (&iter);
+ gtk_bitset_iter_next (&iter, &i))
+ {
+ GFileInfo *info;
+ GFile *file;
- file = _gtk_file_system_model_get_file (GTK_FILE_SYSTEM_MODEL (model), iter);
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ file = _gtk_file_info_get_file (info);
- if (!g_file_trash (file, NULL, &error))
- error_trashing_file (impl, file, error);
-}
+ confirm_delete (impl, file, info);
+ g_clear_object (&info);
+ }
+}
static void
trash_file_cb (GSimpleAction *action,
@@ -1281,10 +1218,28 @@ trash_file_cb (GSimpleAction *action,
gpointer data)
{
GtkFileChooserWidget *impl = data;
- GtkTreeSelection *selection;
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ guint i;
+
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, trash_selected_cb, impl);
+ for (gtk_bitset_iter_init_first (&iter, bitset, &i);
+ gtk_bitset_iter_is_valid (&iter);
+ gtk_bitset_iter_next (&iter, &i))
+ {
+ GFileInfo *info;
+ GError *error = NULL;
+ GFile *file;
+
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ file = _gtk_file_info_get_file (info);
+
+ if (!g_file_trash (file, NULL, &error))
+ error_trashing_file (impl, file, error);
+
+ g_clear_object (&info);
+ }
}
static void
@@ -1352,32 +1307,28 @@ rename_file_rename_clicked (GtkButton *button,
}
static void
-rename_selected_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
+rename_file_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
{
GtkFileChooserWidget *impl = data;
GdkRectangle rect;
+ GFileInfo *info;
+ GFile *file;
char *filename;
- double x, y;
- gtk_tree_model_get (model, iter,
- MODEL_COL_FILE, &impl->rename_file_source_file,
- -1);
+ /* insensitive until we change the name */
+ gtk_widget_set_sensitive (impl->rename_file_rename_button, FALSE);
+
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), impl->browse_files_popover_item);
+ file = _gtk_file_info_get_file (info);
+ g_clear_object (&info);
- gtk_tree_view_get_cell_area (GTK_TREE_VIEW (impl->browse_files_tree_view),
- path, impl->list_name_column, &rect);
+ impl->rename_file_source_file = g_object_ref (file);
+ rect = (GdkRectangle) { 0, 0, 1, 1 };
- gtk_tree_view_convert_bin_window_to_widget_coords (GTK_TREE_VIEW (impl->browse_files_tree_view),
- rect.x, rect.y,
- &rect.x, &rect.y);
- gtk_widget_translate_coordinates (impl->browse_files_tree_view,
- GTK_WIDGET (impl),
- rect.x, rect.y,
- &x, &y);
- rect.x = x;
- rect.y = y;
+ rect.x = impl->browse_files_popover_x;
+ rect.y = impl->browse_files_popover_y;
filename = g_file_get_basename (impl->rename_file_source_file);
gtk_editable_set_text (GTK_EDITABLE (impl->rename_file_name_entry), filename);
@@ -1388,21 +1339,6 @@ rename_selected_cb (GtkTreeModel *model,
gtk_widget_grab_focus (impl->rename_file_popover);
}
-static void
-rename_file_cb (GSimpleAction *action,
- GVariant *parameter,
- gpointer data)
-{
- GtkFileChooserWidget *impl = data;
- GtkTreeSelection *selection;
-
- /* insensitive until we change the name */
- gtk_widget_set_sensitive (impl->rename_file_rename_button, FALSE);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, rename_selected_cb, impl);
-}
-
/* Callback used when the "Copy file’s location" menu item is activated */
static void
copy_file_location_cb (GSimpleAction *action,
@@ -1410,18 +1346,18 @@ copy_file_location_cb (GSimpleAction *action,
gpointer data)
{
GtkFileChooserWidget *impl = data;
- GSList *selected_files = NULL;
+ GFileInfo *info;
+ GFile *file;
+ GdkClipboard *clipboard;
- selected_files = get_selected_files (impl);
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), impl->browse_files_popover_item);
+ file = _gtk_file_info_get_file (info);
- if (selected_files)
- {
- GdkClipboard *clipboard;
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl));
- clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl));
- gdk_clipboard_set (clipboard, GDK_TYPE_FILE_LIST, selected_files);
- g_slist_free_full (selected_files, g_object_unref);
- }
+ gdk_clipboard_set (clipboard, G_TYPE_FILE, file);
+
+ g_clear_object (&info);
}
/* Callback used when the "Visit this file" menu item is activated */
@@ -1431,19 +1367,15 @@ visit_file_cb (GSimpleAction *action,
gpointer data)
{
GtkFileChooserWidget *impl = data;
- GSList *files;
-
- files = get_selected_files (impl);
+ GFileInfo *info;
+ GFile *file;
- /* Sigh, just use the first one */
- if (files)
- {
- GFile *file = files->data;
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), impl->browse_files_popover_item);
+ file = _gtk_file_info_get_file (info);
- gtk_file_chooser_widget_select_file (GTK_FILE_CHOOSER (impl), file, NULL); /* NULL-GError */
- }
+ gtk_file_chooser_widget_select_file (GTK_FILE_CHOOSER (impl), file, NULL); /* NULL-GError */
- g_slist_free_full (files, g_object_unref);
+ g_clear_object (&info);
}
#define FILE_MANAGER_DBUS_NAME "org.freedesktop.FileManager1"
@@ -1459,17 +1391,12 @@ open_folder_cb (GSimpleAction *action,
GtkFileChooserWidget *impl = data;
GtkRoot *root = gtk_widget_get_root (GTK_WIDGET (impl));
GtkWindow *toplevel = GTK_IS_WINDOW (root) ? GTK_WINDOW (root) : NULL;
- GSList *files;
+ GFileInfo *info;
GFile *file;
char *uri;
- files = get_selected_files (impl);
-
- if (!files)
- return;
-
- /* Sigh, just use the first one */
- file = files->data;
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), impl->browse_files_popover_item);
+ file = _gtk_file_info_get_file (info);
#ifdef G_OS_WIN32
@@ -1529,7 +1456,7 @@ open_folder_cb (GSimpleAction *action,
#endif
- g_slist_free_full (files, g_object_unref);
+ g_clear_object (&info);
}
/* callback used when the "Show Hidden Files" menu item is toggled */
@@ -1555,8 +1482,8 @@ change_show_size_state (GSimpleAction *action,
g_simple_action_set_state (action, state);
impl->show_size_column = g_variant_get_boolean (state);
- gtk_tree_view_column_set_visible (impl->list_size_column,
- impl->show_size_column);
+ gtk_column_view_column_set_visible (impl->column_view_size_column,
+ impl->show_size_column);
}
/* Callback used when the "Show Type Column" menu item is toggled */
@@ -1570,8 +1497,8 @@ change_show_type_state (GSimpleAction *action,
g_simple_action_set_state (action, state);
impl->show_type_column = g_variant_get_boolean (state);
- gtk_tree_view_column_set_visible (impl->list_type_column,
- impl->show_type_column);
+ gtk_column_view_column_set_visible (impl->column_view_type_column,
+ impl->show_type_column);
}
static void
@@ -1580,36 +1507,12 @@ change_sort_directories_first_state (GSimpleAction *action,
gpointer data)
{
GtkFileChooserWidget *impl = data;
- GtkTreeSortable *sortable;
g_simple_action_set_state (action, state);
impl->sort_directories_first = g_variant_get_boolean (state);
- /* force resorting */
- sortable = GTK_TREE_SORTABLE (impl->browse_files_model);
- if (sortable == NULL)
- return;
-
- gtk_tree_sortable_set_sort_column_id (sortable,
- GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
- impl->sort_order);
- gtk_tree_sortable_set_sort_column_id (sortable,
- impl->sort_column,
- impl->sort_order);
-}
-
-static void
-clear_model_cache (GtkFileChooserWidget *impl,
- int column)
-{
- if (impl->browse_files_model)
- _gtk_file_system_model_clear_cache (impl->browse_files_model, column);
-
- if (impl->search_model)
- _gtk_file_system_model_clear_cache (impl->search_model, column);
-
- if (impl->recent_model)
- _gtk_file_system_model_clear_cache (impl->recent_model, column);
+ gtk_sorter_changed (gtk_sort_list_model_get_sorter (impl->sort_model),
+ GTK_SORTER_CHANGE_DIFFERENT);
}
static void
@@ -1624,17 +1527,9 @@ set_model_filter (GtkFileChooserWidget *impl,
if (impl->recent_model)
_gtk_file_system_model_set_filter (impl->recent_model, filter);
-}
-static void
-update_time_renderer_visible (GtkFileChooserWidget *impl)
-{
- g_object_set (impl->list_time_renderer,
- "visible", impl->show_time,
- NULL);
- clear_model_cache (impl, MODEL_COL_DATE_TEXT);
- clear_model_cache (impl, MODEL_COL_TIME_TEXT);
- gtk_widget_queue_draw (impl->browse_files_tree_view);
+ gtk_filter_changed (gtk_filter_list_model_get_filter (impl->filter_model),
+ GTK_FILTER_CHANGE_DIFFERENT);
}
static void
@@ -1646,7 +1541,7 @@ change_show_time_state (GSimpleAction *action,
g_simple_action_set_state (action, state);
impl->show_time = g_variant_get_boolean (state);
- update_time_renderer_visible (impl);
+ g_object_notify (G_OBJECT (impl), "show-time");
}
/* Shows an error dialog about not being able to select a dragged file */
@@ -1713,8 +1608,6 @@ file_list_drag_data_received_get_info_cb (GObject *source,
gtk_file_chooser_widget_select_file (chooser, data->files->data, &local_error);
if (local_error)
error_selecting_dragged_file_dialog (data->impl, data->files->data, local_error);
- else
- browse_files_center_selected_row (data->impl);
}
if (impl->select_multiple)
@@ -1760,87 +1653,77 @@ file_list_drag_drop_cb (GtkDropTarget *dest,
return TRUE;
}
-/* Sensitizes the "Copy file’s location" and other context menu items if there is actually
- * a selection active.
- */
static void
check_file_list_popover_sensitivity (GtkFileChooserWidget *impl)
{
- int num_selected;
- gboolean all_files;
- gboolean all_folders;
- gboolean active;
- GAction *action, *action2;
-
- selection_check (impl, &num_selected, &all_files, &all_folders);
+ GAction *action;
+ GFileInfo *info;
+ gboolean is_folder;
+ GSimpleAction *delete_action, *trash_action;
- active = (num_selected != 0);
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), impl->browse_files_popover_item);
+ is_folder = _gtk_file_info_consider_as_directory (info);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "copy-location");
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), active);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "add-shortcut");
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), active && all_folders);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), is_folder);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "visit");
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), active);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "open");
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (num_selected == 1) && all_folders);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), is_folder);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "rename");
- if (num_selected == 1)
- {
- GSList *infos;
- GFileInfo *info;
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
+ g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME));
- infos = get_selected_infos (impl);
- info = G_FILE_INFO (infos->data);
+ action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "delete");
+ delete_action = G_SIMPLE_ACTION (action);
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
- g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME));
+ action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "trash");
+ trash_action = G_SIMPLE_ACTION (action);
- g_slist_free_full (infos, g_object_unref);
+ if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH))
+ {
+ g_simple_action_set_enabled (trash_action, TRUE);
+ g_simple_action_set_enabled (delete_action, FALSE);
+ }
+ else if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE))
+ {
+ g_simple_action_set_enabled (delete_action, TRUE);
+ g_simple_action_set_enabled (trash_action, FALSE);
}
else
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
+ {
+ g_simple_action_set_enabled (trash_action, FALSE);
+ g_simple_action_set_enabled (delete_action, FALSE);
+ }
+}
- action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "delete");
- action2 = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "trash");
+static void
+file_list_show_popover (GtkFileChooserWidget *impl,
+ double x,
+ double y);
- if (num_selected == 1)
- {
- GSimpleAction *delete_action = G_SIMPLE_ACTION (action);
- GSimpleAction *trash_action = G_SIMPLE_ACTION (action2);
- GSList *infos;
- GFileInfo *info;
+static void
+popup_file_list_menu (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ GtkFileChooserWidget *impl = user_data;
+ guint position;
+ double x, y;
- infos = get_selected_infos (impl);
- info = G_FILE_INFO (infos->data);
+ g_variant_get (parameter, "(udd)", &position, &x, &y);
- if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH))
- {
- g_simple_action_set_enabled (trash_action, TRUE);
- g_simple_action_set_enabled (delete_action, FALSE);
- }
- else if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE))
- {
- g_simple_action_set_enabled (delete_action, TRUE);
- g_simple_action_set_enabled (trash_action, FALSE);
- }
- else
- {
- g_simple_action_set_enabled (trash_action, FALSE);
- g_simple_action_set_enabled (delete_action, FALSE);
- }
+ impl->browse_files_popover_item = position;
+ impl->browse_files_popover_x = x;
+ impl->browse_files_popover_y = y;
- g_slist_free_full (infos, g_object_unref);
- }
- else
- {
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action2), FALSE);
- }
+ file_list_show_popover (impl, x, y);
}
static GActionEntry entries[] = {
@@ -1851,6 +1734,7 @@ static GActionEntry entries[] = {
{ "rename", rename_file_cb, NULL, NULL, NULL },
{ "delete", delete_file_cb, NULL, NULL, NULL },
{ "trash", trash_file_cb, NULL, NULL, NULL },
+ { "popup-file-list-menu", popup_file_list_menu, "(udd)", NULL, NULL },
{ "toggle-show-hidden", NULL, NULL, "false", change_show_hidden_state },
{ "toggle-show-size", NULL, NULL, "false", change_show_size_state },
{ "toggle-show-type", NULL, NULL, "false", change_show_type_state },
@@ -1943,18 +1827,17 @@ file_list_update_popover (GtkFileChooserWidget *impl)
/* The sensitivity of the Add to Bookmarks item is set in
* bookmarks_check_add_sensitivity()
*/
- state = impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
- impl->operation_mode != OPERATION_MODE_BROWSE;
+ state = impl->action == GTK_FILE_CHOOSER_ACTION_SAVE &&
+ impl->operation_mode == OPERATION_MODE_BROWSE;
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "rename");
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !state);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), state);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "delete");
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !state);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), state);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "trash");
- g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !state);
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), state);
action = g_action_map_lookup_action (G_ACTION_MAP (impl->item_actions), "visit");
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (impl->operation_mode != OPERATION_MODE_BROWSE));
@@ -1981,46 +1864,11 @@ file_list_show_popover (GtkFileChooserWidget *impl,
double y)
{
GdkRectangle rect;
- GtkTreeSelection *selection;
- GtkTreeModel *model;
- GList *list;
- GtkTreePath *path;
- graphene_rect_t bounds;
-
- if (!gtk_widget_compute_bounds (impl->browse_files_tree_view,
- impl->browse_files_tree_view,
- &bounds))
- return;
file_list_update_popover (impl);
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- list = gtk_tree_selection_get_selected_rows (selection, &model);
- if (list)
- {
- path = list->data;
- gtk_tree_view_get_cell_area (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, &rect);
- gtk_tree_view_convert_bin_window_to_widget_coords (GTK_TREE_VIEW (impl->browse_files_tree_view),
- rect.x, rect.y,
- &rect.x, &rect.y);
- gtk_widget_translate_coordinates (impl->browse_files_tree_view,
- GTK_WIDGET (impl),
- rect.x, rect.y,
- &x, &y);
-
- rect.x = CLAMP (x - 20, 0, bounds.size.width - 40);
- rect.y = y;
- rect.width = 40;
-
- g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
- }
- else
- {
- rect.x = x;
- rect.y = y;
- rect.width = 1;
- rect.height = 1;
- }
+ /* TODO: figure out popover position */
+ rect = (GdkRectangle) { (int) x, (int) y, 1, 1 };
gtk_popover_set_pointing_to (GTK_POPOVER (impl->browse_files_popover), &rect);
gtk_popover_popup (GTK_POPOVER (impl->browse_files_popover));
@@ -2034,36 +1882,16 @@ list_popup_menu_cb (GtkWidget *widget,
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data);
graphene_rect_t bounds;
- if (gtk_widget_compute_bounds (impl->browse_files_tree_view,
- impl->browse_files_tree_view,
- &bounds))
- {
- file_list_show_popover (impl, 0.5 * bounds.size.width, 0.5 * bounds.size.height);
- return TRUE;
- }
+ if (!gtk_widget_compute_bounds (impl->browse_files_column_view,
+ GTK_WIDGET (impl),
+ &bounds))
+ return FALSE;
- return FALSE;
-}
+ file_list_show_popover (impl,
+ bounds.origin.x + bounds.size.width / 2,
+ bounds.origin.y + bounds.size.height / 2);
-static void
-files_list_clicked (GtkGesture *gesture,
- int n_press,
- double x,
- double y,
- GtkFileChooserWidget *impl)
-{
- list_popup_menu_cb (NULL, NULL, impl);
-}
-
-static void
-files_list_restrict_clicking (GtkGestureClick *gesture,
- int n_press,
- double x,
- double y,
- GtkFileChooserWidget *impl)
-{
- if (impl->browse_files_interaction_frozen)
- gtk_event_controller_reset (GTK_EVENT_CONTROLLER (gesture));
+ return TRUE;
}
static gboolean
@@ -2082,137 +1910,192 @@ files_list_restrict_key_presses (GtkEventControllerKey *controller,
return GDK_EVENT_PROPAGATE;
}
-/* Callback used when a button is pressed on the file list. We trap button 3 to
- * bring up a popup menu.
- */
-
-typedef struct {
- GtkFileChooserWidget *impl;
- double x;
- double y;
-} PopoverData;
-
-static gboolean
-file_list_show_popover_in_idle (gpointer data)
+static char *
+column_view_get_file_date (GtkListItem *item,
+ GFileInfo *info)
{
- PopoverData *pd = data;
+ GtkFileChooserWidget *impl;
+ glong time;
- file_list_show_popover (pd->impl, pd->x, pd->y);
- g_free (data);
+ if (!info)
+ return NULL;
- return G_SOURCE_REMOVE;
-}
+ impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (item),
+ GTK_TYPE_FILE_CHOOSER_WIDGET));
+ if (!impl)
+ return NULL;
-static void
-click_cb (GtkGesture *gesture,
- int n_press,
- double x,
- double y,
- GtkFileChooserWidget *impl)
-{
- PopoverData *pd;
+ if (impl->operation_mode == OPERATION_MODE_RECENT)
+ time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
+ else
+ time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
- pd = g_new (PopoverData, 1);
- pd->impl = impl;
- gtk_widget_translate_coordinates (impl->browse_files_tree_view,
- GTK_WIDGET (impl),
- x, y, &x, &y);
- pd->x = x;
- pd->y = y;
+ if (time == 0)
+ return g_strdup (_("Unknown"));
- g_idle_add (file_list_show_popover_in_idle, pd);
+ return my_g_format_date_for_display (impl, time);
}
-static void
-long_press_cb (GtkGesture *gesture,
- double x,
- double y,
- GtkFileChooserWidget *impl)
+static char *
+column_view_get_file_display_name (GtkListItem *item,
+ GFileInfo *info)
{
- file_list_show_popover (impl, x, y);
+ if (info)
+ return g_strdup (g_file_info_get_display_name (info));
+ else
+ return NULL;
}
-typedef struct {
- OperationMode operation_mode;
- int general_column;
- int model_column;
-} ColumnMap;
+static char *
+column_view_get_file_time (GtkListItem *item,
+ GFileInfo *info)
+{
+ GtkFileChooserWidget *impl;
+ glong time;
-/* Sets the sort column IDs for the file list; needs to be done whenever we
- * change the model on the treeview.
- */
-static void
-file_list_set_sort_column_ids (GtkFileChooserWidget *impl)
+ if (!info)
+ return NULL;
+
+ impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (item),
+ GTK_TYPE_FILE_CHOOSER_WIDGET));
+ if (!impl)
+ return NULL;
+
+ if (impl->operation_mode == OPERATION_MODE_RECENT)
+ time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
+ else
+ time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ if (time == 0)
+ return g_strdup (_("Unknown"));
+
+ return my_g_format_time_for_display (impl, time);
+}
+
+static char *
+column_view_get_file_type (GtkListItem *item,
+ GFileInfo *info)
{
+ GtkFileChooserWidget *impl;
+
+ if (!info || _gtk_file_info_consider_as_directory (info))
+ return NULL;
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view), -1);
+ impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (item),
+ GTK_TYPE_FILE_CHOOSER_WIDGET));
+ if (!impl)
+ return NULL;
- gtk_tree_view_column_set_sort_column_id (impl->list_name_column, MODEL_COL_NAME);
- gtk_tree_view_column_set_sort_column_id (impl->list_time_column, MODEL_COL_TIME);
- gtk_tree_view_column_set_sort_column_id (impl->list_size_column, MODEL_COL_SIZE);
- gtk_tree_view_column_set_sort_column_id (impl->list_type_column, MODEL_COL_TYPE);
- gtk_tree_view_column_set_sort_column_id (impl->list_location_column, MODEL_COL_LOCATION_TEXT);
+ return get_type_information (impl, info);
}
-static gboolean
-file_list_query_tooltip_cb (GtkWidget *widget,
- int x,
- int y,
- gboolean keyboard_tip,
- GtkTooltip *tooltip,
- gpointer user_data)
+static char *
+file_chooser_get_location (GtkFileChooserWidget *impl,
+ GFileInfo *info)
{
- GtkFileChooserWidget *impl = user_data;
- GtkTreeModel *model;
- GtkTreePath *path;
- GtkTreeIter iter;
+ GFile *home_location;
+ GFile *dir_location;
GFile *file;
- char *filename;
+ char *location;
- if (impl->operation_mode == OPERATION_MODE_BROWSE)
- return FALSE;
+ if (!info)
+ return NULL;
+ file = _gtk_file_info_get_file (info);
- if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view),
- x, y,
- keyboard_tip,
- &model, &path, &iter))
- return FALSE;
+ home_location = g_file_new_for_path (g_get_home_dir ());
+ if (file)
+ dir_location = g_file_get_parent (file);
+ else
+ dir_location = NULL;
- gtk_tree_model_get (model, &iter,
- MODEL_COL_FILE, &file,
- -1);
+ if (dir_location && file_is_recent_uri (dir_location))
+ {
+ const char *target_uri;
+ GFile *target;
- if (file == NULL)
+ target_uri = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
+ target = g_file_new_for_uri (target_uri);
+ g_object_unref (dir_location);
+ dir_location = g_file_get_parent (target);
+ g_clear_object (&target);
+ }
+ if (!dir_location)
+ location = g_strdup ("/");
+ else if (impl->current_folder && g_file_equal (impl->current_folder, dir_location))
+ location = g_strdup ("");
+ else if (g_file_equal (home_location, dir_location))
+ location = g_strdup (_("Home"));
+ else if (g_file_has_prefix (dir_location, home_location))
{
- gtk_tree_path_free (path);
- return FALSE;
+ char *relative_path;
+
+ relative_path = g_file_get_relative_path (home_location, dir_location);
+ location = g_filename_display_name (relative_path);
+
+ g_free (relative_path);
}
+ else
+ location = g_file_get_path (dir_location);
- filename = g_file_get_path (file);
- gtk_tooltip_set_text (tooltip, filename);
- gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (impl->browse_files_tree_view),
- tooltip,
- path);
+ g_clear_object (&dir_location);
+ g_clear_object (&home_location);
- g_free (filename);
- g_object_unref (file);
- gtk_tree_path_free (path);
+ return g_steal_pointer (&location);
+}
- return TRUE;
+static char *
+column_view_get_location (GtkListItem *list_item,
+ GFileInfo *info)
+{
+ GtkFileChooserWidget *impl;
+
+ impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (list_item),
+ GTK_TYPE_FILE_CHOOSER_WIDGET));
+ if (!impl)
+ return NULL;
+
+ return file_chooser_get_location (impl, info);
}
-static void
-set_icon_cell_renderer_fixed_size (GtkFileChooserWidget *impl)
+static char *
+column_view_get_size (GtkListItem *item,
+ GFileInfo *info)
{
- int xpad, ypad;
+ if (info && !_gtk_file_info_consider_as_directory (info))
+ return g_format_size (g_file_info_get_size (info));
+ else
+ return NULL;
+}
+
+static char *
+column_view_get_tooltip_text (GtkListItem *list_item,
+ GFileInfo *info)
+{
+ GtkFileChooserWidget *impl;
+ GFile *file;
- gtk_cell_renderer_get_padding (impl->list_pixbuf_renderer, &xpad, &ypad);
- gtk_cell_renderer_set_fixed_size (impl->list_pixbuf_renderer,
- xpad * 2 + ICON_SIZE,
- ypad * 2 + ICON_SIZE);
+ if (!info)
+ return NULL;
+
+ impl = GTK_FILE_CHOOSER_WIDGET (gtk_widget_get_ancestor (gtk_list_item_get_child (list_item),
+ GTK_TYPE_FILE_CHOOSER_WIDGET));
+ g_assert (impl != NULL);
+
+ if (impl->operation_mode == OPERATION_MODE_BROWSE)
+ return NULL;
+
+ file = _gtk_file_info_get_file (info);
+
+ return g_file_get_path (file);
}
+typedef struct {
+ OperationMode operation_mode;
+ int general_column;
+ int model_column;
+} ColumnMap;
+
static GtkWidget *
get_accept_action_widget (GtkDialog *dialog,
gboolean sensitive_only)
@@ -2535,7 +2418,7 @@ location_mode_set (GtkFileChooserWidget *impl,
location_switch_to_path_bar (impl);
if (switch_to_file_list)
- gtk_widget_grab_focus (impl->browse_files_tree_view);
+ gtk_widget_grab_focus (impl->browse_files_column_view);
break;
@@ -2661,20 +2544,32 @@ switch_to_home_dir (GtkFileChooserWidget *impl)
/* Sets the file chooser to multiple selection mode */
static void
set_select_multiple (GtkFileChooserWidget *impl,
- gboolean select_multiple)
+ gboolean select_multiple)
{
- GtkTreeSelection *selection;
- GtkSelectionMode mode;
+ GListModel *model;
if (select_multiple == impl->select_multiple)
return;
- mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
+ gtk_column_view_set_enable_rubberband (GTK_COLUMN_VIEW (impl->browse_files_column_view),
+ select_multiple);
+
+ model = g_object_ref (G_LIST_MODEL (impl->sort_model));
+
+ g_clear_object (&impl->selection_model);
+
+ if (select_multiple)
+ impl->selection_model = GTK_SELECTION_MODEL (gtk_multi_selection_new (model));
+ else
+ impl->selection_model = GTK_SELECTION_MODEL (gtk_single_selection_new (model));
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_set_mode (selection, mode);
+ g_signal_connect (impl->selection_model, "selection-changed",
+ G_CALLBACK (list_selection_changed), impl);
+ g_signal_connect (impl->selection_model, "items-changed",
+ G_CALLBACK (list_items_changed), impl);
- gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (impl->browse_files_tree_view), select_multiple);
+ gtk_column_view_set_model (GTK_COLUMN_VIEW (impl->browse_files_column_view),
+ GTK_SELECTION_MODEL (impl->selection_model));
impl->select_multiple = select_multiple;
g_object_notify (G_OBJECT (impl), "select-multiple");
@@ -2682,15 +2577,19 @@ set_select_multiple (GtkFileChooserWidget *impl,
/* Takes the folder stored in a row in the recent_model, and puts it in the pathbar */
static void
-put_recent_folder_in_pathbar (GtkFileChooserWidget *impl, GtkTreeIter *iter)
+put_recent_folder_in_pathbar (GtkFileChooserWidget *impl,
+ guint position)
{
- GFile *file;
+ GFileInfo *info;
- gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), iter,
- MODEL_COL_FILE, &file,
- -1);
- _gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), file, FALSE);
- g_object_unref (file);
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), position);
+ g_assert (info != NULL);
+
+ _gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar),
+ _gtk_file_info_get_file (info),
+ FALSE);
+
+ g_clear_object (&info);
}
/* Sets the location bar in the appropriate mode according to the
@@ -2719,17 +2618,15 @@ location_bar_update (GtkFileChooserWidget *impl)
case OPERATION_MODE_RECENT:
if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
{
- GtkTreeSelection *selection;
- gboolean have_selected;
- GtkTreeIter iter;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ guint position;
/* Save mode means single-selection mode, so the following is valid */
- have_selected = gtk_tree_selection_get_selected (selection, NULL, &iter);
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
- if (have_selected)
- put_recent_folder_in_pathbar (impl, &iter);
+ if (gtk_bitset_iter_init_first (&iter, bitset, &position))
+ put_recent_folder_in_pathbar (impl, position);
}
visible = FALSE;
break;
@@ -2940,11 +2837,6 @@ update_appearance (GtkFileChooserWidget *impl)
_gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action);
location_bar_update (impl);
-
- /* This *is* needed; we need to redraw the file list because the "sensitivity"
- * of files may change depending whether we are in a file or folder-only mode.
- */
- gtk_widget_queue_draw (impl->browse_files_tree_view);
}
static char *
@@ -3001,6 +2893,9 @@ set_show_hidden (GtkFileChooserWidget *impl,
if (impl->browse_files_model)
_gtk_file_system_model_set_show_hidden (impl->browse_files_model, show_hidden);
+
+ gtk_filter_changed (gtk_filter_list_model_get_filter (impl->filter_model),
+ GTK_FILTER_CHANGE_DIFFERENT);
}
}
@@ -3054,7 +2949,6 @@ gtk_file_chooser_widget_set_property (GObject *object,
set_select_multiple (impl, FALSE);
}
impl->action = action;
- update_cell_renderer_attributes (impl);
update_appearance (impl);
settings_load (impl);
}
@@ -3114,6 +3008,10 @@ gtk_file_chooser_widget_get_property (GObject *object,
g_value_take_string (value, gtk_file_chooser_widget_get_subtitle (impl));
break;
+ case PROP_SHOW_TIME:
+ g_value_set_boolean (value, impl->show_time);
+ break;
+
case GTK_FILE_CHOOSER_PROP_ACTION:
g_value_set_enum (value, impl->action);
break;
@@ -3166,28 +3064,21 @@ cancel_all_operations (GtkFileChooserWidget *impl)
search_stop_searching (impl, TRUE);
}
-/* Removes the settings signal handler. It's safe to call multiple times */
-static void
-remove_settings_signal (GtkFileChooserWidget *impl)
-{
- if (impl->settings_signal_id)
- {
- GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (impl));
- GtkSettings *settings = gtk_settings_get_for_display (display);
-
- g_signal_handler_disconnect (settings,
- impl->settings_signal_id);
- impl->settings_signal_id = 0;
- }
-}
+static void sorter_changed (GtkSorter *main_sorter,
+ GParamSpec *pspec,
+ GtkFileChooserWidget *impl);
static void
gtk_file_chooser_widget_dispose (GObject *object)
{
GtkFileChooserWidget *impl = (GtkFileChooserWidget *) object;
+ GtkSorter *sorter;
cancel_all_operations (impl);
+ sorter = gtk_column_view_get_sorter (GTK_COLUMN_VIEW (impl->browse_files_column_view));
+ g_signal_handlers_disconnect_by_func (sorter, G_CALLBACK (sorter_changed), impl);
+
/* browse_files_popover is not a template child */
g_clear_pointer (&impl->browse_files_popover, gtk_widget_unparent);
g_clear_pointer (&impl->bookmarks_manager, _gtk_bookmarks_manager_free);
@@ -3200,8 +3091,6 @@ gtk_file_chooser_widget_dispose (GObject *object)
impl->external_entry = NULL;
}
- remove_settings_signal (impl);
-
gtk_widget_dispose_template (GTK_WIDGET (impl), GTK_TYPE_FILE_CHOOSER_WIDGET);
G_OBJECT_CLASS (gtk_file_chooser_widget_parent_class)->dispose (object);
@@ -3257,78 +3146,6 @@ gtk_file_chooser_widget_unroot (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_file_chooser_widget_parent_class)->unroot (widget);
}
-/* Changes the icons wherever it is needed */
-static void
-change_icon_theme (GtkFileChooserWidget *impl)
-{
- /* the first cell in the first column is the icon column, and we have a fixed size there */
- set_icon_cell_renderer_fixed_size (impl);
-
- clear_model_cache (impl, MODEL_COL_ICON);
- gtk_widget_queue_resize (impl->browse_files_tree_view);
-}
-
-/* Callback used when a GtkSettings value changes */
-static void
-settings_notify_cb (GObject *object,
- GParamSpec *pspec,
- GtkFileChooserWidget *impl)
-{
- const char *name;
-
- name = g_param_spec_get_name (pspec);
-
- if (strcmp (name, "gtk-icon-theme-name") == 0)
- change_icon_theme (impl);
-}
-
-/* Installs a signal handler for GtkSettings so that we can monitor changes in
- * the icon theme.
- */
-static void
-check_icon_theme (GtkFileChooserWidget *impl)
-{
- GtkSettings *settings;
-
- if (impl->settings_signal_id)
- return;
-
- settings = gtk_widget_get_settings (GTK_WIDGET (impl));
- impl->settings_signal_id = g_signal_connect (settings, "notify",
- G_CALLBACK (settings_notify_cb), impl);
-
- change_icon_theme (impl);
-}
-
-static void
-gtk_file_chooser_widget_css_changed (GtkWidget *widget,
- GtkCssStyleChange *change)
-{
- GtkFileChooserWidget *impl;
-
- impl = GTK_FILE_CHOOSER_WIDGET (widget);
-
- GTK_WIDGET_CLASS (gtk_file_chooser_widget_parent_class)->css_changed (widget, change);
-
- change_icon_theme (impl);
-}
-
-static void
-set_sort_column (GtkFileChooserWidget *impl)
-{
- GtkTreeSortable *sortable;
-
- sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
-
- /* can happen when we're still populating the model */
- if (sortable == NULL)
- return;
-
- gtk_tree_sortable_set_sort_column_id (sortable,
- impl->sort_column,
- impl->sort_order);
-}
-
static void
settings_load (GtkFileChooserWidget *impl)
{
@@ -3360,9 +3177,9 @@ settings_load (GtkFileChooserWidget *impl)
set_show_hidden (impl, show_hidden);
impl->show_size_column = show_size_column;
- gtk_tree_view_column_set_visible (impl->list_size_column, show_size_column);
+ gtk_column_view_column_set_visible (impl->column_view_size_column, show_size_column);
impl->show_type_column = show_type_column;
- gtk_tree_view_column_set_visible (impl->list_type_column, show_type_column);
+ gtk_column_view_column_set_visible (impl->column_view_type_column, show_type_column);
impl->sort_column = sort_column;
impl->sort_order = sort_order;
@@ -3377,7 +3194,6 @@ settings_load (GtkFileChooserWidget *impl)
* call set_sort_column() themselves.
*/
- update_time_renderer_visible (impl);
if (sidebar_width < 0)
{
GtkWidget *sidebar = gtk_paned_get_start_child (GTK_PANED (impl->browse_widgets_hpaned));
@@ -3588,239 +3404,6 @@ gtk_file_chooser_widget_unmap (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_file_chooser_widget_parent_class)->unmap (widget);
}
-static int
-compare_directory (GtkFileSystemModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- GtkFileChooserWidget *impl)
-{
- gboolean dir_a, dir_b;
-
- dir_a = g_value_get_boolean (_gtk_file_system_model_get_value (model, a, MODEL_COL_IS_FOLDER));
- dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (model, b, MODEL_COL_IS_FOLDER));
-
- if (impl->sort_directories_first && dir_a != dir_b)
- return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1);
-
- return 0;
-}
-
-static int
-compare_name (GtkFileSystemModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- GtkFileChooserWidget *impl)
-{
- const char *key_a, *key_b;
- int result;
-
- key_a = g_value_get_string (_gtk_file_system_model_get_value (model, a, MODEL_COL_NAME_COLLATED));
- key_b = g_value_get_string (_gtk_file_system_model_get_value (model, b, MODEL_COL_NAME_COLLATED));
-
- if (key_a && key_b)
- result = strcmp (key_a, key_b);
- else if (key_a)
- result = 1;
- else if (key_b)
- result = -1;
- else
- result = 0;
-
- return result;
-}
-
-static int
-compare_size (GtkFileSystemModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- GtkFileChooserWidget *impl)
-{
- gint64 size_a, size_b;
-
- size_a = g_value_get_int64 (_gtk_file_system_model_get_value (model, a, MODEL_COL_SIZE));
- size_b = g_value_get_int64 (_gtk_file_system_model_get_value (model, b, MODEL_COL_SIZE));
-
- return size_a < size_b ? -1 : (size_a == size_b ? 0 : 1);
-}
-
-static int
-compare_type (GtkFileSystemModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- GtkFileChooserWidget *impl)
-{
- const char *key_a, *key_b;
-
- key_a = g_value_get_string (_gtk_file_system_model_get_value (model, a, MODEL_COL_TYPE));
- key_b = g_value_get_string (_gtk_file_system_model_get_value (model, b, MODEL_COL_TYPE));
-
- return g_strcmp0 (key_a, key_b);
-}
-
-static int
-compare_time (GtkFileSystemModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- GtkFileChooserWidget *impl)
-{
- glong ta, tb;
-
- ta = g_value_get_long (_gtk_file_system_model_get_value (model, a, MODEL_COL_TIME));
- tb = g_value_get_long (_gtk_file_system_model_get_value (model, b, MODEL_COL_TIME));
-
- return ta < tb ? -1 : (ta == tb ? 0 : 1);
-}
-
-static int
-compare_location (GtkFileSystemModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- GtkFileChooserWidget *impl)
-{
- const char *key_a, *key_b;
-
- key_a = g_value_get_string (_gtk_file_system_model_get_value (model, a, MODEL_COL_LOCATION_TEXT));
- key_b = g_value_get_string (_gtk_file_system_model_get_value (model, b, MODEL_COL_LOCATION_TEXT));
-
- return g_strcmp0 (key_a, key_b);
-}
-
-/* Sort callback for the filename column */
-static int
-name_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
- GtkFileChooserWidget *impl = user_data;
- int result;
-
- result = compare_directory (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_name (fs_model, a, b, impl);
-
- return result;
-}
-
-/* Sort callback for the size column */
-static int
-size_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
- GtkFileChooserWidget *impl = user_data;
- int result;
-
- result = compare_directory (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_size (fs_model, a, b, impl);
-
- return result;
-}
-
-/* Sort callback for the type column */
-static int
-type_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
- GtkFileChooserWidget *impl = user_data;
- int result;
-
- result = compare_directory (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_type (fs_model, a, b, impl);
-
- return result;
-}
-
-/* Sort callback for the time column */
-static int
-time_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
- GtkFileChooserWidget *impl = user_data;
- int result;
-
- result = compare_directory (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_time (fs_model, a, b, impl);
-
- return result;
-}
-
-static int
-recent_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
- GtkFileChooserWidget *impl = user_data;
- int result;
-
- result = compare_time (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_name (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_location (fs_model, a, b, impl);
-
- return result;
-}
-
-static int
-search_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
- GtkFileChooserWidget *impl = user_data;
- int result;
-
- result = compare_location (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_name (fs_model, a, b, impl);
-
- if (result == 0)
- result = compare_time (fs_model, a, b, impl);
-
- return result;
-}
-
-/* Callback used when the sort column changes. We cache the sort order for use
- * in name_sort_func().
- */
-static void
-list_sort_column_changed_cb (GtkTreeSortable *sortable,
- GtkFileChooserWidget *impl)
-{
- int sort_column_id;
- GtkSortType sort_type;
-
- if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type))
- {
- impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
- impl->sort_column = sort_column_id;
- impl->sort_order = sort_type;
- }
-}
-
static void
set_busy_cursor (GtkFileChooserWidget *impl,
gboolean busy)
@@ -3844,27 +3427,8 @@ update_columns (GtkFileChooserWidget *impl,
gboolean location_visible,
const char *time_title)
{
- gboolean need_resize = FALSE;
-
- if (gtk_tree_view_column_get_visible (impl->list_location_column) != location_visible)
- {
- gtk_tree_view_column_set_visible (impl->list_location_column, location_visible);
- need_resize = TRUE;
- }
-
- if (g_strcmp0 (gtk_tree_view_column_get_title (impl->list_time_column), time_title) != 0)
- {
- gtk_tree_view_column_set_title (impl->list_time_column, time_title);
- need_resize = TRUE;
- }
-
- if (need_resize)
- {
- /* This undoes user resizing of columns when the columns change. */
- gtk_tree_view_column_set_expand (impl->list_name_column, TRUE);
- gtk_tree_view_column_set_expand (impl->list_location_column, TRUE);
- gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view));
- }
+ gtk_column_view_column_set_visible (impl->column_view_location_column, location_visible);
+ gtk_column_view_column_set_title (impl->column_view_time_column, time_title);
}
/* Creates a sort model to wrap the file system model and sets it on the tree view */
@@ -3873,12 +3437,8 @@ load_set_model (GtkFileChooserWidget *impl)
{
g_assert (impl->browse_files_model != NULL);
- gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
- GTK_TREE_MODEL (impl->browse_files_model));
+ set_current_model (impl, G_LIST_MODEL (impl->browse_files_model));
update_columns (impl, FALSE, _("Modified"));
- file_list_set_sort_column_ids (impl);
- set_sort_column (impl);
- impl->list_sort_ascending = TRUE;
g_set_object (&impl->model_for_search, impl->browse_files_model);
}
@@ -3936,113 +3496,43 @@ load_remove_timer (GtkFileChooserWidget *impl, LoadState new_load_state)
}
/* Selects the first row in the file list */
-static void
-browse_files_select_first_row (GtkFileChooserWidget *impl)
-{
- GtkTreePath *path;
- GtkTreeIter dummy_iter;
- GtkTreeModel *tree_model;
-
- tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
-
- if (!tree_model)
- return;
-
- path = gtk_tree_path_new_from_indices (0, -1);
-
- /* If the list is empty, do nothing. */
- if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
- {
- /* Although the following call to gtk_tree_view_set_cursor() is intended to
- * only change the focus to the first row (not select it), GtkTreeView *will*
- * select the row anyway due to bug #492206. So, we'll use a flag to
- * keep our own callbacks from changing the location_entry when the selection
- * is changed. This entire function, browse_files_select_first_row(), may
- * go away when that bug is fixed in GtkTreeView.
- */
- impl->auto_selecting_first_row = TRUE;
-
- gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), path, NULL, FALSE);
-
- impl->auto_selecting_first_row = FALSE;
- }
- gtk_tree_path_free (path);
-}
-
-struct center_selected_row_closure {
- GtkFileChooserWidget *impl;
- gboolean already_centered;
-};
-
-/* Callback used from gtk_tree_selection_selected_foreach(); centers the
- * selected row in the tree view.
- */
-static void
-center_selected_row_foreach_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- struct center_selected_row_closure *closure = data;
-
- if (closure->already_centered)
- return;
-
- gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0);
- closure->already_centered = TRUE;
-}
-
-/* Centers the selected row in the tree view */
-static void
-browse_files_center_selected_row (GtkFileChooserWidget *impl)
-{
- struct center_selected_row_closure closure;
- GtkTreeSelection *selection;
-
- closure.impl = impl;
- closure.already_centered = FALSE;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
-}
-
static gboolean
show_and_select_files (GtkFileChooserWidget *impl,
GSList *files)
{
- GtkTreeSelection *selection;
GtkFileSystemModel *fsmodel;
- gboolean enabled_hidden, removed_filters;
gboolean selected_a_file;
GSList *walk;
g_assert (impl->load_state == LOAD_FINISHED);
g_assert (impl->browse_files_model != NULL);
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
+ fsmodel = GTK_FILE_SYSTEM_MODEL (get_current_model (impl));
g_assert (fsmodel == impl->browse_files_model);
- enabled_hidden = impl->show_hidden;
- removed_filters = (impl->current_filter == NULL);
-
selected_a_file = FALSE;
for (walk = files; walk; walk = walk->next)
{
- GFile *file = walk->data;
- GtkTreeIter iter;
+ GFileInfo *info;
+ GFile *file;
+ guint i;
+ gboolean enabled_hidden, removed_filters;
+
+ file = walk->data;
/* Is it a hidden file? */
- if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
+ info = _gtk_file_system_model_get_info_for_file (fsmodel, file);
+ if (!info)
continue;
- if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
- {
- GFileInfo *info = _gtk_file_system_model_get_info (fsmodel, &iter);
+ enabled_hidden = impl->show_hidden;
+ removed_filters = (impl->current_filter == NULL);
+ if (!g_file_info_get_attribute_boolean (info, "filechooser::visible"))
+ {
if (!enabled_hidden &&
(g_file_info_get_is_hidden (info) ||
g_file_info_get_is_backup (info)))
@@ -4054,10 +3544,7 @@ show_and_select_files (GtkFileChooserWidget *impl,
/* Is it a filtered file? */
- if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
- continue; /* re-get the iter as it may change when the model refilters */
-
- if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
+ if (g_file_info_get_attribute_boolean (info, "filechooser::filtered-out"))
{
/* Maybe we should have a way to ask the fsmodel if it had filtered a file */
if (!removed_filters)
@@ -4067,27 +3554,26 @@ show_and_select_files (GtkFileChooserWidget *impl,
}
}
- /* Okay, can we select the file now? */
- if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
- continue;
+ /* TODO: "accidentally" quadratic! */
- if (_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
+ for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (impl->selection_model)); i++)
{
- GtkTreePath *path;
+ GFileInfo *info2;
- gtk_tree_selection_select_iter (selection, &iter);
+ info2 = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
- path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsmodel), &iter);
- gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view),
- path, NULL, FALSE);
- gtk_tree_path_free (path);
+ if (info2 == info)
+ {
+ gtk_selection_model_select_item (impl->selection_model, i, FALSE);
+ g_clear_object (&info2);
+ selected_a_file = TRUE;
+ break;
+ }
- selected_a_file = TRUE;
+ g_clear_object (&info2);
}
}
- browse_files_center_selected_row (impl);
-
return selected_a_file;
}
@@ -4102,7 +3588,6 @@ pending_select_files_process (GtkFileChooserWidget *impl)
{
show_and_select_files (impl, impl->pending_select_files);
pending_select_files_free (impl);
- browse_files_center_selected_row (impl);
}
else
{
@@ -4115,7 +3600,7 @@ pending_select_files_process (GtkFileChooserWidget *impl)
*/
if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN &&
gtk_widget_get_mapped (GTK_WIDGET (impl)))
- browse_files_select_first_row (impl);
+ gtk_selection_model_select_item (impl->selection_model, 0, TRUE);
}
g_assert (impl->pending_select_files == NULL);
@@ -4184,19 +3669,28 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
/* Callback used when file system model adds or updates a file.
* We detect here when a new renamed file appears and reveal it */
static void
-browse_files_model_row_changed_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- GtkFileChooserWidget *impl)
+browse_files_model_items_changed_cb (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkFileChooserWidget *impl)
{
- GFile *file;
- GSList files;
+ if (!impl->renamed_file)
+ return;
- if (impl->renamed_file)
+ for (guint i = 0; i < added; i++)
{
- gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
+ GFileInfo *info;
+ GFile *file;
+
+ info = g_list_model_get_item (model, position + i);
+ file = _gtk_file_info_get_file (info);
+ g_clear_object (&info);
+
if (g_file_equal (impl->renamed_file, file))
{
+ GSList files;
+
g_clear_object (&impl->renamed_file);
files.data = (gpointer) file;
@@ -4204,8 +3698,6 @@ browse_files_model_row_changed_cb (GtkTreeModel *model,
show_and_select_files (impl, &files);
}
-
- g_object_unref (file);
}
}
@@ -4218,7 +3710,7 @@ stop_loading_and_clear_list_model (GtkFileChooserWidget *impl,
g_set_object (&impl->browse_files_model, NULL);
if (remove)
- gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
+ set_current_model (impl, NULL);
}
/* Replace 'target' with 'replacement' in the input string. */
@@ -4333,53 +3825,6 @@ my_g_format_time_for_display (GtkFileChooserWidget *impl,
return date_str;
}
-static void
-copy_attribute (GFileInfo *to,
- GFileInfo *from,
- const char *attribute)
-{
- GFileAttributeType type;
- gpointer value;
-
- if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
- g_file_info_set_attribute (to, attribute, type, value);
-}
-
-static void
-file_system_model_got_thumbnail (GObject *object,
- GAsyncResult *res,
- gpointer data)
-{
- GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */
- GFile *file = G_FILE (object);
- GFileInfo *queried, *info;
- GtkTreeIter iter;
-
- queried = g_file_query_info_finish (file, res, NULL);
- if (queried == NULL)
- return;
-
- /* now we know model is valid */
-
- /* file was deleted */
- if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
- {
- g_object_unref (queried);
- return;
- }
-
- info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter));
-
- copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
- copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
- copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
-
- _gtk_file_system_model_update_file (model, file, info);
-
- g_object_unref (info);
- g_object_unref (queried);
-}
-
/* Copied from src/nautilus_file.c:get_description() */
struct {
const char *icon_name;
@@ -4472,224 +3917,6 @@ end:
return g_strdup ("");
}
-static gboolean
-file_system_model_set (GtkFileSystemModel *model,
- GFile *file,
- GFileInfo *info,
- int column,
- GValue *value,
- gpointer data)
-{
- GtkFileChooserWidget *impl = data;
-
- switch (column)
- {
- case MODEL_COL_FILE:
- g_value_set_object (value, file);
- break;
- case MODEL_COL_NAME:
- if (info == NULL)
- g_value_set_string (value, DEFAULT_NEW_FOLDER_NAME);
- else
- g_value_set_string (value, g_file_info_get_display_name (info));
- break;
- case MODEL_COL_NAME_COLLATED:
- if (info == NULL)
- g_value_take_string (value, g_utf8_collate_key_for_filename (DEFAULT_NEW_FOLDER_NAME, -1));
- else
- g_value_take_string (value, g_utf8_collate_key_for_filename (g_file_info_get_display_name (info), -1));
- break;
- case MODEL_COL_IS_FOLDER:
- g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info));
- break;
- case MODEL_COL_IS_SENSITIVE:
- if (info)
- {
- gboolean sensitive = TRUE;
-
- if (impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- {
- sensitive = TRUE; /* for file modes... */
- }
- else if (!_gtk_file_info_consider_as_directory (info))
- {
- sensitive = FALSE; /* for folder modes, files are not sensitive... */
- }
- else
- {
- /* ... and for folder modes, folders are sensitive only if the filter says so */
- GtkTreeIter iter;
- if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
- g_assert_not_reached ();
- sensitive = !_gtk_file_system_model_iter_is_filtered_out (model, &iter);
- }
-
- g_value_set_boolean (value, sensitive);
- }
- else
- g_value_set_boolean (value, TRUE);
- break;
- case MODEL_COL_ICON:
- if (info)
- {
- if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON))
- {
- int scale;
- GtkIconTheme *icon_theme;
-
- scale = gtk_widget_get_scale_factor (GTK_WIDGET (impl));
- icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (impl)));
-
- g_value_take_object (value, _gtk_file_info_get_icon (info, ICON_SIZE, scale, icon_theme));
- }
- else
- {
- GtkTreeModel *tree_model;
- GtkTreePath *start, *end;
- GtkTreeIter iter;
- gboolean visible;
-
- if (impl->browse_files_tree_view == NULL ||
- g_file_info_has_attribute (info, "filechooser::queried"))
- return FALSE;
-
- tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view));
- if (tree_model != GTK_TREE_MODEL (model))
- return FALSE;
-
- if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
- g_assert_not_reached ();
-
- if (gtk_tree_view_get_visible_range (GTK_TREE_VIEW (impl->browse_files_tree_view), &start, &end))
- {
- GtkTreePath *path;
-
- gtk_tree_path_prev (start);
- gtk_tree_path_next (end);
- path = gtk_tree_model_get_path (tree_model, &iter);
- visible = gtk_tree_path_compare (start, path) != 1 &&
- gtk_tree_path_compare (path, end) != 1;
- gtk_tree_path_free (path);
- gtk_tree_path_free (start);
- gtk_tree_path_free (end);
- }
- else
- visible = TRUE;
- if (visible)
- {
- g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
- g_file_query_info_async (file,
- G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
- G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
- G_FILE_ATTRIBUTE_STANDARD_ICON,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_DEFAULT,
- _gtk_file_system_model_get_cancellable (model),
- file_system_model_got_thumbnail,
- model);
- }
- return FALSE;
- }
- }
- else
- g_value_set_boxed (value, NULL);
- break;
- case MODEL_COL_SIZE:
- g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0);
- break;
- case MODEL_COL_SIZE_TEXT:
- if (info == NULL || _gtk_file_info_consider_as_directory (info))
- g_value_set_string (value, NULL);
- else
- g_value_take_string (value, g_format_size (g_file_info_get_size (info)));
- break;
- case MODEL_COL_TYPE:
- if (info == NULL || _gtk_file_info_consider_as_directory (info))
- g_value_set_string (value, NULL);
- else
- g_value_take_string (value, get_type_information (impl, info));
- break;
- case MODEL_COL_TIME:
- case MODEL_COL_DATE_TEXT:
- case MODEL_COL_TIME_TEXT:
- {
- glong time;
- if (info == NULL)
- break;
- if (impl->operation_mode == OPERATION_MODE_RECENT)
- time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
- else
- time = (glong) g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
- if (column == MODEL_COL_TIME)
- g_value_set_long (value, time);
- else if (time == 0)
- g_value_set_static_string (value, _("Unknown"));
- else if (column == MODEL_COL_DATE_TEXT)
- g_value_take_string (value, my_g_format_date_for_display (impl, time));
- else
- g_value_take_string (value, my_g_format_time_for_display (impl, time));
- break;
- }
- case MODEL_COL_ELLIPSIZE:
- g_value_set_enum (value, info ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE);
- break;
- case MODEL_COL_LOCATION_TEXT:
- {
- GFile *home_location;
- GFile *dir_location;
- char *location;
-
- home_location = g_file_new_for_path (g_get_home_dir ());
- if (file)
- dir_location = g_file_get_parent (file);
- else
- dir_location = NULL;
-
- if (dir_location && file_is_recent_uri (dir_location))
- {
- const char *target_uri;
- GFile *target;
-
- target_uri = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
- target = g_file_new_for_uri (target_uri);
- g_object_unref (dir_location);
- dir_location = g_file_get_parent (target);
- g_object_unref (target);
- }
-
- if (!dir_location)
- location = g_strdup ("/");
- else if (impl->current_folder && g_file_equal (impl->current_folder, dir_location))
- location = g_strdup ("");
- else if (g_file_equal (home_location, dir_location))
- location = g_strdup (_("Home"));
- else if (g_file_has_prefix (dir_location, home_location))
- {
- char *relative_path;
-
- relative_path = g_file_get_relative_path (home_location, dir_location);
- location = g_filename_display_name (relative_path);
-
- g_free (relative_path);
- }
- else
- location = g_file_get_path (dir_location);
-
- g_value_take_string (value, location);
-
- if (dir_location)
- g_object_unref (dir_location);
- g_object_unref (home_location);
- }
- break;
- default:
- g_assert_not_reached ();
- break;
- }
-
- return TRUE;
-}
-
/* Gets rid of the old list model and creates a new one for the current folder */
static gboolean
set_list_model (GtkFileChooserWidget *impl,
@@ -4707,41 +3934,25 @@ set_list_model (GtkFileChooserWidget *impl,
impl->browse_files_model =
_gtk_file_system_model_new_for_directory (impl->current_folder,
- MODEL_ATTRIBUTES,
- file_system_model_set,
- impl,
- MODEL_COLUMN_TYPES);
+ MODEL_ATTRIBUTES);
_gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_NAME, name_sort_func, impl, NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_TYPE, type_sort_func, impl, NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_TIME, time_sort_func, impl, NULL);
- gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), NULL, NULL, NULL);
- set_sort_column (impl);
- impl->list_sort_ascending = TRUE;
- g_signal_connect (impl->browse_files_model, "sort-column-changed",
- G_CALLBACK (list_sort_column_changed_cb), impl);
-
load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
g_signal_connect (impl->browse_files_model, "finished-loading",
G_CALLBACK (browse_files_model_finished_loading_cb), impl);
- g_signal_connect (impl->browse_files_model, "row-changed",
- G_CALLBACK (browse_files_model_row_changed_cb), impl);
+ g_signal_connect (impl->browse_files_model, "items-changed",
+ G_CALLBACK (browse_files_model_items_changed_cb), impl);
_gtk_file_system_model_set_filter (impl->browse_files_model, impl->current_filter);
+ set_current_model (impl, G_LIST_MODEL (impl->browse_files_model));
+
return TRUE;
}
-struct update_chooser_entry_selected_foreach_closure {
- int num_selected;
- GtkTreeIter first_selected_iter;
-};
-
static int
compare_utf8_filenames (const char *a,
const char *b)
@@ -4761,25 +3972,12 @@ compare_utf8_filenames (const char *a,
}
static void
-update_chooser_entry_selected_foreach (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- struct update_chooser_entry_selected_foreach_closure *closure;
-
- closure = data;
- closure->num_selected++;
-
- if (closure->num_selected == 1)
- closure->first_selected_iter = *iter;
-}
-
-static void
update_chooser_entry (GtkFileChooserWidget *impl)
{
- GtkTreeSelection *selection;
- struct update_chooser_entry_selected_foreach_closure closure;
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ guint n_selected;
+ guint first;
/* no need to update the file chooser's entry if there's no entry */
if (impl->operation_mode == OPERATION_MODE_SEARCH ||
@@ -4794,31 +3992,31 @@ update_chooser_entry (GtkFileChooserWidget *impl)
g_assert (impl->location_entry != NULL);
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- closure.num_selected = 0;
- gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
- if (closure.num_selected == 0)
+ n_selected = 0;
+ if (gtk_bitset_iter_init_first (&iter, bitset, &first))
+ {
+ n_selected++;
+ while (gtk_bitset_iter_next (&iter, NULL))
+ n_selected++;
+ }
+
+ if (n_selected == 0)
{
if (impl->operation_mode == OPERATION_MODE_RECENT)
_gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), NULL);
else
goto maybe_clear_entry;
}
- else if (closure.num_selected == 1)
+ else if (n_selected == 1)
{
if (impl->operation_mode == OPERATION_MODE_BROWSE)
{
GFileInfo *info;
gboolean change_entry;
- info = _gtk_file_system_model_get_info (impl->browse_files_model, &closure.first_selected_iter);
-
- /* If the cursor moved to the row of the newly created folder,
- * retrieving info will return NULL.
- */
- if (!info)
- return;
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), first);
g_free (impl->browse_files_last_selected_name);
impl->browse_files_last_selected_name =
@@ -4845,20 +4043,23 @@ update_chooser_entry (GtkFileChooserWidget *impl)
_gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->location_entry));
}
+ g_clear_object (&info);
+
return;
}
else if (impl->operation_mode == OPERATION_MODE_RECENT
&& impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
{
+ GFileInfo *info;
GFile *folder;
/* Set the base folder on the name entry, so it will do completion relative to the correct recent-folder */
- gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &closure.first_selected_iter,
- MODEL_COL_FILE, &folder,
- -1);
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), first);
+ folder = _gtk_file_info_get_file (info);
+ g_clear_object (&info);
+
_gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), folder);
- g_object_unref (folder);
return;
}
}
@@ -5213,7 +4414,7 @@ gtk_file_chooser_widget_select_file (GtkFileChooser *chooser,
if (!parent_file)
return gtk_file_chooser_set_current_folder (chooser, file, error);
- fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)));
+ fsmodel = GTK_FILE_SYSTEM_MODEL (get_current_model (impl));
if (impl->operation_mode == OPERATION_MODE_SEARCH ||
impl->operation_mode == OPERATION_MODE_RECENT ||
@@ -5265,47 +4466,28 @@ gtk_file_chooser_widget_unselect_file (GtkFileChooser *chooser,
GFile *file)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser);
- GtkTreeView *tree_view;
- GtkTreeModel *model;
- GtkTreeIter iter;
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ guint i;
- tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view);
- model = gtk_tree_view_get_model (tree_view);
- if (!model)
- return;
-
- if (!_gtk_file_system_model_get_iter_for_file (GTK_FILE_SYSTEM_MODEL (model), &iter, file))
- return;
-
- gtk_tree_selection_unselect_iter (gtk_tree_view_get_selection (tree_view), &iter);
-}
-
-static gboolean
-maybe_select (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (data);
- GtkTreeSelection *selection;
- gboolean is_sensitive;
- gboolean is_folder;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
+ for (gtk_bitset_iter_init_first (&iter, bitset, &i);
+ gtk_bitset_iter_is_valid (&iter);
+ gtk_bitset_iter_next (&iter, &i))
+ {
+ GFileInfo *info;
+ GFile *f;
- gtk_tree_model_get (model, iter,
- MODEL_COL_IS_FOLDER, &is_folder,
- MODEL_COL_IS_SENSITIVE, &is_sensitive,
- -1);
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ f = _gtk_file_info_get_file (info);
+ g_clear_object (&info);
- if (is_sensitive &&
- ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
- (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)))
- gtk_tree_selection_select_iter (selection, iter);
- else
- gtk_tree_selection_unselect_iter (selection, iter);
+ if (g_file_equal (f, file))
+ break;
+ }
- return FALSE;
+ if (gtk_bitset_iter_is_valid (&iter))
+ gtk_selection_model_unselect_item (impl->selection_model, i);
}
static void
@@ -5316,25 +4498,39 @@ gtk_file_chooser_widget_select_all (GtkFileChooser *chooser)
if (impl->operation_mode == OPERATION_MODE_SEARCH ||
impl->operation_mode == OPERATION_MODE_RECENT)
{
- GtkTreeSelection *selection;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_select_all (selection);
+ gtk_selection_model_select_all (impl->selection_model);
return;
}
if (impl->select_multiple)
- gtk_tree_model_foreach (GTK_TREE_MODEL (impl->browse_files_model),
- maybe_select, impl);
+ {
+ guint i;
+
+ for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (impl->selection_model)); i++)
+ {
+ GFileInfo *info;
+ gboolean is_folder;
+
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ is_folder = _gtk_file_info_consider_as_directory (info);
+
+ if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) ||
+ (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN))
+ gtk_selection_model_select_item (impl->selection_model, i, FALSE);
+ else
+ gtk_selection_model_unselect_item (impl->selection_model, i);
+
+ g_clear_object (&info);
+ }
+ }
}
static void
gtk_file_chooser_widget_unselect_all (GtkFileChooser *chooser)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser);
- GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_unselect_all (selection);
+ gtk_selection_model_unselect_all (impl->selection_model);
pending_select_files_free (impl);
}
@@ -5431,28 +4627,6 @@ check_save_entry (GtkFileChooserWidget *impl,
*is_folder = _gtk_file_chooser_entry_get_is_folder (chooser_entry, file);
}
-struct get_files_closure {
- GtkFileChooserWidget *impl;
- GListStore *result;
- GFile *file_from_entry;
-};
-
-static void
-get_files_foreach (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
- struct get_files_closure *info = data;
- GFile *file;
-
- file = _gtk_file_system_model_get_file (fs_model, iter);
-
- if (!info->file_from_entry || !g_file_equal (info->file_from_entry, file))
- g_list_store_append (info->result, file);
-}
-
static GListModel *
get_selected_files_as_model (GtkFileChooserWidget *impl)
{
@@ -5460,7 +4634,7 @@ get_selected_files_as_model (GtkFileChooserWidget *impl)
GSList *files, *l;
store = g_list_store_new (G_TYPE_FILE);
- files = get_selected_files (impl);
+ files = gtk_file_chooser_widget_get_selected_files (impl);
for (l = files; l; l = l->next)
g_list_store_append (store, l->data);
g_slist_free_full (files, g_object_unref);
@@ -5472,17 +4646,17 @@ static GListModel *
gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
{
GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser);
- struct get_files_closure info;
+ GListStore *result;
GtkWindow *toplevel;
GtkWidget *current_focus;
+ GFile *file_from_entry;
gboolean file_list_seen;
if (impl->operation_mode == OPERATION_MODE_SEARCH)
return get_selected_files_as_model (impl);
- info.impl = impl;
- info.result = g_list_store_new (G_TYPE_FILE);
- info.file_from_entry = NULL;
+ result = g_list_store_new (G_TYPE_FILE);
+ file_from_entry = NULL;
if (impl->operation_mode == OPERATION_MODE_RECENT)
{
@@ -5493,7 +4667,7 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
}
else
{
- g_object_unref (info.result);
+ g_object_unref (result);
return get_selected_files_as_model (impl);
}
}
@@ -5505,15 +4679,30 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
current_focus = NULL;
file_list_seen = FALSE;
- if (current_focus == impl->browse_files_tree_view)
+ if (current_focus == impl->browse_files_column_view)
{
- GtkTreeSelection *selection;
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ guint i;
file_list:
file_list_seen = TRUE;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info);
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
+ for (gtk_bitset_iter_init_first (&iter, bitset, &i);
+ gtk_bitset_iter_is_valid (&iter);
+ gtk_bitset_iter_next (&iter, &i))
+ {
+ GFileInfo *info;
+ GFile *file;
+
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ file = _gtk_file_info_get_file (info);
+ g_clear_object (&info);
+
+ if (!file_from_entry || !g_file_equal (file_from_entry, file))
+ g_list_store_append (result, file);
+ }
/* If there is no selection in the file list, we probably have this situation:
*
@@ -5523,7 +4712,7 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
* So we want the selection to be "bar/foo.txt". Jump to the case for the
* filename entry to see if that is the case.
*/
- if (g_list_model_get_n_items (G_LIST_MODEL (info.result)) == 0 && impl->location_entry)
+ if (g_list_model_get_n_items (G_LIST_MODEL (result)) == 0 && impl->location_entry)
goto file_entry;
}
else if (impl->location_entry &&
@@ -5535,7 +4724,7 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
file_entry:
- check_save_entry (impl, &info.file_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
+ check_save_entry (impl, &file_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
if (is_empty)
goto out;
@@ -5543,17 +4732,17 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
if (!is_well_formed)
goto empty;
- if (info.file_from_entry)
+ if (file_from_entry)
{
- g_list_store_append (info.result, info.file_from_entry);
- g_object_unref (info.file_from_entry);
+ g_list_store_append (result, file_from_entry);
+ g_object_unref (file_from_entry);
}
else if (!file_list_seen)
goto file_list;
else
goto empty;
}
- else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
+ else if (impl->toplevel_last_focus_widget == impl->browse_files_column_view)
goto file_list;
else if (impl->location_entry && impl->toplevel_last_focus_widget == impl->location_entry)
goto file_entry;
@@ -5572,22 +4761,22 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser)
* then we fall back to the current directory
*/
if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
- g_list_model_get_n_items (G_LIST_MODEL (info.result)) == 0)
+ g_list_model_get_n_items (G_LIST_MODEL (result)) == 0)
{
GFile *current_folder;
current_folder = gtk_file_chooser_get_current_folder (chooser);
if (current_folder)
- g_list_store_append (info.result, current_folder);
+ g_list_store_append (result, current_folder);
}
- return G_LIST_MODEL (info.result);
+ return G_LIST_MODEL (result);
empty:
- g_list_store_remove_all (info.result);
- return G_LIST_MODEL (info.result);
+ g_list_store_remove_all (result);
+ return G_LIST_MODEL (result);
}
/* Shows or hides the filter widgets */
@@ -5703,74 +4892,21 @@ gtk_file_chooser_widget_get_shortcut_folders (GtkFileChooser *chooser)
return gtk_places_sidebar_get_shortcuts (GTK_PLACES_SIDEBAR (impl->places_sidebar));
}
-struct switch_folder_closure {
- GtkFileChooserWidget *impl;
- GFile *file;
- int num_selected;
-};
-
-/* Used from gtk_tree_selection_selected_foreach() in switch_to_selected_folder() */
-static void
-switch_folder_foreach_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- struct switch_folder_closure *closure;
-
- closure = data;
-
- closure->file = _gtk_file_system_model_get_file (GTK_FILE_SYSTEM_MODEL (model), iter);
- closure->num_selected++;
-}
-
/* Changes to the selected folder in the list view */
static void
switch_to_selected_folder (GtkFileChooserWidget *impl)
{
- GtkTreeSelection *selection;
- struct switch_folder_closure closure;
-
- /* We do this with foreach() rather than get_selected() as we may be in
- * multiple selection mode
- */
-
- closure.impl = impl;
- closure.file = NULL;
- closure.num_selected = 0;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure);
-
- g_assert (closure.file && closure.num_selected == 1);
-
- change_folder_and_display_error (impl, closure.file, FALSE);
-}
-
-/* Gets the GFileInfo for the selected row in the file list; assumes single
- * selection mode.
- */
-static GFileInfo *
-get_selected_file_info_from_file_list (GtkFileChooserWidget *impl,
- gboolean *had_selection)
-{
- GtkTreeSelection *selection;
- GtkTreeIter iter;
GFileInfo *info;
- GtkTreeModel *model;
+ GFile *file;
g_assert (!impl->select_multiple);
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- if (!gtk_tree_selection_get_selected (selection, &model, &iter))
- {
- *had_selection = FALSE;
- return NULL;
- }
+ g_assert (GTK_IS_SINGLE_SELECTION (impl->selection_model));
- *had_selection = TRUE;
+ info = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (impl->selection_model));
+ g_assert (info != NULL);
- info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (model), &iter);
- return info;
+ file = _gtk_file_info_get_file (info);
+ change_folder_and_display_error (impl, file, FALSE);
}
/* Gets the display name of the selected file in the file list; assumes single
@@ -5780,10 +4916,11 @@ static const char *
get_display_name_from_file_list (GtkFileChooserWidget *impl)
{
GFileInfo *info;
- gboolean had_selection;
- info = get_selected_file_info_from_file_list (impl, &had_selection);
- g_assert (had_selection);
+ g_assert (!impl->select_multiple);
+ g_assert (GTK_IS_SINGLE_SELECTION (impl->selection_model));
+
+ info = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (impl->selection_model));
g_assert (info != NULL);
return g_file_info_get_display_name (info);
@@ -6180,6 +5317,14 @@ location_popup_on_paste_handler (GtkFileChooserWidget *impl)
}
/* Implementation for GtkFileChooserEmbed::should_respond() */
+static gboolean
+recent_should_respond (GtkFileChooserWidget *impl)
+{
+ g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
+
+ return g_list_model_get_n_items (G_LIST_MODEL (impl->selection_model)) > 0;
+}
+
static void
add_selection_to_recent_list (GtkFileChooserWidget *impl)
{
@@ -6224,7 +5369,7 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserWidget *impl)
current_focus = gtk_root_get_focus (GTK_ROOT (toplevel));
- if (current_focus == impl->browse_files_tree_view)
+ if (current_focus == impl->browse_files_column_view)
{
/* The following array encodes what we do based on the impl->action and the
* number of files selected.
@@ -6426,7 +5571,7 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserWidget *impl)
g_object_unref (file);
}
- else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
+ else if (impl->toplevel_last_focus_widget == impl->browse_files_column_view)
{
/* The focus is on a dialog's action area button, *and* the widget that
* was focused immediately before it is the file list.
@@ -6470,7 +5615,7 @@ gtk_file_chooser_widget_initial_focus (GtkFileChooserWidget *impl)
{
if (impl->location_mode == LOCATION_MODE_PATH_BAR
|| impl->operation_mode == OPERATION_MODE_RECENT)
- widget = impl->browse_files_tree_view;
+ widget = impl->browse_files_column_view;
else
widget = impl->location_entry;
}
@@ -6486,65 +5631,30 @@ gtk_file_chooser_widget_initial_focus (GtkFileChooserWidget *impl)
gtk_widget_grab_focus (widget);
}
-static void
-selected_foreach_get_file_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- GSList **list;
- GFile *file;
-
- list = data;
-
- gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1);
- /* The file already has a new ref courtesy of gtk_tree_model_get();
- * this will be unreffed by the caller
- */
- *list = g_slist_prepend (*list, file);
-}
-
-static GSList *
-get_selected_files (GtkFileChooserWidget *impl)
-{
- GSList *result;
- GtkTreeSelection *selection;
-
- result = NULL;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, selected_foreach_get_file_cb, &result);
- result = g_slist_reverse (result);
-
- return result;
-}
-
-static void
-selected_foreach_get_info_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
+GSList *
+gtk_file_chooser_widget_get_selected_files (GtkFileChooserWidget *impl)
{
- GSList **list;
- GFileInfo *info;
+ GtkBitsetIter iter;
+ GtkBitset *bitset;
+ GSList *result = NULL;
+ guint i;
- list = data;
+ bitset = gtk_selection_model_get_selection (impl->selection_model);
- info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (model), iter);
- *list = g_slist_prepend (*list, g_object_ref (info));
-}
+ for (gtk_bitset_iter_init_first (&iter, bitset, &i);
+ gtk_bitset_iter_is_valid (&iter);
+ gtk_bitset_iter_next (&iter, &i))
+ {
+ GFileInfo *info;
+ GFile *file;
-static GSList *
-get_selected_infos (GtkFileChooserWidget *impl)
-{
- GSList *result;
- GtkTreeSelection *selection;
+ info = g_list_model_get_item (G_LIST_MODEL (impl->selection_model), i);
+ file = _gtk_file_info_get_file (info);
- result = NULL;
+ result = g_slist_prepend (result, g_object_ref (file));
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, selected_foreach_get_info_cb, &result);
- result = g_slist_reverse (result);
+ g_clear_object (&info);
+ }
return result;
}
@@ -6629,9 +5739,8 @@ search_clear_model (GtkFileChooserWidget *impl,
if (!impl->search_model)
return;
- if (remove &&
- gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)) == GTK_TREE_MODEL (impl->search_model))
- gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
+ if (remove && get_current_model (impl) == G_LIST_MODEL (impl->search_model))
+ set_current_model (impl, NULL);
g_clear_object (&impl->search_model);
}
@@ -6669,26 +5778,9 @@ search_setup_model (GtkFileChooserWidget *impl)
{
g_assert (impl->search_model == NULL);
- impl->search_model = _gtk_file_system_model_new (file_system_model_set,
- impl,
- MODEL_COLUMN_TYPES);
-
- gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->search_model),
- search_sort_func,
- impl, NULL);
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->search_model),
- GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
- GTK_SORT_ASCENDING);
-
- gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
- GTK_TREE_MODEL (impl->search_model));
-
- gtk_tree_view_column_set_sort_column_id (impl->list_name_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_time_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_size_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_type_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_location_column, -1);
+ impl->search_model = _gtk_file_system_model_new ();
+ set_current_model (impl, G_LIST_MODEL (impl->search_model));
update_columns (impl, TRUE, _("Modified"));
}
@@ -6835,7 +5927,7 @@ recent_clear_model (GtkFileChooserWidget *impl,
return;
if (remove)
- gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL);
+ set_current_model (impl, NULL);
g_set_object (&impl->recent_model, NULL);
}
@@ -6850,18 +5942,9 @@ recent_start_loading (GtkFileChooserWidget *impl)
/* Setup recent model */
g_assert (impl->recent_model == NULL);
- impl->recent_model = _gtk_file_system_model_new (file_system_model_set,
- impl,
- MODEL_COLUMN_TYPES);
+ impl->recent_model = _gtk_file_system_model_new ();
_gtk_file_system_model_set_filter (impl->recent_model, impl->current_filter);
- gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->recent_model),
- recent_sort_func,
- impl, NULL);
- gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->recent_model),
- GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
- GTK_SORT_DESCENDING);
-
if (!impl->recent_manager)
return;
@@ -6919,33 +6002,11 @@ recent_start_loading (GtkFileChooserWidget *impl)
g_list_free_full (items, (GDestroyNotify) gtk_recent_info_unref);
- gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view),
- GTK_TREE_MODEL (impl->recent_model));
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view), -1);
-
- gtk_tree_view_column_set_sort_column_id (impl->list_name_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_time_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_size_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_type_column, -1);
- gtk_tree_view_column_set_sort_column_id (impl->list_location_column, -1);
+ set_current_model (impl, G_LIST_MODEL (impl->recent_model));
update_columns (impl, TRUE, _("Accessed"));
}
-/* Called from ::should_respond(). We return whether there are selected
- * files in the recent files list.
- */
-static gboolean
-recent_should_respond (GtkFileChooserWidget *impl)
-{
- GtkTreeSelection *selection;
-
- g_assert (impl->operation_mode == OPERATION_MODE_RECENT);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- return (gtk_tree_selection_count_selected_rows (selection) != 0);
-}
-
static void
set_current_filter (GtkFileChooserWidget *impl,
GtkFileFilter *filter)
@@ -6973,7 +6034,6 @@ set_current_filter (GtkFileChooserWidget *impl,
gtk_drop_down_set_selected (GTK_DROP_DOWN (impl->filter_combo), filter_index);
- clear_model_cache (impl, MODEL_COL_IS_SENSITIVE);
set_model_filter (impl, impl->current_filter);
g_object_notify (G_OBJECT (impl), "filter");
}
@@ -6995,39 +6055,13 @@ filter_combo_changed (GtkDropDown *dropdown,
new_filter);
}
-static gboolean
-list_select_func (GtkTreeSelection *selection,
- GtkTreeModel *model,
- GtkTreePath *path,
- gboolean path_currently_selected,
- gpointer data)
-{
- GtkFileChooserWidget *impl = data;
-
- if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- {
- GtkTreeIter iter;
- gboolean is_sensitive;
- gboolean is_folder;
-
- if (!gtk_tree_model_get_iter (model, &iter, path))
- return FALSE;
- gtk_tree_model_get (model, &iter,
- MODEL_COL_IS_SENSITIVE, &is_sensitive,
- MODEL_COL_IS_FOLDER, &is_folder,
- -1);
- if (!is_sensitive || !is_folder)
- return FALSE;
- }
-
- return TRUE;
-}
-
static void
-list_selection_changed (GtkTreeSelection *selection,
+list_selection_changed (GtkSelectionModel *selection_model,
+ guint position,
+ guint n_items,
GtkFileChooserWidget *impl)
{
- if (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)) == NULL)
+ if (get_current_model (impl) == NULL)
return;
if (impl->location_entry &&
@@ -7038,10 +6072,23 @@ list_selection_changed (GtkTreeSelection *selection,
update_default (impl);
}
+static void
+list_items_changed (GListModel *model,
+ guint position,
+ guint removed,
+ guint added,
+ GtkFileChooserWidget *impl)
+{
+ if (get_current_model (impl) == NULL)
+ return;
+
+ update_default (impl);
+}
+
static gboolean
-browse_files_tree_view_keynav_failed_cb (GtkWidget *widget,
- GtkDirectionType direction,
- gpointer user_data)
+browse_files_column_view_keynav_failed_cb (GtkWidget *widget,
+ GtkDirectionType direction,
+ gpointer user_data)
{
GtkFileChooserWidget *impl = user_data;
@@ -7057,42 +6104,30 @@ browse_files_tree_view_keynav_failed_cb (GtkWidget *widget,
/* Callback used when a row in the file list is activated */
static void
-list_row_activated (GtkTreeView *tree_view,
- GtkTreePath *path,
- GtkTreeViewColumn *column,
- GtkFileChooserWidget *impl)
+column_view_row_activated_cb (GtkColumnView *column_view,
+ guint position,
+ GtkFileChooserWidget *self)
{
- GFile *file;
- GtkTreeIter iter;
- GtkTreeModel *model;
- gboolean is_folder;
- gboolean is_sensitive;
-
- model = gtk_tree_view_get_model (tree_view);
-
- if (!gtk_tree_model_get_iter (model, &iter, path))
- return;
+ GFileInfo *info;
- gtk_tree_model_get (model, &iter,
- MODEL_COL_FILE, &file,
- MODEL_COL_IS_FOLDER, &is_folder,
- MODEL_COL_IS_SENSITIVE, &is_sensitive,
- -1);
+ info = g_list_model_get_item (G_LIST_MODEL (self->selection_model),
+ position);
- if (is_sensitive && is_folder && file)
+ if (_gtk_file_info_consider_as_directory (info))
{
- change_folder_and_display_error (impl, file, FALSE);
- goto out;
+ GFile *file = _gtk_file_info_get_file (info);
+ change_folder_and_display_error (self, file, FALSE);
+ }
+ else if (self->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ self->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ {
+ /* prevent recursion */
+ g_signal_handlers_block_by_func (column_view, column_view_row_activated_cb, self);
+ gtk_widget_activate_default (GTK_WIDGET (self));
+ g_signal_handlers_unblock_by_func (column_view, column_view_row_activated_cb, self);
}
- if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
- gtk_widget_activate_default (GTK_WIDGET (impl));
-
- out:
-
- if (file)
- g_object_unref (file);
+ g_clear_object (&info);
}
static void
@@ -7117,54 +6152,6 @@ path_bar_clicked (GtkPathBar *path_bar,
}
static void
-update_cell_renderer_attributes (GtkFileChooserWidget *impl)
-{
- gtk_tree_view_column_set_attributes (impl->list_name_column,
- impl->list_pixbuf_renderer,
- "gicon", MODEL_COL_ICON,
- "sensitive", MODEL_COL_IS_SENSITIVE,
- NULL);
- gtk_tree_view_column_set_attributes (impl->list_name_column,
- impl->list_name_renderer,
- "text", MODEL_COL_NAME,
- "ellipsize", MODEL_COL_ELLIPSIZE,
- "sensitive", MODEL_COL_IS_SENSITIVE,
- NULL);
-
- gtk_tree_view_column_set_attributes (impl->list_size_column,
- impl->list_size_renderer,
- "text", MODEL_COL_SIZE_TEXT,
- "sensitive", MODEL_COL_IS_SENSITIVE,
- NULL);
-
- gtk_tree_view_column_set_attributes (impl->list_type_column,
- impl->list_type_renderer,
- "text", MODEL_COL_TYPE,
- "sensitive", MODEL_COL_IS_SENSITIVE,
- NULL);
-
- gtk_tree_view_column_set_attributes (impl->list_time_column,
- impl->list_date_renderer,
- "text", MODEL_COL_DATE_TEXT,
- "sensitive", MODEL_COL_IS_SENSITIVE,
- NULL);
-
- gtk_tree_view_column_set_attributes (impl->list_time_column,
- impl->list_time_renderer,
- "text", MODEL_COL_TIME_TEXT,
- "sensitive", MODEL_COL_IS_SENSITIVE,
- NULL);
-
- gtk_tree_view_column_set_attributes (impl->list_location_column,
- impl->list_location_renderer,
- "text", MODEL_COL_LOCATION_TEXT,
- "sensitive", MODEL_COL_IS_SENSITIVE,
- NULL);
-
- update_time_renderer_visible (impl);
-}
-
-static void
location_set_user_text (GtkFileChooserWidget *impl,
const char *path)
{
@@ -7348,11 +6335,12 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
widget_class->unmap = gtk_file_chooser_widget_unmap;
widget_class->root = gtk_file_chooser_widget_root;
widget_class->unroot = gtk_file_chooser_widget_unroot;
- widget_class->css_changed = gtk_file_chooser_widget_css_changed;
widget_class->size_allocate = gtk_file_chooser_widget_size_allocate;
widget_class->grab_focus = gtk_widget_grab_focus_child;
widget_class->focus = gtk_widget_focus_child;
+ g_type_ensure (GTK_TYPE_FILE_THUMBNAIL);
+
/*
* Signals
*/
@@ -7724,6 +6712,11 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
"",
GTK_PARAM_READABLE));
+ g_object_class_install_property (gobject_class, PROP_SHOW_TIME,
+ g_param_spec_boolean ("show-time", NULL, NULL,
+ FALSE,
+ GTK_PARAM_READABLE));
+
_gtk_file_chooser_install_properties (gobject_class);
/* Bind class to template */
@@ -7735,13 +6728,18 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_files_stack);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, places_sidebar);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, places_view);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_files_tree_view);
+ gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_files_column_view);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_files_swin);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_header_revealer);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_header_stack);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_new_folder_button);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_path_bar_size_group);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, browse_path_bar);
+ gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_name_column);
+ gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_location_column);
+ gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_size_column);
+ gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_time_column);
+ gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, column_view_type_column);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, filter_combo_hbox);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, filter_combo);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, extra_align);
@@ -7749,18 +6747,6 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, location_entry_box);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, search_entry);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, search_spinner);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_name_column);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_pixbuf_renderer);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_name_renderer);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_time_column);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_date_renderer);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_time_renderer);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_size_column);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_size_renderer);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_type_column);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_type_renderer);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_location_column);
- gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, list_location_renderer);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, new_folder_name_entry);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, new_folder_create_button);
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, new_folder_error_stack);
@@ -7773,10 +6759,7 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_child (widget_class, GtkFileChooserWidget, box);
/* And a *lot* of callbacks to bind ... */
- gtk_widget_class_bind_template_callback (widget_class, file_list_query_tooltip_cb);
- gtk_widget_class_bind_template_callback (widget_class, list_row_activated);
- gtk_widget_class_bind_template_callback (widget_class, list_selection_changed);
- gtk_widget_class_bind_template_callback (widget_class, browse_files_tree_view_keynav_failed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, browse_files_column_view_keynav_failed_cb);
gtk_widget_class_bind_template_callback (widget_class, filter_combo_changed);
gtk_widget_class_bind_template_callback (widget_class, path_bar_clicked);
gtk_widget_class_bind_template_callback (widget_class, places_sidebar_open_location_cb);
@@ -7790,8 +6773,14 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_callback (widget_class, rename_file_name_changed);
gtk_widget_class_bind_template_callback (widget_class, rename_file_rename_clicked);
gtk_widget_class_bind_template_callback (widget_class, rename_file_end);
- gtk_widget_class_bind_template_callback (widget_class, click_cb);
- gtk_widget_class_bind_template_callback (widget_class, long_press_cb);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_get_file_date);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_get_file_display_name);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_get_file_time);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_get_file_type);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_get_location);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_get_size);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_get_tooltip_text);
+ gtk_widget_class_bind_template_callback (widget_class, column_view_row_activated_cb);
gtk_widget_class_set_css_name (widget_class, I_("filechooser"));
@@ -7835,49 +6824,21 @@ captured_key (GtkEventControllerKey *controller,
static void
post_process_ui (GtkFileChooserWidget *impl)
{
- GdkContentFormats *drag_formats;
- GtkTreeSelection *selection;
GFile *file;
GtkDropTarget *target;
- GtkGesture *gesture;
GtkEventController *controller;
GtkShortcutTrigger *trigger;
GtkShortcutAction *action;
GtkShortcut *shortcut;
- /* Setup file list treeview */
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
- gtk_tree_selection_set_select_function (selection,
- list_select_func,
- impl, NULL);
-
- drag_formats = gdk_content_formats_new_for_gtype (GDK_TYPE_FILE_LIST);
- gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->browse_files_tree_view),
- GDK_BUTTON1_MASK,
- drag_formats,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gdk_content_formats_unref (drag_formats);
-
target = gtk_drop_target_new (GDK_TYPE_FILE_LIST, GDK_ACTION_COPY | GDK_ACTION_MOVE);
g_signal_connect (target, "drop", G_CALLBACK (file_list_drag_drop_cb), impl);
- gtk_widget_add_controller (impl->browse_files_tree_view, GTK_EVENT_CONTROLLER (target));
-
- /* File browser treemodel columns are shared between GtkFileChooser implementations,
- * so we don't set cell renderer attributes in GtkBuilder, but rather keep that
- * in code.
- */
- file_list_set_sort_column_ids (impl);
- update_cell_renderer_attributes (impl);
+ gtk_widget_add_controller (impl->browse_files_swin, GTK_EVENT_CONTROLLER (target));
file = g_file_new_for_path ("/");
_gtk_path_bar_set_file (GTK_PATH_BAR (impl->browse_path_bar), file, FALSE);
g_object_unref (file);
- /* Set the fixed size icon renderer, this requires
- * that impl->icon_size be already setup.
- */
- set_icon_cell_renderer_fixed_size (impl);
-
gtk_popover_set_default_widget (GTK_POPOVER (impl->new_folder_popover), impl->new_folder_create_button);
gtk_popover_set_default_widget (GTK_POPOVER (impl->rename_file_popover), impl->rename_file_rename_button);
@@ -7899,32 +6860,18 @@ post_process_ui (GtkFileChooserWidget *impl)
gtk_widget_set_parent (impl->rename_file_popover, GTK_WIDGET (impl));
- gesture = gtk_gesture_click_new ();
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
- g_signal_connect (gesture, "pressed", G_CALLBACK (files_list_clicked), impl);
- gtk_widget_add_controller (GTK_WIDGET (impl->browse_files_tree_view), GTK_EVENT_CONTROLLER (gesture));
-
controller = gtk_shortcut_controller_new ();
trigger = gtk_alternative_trigger_new (gtk_keyval_trigger_new (GDK_KEY_F10, GDK_SHIFT_MASK),
gtk_keyval_trigger_new (GDK_KEY_Menu, 0));
action = gtk_callback_action_new (list_popup_menu_cb, impl, NULL);
shortcut = gtk_shortcut_new (trigger, action);
gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut);
- gtk_widget_add_controller (GTK_WIDGET (impl->browse_files_tree_view), controller);
-
- /* Add ability to restrict interaction on file list (click and key_press events),
- * needed to prevent data loss bug #2288 */
- gesture = gtk_gesture_click_new ();
- gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
- gtk_gesture_single_set_exclusive (GTK_GESTURE_SINGLE (gesture), TRUE);
- gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), GTK_PHASE_CAPTURE);
- g_signal_connect (gesture, "pressed", G_CALLBACK (files_list_restrict_clicking), impl);
- gtk_widget_add_controller (impl->browse_files_tree_view, GTK_EVENT_CONTROLLER (gesture));
+ gtk_widget_add_controller (GTK_WIDGET (impl->browse_files_column_view), controller);
controller = gtk_event_controller_key_new ();
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
g_signal_connect (controller, "key-pressed", G_CALLBACK (files_list_restrict_key_presses), impl);
- gtk_widget_add_controller (impl->browse_files_tree_view, controller);
+ gtk_widget_add_controller (impl->browse_files_column_view, controller);
}
void
@@ -7943,19 +6890,287 @@ gtk_file_chooser_widget_set_save_entry (GtkFileChooserWidget *impl,
}
}
-static void
-display_changed_cb (GtkWidget *wiget,
- GParamSpec *pspec,
- GtkFileChooserWidget *impl)
+static char *
+filter_name (GtkFileFilter *filter)
+{
+ return g_strdup (gtk_file_filter_get_name (filter));
+}
+
+static gboolean
+match_func (gpointer item, gpointer user_data)
{
- remove_settings_signal (impl);
- check_icon_theme (impl);
+ return g_file_info_get_attribute_boolean (G_FILE_INFO (item), "filechooser::visible");
+}
+
+static GtkOrdering
+directory_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ GtkFileChooserWidget *impl = user_data;
+
+ if (impl->sort_directories_first)
+ {
+ gboolean adir = _gtk_file_info_consider_as_directory ((GFileInfo *)a);
+ gboolean bdir = _gtk_file_info_consider_as_directory ((GFileInfo *)b);
+
+ if (adir && !bdir)
+ return GTK_ORDERING_SMALLER;
+ if (!adir && bdir)
+ return GTK_ORDERING_LARGER;
+ }
+
+ return GTK_ORDERING_EQUAL;
+}
+
+static GtkOrdering
+name_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ char *key_a, *key_b;
+ GtkOrdering result;
+
+ /* FIXME: use sortkeys for these */
+ key_a = g_utf8_collate_key_for_filename (g_file_info_get_display_name ((GFileInfo *)a), -1);
+ key_b = g_utf8_collate_key_for_filename (g_file_info_get_display_name ((GFileInfo *)b), -1);
+
+ result = g_strcmp0 (key_a, key_b);
+
+ g_free (key_a);
+ g_free (key_b);
+
+ return result;
+}
+
+static GtkOrdering
+location_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ GtkFileChooserWidget *impl = user_data;
+ char *key_a, *key_b;
+ GtkOrdering result;
+
+ /* FIXME: use sortkeys for these */
+ key_a = g_utf8_collate_key_for_filename (file_chooser_get_location (impl, (GFileInfo *)a), -1);
+ key_b = g_utf8_collate_key_for_filename (file_chooser_get_location (impl, (GFileInfo *)b), -1);
+
+ result = g_strcmp0 (key_a, key_b);
+
+ g_free (key_a);
+ g_free (key_b);
+
+ return result;
+}
+
+static GtkOrdering
+size_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ gint64 size_a, size_b;
+
+ size_a = g_file_info_get_size ((GFileInfo *)a);
+ size_b = g_file_info_get_size ((GFileInfo *)b);
+
+ if (size_a < size_b)
+ return GTK_ORDERING_SMALLER;
+ else if (size_a > size_b)
+ return GTK_ORDERING_LARGER;
+ else
+ return GTK_ORDERING_EQUAL;
+}
+
+static GtkOrdering
+type_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ GtkFileChooserWidget *impl = user_data;
+ char *key_a, *key_b;
+ GtkOrdering result;
+
+ /* FIXME: use sortkeys for these */
+ key_a = get_type_information (impl, (GFileInfo *)a);
+ key_b = get_type_information (impl, (GFileInfo *)b);
+
+ result = g_strcmp0 (key_a, key_b);
+
+ g_free (key_a);
+ g_free (key_b);
+
+ return result;
+}
+
+static GtkOrdering
+time_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ GtkFileChooserWidget *impl = user_data;
+ glong time_a, time_b;
+
+ if (impl->operation_mode == OPERATION_MODE_RECENT)
+ {
+ time_a = (glong) g_file_info_get_attribute_uint64 ((GFileInfo *)a, G_FILE_ATTRIBUTE_TIME_ACCESS);
+ time_b = (glong) g_file_info_get_attribute_uint64 ((GFileInfo *)b, G_FILE_ATTRIBUTE_TIME_ACCESS);
+ }
+
+ else
+ {
+ time_a = (glong) g_file_info_get_attribute_uint64 ((GFileInfo *)a, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ time_b = (glong) g_file_info_get_attribute_uint64 ((GFileInfo *)b, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ }
+
+
+ if (time_a < time_b)
+ return GTK_ORDERING_SMALLER;
+ else if (time_a > time_b)
+ return GTK_ORDERING_LARGER;
+ else
+ return GTK_ORDERING_EQUAL;
+}
+
+static GtkOrdering
+recent_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ GtkOrdering result;
+
+ result = time_sort_func (a, b, user_data);
+
+ if (result == GTK_ORDERING_EQUAL)
+ result = name_sort_func (a, b, user_data);
+
+ if (result == GTK_ORDERING_EQUAL)
+ result = location_sort_func (a, b, user_data);
+
+ return result;
+}
+
+static GtkOrdering
+search_sort_func (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ GtkOrdering result;
+
+ result = location_sort_func (a, b, user_data);
+
+ if (result == GTK_ORDERING_EQUAL)
+ result = name_sort_func (a, b, user_data);
+
+ if (result == GTK_ORDERING_EQUAL)
+ result = time_sort_func (a, b, user_data);
+
+ return result;
}
static char *
-filter_name (GtkFileFilter *filter)
+get_name (GFileInfo *info)
{
- return g_strdup (gtk_file_filter_get_name (filter));
+ return g_strdup (g_file_info_get_display_name (info));
+}
+
+static void
+sorter_changed (GtkSorter *sorter,
+ GParamSpec *pspec,
+ GtkFileChooserWidget *impl)
+{
+ GtkColumnViewSorter *main_sorter = GTK_COLUMN_VIEW_SORTER (sorter);
+ GtkColumnViewColumn *primary;
+
+ primary = gtk_column_view_sorter_get_primary_sort_column (main_sorter);
+ impl->sort_column = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (primary), "column"));
+ impl->sort_order = gtk_column_view_sorter_get_primary_sort_order (main_sorter);
+}
+
+static void
+setup_sorting (GtkFileChooserWidget *impl)
+{
+ GtkFileSystemModel *fsmodel;
+ GtkSorter *main_sorter;
+ GtkSorter *sorter = NULL;
+
+ fsmodel = GTK_FILE_SYSTEM_MODEL (get_current_model (impl));
+
+ main_sorter = gtk_column_view_get_sorter (GTK_COLUMN_VIEW (impl->browse_files_column_view));
+ g_signal_handlers_disconnect_by_func (main_sorter, G_CALLBACK (sorter_changed), impl);
+
+ gtk_column_view_column_set_sorter (impl->column_view_name_column, NULL);
+ gtk_column_view_column_set_sorter (impl->column_view_location_column, NULL);
+ gtk_column_view_column_set_sorter (impl->column_view_size_column, NULL);
+ gtk_column_view_column_set_sorter (impl->column_view_type_column, NULL);
+ gtk_column_view_column_set_sorter (impl->column_view_time_column, NULL);
+
+ if (fsmodel == impl->browse_files_model)
+ {
+ GtkExpression *expression;
+ GtkColumnViewColumn *column;
+
+ expression = gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
+ 0, NULL,
+ G_CALLBACK (get_name),
+ NULL, NULL);
+ sorter = GTK_SORTER (gtk_string_sorter_new (expression));
+ gtk_string_sorter_set_collation (GTK_STRING_SORTER (sorter), GTK_COLLATION_FILENAME);
+ gtk_column_view_column_set_sorter (impl->column_view_name_column, sorter);
+ g_object_unref (sorter);
+
+ sorter = GTK_SORTER (gtk_custom_sorter_new (size_sort_func, impl, NULL));
+ gtk_column_view_column_set_sorter (impl->column_view_size_column, sorter);
+ g_object_unref (sorter);
+
+ sorter = GTK_SORTER (gtk_custom_sorter_new (type_sort_func, impl, NULL));
+ gtk_column_view_column_set_sorter (impl->column_view_type_column, sorter);
+ g_object_unref (sorter);
+
+ sorter = GTK_SORTER (gtk_custom_sorter_new (time_sort_func, impl, NULL));
+ gtk_column_view_column_set_sorter (impl->column_view_time_column, sorter);
+ g_object_unref (sorter);
+
+ sorter = GTK_SORTER (gtk_multi_sorter_new ());
+ gtk_multi_sorter_append (GTK_MULTI_SORTER (sorter), GTK_SORTER (gtk_custom_sorter_new (directory_sort_func, impl, NULL)));
+ gtk_multi_sorter_append (GTK_MULTI_SORTER (sorter), g_object_ref (gtk_column_view_get_sorter (GTK_COLUMN_VIEW (impl->browse_files_column_view))));
+
+ switch (impl->sort_column)
+ {
+ case 0:
+ column = impl->column_view_name_column;
+ break;
+ case 1:
+ column = impl->column_view_size_column;
+ break;
+ case 2:
+ column = impl->column_view_type_column;
+ break;
+ case 3:
+ column = impl->column_view_time_column;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ gtk_column_view_sort_by_column (GTK_COLUMN_VIEW (impl->browse_files_column_view),
+ column, impl->sort_order);
+
+ sorter_changed (main_sorter, NULL, impl);
+ g_signal_connect (main_sorter, "notify::primary-sort-column", G_CALLBACK (sorter_changed), impl);
+ g_signal_connect (main_sorter, "notify::primary-sort-order", G_CALLBACK (sorter_changed), impl);
+ }
+ else if (fsmodel == impl->recent_model)
+ {
+ sorter = GTK_SORTER (gtk_custom_sorter_new (recent_sort_func, impl, NULL));
+ }
+ else if (fsmodel == impl->search_model)
+ {
+ sorter = GTK_SORTER (gtk_custom_sorter_new (search_sort_func, impl, NULL));
+ }
+
+ gtk_sort_list_model_set_sorter (impl->sort_model, sorter);
+ g_clear_object (&sorter);
}
static void
@@ -7963,7 +7178,16 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *impl)
{
GtkExpression *expression;
+ /* Ensure private types used by the template
+ * definition before calling gtk_widget_init_template()
+ */
+ g_type_ensure (GTK_TYPE_PATH_BAR);
+ g_type_ensure (GTK_TYPE_PLACES_VIEW);
+ g_type_ensure (GTK_TYPE_PLACES_SIDEBAR);
+ g_type_ensure (GTK_TYPE_FILE_CHOOSER_ERROR_STACK);
+
impl->select_multiple = FALSE;
+ impl->sort_directories_first = FALSE;
impl->show_hidden = FALSE;
impl->show_size_column = TRUE;
impl->show_type_column = TRUE;
@@ -7973,25 +7197,31 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *impl)
impl->pending_select_files = NULL;
impl->location_mode = LOCATION_MODE_PATH_BAR;
impl->operation_mode = OPERATION_MODE_BROWSE;
- impl->sort_column = MODEL_COL_NAME;
impl->sort_order = GTK_SORT_ASCENDING;
impl->create_folders = TRUE;
impl->auto_selecting_first_row = FALSE;
impl->renamed_file = NULL;
- /* Ensure private types used by the template
- * definition before calling gtk_widget_init_template()
- */
- g_type_ensure (GTK_TYPE_PATH_BAR);
- g_type_ensure (GTK_TYPE_PLACES_VIEW);
- g_type_ensure (GTK_TYPE_PLACES_SIDEBAR);
- g_type_ensure (GTK_TYPE_FILE_CHOOSER_ERROR_STACK);
-
gtk_widget_init_template (GTK_WIDGET (impl));
- gtk_widget_set_size_request (impl->browse_files_tree_view, 280, -1);
- g_signal_connect (impl, "notify::display,", G_CALLBACK (display_changed_cb), impl);
- check_icon_theme (impl);
+ impl->selection_model = GTK_SELECTION_MODEL (gtk_single_selection_new (NULL));
+ impl->filter_model = gtk_filter_list_model_new (NULL, GTK_FILTER (gtk_custom_filter_new (match_func, NULL, NULL)));
+ impl->sort_model = gtk_sort_list_model_new (NULL, NULL);
+
+ g_signal_connect (impl->selection_model, "selection-changed",
+ G_CALLBACK (list_selection_changed), impl);
+ g_signal_connect (impl->selection_model, "items-changed",
+ G_CALLBACK (list_items_changed), impl);
+
+ gtk_single_selection_set_model (GTK_SINGLE_SELECTION (impl->selection_model), G_LIST_MODEL (impl->sort_model));
+ gtk_sort_list_model_set_model (impl->sort_model, G_LIST_MODEL (impl->filter_model));
+
+ gtk_column_view_set_model (GTK_COLUMN_VIEW (impl->browse_files_column_view), impl->selection_model);
+
+ g_object_set_data (G_OBJECT (impl->column_view_name_column), "column", GINT_TO_POINTER (0));
+ g_object_set_data (G_OBJECT (impl->column_view_size_column), "column", GINT_TO_POINTER (1));
+ g_object_set_data (G_OBJECT (impl->column_view_type_column), "column", GINT_TO_POINTER (2));
+ g_object_set_data (G_OBJECT (impl->column_view_time_column), "column", GINT_TO_POINTER (3));
impl->bookmarks_manager = _gtk_bookmarks_manager_new (NULL, NULL);
@@ -8169,3 +7399,8 @@ gtk_file_chooser_widget_get_choice (GtkFileChooser *chooser,
return NULL;
}
+GtkSelectionModel *
+gtk_file_chooser_widget_get_selection_model (GtkFileChooserWidget *chooser)
+{
+ return chooser->selection_model;
+}
diff --git a/gtk/gtkfilechooserwidgetprivate.h b/gtk/gtkfilechooserwidgetprivate.h
index 07b0ce3897..91fdddd962 100644
--- a/gtk/gtkfilechooserwidgetprivate.h
+++ b/gtk/gtkfilechooserwidgetprivate.h
@@ -23,6 +23,7 @@
#include <glib.h>
#include "gtkfilechooserwidget.h"
+#include "gtkselectionmodel.h"
G_BEGIN_DECLS
@@ -36,6 +37,12 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserWidget *chooser);
void
gtk_file_chooser_widget_initial_focus (GtkFileChooserWidget *chooser);
+GSList *
+gtk_file_chooser_widget_get_selected_files (GtkFileChooserWidget *impl);
+
+GtkSelectionModel *
+gtk_file_chooser_widget_get_selection_model (GtkFileChooserWidget *chooser);
+
G_END_DECLS
#endif /* __GTK_FILE_CHOOSER_WIDGET_PRIVATE_H__ */
diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c
index 20b670e774..93f063ae8b 100644
--- a/gtk/gtkfilesystemmodel.c
+++ b/gtk/gtkfilesystemmodel.c
@@ -27,7 +27,6 @@
#include "gtkmarshalers.h"
#include "deprecated/gtktreedatalistprivate.h"
#include "deprecated/gtktreednd.h"
-#include "deprecated/gtktreemodel.h"
#include "gtkfilter.h"
#include "gtkprivate.h"
@@ -67,8 +66,7 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
*
* The model->file_lookup hash table is populated lazily. It is both accessed and populated with the
* node_get_for_file() function. The invariant is that the files in model->files[n] for n < g_hash_table_size
- * (model->file_lookup) are already added to the hash table. The hash table will get cleared when we re-sort the
- * files, as the array will be in a different order and the indexes need to be rebuilt.
+ * (model->file_lookup) are already added to the hash table.
*
* Each FileModelNode has a node->visible field, which indicates whether the node is visible in the GtkTreeView.
* A node may be invisible if, for example, it corresponds to a hidden file and the file chooser is not showing
@@ -108,16 +106,6 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
*
* You never access a node->row directly. Instead, call node_get_tree_row(). That function will validate the nodes
* up to the sought one if the node is not valid yet, and it will return a proper 0-based row.
- *
- * Sorting
- * -------
- *
- * The model implements the GtkTreeSortable interface. To avoid re-sorting
- * every time a node gets added (which would lead to O(n^2) performance during
- * the initial population of the model), the model can freeze itself (with
- * freeze_updates()) during the initial population process. When the model is
- * frozen, sorting will not happen. The model will sort itself when the freeze
- * count goes back to zero, via corresponding calls to thaw_updates().
*/
/*** DEFINES ***/
@@ -145,8 +133,6 @@ struct _FileModelNode
guint visible :1; /* if the file is currently visible */
guint filtered_out :1;/* if the file is currently filtered out (i.e. it didn't pass the filters) */
guint frozen_add :1; /* true if the model was frozen and the entry has not been added yet */
-
- GValue values[1]; /* actually n_columns values */
};
struct _GtkFileSystemModel
@@ -160,33 +146,18 @@ struct _GtkFileSystemModel
GCancellable * cancellable; /* cancellable in use for all operations - cancelled on dispose */
GArray * files; /* array of FileModelNode containing all our files */
- gsize node_size; /* Size of a FileModelNode structure once its ->values field has n_columns */
guint n_nodes_valid; /* count of valid nodes (i.e. those whose node->row is accurate) */
GHashTable * file_lookup; /* mapping of GFile => array index in model->files
* This hash table doesn't always have the same number of entries as the files array;
- * it can get cleared completely when we resort.
* The hash table gets re-populated in node_get_for_file() if this mismatch is
* detected.
*/
- guint n_columns; /* number of columns */
- GType * column_types; /* types of each column */
- GtkFileSystemModelGetValue get_func; /* function to call to fill in values in columns */
- gpointer get_data; /* data to pass to get_func */
-
GtkFileFilter * filter; /* filter to use for deciding which nodes are visible */
- int sort_column_id; /* current sorting column */
- GtkSortType sort_order; /* current sorting order */
- GList * sort_list; /* list of sorting functions */
- GtkTreeIterCompareFunc default_sort_func; /* default sort function */
- gpointer default_sort_data; /* data to pass to default sort func */
- GDestroyNotify default_sort_destroy; /* function to call to destroy default_sort_data */
-
guint frozen; /* number of times we're frozen */
gboolean filter_on_thaw :1;/* set when filtering needs to happen upon thawing */
- gboolean sort_on_thaw :1;/* set when sorting needs to happen upon thawing */
guint show_hidden :1; /* whether to show hidden files */
guint show_folders :1;/* whether to show folders */
@@ -237,10 +208,10 @@ static void remove_file (GtkFileSystemModel *model,
/*** FileModelNode ***/
/* Get a FileModelNode structure given an index in the model->files array of nodes */
-#define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * (_model)->node_size))
+#define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * sizeof (FileModelNode)))
/* Get an index within the model->files array of nodes, given a FileModelNode* */
-#define node_index(_model, _node) (((char *) (_node) - (_model)->files->data) / (_model)->node_size)
+#define node_index(_model, _node) (((char *) (_node) - (_model)->files->data) / sizeof (FileModelNode))
/* @up_to_index: smallest model->files array index that will be valid after this call
* @up_to_row: smallest node->row that will be valid after this call
@@ -276,7 +247,7 @@ node_validate_rows (GtkFileSystemModel *model, guint up_to_index, guint up_to_ro
model->n_nodes_valid = i;
}
-static guint
+static guint G_GNUC_UNUSED
node_get_tree_row (GtkFileSystemModel *model, guint index)
{
if (model->n_nodes_valid <= index)
@@ -291,50 +262,6 @@ node_invalidate_index (GtkFileSystemModel *model, guint id)
model->n_nodes_valid = MIN (model->n_nodes_valid, id);
}
-static GtkTreePath *
-tree_path_new_from_node (GtkFileSystemModel *model, guint id)
-{
- guint r = node_get_tree_row (model, id);
-
- g_assert (r < model->files->len);
-
- return gtk_tree_path_new_from_indices (r, -1);
-}
-
-static void
-emit_row_inserted_for_node (GtkFileSystemModel *model, guint id)
-{
- GtkTreePath *path;
- GtkTreeIter iter;
-
- path = tree_path_new_from_node (model, id);
- ITER_INIT_FROM_INDEX (model, &iter, id);
- gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
- gtk_tree_path_free (path);
-}
-
-static void
-emit_row_changed_for_node (GtkFileSystemModel *model, guint id)
-{
- GtkTreePath *path;
- GtkTreeIter iter;
-
- path = tree_path_new_from_node (model, id);
- ITER_INIT_FROM_INDEX (model, &iter, id);
- gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter);
- gtk_tree_path_free (path);
-}
-
-static void
-emit_row_deleted_for_row (GtkFileSystemModel *model, guint row)
-{
- GtkTreePath *path;
-
- path = gtk_tree_path_new_from_indices (row, -1);
- gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
- gtk_tree_path_free (path);
-}
-
static void
node_set_visible_and_filtered_out (GtkFileSystemModel *model, guint id, gboolean visible, gboolean filtered_out)
{
@@ -342,15 +269,17 @@ node_set_visible_and_filtered_out (GtkFileSystemModel *model, guint id, gboolean
/* Filteredness */
+ g_file_info_set_attribute_boolean (node->info, "filechooser::filtered-out", filtered_out);
+
if (node->filtered_out != filtered_out)
{
node->filtered_out = filtered_out;
- if (node->visible && visible)
- emit_row_changed_for_node (model, id);
}
/* Visibility */
-
+
+ g_file_info_set_attribute_boolean (node->info, "filechooser::visible", visible);
+
if (node->visible == visible ||
node->frozen_add)
return;
@@ -359,18 +288,13 @@ node_set_visible_and_filtered_out (GtkFileSystemModel *model, guint id, gboolean
{
node->visible = TRUE;
node_invalidate_index (model, id);
- emit_row_inserted_for_node (model, id);
}
else
{
- guint row;
-
- row = node_get_tree_row (model, id);
- g_assert (row < model->files->len);
+ g_assert (node_get_tree_row (model, id) < model->files->len);
node->visible = FALSE;
node_invalidate_index (model, id);
- emit_row_deleted_for_row (model, row);
}
}
@@ -385,8 +309,7 @@ node_should_be_filtered_out (GtkFileSystemModel *model, guint id)
if (model->filter == NULL)
return FALSE;
- if (!g_file_info_has_attribute (node->info, "standard::file"))
- g_file_info_set_attribute_object (node->info, "standard::file", G_OBJECT (node->file));
+ g_assert (g_file_info_has_attribute (node->info, "standard::file"));
return !gtk_filter_match (GTK_FILTER (model->filter), node->info);
}
@@ -435,516 +358,44 @@ node_compute_visibility_and_filters (GtkFileSystemModel *model, guint id)
node_set_visible_and_filtered_out (model, id, visible, filtered_out);
}
-/*** GtkTreeModel ***/
-
-static GtkTreeModelFlags
-gtk_file_system_model_get_flags (GtkTreeModel *tree_model)
-{
- /* GTK_TREE_MODEL_ITERS_PERSIST doesn't work with arrays :( */
- return GTK_TREE_MODEL_LIST_ONLY;
-}
-
-static int
-gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
-
- return model->n_columns;
-}
+/*** GListModel ***/
static GType
-gtk_file_system_model_get_column_type (GtkTreeModel *tree_model,
- int i)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
-
- g_return_val_if_fail (i >= 0 && (guint) i < model->n_columns, G_TYPE_NONE);
-
- return model->column_types[i];
-}
-
-static int
-compare_indices (gconstpointer key, gconstpointer _node)
-{
- const FileModelNode *node = _node;
-
- return GPOINTER_TO_UINT (key) - node->row;
-}
-
-static gboolean
-gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model,
- GtkTreeIter *iter,
- GtkTreeIter *parent,
- int n)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
- char *node;
- guint id;
- guint row_to_find;
-
- g_return_val_if_fail (n >= 0, FALSE);
-
- if (parent != NULL)
- return FALSE;
-
- row_to_find = n + 1; /* plus one as our node->row numbers are 1-based; see the "Structure" comment at the beginning */
-
- if (model->n_nodes_valid > 0 &&
- get_node (model, model->n_nodes_valid - 1)->row >= row_to_find)
- {
- /* Fast path - the nodes are valid up to the sought one.
- *
- * First, find a node with the sought row number...*/
-
- node = bsearch (GUINT_TO_POINTER (row_to_find),
- model->files->data,
- model->n_nodes_valid,
- model->node_size,
- compare_indices);
- if (node == NULL)
- return FALSE;
-
- /* ... Second, back up until we find the first visible node with that row number */
-
- id = node_index (model, node);
- while (!get_node (model, id)->visible)
- id--;
-
- g_assert (get_node (model, id)->row == row_to_find);
- }
- else
- {
- /* Slow path - the nodes need to be validated up to the sought one */
-
- node_validate_rows (model, G_MAXUINT, n); /* note that this is really "n", not row_to_find - see node_validate_rows() */
- id = model->n_nodes_valid - 1;
- if (model->n_nodes_valid == 0 || get_node (model, id)->row != row_to_find)
- return FALSE;
- }
-
- ITER_INIT_FROM_INDEX (model, iter, id);
- return TRUE;
-}
-
-static gboolean
-gtk_file_system_model_get_iter (GtkTreeModel *tree_model,
- GtkTreeIter *iter,
- GtkTreePath *path)
-{
- g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
-
- if (gtk_tree_path_get_depth (path) > 1)
- return FALSE;
-
- return gtk_file_system_model_iter_nth_child (tree_model,
- iter,
- NULL,
- gtk_tree_path_get_indices (path)[0]);
-}
-
-static GtkTreePath *
-gtk_file_system_model_get_path (GtkTreeModel *tree_model,
- GtkTreeIter *iter)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
-
- g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL);
-
- return tree_path_new_from_node (model, ITER_INDEX (iter));
-}
-
-static void
-gtk_file_system_model_get_value (GtkTreeModel *tree_model,
- GtkTreeIter *iter,
- int column,
- GValue *value)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
- const GValue *original;
-
- g_return_if_fail ((guint) column < model->n_columns);
- g_return_if_fail (ITER_IS_VALID (model, iter));
-
- original = _gtk_file_system_model_get_value (model, iter, column);
- if (original)
- {
- g_value_init (value, G_VALUE_TYPE (original));
- g_value_copy (original, value);
- }
- else
- g_value_init (value, model->column_types[column]);
-}
-
-static gboolean
-gtk_file_system_model_iter_next (GtkTreeModel *tree_model,
- GtkTreeIter *iter)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
- guint i;
-
- g_return_val_if_fail (ITER_IS_VALID (model, iter), FALSE);
-
- for (i = ITER_INDEX (iter) + 1; i < model->files->len; i++)
- {
- FileModelNode *node = get_node (model, i);
-
- if (node->visible)
- {
- ITER_INIT_FROM_INDEX (model, iter, i);
- return TRUE;
- }
- }
-
- return FALSE;
-}
-
-static gboolean
-gtk_file_system_model_iter_children (GtkTreeModel *tree_model,
- GtkTreeIter *iter,
- GtkTreeIter *parent)
-{
- return FALSE;
-}
-
-static gboolean
-gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model,
- GtkTreeIter *iter)
-{
- return FALSE;
-}
-
-static int
-gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model,
- GtkTreeIter *iter)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model);
-
- if (iter)
- return 0;
-
- return node_get_tree_row (model, model->files->len - 1) + 1;
-}
-
-static gboolean
-gtk_file_system_model_iter_parent (GtkTreeModel *tree_model,
- GtkTreeIter *iter,
- GtkTreeIter *child)
-{
- return FALSE;
-}
-
-static void
-gtk_file_system_model_ref_node (GtkTreeModel *tree_model,
- GtkTreeIter *iter)
+list_model_get_item_type (GListModel *list_model)
{
- /* nothing to do */
+ return G_TYPE_FILE_INFO;
}
-static void
-gtk_file_system_model_unref_node (GtkTreeModel *tree_model,
- GtkTreeIter *iter)
-{
- /* nothing to do */
-}
-
-static void
-gtk_file_system_model_iface_init (GtkTreeModelIface *iface)
-{
- iface->get_flags = gtk_file_system_model_get_flags;
- iface->get_n_columns = gtk_file_system_model_get_n_columns;
- iface->get_column_type = gtk_file_system_model_get_column_type;
- iface->get_iter = gtk_file_system_model_get_iter;
- iface->get_path = gtk_file_system_model_get_path;
- iface->get_value = gtk_file_system_model_get_value;
- iface->iter_next = gtk_file_system_model_iter_next;
- iface->iter_children = gtk_file_system_model_iter_children;
- iface->iter_has_child = gtk_file_system_model_iter_has_child;
- iface->iter_n_children = gtk_file_system_model_iter_n_children;
- iface->iter_nth_child = gtk_file_system_model_iter_nth_child;
- iface->iter_parent = gtk_file_system_model_iter_parent;
- iface->ref_node = gtk_file_system_model_ref_node;
- iface->unref_node = gtk_file_system_model_unref_node;
-}
-
-/*** GtkTreeSortable ***/
-
-typedef struct _SortData SortData;
-struct _SortData {
- GtkFileSystemModel * model;
- GtkTreeIterCompareFunc func;
- gpointer data;
- int order; /* -1 to invert sort order or 1 to keep it */
-};
-
-/* returns FALSE if no sort necessary */
-static gboolean
-sort_data_init (SortData *data, GtkFileSystemModel *model)
-{
- GtkTreeDataSortHeader *header;
-
- if (model->files->len <= 2)
- return FALSE;
-
- switch (model->sort_column_id)
- {
- case GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID:
- if (!model->default_sort_func)
- return FALSE;
- data->func = model->default_sort_func;
- data->data = model->default_sort_data;
- break;
- case GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID:
- return FALSE;
- default:
- header = _gtk_tree_data_list_get_header (model->sort_list, model->sort_column_id);
- if (header == NULL)
- return FALSE;
- data->func = header->func;
- data->data = header->data;
- break;
- }
-
- data->order = model->sort_order == GTK_SORT_DESCENDING ? -1 : 1;
- data->model = model;
- return TRUE;
-}
-
-static int
-compare_array_element (gconstpointer a, gconstpointer b, gpointer user_data)
-{
- SortData *data = user_data;
- GtkTreeIter itera, iterb;
-
- ITER_INIT_FROM_INDEX (data->model, &itera, node_index (data->model, a));
- ITER_INIT_FROM_INDEX (data->model, &iterb, node_index (data->model, b));
- return data->func (GTK_TREE_MODEL (data->model), &itera, &iterb, data->data) * data->order;
-}
-
-static void
-gtk_file_system_model_sort (GtkFileSystemModel *model)
-{
- SortData data;
-
- if (model->frozen)
- {
- model->sort_on_thaw = TRUE;
- return;
- }
-
- if (sort_data_init (&data, model))
- {
- GtkTreePath *path;
- guint i;
- guint r, n_visible_rows;
-
- node_validate_rows (model, G_MAXUINT, G_MAXUINT);
- n_visible_rows = node_get_tree_row (model, model->files->len - 1) + 1;
- model->n_nodes_valid = 0;
- g_hash_table_remove_all (model->file_lookup);
- g_qsort_with_data (get_node (model, 1), /* start at index 1; don't sort the editable row */
- model->files->len - 1,
- model->node_size,
- compare_array_element,
- &data);
- g_assert (model->n_nodes_valid == 0);
- g_assert (g_hash_table_size (model->file_lookup) == 0);
- if (n_visible_rows)
- {
- int *new_order = g_new (int, n_visible_rows);
-
- r = 0;
- for (i = 0; i < model->files->len; i++)
- {
- FileModelNode *node = get_node (model, i);
- if (!node->visible)
- {
- node->row = r;
- continue;
- }
-
- new_order[r] = node->row - 1;
- r++;
- node->row = r;
- }
- g_assert (r == n_visible_rows);
- path = gtk_tree_path_new ();
- gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model),
- path,
- NULL,
- new_order);
- gtk_tree_path_free (path);
- g_free (new_order);
- }
- }
-
- model->sort_on_thaw = FALSE;
-}
-
-static void
-gtk_file_system_model_sort_node (GtkFileSystemModel *model, guint node)
-{
- /* FIXME: improve */
- gtk_file_system_model_sort (model);
-}
-
-static gboolean
-gtk_file_system_model_get_sort_column_id (GtkTreeSortable *sortable,
- int *sort_column_id,
- GtkSortType *order)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
-
- if (sort_column_id)
- *sort_column_id = model->sort_column_id;
- if (order)
- *order = model->sort_order;
-
- if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID ||
- model->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
- return FALSE;
-
- return TRUE;
-}
-
-static void
-gtk_file_system_model_set_sort_column_id (GtkTreeSortable *sortable,
- int sort_column_id,
- GtkSortType order)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
-
- if ((model->sort_column_id == sort_column_id) &&
- (model->sort_order == order))
- return;
-
- if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
- {
- if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
- {
-#ifndef G_DISABLE_CHECKS
- GtkTreeDataSortHeader *header = NULL;
-
- header = _gtk_tree_data_list_get_header (model->sort_list,
- sort_column_id);
-
- /* We want to make sure that we have a function */
- g_return_if_fail (header != NULL);
- g_return_if_fail (header->func != NULL);
-#endif
- }
- else
- {
- g_return_if_fail (model->default_sort_func != NULL);
- }
- }
-
-
- model->sort_column_id = sort_column_id;
- model->sort_order = order;
-
- gtk_tree_sortable_sort_column_changed (sortable);
-
- gtk_file_system_model_sort (model);
-}
-
-static void
-gtk_file_system_model_set_sort_func (GtkTreeSortable *sortable,
- int sort_column_id,
- GtkTreeIterCompareFunc func,
- gpointer data,
- GDestroyNotify destroy)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
-
- model->sort_list = _gtk_tree_data_list_set_header (model->sort_list,
- sort_column_id,
- func, data, destroy);
-
- if (model->sort_column_id == sort_column_id)
- gtk_file_system_model_sort (model);
-}
-
-static void
-gtk_file_system_model_set_default_sort_func (GtkTreeSortable *sortable,
- GtkTreeIterCompareFunc func,
- gpointer data,
- GDestroyNotify destroy)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
-
- if (model->default_sort_destroy)
- {
- GDestroyNotify d = model->default_sort_destroy;
-
- model->default_sort_destroy = NULL;
- d (model->default_sort_data);
- }
-
- model->default_sort_func = func;
- model->default_sort_data = data;
- model->default_sort_destroy = destroy;
-
- if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID)
- gtk_file_system_model_sort (model);
-}
-
-static gboolean
-gtk_file_system_model_has_default_sort_func (GtkTreeSortable *sortable)
-{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable);
-
- return (model->default_sort_func != NULL);
-}
-
-static void
-gtk_file_system_model_sortable_init (GtkTreeSortableIface *iface)
-{
- iface->get_sort_column_id = gtk_file_system_model_get_sort_column_id;
- iface->set_sort_column_id = gtk_file_system_model_set_sort_column_id;
- iface->set_sort_func = gtk_file_system_model_set_sort_func;
- iface->set_default_sort_func = gtk_file_system_model_set_default_sort_func;
- iface->has_default_sort_func = gtk_file_system_model_has_default_sort_func;
-}
-
-/*** GtkTreeDragSource ***/
-
-static gboolean
-drag_source_row_draggable (GtkTreeDragSource *drag_source,
- GtkTreePath *path)
+static guint
+list_model_get_n_items (GListModel *list_model)
{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source);
- GtkTreeIter iter;
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (list_model);
- if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
- return FALSE;
-
- return ITER_INDEX (&iter) != 0;
+ return model->files->len - 1;
}
-static GdkContentProvider *
-drag_source_drag_data_get (GtkTreeDragSource *drag_source,
- GtkTreePath *path)
+static gpointer
+list_model_get_item (GListModel *list_model,
+ guint position)
{
- GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source);
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (list_model);
FileModelNode *node;
- GtkTreeIter iter;
- if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
+ /* The first items of GtkFileSystemModel is not really a file,
+ * so ignore it. */
+ if (position + 1 >= model->files->len)
return NULL;
- node = get_node (model, ITER_INDEX (&iter));
- if (node->file == NULL)
- return FALSE;
-
- return gdk_content_provider_new_typed (G_TYPE_FILE, node->file);
+ node = get_node (model, position + 1);
+ return g_object_ref (node->info);
}
static void
-drag_source_iface_init (GtkTreeDragSourceIface *iface)
+g_list_model_iface_init (GListModelInterface *iface)
{
- iface->row_draggable = drag_source_row_draggable;
- iface->drag_data_get = drag_source_drag_data_get;
- iface->drag_data_delete = NULL;
+ iface->get_item_type = list_model_get_item_type;
+ iface->get_n_items = list_model_get_n_items;
+ iface->get_item = list_model_get_item;
}
/*** GtkFileSystemModel ***/
@@ -960,12 +411,8 @@ static guint file_system_model_signals[LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
- gtk_file_system_model_iface_init)
- G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
- gtk_file_system_model_sortable_init)
- G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
- drag_source_iface_init))
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
+ g_list_model_iface_init))
static void
gtk_file_system_model_dispose (GObject *object)
@@ -994,35 +441,18 @@ gtk_file_system_model_finalize (GObject *object)
for (i = 0; i < model->files->len; i++)
{
- int v;
-
FileModelNode *node = get_node (model, i);
- if (node->file)
- g_object_unref (node->file);
- if (node->info)
- g_object_unref (node->info);
-
- for (v = 0; v < model->n_columns; v++)
- if (G_VALUE_TYPE (&node->values[v]) != G_TYPE_INVALID)
- g_value_unset (&node->values[v]);
+ g_clear_object (&node->file);
+ g_clear_object (&node->info);
}
g_array_free (model->files, TRUE);
- g_object_unref (model->cancellable);
- g_free (model->attributes);
- if (model->dir)
- g_object_unref (model->dir);
- if (model->dir_monitor)
- g_object_unref (model->dir_monitor);
- g_hash_table_destroy (model->file_lookup);
- if (model->filter)
- g_object_unref (model->filter);
-
- g_slice_free1 (sizeof (GType) * model->n_columns, model->column_types);
-
- _gtk_tree_data_list_header_free (model->sort_list);
- if (model->default_sort_destroy)
- model->default_sort_destroy (model->default_sort_data);
+ g_clear_object (&model->cancellable);
+ g_clear_pointer (&model->attributes, g_free);
+ g_clear_object (&model->dir);
+ g_clear_object (&model->dir_monitor);
+ g_clear_pointer (&model->file_lookup, g_hash_table_destroy);
+ g_clear_object (&model->filter);
G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object);
}
@@ -1053,8 +483,6 @@ _gtk_file_system_model_init (GtkFileSystemModel *model)
model->show_hidden = FALSE;
model->filter_folders = FALSE;
- model->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
-
model->file_lookup = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
model->cancellable = g_cancellable_new ();
}
@@ -1160,26 +588,19 @@ query_done_helper (GObject * object,
gpointer data,
gboolean do_thaw_updates)
{
- GtkFileSystemModel *model;
+ GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (data);
GFile *file = G_FILE (object);
GFileInfo *info;
- guint id;
info = g_file_query_info_finish (file, res, NULL);
- if (info == NULL)
- return;
-
- model = GTK_FILE_SYSTEM_MODEL (data);
-
- _gtk_file_system_model_update_file (model, file, info);
-
- id = node_get_for_file (model, file);
- gtk_file_system_model_sort_node (model, id);
+ if (info)
+ {
+ _gtk_file_system_model_update_file (model, file, info);
+ g_object_unref (info);
+ }
if (do_thaw_updates)
thaw_updates (model);
-
- g_object_unref (info);
}
static void
@@ -1266,41 +687,6 @@ gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer
}
static void
-gtk_file_system_model_set_n_columns (GtkFileSystemModel *model,
- int n_columns,
- va_list args)
-{
- guint i;
-
- g_assert (model->files == NULL);
- g_assert (n_columns > 0);
-
- model->n_columns = n_columns;
- model->column_types = g_slice_alloc (sizeof (GType) * n_columns);
-
- model->node_size = sizeof (FileModelNode) + sizeof (GValue) * (n_columns - 1); /* minus 1 because FileModelNode.values[] has a default size of 1 */
-
- for (i = 0; i < (guint) n_columns; i++)
- {
- GType type = va_arg (args, GType);
- if (! _gtk_tree_data_list_check_type (type))
- {
- g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", G_STRLOC, g_type_name (type));
- return; /* not reached */
- }
-
- model->column_types[i] = type;
- }
-
- model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types);
-
- model->files = g_array_sized_new (FALSE, FALSE, model->node_size, FILES_PER_QUERY);
- /* add editable node at start */
- g_array_set_size (model->files, 1);
- memset (get_node (model, 0), 0, model->node_size);
-}
-
-static void
gtk_file_system_model_set_directory (GtkFileSystemModel *model,
GFile * dir,
const char * attributes)
@@ -1320,29 +706,8 @@ gtk_file_system_model_set_directory (GtkFileSystemModel *model,
}
-static GtkFileSystemModel *
-_gtk_file_system_model_new_valist (GtkFileSystemModelGetValue get_func,
- gpointer get_data,
- guint n_columns,
- va_list args)
-{
- GtkFileSystemModel *model;
-
- model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
- model->get_func = get_func;
- model->get_data = get_data;
-
- gtk_file_system_model_set_n_columns (model, n_columns, args);
-
- return model;
-}
-
/**
* _gtk_file_system_model_new:
- * @get_func: function to call for getting a value
- * @get_data: user data argument passed to @get_func
- * @n_columns: number of columns
- * @...: @n_columns `GType` types for the columns
*
* Creates a new `GtkFileSystemModel` object. You need to add files
* to the list using _gtk_file_system_model_add_and_query_file()
@@ -1351,20 +716,16 @@ _gtk_file_system_model_new_valist (GtkFileSystemModelGetValue get_func,
* Returns: the newly created `GtkFileSystemModel`
**/
GtkFileSystemModel *
-_gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
- gpointer get_data,
- guint n_columns,
- ...)
+_gtk_file_system_model_new (void)
{
GtkFileSystemModel *model;
- va_list args;
- g_return_val_if_fail (get_func != NULL, NULL);
- g_return_val_if_fail (n_columns > 0, NULL);
+ model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL);
- va_start (args, n_columns);
- model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
- va_end (args);
+ model->files = g_array_sized_new (FALSE, FALSE, sizeof (FileModelNode), FILES_PER_QUERY);
+ /* add editable node at start */
+ g_array_set_size (model->files, 1);
+ memset (get_node (model, 0), 0, sizeof (FileModelNode));
return model;
}
@@ -1373,10 +734,6 @@ _gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
* _gtk_file_system_model_new_for_directory:
* @directory: the directory to show.
* @attributes: (nullable): attributes to immediately load or %NULL for all
- * @get_func: function that the model should call to query data about a file
- * @get_data: user data to pass to the @get_func
- * @n_columns: number of columns
- * @...: @n_columns `GType` types for the columns
*
* Creates a new `GtkFileSystemModel` object.
*
@@ -1389,24 +746,14 @@ _gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
* Returns: the newly created `GtkFileSystemModel`
**/
GtkFileSystemModel *
-_gtk_file_system_model_new_for_directory (GFile * dir,
- const char * attributes,
- GtkFileSystemModelGetValue get_func,
- gpointer get_data,
- guint n_columns,
- ...)
+_gtk_file_system_model_new_for_directory (GFile *dir,
+ const char *attributes)
{
GtkFileSystemModel *model;
- va_list args;
g_return_val_if_fail (G_IS_FILE (dir), NULL);
- g_return_val_if_fail (get_func != NULL, NULL);
- g_return_val_if_fail (n_columns > 0, NULL);
-
- va_start (args, n_columns);
- model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args);
- va_end (args);
+ model = _gtk_file_system_model_new ();
gtk_file_system_model_set_directory (model, dir, attributes);
return model;
@@ -1545,157 +892,6 @@ _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model)
return model->cancellable;
}
-/**
- * _gtk_file_system_model_iter_is_visible:
- * @model: the model
- * @iter: a valid iterator
- *
- * Checks if the iterator is visible. A visible iterator references
- * a row that is currently exposed using the `GtkTreeModel` API.
- *
- * If the iterator is invisible, it references a file that is not shown
- * for some reason, such as being filtered out by the current filter or
- * being a hidden file.
- *
- * Returns: %TRUE if the iterator is visible
- **/
-gboolean
-_gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model,
- GtkTreeIter *iter)
-{
- FileModelNode *node;
-
- g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE);
- g_return_val_if_fail (iter != NULL, FALSE);
-
- node = get_node (model, ITER_INDEX (iter));
- return node->visible;
-}
-
-/**
- * _gtk_file_system_model_iter_is_filtered_out:
- * @model: the model
- * @iter: a valid iterator
- *
- * Checks if the iterator is filtered out. This is only useful for rows
- * that refer to folders, as those are always visible regardless
- * of what the current filter says. This function lets you see
- * the results of the filter.
- *
- * Returns: %TRUE if the iterator passed the current filter; %FALSE if the
- * filter would not have let the row pass.
- **/
-gboolean
-_gtk_file_system_model_iter_is_filtered_out (GtkFileSystemModel *model,
- GtkTreeIter *iter)
-{
- FileModelNode *node;
-
- g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE);
- g_return_val_if_fail (iter != NULL, FALSE);
-
- node = get_node (model, ITER_INDEX (iter));
- return node->filtered_out;
-}
-
-/**
- * _gtk_file_system_model_get_info:
- * @model: a `GtkFileSystemModel`
- * @iter: a `GtkTreeIter` pointing to a row of @model
- *
- * Gets the `GFileInfo` for a particular row
- * of @model.
- *
- * Returns: a `GFileInfo`. This value is owned by @model and must not
- * be modified or freed. If you want to keep the information for
- * later use, you must take a reference, since the `GFileInfo` may
- * be freed on later changes to the file system.
- */
-GFileInfo *
-_gtk_file_system_model_get_info (GtkFileSystemModel *model,
- GtkTreeIter *iter)
-{
- FileModelNode *node;
-
- g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
- g_return_val_if_fail (iter != NULL, NULL);
-
- node = get_node (model, ITER_INDEX (iter));
- g_assert (node->info == NULL || G_IS_FILE_INFO (node->info));
- return node->info;
-}
-
-/**
- * _gtk_file_system_model_get_file:
- * @model: a `GtkFileSystemModel`
- * @iter: a `GtkTreeIter` pointing to a row of @model
- *
- * Gets the file for a particular row in @model.
- *
- * Returns: the file. This object is owned by @model and
- * or freed. If you want to save the path for later use,
- * you must take a ref, since the object may be freed
- * on later changes to the file system.
- **/
-GFile *
-_gtk_file_system_model_get_file (GtkFileSystemModel *model,
- GtkTreeIter *iter)
-{
- FileModelNode *node;
-
- g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
-
- node = get_node (model, ITER_INDEX (iter));
- return node->file;
-}
-
-/**
- * _gtk_file_system_model_get_value:
- * @model: a `GtkFileSystemModel`
- * @iter: a `GtkTreeIter` pointing to a row of @model
- * @column: the column to get the value for
- *
- * Gets the value associated with the given row @iter and @column.
- * If no value is available yet and the default value should be used,
- * %NULL is returned.
- * This is a performance optimization for the calls
- * gtk_tree_model_get() or gtk_tree_model_get_value(), which copy
- * the value and spend a considerable amount of time in iterator
- * lookups. Both of which are slow.
- *
- * Returns: a pointer to the actual value as stored in @model or %NULL
- * if no value available yet
- */
-const GValue *
-_gtk_file_system_model_get_value (GtkFileSystemModel *model,
- GtkTreeIter * iter,
- int column)
-{
- FileModelNode *node;
-
- g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL);
- g_return_val_if_fail (column >= 0 && (guint) column < model->n_columns, NULL);
-
- node = get_node (model, ITER_INDEX (iter));
-
- if (!G_VALUE_TYPE (&node->values[column]))
- {
- g_value_init (&node->values[column], model->column_types[column]);
- if (!model->get_func (model,
- node->file,
- node->info,
- column,
- &node->values[column],
- model->get_data))
- {
- g_value_unset (&node->values[column]);
- return NULL;
- }
- }
-
- return &node->values[column];
-}
-
static guint
node_get_for_file (GtkFileSystemModel *model,
GFile * file)
@@ -1709,8 +905,7 @@ node_get_for_file (GtkFileSystemModel *model,
/* Node 0 is the editable row and has no associated file or entry in the table, so we start counting from 1.
*
* The invariant here is that the files in model->files[n] for n < g_hash_table_size (model->file_lookup)
- * are already added to the hash table. The table can get cleared when we re-sort; this loop merely rebuilds
- * our (file -> index) mapping on demand.
+ * are already added to the hash table. this loop merely rebuilds our (file -> index) mapping on demand.
*
* If we exit the loop, the next pending batch of mappings will be resolved when this function gets called again
* with another file that is not yet in the mapping.
@@ -1727,38 +922,23 @@ node_get_for_file (GtkFileSystemModel *model,
return 0;
}
-/**
- * _gtk_file_system_model_get_iter_for_file:
- * @model: the model
- * @iter: the iterator to be initialized
- * @file: the file to look up
- *
- * Initializes @iter to point to the row used for @file, if @file is part
- * of the model. Note that upon successful return, @iter may point to an
- * invisible row in the @model. Use
- * _gtk_file_system_model_iter_is_visible() to make sure it is visible to
- * the tree view.
- *
- * Returns: %TRUE if file is part of the model and @iter was initialized
- **/
-gboolean
-_gtk_file_system_model_get_iter_for_file (GtkFileSystemModel *model,
- GtkTreeIter *iter,
- GFile * file)
+GFileInfo *
+_gtk_file_system_model_get_info_for_file (GtkFileSystemModel *model,
+ GFile *file)
{
+ FileModelNode *node;
guint i;
g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE);
- g_return_val_if_fail (iter != NULL, FALSE);
g_return_val_if_fail (G_IS_FILE (file), FALSE);
i = node_get_for_file (model, file);
if (i == 0)
- return FALSE;
+ return NULL;
- ITER_INIT_FROM_INDEX (model, iter, i);
- return TRUE;
+ node = get_node (model, i);
+ return node->info;
}
/* When an element is added or removed to the model->files array, we need to
@@ -1803,24 +983,31 @@ add_file (GtkFileSystemModel *model,
GFileInfo *info)
{
FileModelNode *node;
+ guint position;
g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
g_return_if_fail (G_IS_FILE (file));
g_return_if_fail (G_IS_FILE_INFO (info));
- node = g_slice_alloc0 (model->node_size);
+ node = g_slice_alloc0 (sizeof (FileModelNode));
node->file = g_object_ref (file);
if (info)
- node->info = g_object_ref (info);
+ {
+ g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
+ node->info = g_object_ref (info);
+ }
node->frozen_add = model->frozen ? TRUE : FALSE;
g_array_append_vals (model->files, node, 1);
- g_slice_free1 (model->node_size, node);
+ g_slice_free1 (sizeof (FileModelNode), node);
+
+ position = model->files->len - 1;
if (!model->frozen)
node_compute_visibility_and_filters (model, model->files->len -1);
- gtk_file_system_model_sort_node (model, model->files->len -1);
+ /* Ignore the first item */
+ g_list_model_items_changed (G_LIST_MODEL (model), position - 1, 0, 1);
}
/**
@@ -1837,9 +1024,7 @@ remove_file (GtkFileSystemModel *model,
GFile *file)
{
FileModelNode *node;
- gboolean was_visible;
guint id;
- guint row;
g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
g_return_if_fail (G_IS_FILE (file));
@@ -1849,8 +1034,6 @@ remove_file (GtkFileSystemModel *model,
return;
node = get_node (model, id);
- was_visible = node->visible;
- row = node_get_tree_row (model, id);
node_invalidate_index (model, id);
@@ -1863,10 +1046,7 @@ remove_file (GtkFileSystemModel *model,
g_array_remove_index (model->files, id);
- /* We don't need to resort, as removing a row doesn't change the sorting order of the other rows */
-
- if (was_visible)
- emit_row_deleted_for_row (model, row);
+ g_list_model_items_changed (G_LIST_MODEL (model), id - 1, 1, 0);
}
/**
@@ -1885,8 +1065,7 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model,
GFileInfo *info)
{
FileModelNode *node;
- guint i, id;
- GFileInfo *old_info;
+ guint id;
g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
g_return_if_fail (G_IS_FILE (file));
@@ -1901,19 +1080,9 @@ _gtk_file_system_model_update_file (GtkFileSystemModel *model,
node = get_node (model, id);
- old_info = node->info;
- node->info = g_object_ref (info);
- if (old_info)
- g_object_unref (old_info);
-
- for (i = 0; i < model->n_columns; i++)
- {
- if (G_VALUE_TYPE (&node->values[i]))
- g_value_unset (&node->values[i]);
- }
+ g_set_object (&node->info, info);
- if (node->visible)
- emit_row_changed_for_node (model, id);
+ g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
}
void
@@ -2002,8 +1171,6 @@ thaw_updates (GtkFileSystemModel *model)
if (model->filter_on_thaw)
gtk_file_system_model_refilter_all (model);
- if (model->sort_on_thaw)
- gtk_file_system_model_sort (model);
if (stuff_added)
{
guint i;
@@ -2021,59 +1188,6 @@ thaw_updates (GtkFileSystemModel *model)
}
/**
- * _gtk_file_system_model_clear_cache:
- * @model: a `GtkFileSystemModel`
- * @column: the column to clear or -1 for all columns
- *
- * Clears the cached values in the model for the given @column. Use
- * this function whenever your get_value function would return different
- * values for a column.
- * The file chooser uses this for example when the icon theme changes to
- * invalidate the cached pixbufs.
- **/
-void
-_gtk_file_system_model_clear_cache (GtkFileSystemModel *model,
- int column)
-{
- guint i;
- int start, end;
- gboolean changed;
-
- g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model));
- g_return_if_fail (column >= -1 && (guint) column < model->n_columns);
-
- if (column > -1)
- {
- start = column;
- end = column + 1;
- }
- else
- {
- start = 0;
- end = model->n_columns;
- }
-
- for (i = 0; i < model->files->len; i++)
- {
- FileModelNode *node = get_node (model, i);
- changed = FALSE;
- for (column = start; column < end; column++)
- {
- if (!G_VALUE_TYPE (&node->values[column]))
- continue;
-
- g_value_unset (&node->values[column]);
- changed = TRUE;
- }
-
- if (changed && node->visible)
- emit_row_changed_for_node (model, i);
- }
-
- /* FIXME: resort? */
-}
-
-/**
* _gtk_file_system_model_add_and_query_file:
* @model: a `GtkFileSystemModel`
* @file: the file to add
diff --git a/gtk/gtkfilesystemmodel.h b/gtk/gtkfilesystemmodel.h
index 05383a7328..57cbf4d68f 100644
--- a/gtk/gtkfilesystemmodel.h
+++ b/gtk/gtkfilesystemmodel.h
@@ -21,7 +21,6 @@
#include <gio/gio.h>
#include <gtk/gtkfilefilter.h>
-#include <gtk/deprecated/gtktreemodel.h>
G_BEGIN_DECLS
@@ -33,39 +32,13 @@ typedef struct _GtkFileSystemModel GtkFileSystemModel;
GType _gtk_file_system_model_get_type (void) G_GNUC_CONST;
-typedef gboolean (*GtkFileSystemModelGetValue) (GtkFileSystemModel *model,
- GFile *file,
- GFileInfo *info,
- int column,
- GValue *value,
- gpointer user_data);
-
-GtkFileSystemModel *_gtk_file_system_model_new (GtkFileSystemModelGetValue get_func,
- gpointer get_data,
- guint n_columns,
- ...);
-GtkFileSystemModel *_gtk_file_system_model_new_for_directory(GFile * dir,
- const char * attributes,
- GtkFileSystemModelGetValue get_func,
- gpointer get_data,
- guint n_columns,
- ...);
+GtkFileSystemModel *_gtk_file_system_model_new (void);
+GtkFileSystemModel *_gtk_file_system_model_new_for_directory(GFile *dir,
+ const char *attributes);
GFile * _gtk_file_system_model_get_directory (GtkFileSystemModel *model);
GCancellable * _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model);
-gboolean _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model,
- GtkTreeIter *iter);
-gboolean _gtk_file_system_model_iter_is_filtered_out (GtkFileSystemModel *model,
- GtkTreeIter *iter);
-GFileInfo * _gtk_file_system_model_get_info (GtkFileSystemModel *model,
- GtkTreeIter *iter);
-gboolean _gtk_file_system_model_get_iter_for_file(GtkFileSystemModel *model,
- GtkTreeIter *iter,
+GFileInfo * _gtk_file_system_model_get_info_for_file(GtkFileSystemModel *model,
GFile *file);
-GFile * _gtk_file_system_model_get_file (GtkFileSystemModel *model,
- GtkTreeIter *iter);
-const GValue * _gtk_file_system_model_get_value (GtkFileSystemModel *model,
- GtkTreeIter * iter,
- int column);
void _gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model,
GFile *file,
@@ -88,8 +61,6 @@ void _gtk_file_system_model_set_show_files (GtkFileSystemModel
gboolean show_files);
void _gtk_file_system_model_set_filter_folders (GtkFileSystemModel *model,
gboolean show_folders);
-void _gtk_file_system_model_clear_cache (GtkFileSystemModel *model,
- int column);
void _gtk_file_system_model_set_filter (GtkFileSystemModel *model,
GtkFileFilter *filter);
diff --git a/gtk/gtkfilethumbnail.c b/gtk/gtkfilethumbnail.c
new file mode 100644
index 0000000000..d8a5171119
--- /dev/null
+++ b/gtk/gtkfilethumbnail.c
@@ -0,0 +1,255 @@
+/* gtkfilethumbnail.c
+ *
+ * Copyright 2022 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include "gtkfilethumbnail.h"
+
+#include "gtkbinlayout.h"
+#include "gtkfilechooserutils.h"
+#include "gtkimage.h"
+#include "gtkprivate.h"
+#include "gtkwidget.h"
+
+#define ICON_SIZE 16
+
+struct _GtkFileThumbnail
+{
+ GtkWidget parent;
+
+ GtkWidget *image;
+
+ GCancellable *cancellable;
+ GFileInfo *info;
+};
+
+typedef struct
+{
+ GtkWidgetClass parent;
+} GtkFileThumbnailClass;
+
+G_DEFINE_FINAL_TYPE (GtkFileThumbnail, _gtk_file_thumbnail, GTK_TYPE_WIDGET)
+
+enum {
+ PROP_0,
+ PROP_INFO,
+ N_PROPS,
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+copy_attribute (GFileInfo *to,
+ GFileInfo *from,
+ const char *attribute)
+{
+ GFileAttributeType type;
+ gpointer value;
+
+ if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
+ g_file_info_set_attribute (to, attribute, type, value);
+}
+
+static gboolean
+update_image (GtkFileThumbnail *self)
+{
+ GtkIconTheme *icon_theme;
+ GIcon *icon;
+ int scale;
+
+ if (!g_file_info_has_attribute (self->info, G_FILE_ATTRIBUTE_STANDARD_ICON))
+ return FALSE;
+
+ scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (self)));
+
+ icon = _gtk_file_info_get_icon (self->info, ICON_SIZE, scale, icon_theme);
+
+ gtk_image_set_from_gicon (GTK_IMAGE (self->image), icon);
+
+ g_object_unref (icon);
+
+ return TRUE;
+
+}
+
+static void
+thumbnail_queried_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkFileThumbnail *self = user_data; /* might be unreffed if operation was cancelled */
+ GFile *file = G_FILE (object);
+ GFileInfo *queried;
+
+ queried = g_file_query_info_finish (file, result, NULL);
+ if (queried == NULL)
+ return;
+
+ copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
+ copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
+ copy_attribute (self->info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
+
+ update_image (self);
+
+ g_clear_object (&queried);
+
+ g_clear_object (&self->cancellable);
+}
+
+static void
+cancel_thumbnail (GtkFileThumbnail *self)
+{
+ g_cancellable_cancel (self->cancellable);
+ g_clear_object (&self->cancellable);
+}
+
+static void
+get_thumbnail (GtkFileThumbnail *self)
+{
+ if (!self->info)
+ return;
+
+ if (!update_image (self))
+ {
+ GFile *file;
+
+ if (g_file_info_has_attribute (self->info, "filechooser::queried"))
+ return;
+
+ g_assert (self->cancellable == NULL);
+ self->cancellable = g_cancellable_new ();
+
+ file = _gtk_file_info_get_file (self->info);
+ g_file_info_set_attribute_boolean (self->info, "filechooser::queried", TRUE);
+ g_file_query_info_async (file,
+ G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
+ G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
+ G_FILE_ATTRIBUTE_STANDARD_ICON,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ self->cancellable,
+ thumbnail_queried_cb,
+ self);
+ }
+}
+
+static void
+_gtk_file_thumbnail_dispose (GObject *object)
+{
+ GtkFileThumbnail *self = (GtkFileThumbnail *)object;
+
+ _gtk_file_thumbnail_set_info (self, NULL);
+
+ g_clear_pointer (&self->image, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (_gtk_file_thumbnail_parent_class)->dispose (object);
+}
+
+static void
+_gtk_file_thumbnail_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileThumbnail *self = GTK_FILE_THUMBNAIL (object);
+
+ switch (prop_id)
+ {
+ case PROP_INFO:
+ g_value_set_object (value, self->info);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+_gtk_file_thumbnail_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkFileThumbnail *self = GTK_FILE_THUMBNAIL (object);
+
+ switch (prop_id)
+ {
+ case PROP_INFO:
+ _gtk_file_thumbnail_set_info (self, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+_gtk_file_thumbnail_class_init (GtkFileThumbnailClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->dispose = _gtk_file_thumbnail_dispose;
+ object_class->get_property = _gtk_file_thumbnail_get_property;
+ object_class->set_property = _gtk_file_thumbnail_set_property;
+
+ properties[PROP_INFO] =
+ g_param_spec_object ("file-info", NULL, NULL,
+ G_TYPE_FILE_INFO,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPS, properties);
+
+ gtk_widget_class_set_css_name (widget_class, I_("filethumbnail"));
+
+ gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
+}
+
+static void
+_gtk_file_thumbnail_init (GtkFileThumbnail *self)
+{
+ self->image = gtk_image_new ();
+ gtk_widget_set_parent (self->image, GTK_WIDGET (self));
+}
+
+GFileInfo *
+_gtk_file_thumbnail_get_info (GtkFileThumbnail *self)
+{
+ g_assert (GTK_IS_FILE_THUMBNAIL (self));
+
+ return self->info;
+}
+
+void
+_gtk_file_thumbnail_set_info (GtkFileThumbnail *self,
+ GFileInfo *info)
+{
+ g_assert (GTK_IS_FILE_THUMBNAIL (self));
+ g_assert (info == NULL || G_IS_FILE_INFO (info));
+
+ if (g_set_object (&self->info, info))
+ {
+ cancel_thumbnail (self);
+ get_thumbnail (self);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INFO]);
+ }
+}
+
diff --git a/gtk/gtkfilethumbnail.h b/gtk/gtkfilethumbnail.h
new file mode 100644
index 0000000000..c9c5741d3f
--- /dev/null
+++ b/gtk/gtkfilethumbnail.h
@@ -0,0 +1,46 @@
+/* gtkfilethumbnail.h
+ *
+ * Copyright 2022 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+
+#ifndef __GTK_FILE_THUMBNAIL_H__
+#define __GTK_FILE_THUMBNAIL_H__
+
+#include <gio/gio.h>
+
+#include "gtkfilesystemmodel.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_FILE_THUMBNAIL (_gtk_file_thumbnail_get_type ())
+#define GTK_FILE_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_THUMBNAIL, GtkFileThumbnail))
+#define GTK_IS_FILE_THUMBNAIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_THUMBNAIL))
+
+typedef struct _GtkFileThumbnail GtkFileThumbnail;
+
+GType _gtk_file_thumbnail_get_type (void) G_GNUC_CONST;
+
+GFileInfo *_gtk_file_thumbnail_get_info (GtkFileThumbnail *self);
+void _gtk_file_thumbnail_set_info (GtkFileThumbnail *self,
+ GFileInfo *info);
+
+G_END_DECLS
+
+#endif /* __GTK_FILE_THUMBNAIL_H__ */
+
diff --git a/gtk/gtksearchenginemodel.c b/gtk/gtksearchenginemodel.c
index 3923ea6813..6d174b1ed0 100644
--- a/gtk/gtksearchenginemodel.c
+++ b/gtk/gtksearchenginemodel.c
@@ -24,6 +24,7 @@
#include <gdk/gdk.h>
#include "gtksearchenginemodelprivate.h"
+#include "gtkfilechooserutils.h"
#include "gtkprivate.h"
#include <string.h>
@@ -81,37 +82,33 @@ static gboolean
do_search (gpointer data)
{
GtkSearchEngineModel *model = data;
- GtkTreeIter iter;
GList *hits = NULL;
gboolean got_results = FALSE;
- if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model->model), &iter))
+ for (guint i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model->model)); i++)
{
- do
- {
- GFileInfo *info;
-
- info = _gtk_file_system_model_get_info (model->model, &iter);
- if (info_matches_query (model->query, info))
- {
- GFile *file;
- GtkSearchHit *hit;
-
- file = _gtk_file_system_model_get_file (model->model, &iter);
- hit = g_new (GtkSearchHit, 1);
- hit->file = g_object_ref (file);
- hit->info = g_object_ref (info);
- hits = g_list_prepend (hits, hit);
- }
- }
- while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model->model), &iter));
+ GFileInfo *info = g_list_model_get_item (G_LIST_MODEL (model->model), i);
- if (hits)
+ if (info_matches_query (model->query, info))
{
- _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (model), hits);
- g_list_free_full (hits, (GDestroyNotify)_gtk_search_hit_free);
- got_results = TRUE;
+ GFile *file;
+ GtkSearchHit *hit;
+
+ file = _gtk_file_info_get_file (info);
+ hit = g_new (GtkSearchHit, 1);
+ hit->file = g_object_ref (file);
+ hit->info = g_object_ref (info);
+ hits = g_list_prepend (hits, hit);
}
+
+ g_clear_object (&info);
+ }
+
+ if (hits)
+ {
+ _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (model), hits);
+ g_list_free_full (hits, (GDestroyNotify)_gtk_search_hit_free);
+ got_results = TRUE;
}
model->idle = 0;
diff --git a/gtk/gtksearchenginetracker3.c b/gtk/gtksearchenginetracker3.c
index 6918983941..5ecb2ca211 100644
--- a/gtk/gtksearchenginetracker3.c
+++ b/gtk/gtksearchenginetracker3.c
@@ -114,7 +114,8 @@ free_hit (gpointer data)
}
static GFileInfo *
-create_file_info (TrackerSparqlCursor *cursor)
+create_file_info (GFile *file,
+ TrackerSparqlCursor *cursor)
{
GFileInfo *info;
const char *str;
@@ -140,6 +141,10 @@ create_file_info (TrackerSparqlCursor *cursor)
g_date_time_unref (creation);
}
+ g_file_info_set_attribute_object (info, "standard::file", G_OBJECT (file));
+ g_file_info_set_attribute_boolean (info, "filechooser::filtered-out", FALSE);
+ g_file_info_set_attribute_boolean (info, "filechooser::visible", TRUE);
+
return info;
}
@@ -175,7 +180,7 @@ query_callback (TrackerSparqlStatement *statement,
url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
hit = g_slice_new0 (GtkSearchHit);
hit->file = g_file_new_for_uri (url);
- hit->info = create_file_info (cursor);
+ hit->info = create_file_info (hit->file, cursor);
hits = g_list_prepend (hits, hit);
}
diff --git a/gtk/meson.build b/gtk/meson.build
index 5ce0ae81e3..d7357d8576 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -108,7 +108,9 @@ gtk_private_sources = files([
'gtkfilechoosererrorstack.c',
'gtkfilechoosernativeportal.c',
'gtkfilechooserutils.c',
+ 'gtkfilechoosercell.c',
'gtkfilesystemmodel.c',
+ 'gtkfilethumbnail.c',
'gtkgizmo.c',
'gtkiconcache.c',
'gtkiconcachevalidator.c',
diff --git a/gtk/theme/Default/_common.scss b/gtk/theme/Default/_common.scss
index af11adc527..b6f2f39e14 100644
--- a/gtk/theme/Default/_common.scss
+++ b/gtk/theme/Default/_common.scss
@@ -3369,6 +3369,30 @@ columnview row:not(:selected) cell editablelabel.editing text selection {
}
}
+/********************************************************
+ * Complex Lists *
+ * Put padding on the cell content so event controllers *
+ * can cover the whole area. *
+ ********************************************************/
+
+columnview.complex {
+ > listview > row > cell {
+ padding: 0;
+ > * {
+ padding: 8px 6px;
+ }
+ }
+
+ // shrink vertically for .data-table
+ &.data-table > listview > row > cell {
+ padding: 0;
+ > * {
+ padding-top: 2px;
+ padding-bottom: 2px;
+ }
+ }
+}
+
/*********************
* App Notifications *
*********************/
diff --git a/gtk/ui/gtkfilechooserwidget.ui b/gtk/ui/gtkfilechooserwidget.ui
index 22cd76a881..4a55e83d9c 100644
--- a/gtk/ui/gtkfilechooserwidget.ui
+++ b/gtk/ui/gtkfilechooserwidget.ui
@@ -140,103 +140,278 @@
<property name="hscrollbar-policy">2</property>
<property name="vexpand">1</property>
<child>
- <object class="GtkTreeView" id="browse_files_tree_view">
- <property name="has-tooltip">1</property>
- <property name="enable-search">0</property>
+ <object class="GtkColumnView" id="browse_files_column_view">
+ <style>
+ <class name="complex"/>
+ </style>
+ <signal name="activate" handler="column_view_row_activated_cb" swapped="no"/>
+ <signal name="keynav-failed" handler="browse_files_column_view_keynav_failed_cb"/>
<child>
- <object class="GtkGestureLongPress">
- <property name="touch-only">1</property>
- <signal name="pressed" handler="long_press_cb" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkGestureClick">
- <property name="button">3</property>
- <signal name="pressed" handler="click_cb" swapped="no"/>
- </object>
- </child>
- <signal name="query-tooltip" handler="file_list_query_tooltip_cb" swapped="no"/>
- <signal name="row-activated" handler="list_row_activated" swapped="no"/>
- <signal name="keynav-failed" handler="browse_files_tree_view_keynav_failed_cb"/>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection2">
- <signal name="changed" handler="list_selection_changed" swapped="no"/>
- </object>
- </child>
- <child>
- <object class="GtkTreeViewColumn" id="list_name_column">
+ <object class="GtkColumnViewColumn" id="column_view_name_column">
<property name="title" translatable="yes">Name</property>
- <property name="resizable">1</property>
<property name="expand">1</property>
- <child>
- <object class="GtkCellRendererPixbuf" id="list_pixbuf_renderer">
- <property name="xpad">6</property>
- </object>
- </child>
- <child>
- <object class="GtkCellRendererText" id="list_name_renderer">
- <property name="width-chars">10</property>
- <property name="ellipsize">3</property>
+ <property name="resizable">1</property>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkFileChooserCell">
+ <binding name="position">
+ <lookup name="position">GtkListItem</lookup>
+ </binding>
+ <binding name="item">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ <binding name="selected">
+ <lookup name="selected">GtkListItem</lookup>
+ </binding>
+ <child>
+ <object class="GtkBox">
+ <binding name="tooltip-text">
+ <closure type="gchararray" function="column_view_get_tooltip_text">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ <child>
+ <object class="GtkFileThumbnail">
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <binding name="file-info">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkInscription">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="min-chars">10</property>
+ <binding name="text">
+ <closure type="gchararray" function="column_view_get_file_display_name">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+ ]]></property>
</object>
- </child>
+ </property>
</object>
</child>
+
<child>
- <object class="GtkTreeViewColumn" id="list_location_column">
+ <object class="GtkColumnViewColumn" id="column_view_location_column">
<property name="title" translatable="yes">Location</property>
<property name="resizable">1</property>
<property name="visible">0</property>
<property name="expand">1</property>
- <child>
- <object class="GtkCellRendererText" id="list_location_renderer">
- <property name="xalign">0</property>
- <property name="width-chars">10</property>
- <property name="ellipsize">1</property>
- <property name="xpad">6</property>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkFileChooserCell">
+ <binding name="position">
+ <lookup name="position">GtkListItem</lookup>
+ </binding>
+ <binding name="item">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ <binding name="selected">
+ <lookup name="selected">GtkListItem</lookup>
+ </binding>
+ <child>
+ <object class="GtkInscription">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <property name="min-chars">10</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
+ <binding name="text">
+ <closure type="gchararray" function="column_view_get_location">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ <binding name="tooltip-text">
+ <closure type="gchararray" function="column_view_get_tooltip_text">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+ ]]></property>
</object>
- </child>
+ </property>
</object>
</child>
+
<child>
- <object class="GtkTreeViewColumn" id="list_size_column">
+ <object class="GtkColumnViewColumn" id="column_view_size_column">
<property name="title" translatable="yes">Size</property>
- <property name="sizing">2</property>
- <child>
- <object class="GtkCellRendererText" id="list_size_renderer">
- <property name="xalign">0</property>
- <property name="xpad">6</property>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkFileChooserCell">
+ <binding name="position">
+ <lookup name="position">GtkListItem</lookup>
+ </binding>
+ <binding name="item">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ <binding name="selected">
+ <lookup name="selected">GtkListItem</lookup>
+ </binding>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <binding name="label">
+ <closure type="gchararray" function="column_view_get_size">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ <binding name="tooltip-text">
+ <closure type="gchararray" function="column_view_get_tooltip_text">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+ ]]></property>
</object>
- </child>
+ </property>
</object>
</child>
+
<child>
- <object class="GtkTreeViewColumn" id="list_type_column">
+ <object class="GtkColumnViewColumn" id="column_view_type_column">
<property name="title" translatable="yes">Type</property>
<property name="resizable">1</property>
- <child>
- <object class="GtkCellRendererText" id="list_type_renderer">
- <property name="xalign">0</property>
- <property name="xpad">6</property>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkFileChooserCell">
+ <binding name="position">
+ <lookup name="position">GtkListItem</lookup>
+ </binding>
+ <binding name="item">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ <binding name="selected">
+ <lookup name="selected">GtkListItem</lookup>
+ </binding>
+ <child>
+ <object class="GtkLabel">
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <binding name="label">
+ <closure type="gchararray" function="column_view_get_file_type">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ <binding name="tooltip-text">
+ <closure type="gchararray" function="column_view_get_tooltip_text">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+ ]]></property>
</object>
- </child>
+ </property>
</object>
</child>
+
<child>
- <object class="GtkTreeViewColumn" id="list_time_column">
+ <object class="GtkColumnViewColumn" id="column_view_time_column">
<property name="title" translatable="yes">Modified</property>
- <property name="sizing">2</property>
- <child>
- <object class="GtkCellRendererText" id="list_date_renderer">
- <property name="xpad">6</property>
- </object>
- </child>
- <child>
- <object class="GtkCellRendererText" id="list_time_renderer">
- <property name="xpad">6</property>
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkFileChooserCell" id="file_chooser_cell">
+ <binding name="position">
+ <lookup name="position">GtkListItem</lookup>
+ </binding>
+ <binding name="item">
+ <lookup name="item">GtkListItem</lookup>
+ </binding>
+ <binding name="selected">
+ <lookup name="selected">GtkListItem</lookup>
+ </binding>
+ <child>
+ <object class="GtkBox">
+ <property name="spacing">6</property>
+ <binding name="tooltip-text">
+ <closure type="gchararray" function="column_view_get_tooltip_text">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ <child>
+ <object class="GtkLabel">
+ <binding name="label">
+ <closure type="gchararray" function="column_view_get_file_date">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible" bind-source="file_chooser_cell" bind-property="show-time" bind-flags="sync-create"/>
+ <binding name="label">
+ <closure type="gchararray" function="column_view_get_file_time">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+ ]]></property>
</object>
- </child>
+ </property>
</object>
</child>
+
</object>
</child>
</object>