[file-roller: 7/26] file selector: populate the place list



commit b19f2b1da54cefc7926c01ae3fae818aa09104df
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Wed Aug 8 18:11:04 2012 +0200

    file selector: populate the place list

 src/fr-file-selector-dialog.c |  894 +++++++++++++++++++++++++++++++++++++----
 src/ui/file-selector.ui       |   83 ++--
 2 files changed, 863 insertions(+), 114 deletions(-)
---
diff --git a/src/fr-file-selector-dialog.c b/src/fr-file-selector-dialog.c
index de8f0e5..e2548c1 100644
--- a/src/fr-file-selector-dialog.c
+++ b/src/fr-file-selector-dialog.c
@@ -48,14 +48,26 @@ enum {
 };
 
 
+typedef enum {
+	PLACE_TYPE_NORMAL,
+	PLACE_TYPE_VOLUME,
+	PLACE_TYPE_BOOKMARK
+} PlaceType;
+
+
 enum {
 	PLACE_LIST_COLUMN_ICON,
 	PLACE_LIST_COLUMN_NAME,
 	PLACE_LIST_COLUMN_FILE,
-	PLACE_LIST_COLUMN_IS_SEPARATOR
+	PLACE_LIST_COLUMN_IS_SEPARATOR,
+	PLACE_LIST_COLUMN_TYPE,
+	PLACE_LIST_COLUMN_SORT_ORDER
 };
 
 
+/* -- load_data  -- */
+
+
 typedef struct {
 	FrFileSelectorDialog *dialog;
 	GFile                *folder;
@@ -64,19 +76,6 @@ typedef struct {
 } LoadData;
 
 
-struct _FrFileSelectorDialogPrivate {
-	GtkBuilder    *builder;
-	GtkWidget     *extra_widget;
-	GFile         *current_folder;
-	LoadData      *current_operation;
-	GthIconCache  *icon_cache;
-	GSettings     *settings;
-};
-
-
-/* -- load_data  -- */
-
-
 static LoadData *
 load_data_new (FrFileSelectorDialog *dialog,
 	       GFile                *folder)
@@ -85,9 +84,9 @@ load_data_new (FrFileSelectorDialog *dialog,
 
 	load_data = g_new (LoadData, 1);
 	load_data->dialog = g_object_ref (dialog);
-	load_data->folder = g_object_ref (folder);
+	load_data->folder = _g_object_ref (folder);
 	load_data->cancellable = g_cancellable_new ();
-	file_info_list_free (load_data->files);
+	load_data->files = NULL;
 
 	return load_data;
 }
@@ -99,19 +98,58 @@ load_data_free (LoadData *load_data)
 	if (load_data == NULL)
 		return;
 
-	if (load_data->dialog->priv->current_operation == load_data)
-		load_data->dialog->priv->current_operation = NULL;
-
 	g_object_unref (load_data->dialog);
-	g_object_unref (load_data->folder);
+	_g_object_unref (load_data->folder);
 	g_object_unref (load_data->cancellable);
+	file_info_list_free (load_data->files);
 	g_free (load_data);
 }
 
 
+/*-- bookmarks -- */
+
+
+typedef struct {
+	GFile *file;
+	char  *name;
+} Bookmark;
+
+
+static void
+bookmark_free (Bookmark *b)
+{
+	if (b == NULL)
+		return;
+	_g_object_unref (b->file);
+	g_free (b->name);
+	g_slice_free (Bookmark, b);
+}
+
+
+static void
+bookmark_list_free (GList *list)
+{
+	g_list_foreach (list, (GFunc) bookmark_free, NULL);
+	g_list_free (list);
+}
+
+
 /* -- fr_file_selector_dialog -- */
 
 
+struct _FrFileSelectorDialogPrivate {
+	GtkBuilder    *builder;
+	GtkWidget     *extra_widget;
+	GFile         *current_folder;
+	LoadData      *current_operation;
+	LoadData      *places_operation;
+	GthIconCache  *icon_cache;
+	GSettings     *settings;
+	GList         *special_places;
+	GList         *bookmarks;
+};
+
+
 static void
 fr_file_selector_dialog_finalize (GObject *object)
 {
@@ -122,11 +160,431 @@ fr_file_selector_dialog_finalize (GObject *object)
 	_g_object_unref (self->priv->current_folder);
 	gth_icon_cache_free (self->priv->icon_cache);
 	g_object_unref (self->priv->settings);
+	_g_object_list_unref (self->priv->special_places);
+	bookmark_list_free (self->priv->bookmarks);
 
 	G_OBJECT_CLASS (fr_file_selector_dialog_parent_class)->finalize (object);
 }
 
 
