diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h index a0a622c111..2af859433d 100644 --- a/gtk/gtkfilechooserprivate.h +++ b/gtk/gtkfilechooserprivate.h @@ -32,10 +32,14 @@ #include "gtktreestore.h" #include "gtktreeview.h" #include "gtkbox.h" +#include "gtkiconview.h" +#include "gtkscale.h" G_BEGIN_DECLS #define SETTINGS_KEY_LOCATION_MODE "location-mode" +#define SETTINGS_KEY_VIEW_MODE "view-mode" +#define SETTINGS_KEY_ICON_VIEW_SCALE "icon-view-scale" #define SETTINGS_KEY_SHOW_HIDDEN "show-hidden" #define SETTINGS_KEY_SHOW_SIZE_COLUMN "show-size-column" #define SETTINGS_KEY_SHOW_TYPE_COLUMN "show-type-column" @@ -60,34 +64,34 @@ struct _GtkFileChooserIface /* Methods */ - gboolean (*set_current_folder) (GtkFileChooser *chooser, - GFile *file, - GError **error); - GFile * (*get_current_folder) (GtkFileChooser *chooser); - void (*set_current_name) (GtkFileChooser *chooser, - const gchar *name); + gboolean (*set_current_folder) (GtkFileChooser *chooser, + GFile *file, + GError **error); + GFile * (*get_current_folder) (GtkFileChooser *chooser); + void (*set_current_name) (GtkFileChooser *chooser, + const gchar *name); gchar * (*get_current_name) (GtkFileChooser *chooser); - gboolean (*select_file) (GtkFileChooser *chooser, - GFile *file, - GError **error); - void (*unselect_file) (GtkFileChooser *chooser, - GFile *file); - void (*select_all) (GtkFileChooser *chooser); - void (*unselect_all) (GtkFileChooser *chooser); - GSList * (*get_files) (GtkFileChooser *chooser); - GFile * (*get_preview_file) (GtkFileChooser *chooser); - GtkFileSystem *(*get_file_system) (GtkFileChooser *chooser); - void (*add_filter) (GtkFileChooser *chooser, - GtkFileFilter *filter); - void (*remove_filter) (GtkFileChooser *chooser, - GtkFileFilter *filter); - GSList * (*list_filters) (GtkFileChooser *chooser); + gboolean (*select_file) (GtkFileChooser *chooser, + GFile *file, + GError **error); + void (*unselect_file) (GtkFileChooser *chooser, + GFile *file); + void (*select_all) (GtkFileChooser *chooser); + void (*unselect_all) (GtkFileChooser *chooser); + GSList * (*get_files) (GtkFileChooser *chooser); + GFile * (*get_preview_file) (GtkFileChooser *chooser); + GtkFileSystem *(*get_file_system) (GtkFileChooser *chooser); + void (*add_filter) (GtkFileChooser *chooser, + GtkFileFilter *filter); + void (*remove_filter) (GtkFileChooser *chooser, + GtkFileFilter *filter); + GSList * (*list_filters) (GtkFileChooser *chooser); gboolean (*add_shortcut_folder) (GtkFileChooser *chooser, - GFile *file, - GError **error); + GFile *file, + GError **error); gboolean (*remove_shortcut_folder) (GtkFileChooser *chooser, - GFile *file, - GError **error); + GFile *file, + GError **error); GSList * (*list_shortcut_folders) (GtkFileChooser *chooser); /* Signals @@ -115,11 +119,11 @@ struct _GtkFileChooserIface GtkFileSystem *_gtk_file_chooser_get_file_system (GtkFileChooser *chooser); gboolean _gtk_file_chooser_add_shortcut_folder (GtkFileChooser *chooser, - GFile *folder, - GError **error); + GFile *folder, + GError **error); gboolean _gtk_file_chooser_remove_shortcut_folder (GtkFileChooser *chooser, - GFile *folder, - GError **error); + GFile *folder, + GError **error); GSList * _gtk_file_chooser_list_shortcut_folder_files (GtkFileChooser *chooser); diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index d75a3f7857..623c71e84a 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -44,10 +44,12 @@ #include "gtkfilesystemmodel.h" #include "gtkgrid.h" #include "gtkicontheme.h" +#include "gtkiconview.h" #include "gtklabel.h" #include "gtkmarshalers.h" #include "gtkmessagedialog.h" #include "gtkmountoperation.h" +#include "gtknotebook.h" #include "gtkpaned.h" #include "gtkpathbar.h" #include "gtkplacessidebar.h" @@ -81,6 +83,7 @@ #include "gtkgesturelongpress.h" #include +#include #ifdef HAVE_UNISTD_H #include @@ -198,6 +201,11 @@ typedef enum { STARTUP_MODE_CWD } StartupMode; +typedef enum { + VIEW_MODE_LIST, + VIEW_MODE_ICON, +} ViewMode; + typedef enum { CLOCK_FORMAT_24, CLOCK_FORMAT_12 @@ -228,8 +236,12 @@ struct _GtkFileChooserWidgetPrivate { GtkWidget *browse_header_revealer; GtkWidget *browse_header_stack; GtkWidget *browse_files_stack; - GtkWidget *browse_files_swin; + GtkNotebook *view_notebook; + GtkWidget *browse_files_list_swin; + GtkWidget *browse_files_icon_swin; + GtkWidget *browse_files_current_view; GtkWidget *browse_files_tree_view; + GtkWidget *browse_files_icon_view; GtkWidget *remote_warning_bar; GtkWidget *browse_files_popover; @@ -245,6 +257,12 @@ struct _GtkFileChooserWidgetPrivate { GtkWidget *delete_file_item; GtkWidget *sort_directories_item; GtkWidget *show_time_item; + GtkWidget *arrange_item; + GtkWidget *sort_by_name_item; + GtkWidget *sort_by_size_item; + GtkWidget *sort_by_time_item; + GtkWidget *ascending_item; + GtkWidget *descending_item; GtkWidget *browse_new_folder_button; GtkSizeGroup *browse_path_bar_size_group; @@ -261,6 +279,7 @@ struct _GtkFileChooserWidgetPrivate { GtkGesture *long_press_gesture; + GtkTreeModel *current_model; GtkFileSystemModel *browse_files_model; char *browse_files_last_selected_name; @@ -348,10 +367,17 @@ struct _GtkFileChooserWidgetPrivate { guint location_changed_id; gulong settings_signal_id; - int icon_size; + gint list_view_icon_size; + gint icon_view_icon_size; GSource *focus_entry_idle; + GtkWidget *view_mode_combo_box; + GtkWidget *icon_view_scale; + ViewMode view_mode; + + GtkCellRenderer *list_icon_renderer; + gulong toplevel_set_focus_id; GtkWidget *toplevel_last_focus_widget; @@ -419,7 +445,8 @@ enum { MODEL_COL_NAME_COLLATED, MODEL_COL_IS_FOLDER, MODEL_COL_IS_SENSITIVE, - MODEL_COL_SURFACE, + MODEL_COL_LIST_SURFACE, + MODEL_COL_ICON_PIXBUF, MODEL_COL_SIZE_TEXT, MODEL_COL_DATE_TEXT, MODEL_COL_TIME_TEXT, @@ -439,7 +466,8 @@ enum { G_TYPE_STRING, /* MODEL_COL_NAME_COLLATED */ \ G_TYPE_BOOLEAN, /* MODEL_COL_IS_FOLDER */ \ G_TYPE_BOOLEAN, /* MODEL_COL_IS_SENSITIVE */ \ - CAIRO_GOBJECT_TYPE_SURFACE, /* MODEL_COL_SURFACE */ \ + CAIRO_GOBJECT_TYPE_SURFACE, /* MODEL_COL_LIST_SURFACE */ \ + GDK_TYPE_PIXBUF, /* MODEL_COL_ICON_PIXBUF */ \ G_TYPE_STRING, /* MODEL_COL_SIZE_TEXT */ \ G_TYPE_STRING, /* MODEL_COL_DATE_TEXT */ \ G_TYPE_STRING, /* MODEL_COL_TIME_TEXT */ \ @@ -449,7 +477,10 @@ enum { #define DEFAULT_RECENT_FILES_LIMIT 50 /* Icon size for if we can't get it from the theme */ -#define FALLBACK_ICON_SIZE 16 +#define FALLBACK_LIST_VIEW_ICON_SIZE 16 +#define FALLBACK_ICON_VIEW_ICON_SIZE 48 + +#define ICON_VIEW_ITEM_WIDTH 128 #define PREVIEW_HBOX_SPACING 12 #define NUM_LINES 45 @@ -570,7 +601,7 @@ static gboolean list_select_func (GtkTreeSelection *selection, gboolean path_currently_selected, gpointer data); -static void list_selection_changed (GtkTreeSelection *tree_selection, +static void list_selection_changed (void *selection, GtkFileChooserWidget *impl); static void list_row_activated (GtkTreeView *tree_view, GtkTreePath *path, @@ -578,6 +609,13 @@ static void list_row_activated (GtkTreeView *tree_view, GtkFileChooserWidget *impl); static void list_cursor_changed (GtkTreeView *treeview, GtkFileChooserWidget *impl); +static void icon_item_activated (GtkIconView *icon_view, + GtkTreePath *path, + GtkFileChooserWidget *impl); +static void item_activated (GtkTreeModel *model, + GtkTreePath *path, + GtkFileChooserWidget *impl); + static void path_bar_clicked (GtkPathBar *path_bar, GFile *file, @@ -590,6 +628,13 @@ 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 icon_view_scale_value_changed_cb (GtkRange *range, + GtkFileChooserWidget *impl); + +static void view_mode_set (GtkFileChooserWidget *impl, ViewMode view_mode); +static void view_mode_combo_box_changed_cb (GtkComboBox *combo, + GtkFileChooserWidget *impl); + static void location_switch_to_path_bar (GtkFileChooserWidget *impl); static void stop_loading_and_clear_list_model (GtkFileChooserWidget *impl, @@ -619,6 +664,26 @@ static gboolean recent_should_respond (GtkFileChooserWidget *impl); static void set_file_system_backend (GtkFileChooserWidget *impl); static void unset_file_system_backend (GtkFileChooserWidget *impl); +static gboolean get_selected_tree_iter_from_icon_view (GtkFileChooserWidget *impl, + GtkTreeIter *iter_out); +static void current_selection_selected_foreach (GtkFileChooserWidget *impl, + GtkTreeSelectionForeachFunc func, + gpointer data); +static guint current_selection_count_selected_rows (GtkFileChooserWidget *impl); +static void current_selection_select_iter (GtkFileChooserWidget *impl, + GtkTreeIter *iter); +static void copy_old_selection_to_current_view (GtkFileChooserWidget *impl, + ViewMode old_view_mode); +static void current_selection_unselect_iter (GtkFileChooserWidget *impl, + GtkTreeIter *iter); +static void current_selection_unselect_all (GtkFileChooserWidget *impl); +static void current_view_set_file_model (GtkFileChooserWidget *impl, + GtkTreeModel *model); +static void current_view_set_cursor (GtkFileChooserWidget *impl, + GtkTreePath *path); +static void current_view_set_select_multiple (GtkFileChooserWidget *impl, + gboolean select_multiple); + static void clear_model_cache (GtkFileChooserWidget *impl, gint column); static void set_model_filter (GtkFileChooserWidget *impl, @@ -956,7 +1021,7 @@ update_preview_widget_visibility (GtkFileChooserWidget *impl) } } - if (priv->preview_widget_active && priv->preview_widget) + if (priv->preview_widget_active && priv->preview_widget && priv->view_mode == VIEW_MODE_LIST) gtk_widget_show (priv->preview_box); else gtk_widget_hide (priv->preview_box); @@ -1224,19 +1289,16 @@ selection_check (GtkFileChooserWidget *impl, gboolean *all_files, gboolean *all_folders) { - GtkFileChooserWidgetPrivate *priv = impl->priv; struct selection_check_closure closure; - GtkTreeSelection *selection; closure.impl = impl; closure.num_selected = 0; closure.all_files = TRUE; closure.all_folders = TRUE; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, - selection_check_foreach_cb, - &closure); + current_selection_selected_foreach (impl, + selection_check_foreach_cb, + &closure); g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders)); @@ -1361,7 +1423,7 @@ browse_files_key_press_event_cb (GtkWidget *widget, return TRUE; } - if (key_is_left_or_right (event)) + if (priv->view_mode == VIEW_MODE_LIST && key_is_left_or_right (event)) { if (gtk_widget_child_focus (priv->places_sidebar, GTK_DIR_LEFT)) return TRUE; @@ -1464,12 +1526,8 @@ add_to_shortcuts_cb (GSimpleAction *action, gpointer data) { GtkFileChooserWidget *impl = data; - GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, + current_selection_selected_foreach (impl, add_bookmark_foreach_cb, impl); } @@ -1842,6 +1900,86 @@ open_folder_cb (GSimpleAction *action, } G_GNUC_END_IGNORE_DEPRECATIONS +/* callback used when "Sort by Name" menu item is activated */ +static void +sort_by_name_cb (GSimpleAction *action, + GVariant *parameter, + gpointer data) +{ + GtkFileChooserWidget *impl = data; + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkTreeSortable *sortable; + + sortable = GTK_TREE_SORTABLE (priv->browse_files_model); + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column=MODEL_COL_NAME, + priv->sort_order); +} + +/* callback used when "Sort by Size" menu item is activated */ +static void +sort_by_size_cb (GSimpleAction *action, + GVariant *parameter, + gpointer data) +{ + GtkFileChooserWidget *impl = data; + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkTreeSortable *sortable; + + sortable = GTK_TREE_SORTABLE (priv->browse_files_model); + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column=MODEL_COL_SIZE, + priv->sort_order); +} + +/* callback used when "Sort by Time" menu item is activated */ +static void +sort_by_time_cb (GSimpleAction *action, + GVariant *parameter, + gpointer data) +{ + GtkFileChooserWidget *impl = data; + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkTreeSortable *sortable; + + sortable = GTK_TREE_SORTABLE (priv->browse_files_model); + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column=MODEL_COL_TIME, + priv->sort_order); +} + +/* callback used when "Ascending" menu item is activated */ +static void +ascending_cb (GSimpleAction *action, + GVariant *parameter, + gpointer data) +{ + GtkFileChooserWidget *impl = data; + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkTreeSortable *sortable; + + sortable = GTK_TREE_SORTABLE (priv->browse_files_model); + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column, + priv->sort_order=GTK_SORT_ASCENDING); +} + +/* callback used when "Descending" menu item is activated */ +static void +descending_cb (GSimpleAction *action, + GVariant *parameter, + gpointer data) +{ + GtkFileChooserWidget *impl = data; + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkTreeSortable *sortable; + + sortable = GTK_TREE_SORTABLE (priv->browse_files_model); + gtk_tree_sortable_set_sort_column_id (sortable, + priv->sort_column, + priv->sort_order=GTK_SORT_DESCENDING); +} + /* callback used when the "Show Hidden Files" menu item is toggled */ static void change_show_hidden_state (GSimpleAction *action, @@ -2169,6 +2307,7 @@ check_file_list_popover_sensitivity (GtkFileChooserWidget *impl) gboolean all_files; gboolean all_folders; gboolean active; + gboolean always_active; GActionGroup *actions; GAction *action, *action2; @@ -2177,6 +2316,7 @@ check_file_list_popover_sensitivity (GtkFileChooserWidget *impl) selection_check (impl, &num_selected, &all_files, &all_folders); active = (num_selected != 0); + always_active = (num_selected >= 0); action = g_action_map_lookup_action (G_ACTION_MAP (actions), "copy-location"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), active); @@ -2190,6 +2330,21 @@ check_file_list_popover_sensitivity (GtkFileChooserWidget *impl) action = g_action_map_lookup_action (G_ACTION_MAP (actions), "open"); g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (num_selected == 1) && all_folders); + action = g_action_map_lookup_action (G_ACTION_MAP (actions), "sort-by-name"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), always_active); + + action = g_action_map_lookup_action (G_ACTION_MAP (actions), "sort-by-size"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), always_active); + + action = g_action_map_lookup_action (G_ACTION_MAP (actions), "sort-by-time"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), always_active); + + action = g_action_map_lookup_action (G_ACTION_MAP (actions), "ascending"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), always_active); + + action = g_action_map_lookup_action (G_ACTION_MAP (actions), "descending"); + g_simple_action_set_enabled (G_SIMPLE_ACTION (action), always_active); + action = g_action_map_lookup_action (G_ACTION_MAP (actions), "rename"); if (num_selected == 1) { @@ -2255,6 +2410,11 @@ static GActionEntry entries[] = { { "rename", rename_file_cb, NULL, NULL, NULL }, { "delete", delete_file_cb, NULL, NULL, NULL }, { "trash", trash_file_cb, NULL, NULL, NULL }, + { "sort-by-name", sort_by_name_cb, NULL, NULL, NULL }, + { "sort-by-size", sort_by_size_cb, NULL, NULL, NULL }, + { "sort-by-time", sort_by_time_cb, NULL, NULL, NULL }, + { "ascending", ascending_cb, NULL, NULL, NULL }, + { "descending", descending_cb, NULL, 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 }, @@ -2280,15 +2440,9 @@ append_separator (GtkWidget *box) { GtkWidget *separator; - separator = g_object_new (GTK_TYPE_SEPARATOR, - "orientation", GTK_ORIENTATION_HORIZONTAL, - "visible", TRUE, - "margin-start", 12, - "margin-end", 12, - "margin-top", 6, - "margin-bottom", 6, - NULL); - gtk_container_add (GTK_CONTAINER (box), separator); + separator = gtk_separator_menu_item_new (); + gtk_widget_set_visible (GTK_WIDGET (separator), TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (box), separator); return separator; } @@ -2301,12 +2455,13 @@ add_button (GtkWidget *box, { GtkWidget *item; - item = g_object_new (GTK_TYPE_MODEL_BUTTON, - "visible", TRUE, - "action-name", action, - "text", label, - NULL); - gtk_container_add (GTK_CONTAINER (box), item); + if (g_str_match_string ("toggle", action, TRUE)) + item = gtk_check_menu_item_new_with_mnemonic (label); + else + item = gtk_menu_item_new_with_mnemonic (label); + g_object_set (G_OBJECT (item), "action-name", action, NULL); + gtk_widget_set_visible (GTK_WIDGET (item), TRUE); + gtk_menu_shell_append (GTK_MENU_SHELL (box), item); return item; } @@ -2320,11 +2475,9 @@ file_list_build_popover (GtkFileChooserWidget *impl) if (priv->browse_files_popover) return; - priv->browse_files_popover = gtk_popover_new (priv->browse_files_tree_view); - box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - g_object_set (box, "margin", 10, NULL); - gtk_widget_show (box); - gtk_container_add (GTK_CONTAINER (priv->browse_files_popover), box); + priv->browse_files_popover = gtk_menu_new (); + gtk_menu_attach_to_widget (GTK_MENU (priv->browse_files_popover), GTK_WIDGET (priv->browse_files_tree_view), NULL); + box = priv->browse_files_popover; priv->visit_file_item = add_button (box, _("_Visit File"), "item.visit"); priv->open_folder_item = add_button (box, _("_Open With File Manager"), "item.open"); @@ -2336,11 +2489,29 @@ file_list_build_popover (GtkFileChooserWidget *impl) append_separator (box); - priv->hidden_files_item = add_button (box, _("Show _Hidden Files"), "item.toggle-show-hidden"); - priv->size_column_item = add_button (box, _("Show _Size Column"), "item.toggle-show-size"); - priv->type_column_item = add_button (box, _("Show T_ype Column"), "item.toggle-show-type"); - priv->show_time_item = add_button (box, _("Show _Time"), "item.toggle-show-time"); - priv->sort_directories_item = add_button (box, _("Sort _Folders before Files"), "item.toggle-sort-dirs-first"); + if (priv->view_mode == VIEW_MODE_LIST) + { + priv->hidden_files_item = add_button (box, _("Show _Hidden Files"), "item.toggle-show-hidden"); + priv->size_column_item = add_button (box, _("Show _Size Column"), "item.toggle-show-size"); + priv->type_column_item = add_button (box, _("Show T_ype Column"), "item.toggle-show-type"); + priv->show_time_item = add_button (box, _("Show _Time"), "item.toggle-show-time"); + priv->sort_directories_item = add_button (box, _("Sort _Folders before Files"), "item.toggle-sort-dirs-first"); + } + if (priv->view_mode == VIEW_MODE_ICON) + { + GtkWidget *menu; + priv->arrange_item = add_button (box, _("Arrange Items"), NULL); + menu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (priv->arrange_item), menu); + priv->sort_by_name_item = add_button (menu, _("Sort _by Name"), "item.sort-by-name"); + priv->sort_by_size_item = add_button (menu, _("Sort _by Size"), "item.sort-by-size"); + priv->sort_by_time_item = add_button (menu, _("Sort _by Time"), "item.sort-by-time"); + append_separator (menu); + priv->ascending_item = add_button (menu, _("Ascending"), "item.ascending"); + priv->descending_item = add_button (menu, _("Descending"), "item.descending"); + priv->hidden_files_item = add_button (box, _("Show _Hidden Files"), "item.toggle-show-hidden"); + priv->sort_directories_item = add_button (box, _("Sort _Folders before Files"), "item.toggle-sort-dirs-first"); + } } /* Updates the popover for the file list, creating it if necessary */ @@ -2392,39 +2563,11 @@ file_list_show_popover (GtkFileChooserWidget *impl, gdouble y) { GtkFileChooserWidgetPrivate *priv = impl->priv; - GdkRectangle rect; - GtkTreeSelection *selection; - GtkTreeModel *model; - GList *list; - GtkTreePath *path; - file_list_update_popover (impl); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->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 (priv->browse_files_tree_view), path, NULL, &rect); - gtk_tree_view_convert_bin_window_to_widget_coords (GTK_TREE_VIEW (priv->browse_files_tree_view), - rect.x, rect.y, &rect.x, &rect.y); - - rect.x = CLAMP (x - 20, 0, gtk_widget_get_allocated_width (priv->browse_files_tree_view) - 40); - 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; - } - - gtk_popover_set_pointing_to (GTK_POPOVER (priv->browse_files_popover), &rect); - gtk_popover_popup (GTK_POPOVER (priv->browse_files_popover)); + gtk_menu_popup_at_pointer (GTK_MENU (priv->browse_files_popover), NULL); + return; } /* Callback used for the GtkWidget::popup-menu signal of the file list */ @@ -2461,7 +2604,7 @@ list_button_press_event_cb (GtkWidget *widget, return FALSE; in_press = TRUE; - gtk_widget_event (priv->browse_files_tree_view, (GdkEvent *) event); + gtk_widget_event (widget, (GdkEvent *) event); in_press = FALSE; file_list_show_popover (impl, event->x, event->y); @@ -2492,13 +2635,16 @@ file_list_set_sort_column_ids (GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; - gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), -1); + if (priv->view_mode == VIEW_MODE_LIST) + { + gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), -1); - gtk_tree_view_column_set_sort_column_id (priv->list_name_column, MODEL_COL_NAME); - gtk_tree_view_column_set_sort_column_id (priv->list_time_column, MODEL_COL_TIME); - gtk_tree_view_column_set_sort_column_id (priv->list_size_column, MODEL_COL_SIZE); - gtk_tree_view_column_set_sort_column_id (priv->list_type_column, MODEL_COL_TYPE); - gtk_tree_view_column_set_sort_column_id (priv->list_location_column, MODEL_COL_LOCATION_TEXT); + gtk_tree_view_column_set_sort_column_id (priv->list_name_column, MODEL_COL_NAME); + gtk_tree_view_column_set_sort_column_id (priv->list_time_column, MODEL_COL_TIME); + gtk_tree_view_column_set_sort_column_id (priv->list_size_column, MODEL_COL_SIZE); + gtk_tree_view_column_set_sort_column_id (priv->list_type_column, MODEL_COL_TYPE); + gtk_tree_view_column_set_sort_column_id (priv->list_location_column, MODEL_COL_LOCATION_TEXT); + } } static gboolean @@ -2521,11 +2667,22 @@ file_list_query_tooltip_cb (GtkWidget *widget, return FALSE; - if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (priv->browse_files_tree_view), - &x, &y, - keyboard_tip, - &model, &path, &iter)) - return FALSE; + if (priv->view_mode == VIEW_MODE_LIST) + { + if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (priv->browse_files_tree_view), + &x, &y, + keyboard_tip, + &model, &path, &iter)) + return FALSE; + } + else if(priv->view_mode == VIEW_MODE_ICON) + { + if (!gtk_icon_view_get_tooltip_context (GTK_ICON_VIEW (priv->browse_files_icon_view), + &x, &y, + keyboard_tip, + &model, &path, &iter)) + return FALSE; + } gtk_tree_model_get (model, &iter, MODEL_COL_FILE, &file, @@ -2539,10 +2696,18 @@ file_list_query_tooltip_cb (GtkWidget *widget, filename = g_file_get_path (file); gtk_tooltip_set_text (tooltip, filename); - gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (priv->browse_files_tree_view), - tooltip, - path); - + if (priv->view_mode == VIEW_MODE_LIST) + { + gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (priv->browse_files_tree_view), + tooltip, + path); + } + else if(priv->view_mode == VIEW_MODE_ICON) + { + gtk_icon_view_set_tooltip_item (GTK_ICON_VIEW (priv->browse_files_icon_view), + tooltip, + path); + } g_free (filename); g_object_unref (file); gtk_tree_path_free (path); @@ -2558,8 +2723,8 @@ set_icon_cell_renderer_fixed_size (GtkFileChooserWidget *impl) gtk_cell_renderer_get_padding (priv->list_pixbuf_renderer, &xpad, &ypad); gtk_cell_renderer_set_fixed_size (priv->list_pixbuf_renderer, - xpad * 2 + priv->icon_size, - ypad * 2 + priv->icon_size); + xpad * 2 + priv->list_view_icon_size, + ypad * 2 + priv->list_view_icon_size); } static gboolean @@ -2843,7 +3008,7 @@ location_mode_set (GtkFileChooserWidget *impl, location_switch_to_path_bar (impl); if (switch_to_file_list) - gtk_widget_grab_focus (priv->browse_files_tree_view); + gtk_widget_grab_focus (priv->browse_files_current_view); break; @@ -2914,6 +3079,119 @@ location_toggle_popup_handler (GtkFileChooserWidget *impl) } } +/* Creates icon view (alternative for the list view) */ +static GtkWidget * +create_browse_files_icon_view (GtkFileChooserWidget *impl) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + + gtk_icon_view_set_text_column (GTK_ICON_VIEW (priv->browse_files_icon_view), + MODEL_COL_NAME); + gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (priv->browse_files_icon_view), + MODEL_COL_ICON_PIXBUF); + gtk_icon_view_set_item_width (GTK_ICON_VIEW (priv->browse_files_icon_view), + ICON_VIEW_ITEM_WIDTH); + + return priv->browse_files_icon_view; +} + +static void +view_mode_set (GtkFileChooserWidget *impl, ViewMode view_mode) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkWidget *old_view = NULL; + ViewMode old_view_mode = priv->view_mode; + priv->browse_files_popover = NULL; + + if (old_view_mode == view_mode) + return; + + g_debug("GtkFileChooserWidget::view_mode_set %d", view_mode); + + priv->view_mode = view_mode; + gtk_combo_box_set_active (GTK_COMBO_BOX (priv->view_mode_combo_box), + view_mode); + + /* Creating the target view */ + if (view_mode == VIEW_MODE_ICON) + { + priv->browse_files_current_view = priv->browse_files_icon_view; + old_view = priv->browse_files_tree_view; + gtk_widget_show (priv->icon_view_scale); + } + else if (view_mode == VIEW_MODE_LIST) + { + priv->browse_files_current_view = priv->browse_files_tree_view; + old_view = priv->browse_files_icon_view; + gtk_widget_hide (priv->icon_view_scale); + } + else + g_assert_not_reached (); + + /* Set model and selection */ + current_view_set_file_model (impl, priv->current_model); + current_view_set_select_multiple (impl, priv->select_multiple); + copy_old_selection_to_current_view (impl, old_view_mode); + + /* Hide the old view */ + g_object_set (old_view, "model", NULL, NULL); + gtk_widget_hide (old_view); + + /* Show the new view */ + gtk_widget_show (priv->browse_files_current_view); + gtk_notebook_set_current_page(priv->view_notebook, view_mode); +} + +/* Callback used when view mode combo box active item is changed */ +static void +view_mode_combo_box_changed_cb (GtkComboBox *combo, + GtkFileChooserWidget *impl) +{ + ViewMode target = gtk_combo_box_get_active (combo); + + view_mode_set (impl, target); +} + +/* Callback used when view mode is changed */ +gboolean +view_notebook_switch_page_cb (GtkNotebook *notebook, + GtkWidget *page, + guint page_num, + gpointer user_data) +{ + GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (user_data); + view_mode_set (impl, page_num); + return TRUE; +} + +static void +icon_view_scale_value_changed_cb (GtkRange *range, + GtkFileChooserWidget *impl) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + gdouble value = gtk_range_get_value (range); + value = round (value / 16) * 16; + + if (priv->icon_view_icon_size == (gint)value) + return; + + priv->icon_view_icon_size = (gint)value; + + if (priv->view_mode != VIEW_MODE_ICON) + return; + + set_icon_cell_renderer_fixed_size (impl); + + if (priv->browse_files_model) + _gtk_file_system_model_clear_cache (priv->browse_files_model, MODEL_COL_ICON_PIXBUF); + if (priv->search_model) + _gtk_file_system_model_clear_cache (priv->search_model, MODEL_COL_ICON_PIXBUF); + if (priv->recent_model) + _gtk_file_system_model_clear_cache (priv->recent_model, MODEL_COL_ICON_PIXBUF); + + gtk_widget_queue_resize (priv->browse_files_current_view); +} + static void gtk_file_chooser_widget_constructed (GObject *object) { @@ -3022,18 +3300,11 @@ set_select_multiple (GtkFileChooserWidget *impl, gboolean property_notify) { GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection; - GtkSelectionMode mode; if (select_multiple == priv->select_multiple) return; - mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_set_mode (selection, mode); - - gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (priv->browse_files_tree_view), select_multiple); + current_view_set_select_multiple (impl, select_multiple); priv->select_multiple = select_multiple; g_object_notify (G_OBJECT (impl), "select-multiple"); @@ -3160,6 +3431,7 @@ operation_mode_set_enter_location (GtkFileChooserWidget *impl) gtk_stack_set_visible_child_name (GTK_STACK (priv->browse_header_stack), "location"); gtk_revealer_set_reveal_child (GTK_REVEALER (priv->browse_header_revealer), TRUE); location_bar_update (impl); + gtk_tree_view_column_set_visible (priv->list_location_column, FALSE); gtk_widget_set_sensitive (priv->filter_combo, TRUE); location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY); } @@ -3189,7 +3461,7 @@ operation_mode_set_search (GtkFileChooserWidget *impl) visible_widget = gtk_stack_get_visible_child (GTK_STACK (priv->browse_files_stack)); if (visible_widget != priv->places_view && - visible_widget != priv->browse_files_swin) + visible_widget != priv->browse_files_list_swin) { gtk_stack_set_visible_child_name (GTK_STACK (priv->browse_files_stack), "list"); } @@ -3314,6 +3586,12 @@ update_appearance (GtkFileChooserWidget *impl) location_mode_set (impl, priv->location_mode); } + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + gtk_widget_show (priv->view_mode_combo_box); + else + gtk_widget_hide (priv->view_mode_combo_box); + if (priv->location_entry) _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->action); @@ -3322,7 +3600,7 @@ update_appearance (GtkFileChooserWidget *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 (priv->browse_files_tree_view); + gtk_widget_queue_draw (priv->browse_files_current_view); emit_default_size_changed (impl); } @@ -3752,14 +4030,27 @@ change_icon_theme (GtkFileChooserWidget *impl) profile_start ("start", NULL); if (gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height)) - priv->icon_size = MAX (width, height); + priv->list_view_icon_size = MAX (width, height); else - priv->icon_size = FALLBACK_ICON_SIZE; + priv->list_view_icon_size = FALLBACK_LIST_VIEW_ICON_SIZE; - /* 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); + if (gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &width, &height)) + { + priv->icon_view_icon_size = MAX (width, height); + } + else + priv->list_view_icon_size = FALLBACK_LIST_VIEW_ICON_SIZE; - clear_model_cache (impl, MODEL_COL_SURFACE); + /* the first cell in the first column is the icon column, and we have a fixed size there */ + if (priv->view_mode == VIEW_MODE_LIST) + { + set_icon_cell_renderer_fixed_size (impl); + } + if (priv->browse_files_model) + { + clear_model_cache (impl, MODEL_COL_LIST_SURFACE); + clear_model_cache (impl, MODEL_COL_ICON_PIXBUF); + } gtk_widget_queue_resize (priv->browse_files_tree_view); profile_end ("end", NULL); @@ -3860,7 +4151,7 @@ set_sort_column (GtkFileChooserWidget *impl) GtkFileChooserWidgetPrivate *priv = impl->priv; GtkTreeSortable *sortable; - sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view))); + sortable = GTK_TREE_SORTABLE (priv->current_model); /* can happen when we're still populating the model */ if (sortable == NULL) @@ -3875,6 +4166,7 @@ static void settings_load (GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; + ViewMode view_mode; gboolean show_hidden; gboolean show_size_column; gboolean show_type_column; @@ -3882,6 +4174,7 @@ settings_load (GtkFileChooserWidget *impl) DateFormat date_format; TypeFormat type_format; gint sort_column; + gint icon_view_scale; GtkSortType sort_order; StartupMode startup_mode; gint sidebar_width; @@ -3889,6 +4182,8 @@ settings_load (GtkFileChooserWidget *impl) settings = _gtk_file_chooser_get_settings_for_widget (GTK_WIDGET (impl)); + view_mode = g_settings_get_enum (settings, SETTINGS_KEY_VIEW_MODE); + icon_view_scale = g_settings_get_int (settings, SETTINGS_KEY_ICON_VIEW_SCALE); show_hidden = g_settings_get_boolean (settings, SETTINGS_KEY_SHOW_HIDDEN); show_size_column = g_settings_get_boolean (settings, SETTINGS_KEY_SHOW_SIZE_COLUMN); show_type_column = g_settings_get_boolean (settings, SETTINGS_KEY_SHOW_TYPE_COLUMN); @@ -3900,12 +4195,20 @@ settings_load (GtkFileChooserWidget *impl) date_format = g_settings_get_enum (settings, SETTINGS_KEY_DATE_FORMAT); type_format = g_settings_get_enum (settings, SETTINGS_KEY_TYPE_FORMAT); + gtk_range_set_value (GTK_RANGE (priv->icon_view_scale), icon_view_scale); + priv->icon_view_icon_size = icon_view_scale; + + view_mode_set (impl, view_mode); + if (!priv->show_hidden_set) set_show_hidden (impl, show_hidden); priv->show_size_column = show_size_column; - gtk_tree_view_column_set_visible (priv->list_size_column, show_size_column); - priv->show_type_column = show_type_column; - gtk_tree_view_column_set_visible (priv->list_type_column, show_type_column); + + if (priv->list_size_column) { + gtk_tree_view_column_set_visible (priv->list_size_column, show_size_column); + priv->show_type_column = show_type_column; + gtk_tree_view_column_set_visible (priv->list_type_column, show_type_column); + } priv->sort_column = sort_column; priv->sort_order = sort_order; @@ -3934,6 +4237,8 @@ settings_save (GtkFileChooserWidget *impl) /* All the other state */ g_settings_set_enum (settings, SETTINGS_KEY_LOCATION_MODE, priv->location_mode); + g_settings_set_enum (settings, SETTINGS_KEY_VIEW_MODE, priv->view_mode); + g_settings_set_int (settings, SETTINGS_KEY_ICON_VIEW_SCALE, priv->icon_view_icon_size); g_settings_set_boolean (settings, SETTINGS_KEY_SHOW_HIDDEN, gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl))); g_settings_set_boolean (settings, SETTINGS_KEY_SHOW_SIZE_COLUMN, priv->show_size_column); @@ -4445,10 +4750,14 @@ load_set_model (GtkFileChooserWidget *impl) g_assert (priv->browse_files_model != NULL); profile_msg (" gtk_tree_view_set_model start", NULL); - gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), - GTK_TREE_MODEL (priv->browse_files_model)); - update_columns (impl, FALSE, _("Modified")); - file_list_set_sort_column_ids (impl); + current_view_set_file_model (impl, GTK_TREE_MODEL (priv->browse_files_model)); + if (priv->view_mode == VIEW_MODE_LIST) + { + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), + GTK_TREE_MODEL (priv->browse_files_model)); + update_columns (impl, FALSE, _("Modified")); + file_list_set_sort_column_ids (impl); + } set_sort_column (impl); profile_msg (" gtk_tree_view_set_model end", NULL); priv->list_sort_ascending = TRUE; @@ -4528,7 +4837,7 @@ browse_files_select_first_row (GtkFileChooserWidget *impl) GtkTreeIter dummy_iter; GtkTreeModel *tree_model; - tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)); + tree_model = priv->current_model; if (!tree_model) return; @@ -4547,8 +4856,7 @@ browse_files_select_first_row (GtkFileChooserWidget *impl) */ priv->auto_selecting_first_row = TRUE; - gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), path, NULL, FALSE); - + current_view_set_cursor (impl, path); priv->auto_selecting_first_row = FALSE; } gtk_tree_path_free (path); @@ -4574,7 +4882,13 @@ center_selected_row_foreach_cb (GtkTreeModel *model, if (closure->already_centered) return; - gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->priv->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0); + if (closure->impl->priv->view_mode == VIEW_MODE_LIST) + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->priv->browse_files_tree_view), path, NULL, TRUE, 0.5, 0.0); + else if (closure->impl->priv->view_mode == VIEW_MODE_ICON) + gtk_icon_view_scroll_to_path (GTK_ICON_VIEW (closure->impl->priv->browse_files_icon_view), path, TRUE, 0.5, 0.0); + else + g_assert_not_reached (); + closure->already_centered = TRUE; } @@ -4582,15 +4896,11 @@ center_selected_row_foreach_cb (GtkTreeModel *model, static void browse_files_center_selected_row (GtkFileChooserWidget *impl) { - GtkFileChooserWidgetPrivate *priv = impl->priv; struct center_selected_row_closure closure; - GtkTreeSelection *selection; - closure.impl = impl; closure.already_centered = FALSE; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure); + current_selection_selected_foreach(impl, center_selected_row_foreach_cb, &closure); } static gboolean @@ -4598,7 +4908,6 @@ show_and_select_files (GtkFileChooserWidget *impl, GSList *files) { GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection; GtkFileSystemModel *fsmodel; gboolean enabled_hidden, removed_filters; gboolean selected_a_file; @@ -4607,8 +4916,7 @@ show_and_select_files (GtkFileChooserWidget *impl, g_assert (priv->load_state == LOAD_FINISHED); g_assert (priv->browse_files_model != NULL); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view))); + fsmodel = GTK_FILE_SYSTEM_MODEL (priv->current_model); g_assert (fsmodel == priv->browse_files_model); @@ -4663,11 +4971,10 @@ show_and_select_files (GtkFileChooserWidget *impl, { GtkTreePath *path; - gtk_tree_selection_select_iter (selection, &iter); + current_selection_select_iter (impl, &iter); path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsmodel), &iter); - gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), - path, NULL, FALSE); + current_view_set_cursor (impl, path); gtk_tree_path_free (path); selected_a_file = TRUE; @@ -4818,12 +5125,15 @@ stop_loading_and_clear_list_model (GtkFileChooserWidget *impl, { GtkFileChooserWidgetPrivate *priv = impl->priv; + if (priv->current_model == GTK_TREE_MODEL (priv->browse_files_model)) + priv->current_model = NULL; + load_remove_timer (impl, LOAD_EMPTY); g_set_object (&priv->browse_files_model, NULL); if (remove) - gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL); + current_view_set_file_model (impl, NULL); } /* Replace 'target' with 'replacement' in the input string. */ @@ -5093,6 +5403,18 @@ end: return g_strdup (""); } +static gboolean +get_visible_range (GtkTreePath **start, GtkTreePath **end, + GtkFileChooserWidget *impl) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + if (priv->view_mode == VIEW_MODE_LIST) + return gtk_tree_view_get_visible_range (GTK_TREE_VIEW (priv->browse_files_tree_view), start, end); + if (priv->view_mode == VIEW_MODE_ICON) + return gtk_icon_view_get_visible_range (GTK_ICON_VIEW (priv->browse_files_icon_view), start, end); + g_assert_not_reached (); +} + static gboolean file_system_model_set (GtkFileSystemModel *model, GFile *file, @@ -5152,12 +5474,15 @@ file_system_model_set (GtkFileSystemModel *model, else g_value_set_boolean (value, TRUE); break; - case MODEL_COL_SURFACE: + case MODEL_COL_LIST_SURFACE: + /* don't load list view icons in other view modes */ + if(priv->view_mode != VIEW_MODE_LIST) + return FALSE; if (info) { if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON)) { - g_value_take_boxed (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), priv->icon_size)); + g_value_take_boxed (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), priv->list_view_icon_size)); } else { @@ -5211,6 +5536,76 @@ file_system_model_set (GtkFileSystemModel *model, else g_value_set_boxed (value, NULL); break; + case MODEL_COL_ICON_PIXBUF: + if (info) + { + GtkTreeModel *tree_model; + GtkTreePath *path, *start, *end; + GtkTreeIter iter; + int icon_size; + gboolean file_visible; + + tree_model = priv->current_model; + if (tree_model != GTK_TREE_MODEL (model)) + return FALSE; + + /* #1 use standard icon if it is loaded */ + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON)) + { + icon_size = priv->icon_view_icon_size; + + cairo_surface_t *icon_surface = _gtk_file_info_render_icon (info, GTK_WIDGET (impl), icon_size); + GdkPixbuf *icon_pixbuf = gdk_pixbuf_get_from_surface(icon_surface, + 0, 0, + cairo_image_surface_get_width(icon_surface), + cairo_image_surface_get_height(icon_surface)); + cairo_surface_destroy(icon_surface); + + g_value_take_object (value, icon_pixbuf); + return TRUE; + } + + if (!get_visible_range (&start, &end, impl)) + return FALSE; + + if (!_gtk_file_system_model_get_iter_for_file (model, + &iter, + file)) + g_assert_not_reached (); + + path = gtk_tree_model_get_path (tree_model, &iter); + file_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); + + if (file_visible) + { + /* #2 start loading standard icon (callback will be handled by #1) */ + if (!g_file_info_has_attribute (info, "filechooser::icon_queried")) + { + g_file_info_set_attribute_boolean (info, "filechooser::icon_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_object (value, NULL); + } + break; case MODEL_COL_SIZE: g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0); break; @@ -5403,7 +5798,6 @@ static void update_chooser_entry (GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection; struct update_chooser_entry_selected_foreach_closure closure; /* no need to update the file chooser's entry if there's no entry */ @@ -5420,9 +5814,8 @@ update_chooser_entry (GtkFileChooserWidget *impl) g_assert (priv->location_entry != NULL); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); closure.num_selected = 0; - gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure); + current_selection_selected_foreach (impl, update_chooser_entry_selected_foreach, &closure); if (closure.num_selected == 0) { @@ -5919,19 +6312,15 @@ gtk_file_chooser_widget_unselect_file (GtkFileChooser *chooser, { GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser); GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeView *tree_view; - GtkTreeModel *model; GtkTreeIter iter; - tree_view = GTK_TREE_VIEW (priv->browse_files_tree_view); - model = gtk_tree_view_get_model (tree_view); - if (!model) + if (!priv->current_model) return; - if (!_gtk_file_system_model_get_iter_for_file (GTK_FILE_SYSTEM_MODEL (model), &iter, file)) + if (!_gtk_file_system_model_get_iter_for_file (GTK_FILE_SYSTEM_MODEL (priv->current_model), &iter, file)) return; - gtk_tree_selection_unselect_iter (gtk_tree_view_get_selection (tree_view), &iter); + current_selection_unselect_iter (impl, &iter); } static gboolean @@ -5942,12 +6331,9 @@ maybe_select (GtkTreeModel *model, { GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (data); GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection; gboolean is_sensitive; gboolean is_folder; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_model_get (model, iter, MODEL_COL_IS_FOLDER, &is_folder, MODEL_COL_IS_SENSITIVE, &is_sensitive, @@ -5956,9 +6342,9 @@ maybe_select (GtkTreeModel *model, if (is_sensitive && ((is_folder && priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) || (!is_folder && priv->action == GTK_FILE_CHOOSER_ACTION_OPEN))) - gtk_tree_selection_select_iter (selection, iter); + current_selection_select_iter (impl, iter); else - gtk_tree_selection_unselect_iter (selection, iter); + current_selection_unselect_iter (impl, iter); return FALSE; } @@ -5974,8 +6360,15 @@ gtk_file_chooser_widget_select_all (GtkFileChooser *chooser) { GtkTreeSelection *selection; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_select_all (selection); + if (priv->view_mode == VIEW_MODE_LIST) + { + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + gtk_tree_selection_select_all (selection); + } + else if (priv->view_mode == VIEW_MODE_ICON) + gtk_icon_view_select_all (GTK_ICON_VIEW (priv->browse_files_icon_view)); + else + g_assert_not_reached(); return; } @@ -5988,10 +6381,7 @@ static void gtk_file_chooser_widget_unselect_all (GtkFileChooser *chooser) { GtkFileChooserWidget *impl = GTK_FILE_CHOOSER_WIDGET (chooser); - GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - - gtk_tree_selection_unselect_all (selection); + current_selection_unselect_all (impl); pending_select_files_free (impl); } @@ -6147,15 +6537,13 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser) current_focus = NULL; file_list_seen = FALSE; - if (current_focus == priv->browse_files_tree_view) + if (current_focus == priv->browse_files_current_view) { - GtkTreeSelection *selection; - file_list: file_list_seen = TRUE; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info); + + current_selection_selected_foreach (impl, get_files_foreach, &info); /* If there is no selection in the file list, we probably have this situation: * @@ -6189,7 +6577,7 @@ gtk_file_chooser_widget_get_files (GtkFileChooser *chooser) else return NULL; } - else if (priv->toplevel_last_focus_widget == priv->browse_files_tree_view) + else if (priv->toplevel_last_focus_widget == priv->browse_files_current_view) goto file_list; else if (priv->location_entry && priv->toplevel_last_focus_widget == priv->location_entry) goto file_entry; @@ -6467,8 +6855,6 @@ switch_folder_foreach_cb (GtkTreeModel *model, static void switch_to_selected_folder (GtkFileChooserWidget *impl) { - GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection; struct switch_folder_closure closure; /* We do this with foreach() rather than get_selected() as we may be in @@ -6479,8 +6865,7 @@ switch_to_selected_folder (GtkFileChooserWidget *impl) closure.file = NULL; closure.num_selected = 0; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, switch_folder_foreach_cb, &closure); + current_selection_selected_foreach (impl, switch_folder_foreach_cb, &closure); g_assert (closure.file && closure.num_selected == 1); @@ -6498,19 +6883,33 @@ get_selected_file_info_from_file_list (GtkFileChooserWidget *impl, GtkTreeSelection *selection; GtkTreeIter iter; GFileInfo *info; - GtkTreeModel *model; g_assert (!priv->select_multiple); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + + if (priv->view_mode == VIEW_MODE_LIST) { - *had_selection = FALSE; - return NULL; - } + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) + { + *had_selection = FALSE; + return NULL; + } - *had_selection = TRUE; + *had_selection = TRUE; + } + else if (priv->view_mode == VIEW_MODE_ICON) + { + if (!get_selected_tree_iter_from_icon_view (impl, &iter)) + { + *had_selection = FALSE; + return NULL; + } + *had_selection = TRUE; + } + else + g_assert_not_reached(); - info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (model), &iter); + info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (priv->current_model), &iter); return info; } @@ -7007,7 +7406,7 @@ gtk_file_chooser_widget_should_respond (GtkFileChooserEmbed *chooser_embed) current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); - if (current_focus == priv->browse_files_tree_view) + if (current_focus == priv->browse_files_current_view) { /* The following array encodes what we do based on the priv->action and the * number of files selected. @@ -7254,9 +7653,9 @@ gtk_file_chooser_widget_initial_focus (GtkFileChooserEmbed *chooser_embed) { if (priv->location_mode == LOCATION_MODE_PATH_BAR || priv->operation_mode == OPERATION_MODE_RECENT) - widget = priv->browse_files_tree_view; + widget = priv->browse_files_current_view; else - widget = priv->location_entry; + widget = priv->location_entry; } else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) @@ -7269,6 +7668,7 @@ gtk_file_chooser_widget_initial_focus (GtkFileChooserEmbed *chooser_embed) g_assert (widget != NULL); gtk_widget_grab_focus (widget); + create_browse_files_icon_view (impl); } static void @@ -7292,45 +7692,23 @@ selected_foreach_get_file_cb (GtkTreeModel *model, static GSList * get_selected_files (GtkFileChooserWidget *impl) { - GtkFileChooserWidgetPrivate *priv = impl->priv; GSList *result; - GtkTreeSelection *selection; - result = NULL; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, selected_foreach_get_file_cb, &result); + current_selection_selected_foreach (impl, 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 **list; - GFileInfo *info; - - list = data; - - info = _gtk_file_system_model_get_info (GTK_FILE_SYSTEM_MODEL (model), iter); - *list = g_slist_prepend (*list, g_object_ref (info)); -} - static GSList * get_selected_infos (GtkFileChooserWidget *impl) { - GtkFileChooserWidgetPrivate *priv = impl->priv; GSList *result; - GtkTreeSelection *selection; result = NULL; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, selected_foreach_get_info_cb, &result); + current_selection_selected_foreach (impl, selected_foreach_get_file_cb, &result); result = g_slist_reverse (result); return result; @@ -7402,6 +7780,7 @@ search_engine_finished_cb (GtkSearchEngine *engine, gtk_stack_set_visible_child_name (GTK_STACK (priv->browse_files_stack), "empty"); gtk_entry_grab_focus_without_selecting (GTK_ENTRY (priv->search_entry)); } + current_view_set_file_model (impl, GTK_TREE_MODEL (priv->search_model)); } static void @@ -7427,7 +7806,7 @@ search_clear_model (GtkFileChooserWidget *impl, if (remove && gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)) == GTK_TREE_MODEL (priv->search_model)) - gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL); + current_view_set_file_model (impl, NULL); g_clear_object (&priv->search_model); } @@ -7637,7 +8016,7 @@ recent_clear_model (GtkFileChooserWidget *impl, return; if (remove) - gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL); + current_view_set_file_model (impl, NULL); g_set_object (&priv->recent_model, NULL); } @@ -7690,8 +8069,7 @@ recent_idle_cleanup (gpointer data) GtkFileChooserWidget *impl = load_data->impl; GtkFileChooserWidgetPrivate *priv = impl->priv; - gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), - GTK_TREE_MODEL (priv->recent_model)); + current_view_set_file_model (impl, GTK_TREE_MODEL (priv->recent_model)); gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), -1); gtk_tree_view_column_set_sort_column_id (priv->list_name_column, -1); @@ -7839,12 +8217,8 @@ static gboolean recent_should_respond (GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; - GtkTreeSelection *selection; - g_assert (priv->operation_mode == OPERATION_MODE_RECENT); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - return (gtk_tree_selection_count_selected_rows (selection) != 0); + return (current_selection_count_selected_rows (impl) != 0); } static void @@ -7904,29 +8278,40 @@ check_preview_change (GtkFileChooserWidget *impl) GtkTreeModel *model; GtkTreeSelection *selection; - model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); - if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_SINGLE || - gtk_tree_selection_get_mode (selection) == GTK_SELECTION_BROWSE) + model = priv->current_model; + + if (priv->view_mode == VIEW_MODE_LIST) { - GtkTreeIter iter; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_SINGLE || + gtk_tree_selection_get_mode (selection) == GTK_SELECTION_BROWSE) + { + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) + path = gtk_tree_model_get_path (model, &iter); + else + path = NULL; + } - if (gtk_tree_selection_get_selected (selection, NULL, &iter)) - path = gtk_tree_model_get_path (model, &iter); else - path = NULL; - } - else - { - gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), &path, NULL); - if (path && !gtk_tree_selection_path_is_selected (selection, path)) { - gtk_tree_path_free (path); - path = NULL; + gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), &path, NULL); + if (path && !gtk_tree_selection_path_is_selected (selection, path)) + { + gtk_tree_path_free (path); + path = NULL; + } } } + else if (priv->view_mode == VIEW_MODE_ICON) + { + gtk_icon_view_get_cursor (GTK_ICON_VIEW (priv->browse_files_icon_view), &path, NULL); + } + else + g_assert_not_reached (); - if (path) + if (path && model) { GtkTreeIter iter; @@ -8010,15 +8395,13 @@ list_select_func (GtkTreeSelection *selection, return TRUE; } +/* GtkTreeSelection or GtkIconView selection changed. */ static void -list_selection_changed (GtkTreeSelection *selection, - GtkFileChooserWidget *impl) +list_selection_changed (void *selection, + GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; - if (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)) == NULL) - return; - if (priv->location_entry) update_chooser_entry (impl); @@ -8042,16 +8425,35 @@ list_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, GtkFileChooserWidget *impl) +{ + GtkTreeModel *model; + model = gtk_tree_view_get_model (tree_view); + item_activated (model, path, impl); +} + +/* Callback used when a item in the icon file list is activated. */ +static void +icon_item_activated (GtkIconView *icon_view, + GtkTreePath *path, + GtkFileChooserWidget *impl) +{ + GtkTreeModel *model; + model = gtk_icon_view_get_model (icon_view); + item_activated (model, path, impl); +} + +/* Common implementation for list_row_activated and icon_item_activated */ +static void +item_activated (GtkTreeModel *model, + GtkTreePath *path, + GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; 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; @@ -8071,7 +8473,7 @@ list_row_activated (GtkTreeView *tree_view, priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) g_signal_emit_by_name (impl, "file-activated"); - out: + out: if (file) g_object_unref (file); @@ -8102,10 +8504,13 @@ static void update_cell_renderer_attributes (GtkFileChooserWidget *impl) { GtkFileChooserWidgetPrivate *priv = impl->priv; + /* only applicable in the tree view (i.e. list view) */ + if (!priv->browse_files_tree_view) + return; gtk_tree_view_column_set_attributes (priv->list_name_column, priv->list_pixbuf_renderer, - "surface", MODEL_COL_SURFACE, + "surface", MODEL_COL_LIST_SURFACE, "sensitive", MODEL_COL_IS_SENSITIVE, NULL); gtk_tree_view_column_set_attributes (priv->list_name_column, @@ -8688,15 +9093,19 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_files_stack); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, places_sidebar); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, places_view); + gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, view_notebook); + gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_files_list_swin); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_files_tree_view); - gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_files_swin); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_header_revealer); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_header_stack); + gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_files_icon_swin); + gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_files_icon_view); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_new_folder_button); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_path_bar_size_group); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, browse_path_bar); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, filter_combo_hbox); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, filter_combo); + gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, icon_view_scale); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, preview_box); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, extra_align); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, extra_and_filters); @@ -8715,6 +9124,7 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_type_renderer); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_location_column); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_location_renderer); + gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_icon_renderer); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, new_folder_name_entry); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, new_folder_create_button); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, new_folder_error_label); @@ -8724,6 +9134,7 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, rename_file_error_label); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, rename_file_popover); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, remote_warning_bar); + gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, view_mode_combo_box); /* And a *lot* of callbacks to bind ... */ gtk_widget_class_bind_template_callback (widget_class, browse_files_key_press_event_cb); @@ -8738,6 +9149,10 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gtk_widget_class_bind_template_callback (widget_class, file_list_drag_end_cb); gtk_widget_class_bind_template_callback (widget_class, list_selection_changed); gtk_widget_class_bind_template_callback (widget_class, list_cursor_changed); + gtk_widget_class_bind_template_callback (widget_class, icon_item_activated); + gtk_widget_class_bind_template_callback (widget_class, icon_view_scale_value_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, view_mode_combo_box_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, view_notebook_switch_page_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); @@ -8785,6 +9200,18 @@ post_process_ui (GtkFileChooserWidget *impl) GDK_ACTION_COPY | GDK_ACTION_MOVE); gtk_drag_dest_add_uri_targets (impl->priv->browse_files_tree_view); + /* Setup file list iconview */ + gtk_icon_view_enable_model_drag_source (GTK_ICON_VIEW (impl->priv->browse_files_icon_view), + GDK_BUTTON1_MASK, + NULL, 0, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + gtk_drag_source_add_uri_targets (impl->priv->browse_files_icon_view); + gtk_drag_dest_set (impl->priv->browse_files_icon_view, + GTK_DEST_DEFAULT_ALL, + NULL, 0, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + gtk_drag_dest_add_uri_targets (impl->priv->browse_files_icon_view); + /* 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. @@ -8816,6 +9243,7 @@ post_process_ui (GtkFileChooserWidget *impl) * that priv->icon_size be already setup. */ set_icon_cell_renderer_fixed_size (impl); + impl->priv->browse_files_current_view = impl->priv->browse_files_tree_view; atk_obj = gtk_widget_get_accessible (impl->priv->browse_new_folder_button); if (GTK_IS_ACCESSIBLE (atk_obj)) @@ -8867,7 +9295,8 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *impl) priv->show_size_column = TRUE; priv->show_type_column = TRUE; priv->type_format = TYPE_FORMAT_MIME; - priv->icon_size = FALLBACK_ICON_SIZE; + priv->list_view_icon_size = FALLBACK_LIST_VIEW_ICON_SIZE; + priv->icon_view_icon_size = FALLBACK_ICON_VIEW_ICON_SIZE; priv->load_state = LOAD_EMPTY; priv->reload_state = RELOAD_EMPTY; priv->pending_select_files = NULL; @@ -8879,6 +9308,7 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *impl) priv->create_folders = TRUE; priv->auto_selecting_first_row = FALSE; priv->renamed_file = NULL; + priv->view_mode = VIEW_MODE_LIST; /* Ensure GTK+ private types used by the template * definition before calling gtk_widget_init_template() @@ -8926,6 +9356,258 @@ gtk_file_chooser_widget_new (GtkFileChooserAction action) NULL); } +static gboolean +get_selected_tree_iter_from_icon_view (GtkFileChooserWidget *impl, + GtkTreeIter *iter_out) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + GList *icon_selection; + GtkTreePath *icon_selection_path; + + icon_selection = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->browse_files_icon_view)); + if (!icon_selection) + return FALSE; + + icon_selection_path = g_list_nth_data (icon_selection, 0); + gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->current_model), + iter_out, + icon_selection_path); + + g_list_foreach (icon_selection, (GFunc) gtk_tree_path_free, NULL); + g_list_free (icon_selection); + return TRUE; +} + +static void +icon_view_selection_selected_foreach (GtkFileChooserWidget *impl, + GtkTreeSelectionForeachFunc func, + gpointer data) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkTreeIter iter; + GList *icon_selection; + GList *elem; + GtkTreePath *icon_selection_path; + + icon_selection = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->browse_files_icon_view)); + for (elem = icon_selection; elem; elem = elem->next) + { + icon_selection_path = elem->data; + gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->current_model), + &iter, + icon_selection_path); + (* func) (GTK_TREE_MODEL (priv->current_model), + icon_selection_path, + &iter, + data); + } + + g_list_foreach (icon_selection, (GFunc) gtk_tree_path_free, NULL); + g_list_free (icon_selection); +} + +static void +selection_selected_foreach (GtkFileChooserWidget *impl, + ViewMode view, + GtkTreeSelectionForeachFunc func, + gpointer data) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + if (priv->current_model == NULL) + return; + + if (view == VIEW_MODE_LIST) + { + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + gtk_tree_selection_selected_foreach (selection, func, data); + } + else if (view == VIEW_MODE_ICON) + icon_view_selection_selected_foreach (impl, func, data); + else + g_assert_not_reached (); +} + +static void +current_selection_selected_foreach (GtkFileChooserWidget *impl, + GtkTreeSelectionForeachFunc func, + gpointer data) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + selection_selected_foreach (impl, priv->view_mode, func, data); +} + +static guint +current_selection_count_selected_rows (GtkFileChooserWidget *impl) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + if (priv->view_mode == VIEW_MODE_LIST) + { + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + return gtk_tree_selection_count_selected_rows (selection); + } + if (priv->view_mode == VIEW_MODE_ICON) + { + GList *icon_selection; + icon_selection = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (priv->browse_files_icon_view)); + guint count = g_list_length (icon_selection); + g_list_foreach (icon_selection, (GFunc) gtk_tree_path_free, NULL); + g_list_free (icon_selection); + return count; + } + g_assert_not_reached (); + return 0; +} + +static void +selection_select_iter (GtkFileChooserWidget *impl, + GtkTreeIter *iter, + ViewMode target) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + if (target == VIEW_MODE_LIST) + { + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + gtk_tree_selection_select_iter (selection, iter); + } + else if (target == VIEW_MODE_ICON) + { + GtkTreePath *path; + path = gtk_tree_model_get_path (priv->current_model, iter); + gtk_icon_view_select_path (GTK_ICON_VIEW (priv->browse_files_icon_view), path); + gtk_tree_path_free (path); + } + else + g_assert_not_reached (); +} + +static void +current_selection_select_iter (GtkFileChooserWidget *impl, + GtkTreeIter *iter) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + selection_select_iter (impl, iter, priv->view_mode); +} + +struct copy_old_selection_to_current_view_closure { + GtkFileChooserWidget *impl; +}; + +static void +copy_old_selection_to_current_view_foreach_cp (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + struct copy_old_selection_to_current_view_closure *closure; + closure = data; + selection_select_iter (closure->impl, iter, closure->impl->priv->view_mode); +} + +static void +copy_old_selection_to_current_view (GtkFileChooserWidget *impl, + ViewMode old_view_mode) +{ + struct copy_old_selection_to_current_view_closure closure; + closure.impl = impl; + + selection_selected_foreach(impl, + old_view_mode, + copy_old_selection_to_current_view_foreach_cp, + &closure); +} + +static void +current_selection_unselect_iter (GtkFileChooserWidget *impl, + GtkTreeIter *iter) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + if (priv->view_mode == VIEW_MODE_LIST) + { + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + gtk_tree_selection_unselect_iter (selection, iter); + } + else if (priv->view_mode == VIEW_MODE_ICON) + { + GtkTreePath *path; + path = gtk_tree_model_get_path (priv->current_model, iter); + gtk_icon_view_unselect_path (GTK_ICON_VIEW (priv->browse_files_icon_view), path); + gtk_tree_path_free (path); + } + else + g_assert_not_reached (); +} + +static void +current_selection_unselect_all (GtkFileChooserWidget *impl) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + if (priv->view_mode == VIEW_MODE_LIST) + { + GtkTreeSelection *selection; + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + gtk_tree_selection_unselect_all (selection); + } + else if (priv->view_mode == VIEW_MODE_ICON) + gtk_icon_view_unselect_all (GTK_ICON_VIEW (priv->browse_files_icon_view)); + else + g_assert_not_reached (); +} + +static void +current_view_set_file_model (GtkFileChooserWidget *impl, GtkTreeModel *model) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkWidget *view; + + priv->current_model = model; + + if (priv->view_mode == VIEW_MODE_LIST) + view = priv->browse_files_tree_view; + else if (priv->view_mode == VIEW_MODE_ICON) + view = priv->browse_files_icon_view; + else + g_assert_not_reached (); + + g_object_set (view, "model", priv->current_model, NULL); +} + +static void +current_view_set_cursor (GtkFileChooserWidget *impl, GtkTreePath *path) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + if (priv->view_mode == VIEW_MODE_LIST) + gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), path, NULL, FALSE); + else if (priv->view_mode == VIEW_MODE_ICON) + gtk_icon_view_set_cursor (GTK_ICON_VIEW (priv->browse_files_icon_view), path, NULL, FALSE); + else + g_assert_not_reached (); +} + +static void +current_view_set_select_multiple (GtkFileChooserWidget *impl, gboolean select_multiple) +{ + GtkFileChooserWidgetPrivate *priv = impl->priv; + GtkTreeSelection *selection; + GtkSelectionMode mode; + + mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_BROWSE; + + if (priv->view_mode == VIEW_MODE_LIST) + { + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); + gtk_tree_selection_set_mode (selection, mode); + gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (priv->browse_files_tree_view), select_multiple); + } + else if (priv->view_mode == VIEW_MODE_ICON) + gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (priv->browse_files_icon_view), mode); + else + g_assert_not_reached (); +} + static void gtk_file_chooser_widget_add_choice (GtkFileChooser *chooser, const char *id, @@ -9045,4 +9727,3 @@ gtk_file_chooser_widget_get_choice (GtkFileChooser *chooser, return NULL; } - diff --git a/gtk/org.gtk.Settings.FileChooser.gschema.xml b/gtk/org.gtk.Settings.FileChooser.gschema.xml index dda603ab61..d0f275a01e 100644 --- a/gtk/org.gtk.Settings.FileChooser.gschema.xml +++ b/gtk/org.gtk.Settings.FileChooser.gschema.xml @@ -55,6 +55,11 @@ + + + + + "" @@ -63,16 +68,30 @@ 'path-bar' Location mode - Controls whether the file chooser shows just a path bar, or a visible entry + Controls whether the file chooser shows just a path bar, or a visible entry for the filename as well, for the benefit of typing-oriented users. The possible values for these modes are "path-bar" and "filename-entry". + + 'list-view' + Change view mode + + Controls the view mode used. + + + + 48 + Change icon size + + Controls the size of the icons in icon view mode. + + false Show hidden files - Controls whether the file chooser shows hidden files or not. + Controls whether the file chooser shows hidden files or not. @@ -91,37 +110,37 @@ true Show file sizes - Controls whether the file chooser shows a column with file sizes. + Controls whether the file chooser shows a column with file sizes. true Show file types - Controls whether the file chooser shows a column with file types. + Controls whether the file chooser shows a column with file types. 'name' Sort column - Can be one of "name", "modified", or "size". It controls - which of the columns in the file chooser is used for sorting - the list of files. + Can be one of "name", "modified", or "size". It controls + which of the columns in the file chooser is used for sorting + the list of files. 'ascending' Sort order - Can be one of the strings "ascending" or "descending". + Can be one of the strings "ascending" or "descending". (-1, -1) Window position - The (x, y) coordinates of the upper-left corner of the GtkFileChooserDialog's + The (x, y) coordinates of the upper-left corner of the GtkFileChooserDialog's window. @@ -129,23 +148,23 @@ (-1, -1) Window size - The size (width, height) of the GtkFileChooserDialog's window, in pixels. + The size (width, height) of the GtkFileChooserDialog's window, in pixels. 'recent' Startup mode - Either "recent" or "cwd"; controls whether the file chooser - starts up showing the list of recently-used files, or the - contents of the current working directory. + Either "recent" or "cwd"; controls whether the file chooser + starts up showing the list of recently-used files, or the + contents of the current working directory. 148 Sidebar width - Width in pixels of the file chooser's places sidebar. + Width in pixels of the file chooser's places sidebar. diff --git a/gtk/ui/gtkfilechooserwidget.ui b/gtk/ui/gtkfilechooserwidget.ui index 01d4075f6c..269a95fd4b 100644 --- a/gtk/ui/gtkfilechooserwidget.ui +++ b/gtk/ui/gtkfilechooserwidget.ui @@ -8,6 +8,56 @@ 1 vertical + + + 1 + + + + List View + Icon View + + 0 + 1 + Select filechooser view + start + start + + + + + 0 + start + + + + + 256 + 32 + 32 + 16 + + + + + 0 + horizontal + icon_view_icon_size + 1 + end + fill + 10 + 100 + False + + + + 1 + end + + + + 1 @@ -156,110 +206,172 @@ 1 vertical - + 1 - never + False + - + 1 - 1 - 0 - - - Files - - - - - - - - - - - - - - - - - - + never - - Name - 1 - 1 + + True + True + True + False + + + + + + + + + + + + + + - - 6 + + True + Name + True + + + 6 + + + + + 10 + end + + - - 10 - end + + Location + 1 + 0 + 1 + + + 0 + 10 + start + 6 + + - - - - - Location - 1 - 0 - 1 - - 0 - 10 - start - 6 + + Size + fixed + + + 0 + 6 + + - - - - - Size - fixed - - 0 - 6 + + Type + 1 + + + 0 + 6 + + - - - - - Type - 1 - - 0 - 6 + + Modified + fixed + + + 6 + + + + + 6 + + + + + + + True + False + List View + + + False + + + + + True + True + in + GTK_POLICY_AUTOMATIC - - Modified - fixed + + True + True + True + 2 + + + + + + + + - - 6 + - - 6 - + + + 1 + + + + + True + False + Icon View + + + 1 + False + + + + + + + @@ -378,7 +490,7 @@ 1 - 1 + 2