diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index b19ee1adc1..5f1ccdb8ad 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,7 @@ struct _GtkFileChooserImplDefault GtkFileSystem *file_system; GtkFileSystemModel *tree_model; GtkFileSystemModel *list_model; + GtkTreeModelSort *sort_model; GtkFileChooserAction action; @@ -197,6 +199,7 @@ gtk_file_chooser_impl_default_init (GtkFileChooserImplDefault *impl) gtk_widget_show (impl->list_scrollwin); impl->list = gtk_tree_view_new (); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->list), TRUE); gtk_container_add (GTK_CONTAINER (impl->list_scrollwin), impl->list); gtk_widget_show (impl->list); @@ -379,6 +382,22 @@ gtk_file_chooser_impl_default_get_current_folder (GtkFileChooser *chooser) return NULL; } +static void +select_func (GtkFileSystemModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkFileChooserImplDefault *impl = user_data; + GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list); + GtkTreePath *sorted_path; + + sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, path); + gtk_tree_view_set_cursor (tree_view, sorted_path, NULL, FALSE); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->tree), sorted_path, NULL, TRUE, 0.3, 0.0); + gtk_tree_path_free (sorted_path); +} + static void gtk_file_chooser_impl_default_select_uri (GtkFileChooser *chooser, const char *uri) @@ -398,7 +417,7 @@ gtk_file_chooser_impl_default_select_uri (GtkFileChooser *chooser, gtk_file_chooser_set_current_folder_uri (chooser, parent_uri); g_free (parent_uri); _gtk_file_system_model_uri_do (impl->list_model, uri, - expand_and_select_func, impl); + select_func, impl); } } @@ -410,9 +429,13 @@ unselect_func (GtkFileSystemModel *model, { GtkFileChooserImplDefault *impl = user_data; GtkTreeView *tree_view = GTK_TREE_VIEW (impl->list); + GtkTreePath *sorted_path; + sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, + path); gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view), - path); + sorted_path); + gtk_tree_path_free (sorted_path); } static void @@ -451,12 +474,20 @@ get_uris_foreach (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { + GtkTreePath *child_path; + GtkTreeIter child_iter; + const gchar *uri; + struct { GSList *result; GtkFileChooserImplDefault *impl; } *info = data; - - const gchar *uri = _gtk_file_system_model_get_uri (info->impl->tree_model, iter); + + child_path = gtk_tree_model_sort_convert_path_to_child_path (info->impl->sort_model, path); + gtk_tree_model_get_iter (GTK_TREE_MODEL (info->impl->list_model), &child_iter, child_path); + gtk_tree_path_free (child_path); + + uri = _gtk_file_system_model_get_uri (info->impl->tree_model, &child_iter); info->result = g_slist_prepend (info->result, g_strdup (uri)); } @@ -478,6 +509,91 @@ gtk_file_chooser_impl_default_get_uris (GtkFileChooser *chooser) return g_slist_reverse (info.result); } +static gint +name_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + GtkFileChooserImplDefault *impl = user_data; + const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->tree_model, a); + const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->tree_model, b); + + return g_utf8_collate (gtk_file_info_get_display_name (info_a), gtk_file_info_get_display_name (info_b)); +} + +static gint +size_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + GtkFileChooserImplDefault *impl = user_data; + const GtkFileInfo *info_a = _gtk_file_system_model_get_info (impl->tree_model, a); + const GtkFileInfo *info_b = _gtk_file_system_model_get_info (impl->tree_model, b); + gint64 size_a = gtk_file_info_get_size (info_a); + gint64 size_b = gtk_file_info_get_size (info_b); + + return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1); +} + +static void +open_and_close (GtkTreeView *tree_view, + GtkTreePath *target_path) +{ + GtkTreeModel *model = gtk_tree_view_get_model (tree_view); + GtkTreeIter iter; + GtkTreePath *path; + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, 0); + + gtk_tree_model_get_iter (model, &iter, path); + + while (TRUE) + { + if (gtk_tree_path_is_ancestor (path, target_path) || + gtk_tree_path_compare (path, target_path) == 0) + { + GtkTreeIter child_iter; + gtk_tree_view_expand_row (tree_view, path, FALSE); + if (gtk_tree_model_iter_children (model, &child_iter, &iter)) + { + iter = child_iter; + gtk_tree_path_down (path); + goto next; + } + } + else + gtk_tree_view_collapse_row (tree_view, path); + + while (TRUE) + { + GtkTreeIter parent_iter; + GtkTreeIter next_iter; + + next_iter = iter; + if (gtk_tree_model_iter_next (model, &next_iter)) + { + iter = next_iter; + gtk_tree_path_next (path); + goto next; + } + + if (!gtk_tree_model_iter_parent (model, &parent_iter, &iter)) + goto out; + + iter = parent_iter; + gtk_tree_path_up (path); + } + next: + ; + } + + out: + gtk_tree_path_free (path); +} + static void tree_selection_changed (GtkTreeSelection *selection, GtkFileChooserImplDefault *impl) @@ -488,21 +604,42 @@ tree_selection_changed (GtkTreeSelection *selection, { g_object_unref (impl->list_model); impl->list_model = NULL; + + g_object_unref (impl->sort_model); + impl->sort_model = NULL; } if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { - const gchar *uri = _gtk_file_system_model_get_uri (impl->tree_model, &iter); + GtkTreePath *path; + const gchar *uri; + + /* Close the tree up to only the parents of the newly selected + * node and it's immediate children visible. + */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->tree_model), &iter); + open_and_close (GTK_TREE_VIEW (impl->tree), path); + gtk_tree_path_free (path); + + /* Now update the list view to show the new row. + */ + uri = _gtk_file_system_model_get_uri (impl->tree_model, &iter); impl->list_model = _gtk_file_system_model_new (impl->file_system, uri, 0, - GTK_FILE_INFO_DISPLAY_NAME); - + GTK_FILE_INFO_DISPLAY_NAME | + GTK_FILE_INFO_SIZE); _gtk_file_system_model_set_show_folders (impl->list_model, FALSE); + + impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->list_model)); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), 0, name_sort_func, impl, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), 1, size_sort_func, impl, NULL); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), + name_sort_func, impl, NULL); } gtk_tree_view_set_model (GTK_TREE_VIEW (impl->list), - GTK_TREE_MODEL (impl->list_model)); + GTK_TREE_MODEL (impl->sort_model)); g_signal_emit_by_name (impl, "current_folder_changed", 0); g_signal_emit_by_name (impl, "selection_changed", 0); @@ -515,12 +652,25 @@ list_selection_changed (GtkTreeSelection *selection, g_signal_emit_by_name (impl, "selection_changed", 0); } +const GtkFileInfo * +get_list_file_info (GtkFileChooserImplDefault *impl, + GtkTreeIter *iter) +{ + GtkTreeIter child_iter; + + gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, + &child_iter, + iter); + + return _gtk_file_system_model_get_info (impl->tree_model, &child_iter); +} + static void -name_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) +tree_name_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) { GtkFileChooserImplDefault *impl = data; const GtkFileInfo *info = _gtk_file_system_model_get_info (impl->tree_model, iter); @@ -533,29 +683,95 @@ name_data_func (GtkTreeViewColumn *tree_column, } } +static void +list_name_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + GtkFileChooserImplDefault *impl = data; + const GtkFileInfo *info = get_list_file_info (impl, iter); + + if (info) + { + g_object_set (cell, + "text", gtk_file_info_get_display_name (info), + NULL); + } +} + +static void +list_size_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *tree_model, + GtkTreeIter *iter, + gpointer data) +{ + GtkFileChooserImplDefault *impl = data; + const GtkFileInfo *info = get_list_file_info (impl, iter); + + if (info) + { + gint64 size = gtk_file_info_get_size (info); + gchar *str; + + if (size < (gint64)1024) + str = g_strdup_printf ("%d bytes", (gint)size); + else if (size < (gint64)1024*1024) + str = g_strdup_printf ("%.1f K", size / (1024.)); + else if (size < (gint64)1024*1024*1024) + str = g_strdup_printf ("%.1f M", size / (1024.*1024.)); + else + str = g_strdup_printf ("%.1f G", size / (1024.*1024.*1024.)); + + g_object_set (cell, + "text", str, + NULL); + + g_free (str); + } + +} + GtkWidget * _gtk_file_chooser_impl_default_new (GtkFileSystem *file_system) { GtkWidget *result = g_object_new (GTK_TYPE_FILE_CHOOSER_IMPL_DEFAULT, NULL); GtkFileChooserImplDefault *impl = GTK_FILE_CHOOSER_IMPL_DEFAULT (result); + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; impl->file_system = file_system; - impl->tree_model = _gtk_file_system_model_new (file_system, NULL, -1, GTK_FILE_INFO_DISPLAY_NAME); + impl->tree_model = _gtk_file_system_model_new (file_system, NULL, -1, + GTK_FILE_INFO_DISPLAY_NAME); _gtk_file_system_model_set_show_files (impl->tree_model, FALSE); gtk_tree_view_set_model (GTK_TREE_VIEW (impl->tree), GTK_TREE_MODEL (impl->tree_model)); gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->tree), 0, - "Name", + "File name", gtk_cell_renderer_text_new (), - name_data_func, impl, NULL); + tree_name_data_func, impl, NULL); - gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->list), 0, - "Name", - gtk_cell_renderer_text_new (), - name_data_func, impl, NULL); + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, "File name"); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_cell_data_func (column, renderer, + list_name_data_func, impl, NULL); + gtk_tree_view_column_set_sort_column_id (column, 0); + gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column); + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, "Size"); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_cell_data_func (column, renderer, + list_size_data_func, impl, NULL); + gtk_tree_view_column_set_sort_column_id (column, 1); + gtk_tree_view_append_column (GTK_TREE_VIEW (impl->list), column); return result; } - diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c index 04a6002ff2..f2faa570fc 100644 --- a/gtk/gtkfilesystem.c +++ b/gtk/gtkfilesystem.c @@ -204,7 +204,7 @@ gtk_file_info_set_size (GtkFileInfo *info, gint64 size) { g_return_if_fail (info != NULL); - g_return_if_fail (size < 0); + g_return_if_fail (size >= 0); info->size = size; } diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c index b7a29fe1e3..5274be2de9 100644 --- a/gtk/gtkfilesystemmodel.c +++ b/gtk/gtkfilesystemmodel.c @@ -563,6 +563,8 @@ _gtk_file_system_model_new (GtkFileSystem *file_system, } else roots = gtk_file_system_list_roots (file_system); + + roots = g_slist_sort (roots, (GCompareFunc)strcmp); for (tmp_list = roots; tmp_list; tmp_list = tmp_list->next) { @@ -1099,6 +1101,8 @@ file_model_node_get_children (GtkFileSystemModel *model, if (gtk_file_folder_list_children (node->folder, &child_uris, NULL)) /* NULL-GError */ { + child_uris = g_slist_sort (child_uris, (GCompareFunc)strcmp); + for (tmp_list = child_uris; tmp_list; tmp_list = tmp_list->next) { FileModelNode *child_node = file_model_node_new (tmp_list->data); diff --git a/gtk/gtkfilesystemunix.c b/gtk/gtkfilesystemunix.c index dfed7d5ccb..69f2d9e6c2 100644 --- a/gtk/gtkfilesystemunix.c +++ b/gtk/gtkfilesystemunix.c @@ -547,7 +547,7 @@ filename_get_info (const gchar *filename, if (types & GTK_FILE_INFO_SIZE) { - gtk_file_info_set_size (info, (gint64)512 * (gint16)statbuf.st_blocks); + gtk_file_info_set_size (info, (gint64)statbuf.st_size); } if (types & GTK_FILE_INFO_ICON)