+static GList *
+get_system_bookmarks (void)
+{
+	char  *filename;
+	GFile *file;
+	GList *list;
+	char  *contents;
+
+	filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL);
+	file = g_file_new_for_path (filename);
+	if (! g_file_query_exists (file, NULL)) {
+		g_free (filename);
+		g_object_unref (file);
+
+		filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
+		file = g_file_new_for_path (filename);
+	}
+
+	list = NULL;
+	if (g_file_load_contents (file, NULL, &contents, NULL, NULL, NULL)) {
+		char **lines;
+		int    i;
+
+		lines = g_strsplit (contents, "\n", -1);
+		for (i = 0; lines[i] != NULL; i++) {
+			Bookmark *bookmark;
+			char     *space;
+
+			if (lines[i][0] == '\0')
+				continue;
+
+			bookmark = g_slice_new0 (Bookmark);
+
+			if ((space = strchr (lines[i], ' ')) != NULL) {
+				space[0] = '\0';
+				bookmark->name = g_strdup (space + 1);
+			}
+			bookmark->file = g_file_new_for_uri (lines[i]);
+
+			list = g_list_prepend (list, bookmark);
+		}
+
+		g_strfreev (lines);
+		g_free (contents);
+	}
+
+	g_object_unref (file);
+	g_free (filename);
+
+	return g_list_reverse (list);
+}
+
+
+static void
+_gtk_list_store_clear_type (GtkListStore *list_store,
+			    PlaceType     type_to_delete)
+{
+	GtkTreeIter iter;
+	gboolean    iter_is_valid;
+
+	iter_is_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter);
+		return;
+
+	while (iter_is_valid) {
+		PlaceType item_type;
+
+	        gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter,
+	        		    PLACE_LIST_COLUMN_TYPE, &item_type,
+	                            -1);
+
+	        if (item_type == type_to_delete)
+	        	iter_is_valid = gtk_list_store_remove (list_store, &iter);
+	        else
+	        	iter_is_valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter);
+	}
+}
+
+
+static void
+update_bookmarks (FrFileSelectorDialog *self)
+{
+	GtkListStore *list_store;
+	GList        *scan;
+	GtkTreeIter   iter;
+	int           sort_order = 0;
+
+	bookmark_list_free (self->priv->bookmarks);
+	self->priv->bookmarks = get_system_bookmarks ();
+
+	list_store = GTK_LIST_STORE (GET_WIDGET ("places_liststore"));
+	_gtk_list_store_clear_type (list_store, PLACE_TYPE_BOOKMARK);
+
+	/* separator */
+
+	gtk_list_store_append (list_store, &iter);
+	gtk_list_store_set (list_store, &iter,
+			    PLACE_LIST_COLUMN_IS_SEPARATOR, TRUE,
+			    PLACE_LIST_COLUMN_TYPE, PLACE_TYPE_BOOKMARK,
+			    PLACE_LIST_COLUMN_SORT_ORDER, sort_order++,
+			    -1);
+
+
+	for (scan = self->priv->bookmarks; scan; scan = scan->next) {
+		Bookmark  *bookmark = scan->data;
+		char      *name;
+		GIcon     *icon;
+		GdkPixbuf *icon_pixbuf;
+
+		name = g_strdup (bookmark->name);
+
+		if (g_file_is_native (bookmark->file)) {
+			GFileInfo *info;
+
+			info = g_file_query_info (bookmark->file,
+						  (G_FILE_ATTRIBUTE_STANDARD_NAME ","
+						   G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
+						   G_FILE_ATTRIBUTE_STANDARD_ICON),
+						  0,
+						  NULL,
+						  NULL);
+
+			if (info == NULL) {
+				g_free (name);
+				continue;
+			}
+
+			if (name == NULL)
+				name = g_strdup (g_file_info_get_display_name (info));
+			icon = g_object_ref (g_file_info_get_icon (info));
+
+			g_object_unref (info);
+		}
+		else {
+			if (name == NULL)
+				name = _g_file_get_display_basename (bookmark->file);
+			icon = g_themed_icon_new ("folder-remote");
+		}
+
+		gtk_list_store_append (list_store, &iter);
+
+		icon_pixbuf = gth_icon_cache_get_pixbuf (self->priv->icon_cache, icon);
+		gtk_list_store_set (list_store, &iter,
+				    PLACE_LIST_COLUMN_ICON, icon_pixbuf,
+				    PLACE_LIST_COLUMN_NAME, name,
+				    PLACE_LIST_COLUMN_FILE, bookmark->file,
+				    PLACE_LIST_COLUMN_TYPE, PLACE_TYPE_BOOKMARK,
+				    PLACE_LIST_COLUMN_SORT_ORDER, sort_order++,
+				    -1);
+
+		g_object_unref (icon_pixbuf);
+		g_object_unref (icon);
+		g_free (name);
+	}
+}
+
+
+static void
+update_places_list_ready_cb (GList    *files, /* FileInfo list */
+			     GError   *error,
+			     gpointer  user_data)
+{
+	LoadData             *load_data = user_data;
+	FrFileSelectorDialog *self = load_data->dialog;
+	GtkListStore         *list_store;
+	GList                *scan;
+	GtkTreeIter           iter;
+	int                   sort_order = 0;
+
+	/* normal places */
+
+	list_store = GTK_LIST_STORE (GET_WIDGET ("places_liststore"));
+	_gtk_list_store_clear_type (list_store, PLACE_TYPE_NORMAL);
+	_gtk_list_store_clear_type (list_store, PLACE_TYPE_VOLUME);
+
+	for (scan = files; scan; scan = scan->next) {
+		FileInfo  *file_info = scan->data;
+		GdkPixbuf *icon_pixbuf;
+
+		gtk_list_store_append (list_store, &iter);
+
+		icon_pixbuf = gth_icon_cache_get_pixbuf (self->priv->icon_cache, g_file_info_get_icon (file_info->info));
+		gtk_list_store_set (list_store, &iter,
+				    PLACE_LIST_COLUMN_ICON, icon_pixbuf,
+				    PLACE_LIST_COLUMN_NAME, g_file_info_get_display_name (file_info->info),
+				    PLACE_LIST_COLUMN_FILE, file_info->file,
+				    PLACE_LIST_COLUMN_TYPE, PLACE_TYPE_NORMAL,
+				    PLACE_LIST_COLUMN_SORT_ORDER, sort_order++,
+				    -1);
+
+		g_object_unref (icon_pixbuf);
+	}
+
+	/* root filesystem */
+
+	{
+		GIcon     *icon;
+		GdkPixbuf *icon_pixbuf;
+		GFile     *file;
+
+		gtk_list_store_append (list_store, &iter);
+
+		icon = g_themed_icon_new ("drive-harddisk");
+		icon_pixbuf = gth_icon_cache_get_pixbuf (self->priv->icon_cache, icon);
+		file = g_file_new_for_path ("file:///");
+		gtk_list_store_set (list_store, &iter,
+				    PLACE_LIST_COLUMN_ICON, icon_pixbuf,
+				    PLACE_LIST_COLUMN_NAME, N_("File System"),
+				    PLACE_LIST_COLUMN_FILE, file,
+				    PLACE_LIST_COLUMN_TYPE, PLACE_TYPE_VOLUME,
+				    PLACE_LIST_COLUMN_SORT_ORDER, sort_order++,
+				    -1);
+
+		g_object_unref (icon_pixbuf);
+		g_object_unref (icon);
+		g_object_unref (file);
+	}
+
+	/* drives / volumes / mounts */
+
+	for (scan = self->priv->special_places; scan; scan = scan->next) {
+		GObject   *place = scan->data;
+		GIcon     *icon;
+		char      *name;
+		GFile     *file;
+		GdkPixbuf *icon_pixbuf;
+
+		gtk_list_store_append (list_store, &iter);
+
+		if (G_IS_DRIVE (place)) {
+			icon = g_drive_get_icon (G_DRIVE (place));
+			name = g_drive_get_name (G_DRIVE (place));
+			file = NULL;
+		}
+		else if (G_IS_VOLUME (place)) {
+			icon = g_volume_get_icon (G_VOLUME (place));
+			name = g_volume_get_name (G_VOLUME (place));
+			file = NULL;
+		}
+		else if (G_IS_MOUNT (place)) {
+			icon = g_mount_get_icon (G_MOUNT (place));
+			name = g_mount_get_name (G_MOUNT (place));
+			file = g_mount_get_root (G_MOUNT (place));
+		}
+		else
+			continue;
+
+		icon_pixbuf = gth_icon_cache_get_pixbuf (self->priv->icon_cache, icon);
+		gtk_list_store_set (list_store, &iter,
+				    PLACE_LIST_COLUMN_ICON, icon_pixbuf,
+				    PLACE_LIST_COLUMN_NAME, name,
+				    PLACE_LIST_COLUMN_FILE, file,
+				    PLACE_LIST_COLUMN_TYPE, PLACE_TYPE_VOLUME,
+				    PLACE_LIST_COLUMN_SORT_ORDER, sort_order++,
+				    -1);
+
+		g_object_unref (icon_pixbuf);
+		_g_object_unref (icon);
+		g_free (name);
+		_g_object_unref (file);
+	}
+
+	if (load_data->dialog->priv->places_operation == load_data)
+		load_data->dialog->priv->places_operation = NULL;
+
+	load_data_free (load_data);
+}
+
+
+static gboolean
+mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount)
+{
+  GList *l;
+  GFile *mount_root;
+  gboolean ret;
+
+  ret = FALSE;
+
+  mount_root = g_mount_get_root (mount);
+
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      GVolume *volume = G_VOLUME (l->data);
+      GFile *volume_activation_root;
+
+      volume_activation_root = g_volume_get_activation_root (volume);
+      if (volume_activation_root != NULL)
+        {
+          if (g_file_has_prefix (volume_activation_root, mount_root))
+            {
+              ret = TRUE;
+              g_object_unref (volume_activation_root);
+              break;
+            }
+          g_object_unref (volume_activation_root);
+        }
+    }
+
+  g_object_unref (mount_root);
+  return ret;
+}
+
+
+static void
+update_places_list (FrFileSelectorDialog *self)
+{
+	GList *places;
+	GList *drives;
+	GList *volumes;
+	GList *mounts;
+	GList *scan;
+
+	if (self->priv->places_operation != NULL)
+		g_cancellable_cancel (self->priv->places_operation->cancellable);
+
+	self->priv->places_operation = load_data_new (self, NULL);
+
+	/* drives / volumes /mounts */
+
+	_g_object_list_unref (self->priv->special_places);
+	self->priv->special_places = NULL;
+
+	/* connected drives */
+
+	drives = g_volume_monitor_get_connected_drives (g_volume_monitor_get ());
+	for (scan = drives; scan; scan = scan->next) {
+		GDrive *drive = scan->data;
+		GList  *volumes;
+		GList  *scan_volume;
+
+		volumes = g_drive_get_volumes (drive);
+		if (volumes != NULL) {
+			for (scan_volume = volumes; scan_volume; scan_volume = scan_volume->next) {
+				GVolume *volume = scan_volume->data;
+				GMount  *mount;
+
+				mount = g_volume_get_mount (volume);
+				if (mount != NULL)
+					self->priv->special_places = g_list_prepend (self->priv->special_places, mount);
+				else
+					self->priv->special_places = g_list_prepend (self->priv->special_places, g_object_ref (volume));
+			}
+			_g_object_list_unref (volumes);
+		}
+		else if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
+			self->priv->special_places = g_list_prepend (self->priv->special_places, g_object_ref (drive));
+	}
+	_g_object_list_unref (drives);
+
+	/* special_places */
+
+	volumes = g_volume_monitor_get_volumes (g_volume_monitor_get ());
+	for (scan = volumes; scan; scan = scan->next) {
+		GVolume *volume = scan->data;
+		GDrive  *drive;
+		GMount  *mount;
+
+		drive = g_volume_get_drive (volume);
+		if (drive != NULL) {
+			g_object_unref (drive);
+			continue;
+		}
+
+		mount = g_volume_get_mount (volume);
+		if (mount != NULL)
+			self->priv->special_places = g_list_prepend (self->priv->special_places, mount);
+		else
+			self->priv->special_places = g_list_prepend (self->priv->special_places, g_object_ref (volume));
+	}
+
+	/* mounts */
+
+	mounts = g_volume_monitor_get_mounts (g_volume_monitor_get ());
+	for (scan = mounts; scan; scan = scan->next) {
+		GMount  *mount = scan->data;
+		GVolume *volume;
+
+		volume = g_mount_get_volume (mount);
+		if (volume != NULL) {
+			g_object_unref (volume);
+			continue;
+		}
+
+		if (mount_referenced_by_volume_activation_root (volumes, mount)) {
+			g_object_unref (mount);
+			continue;
+		}
+
+		self->priv->special_places = g_list_prepend (self->priv->special_places, g_object_ref (mount));
+	}
+
+	self->priv->special_places = g_list_reverse (self->priv->special_places);
+
+	_g_object_list_unref (mounts);
+	_g_object_list_unref (volumes);
+
+	/* other resourses */
+
+	places = NULL;
+	places = g_list_prepend (places, g_object_ref (_g_file_get_home ()));
+	places = g_list_prepend (places, g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)));
+	places = g_list_reverse (places);
+	_g_file_list_query_info_async (places,
+				       FILE_LIST_DEFAULT,
+				       (G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+					G_FILE_ATTRIBUTE_STANDARD_NAME ","
+					G_FILE_ATTRIBUTE_STANDARD_SIZE ","
+					G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
+					G_FILE_ATTRIBUTE_STANDARD_ICON),
+				       self->priv->places_operation->cancellable,
+				       NULL,
+				       NULL,
+				       update_places_list_ready_cb,
+				       self->priv->places_operation);
+
+	_g_object_list_unref (places);
+}
+
+
 /* Taken from the Gtk+ file gtkfilechooserdefault.c
  * Copyright (C) 2003, Red Hat, Inc.  */
 
@@ -194,19 +652,28 @@ fr_file_selector_dialog_get_default_size (FrFileSelectorDialog *self,
 
 
 static void
+_fr_file_selector_dialog_update_size (FrFileSelectorDialog *self)
+{
+	int default_width;
+	int default_height;
+
+	fr_file_selector_dialog_get_default_size (self, &default_width, &default_height);
+	gtk_window_set_default_size (GTK_WINDOW (self), default_width, default_height);
+}
+
+
+static void
 fr_file_selector_dialog_realize (GtkWidget *widget)
 {
 	FrFileSelectorDialog *self;
-	int                   default_width;
-	int                   default_height;
 
 	GTK_WIDGET_CLASS (fr_file_selector_dialog_parent_class)->realize (widget);
 
 	self = FR_FILE_SELECTOR_DIALOG (widget);
+	_fr_file_selector_dialog_update_size (self);
 
-	fr_file_selector_dialog_get_default_size (self, &default_width, &default_height);
-	gtk_window_set_default_size (GTK_WINDOW (self), default_width, default_height);
-
+	update_places_list (self);
+	update_bookmarks (self);
 }
 
 
@@ -247,16 +714,19 @@ fr_file_selector_dialog_class_init (FrFileSelectorDialogClass *klass)
 
 
 static gint
-compare_name_func (GtkTreeModel *model,
-		   GtkTreeIter  *a,
-		   GtkTreeIter  *b,
-		   gpointer      user_data)
+files_name_column_sort_func (GtkTreeModel *model,
+			     GtkTreeIter  *a,
+			     GtkTreeIter  *b,
+			     gpointer      user_data)
 {
-        char     *key_a;
-        char     *key_b;
-        gboolean  is_folder_a;
-        gboolean  is_folder_b;
-        gint      result;
+	GtkSortType  sort_order;
+        char        *key_a;
+        char        *key_b;
+        gboolean     is_folder_a;
+        gboolean     is_folder_b;
+        gint         result;
+
+	gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), NULL, &sort_order);
 
         gtk_tree_model_get (model, a,
         		    FILE_LIST_COLUMN_NAME_ORDER, &key_a,
@@ -267,12 +737,64 @@ compare_name_func (GtkTreeModel *model,
         		    FILE_LIST_COLUMN_IS_FOLDER, &is_folder_b,
                             -1);
 
-        if (is_folder_a == is_folder_b)
+        if (is_folder_a == is_folder_b) {
         	result = strcmp (key_a, key_b);
-        else if (is_folder_a)
-        	return -1;
-        else
-        	return 1;
+        }
+        else {
+        	result = is_folder_a ? -1 : 1;
+        	if (sort_order == GTK_SORT_DESCENDING)
+        		result = -1 * result;
+        }
+
+        g_free (key_a);
+        g_free (key_b);
+
+        return result;
+}
+
+
+static gint
+files_size_column_sort_func (GtkTreeModel *model,
+			     GtkTreeIter  *a,
+			     GtkTreeIter  *b,
+			     gpointer      user_data)
+{
+	GtkSortType  sort_order;
+        char        *key_a;
+        char        *key_b;
+        gint64       size_a;
+        gint64       size_b;
+        gboolean     is_folder_a;
+        gboolean     is_folder_b;
+        int          result;
+
+        gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), NULL, &sort_order);
+
+        gtk_tree_model_get (model, a,
+        		    FILE_LIST_COLUMN_NAME_ORDER, &key_a,
+        		    FILE_LIST_COLUMN_SIZE_ORDER, &size_a,
+        		    FILE_LIST_COLUMN_IS_FOLDER, &is_folder_a,
+                            -1);
+        gtk_tree_model_get (model, b,
+        		    FILE_LIST_COLUMN_NAME_ORDER, &key_b,
+        		    FILE_LIST_COLUMN_SIZE_ORDER, &size_b,
+        		    FILE_LIST_COLUMN_IS_FOLDER, &is_folder_b,
+                            -1);
+
+        if (is_folder_a == is_folder_b) {
+        	if (is_folder_a) {
+                	result = strcmp (key_a, key_b);
+                	if (sort_order == GTK_SORT_DESCENDING)
+                		result = -1 * result;
+        	}
+        	else
+        		result = size_a - size_b;
+        }
+        else {
+        	result = is_folder_a ? -1 : 1;
+        	if (sort_order == GTK_SORT_DESCENDING)
+        		result = -1 * result;
+        }
 
         g_free (key_a);
         g_free (key_b);
@@ -281,6 +803,69 @@ compare_name_func (GtkTreeModel *model,
 }
 
 
+static gint
+files_modified_column_sort_func (GtkTreeModel *model,
+				 GtkTreeIter  *a,
+				 GtkTreeIter  *b,
+				 gpointer      user_data)
+{
+	GtkSortType sort_order;
+        glong       modified_a;
+        glong       modified_b;
+        gboolean    is_folder_a;
+        gboolean    is_folder_b;
+        int         result;
+
+        gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), NULL, &sort_order);
+
+        gtk_tree_model_get (model, a,
+        		    FILE_LIST_COLUMN_MODIFIED_ORDER, &modified_a,
+        		    FILE_LIST_COLUMN_IS_FOLDER, &is_folder_a,
+                            -1);
+        gtk_tree_model_get (model, b,
+        		    FILE_LIST_COLUMN_MODIFIED_ORDER, &modified_b,
+        		    FILE_LIST_COLUMN_IS_FOLDER, &is_folder_b,
+                            -1);
+
+        if (is_folder_a == is_folder_b) {
+        	result = modified_a - modified_b;
+        }
+        else {
+        	result = is_folder_a ? -1 : 1;
+        	if (sort_order == GTK_SORT_DESCENDING)
+        		result = -1 * result;
+        }
+
+        return result;
+}
+
+
+static gint
+places_default_sort_func (GtkTreeModel *model,
+			  GtkTreeIter  *a,
+			  GtkTreeIter  *b,
+			  gpointer      user_data)
+{
+	int type_a, type_b;
+	int sort_order_a;
+	int sort_order_b;
+
+        gtk_tree_model_get (model, a,
+        		    PLACE_LIST_COLUMN_TYPE, &type_a,
+        		    PLACE_LIST_COLUMN_SORT_ORDER, &sort_order_a,
+                            -1);
+        gtk_tree_model_get (model, b,
+        		    PLACE_LIST_COLUMN_TYPE, &type_b,
+        		    PLACE_LIST_COLUMN_SORT_ORDER, &sort_order_b,
+                            -1);
+
+        if (type_a == type_b)
+        	return sort_order_a - sort_order_b;
+        else
+        	return type_a - type_b;
+}
+
+
 static void
 is_selected_cellrenderertoggle_toggled_cb (GtkCellRendererToggle *cell_renderer,
 					   gchar                 *path,
@@ -312,6 +897,89 @@ is_selected_cellrenderertoggle_toggled_cb (GtkCellRendererToggle *cell_renderer,
 
 
 static void
+files_treeview_row_activated_cb (GtkTreeView       *tree_view,
+				 GtkTreePath       *path,
+				 GtkTreeViewColumn *column,
+				 gpointer           user_data)
+{
+	FrFileSelectorDialog *self = user_data;
+	GtkTreeModel         *tree_model;
+	GtkTreeIter           iter;
+	GFile                *file;
+	gboolean              is_folder;
+
+	tree_model = gtk_tree_view_get_model (tree_view);
+	if (! gtk_tree_model_get_iter (tree_model, &iter, path))
+		return;
+
+        gtk_tree_model_get (tree_model, &iter,
+        		    FILE_LIST_COLUMN_FILE, &file,
+        		    FILE_LIST_COLUMN_IS_FOLDER, &is_folder,
+        		    -1);
+        if (is_folder)
+        	fr_file_selector_dialog_set_current_folder (self, file);
+
+        g_object_unref (file);
+}
+
+
+static void
+places_treeview_selection_changed_cb (GtkTreeSelection *treeselection,
+				      gpointer          user_data)
+{
+	FrFileSelectorDialog *self = user_data;
+	GtkTreeModel         *tree_model;
+	GtkTreeIter           iter;
+	GFile                *file;
+
+	if (! gtk_tree_selection_get_selected (treeselection, &tree_model, &iter))
+		return;
+
+        gtk_tree_model_get (tree_model, &iter,
+        		    PLACE_LIST_COLUMN_FILE, &file,
+        		    -1);
+       	fr_file_selector_dialog_set_current_folder (self, file);
+
+        g_object_unref (file);
+}
+
+
+static gboolean
+places_treeview_row_separator_func (GtkTreeModel *model,
+				    GtkTreeIter  *iter,
+				    gpointer      user_data)
+{
+	gboolean is_separator;
+
+        gtk_tree_model_get (model, iter,
+        		    PLACE_LIST_COLUMN_IS_SEPARATOR, &is_separator,
+        		    -1);
+
+        return is_separator;
+}
+
+
+static void
+go_up_button_clicked_cb (GtkButton *button,
+			 gpointer   user_data)
+{
+	FrFileSelectorDialog *self = user_data;
+	GFile                *parent;
+
+	if (self->priv->current_folder == NULL)
+		return;
+
+	parent = g_file_get_parent (self->priv->current_folder);
+	if (parent == NULL)
+		return;
+
+	fr_file_selector_dialog_set_current_folder (self, parent);
+
+	g_object_unref (parent);
+}
+
+
+static void
 fr_file_selector_dialog_init (FrFileSelectorDialog *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FR_TYPE_FILE_SELECTOR_DIALOG, FrFileSelectorDialogPrivate);
@@ -319,17 +987,38 @@ fr_file_selector_dialog_init (FrFileSelectorDialog *self)
 	self->priv->builder = _gtk_builder_new_from_resource ("file-selector.ui");
 	self->priv->icon_cache = gth_icon_cache_new_for_widget (GTK_WIDGET (self), GTK_ICON_SIZE_MENU);
 	self->priv->settings = g_settings_new ("org.gnome.FileRoller.FileSelector");
+	self->priv->special_places = NULL;
 
 	gtk_container_set_border_width (GTK_CONTAINER (self), 5);
 	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (self))), GET_WIDGET ("content"));
 
-	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), FILE_LIST_COLUMN_NAME_ORDER, compare_name_func, self, NULL);
-	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), FILE_LIST_COLUMN_NAME_ORDER, GTK_SORT_ASCENDING);
+	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), FILE_LIST_COLUMN_NAME, files_name_column_sort_func, self, NULL);
+	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), FILE_LIST_COLUMN_SIZE, files_size_column_sort_func, self, NULL);
+	gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), FILE_LIST_COLUMN_MODIFIED, files_modified_column_sort_func, self, NULL);
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), FILE_LIST_COLUMN_NAME, GTK_SORT_ASCENDING);
+
+	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (GET_WIDGET ("places_liststore")), places_default_sort_func, self, NULL);
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GET_WIDGET ("places_liststore")), GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+	gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (GET_WIDGET ("places_treeview")), places_treeview_row_separator_func, self, NULL);
 
 	g_signal_connect (GET_WIDGET ("is_selected_cellrenderertoggle"),
 			  "toggled",
 			  G_CALLBACK (is_selected_cellrenderertoggle_toggled_cb),
 			  self);
+	g_signal_connect (GET_WIDGET ("files_treeview"),
+			  "row-activated",
+			  G_CALLBACK (files_treeview_row_activated_cb),
+			  self);
+	g_signal_connect (GET_WIDGET ("go_up_button"),
+			  "clicked",
+			  G_CALLBACK (go_up_button_clicked_cb),
+			  self);
+	g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (GET_WIDGET ("places_treeview"))),
+			  "changed",
+			  G_CALLBACK (places_treeview_selection_changed_cb),
+			  self);
+
+	_fr_file_selector_dialog_update_size (self);
 }
 
 
@@ -363,6 +1052,59 @@ fr_file_selector_dialog_get_extra_widget (FrFileSelectorDialog *self)
 
 
 static gboolean
+_gtk_list_store_get_iter_for_file (GtkListStore *list_store,
+				   GtkTreeIter  *iter,
+				   GFile        *file)
+{
+	if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), iter))
+		return FALSE;
+
+	do {
+		GFile *item_file;
+
+	        gtk_tree_model_get (GTK_TREE_MODEL (list_store), iter,
+	        		    PLACE_LIST_COLUMN_FILE, &item_file,
+	                            -1);
+
+	        if ((item_file != NULL) && g_file_equal (item_file, file)) {
+	        	_g_object_unref (item_file);
+	        	return TRUE;
+	        }
+
+	        _g_object_unref (item_file);
+	}
+	while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), iter));
+
+	return FALSE;
+}
+
+
+static void
+set_current_folder (FrFileSelectorDialog *self,
+		    GFile                *folder)
+{
+	char        *folder_name;
+	GtkTreeIter  iter;
+
+	_g_clear_object (&self->priv->current_folder);
+	self->priv->current_folder = g_object_ref (folder);
+
+	folder_name = g_file_get_parse_name (folder);
+	gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("location_entry")), folder_name);
+
+	if (_gtk_list_store_get_iter_for_file (GTK_LIST_STORE (GET_WIDGET ("places_liststore")), &iter, folder)) {
+		GtkTreeSelection *tree_selection;
+
+		tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (GET_WIDGET ("places_treeview")));
+
+		g_signal_handlers_block_by_func (tree_selection, places_treeview_selection_changed_cb, self);
+		gtk_tree_selection_select_iter (tree_selection, &iter);
+		g_signal_handlers_unblock_by_func (tree_selection, places_treeview_selection_changed_cb, self);
+	}
+}
+
+
+static gboolean
 _g_date_time_same_day (GDateTime *dt1,
 		       GDateTime *dt2)
 {
@@ -386,10 +1128,17 @@ get_folder_content_done_cb (GError   *error,
 	GList                *scan;
 	GtkTreeIter           iter;
 	GDateTime            *today;
+	int                   sort_column_id;
+	GtkSortType           sort_order;
 
 	if (error != NULL) {
-		g_warning ("%s", error->message);
+		if (! g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			_gtk_error_dialog_run (GTK_WINDOW (self), _("Could not load the location"), "%s", error->message);
+
+		if (load_data->dialog->priv->current_operation == load_data)
+			load_data->dialog->priv->current_operation = NULL;
 		load_data_free (load_data);
+
 		return;
 	}
 
@@ -397,6 +1146,9 @@ get_folder_content_done_cb (GError   *error,
 
 	today = g_date_time_new_now_local ();
 
+	gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), &sort_column_id, &sort_order);
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, 0);
+
 	list_store = GTK_LIST_STORE (GET_WIDGET ("files_liststore"));
 	gtk_list_store_clear (list_store);
 	for (scan = load_data->files; scan; scan = scan->next) {
@@ -407,6 +1159,10 @@ get_folder_content_done_cb (GError   *error,
 		GDateTime *datetime;
 		char      *modified;
 		char      *collate_key;
+		gboolean   is_folder;
+
+		if (g_file_info_get_is_hidden (file_info->info))
+			continue;
 
 		gtk_list_store_append (list_store, &iter);
 
@@ -416,17 +1172,18 @@ get_folder_content_done_cb (GError   *error,
 		datetime = g_date_time_new_from_timeval_local (&timeval);
 		modified = g_date_time_format (datetime, _g_date_time_same_day (datetime, today) ? "%X" : "%x");
 		collate_key = g_utf8_collate_key_for_filename (g_file_info_get_display_name (file_info->info), -1);
+		is_folder = (g_file_info_get_file_type (file_info->info) == G_FILE_TYPE_DIRECTORY);
 
 		gtk_list_store_set (list_store, &iter,
 				    FILE_LIST_COLUMN_ICON, icon_pixbuf,
 				    FILE_LIST_COLUMN_NAME, g_file_info_get_display_name (file_info->info),
-				    FILE_LIST_COLUMN_SIZE, size,
+				    FILE_LIST_COLUMN_SIZE, (is_folder ? "" : size),
 				    FILE_LIST_COLUMN_MODIFIED, modified,
 				    FILE_LIST_COLUMN_FILE, file_info->file,
 				    FILE_LIST_COLUMN_NAME_ORDER, collate_key,
 				    FILE_LIST_COLUMN_SIZE_ORDER, g_file_info_get_size (file_info->info),
 				    FILE_LIST_COLUMN_MODIFIED_ORDER, timeval.tv_sec,
-				    FILE_LIST_COLUMN_IS_FOLDER, (g_file_info_get_file_type (file_info->info) == G_FILE_TYPE_DIRECTORY),
+				    FILE_LIST_COLUMN_IS_FOLDER, is_folder,
 				    -1);
 
 		g_free (collate_key);
@@ -436,6 +1193,12 @@ get_folder_content_done_cb (GError   *error,
 		g_object_unref (icon_pixbuf);
 	}
 
+	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (GET_WIDGET ("files_liststore")), sort_column_id, sort_order);
+	set_current_folder (self, load_data->folder);
+
+	if (load_data->dialog->priv->current_operation == load_data)
+		load_data->dialog->priv->current_operation = NULL;
+
 	g_date_time_unref (today);
 	load_data_free (load_data);
 }
@@ -454,17 +1217,20 @@ get_folder_content_for_each_child_cb (GFile     *file,
 }
 
 
-static void
-get_folder_content (LoadData *load_data)
+void
+fr_file_selector_dialog_set_current_folder (FrFileSelectorDialog *self,
+					    GFile                *folder)
 {
-	FrFileSelectorDialog *self = load_data->dialog;
-	char                 *folder_name;
+	g_return_if_fail (folder != NULL);
 
-	folder_name = g_file_get_parse_name (load_data->folder);
-	gtk_entry_set_text (GTK_ENTRY (GET_WIDGET ("location_entry")), folder_name);
+	if (self->priv->current_operation != NULL)
+		g_cancellable_cancel (self->priv->current_operation->cancellable);
 
-	load_data->files = NULL;
-	g_directory_foreach_child (load_data->folder,
+	self->priv->current_operation = load_data_new (self, folder);
+
+	gtk_list_store_clear (GTK_LIST_STORE (GET_WIDGET ("files_liststore")));
+
+	g_directory_foreach_child (self->priv->current_operation->folder,
 				   FALSE,
 				   TRUE,
 			           (G_FILE_ATTRIBUTE_STANDARD_TYPE ","
@@ -473,33 +1239,13 @@ get_folder_content (LoadData *load_data)
 			            G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
 			            G_FILE_ATTRIBUTE_STANDARD_ICON ","
 			            G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN ","
-			            G_FILE_ATTRIBUTE_STANDARD_SORT_ORDER ","
 			            G_FILE_ATTRIBUTE_TIME_MODIFIED ","
 			            G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC),
-			           load_data->cancellable,
+			           self->priv->current_operation->cancellable,
 			           NULL,
 			           get_folder_content_for_each_child_cb,
 			           get_folder_content_done_cb,
-				   load_data);
-
-	g_free (folder_name);
-}
-
-
-void
-fr_file_selector_dialog_set_current_folder (FrFileSelectorDialog *self,
-					    GFile                *folder)
-{
-	g_return_if_fail (folder != NULL);
-
-	_g_clear_object (&self->priv->current_folder);
-	self->priv->current_folder = g_object_ref (folder);
-
-	if (self->priv->current_operation != NULL)
-		g_cancellable_cancel (self->priv->current_operation->cancellable);
-
-	self->priv->current_operation = load_data_new (self, folder);
-	get_folder_content (self->priv->current_operation);
+			           self->priv->current_operation);
 }
 
 
diff --git a/src/ui/file-selector.ui b/src/ui/file-selector.ui
index 132711b..d55691b 100644
--- a/src/ui/file-selector.ui
+++ b/src/ui/file-selector.ui
@@ -1,42 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
-  <object class="GtkListStore" id="files_liststore">
-    <columns>
-      <!-- column-name icon -->
-      <column type="GdkPixbuf"/>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-      <!-- column-name size -->
-      <column type="gchararray"/>
-      <!-- column-name modified -->
-      <column type="gchararray"/>
-      <!-- column-name file -->
-      <column type="GObject"/>
-      <!-- column-name name_order -->
-      <column type="gchararray"/>
-      <!-- column-name size_order -->
-      <column type="gint64"/>
-      <!-- column-name modified_order -->
-      <column type="gint64"/>
-      <!-- column-name is_folder -->
-      <column type="gboolean"/>
-      <!-- column-name is_selected -->
-      <column type="gboolean"/>
-    </columns>
-  </object>
-  <object class="GtkListStore" id="places_liststore">
-    <columns>
-      <!-- column-name icon -->
-      <column type="GdkPixbuf"/>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-      <!-- column-name file -->
-      <column type="GObject"/>
-      <!-- column-name is_separator -->
-      <column type="gboolean"/>
-    </columns>
-  </object>
   <object class="GtkBox" id="content">
     <property name="visible">True</property>
     <property name="can_focus">False</property>
@@ -155,7 +119,6 @@
                     <child>
                       <object class="GtkTreeViewColumn" id="treeviewcolumn1">
                         <property name="title" translatable="yes">Places</property>
-                        <property name="sort_column_id">1</property>
                         <child>
                           <object class="GtkCellRendererPixbuf" id="cellrendererpixbuf1"/>
                           <attributes>
@@ -201,7 +164,7 @@
                         <property name="title" translatable="yes" context="File">Name</property>
                         <property name="expand">True</property>
                         <property name="reorderable">True</property>
-                        <property name="sort_column_id">5</property>
+                        <property name="sort_column_id">1</property>
                         <child>
                           <object class="GtkCellRendererToggle" id="is_selected_cellrenderertoggle"/>
                           <attributes>
@@ -231,7 +194,7 @@
                         <property name="fixed_width">100</property>
                         <property name="title" translatable="yes" context="File">Size</property>
                         <property name="reorderable">True</property>
-                        <property name="sort_column_id">6</property>
+                        <property name="sort_column_id">2</property>
                         <child>
                           <object class="GtkCellRendererText" id="cellrenderertext2"/>
                           <attributes>
@@ -247,7 +210,7 @@
                         <property name="fixed_width">100</property>
                         <property name="title" translatable="yes" context="File">Modified</property>
                         <property name="reorderable">True</property>
-                        <property name="sort_column_id">7</property>
+                        <property name="sort_column_id">3</property>
                         <child>
                           <object class="GtkCellRendererText" id="cellrenderertext3"/>
                           <attributes>
@@ -294,4 +257,44 @@
       </packing>
     </child>
   </object>
+  <object class="GtkListStore" id="files_liststore">
+    <columns>
+      <!-- column-name icon -->
+      <column type="GdkPixbuf"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name size -->
+      <column type="gchararray"/>
+      <!-- column-name modified -->
+      <column type="gchararray"/>
+      <!-- column-name file -->
+      <column type="GObject"/>
+      <!-- column-name name_order -->
+      <column type="gchararray"/>
+      <!-- column-name size_order -->
+      <column type="gint64"/>
+      <!-- column-name modified_order -->
+      <column type="gint64"/>
+      <!-- column-name is_folder -->
+      <column type="gboolean"/>
+      <!-- column-name is_selected -->
+      <column type="gboolean"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="places_liststore">
+    <columns>
+      <!-- column-name icon -->
+      <column type="GdkPixbuf"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name file -->
+      <column type="GObject"/>
+      <!-- column-name is_separator -->
+      <column type="gboolean"/>
+      <!-- column-name type -->
+      <column type="gint"/>
+      <!-- column-name sort_order -->
+      <column type="gint"/>
+    </columns>
+  </object>
 </interface>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]