diff options
author | Federico Mena Quintero <federico@gnome.org> | 2013-04-15 19:29:37 -0500 |
---|---|---|
committer | Federico Mena Quintero <federico@gnome.org> | 2013-04-15 19:29:37 -0500 |
commit | 3b3eb4bdbc90ee6df387ba8d8fe061f5956ca442 (patch) | |
tree | 0733da2f4e168f1231f4d05c182e037682763ce9 | |
parent | e3a22b3eba58b7dcb52496db893795f9f2d65983 (diff) | |
parent | a6f9ef0d3a9149c6e5582b5907ebc3085cffdfce (diff) | |
download | gtk+-3b3eb4bdbc90ee6df387ba8d8fe061f5956ca442.tar.gz |
Merge branch 'merge-places-sidebar'
This is the new GtkPlacesSidebar widget, which is the sidebar used in
GtkFileChooser to display a list of places/folders that the user may
want to use. File managers are encouraged to use this widget for
their own global list of folders.
-rw-r--r-- | docs/reference/gtk/gtk-docs.sgml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk3-sections.txt | 26 | ||||
-rw-r--r-- | gtk/Makefile.am | 6 | ||||
-rw-r--r-- | gtk/glade/gtk-private-widgets.xml | 2 | ||||
-rw-r--r-- | gtk/gtk-default.css | 7 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtkbookmarksmanager.c | 596 | ||||
-rw-r--r-- | gtk/gtkbookmarksmanager.h | 91 | ||||
-rw-r--r-- | gtk/gtkfilechooser.c | 5 | ||||
-rw-r--r-- | gtk/gtkfilechooserbutton.c | 35 | ||||
-rw-r--r-- | gtk/gtkfilechooserdefault.c | 5270 | ||||
-rw-r--r-- | gtk/gtkfilechooserdefault.ui | 158 | ||||
-rw-r--r-- | gtk/gtkfilechooserdialog.c | 8 | ||||
-rw-r--r-- | gtk/gtkfilechooserprivate.h | 55 | ||||
-rw-r--r-- | gtk/gtkfilechooserwidget.c | 17 | ||||
-rw-r--r-- | gtk/gtkfilesystem.c | 433 | ||||
-rw-r--r-- | gtk/gtkfilesystem.h | 20 | ||||
-rw-r--r-- | gtk/gtkgladecatalog.c | 2 | ||||
-rw-r--r-- | gtk/gtkmarshalers.list | 8 | ||||
-rw-r--r-- | gtk/gtkplacessidebar.c | 4335 | ||||
-rw-r--r-- | gtk/gtkplacessidebar.h | 100 | ||||
-rw-r--r-- | gtk/gtktrashmonitor.c | 267 | ||||
-rw-r--r-- | gtk/gtktrashmonitor.h | 48 | ||||
-rw-r--r-- | gtk/org.gtk.Settings.FileChooser.gschema.xml | 8 | ||||
-rw-r--r-- | gtk/tests/filechooser.c | 1 | ||||
-rw-r--r-- | po/POTFILES.in | 1 |
26 files changed, 6908 insertions, 4593 deletions
diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index df6af83c7b..671c3b67d6 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -220,6 +220,7 @@ <xi:include href="xml/gtkfontbutton.xml" /> <xi:include href="xml/gtkfontchooserwidget.xml" /> <xi:include href="xml/gtkfontchooserdialog.xml" /> + <xi:include href="xml/gtkplacessidebar.xml" /> </chapter> <chapter id="LayoutContainers"> diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index deb83f9eb5..39721dd959 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -2484,6 +2484,32 @@ gtk_paned_get_type </SECTION> <SECTION> +<FILE>gtkplacessidebar</FILE> +<TITLE>GtkPlacesSidebar</FILE> +GtkPlacesSidebar +GtkPlacesOpenFlags +gtk_places_sidebar_new +gtk_places_sidebar_set_open_flags +gtk_places_sidebar_set_location +gtk_places_sidebar_get_location +gtk_places_sidebar_set_show_desktop +gtk_places_sidebar_set_accept_uri_drops +gtk_places_sidebar_add_shortcut +gtk_places_sidebar_remove_shortcut +gtk_places_sidebar_list_shortcuts +gtk_places_sidebar_get_nth_bookmark +<SUBSECTION Standard> +GTK_PLACES_SIDEBAR +GTK_IS_PLACES_SIDEBAR +GTK_TYPE_PLACES_SIDEBAR +GTK_PLACES_SIDEBAR_CLASS +GTK_IS_PLACES_SIDEBAR_CLASS +GTK_PLACES_SIDEBAR_GET_CLASS +<SUBSECTION Private> +gtk_places_sidebar_get_type +</SECTION> + +<SECTION> <FILE>gtkplug</FILE> <TITLE>GtkPlug</TITLE> GtkPlug diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 99f0e33941..9df87a0675 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -295,6 +295,7 @@ gtk_public_h_sources = \ gtkpagesetup.h \ gtkpaned.h \ gtkpapersize.h \ + gtkplacessidebar.h \ gtkplug.h \ gtkprintcontext.h \ gtkprintoperation.h \ @@ -421,6 +422,7 @@ gtk_private_h_sources = \ gtkbindingsprivate.h \ gtkbitmaskprivate.h \ gtkbitmaskprivateimpl.h \ + gtkbookmarksmanager.h \ gtkborderimageprivate.h \ gtkboxprivate.h \ gtkbubblewindowprivate.h \ @@ -547,6 +549,7 @@ gtk_private_h_sources = \ gtktextutil.h \ gtkthemingbackgroundprivate.h \ gtkthemingengineprivate.h \ + gtktrashmonitor.h \ gtktoolpaletteprivate.h \ gtktreedatalist.h \ gtktreeprivate.h \ @@ -621,6 +624,7 @@ gtk_base_c_sources = \ gtkbbox.c \ gtkbin.c \ gtkbindings.c \ + gtkbookmarksmanager.c \ gtkborder.c \ gtkborderimage.c \ gtkbox.c \ @@ -783,6 +787,7 @@ gtk_base_c_sources = \ gtkpango.c \ gtkpapersize.c \ gtkpathbar.c \ + gtkplacessidebar.c \ gtkpressandhold.c \ gtkprintcontext.c \ gtkprintoperation.c \ @@ -866,6 +871,7 @@ gtk_base_c_sources = \ gtktoolpalette.c \ gtktoolshell.c \ gtktooltip.c \ + gtktrashmonitor.c \ gtktreedatalist.c \ gtktreednd.c \ gtktreemenu.c \ diff --git a/gtk/glade/gtk-private-widgets.xml b/gtk/glade/gtk-private-widgets.xml index 1a65e252e2..f1de19b9e6 100644 --- a/gtk/glade/gtk-private-widgets.xml +++ b/gtk/glade/gtk-private-widgets.xml @@ -22,6 +22,7 @@ <property id="size" disabled="True"/> </properties> </glade-widget-class> + <glade-widget-class name="GtkPlacesSidebar" generic-name="placessidebar" title="Places Sidebar"/> </glade-widget-classes> <glade-widget-group name="gtk-private" title="Private GTK+ Classes"> @@ -32,5 +33,6 @@ <glade-widget-class-ref name="GtkScaleButtonScale"/> <glade-widget-class-ref name="ShortcutsPaneModelFilter"/> <glade-widget-class-ref name="GtkPrinterOptionWidget"/> + <glade-widget-class-ref name="GtkPlacesSidebar"/> </glade-widget-group> </glade-catalog> diff --git a/gtk/gtk-default.css b/gtk/gtk-default.css index d0776802f5..b125cf68e2 100644 --- a/gtk/gtk-default.css +++ b/gtk/gtk-default.css @@ -41,6 +41,13 @@ GtkTreeView.view.expander:selected:hover { color: @text_color; } +GtkTreeView.dnd { + border-color: @internal_element_color; + border-radius: 0; + border-width: 1px; + border-style: solid; +} + *:insensitive { border-color: shade (@bg_color, 0.7); background-color: shade (@bg_color, 0.9); @@ -145,6 +145,7 @@ #include <gtk/gtkpagesetup.h> #include <gtk/gtkpapersize.h> #include <gtk/gtkpaned.h> +#include <gtk/gtkplacessidebar.h> #include <gtk/gtkprintcontext.h> #include <gtk/gtkprintoperation.h> #include <gtk/gtkprintoperationpreview.h> diff --git a/gtk/gtkbookmarksmanager.c b/gtk/gtkbookmarksmanager.c new file mode 100644 index 0000000000..91ccd89c29 --- /dev/null +++ b/gtk/gtkbookmarksmanager.c @@ -0,0 +1,596 @@ +/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ +/* GTK - The GIMP Toolkit + * gtkbookmarksmanager.c: Utilities to manage and monitor ~/.gtk-bookmarks + * Copyright (C) 2003, Red Hat, Inc. + * Copyright (C) 2007-2008 Carlos Garnacho + * Copyright (C) 2011 Suse + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: Federico Mena Quintero <federico@gnome.org> + */ + +#include "config.h" + +#include <string.h> + +#include <glib/gi18n-lib.h> + +#include "gtkbookmarksmanager.h" +#include "gtkfilechooser.h" /* for the GError types */ + +static void +_gtk_bookmark_free (GtkBookmark *bookmark) +{ + g_object_unref (bookmark->file); + g_free (bookmark->label); + g_slice_free (GtkBookmark, bookmark); +} + +static GFile * +get_legacy_bookmarks_file (void) +{ + GFile *file; + gchar *filename; + + filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL); + file = g_file_new_for_path (filename); + g_free (filename); + + return file; +} + +static GFile * +get_bookmarks_file (void) +{ + GFile *file; + gchar *filename; + + filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL); + file = g_file_new_for_path (filename); + g_free (filename); + + return file; +} + +static GSList * +read_bookmarks (GFile *file) +{ + gchar *contents; + gchar **lines, *space; + GSList *bookmarks = NULL; + gint i; + + if (!g_file_load_contents (file, NULL, &contents, + NULL, NULL, NULL)) + return NULL; + + lines = g_strsplit (contents, "\n", -1); + + for (i = 0; lines[i]; i++) + { + GtkBookmark *bookmark; + + if (!*lines[i]) + continue; + + if (!g_utf8_validate (lines[i], -1, NULL)) + continue; + + bookmark = g_slice_new0 (GtkBookmark); + + if ((space = strchr (lines[i], ' ')) != NULL) + { + space[0] = '\0'; + bookmark->label = g_strdup (space + 1); + } + + bookmark->file = g_file_new_for_uri (lines[i]); + bookmarks = g_slist_prepend (bookmarks, bookmark); + } + + bookmarks = g_slist_reverse (bookmarks); + g_strfreev (lines); + g_free (contents); + + return bookmarks; +} + +static void +save_bookmarks (GFile *bookmarks_file, + GSList *bookmarks) +{ + GError *error = NULL; + GString *contents; + GSList *l; + + contents = g_string_new (""); + + for (l = bookmarks; l; l = l->next) + { + GtkBookmark *bookmark = l->data; + gchar *uri; + + uri = g_file_get_uri (bookmark->file); + if (!uri) + continue; + + g_string_append (contents, uri); + + if (bookmark->label) + g_string_append_printf (contents, " %s", bookmark->label); + + g_string_append_c (contents, '\n'); + g_free (uri); + } + + if (!g_file_replace_contents (bookmarks_file, + contents->str, + strlen (contents->str), + NULL, FALSE, 0, NULL, + NULL, &error)) + { + g_critical ("%s", error->message); + g_error_free (error); + } + + g_string_free (contents, TRUE); +} + +static void +notify_changed (GtkBookmarksManager *manager) +{ + if (manager->changed_func) + manager->changed_func (manager->changed_func_data); +} + +static void +bookmarks_file_changed (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer data) +{ + GtkBookmarksManager *manager = data; + + switch (event) + { + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + case G_FILE_MONITOR_EVENT_CREATED: + case G_FILE_MONITOR_EVENT_DELETED: + g_slist_foreach (manager->bookmarks, (GFunc) _gtk_bookmark_free, NULL); + g_slist_free (manager->bookmarks); + + manager->bookmarks = read_bookmarks (file); + + gdk_threads_enter (); + notify_changed (manager); + gdk_threads_leave (); + break; + + default: + /* ignore at the moment */ + break; + } +} + +GtkBookmarksManager * +_gtk_bookmarks_manager_new (GtkBookmarksChangedFunc changed_func, gpointer changed_func_data) +{ + GtkBookmarksManager *manager; + GFile *bookmarks_file; + GError *error; + + manager = g_new0 (GtkBookmarksManager, 1); + + manager->changed_func = changed_func; + manager->changed_func_data = changed_func_data; + + bookmarks_file = get_bookmarks_file (); + manager->bookmarks = read_bookmarks (bookmarks_file); + if (!manager->bookmarks) + { + GFile *legacy_bookmarks_file; + + /* Read the legacy one and write it to the new one */ + legacy_bookmarks_file = get_legacy_bookmarks_file (); + manager->bookmarks = read_bookmarks (legacy_bookmarks_file); + save_bookmarks (bookmarks_file, manager->bookmarks); + + g_object_unref (legacy_bookmarks_file); + } + + error = NULL; + manager->bookmarks_monitor = g_file_monitor_file (bookmarks_file, + G_FILE_MONITOR_NONE, + NULL, &error); + if (error) + { + g_warning ("%s", error->message); + g_error_free (error); + } + else + manager->bookmarks_monitor_changed_id = g_signal_connect (manager->bookmarks_monitor, "changed", + G_CALLBACK (bookmarks_file_changed), manager); + + g_object_unref (bookmarks_file); + + return manager; +} + +void +_gtk_bookmarks_manager_free (GtkBookmarksManager *manager) +{ + g_return_if_fail (manager != NULL); + + if (manager->bookmarks_monitor) + { + g_file_monitor_cancel (manager->bookmarks_monitor); + g_signal_handler_disconnect (manager->bookmarks_monitor, manager->bookmarks_monitor_changed_id); + manager->bookmarks_monitor_changed_id = 0; + g_object_unref (manager->bookmarks_monitor); + } + + if (manager->bookmarks) + { + g_slist_foreach (manager->bookmarks, (GFunc) _gtk_bookmark_free, NULL); + g_slist_free (manager->bookmarks); + } + + g_free (manager); +} + +GSList * +_gtk_bookmarks_manager_list_bookmarks (GtkBookmarksManager *manager) +{ + GSList *bookmarks, *files = NULL; + + g_return_val_if_fail (manager != NULL, NULL); + + bookmarks = manager->bookmarks; + + while (bookmarks) + { + GtkBookmark *bookmark; + + bookmark = bookmarks->data; + bookmarks = bookmarks->next; + + files = g_slist_prepend (files, g_object_ref (bookmark->file)); + } + + return g_slist_reverse (files); +} + +static GSList * +find_bookmark_link_for_file (GSList *bookmarks, GFile *file, int *position_ret) +{ + int pos; + + pos = 0; + for (; bookmarks; bookmarks = bookmarks->next) + { + GtkBookmark *bookmark = bookmarks->data; + + if (g_file_equal (file, bookmark->file)) + { + if (position_ret) + *position_ret = pos; + + return bookmarks; + } + + pos++; + } + + if (position_ret) + *position_ret = -1; + + return NULL; +} + +gboolean +_gtk_bookmarks_manager_has_bookmark (GtkBookmarksManager *manager, + GFile *file) +{ + GSList *link; + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + return (link != NULL); +} + +gboolean +_gtk_bookmarks_manager_insert_bookmark (GtkBookmarksManager *manager, + GFile *file, + gint position, + GError **error) +{ + GSList *link; + GtkBookmark *bookmark; + GFile *bookmarks_file; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + + if (link) + { + bookmark = link->data; + gchar *uri = g_file_get_uri (bookmark->file); + + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS, + "%s already exists in the bookmarks list", + uri); + + g_free (uri); + + return FALSE; + } + + bookmark = g_slice_new0 (GtkBookmark); + bookmark->file = g_object_ref (file); + + manager->bookmarks = g_slist_insert (manager->bookmarks, bookmark, position); + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +gboolean +_gtk_bookmarks_manager_remove_bookmark (GtkBookmarksManager *manager, + GFile *file, + GError **error) +{ + GSList *link; + GFile *bookmarks_file; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (!manager->bookmarks) + return FALSE; + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + if (link) + { + GtkBookmark *bookmark = link->data; + + manager->bookmarks = g_slist_remove_link (manager->bookmarks, link); + _gtk_bookmark_free (bookmark); + g_slist_free_1 (link); + } + else + { + gchar *uri = g_file_get_uri (file); + + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_NONEXISTENT, + "%s does not exist in the bookmarks list", + uri); + + g_free (uri); + + return FALSE; + } + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +gboolean +_gtk_bookmarks_manager_reorder_bookmark (GtkBookmarksManager *manager, + GFile *file, + gint new_position, + GError **error) +{ + GSList *link; + GFile *bookmarks_file; + int old_position; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (new_position >= 0, FALSE); + + if (!manager->bookmarks) + return FALSE; + + link = find_bookmark_link_for_file (manager->bookmarks, file, &old_position); + if (new_position == old_position) + return TRUE; + + if (link) + { + GtkBookmark *bookmark = link->data; + + manager->bookmarks = g_slist_remove_link (manager->bookmarks, link); + g_slist_free_1 (link); + + if (new_position > old_position) + new_position--; + + manager->bookmarks = g_slist_insert (manager->bookmarks, bookmark, new_position); + } + else + { + gchar *uri = g_file_get_uri (file); + + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_NONEXISTENT, + "%s does not exist in the bookmarks list", + uri); + + g_free (uri); + + return FALSE; + } + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +gchar * +_gtk_bookmarks_manager_get_bookmark_label (GtkBookmarksManager *manager, + GFile *file) +{ + GSList *bookmarks; + gchar *label = NULL; + + g_return_val_if_fail (manager != NULL, NULL); + g_return_val_if_fail (file != NULL, NULL); + + bookmarks = manager->bookmarks; + + while (bookmarks) + { + GtkBookmark *bookmark; + + bookmark = bookmarks->data; + bookmarks = bookmarks->next; + + if (g_file_equal (file, bookmark->file)) + { + label = g_strdup (bookmark->label); + break; + } + } + + return label; +} + +gboolean +_gtk_bookmarks_manager_set_bookmark_label (GtkBookmarksManager *manager, + GFile *file, + const gchar *label, + GError **error) +{ + GFile *bookmarks_file; + GSList *link; + + g_return_val_if_fail (manager != NULL, FALSE); + g_return_val_if_fail (file != NULL, FALSE); + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + if (link) + { + GtkBookmark *bookmark = link->data; + + g_free (bookmark->label); + bookmark->label = g_strdup (label); + } + else + { + gchar *uri = g_file_get_uri (file); + + g_set_error (error, + GTK_FILE_CHOOSER_ERROR, + GTK_FILE_CHOOSER_ERROR_NONEXISTENT, + "%s does not exist in the bookmarks list", + uri); + + g_free (uri); + + return FALSE; + } + + bookmarks_file = get_bookmarks_file (); + save_bookmarks (bookmarks_file, manager->bookmarks); + g_object_unref (bookmarks_file); + + notify_changed (manager); + + return TRUE; +} + +gboolean +_gtk_bookmarks_manager_get_xdg_type (GtkBookmarksManager *manager, + GFile *file, + GUserDirectory *directory) +{ + GSList *link; + gboolean match; + GFile *location; + const gchar *path; + GUserDirectory dir; + GtkBookmark *bookmark; + + link = find_bookmark_link_for_file (manager->bookmarks, file, NULL); + if (!link) + return FALSE; + + match = FALSE; + bookmark = link->data; + + for (dir = 0; dir < G_USER_N_DIRECTORIES; dir++) + { + path = g_get_user_special_dir (dir); + if (!path) + continue; + + location = g_file_new_for_path (path); + match = g_file_equal (location, bookmark->file); + g_object_unref (location); + + if (match) + break; + } + + if (match && directory != NULL) + *directory = dir; + + return match; +} + +gboolean +_gtk_bookmarks_manager_get_is_builtin (GtkBookmarksManager *manager, + GFile *file) +{ + GUserDirectory xdg_type; + + /* if this is not an XDG dir, it's never builtin */ + if (!_gtk_bookmarks_manager_get_xdg_type (manager, file, &xdg_type)) + return FALSE; + + /* exclude XDG locations we don't display by default */ + return _gtk_bookmarks_manager_get_is_xdg_dir_builtin (xdg_type); +} + +gboolean +_gtk_bookmarks_manager_get_is_xdg_dir_builtin (GUserDirectory xdg_type) +{ + return (xdg_type != G_USER_DIRECTORY_DESKTOP) && + (xdg_type != G_USER_DIRECTORY_TEMPLATES) && + (xdg_type != G_USER_DIRECTORY_PUBLIC_SHARE); +} diff --git a/gtk/gtkbookmarksmanager.h b/gtk/gtkbookmarksmanager.h new file mode 100644 index 0000000000..f2efd6577b --- /dev/null +++ b/gtk/gtkbookmarksmanager.h @@ -0,0 +1,91 @@ +/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */ +/* GTK - The GIMP Toolkit + * gtkbookmarksmanager.h: Utilities to manage and monitor ~/.gtk-bookmarks + * Copyright (C) 2003, Red Hat, Inc. + * Copyright (C) 2007-2008 Carlos Garnacho + * Copyright (C) 2011 Suse + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: Federico Mena Quintero <federico@gnome.org> + */ + +#ifndef __GTK_BOOKMARKS_MANAGER_H__ +#define __GTK_BOOKMARKS_MANAGER_H__ + +#include <gio/gio.h> + +typedef void (* GtkBookmarksChangedFunc) (gpointer data); + +typedef struct +{ + /* This list contains GtkBookmark structs */ + GSList *bookmarks; + + GFileMonitor *bookmarks_monitor; + gulong bookmarks_monitor_changed_id; + + gpointer changed_func_data; + GtkBookmarksChangedFunc changed_func; +} GtkBookmarksManager; + +typedef struct +{ + GFile *file; + gchar *label; +} GtkBookmark; + +GtkBookmarksManager *_gtk_bookmarks_manager_new (GtkBookmarksChangedFunc changed_func, + gpointer changed_func_data); + + +void _gtk_bookmarks_manager_free (GtkBookmarksManager *manager); + +GSList *_gtk_bookmarks_manager_list_bookmarks (GtkBookmarksManager *manager); + +gboolean _gtk_bookmarks_manager_insert_bookmark (GtkBookmarksManager *manager, + GFile *file, + gint position, + GError **error); + +gboolean _gtk_bookmarks_manager_remove_bookmark (GtkBookmarksManager *manager, + GFile *file, + GError **error); + +gboolean _gtk_bookmarks_manager_reorder_bookmark (GtkBookmarksManager *manager, + GFile *file, + gint new_position, + GError **error); + +gboolean _gtk_bookmarks_manager_has_bookmark (GtkBookmarksManager *manager, + GFile *file); + +gchar * _gtk_bookmarks_manager_get_bookmark_label (GtkBookmarksManager *manager, + GFile *file); + +gboolean _gtk_bookmarks_manager_set_bookmark_label (GtkBookmarksManager *manager, + GFile *file, + const gchar *label, + GError **error); + +gboolean _gtk_bookmarks_manager_get_xdg_type (GtkBookmarksManager *manager, + GFile *file, + GUserDirectory *directory); +gboolean _gtk_bookmarks_manager_get_is_builtin (GtkBookmarksManager *manager, + GFile *file); + +gboolean _gtk_bookmarks_manager_get_is_xdg_dir_builtin (GUserDirectory xdg_type); + +#endif /* __GTK_BOOKMARKS_MANAGER_H__ */ diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c index 141e8bf76e..2600a136a9 100644 --- a/gtk/gtkfilechooser.c +++ b/gtk/gtkfilechooser.c @@ -55,9 +55,8 @@ * <varlistentry> * <term>Shortcuts</term> * <listitem> - * can be provided by the application or by the underlying filesystem - * abstraction (e.g. both the gnome-vfs and the Windows filesystems - * provide "Desktop" shortcuts). Shortcuts cannot be modified by the + * can be provided by the application. For example, a Paint program may + * want to add a shortcut for a Clipart folder. Shortcuts cannot be modified by the * user. * </listitem> * </varlistentry> diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c index 86403d650c..8c744ce8fd 100644 --- a/gtk/gtkfilechooserbutton.c +++ b/gtk/gtkfilechooserbutton.c @@ -186,12 +186,13 @@ struct _GtkFileChooserButtonPrivate GFile *current_folder_while_inactive; gulong fs_volumes_changed_id; - gulong fs_bookmarks_changed_id; GCancellable *dnd_select_folder_cancellable; GCancellable *update_button_cancellable; GSList *change_icon_theme_cancellables; + GtkBookmarksManager *bookmarks_manager; + gint icon_size; guint8 n_special; @@ -323,8 +324,7 @@ static void update_label_and_image (GtkFileChooserButton *button) /* Child Object Callbacks */ static void fs_volumes_changed_cb (GtkFileSystem *fs, gpointer user_data); -static void fs_bookmarks_changed_cb (GtkFileSystem *fs, - gpointer user_data); +static void bookmarks_changed_cb (gpointer user_data); static void combo_box_changed_cb (GtkComboBox *combo_box, gpointer user_data); @@ -502,6 +502,8 @@ gtk_file_chooser_button_init (GtkFileChooserButton *button) gtk_widget_init_template (GTK_WIDGET (button)); + /* Bookmarks manager */ + priv->bookmarks_manager = _gtk_bookmarks_manager_new (bookmarks_changed_cb, button); gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_box), priv->name_cell, name_cell_data_func, NULL, NULL); @@ -849,7 +851,7 @@ gtk_file_chooser_button_constructor (GType type, model_add_volumes (button, list); g_slist_free (list); - list = _gtk_file_system_list_bookmarks (priv->fs); + list = _gtk_bookmarks_manager_list_bookmarks (priv->bookmarks_manager); model_add_bookmarks (button, list); g_slist_foreach (list, (GFunc) g_object_unref, NULL); g_slist_free (list); @@ -878,9 +880,6 @@ gtk_file_chooser_button_constructor (GType type, priv->fs_volumes_changed_id = g_signal_connect (priv->fs, "volumes-changed", G_CALLBACK (fs_volumes_changed_cb), object); - priv->fs_bookmarks_changed_id = - g_signal_connect (priv->fs, "bookmarks-changed", - G_CALLBACK (fs_bookmarks_changed_cb), object); update_label_and_image (button); update_combo_box (button); @@ -964,7 +963,7 @@ gtk_file_chooser_button_set_property (GObject *object, case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY: g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value); fs_volumes_changed_cb (priv->fs, button); - fs_bookmarks_changed_cb (priv->fs, button); + bookmarks_changed_cb (button); break; case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE: @@ -1089,11 +1088,16 @@ gtk_file_chooser_button_destroy (GtkWidget *widget) if (priv->fs) { g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id); - g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id); g_object_unref (priv->fs); priv->fs = NULL; } + if (priv->bookmarks_manager) + { + _gtk_bookmarks_manager_free (priv->bookmarks_manager); + priv->bookmarks_manager = NULL; + } + GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget); } @@ -1604,7 +1608,7 @@ set_info_for_file_at_iter (GtkFileChooserButton *button, data = g_new0 (struct SetDisplayNameData, 1); data->button = g_object_ref (button); - data->label = _gtk_file_system_get_bookmark_label (button->priv->fs, file); + data->label = _gtk_bookmarks_manager_get_bookmark_label (button->priv->bookmarks_manager, file); tree_path = gtk_tree_model_get_path (button->priv->model, iter); data->row_ref = gtk_tree_row_reference_new (button->priv->model, tree_path); @@ -1988,7 +1992,7 @@ model_add_bookmarks (GtkFileChooserButton *button, * If we switch to a better bookmarks file format (XBEL), we * should use mime info to get a better icon. */ - label = _gtk_file_system_get_bookmark_label (button->priv->fs, file); + label = _gtk_bookmarks_manager_get_bookmark_label (button->priv->bookmarks_manager, file); if (!label) label = _gtk_file_chooser_label_for_file (file); @@ -2091,7 +2095,7 @@ model_update_current_folder (GtkFileChooserButton *button, * If we switch to a better bookmarks file format (XBEL), we * should use mime info to get a better icon. */ - label = _gtk_file_system_get_bookmark_label (button->priv->fs, file); + label = _gtk_bookmarks_manager_get_bookmark_label (button->priv->bookmarks_manager, file); if (!label) label = _gtk_file_chooser_label_for_file (file); @@ -2548,7 +2552,7 @@ update_label_and_image (GtkFileChooserButton *button) { GdkPixbuf *pixbuf; - label_text = _gtk_file_system_get_bookmark_label (button->priv->fs, file); + label_text = _gtk_bookmarks_manager_get_bookmark_label (button->priv->bookmarks_manager, file); pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)), "text-x-generic", priv->icon_size, 0, NULL); @@ -2616,14 +2620,13 @@ fs_volumes_changed_cb (GtkFileSystem *fs, } static void -fs_bookmarks_changed_cb (GtkFileSystem *fs, - gpointer user_data) +bookmarks_changed_cb (gpointer user_data) { GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data); GtkFileChooserButtonPrivate *priv = button->priv; GSList *bookmarks; - bookmarks = _gtk_file_system_list_bookmarks (fs); + bookmarks = _gtk_bookmarks_manager_list_bookmarks (priv->bookmarks_manager); model_remove_rows (user_data, model_get_type_position (user_data, ROW_TYPE_BOOKMARK_SEPARATOR), diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index 75dcfde859..c6bea3946f 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -17,6 +17,11 @@ * License along with this library. If not, see <http://www.gnu.org/licenses/>. */ +/* TODO: + * + * * Fix FIXME-places-sidebar + */ + #include "config.h" #include "gtkfilechooserdefault.h" @@ -51,6 +56,7 @@ #include "gtkmountoperation.h" #include "gtkpaned.h" #include "gtkpathbar.h" +#include "gtkplacessidebar.h" #include "gtkprivate.h" #include "gtkradiobutton.h" #include "gtkrecentfilter.h" @@ -140,9 +146,35 @@ _gtk_file_chooser_profile_log (const char *func, int indent, const char *msg1, c #endif +typedef enum { + LOAD_EMPTY, /* There is no model */ + LOAD_PRELOAD, /* Model is loading and a timer is running; model isn't inserted into the tree yet */ + LOAD_LOADING, /* Timeout expired, model is inserted into the tree, but not fully loaded yet */ + LOAD_FINISHED /* Model is fully loaded and inserted into the tree */ +} LoadState; -struct _GtkFileChooserDefaultPrivate -{ +typedef enum { + RELOAD_EMPTY, /* No folder has been set */ + RELOAD_HAS_FOLDER /* We have a folder, although it may not be completely loaded yet; no need to reload */ +} ReloadState; + +typedef enum { + LOCATION_MODE_PATH_BAR, + LOCATION_MODE_FILENAME_ENTRY +} LocationMode; + +typedef enum { + OPERATION_MODE_BROWSE, + OPERATION_MODE_SEARCH, + OPERATION_MODE_RECENT +} OperationMode; + +typedef enum { + STARTUP_MODE_RECENT, + STARTUP_MODE_CWD +} StartupMode; + +typedef struct { GtkFileChooserAction action; GtkFileSystem *file_system; @@ -157,14 +189,6 @@ struct _GtkFileChooserDefaultPrivate GtkWidget *browse_widgets_box; GtkWidget *browse_widgets_hpaned; GtkWidget *browse_header_box; - GtkWidget *browse_shortcuts_swin; - GtkWidget *browse_shortcuts_toolbar; - GtkWidget *browse_shortcuts_tree_view; - GtkWidget *browse_shortcuts_add_button; - GtkWidget *browse_shortcuts_remove_button; - GtkWidget *browse_shortcuts_popup_menu; - GtkWidget *browse_shortcuts_popup_menu_remove_item; - GtkWidget *browse_shortcuts_popup_menu_rename_item; GtkWidget *browse_files_tree_view; GtkWidget *browse_files_popup_menu; GtkWidget *browse_files_popup_menu_add_shortcut_item; @@ -185,6 +209,9 @@ struct _GtkFileChooserDefaultPrivate GtkFileSystemModel *browse_files_model; char *browse_files_last_selected_name; + GtkWidget *places_sidebar; + StartupMode startup_mode; + /* OPERATION_MODE_SEARCH */ GtkWidget *search_hbox; GtkWidget *search_entry; @@ -211,13 +238,6 @@ struct _GtkFileChooserDefaultPrivate GtkWidget *location_entry; LocationMode location_mode; - GtkListStore *shortcuts_model; - - /* Filter for the shortcuts pane. We filter out the "current folder" row and - * the separator that we use for the "Save in folder" combo. - */ - GtkTreeModel *shortcuts_pane_filter_model; - /* Handles */ GSList *loading_shortcuts; GSList *reload_icon_cancellables; @@ -239,6 +259,8 @@ struct _GtkFileChooserDefaultPrivate GtkFileFilter *current_filter; GSList *filters; + GtkBookmarksManager *bookmarks_manager; + int num_volumes; int num_shortcuts; int num_bookmarks; @@ -252,8 +274,8 @@ struct _GtkFileChooserDefaultPrivate char *preview_display_name; GtkTreeViewColumn *list_name_column; - GtkCellRenderer *list_pixbuf_renderer; GtkCellRenderer *list_name_renderer; + GtkCellRenderer *list_pixbuf_renderer; GtkTreeViewColumn *list_mtime_column; GtkTreeViewColumn *list_size_column; @@ -271,11 +293,6 @@ struct _GtkFileChooserDefaultPrivate gint sort_column; GtkSortType sort_order; -#if 0 - GdkDragContext *shortcuts_drag_context; - GSource *shortcuts_drag_outside_idle; -#endif - /* Flags */ guint local_only : 1; @@ -293,13 +310,7 @@ struct _GtkFileChooserDefaultPrivate guint has_recent: 1; guint show_size_column : 1; guint create_folders : 1; - -#if 0 - guint shortcuts_drag_outside : 1; -#endif -}; - -typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass; +} GtkFileChooserDefaultPrivate; #define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass)) #define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT)) @@ -309,6 +320,15 @@ typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass; #define DEFAULT_NEW_FOLDER_NAME _("Type name of new folder") +typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass; + +struct _GtkFileChooserDefault +{ + GtkBox parent_instance; + + GtkFileChooserDefaultPrivate *priv; +}; + struct _GtkFileChooserDefaultClass { GtkBoxClass parent_class; @@ -333,26 +353,6 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -/* Column numbers for the shortcuts tree. Keep these in sync with shortcuts_model_create() */ -enum { - SHORTCUTS_COL_PIXBUF, - SHORTCUTS_COL_NAME, - SHORTCUTS_COL_DATA, - SHORTCUTS_COL_TYPE, - SHORTCUTS_COL_REMOVABLE, - SHORTCUTS_COL_PIXBUF_VISIBLE, - SHORTCUTS_COL_CANCELLABLE, - SHORTCUTS_COL_NUM_COLUMNS -}; - -typedef enum { - SHORTCUT_TYPE_FILE, - SHORTCUT_TYPE_VOLUME, - SHORTCUT_TYPE_SEPARATOR, - SHORTCUT_TYPE_SEARCH, - SHORTCUT_TYPE_RECENT -} ShortcutType; - #define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \ "standard::is-hidden,standard::is-backup,standard::size," \ "standard::content-type,time::modified" @@ -392,21 +392,6 @@ enum { GTK_TREE_MODEL_ROW, }; -/* Interesting places in the shortcuts bar */ -typedef enum { - SHORTCUTS_SEARCH, - SHORTCUTS_RECENT, - SHORTCUTS_RECENT_SEPARATOR, - SHORTCUTS_HOME, - SHORTCUTS_DESKTOP, - SHORTCUTS_VOLUMES, - SHORTCUTS_SHORTCUTS, - SHORTCUTS_BOOKMARKS_SEPARATOR, - SHORTCUTS_BOOKMARKS, - SHORTCUTS_CURRENT_FOLDER_SEPARATOR, - SHORTCUTS_CURRENT_FOLDER -} ShortcutsIndex; - /* Icon size for if we can't get it from the theme */ #define FALLBACK_ICON_SIZE 16 @@ -497,6 +482,8 @@ static void search_shortcut_handler (GtkFileChooserDefault *impl); static void recent_shortcut_handler (GtkFileChooserDefault *impl); static void update_appearance (GtkFileChooserDefault *impl); +static void operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode); + static void set_current_filter (GtkFileChooserDefault *impl, GtkFileFilter *filter); static void check_preview_change (GtkFileChooserDefault *impl); @@ -504,26 +491,6 @@ static void check_preview_change (GtkFileChooserDefault *impl); static void filter_combo_changed (GtkComboBox *combo_box, GtkFileChooserDefault *impl); -static gboolean shortcuts_key_press_event_cb (GtkWidget *widget, - GdkEventKey *event, - GtkFileChooserDefault *impl); - -static gboolean shortcuts_select_func (GtkTreeSelection *selection, - GtkTreeModel *model, - GtkTreePath *path, - gboolean path_currently_selected, - gpointer data); -static gboolean shortcuts_get_selected (GtkFileChooserDefault *impl, - GtkTreeIter *iter); -static void shortcuts_activate_iter (GtkFileChooserDefault *impl, - GtkTreeIter *iter); -static int shortcuts_get_index (GtkFileChooserDefault *impl, - ShortcutsIndex where); -static int shortcut_find_position (GtkFileChooserDefault *impl, - GFile *file); - -static void bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl); - static gboolean list_select_func (GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, @@ -543,11 +510,6 @@ static void path_bar_clicked (GtkPathBar *path_bar, gboolean child_is_hidden, GtkFileChooserDefault *impl); -static void add_bookmark_button_clicked_cb (GtkButton *button, - GtkFileChooserDefault *impl); -static void remove_bookmark_button_clicked_cb (GtkButton *button, - GtkFileChooserDefault *impl); - static void update_cell_renderer_attributes (GtkFileChooserDefault *impl); static void load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state); @@ -580,34 +542,6 @@ static GSList * recent_get_selected_files (GtkFileChooserDefault *impl); static void set_file_system_backend (GtkFileChooserDefault *impl); static void unset_file_system_backend (GtkFileChooserDefault *impl); - - - -/* Drag and drop interface declarations */ - -typedef struct { - GtkTreeModelFilter parent; - - GtkFileChooserDefault *impl; -} ShortcutsPaneModelFilter; - -typedef struct { - GtkTreeModelFilterClass parent_class; -} ShortcutsPaneModelFilterClass; - -#define SHORTCUTS_PANE_MODEL_FILTER_TYPE (_shortcuts_pane_model_filter_get_type ()) -#define SHORTCUTS_PANE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_PANE_MODEL_FILTER_TYPE, ShortcutsPaneModelFilter)) - -static void shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface); - -GType _shortcuts_pane_model_filter_get_type (void); - -G_DEFINE_TYPE_WITH_CODE (ShortcutsPaneModelFilter, - _shortcuts_pane_model_filter, - GTK_TYPE_TREE_MODEL_FILTER, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - shortcuts_pane_model_filter_drag_source_iface_init)) - G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_BOX, @@ -616,128 +550,88 @@ G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_T G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED, gtk_file_chooser_embed_default_iface_init)); - -static void -add_normal_and_shifted_binding (GtkBindingSet *binding_set, - guint keyval, - GdkModifierType modifiers, - const gchar *signal_name) -{ - gtk_binding_entry_add_signal (binding_set, - keyval, modifiers, - signal_name, 0); - - gtk_binding_entry_add_signal (binding_set, - keyval, modifiers | GDK_SHIFT_MASK, - signal_name, 0); -} - -/* Frees the data columns for the specified iter in the shortcuts model*/ static void -shortcuts_free_row_data (GtkFileChooserDefault *impl, - GtkTreeIter *iter) +gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface) { - gpointer col_data; - ShortcutType shortcut_type; - GCancellable *cancellable; - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - SHORTCUTS_COL_CANCELLABLE, &cancellable, - -1); - - if (cancellable) - { - g_cancellable_cancel (cancellable); - g_object_unref (cancellable); - } - - if (!(shortcut_type == SHORTCUT_TYPE_FILE || - shortcut_type == SHORTCUT_TYPE_VOLUME) || - !col_data) - return; - - if (shortcut_type == SHORTCUT_TYPE_VOLUME) - { - GtkFileSystemVolume *volume; - - volume = col_data; - _gtk_file_system_volume_unref (volume); - } - if (shortcut_type == SHORTCUT_TYPE_FILE) - { - GFile *file; - - file = col_data; - g_object_unref (file); - } + iface->select_file = gtk_file_chooser_default_select_file; + iface->unselect_file = gtk_file_chooser_default_unselect_file; + iface->select_all = gtk_file_chooser_default_select_all; + iface->unselect_all = gtk_file_chooser_default_unselect_all; + iface->get_files = gtk_file_chooser_default_get_files; + iface->get_preview_file = gtk_file_chooser_default_get_preview_file; + iface->get_file_system = gtk_file_chooser_default_get_file_system; + iface->set_current_folder = gtk_file_chooser_default_set_current_folder; + iface->get_current_folder = gtk_file_chooser_default_get_current_folder; + iface->set_current_name = gtk_file_chooser_default_set_current_name; + iface->add_filter = gtk_file_chooser_default_add_filter; + iface->remove_filter = gtk_file_chooser_default_remove_filter; + iface->list_filters = gtk_file_chooser_default_list_filters; + iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder; + iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder; + iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders; } -/* Frees all the data columns in the shortcuts model */ static void -shortcuts_free (GtkFileChooserDefault *impl) +gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface) { - GtkTreeIter iter; - - if (!impl->priv->shortcuts_model) - return; - - if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter)) - do - { - shortcuts_free_row_data (impl, &iter); - } - while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter)); - - impl->priv->shortcuts_model = NULL; + iface->get_default_size = gtk_file_chooser_default_get_default_size; + iface->should_respond = gtk_file_chooser_default_should_respond; + iface->initial_focus = gtk_file_chooser_default_initial_focus; } static void pending_select_files_free (GtkFileChooserDefault *impl) { - g_slist_free_full (impl->priv->pending_select_files, g_object_unref); - impl->priv->pending_select_files = NULL; + GtkFileChooserDefaultPrivate *priv = impl->priv; + + g_slist_free_full (priv->pending_select_files, g_object_unref); + priv->pending_select_files = NULL; } static void pending_select_files_add (GtkFileChooserDefault *impl, GFile *file) { - impl->priv->pending_select_files = - g_slist_prepend (impl->priv->pending_select_files, g_object_ref (file)); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + priv->pending_select_files = + g_slist_prepend (priv->pending_select_files, g_object_ref (file)); } static void gtk_file_chooser_default_finalize (GObject *object) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object); + GtkFileChooserDefaultPrivate *priv = impl->priv; GSList *l; unset_file_system_backend (impl); - g_free (impl->priv->browse_files_last_selected_name); + g_free (priv->browse_files_last_selected_name); - for (l = impl->priv->filters; l; l = l->next) + for (l = priv->filters; l; l = l->next) { GtkFileFilter *filter; filter = GTK_FILE_FILTER (l->data); g_object_unref (filter); } - g_slist_free (impl->priv->filters); + g_slist_free (priv->filters); + + if (priv->current_filter) + g_object_unref (priv->current_filter); - if (impl->priv->current_filter) - g_object_unref (impl->priv->current_filter); + if (priv->current_volume_file) + g_object_unref (priv->current_volume_file); - if (impl->priv->current_volume_file) - g_object_unref (impl->priv->current_volume_file); + if (priv->current_folder) + g_object_unref (priv->current_folder); - if (impl->priv->current_folder) - g_object_unref (impl->priv->current_folder); + if (priv->preview_file) + g_object_unref (priv->preview_file); - if (impl->priv->preview_file) - g_object_unref (impl->priv->preview_file); + if (priv->browse_path_bar_size_group) + g_object_unref (priv->browse_path_bar_size_group); /* Free all the Models we have */ stop_loading_and_clear_list_model (impl, FALSE); @@ -745,11 +639,13 @@ gtk_file_chooser_default_finalize (GObject *object) recent_clear_model (impl, FALSE); /* stopping the load above should have cleared this */ - g_assert (impl->priv->load_timeout_id == 0); + g_assert (priv->load_timeout_id == 0); + + g_free (priv->preview_display_name); - g_free (impl->priv->preview_display_name); + g_free (priv->edited_new_text); - g_free (impl->priv->edited_new_text); + impl->priv = NULL; G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object); } @@ -823,41 +719,6 @@ error_dialog (GtkFileChooserDefault *impl, } } -/* Displays an error message about not being able to get information for a file. - * Frees the GError as well. - */ -static void -error_getting_info_dialog (GtkFileChooserDefault *impl, - GFile *file, - GError *error) -{ - error_dialog (impl, - _("Could not retrieve information about the file"), - file, error); -} - -/* Shows an error dialog about not being able to add a bookmark */ -static void -error_adding_bookmark_dialog (GtkFileChooserDefault *impl, - GFile *file, - GError *error) -{ - error_dialog (impl, - _("Could not add a bookmark"), - file, error); -} - -/* Shows an error dialog about not being able to remove a bookmark */ -static void -error_removing_bookmark_dialog (GtkFileChooserDefault *impl, - GFile *file, - GError *error) -{ - error_dialog (impl, - _("Could not remove bookmark"), - file, error); -} - /* Shows an error dialog about not being able to create a folder */ static void error_creating_folder_dialog (GtkFileChooserDefault *impl, @@ -971,30 +832,32 @@ emit_default_size_changed (GtkFileChooserDefault *impl) static void update_preview_widget_visibility (GtkFileChooserDefault *impl) { - if (impl->priv->use_preview_label) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->use_preview_label) { - if (!impl->priv->preview_label) + if (!priv->preview_label) { - impl->priv->preview_label = gtk_label_new (impl->priv->preview_display_name); - gtk_box_pack_start (GTK_BOX (impl->priv->preview_box), impl->priv->preview_label, FALSE, FALSE, 0); - gtk_box_reorder_child (GTK_BOX (impl->priv->preview_box), impl->priv->preview_label, 0); - gtk_label_set_ellipsize (GTK_LABEL (impl->priv->preview_label), PANGO_ELLIPSIZE_MIDDLE); - gtk_widget_show (impl->priv->preview_label); + priv->preview_label = gtk_label_new (priv->preview_display_name); + gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_label, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (priv->preview_box), priv->preview_label, 0); + gtk_label_set_ellipsize (GTK_LABEL (priv->preview_label), PANGO_ELLIPSIZE_MIDDLE); + gtk_widget_show (priv->preview_label); } } else { - if (impl->priv->preview_label) + if (priv->preview_label) { - gtk_widget_destroy (impl->priv->preview_label); - impl->priv->preview_label = NULL; + gtk_widget_destroy (priv->preview_label); + priv->preview_label = NULL; } } - if (impl->priv->preview_widget_active && impl->priv->preview_widget) - gtk_widget_show (impl->priv->preview_box); + if (priv->preview_widget_active && priv->preview_widget) + gtk_widget_show (priv->preview_box); else - gtk_widget_hide (impl->priv->preview_box); + gtk_widget_hide (priv->preview_box); if (!gtk_widget_get_mapped (GTK_WIDGET (impl))) emit_default_size_changed (impl); @@ -1004,413 +867,28 @@ static void set_preview_widget (GtkFileChooserDefault *impl, GtkWidget *preview_widget) { - if (preview_widget == impl->priv->preview_widget) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (preview_widget == priv->preview_widget) return; - if (impl->priv->preview_widget) - gtk_container_remove (GTK_CONTAINER (impl->priv->preview_box), - impl->priv->preview_widget); + if (priv->preview_widget) + gtk_container_remove (GTK_CONTAINER (priv->preview_box), + priv->preview_widget); - impl->priv->preview_widget = preview_widget; - if (impl->priv->preview_widget) + priv->preview_widget = preview_widget; + if (priv->preview_widget) { - gtk_widget_show (impl->priv->preview_widget); - gtk_box_pack_start (GTK_BOX (impl->priv->preview_box), impl->priv->preview_widget, TRUE, TRUE, 0); - gtk_box_reorder_child (GTK_BOX (impl->priv->preview_box), - impl->priv->preview_widget, - (impl->priv->use_preview_label && impl->priv->preview_label) ? 1 : 0); + gtk_widget_show (priv->preview_widget); + gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_widget, TRUE, TRUE, 0); + gtk_box_reorder_child (GTK_BOX (priv->preview_box), + priv->preview_widget, + (priv->use_preview_label && priv->preview_label) ? 1 : 0); } update_preview_widget_visibility (impl); } -/* Renders a "Search" icon at an appropriate size for a tree view */ -static GdkPixbuf * -render_search_icon (GtkFileChooserDefault *impl) -{ - return gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FIND, GTK_ICON_SIZE_MENU); -} - -static GdkPixbuf * -render_recent_icon (GtkFileChooserDefault *impl) -{ - GtkIconTheme *theme; - GdkPixbuf *retval; - - if (gtk_widget_has_screen (GTK_WIDGET (impl))) - theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); - else - theme = gtk_icon_theme_get_default (); - - retval = gtk_icon_theme_load_icon (theme, "document-open-recent", - impl->priv->icon_size, 0, - NULL); - - /* fallback */ - if (!retval) - retval = gtk_widget_render_icon_pixbuf (GTK_WIDGET (impl), GTK_STOCK_FILE, GTK_ICON_SIZE_MENU); - - return retval; -} - - -/* Re-reads all the icons for the shortcuts, used when the theme changes */ -struct ReloadIconsData -{ - GtkFileChooserDefault *impl; - GtkTreeRowReference *row_ref; -}; - -static void -shortcuts_reload_icons_get_info_cb (GCancellable *cancellable, - GFileInfo *info, - const GError *error, - gpointer user_data) -{ - GdkPixbuf *pixbuf; - GtkTreeIter iter; - GtkTreePath *path; - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - struct ReloadIconsData *data = user_data; - - if (!g_slist_find (data->impl->priv->reload_icon_cancellables, cancellable)) - goto out; - - data->impl->priv->reload_icon_cancellables = g_slist_remove (data->impl->priv->reload_icon_cancellables, cancellable); - - if (cancelled || error) - goto out; - - pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->impl), data->impl->priv->icon_size); - - path = gtk_tree_row_reference_get_path (data->row_ref); - if (path) - { - gtk_tree_model_get_iter (GTK_TREE_MODEL (data->impl->priv->shortcuts_model), &iter, path); - gtk_list_store_set (data->impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_PIXBUF, pixbuf, - -1); - gtk_tree_path_free (path); - } - - if (pixbuf) - g_object_unref (pixbuf); - -out: - gtk_tree_row_reference_free (data->row_ref); - g_object_unref (data->impl); - g_free (data); - - g_object_unref (cancellable); -} - -static void -shortcuts_reload_icons (GtkFileChooserDefault *impl) -{ - GSList *l; - GtkTreeIter iter; - - profile_start ("start", NULL); - - if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter)) - goto out; - - for (l = impl->priv->reload_icon_cancellables; l; l = l->next) - { - GCancellable *cancellable = G_CANCELLABLE (l->data); - g_cancellable_cancel (cancellable); - } - g_slist_free (impl->priv->reload_icon_cancellables); - impl->priv->reload_icon_cancellables = NULL; - - do - { - gpointer data; - ShortcutType shortcut_type; - gboolean pixbuf_visible; - GdkPixbuf *pixbuf; - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_DATA, &data, - SHORTCUTS_COL_TYPE, &shortcut_type, - SHORTCUTS_COL_PIXBUF_VISIBLE, &pixbuf_visible, - -1); - - pixbuf = NULL; - if (pixbuf_visible) - { - if (shortcut_type == SHORTCUT_TYPE_VOLUME) - { - GtkFileSystemVolume *volume; - - volume = data; - pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl), - impl->priv->icon_size, NULL); - } - else if (shortcut_type == SHORTCUT_TYPE_FILE) - { - if (g_file_is_native (G_FILE (data))) - { - GFile *file; - struct ReloadIconsData *info; - GtkTreePath *tree_path; - GCancellable *cancellable; - - file = data; - - info = g_new0 (struct ReloadIconsData, 1); - info->impl = g_object_ref (impl); - tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter); - info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->priv->shortcuts_model), tree_path); - gtk_tree_path_free (tree_path); - - cancellable = _gtk_file_system_get_info (impl->priv->file_system, file, - "standard::icon", - shortcuts_reload_icons_get_info_cb, - info); - impl->priv->reload_icon_cancellables = g_slist_append (impl->priv->reload_icon_cancellables, cancellable); - } - else - { - GtkIconTheme *icon_theme; - - /* Don't call get_info for remote paths to avoid latency and - * auth dialogs. - * If we switch to a better bookmarks file format (XBEL), we - * should use mime info to get a better icon. - */ - icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); - pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", - impl->priv->icon_size, 0, NULL); - } - } - else if (shortcut_type == SHORTCUT_TYPE_SEARCH) - { - pixbuf = render_search_icon (impl); - } - else if (shortcut_type == SHORTCUT_TYPE_RECENT) - { - pixbuf = render_recent_icon (impl); - } - - gtk_list_store_set (impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_PIXBUF, pixbuf, - -1); - - if (pixbuf) - g_object_unref (pixbuf); - - } - } - while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->priv->shortcuts_model),&iter)); - - out: - - profile_end ("end", NULL); -} - -static void -shortcuts_find_folder (GtkFileChooserDefault *impl, - GFile *folder) -{ - GtkTreeSelection *selection; - int pos; - GtkTreePath *path; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view)); - - g_assert (folder != NULL); - pos = shortcut_find_position (impl, folder); - if (pos == -1) - { - gtk_tree_selection_unselect_all (selection); - return; - } - - path = gtk_tree_path_new_from_indices (pos, -1); - gtk_tree_selection_select_path (selection, path); - gtk_tree_path_free (path); -} - -/* If a shortcut corresponds to the current folder, selects it */ -static void -shortcuts_find_current_folder (GtkFileChooserDefault *impl) -{ - shortcuts_find_folder (impl, impl->priv->current_folder); -} - -/* Removes the specified number of rows from the shortcuts list */ -static void -shortcuts_remove_rows (GtkFileChooserDefault *impl, - int start_row, - int n_rows) -{ - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices (start_row, -1); - - for (; n_rows; n_rows--) - { - GtkTreeIter iter; - - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, path)) - g_assert_not_reached (); - - shortcuts_free_row_data (impl, &iter); - gtk_list_store_remove (impl->priv->shortcuts_model, &iter); - } - - gtk_tree_path_free (path); -} - -static void -shortcuts_update_count (GtkFileChooserDefault *impl, - ShortcutsIndex type, - gint value) -{ - switch (type) - { - case SHORTCUTS_HOME: - if (value < 0) - impl->priv->has_home = FALSE; - else - impl->priv->has_home = TRUE; - break; - - case SHORTCUTS_DESKTOP: - if (value < 0) - impl->priv->has_desktop = FALSE; - else - impl->priv->has_desktop = TRUE; - break; - - case SHORTCUTS_VOLUMES: - impl->priv->num_volumes += value; - break; - - case SHORTCUTS_SHORTCUTS: - impl->priv->num_shortcuts += value; - break; - - case SHORTCUTS_BOOKMARKS: - impl->priv->num_bookmarks += value; - break; - - case SHORTCUTS_CURRENT_FOLDER: - if (value < 0) - impl->priv->shortcuts_current_folder_active = FALSE; - else - impl->priv->shortcuts_current_folder_active = TRUE; - break; - - default: - /* nothing */ - break; - } -} - -struct ShortcutsInsertRequest -{ - GtkFileChooserDefault *impl; - GFile *file; - int pos; - char *label_copy; - GtkTreeRowReference *row_ref; - ShortcutsIndex type; - gboolean name_only; - gboolean removable; -}; - -static void -get_file_info_finished (GCancellable *cancellable, - GFileInfo *info, - const GError *error, - gpointer data) -{ - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - GdkPixbuf *pixbuf; - GtkTreePath *path; - GtkTreeIter iter; - GCancellable *model_cancellable = NULL; - struct ShortcutsInsertRequest *request = data; - - path = gtk_tree_row_reference_get_path (request->row_ref); - if (!path) - /* Handle doesn't exist anymore in the model */ - goto out; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->priv->shortcuts_model), - &iter, path); - gtk_tree_path_free (path); - - /* validate cancellable, else goto out */ - gtk_tree_model_get (GTK_TREE_MODEL (request->impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_CANCELLABLE, &model_cancellable, - -1); - if (cancellable != model_cancellable) - goto out; - - /* set the cancellable to NULL in the model (we unref later on) */ - gtk_list_store_set (request->impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_CANCELLABLE, NULL, - -1); - - if (cancelled) - goto out; - - if (!info) - { - gtk_list_store_remove (request->impl->priv->shortcuts_model, &iter); - shortcuts_update_count (request->impl, request->type, -1); - - if (request->type == SHORTCUTS_HOME) - { - GFile *home; - - home = g_file_new_for_path (g_get_home_dir ()); - error_getting_info_dialog (request->impl, home, g_error_copy (error)); - g_object_unref (home); - } - else if (request->type == SHORTCUTS_CURRENT_FOLDER) - { - /* Remove the current folder separator */ - gint separator_pos = shortcuts_get_index (request->impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR); - shortcuts_remove_rows (request->impl, separator_pos, 1); - } - - goto out; - } - - if (!request->label_copy) - request->label_copy = g_strdup (g_file_info_get_display_name (info)); - pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl), - request->impl->priv->icon_size); - - gtk_list_store_set (request->impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_PIXBUF, pixbuf, - SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE, - SHORTCUTS_COL_NAME, request->label_copy, - SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE, - SHORTCUTS_COL_REMOVABLE, request->removable, - -1); - - if (request->impl->priv->shortcuts_pane_filter_model) - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (request->impl->priv->shortcuts_pane_filter_model)); - - if (pixbuf) - g_object_unref (pixbuf); - -out: - g_object_unref (request->impl); - g_object_unref (request->file); - gtk_tree_row_reference_free (request->row_ref); - g_free (request->label_copy); - g_free (request); - - if (model_cancellable) - g_object_unref (model_cancellable); -} - /* FIXME: GtkFileSystem needs a function to split a remote path * into hostname and path components, or maybe just have a * gtk_file_system_path_get_display_name(). @@ -1467,639 +945,32 @@ _gtk_file_chooser_label_for_file (GFile *file) return label; } -/* Inserts a path in the shortcuts tree, making a copy of it; alternatively, - * inserts a volume. A position of -1 indicates the end of the tree. - */ -static void -shortcuts_insert_file (GtkFileChooserDefault *impl, - int pos, - ShortcutType shortcut_type, - GtkFileSystemVolume *volume, - GFile *file, - const char *label, - gboolean removable, - ShortcutsIndex type) -{ - char *label_copy; - GdkPixbuf *pixbuf = NULL; - gpointer data = NULL; - GtkTreeIter iter; - GtkIconTheme *icon_theme; - - profile_start ("start shortcut", NULL); - - if (shortcut_type == SHORTCUT_TYPE_VOLUME) - { - data = volume; - label_copy = _gtk_file_system_volume_get_display_name (volume); - pixbuf = _gtk_file_system_volume_render_icon (volume, GTK_WIDGET (impl), - impl->priv->icon_size, NULL); - } - else if (shortcut_type == SHORTCUT_TYPE_FILE) - { - if (g_file_is_native (file)) - { - struct ShortcutsInsertRequest *request; - GCancellable *cancellable; - GtkTreePath *p; - - request = g_new0 (struct ShortcutsInsertRequest, 1); - request->impl = g_object_ref (impl); - request->file = g_object_ref (file); - request->name_only = TRUE; - request->removable = removable; - request->pos = pos; - request->type = type; - if (label) - request->label_copy = g_strdup (label); - - if (pos == -1) - gtk_list_store_append (impl->priv->shortcuts_model, &iter); - else - gtk_list_store_insert (impl->priv->shortcuts_model, &iter, pos); - - p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter); - request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->priv->shortcuts_model), p); - gtk_tree_path_free (p); - - cancellable = _gtk_file_system_get_info (request->impl->priv->file_system, request->file, - "standard::is-hidden,standard::is-backup,standard::display-name,standard::icon", - get_file_info_finished, request); - - gtk_list_store_set (impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_DATA, g_object_ref (file), - SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_FILE, - SHORTCUTS_COL_CANCELLABLE, cancellable, - -1); - - shortcuts_update_count (impl, type, 1); - - return; - } - else - { - /* Don't call get_info for remote paths to avoid latency and - * auth dialogs. - */ - data = g_object_ref (file); - if (label) - label_copy = g_strdup (label); - else - label_copy = _gtk_file_chooser_label_for_file (file); - - /* If we switch to a better bookmarks file format (XBEL), we - * should use mime info to get a better icon. - */ - icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); - pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote", - impl->priv->icon_size, 0, NULL); - } - } - else - { - g_assert_not_reached (); - - return; - } - - if (pos == -1) - gtk_list_store_append (impl->priv->shortcuts_model, &iter); - else - gtk_list_store_insert (impl->priv->shortcuts_model, &iter, pos); - - shortcuts_update_count (impl, type, 1); - - gtk_list_store_set (impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_PIXBUF, pixbuf, - SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE, - SHORTCUTS_COL_NAME, label_copy, - SHORTCUTS_COL_DATA, data, - SHORTCUTS_COL_TYPE, shortcut_type, - SHORTCUTS_COL_REMOVABLE, removable, - SHORTCUTS_COL_CANCELLABLE, NULL, - -1); - - if (impl->priv->shortcuts_pane_filter_model) - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->priv->shortcuts_pane_filter_model)); - - g_free (label_copy); - - if (pixbuf) - g_object_unref (pixbuf); - - profile_end ("end", NULL); -} - -static void -shortcuts_append_search (GtkFileChooserDefault *impl) -{ - GdkPixbuf *pixbuf; - GtkTreeIter iter; - - pixbuf = render_search_icon (impl); - - gtk_list_store_append (impl->priv->shortcuts_model, &iter); - gtk_list_store_set (impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_PIXBUF, pixbuf, - SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE, - SHORTCUTS_COL_NAME, _("Search"), - SHORTCUTS_COL_DATA, NULL, - SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEARCH, - SHORTCUTS_COL_REMOVABLE, FALSE, - -1); - - if (pixbuf) - g_object_unref (pixbuf); - - impl->priv->has_search = TRUE; -} - -static gboolean -shortcuts_get_recent_enabled (GtkWidget *widget) -{ - GtkSettings *settings; - gboolean enabled; - - if (gtk_widget_has_screen (widget)) - settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget)); - else - settings = gtk_settings_get_default (); - - g_object_get (settings, "gtk-recent-files-enabled", &enabled, NULL); - return enabled; -} - -static void -shortcuts_append_recent (GtkFileChooserDefault *impl) -{ - GdkPixbuf *pixbuf; - GtkTreeIter iter; - gboolean enabled; - - enabled = shortcuts_get_recent_enabled (GTK_WIDGET (impl)); - if (!enabled) - return; - - pixbuf = render_recent_icon (impl); - - gtk_list_store_append (impl->priv->shortcuts_model, &iter); - gtk_list_store_set (impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_PIXBUF, pixbuf, - SHORTCUTS_COL_PIXBUF_VISIBLE, TRUE, - SHORTCUTS_COL_NAME, _("Recently Used"), - SHORTCUTS_COL_DATA, NULL, - SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_RECENT, - SHORTCUTS_COL_REMOVABLE, FALSE, - -1); - - if (pixbuf) - g_object_unref (pixbuf); - - impl->priv->has_recent = TRUE; -} - -/* Appends an item for the user's home directory to the shortcuts model */ -static void -shortcuts_append_home (GtkFileChooserDefault *impl) -{ - const char *home_path; - GFile *home; - - profile_start ("start", NULL); - - home_path = g_get_home_dir (); - if (home_path == NULL) - { - profile_end ("end - no home directory!?", NULL); - return; - } - - home = g_file_new_for_path (home_path); - shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, home, NULL, FALSE, SHORTCUTS_HOME); - impl->priv->has_home = TRUE; - - g_object_unref (home); - - profile_end ("end", NULL); -} - -/* Appends the ~/Desktop directory to the shortcuts model */ -static void -shortcuts_append_desktop (GtkFileChooserDefault *impl) -{ - const char *name; - GFile *file; - - profile_start ("start", NULL); - - name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); - /* "To disable a directory, point it to the homedir." - * See http://freedesktop.org/wiki/Software/xdg-user-dirs - **/ - if (!g_strcmp0 (name, g_get_home_dir ())) - { - profile_end ("end", NULL); - return; - } - - file = g_file_new_for_path (name); - shortcuts_insert_file (impl, -1, SHORTCUT_TYPE_FILE, NULL, file, _("Desktop"), FALSE, SHORTCUTS_DESKTOP); - impl->priv->has_desktop = TRUE; - - /* We do not actually pop up an error dialog if there is no desktop directory - * because some people may really not want to have one. - */ - - g_object_unref (file); - - profile_end ("end", NULL); -} - -/* Appends a list of GFile to the shortcuts model; returns how many were inserted */ -static int -shortcuts_append_bookmarks (GtkFileChooserDefault *impl, - GSList *bookmarks) -{ - int start_row; - int num_inserted; - gchar *label; - - profile_start ("start", NULL); - - start_row = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR) + 1; - num_inserted = 0; - - for (; bookmarks; bookmarks = bookmarks->next) - { - GFile *file; - - file = bookmarks->data; - - if (impl->priv->local_only && !_gtk_file_has_native_path (file)) - continue; - - if (shortcut_find_position (impl, file) != -1) - continue; - - label = _gtk_file_system_get_bookmark_label (impl->priv->file_system, file); - - shortcuts_insert_file (impl, start_row + num_inserted, SHORTCUT_TYPE_FILE, NULL, file, label, TRUE, SHORTCUTS_BOOKMARKS); - g_free (label); - - num_inserted++; - } - - profile_end ("end", NULL); - - return num_inserted; -} - -/* Returns the index for the corresponding item in the shortcuts bar */ -static int -shortcuts_get_index (GtkFileChooserDefault *impl, - ShortcutsIndex where) -{ - int n; - - n = 0; - - if (where == SHORTCUTS_SEARCH) - goto out; - - n += impl->priv->has_search ? 1 : 0; - - if (where == SHORTCUTS_RECENT) - goto out; - - n += impl->priv->has_recent ? 1 : 0; - - if (where == SHORTCUTS_RECENT_SEPARATOR) - goto out; - - n += 1; /* we always have the separator after the recently-used item */ - - if (where == SHORTCUTS_HOME) - goto out; - - n += impl->priv->has_home ? 1 : 0; - - if (where == SHORTCUTS_DESKTOP) - goto out; - - n += impl->priv->has_desktop ? 1 : 0; - - if (where == SHORTCUTS_VOLUMES) - goto out; - - n += impl->priv->num_volumes; - - if (where == SHORTCUTS_SHORTCUTS) - goto out; - - n += impl->priv->num_shortcuts; - - if (where == SHORTCUTS_BOOKMARKS_SEPARATOR) - goto out; - - /* If there are no bookmarks there won't be a separator */ - n += (impl->priv->num_bookmarks > 0) ? 1 : 0; - - if (where == SHORTCUTS_BOOKMARKS) - goto out; - - n += impl->priv->num_bookmarks; - - if (where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR) - goto out; - - n += 1; - - if (where == SHORTCUTS_CURRENT_FOLDER) - goto out; - - g_assert_not_reached (); - - out: - - return n; -} - -/* Adds all the file system volumes to the shortcuts model */ -static void -shortcuts_add_volumes (GtkFileChooserDefault *impl) -{ - int start_row; - GSList *list, *l; - int n; - gboolean old_changing_folders; - - profile_start ("start", NULL); - - old_changing_folders = impl->priv->changing_folder; - impl->priv->changing_folder = TRUE; - - start_row = shortcuts_get_index (impl, SHORTCUTS_VOLUMES); - shortcuts_remove_rows (impl, start_row, impl->priv->num_volumes); - impl->priv->num_volumes = 0; - - list = _gtk_file_system_list_volumes (impl->priv->file_system); - - n = 0; - - for (l = list; l; l = l->next) - { - GtkFileSystemVolume *volume; - - volume = l->data; - - if (impl->priv->local_only) - { - if (_gtk_file_system_volume_is_mounted (volume)) - { - GFile *base_file; - gboolean base_has_native_path = FALSE; - - base_file = _gtk_file_system_volume_get_root (volume); - if (base_file != NULL) - { - base_has_native_path = _gtk_file_has_native_path (base_file); - g_object_unref (base_file); - } - - if (!base_has_native_path) - continue; - } - } - - shortcuts_insert_file (impl, - start_row + n, - SHORTCUT_TYPE_VOLUME, - _gtk_file_system_volume_ref (volume), - NULL, - NULL, - FALSE, - SHORTCUTS_VOLUMES); - n++; - } - - impl->priv->num_volumes = n; - g_slist_free (list); - - if (impl->priv->shortcuts_pane_filter_model) - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->priv->shortcuts_pane_filter_model)); - - impl->priv->changing_folder = old_changing_folders; - - profile_end ("end", NULL); -} - -/* Inserts a separator node in the shortcuts list */ -static void -shortcuts_insert_separator (GtkFileChooserDefault *impl, - ShortcutsIndex where) -{ - GtkTreeIter iter; - - g_assert (where == SHORTCUTS_RECENT_SEPARATOR || - where == SHORTCUTS_BOOKMARKS_SEPARATOR || - where == SHORTCUTS_CURRENT_FOLDER_SEPARATOR); - - gtk_list_store_insert (impl->priv->shortcuts_model, &iter, - shortcuts_get_index (impl, where)); - gtk_list_store_set (impl->priv->shortcuts_model, &iter, - SHORTCUTS_COL_PIXBUF, NULL, - SHORTCUTS_COL_PIXBUF_VISIBLE, FALSE, - SHORTCUTS_COL_NAME, NULL, - SHORTCUTS_COL_DATA, NULL, - SHORTCUTS_COL_TYPE, SHORTCUT_TYPE_SEPARATOR, - -1); -} - -/* Updates the list of bookmarks */ -static void -shortcuts_add_bookmarks (GtkFileChooserDefault *impl) -{ - GSList *bookmarks; - gboolean old_changing_folders; - GtkTreeIter iter; - GFile *list_selected = NULL; - ShortcutType shortcut_type; - gpointer col_data; - - profile_start ("start", NULL); - - old_changing_folders = impl->priv->changing_folder; - impl->priv->changing_folder = TRUE; - - if (shortcuts_get_selected (impl, &iter)) - { - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), - &iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - -1); - - if (col_data && shortcut_type == SHORTCUT_TYPE_FILE) - list_selected = g_object_ref (col_data); - } - - if (impl->priv->num_bookmarks > 0) - shortcuts_remove_rows (impl, - shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), - impl->priv->num_bookmarks + 1); - - impl->priv->num_bookmarks = 0; - shortcuts_insert_separator (impl, SHORTCUTS_BOOKMARKS_SEPARATOR); - - bookmarks = _gtk_file_system_list_bookmarks (impl->priv->file_system); - shortcuts_append_bookmarks (impl, bookmarks); - g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL); - g_slist_free (bookmarks); - - if (impl->priv->num_bookmarks == 0) - shortcuts_remove_rows (impl, shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR), 1); - - if (impl->priv->shortcuts_pane_filter_model) - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (impl->priv->shortcuts_pane_filter_model)); - - if (list_selected) - { - shortcuts_find_folder (impl, list_selected); - g_object_unref (list_selected); - } - - impl->priv->changing_folder = old_changing_folders; - - profile_end ("end", NULL); -} - -/* Appends a separator and a row to the shortcuts list for the current folder */ -static void -shortcuts_add_current_folder (GtkFileChooserDefault *impl) -{ - int pos; - - g_assert (!impl->priv->shortcuts_current_folder_active); - - g_assert (impl->priv->current_folder != NULL); - - pos = shortcut_find_position (impl, impl->priv->current_folder); - if (pos == -1) - { - GtkFileSystemVolume *volume; - GFile *base_file; - - /* Separator */ - shortcuts_insert_separator (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR); - - /* Item */ - pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER); - - volume = _gtk_file_system_get_volume_for_file (impl->priv->file_system, impl->priv->current_folder); - if (volume) - base_file = _gtk_file_system_volume_get_root (volume); - else - base_file = NULL; - - if (base_file && g_file_equal (base_file, impl->priv->current_folder)) - shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_VOLUME, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER); - else - shortcuts_insert_file (impl, pos, SHORTCUT_TYPE_FILE, NULL, impl->priv->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER); - - if (base_file) - g_object_unref (base_file); - } -} - -/* Updates the current folder row in the shortcuts model */ -static void -shortcuts_update_current_folder (GtkFileChooserDefault *impl) -{ - int pos; - - pos = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR); - - if (impl->priv->shortcuts_current_folder_active) - { - shortcuts_remove_rows (impl, pos, 2); - impl->priv->shortcuts_current_folder_active = FALSE; - } - - shortcuts_add_current_folder (impl); -} - -/* Filter function used for the shortcuts filter model */ -static gboolean -shortcuts_pane_filter_cb (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - GtkFileChooserDefault *impl; - GtkTreePath *path; - int pos; - - impl = GTK_FILE_CHOOSER_DEFAULT (data); - - path = gtk_tree_model_get_path (model, iter); - if (!path) - return FALSE; - - pos = *gtk_tree_path_get_indices (path); - gtk_tree_path_free (path); - - return (pos < shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR)); -} - -/* Creates the list model for shortcuts */ -static void -shortcuts_model_populate (GtkFileChooserDefault *impl) -{ - shortcuts_append_search (impl); - - if (impl->priv->recent_manager) - { - shortcuts_append_recent (impl); - shortcuts_insert_separator (impl, SHORTCUTS_RECENT_SEPARATOR); - } - - if (impl->priv->file_system) - { - shortcuts_append_home (impl); - shortcuts_append_desktop (impl); - shortcuts_add_volumes (impl); - } - - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (impl->priv->shortcuts_pane_filter_model), - shortcuts_pane_filter_cb, - impl, - NULL); -} - /* Callback used when the "New Folder" button is clicked */ static void new_folder_button_clicked (GtkButton *button, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeIter iter; GtkTreePath *path; - if (!impl->priv->browse_files_model) + if (!priv->browse_files_model) return; /* FIXME: this sucks. Disable the New Folder button or something. */ /* Prevent button from being clicked twice */ - gtk_widget_set_sensitive (impl->priv->browse_new_folder_button, FALSE); + gtk_widget_set_sensitive (priv->browse_new_folder_button, FALSE); - _gtk_file_system_model_add_editable (impl->priv->browse_files_model, &iter); + _gtk_file_system_model_add_editable (priv->browse_files_model, &iter); - path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->priv->browse_files_model), &iter); - gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), - path, impl->priv->list_name_column, + path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->browse_files_model), &iter); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->browse_files_tree_view), + path, priv->list_name_column, FALSE, 0.0, 0.0); - g_object_set (impl->priv->list_name_renderer, "editable", TRUE, NULL); - gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), + g_object_set (priv->list_name_renderer, "editable", TRUE, NULL); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), path, - impl->priv->list_name_column, + priv->list_name_column, TRUE); gtk_tree_path_free (path); @@ -2124,25 +995,27 @@ add_idle_while_impl_is_alive (GtkFileChooserDefault *impl, GCallback callback) static gboolean edited_idle_cb (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + gdk_threads_enter (); - g_source_destroy (impl->priv->edited_idle); - impl->priv->edited_idle = NULL; + g_source_destroy (priv->edited_idle); + priv->edited_idle = NULL; - _gtk_file_system_model_remove_editable (impl->priv->browse_files_model); - g_object_set (impl->priv->list_name_renderer, "editable", FALSE, NULL); + _gtk_file_system_model_remove_editable (priv->browse_files_model); + g_object_set (priv->list_name_renderer, "editable", FALSE, NULL); - gtk_widget_set_sensitive (impl->priv->browse_new_folder_button, TRUE); + gtk_widget_set_sensitive (priv->browse_new_folder_button, TRUE); - if (impl->priv->edited_new_text /* not cancelled? */ - && (strlen (impl->priv->edited_new_text) != 0) - && (strcmp (impl->priv->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */ + if (priv->edited_new_text /* not cancelled? */ + && (strlen (priv->edited_new_text) != 0) + && (strcmp (priv->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */ { GError *error = NULL; GFile *file; - file = g_file_get_child_for_display_name (impl->priv->current_folder, - impl->priv->edited_new_text, + file = g_file_get_child_for_display_name (priv->current_folder, + priv->edited_new_text, &error); if (file) { @@ -2158,8 +1031,8 @@ edited_idle_cb (GtkFileChooserDefault *impl) else error_creating_folder_dialog (impl, file, error); - g_free (impl->priv->edited_new_text); - impl->priv->edited_new_text = NULL; + g_free (priv->edited_new_text); + priv->edited_new_text = NULL; } gdk_threads_leave (); @@ -2171,15 +1044,17 @@ static void queue_edited_idle (GtkFileChooserDefault *impl, const gchar *new_text) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + /* We create the folder in an idle handler so that we don't modify the tree * just now. */ - if (!impl->priv->edited_idle) - impl->priv->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb)); + if (!priv->edited_idle) + priv->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb)); - g_free (impl->priv->edited_new_text); - impl->priv->edited_new_text = g_strdup (new_text); + g_free (priv->edited_new_text); + priv->edited_new_text = g_strdup (new_text); } /* Callback used from the text cell renderer when the new folder is named */ @@ -2208,204 +1083,6 @@ renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text, queue_edited_idle (impl, NULL); } -/* Looks for a path among the shortcuts; returns its index or -1 if it doesn't exist */ -static int -shortcut_find_position (GtkFileChooserDefault *impl, - GFile *file) -{ - GtkTreeIter iter; - int i; - int current_folder_separator_idx; - - if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter)) - return -1; - - current_folder_separator_idx = shortcuts_get_index (impl, SHORTCUTS_CURRENT_FOLDER_SEPARATOR); - -#if 0 - /* FIXME: is this still needed? */ - if (current_folder_separator_idx >= impl->priv->shortcuts_model->length) - return -1; -#endif - - for (i = 0; i < current_folder_separator_idx; i++) - { - gpointer col_data; - ShortcutType shortcut_type; - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - -1); - - if (col_data) - { - if (shortcut_type == SHORTCUT_TYPE_VOLUME) - { - GtkFileSystemVolume *volume; - GFile *base_file; - gboolean exists; - - volume = col_data; - base_file = _gtk_file_system_volume_get_root (volume); - - exists = base_file && g_file_equal (file, base_file); - - if (base_file) - g_object_unref (base_file); - - if (exists) - return i; - } - else if (shortcut_type == SHORTCUT_TYPE_FILE) - { - GFile *model_file; - - model_file = col_data; - - if (model_file && g_file_equal (model_file, file)) - return i; - } - } - - if (i < current_folder_separator_idx - 1) - { - if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter)) - g_assert_not_reached (); - } - } - - return -1; -} - -/* Tries to add a bookmark from a path name */ -static gboolean -shortcuts_add_bookmark_from_file (GtkFileChooserDefault *impl, - GFile *file, - int pos) -{ - GError *error; - - g_return_val_if_fail (G_IS_FILE (file), FALSE); - - if (shortcut_find_position (impl, file) != -1) - return FALSE; - - error = NULL; - if (!_gtk_file_system_insert_bookmark (impl->priv->file_system, file, pos, &error)) - { - error_adding_bookmark_dialog (impl, file, error); - return FALSE; - } - - return TRUE; -} - -static void -add_bookmark_foreach_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - GtkFileChooserDefault *impl; - GFile *file; - - impl = (GtkFileChooserDefault *) data; - - gtk_tree_model_get (model, iter, - MODEL_COL_FILE, &file, - -1); - - shortcuts_add_bookmark_from_file (impl, file, -1); - - g_object_unref (file); -} - -/* Adds a bookmark from the currently selected item in the file list */ -static void -bookmarks_add_selected_folder (GtkFileChooserDefault *impl) -{ - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); - - if (gtk_tree_selection_count_selected_rows (selection) == 0) - shortcuts_add_bookmark_from_file (impl, impl->priv->current_folder, -1); - else - gtk_tree_selection_selected_foreach (selection, - add_bookmark_foreach_cb, - impl); -} - -/* Callback used when the "Add bookmark" button is clicked */ -static void -add_bookmark_button_clicked_cb (GtkButton *button, - GtkFileChooserDefault *impl) -{ - bookmarks_add_selected_folder (impl); -} - -/* Returns TRUE plus an iter in the shortcuts_model if a row is selected; - * returns FALSE if no shortcut is selected. - */ -static gboolean -shortcuts_get_selected (GtkFileChooserDefault *impl, - GtkTreeIter *iter) -{ - GtkTreeSelection *selection; - GtkTreeIter parent_iter; - - if (!impl->priv->browse_shortcuts_tree_view) - return FALSE; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view)); - - if (!gtk_tree_selection_get_selected (selection, NULL, &parent_iter)) - return FALSE; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->priv->shortcuts_pane_filter_model), - iter, - &parent_iter); - return TRUE; -} - -/* Removes the selected bookmarks */ -static void -remove_selected_bookmarks (GtkFileChooserDefault *impl) -{ - GtkTreeIter iter; - gpointer col_data; - GFile *file; - gboolean removable; - GError *error; - - if (!shortcuts_get_selected (impl, &iter)) - return; - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_REMOVABLE, &removable, - -1); - - if (!removable) - return; - - g_assert (col_data != NULL); - - file = col_data; - - error = NULL; - if (!_gtk_file_system_remove_bookmark (impl->priv->file_system, file, &error)) - error_removing_bookmark_dialog (impl, file, error); -} - -/* Callback used when the "Remove bookmark" button is clicked */ -static void -remove_bookmark_button_clicked_cb (GtkButton *button, - GtkFileChooserDefault *impl) -{ - remove_selected_bookmarks (impl); -} struct selection_check_closure { GtkFileChooserDefault *impl; @@ -2449,6 +1126,7 @@ selection_check (GtkFileChooserDefault *impl, gboolean *all_files, gboolean *all_folders) { + GtkFileChooserDefaultPrivate *priv = impl->priv; struct selection_check_closure closure; GtkTreeSelection *selection; @@ -2457,7 +1135,7 @@ selection_check (GtkFileChooserDefault *impl, closure.all_files = TRUE; closure.all_folders = TRUE; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + 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); @@ -2474,859 +1152,55 @@ selection_check (GtkFileChooserDefault *impl, *all_folders = closure.all_folders; } -struct get_selected_file_closure { - GtkFileChooserDefault *impl; - GFile *file; -}; - -static void -get_selected_file_foreach_cb (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - struct get_selected_file_closure *closure = data; - - if (closure->file) - { - /* Just in case this function gets run more than once with a multiple selection; we only care about one file */ - g_object_unref (closure->file); - closure->file = NULL; - } - - gtk_tree_model_get (model, iter, - MODEL_COL_FILE, &closure->file, /* this will give us a reffed file */ - -1); -} - -/* Returns a selected path from the file list */ -static GFile * -get_selected_file (GtkFileChooserDefault *impl) -{ - struct get_selected_file_closure closure; - GtkTreeSelection *selection; - - closure.impl = impl; - closure.file = NULL; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); - gtk_tree_selection_selected_foreach (selection, - get_selected_file_foreach_cb, - &closure); - - return closure.file; -} - -typedef struct { - GtkFileChooserDefault *impl; - gchar *tip; -} UpdateTooltipData; - -static void -update_tooltip (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - UpdateTooltipData *udata = data; - - if (udata->tip == NULL) - { - gchar *display_name; - - gtk_tree_model_get (model, iter, - MODEL_COL_NAME, &display_name, - -1); - - udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"), - display_name); - g_free (display_name); - } -} - - -/* Sensitize the "add bookmark" button if all the selected items are folders, or - * if there are no selected items *and* the current folder is not in the - * bookmarks list. De-sensitize the button otherwise. - */ -static void -bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl) -{ - gint num_selected; - gboolean all_folders; - gboolean active; - gchar *tip; - - selection_check (impl, &num_selected, NULL, &all_folders); - - if (num_selected == 0) - active = (impl->priv->current_folder != NULL) && (shortcut_find_position (impl, impl->priv->current_folder) == -1); - else if (num_selected == 1) - { - GFile *file; - - file = get_selected_file (impl); - active = file && all_folders && (shortcut_find_position (impl, file) == -1); - if (file) - g_object_unref (file); - } - else - active = all_folders; - - gtk_widget_set_sensitive (impl->priv->browse_shortcuts_add_button, active); - - if (impl->priv->browse_files_popup_menu_add_shortcut_item) - gtk_widget_set_sensitive (impl->priv->browse_files_popup_menu_add_shortcut_item, - (num_selected == 0) ? FALSE : active); - - if (active) - { - if (num_selected == 0) - tip = g_strdup_printf (_("Add the current folder to the bookmarks")); - else if (num_selected > 1) - tip = g_strdup_printf (_("Add the selected folders to the bookmarks")); - else - { - GtkTreeSelection *selection; - UpdateTooltipData data; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); - data.impl = impl; - data.tip = NULL; - gtk_tree_selection_selected_foreach (selection, update_tooltip, &data); - tip = data.tip; - } - - gtk_widget_set_tooltip_text (impl->priv->browse_shortcuts_add_button, tip); - g_free (tip); - } -} - -/* Sets the sensitivity of the "remove bookmark" button depending on whether a - * bookmark row is selected in the shortcuts tree. - */ -static void -bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl) -{ - GtkTreeIter iter; - gboolean removable = FALSE; - gchar *name = NULL; - gchar *tip; - - if (shortcuts_get_selected (impl, &iter)) - { - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_REMOVABLE, &removable, - SHORTCUTS_COL_NAME, &name, - -1); - gtk_widget_set_sensitive (impl->priv->browse_shortcuts_remove_button, removable); - - if (removable) - tip = g_strdup_printf (_("Remove the bookmark '%s'"), name); - else - tip = g_strdup_printf (_("Bookmark '%s' cannot be removed"), name); - - gtk_widget_set_tooltip_text (impl->priv->browse_shortcuts_remove_button, tip); - g_free (tip); - } - else - gtk_widget_set_tooltip_text (impl->priv->browse_shortcuts_remove_button, - _("Remove the selected bookmark")); - g_free (name); -} - -static void -shortcuts_check_popup_sensitivity (GtkFileChooserDefault *impl) -{ - GtkTreeIter iter; - gboolean removable = FALSE; - - if (impl->priv->browse_shortcuts_popup_menu == NULL) - return; - - if (shortcuts_get_selected (impl, &iter)) - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_REMOVABLE, &removable, - -1); - - gtk_widget_set_sensitive (impl->priv->browse_shortcuts_popup_menu_remove_item, removable); - gtk_widget_set_sensitive (impl->priv->browse_shortcuts_popup_menu_rename_item, removable); -} - -/* GtkWidget::drag-begin handler for the shortcuts list. */ -static void -shortcuts_drag_begin_cb (GtkWidget *widget, - GdkDragContext *context, - GtkFileChooserDefault *impl) -{ -#if 0 - impl->priv->shortcuts_drag_context = g_object_ref (context); -#endif -} - -#if 0 -/* Removes the idle handler for outside drags */ -static void -shortcuts_cancel_drag_outside_idle (GtkFileChooserDefault *impl) -{ - if (!impl->priv->shortcuts_drag_outside_idle) - return; - - g_source_destroy (impl->priv->shortcuts_drag_outside_idle); - impl->priv->shortcuts_drag_outside_idle = NULL; -} -#endif - -/* GtkWidget::drag-end handler for the shortcuts list. */ -static void -shortcuts_drag_end_cb (GtkWidget *widget, - GdkDragContext *context, - GtkFileChooserDefault *impl) -{ -#if 0 - g_object_unref (impl->priv->shortcuts_drag_context); - - shortcuts_cancel_drag_outside_idle (impl); - - if (!impl->priv->shortcuts_drag_outside) - return; - - gtk_button_clicked (GTK_BUTTON (impl->priv->browse_shortcuts_remove_button)); - - impl->priv->shortcuts_drag_outside = FALSE; -#endif -} - -/* GtkWidget::drag-data-delete handler for the shortcuts list. */ -static void -shortcuts_drag_data_delete_cb (GtkWidget *widget, - GdkDragContext *context, - GtkFileChooserDefault *impl) -{ - g_signal_stop_emission_by_name (widget, "drag-data-delete"); -} - -/* GtkWidget::drag-leave handler for the shortcuts list. We unhighlight the - * drop position. - */ -static void -shortcuts_drag_leave_cb (GtkWidget *widget, - GdkDragContext *context, - guint time_, - GtkFileChooserDefault *impl) -{ -#if 0 - if (gtk_drag_get_source_widget (context) == widget && !impl->priv->shortcuts_drag_outside_idle) - { - impl->priv->shortcuts_drag_outside_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (shortcuts_drag_outside_idle_cb)); - } -#endif - - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view), - NULL, - GTK_TREE_VIEW_DROP_BEFORE); - - g_signal_stop_emission_by_name (widget, "drag-leave"); -} - -/* Computes the appropriate row and position for dropping */ -static void -shortcuts_compute_drop_position (GtkFileChooserDefault *impl, - int x, - int y, - GtkTreePath **path, - GtkTreeViewDropPosition *pos) -{ - GtkTreeView *tree_view; - GtkTreeViewColumn *column; - int cell_y; - GdkRectangle cell; - int row; - int bookmarks_index; - int header_height = 0; - - tree_view = GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view); - - if (gtk_tree_view_get_headers_visible (tree_view)) - header_height = _gtk_tree_view_get_header_height (tree_view); - - bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS); - - if (!gtk_tree_view_get_path_at_pos (tree_view, - x, - y - header_height, - path, - &column, - NULL, - &cell_y)) - { - row = bookmarks_index + impl->priv->num_bookmarks - 1; - *path = gtk_tree_path_new_from_indices (row, -1); - *pos = GTK_TREE_VIEW_DROP_AFTER; - return; - } - - row = *gtk_tree_path_get_indices (*path); - gtk_tree_view_get_background_area (tree_view, *path, column, &cell); - gtk_tree_path_free (*path); - - if (row < bookmarks_index) - { - row = bookmarks_index; - *pos = GTK_TREE_VIEW_DROP_BEFORE; - } - else if (row > bookmarks_index + impl->priv->num_bookmarks - 1) - { - row = bookmarks_index + impl->priv->num_bookmarks - 1; - *pos = GTK_TREE_VIEW_DROP_AFTER; - } - else - { - if (cell_y < cell.height / 2) - *pos = GTK_TREE_VIEW_DROP_BEFORE; - else - *pos = GTK_TREE_VIEW_DROP_AFTER; - } - - *path = gtk_tree_path_new_from_indices (row, -1); -} - -/* GtkWidget::drag-motion handler for the shortcuts list. We basically - * implement the destination side of DnD by hand, due to limitations in - * GtkTreeView's DnD API. - */ -static gboolean -shortcuts_drag_motion_cb (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time_, - GtkFileChooserDefault *impl) -{ - GtkTreePath *path; - GtkTreeViewDropPosition pos; - GdkDragAction action; - -#if 0 - if (gtk_drag_get_source_widget (context) == widget) - { - shortcuts_cancel_drag_outside_idle (impl); - - if (impl->priv->shortcuts_drag_outside) - { - shortcuts_drag_set_delete_cursor (impl, FALSE); - impl->priv->shortcuts_drag_outside = FALSE; - } - } -#endif - - if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_COPY || - (gdk_drag_context_get_actions (context) & GDK_ACTION_COPY) != 0) - action = GDK_ACTION_COPY; - else if (gdk_drag_context_get_suggested_action (context) == GDK_ACTION_MOVE || - (gdk_drag_context_get_actions (context) & GDK_ACTION_MOVE) != 0) - action = GDK_ACTION_MOVE; - else - { - action = 0; - goto out; - } - - shortcuts_compute_drop_position (impl, x, y, &path, &pos); - gtk_tree_view_set_drag_dest_row (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view), path, pos); - gtk_tree_path_free (path); - - out: - - g_signal_stop_emission_by_name (widget, "drag-motion"); - - if (action != 0) - { - gdk_drag_status (context, action, time_); - return TRUE; - } - else - return FALSE; -} - -/* GtkWidget::drag-drop handler for the shortcuts list. */ static gboolean -shortcuts_drag_drop_cb (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - guint time_, - GtkFileChooserDefault *impl) +file_is_recent_uri (GFile *file) { -#if 0 - shortcuts_cancel_drag_outside_idle (impl); -#endif - - g_signal_stop_emission_by_name (widget, "drag-drop"); - return TRUE; -} - -/* Parses a "text/uri-list" string and inserts its URIs as bookmarks */ -static void -shortcuts_drop_uris (GtkFileChooserDefault *impl, - GtkSelectionData *selection_data, - int position) -{ - gchar **uris; - gint i; + GFile *recent; + gboolean same; - uris = gtk_selection_data_get_uris (selection_data); - if (!uris) - return; + recent = g_file_new_for_uri ("recent:///"); + same = g_file_equal (file, recent); + g_object_unref (recent); - for (i = 0; uris[i]; i++) - { - char *uri; - GFile *file; - - uri = uris[i]; - file = g_file_new_for_uri (uri); - - if (shortcuts_add_bookmark_from_file (impl, file, position)) - position++; - - g_object_unref (file); - } - - g_strfreev (uris); + return same; } -/* Reorders the selected bookmark to the specified position */ static void -shortcuts_reorder (GtkFileChooserDefault *impl, - int new_position) +places_sidebar_open_location_cb (GtkPlacesSidebar *sidebar, GFile *location, GtkPlacesOpenFlags open_flags, GtkFileChooserDefault *impl) { - GtkTreeIter iter; - gpointer col_data; - ShortcutType shortcut_type; - GtkTreePath *path; - int old_position; - int bookmarks_index; - GFile *file; - GError *error; - gchar *name = NULL; - - /* Get the selected path */ - - if (!shortcuts_get_selected (impl, &iter)) - g_assert_not_reached (); - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter); - old_position = *gtk_tree_path_get_indices (path); - gtk_tree_path_free (path); - - bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS); - old_position -= bookmarks_index; - g_assert (old_position >= 0 && old_position < impl->priv->num_bookmarks); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_NAME, &name, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - -1); - g_assert (col_data != NULL); - g_assert (shortcut_type == SHORTCUT_TYPE_FILE); - - file = col_data; - g_object_ref (file); /* removal below will free file, so we need a new ref */ - - /* Remove the path from the old position and insert it in the new one */ - - if (new_position > old_position) - new_position--; - - if (old_position == new_position) - goto out; + GtkFileChooserDefaultPrivate *priv = impl->priv; + gboolean clear_entry; - error = NULL; - if (_gtk_file_system_remove_bookmark (impl->priv->file_system, file, &error)) - { - shortcuts_add_bookmark_from_file (impl, file, new_position); - _gtk_file_system_set_bookmark_label (impl->priv->file_system, file, name); - } + /* In the Save modes, we want to preserve what the uesr typed in the filename + * entry, so that he may choose another folder without erasing his typed name. + */ + if (priv->location_entry + && !(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) + clear_entry = TRUE; else - error_adding_bookmark_dialog (impl, file, error); - - out: - - g_object_unref (file); - g_free (name); -} - -/* Callback used when we get the drag data for the bookmarks list. We add the - * received URIs as bookmarks if they are folders. - */ -static void -shortcuts_drag_data_received_cb (GtkWidget *widget, - GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *selection_data, - guint info, - guint time_, - gpointer data) -{ - GtkFileChooserDefault *impl; - GtkTreePath *tree_path; - GtkTreeViewDropPosition tree_pos; - GdkAtom target; - int position; - int bookmarks_index; - - impl = GTK_FILE_CHOOSER_DEFAULT (data); - - /* Compute position */ - - bookmarks_index = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS); - - shortcuts_compute_drop_position (impl, x, y, &tree_path, &tree_pos); - position = *gtk_tree_path_get_indices (tree_path); - gtk_tree_path_free (tree_path); - - if (tree_pos == GTK_TREE_VIEW_DROP_AFTER) - position++; - - g_assert (position >= bookmarks_index); - position -= bookmarks_index; - - target = gtk_selection_data_get_target (selection_data); - - if (gtk_targets_include_uri (&target, 1)) - shortcuts_drop_uris (impl, selection_data, position); - else if (target == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW")) - shortcuts_reorder (impl, position); - - g_signal_stop_emission_by_name (widget, "drag-data-received"); -} - -/* Callback used to display a tooltip in the shortcuts tree */ -static gboolean -shortcuts_query_tooltip_cb (GtkWidget *widget, - gint x, - gint y, - gboolean keyboard_mode, - GtkTooltip *tooltip, - GtkFileChooserDefault *impl) -{ - GtkTreeModel *model; - GtkTreeIter iter; - - if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), - &x, &y, - keyboard_mode, - &model, - NULL, - &iter)) - { - gpointer col_data; - ShortcutType shortcut_type; - - gtk_tree_model_get (model, &iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - -1); - - if (shortcut_type == SHORTCUT_TYPE_SEPARATOR) - return FALSE; - else if (shortcut_type == SHORTCUT_TYPE_VOLUME) - return FALSE; - else if (shortcut_type == SHORTCUT_TYPE_FILE) - { - GFile *file; - char *parse_name; + clear_entry = FALSE; - file = G_FILE (col_data); - parse_name = g_file_get_parse_name (file); - - gtk_tooltip_set_text (tooltip, parse_name); - - g_free (parse_name); - - return TRUE; - } - else if (shortcut_type == SHORTCUT_TYPE_SEARCH) - { - return FALSE; - } - else if (shortcut_type == SHORTCUT_TYPE_RECENT) - { - return FALSE; - } - } - - return FALSE; -} - - -/* Callback used when the selection in the shortcuts tree changes */ -static void -shortcuts_selection_changed_cb (GtkTreeSelection *selection, - GtkFileChooserDefault *impl) -{ - GtkTreeIter iter; - GtkTreeIter child_iter; - - bookmarks_check_remove_sensitivity (impl); - shortcuts_check_popup_sensitivity (impl); - - if (impl->priv->changing_folder) - return; - - if (gtk_tree_selection_get_selected(selection, NULL, &iter)) - { - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->priv->shortcuts_pane_filter_model), - &child_iter, - &iter); - shortcuts_activate_iter (impl, &child_iter); - } -} - -static gboolean -shortcuts_row_separator_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - ShortcutType shortcut_type; - - gtk_tree_model_get (model, iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1); - - return shortcut_type == SHORTCUT_TYPE_SEPARATOR; -} - -static gboolean -shortcuts_key_press_event_after_cb (GtkWidget *tree_view, - GdkEventKey *event, - GtkFileChooserDefault *impl) -{ - GtkWidget *entry; - - /* Connect "after" to key-press-event on the shortcuts pane. We want this action to be possible: - * - * 1. user brings up a SAVE dialog - * 2. user clicks on a shortcut in the shortcuts pane - * 3. user starts typing a filename + /* FIXME-places-sidebar: * - * Normally, the user's typing would be ignored, as the shortcuts treeview doesn't - * support interactive search. However, we'd rather focus the location entry - * so that the user can type *there*. - * - * To preserve keyboard navigation in the shortcuts pane, we don't focus the - * filename entry if one clicks on a shortcut; rather, we focus the entry only - * if the user starts typing while the focus is in the shortcuts pane. + * GtkPlacesSidebar doesn't have a Search item anymore. We should put that function in a toolbar-like button, like + * in Nautilus, and do operation_mode_set (impl, OPERATION_MODE_SEARCH); */ - /* don't screw up focus switching with Tab */ - if (event->keyval == GDK_KEY_Tab - || event->keyval == GDK_KEY_KP_Tab - || event->keyval == GDK_KEY_ISO_Left_Tab - || event->length < 1) - return FALSE; - - if (impl->priv->location_entry) - entry = impl->priv->location_entry; - else if (impl->priv->search_entry) - entry = impl->priv->search_entry; + if (file_is_recent_uri (location)) + operation_mode_set (impl, OPERATION_MODE_RECENT); else - entry = NULL; - - if (entry) - { - gtk_widget_grab_focus (entry); - return gtk_widget_event (entry, (GdkEvent *) event); - } - else - return FALSE; -} - -/* Callback used when the file list's popup menu is detached */ -static void -shortcuts_popup_menu_detach_cb (GtkWidget *attach_widget, - GtkMenu *menu) -{ - GtkFileChooserDefault *impl; - - impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault"); - g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl)); - - impl->priv->browse_shortcuts_popup_menu = NULL; - impl->priv->browse_shortcuts_popup_menu_remove_item = NULL; - impl->priv->browse_shortcuts_popup_menu_rename_item = NULL; -} - -static void -remove_shortcut_cb (GtkMenuItem *item, - GtkFileChooserDefault *impl) -{ - remove_selected_bookmarks (impl); -} - -/* Rename the selected bookmark */ -static void -rename_selected_bookmark (GtkFileChooserDefault *impl) -{ - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeViewColumn *column; - GtkCellRenderer *cell; - GList *renderers; - - if (shortcuts_get_selected (impl, &iter)) - { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter); - column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view), 0); - renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); - cell = g_list_nth_data (renderers, 1); - g_list_free (renderers); - g_object_set (cell, "editable", TRUE, NULL); - gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view), - path, column, cell, TRUE); - gtk_tree_path_free (path); - } -} - -static void -rename_shortcut_cb (GtkMenuItem *item, - GtkFileChooserDefault *impl) -{ - rename_selected_bookmark (impl); -} - -/* Constructs the popup menu for the file list if needed */ -static void -shortcuts_build_popup_menu (GtkFileChooserDefault *impl) -{ - GtkWidget *item; - - if (impl->priv->browse_shortcuts_popup_menu) - return; - - impl->priv->browse_shortcuts_popup_menu = gtk_menu_new (); - gtk_menu_attach_to_widget (GTK_MENU (impl->priv->browse_shortcuts_popup_menu), - impl->priv->browse_shortcuts_tree_view, - shortcuts_popup_menu_detach_cb); - - item = gtk_image_menu_item_new_with_label (_("Remove")); - impl->priv->browse_shortcuts_popup_menu_remove_item = item; - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), - gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU)); - g_signal_connect (item, "activate", - G_CALLBACK (remove_shortcut_cb), impl); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (impl->priv->browse_shortcuts_popup_menu), item); - - item = gtk_menu_item_new_with_label (_("Rename…")); - impl->priv->browse_shortcuts_popup_menu_rename_item = item; - g_signal_connect (item, "activate", - G_CALLBACK (rename_shortcut_cb), impl); - gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (impl->priv->browse_shortcuts_popup_menu), item); -} - -static void -shortcuts_update_popup_menu (GtkFileChooserDefault *impl) -{ - shortcuts_build_popup_menu (impl); - shortcuts_check_popup_sensitivity (impl); -} - -static void -popup_position_func (GtkMenu *menu, - gint *x, - gint *y, - gboolean *push_in, - gpointer user_data); - -static void -shortcuts_popup_menu (GtkFileChooserDefault *impl, - GdkEventButton *event) -{ - shortcuts_update_popup_menu (impl); - if (event) - gtk_menu_popup (GTK_MENU (impl->priv->browse_shortcuts_popup_menu), - NULL, NULL, NULL, NULL, - event->button, event->time); - else - { - gtk_menu_popup (GTK_MENU (impl->priv->browse_shortcuts_popup_menu), - NULL, NULL, - popup_position_func, impl->priv->browse_shortcuts_tree_view, - 0, GDK_CURRENT_TIME); - gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->priv->browse_shortcuts_popup_menu), - FALSE); - } -} - -/* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */ -static gboolean -shortcuts_popup_menu_cb (GtkWidget *widget, - GtkFileChooserDefault *impl) -{ - shortcuts_popup_menu (impl, NULL); - return TRUE; -} - -/* Callback used when a button is pressed on the shortcuts list. - * We trap button 3 to bring up a popup menu. - */ -static gboolean -shortcuts_button_press_event_cb (GtkWidget *widget, - GdkEventButton *event, - GtkFileChooserDefault *impl) -{ - static gboolean in_press = FALSE; - gboolean handled; - - if (in_press) - return FALSE; - - if (!gdk_event_triggers_context_menu ((GdkEvent *) event)) - return FALSE; - - in_press = TRUE; - handled = gtk_widget_event (impl->priv->browse_shortcuts_tree_view, (GdkEvent *) event); - in_press = FALSE; - - if (!handled) - return FALSE; - - shortcuts_popup_menu (impl, event); - return TRUE; + change_folder_and_display_error (impl, location, clear_entry); } +/* Callback used when the places sidebar needs us to display an error message */ static void -shortcuts_edited (GtkCellRenderer *cell, - gchar *path_string, - gchar *new_text, - GtkFileChooserDefault *impl) +places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar, + const char *primary, + const char *secondary, + GtkFileChooserDefault *impl) { - GtkTreePath *path; - GtkTreeIter iter; - GFile *shortcut; - - g_object_set (cell, "editable", FALSE, NULL); - - path = gtk_tree_path_new_from_string (path_string); - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, path)) - g_assert_not_reached (); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_DATA, &shortcut, - -1); - gtk_tree_path_free (path); - - _gtk_file_system_set_bookmark_label (impl->priv->file_system, shortcut, new_text); -} - -static void -shortcuts_editing_canceled (GtkCellRenderer *cell, - GtkFileChooserDefault *impl) -{ - g_object_set (cell, "editable", FALSE, NULL); + error_message (impl, primary, secondary); } static gboolean @@ -3352,11 +1226,10 @@ browse_files_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer data) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = (GtkFileChooserDefault *) data; + GtkFileChooserDefaultPrivate *priv = impl->priv; GdkModifierType no_text_input_mask; - impl = (GtkFileChooserDefault *) data; - no_text_input_mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_NO_TEXT_INPUT); @@ -3373,7 +1246,7 @@ browse_files_key_press_event_cb (GtkWidget *widget, if (key_is_left_or_right (event)) { - gtk_widget_grab_focus (impl->priv->browse_shortcuts_tree_view); + gtk_widget_grab_focus (priv->places_sidebar); return TRUE; } @@ -3383,8 +1256,8 @@ browse_files_key_press_event_cb (GtkWidget *widget, || event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space) && !(event->state & gtk_accelerator_get_default_mod_mask ()) - && !(impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) + && !(priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) { GtkWindow *window; @@ -3414,15 +1287,39 @@ static void popup_menu_detach_cb (GtkWidget *attach_widget, GtkMenu *menu) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault"); + GtkFileChooserDefaultPrivate *priv; - impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault"); g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl)); - impl->priv->browse_files_popup_menu = NULL; - impl->priv->browse_files_popup_menu_add_shortcut_item = NULL; - impl->priv->browse_files_popup_menu_hidden_files_item = NULL; - impl->priv->browse_files_popup_menu_copy_file_location_item = NULL; + priv = impl->priv; + + priv->browse_files_popup_menu = NULL; + priv->browse_files_popup_menu_add_shortcut_item = NULL; + priv->browse_files_popup_menu_hidden_files_item = NULL; + priv->browse_files_popup_menu_copy_file_location_item = NULL; +} + +/* Callback used from gtk_tree_selection_selected_foreach(); adds a bookmark for + * each selected item in the file list. + */ +static void +add_bookmark_foreach_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkFileChooserDefault *impl = (GtkFileChooserDefault *) data; + GtkFileChooserDefaultPrivate *priv = impl->priv; + GFile *file; + + gtk_tree_model_get (model, iter, + MODEL_COL_FILE, &file, + -1); + + _gtk_bookmarks_manager_insert_bookmark (priv->bookmarks_manager, file, 0, NULL); /* NULL-GError */ + + g_object_unref (file); } /* Callback used when the "Add to Bookmarks" menu item is activated */ @@ -3430,7 +1327,14 @@ static void add_to_shortcuts_cb (GtkMenuItem *item, GtkFileChooserDefault *impl) { - bookmarks_add_selected_folder (impl); + GtkFileChooserDefaultPrivate *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, + add_bookmark_foreach_cb, + impl); } /* callback used to set data to clipboard */ @@ -3560,10 +1464,12 @@ static void show_size_column_toggled_cb (GtkCheckMenuItem *item, GtkFileChooserDefault *impl) { - impl->priv->show_size_column = gtk_check_menu_item_get_active (item); + GtkFileChooserDefaultPrivate *priv = impl->priv; - gtk_tree_view_column_set_visible (impl->priv->list_size_column, - impl->priv->show_size_column); + priv->show_size_column = gtk_check_menu_item_get_active (item); + + gtk_tree_view_column_set_visible (priv->list_size_column, + priv->show_size_column); } /* Shows an error dialog about not being able to select a dragged file */ @@ -3617,17 +1523,18 @@ file_list_drag_data_received_get_info_cb (GCancellable *cancellable, gboolean cancelled = g_cancellable_is_cancelled (cancellable); struct FileListDragData *data = user_data; GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl); + GtkFileChooserDefaultPrivate *priv = data->impl->priv; - if (cancellable != data->impl->priv->file_list_drag_data_received_cancellable) + if (cancellable != priv->file_list_drag_data_received_cancellable) goto out; - data->impl->priv->file_list_drag_data_received_cancellable = NULL; + priv->file_list_drag_data_received_cancellable = NULL; if (cancelled || error) goto out; - if ((data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) && + if ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) && data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info)) change_folder_and_display_error (data->impl, data->file, FALSE); else @@ -3642,7 +1549,7 @@ file_list_drag_data_received_get_info_cb (GCancellable *cancellable, browse_files_center_selected_row (data->impl); } - if (data->impl->priv->select_multiple) + if (priv->select_multiple) file_list_drag_data_select_uris (data->impl, data->uris); out: @@ -3664,13 +1571,12 @@ file_list_drag_data_received_cb (GtkWidget *widget, guint time_, gpointer data) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data); + GtkFileChooserDefaultPrivate *priv = impl->priv; gchar **uris; char *uri; GFile *file; - impl = GTK_FILE_CHOOSER_DEFAULT (data); - /* Allow only drags from other widgets; see bug #533891. */ if (gtk_drag_get_source_widget (context) == widget) { @@ -3692,11 +1598,11 @@ file_list_drag_data_received_cb (GtkWidget *widget, data->uris = uris; data->file = file; - if (impl->priv->file_list_drag_data_received_cancellable) - g_cancellable_cancel (impl->priv->file_list_drag_data_received_cancellable); + if (priv->file_list_drag_data_received_cancellable) + g_cancellable_cancel (priv->file_list_drag_data_received_cancellable); - impl->priv->file_list_drag_data_received_cancellable = - _gtk_file_system_get_info (impl->priv->file_system, file, + priv->file_list_drag_data_received_cancellable = + _gtk_file_system_get_info (priv->file_system, file, "standard::type", file_list_drag_data_received_get_info_cb, data); @@ -3738,17 +1644,18 @@ file_list_drag_motion_cb (GtkWidget *widget, static void check_copy_file_location_sensitivity (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; gboolean active; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); if (gtk_tree_selection_count_selected_rows (selection) == 0) active = FALSE; else active = TRUE; - if (impl->priv->browse_files_popup_menu_copy_file_location_item) - gtk_widget_set_sensitive (impl->priv->browse_files_popup_menu_copy_file_location_item, active); + if (priv->browse_files_popup_menu_copy_file_location_item) + gtk_widget_set_sensitive (priv->browse_files_popup_menu_copy_file_location_item, active); } static GtkWidget * @@ -3757,6 +1664,7 @@ file_list_add_image_menu_item (GtkFileChooserDefault *impl, const char *mnemonic_label, GCallback callback) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *item; item = gtk_image_menu_item_new_with_mnemonic (mnemonic_label); @@ -3764,7 +1672,7 @@ file_list_add_image_menu_item (GtkFileChooserDefault *impl, gtk_image_new_from_stock (stock_name, GTK_ICON_SIZE_MENU)); g_signal_connect (item, "activate", callback, impl); gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (impl->priv->browse_files_popup_menu), item); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item); return item; } @@ -3774,12 +1682,13 @@ file_list_add_check_menu_item (GtkFileChooserDefault *impl, const char *mnemonic_label, GCallback callback) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *item; item = gtk_check_menu_item_new_with_mnemonic (mnemonic_label); g_signal_connect (item, "toggled", callback, impl); gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (impl->priv->browse_files_popup_menu), item); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item); return item; } @@ -3788,36 +1697,36 @@ file_list_add_check_menu_item (GtkFileChooserDefault *impl, static void file_list_build_popup_menu (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *item; - if (impl->priv->browse_files_popup_menu) + if (priv->browse_files_popup_menu) return; - impl->priv->browse_files_popup_menu = gtk_menu_new (); - gtk_menu_attach_to_widget (GTK_MENU (impl->priv->browse_files_popup_menu), - impl->priv->browse_files_tree_view, + priv->browse_files_popup_menu = gtk_menu_new (); + gtk_menu_attach_to_widget (GTK_MENU (priv->browse_files_popup_menu), + priv->browse_files_tree_view, popup_menu_detach_cb); - impl->priv->browse_files_popup_menu_visit_file_item = file_list_add_image_menu_item (impl, GTK_STOCK_DIRECTORY, _("_Visit this file"), + priv->browse_files_popup_menu_visit_file_item = file_list_add_image_menu_item (impl, GTK_STOCK_DIRECTORY, _("_Visit this file"), G_CALLBACK (visit_file_cb)); - impl->priv->browse_files_popup_menu_copy_file_location_item = file_list_add_image_menu_item (impl, GTK_STOCK_COPY, _("_Copy file’s location"), + priv->browse_files_popup_menu_copy_file_location_item = file_list_add_image_menu_item (impl, GTK_STOCK_COPY, _("_Copy file’s location"), G_CALLBACK (copy_file_location_cb)); - impl->priv->browse_files_popup_menu_add_shortcut_item = file_list_add_image_menu_item (impl, GTK_STOCK_ADD, _("_Add to Bookmarks"), + priv->browse_files_popup_menu_add_shortcut_item = file_list_add_image_menu_item (impl, GTK_STOCK_ADD, _("_Add to Bookmarks"), G_CALLBACK (add_to_shortcuts_cb)); item = gtk_separator_menu_item_new (); gtk_widget_show (item); - gtk_menu_shell_append (GTK_MENU_SHELL (impl->priv->browse_files_popup_menu), item); + gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item); - impl->priv->browse_files_popup_menu_hidden_files_item = file_list_add_check_menu_item (impl, _("Show _Hidden Files"), + priv->browse_files_popup_menu_hidden_files_item = file_list_add_check_menu_item (impl, _("Show _Hidden Files"), G_CALLBACK (show_hidden_toggled_cb)); - impl->priv->browse_files_popup_menu_size_column_item = file_list_add_check_menu_item (impl, _("Show _Size Column"), + priv->browse_files_popup_menu_size_column_item = file_list_add_check_menu_item (impl, _("Show _Size Column"), G_CALLBACK (show_size_column_toggled_cb)); - bookmarks_check_add_sensitivity (impl); check_copy_file_location_sensitivity (impl); } @@ -3825,6 +1734,8 @@ file_list_build_popup_menu (GtkFileChooserDefault *impl) static void file_list_update_popup_menu (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + file_list_build_popup_menu (impl); /* The sensitivity of the Add to Bookmarks item is set in @@ -3832,22 +1743,22 @@ file_list_update_popup_menu (GtkFileChooserDefault *impl) */ /* 'Visit this file' */ - gtk_widget_set_visible (impl->priv->browse_files_popup_menu_visit_file_item, (impl->priv->operation_mode != OPERATION_MODE_BROWSE)); + gtk_widget_set_visible (priv->browse_files_popup_menu_visit_file_item, (priv->operation_mode != OPERATION_MODE_BROWSE)); /* 'Show Hidden Files' */ - g_signal_handlers_block_by_func (impl->priv->browse_files_popup_menu_hidden_files_item, + g_signal_handlers_block_by_func (priv->browse_files_popup_menu_hidden_files_item, G_CALLBACK (show_hidden_toggled_cb), impl); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->priv->browse_files_popup_menu_hidden_files_item), - impl->priv->show_hidden); - g_signal_handlers_unblock_by_func (impl->priv->browse_files_popup_menu_hidden_files_item, + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->browse_files_popup_menu_hidden_files_item), + priv->show_hidden); + g_signal_handlers_unblock_by_func (priv->browse_files_popup_menu_hidden_files_item, G_CALLBACK (show_hidden_toggled_cb), impl); /* 'Show Size Column' */ - g_signal_handlers_block_by_func (impl->priv->browse_files_popup_menu_size_column_item, + g_signal_handlers_block_by_func (priv->browse_files_popup_menu_size_column_item, G_CALLBACK (show_size_column_toggled_cb), impl); - gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (impl->priv->browse_files_popup_menu_size_column_item), - impl->priv->show_size_column); - g_signal_handlers_unblock_by_func (impl->priv->browse_files_popup_menu_size_column_item, + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->browse_files_popup_menu_size_column_item), + priv->show_size_column); + g_signal_handlers_unblock_by_func (priv->browse_files_popup_menu_size_column_item, G_CALLBACK (show_size_column_toggled_cb), impl); } @@ -3890,18 +1801,20 @@ static void file_list_popup_menu (GtkFileChooserDefault *impl, GdkEventButton *event) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + file_list_update_popup_menu (impl); if (event) - gtk_menu_popup (GTK_MENU (impl->priv->browse_files_popup_menu), + gtk_menu_popup (GTK_MENU (priv->browse_files_popup_menu), NULL, NULL, NULL, NULL, event->button, event->time); else { - gtk_menu_popup (GTK_MENU (impl->priv->browse_files_popup_menu), + gtk_menu_popup (GTK_MENU (priv->browse_files_popup_menu), NULL, NULL, - popup_position_func, impl->priv->browse_files_tree_view, + popup_position_func, priv->browse_files_tree_view, 0, GDK_CURRENT_TIME); - gtk_menu_shell_select_first (GTK_MENU_SHELL (impl->priv->browse_files_popup_menu), + gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->browse_files_popup_menu), FALSE); } @@ -3924,6 +1837,8 @@ list_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + static gboolean in_press = FALSE; if (in_press) @@ -3933,7 +1848,7 @@ list_button_press_event_cb (GtkWidget *widget, return FALSE; in_press = TRUE; - gtk_widget_event (impl->priv->browse_files_tree_view, (GdkEvent *) event); + gtk_widget_event (priv->browse_files_tree_view, (GdkEvent *) event); in_press = FALSE; file_list_popup_menu (impl, event); @@ -3952,9 +1867,11 @@ typedef struct { static void file_list_set_sort_column_ids (GtkFileChooserDefault *impl) { - gtk_tree_view_column_set_sort_column_id (impl->priv->list_name_column, MODEL_COL_NAME); - gtk_tree_view_column_set_sort_column_id (impl->priv->list_mtime_column, MODEL_COL_MTIME); - gtk_tree_view_column_set_sort_column_id (impl->priv->list_size_column, MODEL_COL_SIZE); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + 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_mtime_column, MODEL_COL_MTIME); + gtk_tree_view_column_set_sort_column_id (priv->list_size_column, MODEL_COL_SIZE); } static gboolean @@ -3966,17 +1883,18 @@ file_list_query_tooltip_cb (GtkWidget *widget, gpointer user_data) { GtkFileChooserDefault *impl = user_data; + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; GFile *file; gchar *filename; - if (impl->priv->operation_mode == OPERATION_MODE_BROWSE) + if (priv->operation_mode == OPERATION_MODE_BROWSE) return FALSE; - if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), + if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (priv->browse_files_tree_view), &x, &y, keyboard_tip, &model, &path, &iter)) @@ -3994,7 +1912,7 @@ 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 (impl->priv->browse_files_tree_view), + gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (priv->browse_files_tree_view), tooltip, path); @@ -4008,87 +1926,94 @@ file_list_query_tooltip_cb (GtkWidget *widget, static void set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; gint xpad, ypad; - gtk_cell_renderer_get_padding (impl->priv->list_pixbuf_renderer, &xpad, &ypad); - gtk_cell_renderer_set_fixed_size (impl->priv->list_pixbuf_renderer, - xpad * 2 + impl->priv->icon_size, - ypad * 2 + impl->priv->icon_size); + 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); } + static void location_entry_create (GtkFileChooserDefault *impl) { - if (!impl->priv->location_entry) - impl->priv->location_entry = _gtk_file_chooser_entry_new (TRUE); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (!priv->location_entry) + priv->location_entry = _gtk_file_chooser_entry_new (TRUE); - _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), impl->priv->local_only); - _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), impl->priv->action); - gtk_entry_set_width_chars (GTK_ENTRY (impl->priv->location_entry), 45); - gtk_entry_set_activates_default (GTK_ENTRY (impl->priv->location_entry), TRUE); + _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->local_only); + _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->action); + gtk_entry_set_width_chars (GTK_ENTRY (priv->location_entry), 45); + gtk_entry_set_activates_default (GTK_ENTRY (priv->location_entry), TRUE); } /* Creates the widgets specific to Save mode */ static void save_widgets_create (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *vbox; GtkWidget *widget; - if (impl->priv->save_widgets != NULL) + if (priv->save_widgets != NULL) return; location_switch_to_path_bar (impl); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); - impl->priv->save_widgets_table = gtk_grid_new (); - gtk_box_pack_start (GTK_BOX (vbox), impl->priv->save_widgets_table, FALSE, FALSE, 0); - gtk_widget_show (impl->priv->save_widgets_table); - gtk_grid_set_row_spacing (GTK_GRID (impl->priv->save_widgets_table), 12); - gtk_grid_set_column_spacing (GTK_GRID (impl->priv->save_widgets_table), 12); + priv->save_widgets_table = gtk_grid_new (); + gtk_box_pack_start (GTK_BOX (vbox), priv->save_widgets_table, FALSE, FALSE, 0); + gtk_widget_show (priv->save_widgets_table); + gtk_grid_set_row_spacing (GTK_GRID (priv->save_widgets_table), 12); + gtk_grid_set_column_spacing (GTK_GRID (priv->save_widgets_table), 12); /* Label */ widget = gtk_label_new_with_mnemonic (_("_Name:")); gtk_widget_set_halign (widget, GTK_ALIGN_START); gtk_widget_set_valign (widget, GTK_ALIGN_CENTER); - gtk_grid_attach (GTK_GRID (impl->priv->save_widgets_table), widget, 0, 0, 1, 1); + gtk_grid_attach (GTK_GRID (priv->save_widgets_table), widget, 0, 0, 1, 1); gtk_widget_show (widget); /* Location entry */ location_entry_create (impl); - gtk_widget_set_hexpand (impl->priv->location_entry, TRUE); - gtk_grid_attach (GTK_GRID (impl->priv->save_widgets_table), impl->priv->location_entry, 1, 0, 1, 1); - gtk_widget_show (impl->priv->location_entry); - gtk_label_set_mnemonic_widget (GTK_LABEL (widget), impl->priv->location_entry); + gtk_widget_set_hexpand (priv->location_entry, TRUE); + gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->location_entry, 1, 0, 1, 1); + gtk_widget_show (priv->location_entry); + gtk_label_set_mnemonic_widget (GTK_LABEL (widget), priv->location_entry); /* Folder combo */ - impl->priv->save_folder_label = gtk_label_new (NULL); - gtk_widget_set_halign (impl->priv->save_folder_label, GTK_ALIGN_START); - gtk_widget_set_valign (impl->priv->save_folder_label, GTK_ALIGN_CENTER); - gtk_grid_attach (GTK_GRID (impl->priv->save_widgets_table), impl->priv->save_folder_label, 0, 1, 1, 1); - gtk_widget_show (impl->priv->save_folder_label); + priv->save_folder_label = gtk_label_new (NULL); + gtk_widget_set_halign (priv->save_folder_label, GTK_ALIGN_START); + gtk_widget_set_valign (priv->save_folder_label, GTK_ALIGN_CENTER); + gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->save_folder_label, 0, 1, 1, 1); + gtk_widget_show (priv->save_folder_label); - impl->priv->save_widgets = vbox; - gtk_box_pack_start (GTK_BOX (impl), impl->priv->save_widgets, FALSE, FALSE, 0); - gtk_box_reorder_child (GTK_BOX (impl), impl->priv->save_widgets, 0); - gtk_widget_show (impl->priv->save_widgets); + priv->save_widgets = vbox; + gtk_box_pack_start (GTK_BOX (impl), priv->save_widgets, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (impl), priv->save_widgets, 0); + gtk_widget_show (priv->save_widgets); } /* Destroys the widgets specific to Save mode */ static void save_widgets_destroy (GtkFileChooserDefault *impl) { - if (impl->priv->save_widgets == NULL) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->save_widgets == NULL) return; - gtk_widget_destroy (impl->priv->save_widgets); - impl->priv->save_widgets = NULL; - impl->priv->save_widgets_table = NULL; - impl->priv->location_entry = NULL; - impl->priv->save_folder_label = NULL; + gtk_widget_destroy (priv->save_widgets); + priv->save_widgets = NULL; + priv->save_widgets_table = NULL; + priv->location_entry = NULL; + priv->save_folder_label = NULL; } /* Turns on the path bar widget. Can be called even if we are already in that @@ -4097,13 +2022,15 @@ save_widgets_destroy (GtkFileChooserDefault *impl) static void location_switch_to_path_bar (GtkFileChooserDefault *impl) { - if (impl->priv->location_entry) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->location_entry) { - gtk_widget_destroy (impl->priv->location_entry); - impl->priv->location_entry = NULL; + gtk_widget_destroy (priv->location_entry); + priv->location_entry = NULL; } - gtk_widget_hide (impl->priv->location_entry_box); + gtk_widget_hide (priv->location_entry_box); } /* Turns on the location entry. Can be called even if we are already in that @@ -4112,35 +2039,37 @@ location_switch_to_path_bar (GtkFileChooserDefault *impl) static void location_switch_to_filename_entry (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + /* when in search or recent files mode, we are not showing the * location_entry_box container, so there's no point in switching * to it. */ - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH || - impl->priv->operation_mode == OPERATION_MODE_RECENT) + if (priv->operation_mode == OPERATION_MODE_SEARCH || + priv->operation_mode == OPERATION_MODE_RECENT) return; /* Box */ - gtk_widget_show (impl->priv->location_entry_box); + gtk_widget_show (priv->location_entry_box); /* Entry */ - if (!impl->priv->location_entry) + if (!priv->location_entry) { location_entry_create (impl); - gtk_box_pack_start (GTK_BOX (impl->priv->location_entry_box), impl->priv->location_entry, TRUE, TRUE, 0); - gtk_label_set_mnemonic_widget (GTK_LABEL (impl->priv->location_label), impl->priv->location_entry); + gtk_box_pack_start (GTK_BOX (priv->location_entry_box), priv->location_entry, TRUE, TRUE, 0); + gtk_label_set_mnemonic_widget (GTK_LABEL (priv->location_label), priv->location_entry); } /* Configure the entry */ - _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), impl->priv->current_folder); + _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->current_folder); /* Done */ - gtk_widget_show (impl->priv->location_entry); - gtk_widget_grab_focus (impl->priv->location_entry); + gtk_widget_show (priv->location_entry); + gtk_widget_grab_focus (priv->location_entry); } /* Sets a new location mode. set_buttons determines whether the toggle button @@ -4151,8 +2080,10 @@ location_mode_set (GtkFileChooserDefault *impl, LocationMode new_mode, gboolean set_button) { - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { GtkWindow *toplevel; GtkWidget *current_focus; @@ -4173,14 +2104,14 @@ location_mode_set (GtkFileChooserDefault *impl, if (toplevel) { current_focus = gtk_window_get_focus (toplevel); - if (!current_focus || current_focus == impl->priv->location_entry) + if (!current_focus || current_focus == priv->location_entry) switch_to_file_list = TRUE; } location_switch_to_path_bar (impl); if (switch_to_file_list) - gtk_widget_grab_focus (impl->priv->browse_files_tree_view); + gtk_widget_grab_focus (priv->browse_files_tree_view); break; @@ -4196,46 +2127,48 @@ location_mode_set (GtkFileChooserDefault *impl, if (set_button) { - g_signal_handlers_block_by_func (impl->priv->location_button, + g_signal_handlers_block_by_func (priv->location_button, G_CALLBACK (location_button_toggled_cb), impl); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (impl->priv->location_button), button_active); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->location_button), button_active); - g_signal_handlers_unblock_by_func (impl->priv->location_button, + g_signal_handlers_unblock_by_func (priv->location_button, G_CALLBACK (location_button_toggled_cb), impl); } } - impl->priv->location_mode = new_mode; + priv->location_mode = new_mode; } static void location_toggle_popup_handler (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + /* when in search or recent files mode, we are not showing the * location_entry_box container, so there's no point in switching * to it. */ - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH || - impl->priv->operation_mode == OPERATION_MODE_RECENT) + if (priv->operation_mode == OPERATION_MODE_SEARCH || + priv->operation_mode == OPERATION_MODE_RECENT) return; /* If the file entry is not visible, show it. * If it is visible, turn it off only if it is focused. Otherwise, switch to the entry. */ - if (impl->priv->location_mode == LOCATION_MODE_PATH_BAR) + if (priv->location_mode == LOCATION_MODE_PATH_BAR) { location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE); } - else if (impl->priv->location_mode == LOCATION_MODE_FILENAME_ENTRY) + else if (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY) { - if (gtk_widget_has_focus (impl->priv->location_entry)) + if (gtk_widget_has_focus (priv->location_entry)) { location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE); } else { - gtk_widget_grab_focus (impl->priv->location_entry); + gtk_widget_grab_focus (priv->location_entry); } } } @@ -4245,6 +2178,7 @@ static void location_button_toggled_cb (GtkToggleButton *toggle, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; gboolean is_active; LocationMode new_mode; @@ -4252,12 +2186,12 @@ location_button_toggled_cb (GtkToggleButton *toggle, if (is_active) { - g_assert (impl->priv->location_mode == LOCATION_MODE_PATH_BAR); + g_assert (priv->location_mode == LOCATION_MODE_PATH_BAR); new_mode = LOCATION_MODE_FILENAME_ENTRY; } else { - g_assert (impl->priv->location_mode == LOCATION_MODE_FILENAME_ENTRY); + g_assert (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY); new_mode = LOCATION_MODE_PATH_BAR; } @@ -4277,6 +2211,7 @@ typedef enum { static void info_bar_set (GtkFileChooserDefault *impl, PathBarMode mode) { + GtkFileChooserDefaultPrivate *priv = impl->priv; char *str; gboolean free_str; GtkMessageType message_type; @@ -4306,11 +2241,11 @@ info_bar_set (GtkFileChooserDefault *impl, PathBarMode mode) return; } - gtk_info_bar_set_message_type (GTK_INFO_BAR (impl->priv->browse_select_a_folder_info_bar), message_type); - gtk_image_set_from_stock (GTK_IMAGE (impl->priv->browse_select_a_folder_icon), + gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->browse_select_a_folder_info_bar), message_type); + gtk_image_set_from_stock (GTK_IMAGE (priv->browse_select_a_folder_icon), (message_type == GTK_MESSAGE_WARNING) ? GTK_STOCK_DIALOG_WARNING : GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU); - gtk_label_set_markup (GTK_LABEL (impl->priv->browse_select_a_folder_label), str); + gtk_label_set_markup (GTK_LABEL (priv->browse_select_a_folder_label), str); if (free_str) g_free (str); @@ -4324,6 +2259,7 @@ info_bar_set (GtkFileChooserDefault *impl, PathBarMode mode) static void path_bar_set_mode (GtkFileChooserDefault *impl, PathBarMode mode) { + GtkFileChooserDefaultPrivate *priv = impl->priv; gboolean path_bar_visible = FALSE; gboolean special_mode_widgets_visible = FALSE; gboolean info_bar_visible = FALSE; @@ -4345,20 +2281,20 @@ path_bar_set_mode (GtkFileChooserDefault *impl, PathBarMode mode) break; case PATH_BAR_RECENTLY_USED: - gtk_image_set_from_icon_name (GTK_IMAGE (impl->priv->browse_special_mode_icon), "document-open-recent", GTK_ICON_SIZE_BUTTON); + gtk_image_set_from_icon_name (GTK_IMAGE (priv->browse_special_mode_icon), "document-open-recent", GTK_ICON_SIZE_BUTTON); tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used")); - gtk_label_set_markup (GTK_LABEL (impl->priv->browse_special_mode_label), tmp); + gtk_label_set_markup (GTK_LABEL (priv->browse_special_mode_label), tmp); g_free (tmp); special_mode_widgets_visible = TRUE; break; case PATH_BAR_SEARCH: - gtk_image_set_from_stock (GTK_IMAGE (impl->priv->browse_special_mode_icon), GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON); + gtk_image_set_from_stock (GTK_IMAGE (priv->browse_special_mode_icon), GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON); tmp = g_strdup_printf ("<b>%s</b>", _("Search:")); - gtk_label_set_markup (GTK_LABEL (impl->priv->browse_special_mode_label), tmp); + gtk_label_set_markup (GTK_LABEL (priv->browse_special_mode_label), tmp); g_free (tmp); special_mode_widgets_visible = TRUE; @@ -4368,28 +2304,30 @@ path_bar_set_mode (GtkFileChooserDefault *impl, PathBarMode mode) g_assert_not_reached (); } - gtk_widget_set_visible (impl->priv->browse_path_bar, path_bar_visible); - gtk_widget_set_visible (impl->priv->browse_special_mode_icon, special_mode_widgets_visible); - gtk_widget_set_visible (impl->priv->browse_special_mode_label, special_mode_widgets_visible); - gtk_widget_set_visible (impl->priv->browse_select_a_folder_info_bar, info_bar_visible); + gtk_widget_set_visible (priv->browse_path_bar, path_bar_visible); + gtk_widget_set_visible (priv->browse_special_mode_icon, special_mode_widgets_visible); + gtk_widget_set_visible (priv->browse_special_mode_label, special_mode_widgets_visible); + gtk_widget_set_visible (priv->browse_select_a_folder_info_bar, info_bar_visible); if (path_bar_visible) { - if (impl->priv->create_folders - && impl->priv->action != GTK_FILE_CHOOSER_ACTION_OPEN - && impl->priv->operation_mode != OPERATION_MODE_RECENT) + if (priv->create_folders + && priv->action != GTK_FILE_CHOOSER_ACTION_OPEN + && priv->operation_mode != OPERATION_MODE_RECENT) create_folder_visible = TRUE; } - gtk_widget_set_visible (impl->priv->browse_new_folder_button, create_folder_visible); + gtk_widget_set_visible (priv->browse_new_folder_button, create_folder_visible); } + static GObject* gtk_file_chooser_default_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GtkFileChooserDefault *impl; + GtkFileChooserDefaultPrivate *priv; GObject *object; profile_start ("start", NULL); @@ -4398,9 +2336,9 @@ gtk_file_chooser_default_constructor (GType type, n_construct_properties, construct_params); impl = GTK_FILE_CHOOSER_DEFAULT (object); + priv = impl->priv; - - g_assert (impl->priv->file_system); + g_assert (priv->file_system); update_appearance (impl); @@ -4414,6 +2352,8 @@ static void set_extra_widget (GtkFileChooserDefault *impl, GtkWidget *extra_widget) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + if (extra_widget) { g_object_ref (extra_widget); @@ -4421,93 +2361,84 @@ set_extra_widget (GtkFileChooserDefault *impl, gtk_widget_show (extra_widget); } - if (impl->priv->extra_widget) + if (priv->extra_widget) { - gtk_container_remove (GTK_CONTAINER (impl->priv->extra_align), impl->priv->extra_widget); - g_object_unref (impl->priv->extra_widget); + gtk_container_remove (GTK_CONTAINER (priv->extra_align), priv->extra_widget); + g_object_unref (priv->extra_widget); } - impl->priv->extra_widget = extra_widget; - if (impl->priv->extra_widget) + priv->extra_widget = extra_widget; + if (priv->extra_widget) { - gtk_container_add (GTK_CONTAINER (impl->priv->extra_align), impl->priv->extra_widget); - gtk_widget_show (impl->priv->extra_align); + gtk_container_add (GTK_CONTAINER (priv->extra_align), priv->extra_widget); + gtk_widget_show (priv->extra_align); } else - gtk_widget_hide (impl->priv->extra_align); + gtk_widget_hide (priv->extra_align); +} + +static void +switch_to_home_dir (GtkFileChooserDefault *impl) +{ + const gchar *home = g_get_home_dir (); + GFile *home_file; + + if (home == NULL) + return; + + home_file = g_file_new_for_path (home); + + gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL); /* NULL-GError */ + + g_object_unref (home_file); } static void set_local_only (GtkFileChooserDefault *impl, gboolean local_only) { - if (local_only != impl->priv->local_only) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (local_only != priv->local_only) { - impl->priv->local_only = local_only; + priv->local_only = local_only; - if (impl->priv->location_entry) - _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), local_only); + if (priv->location_entry) + _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), local_only); - if (impl->priv->shortcuts_model && impl->priv->file_system) - { - shortcuts_add_volumes (impl); - shortcuts_add_bookmarks (impl); - } - - if (local_only && impl->priv->current_folder && - !_gtk_file_has_native_path (impl->priv->current_folder)) + if (local_only && priv->current_folder && + !_gtk_file_has_native_path (priv->current_folder)) { /* If we are pointing to a non-local folder, make an effort to change * back to a local folder, but it's really up to the app to not cause * such a situation, so we ignore errors. */ - const gchar *home = g_get_home_dir (); - GFile *home_file; - - if (home == NULL) - return; - - home_file = g_file_new_for_path (home); - - gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL); - - g_object_unref (home_file); + switch_to_home_dir (impl); } } } -static void -volumes_bookmarks_changed_cb (GtkFileSystem *file_system, - GtkFileChooserDefault *impl) -{ - shortcuts_add_volumes (impl); - shortcuts_add_bookmarks (impl); - - bookmarks_check_add_sensitivity (impl); - bookmarks_check_remove_sensitivity (impl); - shortcuts_check_popup_sensitivity (impl); -} - /* Sets the file chooser to multiple selection mode */ static void set_select_multiple (GtkFileChooserDefault *impl, gboolean select_multiple, gboolean property_notify) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; GtkSelectionMode mode; - if (select_multiple == impl->priv->select_multiple) + 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 (impl->priv->browse_files_tree_view)); + 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 (impl->priv->browse_files_tree_view), select_multiple); + gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (priv->browse_files_tree_view), select_multiple); - impl->priv->select_multiple = select_multiple; + priv->select_multiple = select_multiple; g_object_notify (G_OBJECT (impl), "select-multiple"); check_preview_change (impl); @@ -4516,14 +2447,11 @@ set_select_multiple (GtkFileChooserDefault *impl, static void set_file_system_backend (GtkFileChooserDefault *impl) { - profile_start ("start for backend", "default"); + GtkFileChooserDefaultPrivate *priv = impl->priv; - impl->priv->file_system = _gtk_file_system_new (); + profile_start ("start for backend", "default"); - g_signal_connect (impl->priv->file_system, "volumes-changed", - G_CALLBACK (volumes_bookmarks_changed_cb), impl); - g_signal_connect (impl->priv->file_system, "bookmarks-changed", - G_CALLBACK (volumes_bookmarks_changed_cb), impl); + priv->file_system = _gtk_file_system_new (); profile_end ("end", NULL); } @@ -4531,12 +2459,11 @@ set_file_system_backend (GtkFileChooserDefault *impl) static void unset_file_system_backend (GtkFileChooserDefault *impl) { - g_signal_handlers_disconnect_by_func (impl->priv->file_system, - G_CALLBACK (volumes_bookmarks_changed_cb), impl); + GtkFileChooserDefaultPrivate *priv = impl->priv; - g_object_unref (impl->priv->file_system); + g_object_unref (priv->file_system); - impl->priv->file_system = NULL; + priv->file_system = NULL; } /* Saves the widgets around the pathbar so they can be reparented later @@ -4546,13 +2473,14 @@ unset_file_system_backend (GtkFileChooserDefault *impl) static void save_path_bar (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *parent; - g_object_ref (impl->priv->browse_path_bar_hbox); + g_object_ref (priv->browse_path_bar_hbox); - parent = gtk_widget_get_parent (impl->priv->browse_path_bar_hbox); + parent = gtk_widget_get_parent (priv->browse_path_bar_hbox); if (parent) - gtk_container_remove (GTK_CONTAINER (parent), impl->priv->browse_path_bar_hbox); + gtk_container_remove (GTK_CONTAINER (parent), priv->browse_path_bar_hbox); } /* Reparents the path bar and the "Create folder" button to the right place: @@ -4563,61 +2491,65 @@ save_path_bar (GtkFileChooserDefault *impl) static void restore_path_bar (GtkFileChooserDefault *impl) { - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN + || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { - gtk_box_pack_start (GTK_BOX (impl->priv->browse_header_box), impl->priv->browse_path_bar_hbox, FALSE, FALSE, 0); - gtk_box_reorder_child (GTK_BOX (impl->priv->browse_header_box), impl->priv->browse_path_bar_hbox, 0); + gtk_box_pack_start (GTK_BOX (priv->browse_header_box), priv->browse_path_bar_hbox, FALSE, FALSE, 0); + gtk_box_reorder_child (GTK_BOX (priv->browse_header_box), priv->browse_path_bar_hbox, 0); } - else if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { - gtk_widget_set_hexpand (impl->priv->browse_path_bar_hbox, TRUE); - gtk_grid_attach (GTK_GRID (impl->priv->save_widgets_table), impl->priv->browse_path_bar_hbox, 1, 1, 1, 1); - gtk_label_set_mnemonic_widget (GTK_LABEL (impl->priv->save_folder_label), impl->priv->browse_path_bar); + gtk_widget_set_hexpand (priv->browse_path_bar_hbox, TRUE); + gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->browse_path_bar_hbox, 1, 1, 1, 1); + gtk_label_set_mnemonic_widget (GTK_LABEL (priv->save_folder_label), priv->browse_path_bar); } else g_assert_not_reached (); - g_object_unref (impl->priv->browse_path_bar_hbox); + g_object_unref (priv->browse_path_bar_hbox); } /* Takes the folder stored in a row in the recent_model, and puts it in the pathbar */ static void put_recent_folder_in_pathbar (GtkFileChooserDefault *impl, GtkTreeIter *iter) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GFile *file; - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->recent_model), iter, + gtk_tree_model_get (GTK_TREE_MODEL (priv->recent_model), iter, MODEL_COL_FILE, &file, -1); - _gtk_path_bar_set_file (GTK_PATH_BAR (impl->priv->browse_path_bar), file, FALSE); + _gtk_path_bar_set_file (GTK_PATH_BAR (priv->browse_path_bar), file, FALSE); g_object_unref (file); } /* Sets the pathbar in the appropriate mode according to the current operation mode and action. This is the central function for - * dealing with the pathbar's widgets; as long as impl->priv->action and impl->priv->operation_mode are set correctly, then calling this + * dealing with the pathbar's widgets; as long as impl->action and impl->operation_mode are set correctly, then calling this * function will update all the pathbar's widgets. */ static void path_bar_update (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; PathBarMode mode; - switch (impl->priv->operation_mode) + switch (priv->operation_mode) { case OPERATION_MODE_BROWSE: mode = PATH_BAR_FOLDER_PATH; break; case OPERATION_MODE_RECENT: - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { GtkTreeSelection *selection; gboolean have_selected; GtkTreeIter iter; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); /* Save mode means single-selection mode, so the following is valid */ have_selected = gtk_tree_selection_get_selected (selection, NULL, &iter); @@ -4650,12 +2582,14 @@ path_bar_update (GtkFileChooserDefault *impl) static void operation_mode_discard_search_widgets (GtkFileChooserDefault *impl) { - if (impl->priv->search_hbox) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->search_hbox) { - gtk_widget_destroy (impl->priv->search_hbox); + gtk_widget_destroy (priv->search_hbox); - impl->priv->search_hbox = NULL; - impl->priv->search_entry = NULL; + priv->search_hbox = NULL; + priv->search_entry = NULL; } } @@ -4689,25 +2623,29 @@ operation_mode_stop (GtkFileChooserDefault *impl, OperationMode mode) static void operation_mode_set_browse (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + path_bar_update (impl); - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { - gtk_widget_show (impl->priv->location_button); - location_mode_set (impl, impl->priv->location_mode, TRUE); + gtk_widget_show (priv->location_button); + location_mode_set (impl, priv->location_mode, TRUE); - if (impl->priv->location_mode == LOCATION_MODE_FILENAME_ENTRY) - gtk_widget_show (impl->priv->location_entry_box); + if (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY) + gtk_widget_show (priv->location_entry_box); } } static void operation_mode_set_search (GtkFileChooserDefault *impl) { - g_assert (impl->priv->search_hbox == NULL); - g_assert (impl->priv->search_entry == NULL); - g_assert (impl->priv->search_model == NULL); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + g_assert (priv->search_hbox == NULL); + g_assert (priv->search_entry == NULL); + g_assert (priv->search_model == NULL); search_setup_widgets (impl); } @@ -4715,70 +2653,52 @@ operation_mode_set_search (GtkFileChooserDefault *impl) static void operation_mode_set_recent (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + path_bar_update (impl); /* Hide the location widgets temporarily */ - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { - gtk_widget_hide (impl->priv->location_button); - gtk_widget_hide (impl->priv->location_entry_box); + gtk_widget_hide (priv->location_button); + gtk_widget_hide (priv->location_entry_box); } recent_start_loading (impl); } -/* Sometimes we need to frob the selection in the shortcuts list manually */ -static void -shortcuts_select_item_without_activating (GtkFileChooserDefault *impl, int pos) -{ - GtkTreeSelection *selection; - GtkTreePath *path; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view)); - - g_signal_handlers_block_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl); - - path = gtk_tree_path_new_from_indices (pos, -1); - gtk_tree_selection_select_path (selection, path); - gtk_tree_path_free (path); - - g_signal_handlers_unblock_by_func (selection, G_CALLBACK (shortcuts_selection_changed_cb), impl); -} - static void operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode) { - ShortcutsIndex shortcut_to_select; + GtkFileChooserDefaultPrivate *priv = impl->priv; + GFile *file; - operation_mode_stop (impl, impl->priv->operation_mode); + operation_mode_stop (impl, priv->operation_mode); - impl->priv->operation_mode = mode; + priv->operation_mode = mode; - switch (impl->priv->operation_mode) + switch (priv->operation_mode) { case OPERATION_MODE_BROWSE: operation_mode_set_browse (impl); - shortcut_to_select = SHORTCUTS_CURRENT_FOLDER; break; case OPERATION_MODE_SEARCH: operation_mode_set_search (impl); - shortcut_to_select = SHORTCUTS_SEARCH; break; case OPERATION_MODE_RECENT: operation_mode_set_recent (impl); - shortcut_to_select = SHORTCUTS_RECENT; + file = g_file_new_for_uri ("recent:///"); + gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (priv->places_sidebar), file); + g_object_unref (file); break; default: g_assert_not_reached (); return; } - - if (shortcut_to_select != SHORTCUTS_CURRENT_FOLDER) - shortcuts_select_item_without_activating (impl, shortcuts_get_index (impl, shortcut_to_select)); } /* This function is basically a do_all function. @@ -4789,40 +2709,42 @@ operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode) static void update_appearance (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + save_path_bar (impl); - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { const char *text; - gtk_widget_hide (impl->priv->location_button); + gtk_widget_hide (priv->location_button); save_widgets_create (impl); - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) text = _("Save in _folder:"); else text = _("Create in _folder:"); - gtk_label_set_text_with_mnemonic (GTK_LABEL (impl->priv->save_folder_label), text); + gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->save_folder_label), text); - if (impl->priv->select_multiple) + if (priv->select_multiple) { g_warning ("Save mode cannot be set in conjunction with multiple selection mode. " "Re-setting to single selection mode."); set_select_multiple (impl, FALSE, TRUE); } } - else if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { - gtk_widget_show (impl->priv->location_button); + gtk_widget_show (priv->location_button); save_widgets_destroy (impl); - location_mode_set (impl, impl->priv->location_mode, TRUE); + location_mode_set (impl, priv->location_mode, TRUE); } - if (impl->priv->location_entry) - _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), impl->priv->action); + if (priv->location_entry) + _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->action); restore_path_bar (impl); path_bar_update (impl); @@ -4830,7 +2752,7 @@ update_appearance (GtkFileChooserDefault *impl) /* This *is* needed; we need to redraw the file list because the "sensitivity" * of files may change depending whether we are in a file or folder-only mode. */ - gtk_widget_queue_draw (impl->priv->browse_files_tree_view); + gtk_widget_queue_draw (priv->browse_files_tree_view); emit_default_size_changed (impl); } @@ -4843,6 +2765,7 @@ gtk_file_chooser_default_set_property (GObject *object, { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object); + GtkFileChooserDefaultPrivate *priv = impl->priv; switch (prop_id) { @@ -4850,20 +2773,20 @@ gtk_file_chooser_default_set_property (GObject *object, { GtkFileChooserAction action = g_value_get_enum (value); - if (action != impl->priv->action) + if (action != priv->action) { gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl)); if ((action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - && impl->priv->select_multiple) + && priv->select_multiple) { g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but " "this is not allowed in multiple selection mode. Resetting the file chooser " "to single selection mode."); set_select_multiple (impl, FALSE, TRUE); } - impl->priv->action = action; + priv->action = action; update_cell_renderer_attributes (impl); update_appearance (impl); settings_load (impl); @@ -4884,12 +2807,12 @@ gtk_file_chooser_default_set_property (GObject *object, break; case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE: - impl->priv->preview_widget_active = g_value_get_boolean (value); + priv->preview_widget_active = g_value_get_boolean (value); update_preview_widget_visibility (impl); break; case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL: - impl->priv->use_preview_label = g_value_get_boolean (value); + priv->use_preview_label = g_value_get_boolean (value); update_preview_widget_visibility (impl); break; @@ -4900,8 +2823,8 @@ gtk_file_chooser_default_set_property (GObject *object, case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE: { gboolean select_multiple = g_value_get_boolean (value); - if ((impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + if ((priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) && select_multiple) { g_warning ("Tried to set the file chooser to multiple selection mode, but this is " @@ -4917,12 +2840,12 @@ gtk_file_chooser_default_set_property (GObject *object, case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN: { gboolean show_hidden = g_value_get_boolean (value); - if (show_hidden != impl->priv->show_hidden) + if (show_hidden != priv->show_hidden) { - impl->priv->show_hidden = show_hidden; + priv->show_hidden = show_hidden; - if (impl->priv->browse_files_model) - _gtk_file_system_model_set_show_hidden (impl->priv->browse_files_model, show_hidden); + if (priv->browse_files_model) + _gtk_file_system_model_set_show_hidden (priv->browse_files_model, show_hidden); } } break; @@ -4930,14 +2853,14 @@ gtk_file_chooser_default_set_property (GObject *object, case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION: { gboolean do_overwrite_confirmation = g_value_get_boolean (value); - impl->priv->do_overwrite_confirmation = do_overwrite_confirmation; + priv->do_overwrite_confirmation = do_overwrite_confirmation; } break; case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS: { gboolean create_folders = g_value_get_boolean (value); - impl->priv->create_folders = create_folders; + priv->create_folders = create_folders; update_appearance (impl); } break; @@ -4955,51 +2878,52 @@ gtk_file_chooser_default_get_property (GObject *object, GParamSpec *pspec) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object); + GtkFileChooserDefaultPrivate *priv = impl->priv; switch (prop_id) { case GTK_FILE_CHOOSER_PROP_ACTION: - g_value_set_enum (value, impl->priv->action); + g_value_set_enum (value, priv->action); break; case GTK_FILE_CHOOSER_PROP_FILTER: - g_value_set_object (value, impl->priv->current_filter); + g_value_set_object (value, priv->current_filter); break; case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY: - g_value_set_boolean (value, impl->priv->local_only); + g_value_set_boolean (value, priv->local_only); break; case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET: - g_value_set_object (value, impl->priv->preview_widget); + g_value_set_object (value, priv->preview_widget); break; case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE: - g_value_set_boolean (value, impl->priv->preview_widget_active); + g_value_set_boolean (value, priv->preview_widget_active); break; case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL: - g_value_set_boolean (value, impl->priv->use_preview_label); + g_value_set_boolean (value, priv->use_preview_label); break; case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET: - g_value_set_object (value, impl->priv->extra_widget); + g_value_set_object (value, priv->extra_widget); break; case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE: - g_value_set_boolean (value, impl->priv->select_multiple); + g_value_set_boolean (value, priv->select_multiple); break; case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN: - g_value_set_boolean (value, impl->priv->show_hidden); + g_value_set_boolean (value, priv->show_hidden); break; case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION: - g_value_set_boolean (value, impl->priv->do_overwrite_confirmation); + g_value_set_boolean (value, priv->do_overwrite_confirmation); break; case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS: - g_value_set_boolean (value, impl->priv->create_folders); + g_value_set_boolean (value, priv->create_folders); break; default: @@ -5012,66 +2936,67 @@ gtk_file_chooser_default_get_property (GObject *object, static void cancel_all_operations (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GSList *l; pending_select_files_free (impl); - if (impl->priv->reload_icon_cancellables) + if (priv->reload_icon_cancellables) { - for (l = impl->priv->reload_icon_cancellables; l; l = l->next) + for (l = priv->reload_icon_cancellables; l; l = l->next) { GCancellable *cancellable = G_CANCELLABLE (l->data); g_cancellable_cancel (cancellable); } - g_slist_free (impl->priv->reload_icon_cancellables); - impl->priv->reload_icon_cancellables = NULL; + g_slist_free (priv->reload_icon_cancellables); + priv->reload_icon_cancellables = NULL; } - if (impl->priv->loading_shortcuts) + if (priv->loading_shortcuts) { - for (l = impl->priv->loading_shortcuts; l; l = l->next) + for (l = priv->loading_shortcuts; l; l = l->next) { GCancellable *cancellable = G_CANCELLABLE (l->data); g_cancellable_cancel (cancellable); } - g_slist_free (impl->priv->loading_shortcuts); - impl->priv->loading_shortcuts = NULL; + g_slist_free (priv->loading_shortcuts); + priv->loading_shortcuts = NULL; } - if (impl->priv->file_list_drag_data_received_cancellable) + if (priv->file_list_drag_data_received_cancellable) { - g_cancellable_cancel (impl->priv->file_list_drag_data_received_cancellable); - impl->priv->file_list_drag_data_received_cancellable = NULL; + g_cancellable_cancel (priv->file_list_drag_data_received_cancellable); + priv->file_list_drag_data_received_cancellable = NULL; } - if (impl->priv->update_current_folder_cancellable) + if (priv->update_current_folder_cancellable) { - g_cancellable_cancel (impl->priv->update_current_folder_cancellable); - impl->priv->update_current_folder_cancellable = NULL; + g_cancellable_cancel (priv->update_current_folder_cancellable); + priv->update_current_folder_cancellable = NULL; } - if (impl->priv->should_respond_get_info_cancellable) + if (priv->should_respond_get_info_cancellable) { - g_cancellable_cancel (impl->priv->should_respond_get_info_cancellable); - impl->priv->should_respond_get_info_cancellable = NULL; + g_cancellable_cancel (priv->should_respond_get_info_cancellable); + priv->should_respond_get_info_cancellable = NULL; } - if (impl->priv->file_exists_get_info_cancellable) + if (priv->file_exists_get_info_cancellable) { - g_cancellable_cancel (impl->priv->file_exists_get_info_cancellable); - impl->priv->file_exists_get_info_cancellable = NULL; + g_cancellable_cancel (priv->file_exists_get_info_cancellable); + priv->file_exists_get_info_cancellable = NULL; } - if (impl->priv->update_from_entry_cancellable) + if (priv->update_from_entry_cancellable) { - g_cancellable_cancel (impl->priv->update_from_entry_cancellable); - impl->priv->update_from_entry_cancellable = NULL; + g_cancellable_cancel (priv->update_from_entry_cancellable); + priv->update_from_entry_cancellable = NULL; } - if (impl->priv->shortcuts_activate_iter_cancellable) + if (priv->shortcuts_activate_iter_cancellable) { - g_cancellable_cancel (impl->priv->shortcuts_activate_iter_cancellable); - impl->priv->shortcuts_activate_iter_cancellable = NULL; + g_cancellable_cancel (priv->shortcuts_activate_iter_cancellable); + priv->shortcuts_activate_iter_cancellable = NULL; } search_stop_searching (impl, TRUE); @@ -5083,14 +3008,16 @@ static void remove_settings_signal (GtkFileChooserDefault *impl, GdkScreen *screen) { - if (impl->priv->settings_signal_id) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->settings_signal_id) { GtkSettings *settings; settings = gtk_settings_get_for_screen (screen); g_signal_handler_disconnect (settings, - impl->priv->settings_signal_id); - impl->priv->settings_signal_id = 0; + priv->settings_signal_id); + priv->settings_signal_id = 0; } } @@ -5098,19 +3025,24 @@ static void gtk_file_chooser_default_dispose (GObject *object) { GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object; + GtkFileChooserDefaultPrivate *priv = impl->priv; cancel_all_operations (impl); - shortcuts_free (impl); - - if (impl->priv->extra_widget) + if (priv->extra_widget) { - g_object_unref (impl->priv->extra_widget); - impl->priv->extra_widget = NULL; + g_object_unref (priv->extra_widget); + priv->extra_widget = NULL; } remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl))); + if (priv->bookmarks_manager) + { + _gtk_bookmarks_manager_free (priv->bookmarks_manager); + priv->bookmarks_manager = NULL; + } + G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object); } @@ -5122,11 +3054,12 @@ static void gtk_file_chooser_default_show_all (GtkWidget *widget) { GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget; + GtkFileChooserDefaultPrivate *priv = impl->priv; gtk_widget_show (widget); - if (impl->priv->extra_widget) - gtk_widget_show_all (impl->priv->extra_widget); + if (priv->extra_widget) + gtk_widget_show_all (priv->extra_widget); } /* Handler for GtkWindow::set-focus; this is where we save the last-focused @@ -5137,7 +3070,9 @@ toplevel_set_focus_cb (GtkWindow *window, GtkWidget *focus, GtkFileChooserDefault *impl) { - impl->priv->toplevel_last_focus_widget = gtk_window_get_focus (window); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + priv->toplevel_last_focus_widget = gtk_window_get_focus (window); } /* We monitor the focus widget on our toplevel to be able to know which widget @@ -5147,27 +3082,27 @@ static void gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget, GtkWidget *previous_toplevel) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget); + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *toplevel; - impl = GTK_FILE_CHOOSER_DEFAULT (widget); toplevel = gtk_widget_get_toplevel (widget); if (previous_toplevel && - impl->priv->toplevel_set_focus_id != 0) + priv->toplevel_set_focus_id != 0) { g_signal_handler_disconnect (previous_toplevel, - impl->priv->toplevel_set_focus_id); - impl->priv->toplevel_set_focus_id = 0; - impl->priv->toplevel_last_focus_widget = NULL; + priv->toplevel_set_focus_id); + priv->toplevel_set_focus_id = 0; + priv->toplevel_last_focus_widget = NULL; } if (gtk_widget_is_toplevel (toplevel)) { - g_assert (impl->priv->toplevel_set_focus_id == 0); - impl->priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus", + g_assert (priv->toplevel_set_focus_id == 0); + priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus", G_CALLBACK (toplevel_set_focus_cb), impl); - impl->priv->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel)); + priv->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel)); } } @@ -5175,6 +3110,7 @@ gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget, static void change_icon_theme (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkSettings *settings; gint width, height; @@ -5183,20 +3119,16 @@ change_icon_theme (GtkFileChooserDefault *impl) settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height)) - impl->priv->icon_size = MAX (width, height); + priv->icon_size = MAX (width, height); else - impl->priv->icon_size = FALLBACK_ICON_SIZE; - - shortcuts_reload_icons (impl); + priv->icon_size = FALLBACK_ICON_SIZE; - /* the first cell in the first column is the icon column, - * and we have a fixed size there - */ + /* 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 (impl->priv->browse_files_model) - _gtk_file_system_model_clear_cache (impl->priv->browse_files_model, MODEL_COL_PIXBUF); - gtk_widget_queue_resize (impl->priv->browse_files_tree_view); + if (priv->browse_files_model) + _gtk_file_system_model_clear_cache (priv->browse_files_model, MODEL_COL_PIXBUF); + gtk_widget_queue_resize (priv->browse_files_tree_view); profile_end ("end", NULL); } @@ -5226,11 +3158,12 @@ settings_notify_cb (GObject *object, static void check_icon_theme (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkSettings *settings; profile_start ("start", NULL); - if (impl->priv->settings_signal_id) + if (priv->settings_signal_id) { profile_end ("end", NULL); return; @@ -5239,7 +3172,7 @@ check_icon_theme (GtkFileChooserDefault *impl) if (gtk_widget_has_screen (GTK_WIDGET (impl))) { settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl))); - impl->priv->settings_signal_id = g_signal_connect (settings, "notify", + priv->settings_signal_id = g_signal_connect (settings, "notify", G_CALLBACK (settings_notify_cb), impl); change_icon_theme (impl); @@ -5293,27 +3226,30 @@ gtk_file_chooser_default_screen_changed (GtkWidget *widget, static void set_sort_column (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSortable *sortable; - sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view))); + sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view))); /* can happen when we're still populating the model */ if (sortable == NULL) return; gtk_tree_sortable_set_sort_column_id (sortable, - impl->priv->sort_column, - impl->priv->sort_order); + priv->sort_column, + priv->sort_order); } static void settings_load (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; LocationMode location_mode; gboolean show_hidden; gboolean show_size_column; gint sort_column; GtkSortType sort_order; + StartupMode startup_mode; gint sidebar_width; GSettings *settings; @@ -5325,41 +3261,45 @@ settings_load (GtkFileChooserDefault *impl) sort_column = g_settings_get_enum (settings, SETTINGS_KEY_SORT_COLUMN); sort_order = g_settings_get_enum (settings, SETTINGS_KEY_SORT_ORDER); sidebar_width = g_settings_get_int (settings, SETTINGS_KEY_SIDEBAR_WIDTH); + startup_mode = g_settings_get_enum (settings, SETTINGS_KEY_STARTUP_MODE); location_mode_set (impl, location_mode, TRUE); gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden); - impl->priv->show_size_column = show_size_column; - gtk_tree_view_column_set_visible (impl->priv->list_size_column, show_size_column); + priv->show_size_column = show_size_column; + gtk_tree_view_column_set_visible (priv->list_size_column, show_size_column); + + priv->sort_column = sort_column; + priv->sort_order = sort_order; + priv->startup_mode = startup_mode; - impl->priv->sort_column = sort_column; - impl->priv->sort_order = sort_order; /* We don't call set_sort_column() here as the models may not have been * created yet. The individual functions that create and set the models will * call set_sort_column() themselves. */ - gtk_paned_set_position (GTK_PANED (impl->priv->browse_widgets_hpaned), sidebar_width); + gtk_paned_set_position (GTK_PANED (priv->browse_widgets_hpaned), sidebar_width); } static void settings_save (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GSettings *settings; settings = _gtk_file_chooser_get_settings_for_widget (GTK_WIDGET (impl)); /* All the other state */ - g_settings_set_enum (settings, SETTINGS_KEY_LOCATION_MODE, impl->priv->location_mode); + g_settings_set_enum (settings, SETTINGS_KEY_LOCATION_MODE, priv->location_mode); 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, impl->priv->show_size_column); - g_settings_set_enum (settings, SETTINGS_KEY_SORT_COLUMN, impl->priv->sort_column); - g_settings_set_enum (settings, SETTINGS_KEY_SORT_ORDER, impl->priv->sort_order); + g_settings_set_boolean (settings, SETTINGS_KEY_SHOW_SIZE_COLUMN, priv->show_size_column); + g_settings_set_enum (settings, SETTINGS_KEY_SORT_COLUMN, priv->sort_column); + g_settings_set_enum (settings, SETTINGS_KEY_SORT_ORDER, priv->sort_order); g_settings_set_int (settings, SETTINGS_KEY_SIDEBAR_WIDTH, - gtk_paned_get_position (GTK_PANED (impl->priv->browse_widgets_hpaned))); + gtk_paned_get_position (GTK_PANED (priv->browse_widgets_hpaned))); /* Now apply the settings */ g_settings_apply (settings); @@ -5378,24 +3318,120 @@ gtk_file_chooser_default_realize (GtkWidget *widget) emit_default_size_changed (impl); } +/* Changes the current folder to $CWD */ +static void +switch_to_cwd (GtkFileChooserDefault *impl) +{ + char *current_working_dir; + + current_working_dir = g_get_current_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), current_working_dir); + g_free (current_working_dir); +} + +/* Sets the file chooser to showing Recent Files or $CWD, depending on the + * user's settings. + */ +static void +set_startup_mode (GtkFileChooserDefault *impl) +{ + GtkFileChooserDefaultPrivate *priv = impl->priv; + + switch (priv->startup_mode) + { + case STARTUP_MODE_RECENT: + operation_mode_set (impl, OPERATION_MODE_RECENT); + break; + + case STARTUP_MODE_CWD: + switch_to_cwd (impl); + break; + + default: + g_assert_not_reached (); + } +} + +static gboolean +shortcut_exists (GtkFileChooserDefault *impl, GFile *needle) +{ + GtkFileChooserDefaultPrivate *priv = impl->priv; + GSList *haystack; + GSList *l; + gboolean exists; + + exists = FALSE; + + haystack = gtk_places_sidebar_list_shortcuts (GTK_PLACES_SIDEBAR (priv->places_sidebar)); + for (l = haystack; l; l = l->next) + { + GFile *hay; + + hay = G_FILE (l->data); + if (g_file_equal (hay, needle)) + { + exists = TRUE; + break; + } + } + g_slist_free_full (haystack, g_object_unref); + + return exists; +} + +static void +add_cwd_to_sidebar_if_needed (GtkFileChooserDefault *impl) +{ + GtkFileChooserDefaultPrivate *priv = impl->priv; + char *cwd; + GFile *cwd_file; + GFile *home_file; + + cwd = g_get_current_dir (); + cwd_file = g_file_new_for_path (cwd); + g_free (cwd); + + if (shortcut_exists (impl, cwd_file)) + goto out; + + home_file = g_file_new_for_path (g_get_home_dir ()); + + /* We only add an item for $CWD if it is different from $HOME. This way, + * applications which get launched from a shell in a terminal (by someone who + * knows what they are doing) will get an item for $CWD in the places sidebar, + * and "normal" applications launched from the desktop shell (whose $CWD is + * $HOME) won't get any extra clutter in the sidebar. + */ + if (!g_file_equal (home_file, cwd_file)) + gtk_places_sidebar_add_shortcut (GTK_PLACES_SIDEBAR (priv->places_sidebar), cwd_file); + + g_object_unref (home_file); + + out: + g_object_unref (cwd_file); +} + /* GtkWidget::map method */ static void gtk_file_chooser_default_map (GtkWidget *widget) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget); + GtkFileChooserDefaultPrivate *priv = impl->priv; profile_start ("start", NULL); - impl = GTK_FILE_CHOOSER_DEFAULT (widget); - GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget); - if (impl->priv->operation_mode == OPERATION_MODE_BROWSE) + settings_load (impl); + + add_cwd_to_sidebar_if_needed (impl); + + if (priv->operation_mode == OPERATION_MODE_BROWSE) { - switch (impl->priv->reload_state) + switch (priv->reload_state) { case RELOAD_EMPTY: - recent_shortcut_handler (impl); + set_startup_mode (impl); break; case RELOAD_HAS_FOLDER: @@ -5409,10 +3445,6 @@ gtk_file_chooser_default_map (GtkWidget *widget) } } - volumes_bookmarks_changed_cb (impl->priv->file_system, impl); - - settings_load (impl); - profile_end ("end", NULL); } @@ -5420,20 +3452,20 @@ gtk_file_chooser_default_map (GtkWidget *widget) static void gtk_file_chooser_default_unmap (GtkWidget *widget) { - GtkFileChooserDefault *impl; - - impl = GTK_FILE_CHOOSER_DEFAULT (widget); + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget); + GtkFileChooserDefaultPrivate *priv = impl->priv; settings_save (impl); cancel_all_operations (impl); - impl->priv->reload_state = RELOAD_EMPTY; + priv->reload_state = RELOAD_EMPTY; GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget); } #define COMPARE_DIRECTORIES \ GtkFileChooserDefault *impl = user_data; \ + GtkFileChooserDefaultPrivate *priv = impl->priv; \ GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model); \ gboolean dir_a, dir_b; \ \ @@ -5441,7 +3473,7 @@ gtk_file_chooser_default_unmap (GtkWidget *widget) dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_IS_FOLDER)); \ \ if (dir_a != dir_b) \ - return impl->priv->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */ + return priv->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */ /* Sort callback for the filename column */ static gint @@ -5517,14 +3549,15 @@ static void list_sort_column_changed_cb (GtkTreeSortable *sortable, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; gint sort_column_id; GtkSortType sort_type; if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type)) { - impl->priv->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING); - impl->priv->sort_column = sort_column_id; - impl->priv->sort_order = sort_type; + priv->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING); + priv->sort_column = sort_column_id; + priv->sort_order = sort_type; } } @@ -5560,20 +3593,22 @@ set_busy_cursor (GtkFileChooserDefault *impl, static void load_set_model (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + profile_start ("start", NULL); - g_assert (impl->priv->browse_files_model != NULL); + g_assert (priv->browse_files_model != NULL); profile_msg (" gtk_tree_view_set_model start", NULL); - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), - GTK_TREE_MODEL (impl->priv->browse_files_model)); - gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); - gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), + GTK_TREE_MODEL (priv->browse_files_model)); + gtk_tree_view_columns_autosize (GTK_TREE_VIEW (priv->browse_files_tree_view)); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view), MODEL_COL_NAME); file_list_set_sort_column_ids (impl); set_sort_column (impl); profile_msg (" gtk_tree_view_set_model end", NULL); - impl->priv->list_sort_ascending = TRUE; + priv->list_sort_ascending = TRUE; profile_end ("end", NULL); } @@ -5582,17 +3617,17 @@ load_set_model (GtkFileChooserDefault *impl) static gboolean load_timeout_cb (gpointer data) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data); + GtkFileChooserDefaultPrivate *priv = impl->priv; profile_start ("start", NULL); - impl = GTK_FILE_CHOOSER_DEFAULT (data); - g_assert (impl->priv->load_state == LOAD_PRELOAD); - g_assert (impl->priv->load_timeout_id != 0); - g_assert (impl->priv->browse_files_model != NULL); + g_assert (priv->load_state == LOAD_PRELOAD); + g_assert (priv->load_timeout_id != 0); + g_assert (priv->browse_files_model != NULL); - impl->priv->load_timeout_id = 0; - impl->priv->load_state = LOAD_LOADING; + priv->load_timeout_id = 0; + priv->load_state = LOAD_LOADING; load_set_model (impl); @@ -5605,44 +3640,49 @@ load_timeout_cb (gpointer data) static void load_setup_timer (GtkFileChooserDefault *impl) { - g_assert (impl->priv->load_timeout_id == 0); - g_assert (impl->priv->load_state != LOAD_PRELOAD); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + g_assert (priv->load_timeout_id == 0); + g_assert (priv->load_state != LOAD_PRELOAD); - impl->priv->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl); - impl->priv->load_state = LOAD_PRELOAD; + priv->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl); + priv->load_state = LOAD_PRELOAD; } -/* Removes the load timeout; changes the impl->priv->load_state to the specified value. */ +/* Removes the load timeout; changes the impl->load_state to the specified value. */ static void load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state) { - if (impl->priv->load_timeout_id != 0) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->load_timeout_id != 0) { - g_assert (impl->priv->load_state == LOAD_PRELOAD); + g_assert (priv->load_state == LOAD_PRELOAD); - g_source_remove (impl->priv->load_timeout_id); - impl->priv->load_timeout_id = 0; + g_source_remove (priv->load_timeout_id); + priv->load_timeout_id = 0; } else - g_assert (impl->priv->load_state == LOAD_EMPTY || - impl->priv->load_state == LOAD_LOADING || - impl->priv->load_state == LOAD_FINISHED); + g_assert (priv->load_state == LOAD_EMPTY || + priv->load_state == LOAD_LOADING || + priv->load_state == LOAD_FINISHED); g_assert (new_load_state == LOAD_EMPTY || new_load_state == LOAD_LOADING || new_load_state == LOAD_FINISHED); - impl->priv->load_state = new_load_state; + priv->load_state = new_load_state; } /* Selects the first row in the file list */ static void browse_files_select_first_row (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreePath *path; GtkTreeIter dummy_iter; GtkTreeModel *tree_model; - tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)); if (!tree_model) return; @@ -5651,7 +3691,7 @@ browse_files_select_first_row (GtkFileChooserDefault *impl) /* If the list is empty, do nothing. */ if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path)) - gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), path, NULL, FALSE); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), path, NULL, FALSE); gtk_tree_path_free (path); } @@ -5684,13 +3724,14 @@ center_selected_row_foreach_cb (GtkTreeModel *model, static void browse_files_center_selected_row (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *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 (impl->priv->browse_files_tree_view)); + 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); } @@ -5698,22 +3739,23 @@ static gboolean show_and_select_files (GtkFileChooserDefault *impl, GSList *files) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; GtkFileSystemModel *fsmodel; gboolean enabled_hidden, removed_filters; gboolean selected_a_file; GSList *walk; - g_assert (impl->priv->load_state == LOAD_FINISHED); - g_assert (impl->priv->browse_files_model != NULL); + g_assert (priv->load_state == LOAD_FINISHED); + g_assert (priv->browse_files_model != NULL); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); - fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view))); + 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))); - g_assert (fsmodel == impl->priv->browse_files_model); + g_assert (fsmodel == priv->browse_files_model); - enabled_hidden = impl->priv->show_hidden; - removed_filters = (impl->priv->current_filter == NULL); + enabled_hidden = priv->show_hidden; + removed_filters = (priv->current_filter == NULL); selected_a_file = FALSE; @@ -5767,7 +3809,7 @@ show_and_select_files (GtkFileChooserDefault *impl, gtk_tree_selection_select_iter (selection, &iter); path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsmodel), &iter); - gtk_tree_view_set_cursor (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), + gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), path, NULL, FALSE); gtk_tree_path_free (path); @@ -5784,12 +3826,14 @@ show_and_select_files (GtkFileChooserDefault *impl, static void pending_select_files_process (GtkFileChooserDefault *impl) { - g_assert (impl->priv->load_state == LOAD_FINISHED); - g_assert (impl->priv->browse_files_model != NULL); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + g_assert (priv->load_state == LOAD_FINISHED); + g_assert (priv->browse_files_model != NULL); - if (impl->priv->pending_select_files) + if (priv->pending_select_files) { - show_and_select_files (impl, impl->priv->pending_select_files); + show_and_select_files (impl, priv->pending_select_files); pending_select_files_free (impl); browse_files_center_selected_row (impl); } @@ -5802,21 +3846,22 @@ pending_select_files_process (GtkFileChooserDefault *impl) * that case, the chooser's selection should be what the caller expects, * as the user can't see that something else got selected. See bug #165264. */ - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN && + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN && gtk_widget_get_mapped (GTK_WIDGET (impl))) browse_files_select_first_row (impl); } - g_assert (impl->priv->pending_select_files == NULL); + g_assert (priv->pending_select_files == NULL); } static void show_error_on_reading_current_folder (GtkFileChooserDefault *impl, GError *error) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GFileInfo *info; char *msg; - info = g_file_query_info (impl->priv->current_folder, + info = g_file_query_info (priv->current_folder, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NONE, NULL, @@ -5839,17 +3884,19 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel *model, GError *error, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + profile_start ("start", NULL); if (error) show_error_on_reading_current_folder (impl, error); - if (impl->priv->load_state == LOAD_PRELOAD) + if (priv->load_state == LOAD_PRELOAD) { load_remove_timer (impl, LOAD_FINISHED); load_set_model (impl); } - else if (impl->priv->load_state == LOAD_LOADING) + else if (priv->load_state == LOAD_LOADING) { /* Nothing */ } @@ -5862,9 +3909,9 @@ browse_files_model_finished_loading_cb (GtkFileSystemModel *model, return; } - g_assert (impl->priv->load_timeout_id == 0); + g_assert (priv->load_timeout_id == 0); - impl->priv->load_state = LOAD_FINISHED; + priv->load_state = LOAD_FINISHED; pending_select_files_process (impl); set_busy_cursor (impl, FALSE); @@ -5879,16 +3926,18 @@ static void stop_loading_and_clear_list_model (GtkFileChooserDefault *impl, gboolean remove_from_treeview) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + load_remove_timer (impl, LOAD_EMPTY); - if (impl->priv->browse_files_model) + if (priv->browse_files_model) { - g_object_unref (impl->priv->browse_files_model); - impl->priv->browse_files_model = NULL; + g_object_unref (priv->browse_files_model); + priv->browse_files_model = NULL; } if (remove_from_treeview) - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), NULL); + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL); } static char * @@ -6049,6 +4098,7 @@ file_system_model_set (GtkFileSystemModel *model, gpointer data) { GtkFileChooserDefault *impl = data; + GtkFileChooserDefaultPrivate *priv = impl->priv; switch (column) { @@ -6075,8 +4125,8 @@ file_system_model_set (GtkFileSystemModel *model, { gboolean sensitive = TRUE; - if (!(impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) + if (!(priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) { sensitive = TRUE; /* for file modes... */ } @@ -6103,7 +4153,7 @@ file_system_model_set (GtkFileSystemModel *model, { if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON)) { - g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->priv->icon_size)); + g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), priv->icon_size)); } else { @@ -6111,11 +4161,11 @@ file_system_model_set (GtkFileSystemModel *model, GtkTreePath *path, *start, *end; GtkTreeIter iter; - if (impl->priv->browse_files_tree_view == NULL || + if (priv->browse_files_tree_view == NULL || g_file_info_has_attribute (info, "filechooser::queried")) return FALSE; - tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)); if (tree_model != GTK_TREE_MODEL (model)) return FALSE; @@ -6123,7 +4173,7 @@ file_system_model_set (GtkFileSystemModel *model, &iter, file)) g_assert_not_reached (); - if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), &start, &end)) + if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (priv->browse_files_tree_view), &start, &end)) return FALSE; path = gtk_tree_model_get_path (tree_model, &iter); if (gtk_tree_path_compare (start, path) != 1 && @@ -6189,7 +4239,9 @@ static gboolean set_list_model (GtkFileChooserDefault *impl, GError **error) { - g_assert (impl->priv->current_folder != NULL); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + g_assert (priv->current_folder != NULL); profile_start ("start", NULL); @@ -6197,31 +4249,31 @@ set_list_model (GtkFileChooserDefault *impl, set_busy_cursor (impl, TRUE); - impl->priv->browse_files_model = - _gtk_file_system_model_new_for_directory (impl->priv->current_folder, + priv->browse_files_model = + _gtk_file_system_model_new_for_directory (priv->current_folder, MODEL_ATTRIBUTES, file_system_model_set, impl, MODEL_COLUMN_TYPES); - _gtk_file_system_model_set_show_hidden (impl->priv->browse_files_model, impl->priv->show_hidden); + _gtk_file_system_model_set_show_hidden (priv->browse_files_model, priv->show_hidden); profile_msg (" set sort function", NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->browse_files_model), MODEL_COL_NAME, name_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL); - gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->priv->browse_files_model), NULL, NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_NAME, name_sort_func, impl, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), NULL, NULL, NULL); set_sort_column (impl); - impl->priv->list_sort_ascending = TRUE; - g_signal_connect (impl->priv->browse_files_model, "sort-column-changed", + priv->list_sort_ascending = TRUE; + g_signal_connect (priv->browse_files_model, "sort-column-changed", G_CALLBACK (list_sort_column_changed_cb), impl); load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */ - g_signal_connect (impl->priv->browse_files_model, "finished-loading", + g_signal_connect (priv->browse_files_model, "finished-loading", G_CALLBACK (browse_files_model_finished_loading_cb), impl); - _gtk_file_system_model_set_filter (impl->priv->browse_files_model, impl->priv->current_filter); + _gtk_file_system_model_set_filter (priv->browse_files_model, priv->current_filter); profile_end ("end", NULL); @@ -6269,42 +4321,43 @@ update_chooser_entry_selected_foreach (GtkTreeModel *model, static void update_chooser_entry (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *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 */ - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH || - !impl->priv->location_entry) + if (priv->operation_mode == OPERATION_MODE_SEARCH || + !priv->location_entry) return; - if (!(impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER - || ((impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) - && impl->priv->location_mode == LOCATION_MODE_FILENAME_ENTRY))) + if (!(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER + || ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN + || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + && priv->location_mode == LOCATION_MODE_FILENAME_ENTRY))) return; - g_assert (impl->priv->location_entry != NULL); + g_assert (priv->location_entry != NULL); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + 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); if (closure.num_selected == 0) { - if (impl->priv->operation_mode == OPERATION_MODE_RECENT) - _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), NULL); + if (priv->operation_mode == OPERATION_MODE_RECENT) + _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), NULL); else goto maybe_clear_entry; } else if (closure.num_selected == 1) { - if (impl->priv->operation_mode == OPERATION_MODE_BROWSE) + if (priv->operation_mode == OPERATION_MODE_BROWSE) { GFileInfo *info; gboolean change_entry; - info = _gtk_file_system_model_get_info (impl->priv->browse_files_model, &closure.first_selected_iter); + info = _gtk_file_system_model_get_info (priv->browse_files_model, &closure.first_selected_iter); /* If the cursor moved to the row of the newly created folder, * retrieving info will return NULL. @@ -6312,13 +4365,13 @@ update_chooser_entry (GtkFileChooserDefault *impl) if (!info) return; - g_free (impl->priv->browse_files_last_selected_name); - impl->priv->browse_files_last_selected_name = + g_free (priv->browse_files_last_selected_name); + priv->browse_files_last_selected_name = g_strdup (g_file_info_get_display_name (info)); - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { /* Don't change the name when clicking on a folder... */ change_entry = (! _gtk_file_info_consider_as_directory (info)); @@ -6328,52 +4381,52 @@ update_chooser_entry (GtkFileChooserDefault *impl) if (change_entry) { - gtk_entry_set_text (GTK_ENTRY (impl->priv->location_entry), impl->priv->browse_files_last_selected_name); + gtk_entry_set_text (GTK_ENTRY (priv->location_entry), priv->browse_files_last_selected_name); - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) - _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry)); + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (priv->location_entry)); } return; } - else if (impl->priv->operation_mode == OPERATION_MODE_RECENT - && impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + else if (priv->operation_mode == OPERATION_MODE_RECENT + && priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { GFile *folder; /* Set the base folder on the name entry, so it will do completion relative to the correct recent-folder */ - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->recent_model), &closure.first_selected_iter, + gtk_tree_model_get (GTK_TREE_MODEL (priv->recent_model), &closure.first_selected_iter, MODEL_COL_FILE, &folder, -1); - _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), folder); + _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), folder); g_object_unref (folder); return; } } else { - g_assert (!(impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)); + g_assert (!(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)); /* Multiple selection, so just clear the entry. */ - g_free (impl->priv->browse_files_last_selected_name); - impl->priv->browse_files_last_selected_name = NULL; + g_free (priv->browse_files_last_selected_name); + priv->browse_files_last_selected_name = NULL; - gtk_entry_set_text (GTK_ENTRY (impl->priv->location_entry), ""); + gtk_entry_set_text (GTK_ENTRY (priv->location_entry), ""); return; } maybe_clear_entry: - if ((impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) - && impl->priv->browse_files_last_selected_name) + if ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + && priv->browse_files_last_selected_name) { const char *entry_text; int len; gboolean clear_entry; - entry_text = gtk_entry_get_text (GTK_ENTRY (impl->priv->location_entry)); + entry_text = gtk_entry_get_text (GTK_ENTRY (priv->location_entry)); len = strlen (entry_text); if (len != 0) { @@ -6385,17 +4438,17 @@ update_chooser_entry (GtkFileChooserDefault *impl) gchar *tmp; tmp = g_strndup (entry_text, len - 1); - clear_entry = (compare_utf8_filenames (impl->priv->browse_files_last_selected_name, tmp) == 0); + clear_entry = (compare_utf8_filenames (priv->browse_files_last_selected_name, tmp) == 0); g_free (tmp); } else - clear_entry = (compare_utf8_filenames (impl->priv->browse_files_last_selected_name, entry_text) == 0); + clear_entry = (compare_utf8_filenames (priv->browse_files_last_selected_name, entry_text) == 0); } else clear_entry = FALSE; if (clear_entry) - gtk_entry_set_text (GTK_ENTRY (impl->priv->location_entry), ""); + gtk_entry_set_text (GTK_ENTRY (priv->location_entry), ""); } } @@ -6424,14 +4477,15 @@ update_current_folder_mount_enclosing_volume_cb (GCancellable *cancellabl const GError *error, gpointer user_data) { - gboolean cancelled = g_cancellable_is_cancelled (cancellable); struct UpdateCurrentFolderData *data = user_data; GtkFileChooserDefault *impl = data->impl; + GtkFileChooserDefaultPrivate *priv = impl->priv; + gboolean cancelled = g_cancellable_is_cancelled (cancellable); - if (cancellable != impl->priv->update_current_folder_cancellable) + if (cancellable != priv->update_current_folder_cancellable) goto out; - impl->priv->update_current_folder_cancellable = NULL; + priv->update_current_folder_cancellable = NULL; set_busy_cursor (impl, FALSE); if (cancelled) @@ -6440,7 +4494,7 @@ update_current_folder_mount_enclosing_volume_cb (GCancellable *cancellabl if (error) { error_changing_folder_dialog (data->impl, data->file, g_error_copy (error)); - impl->priv->reload_state = RELOAD_EMPTY; + priv->reload_state = RELOAD_EMPTY; goto out; } @@ -6462,12 +4516,13 @@ update_current_folder_get_info_cb (GCancellable *cancellable, gboolean cancelled = g_cancellable_is_cancelled (cancellable); struct UpdateCurrentFolderData *data = user_data; GtkFileChooserDefault *impl = data->impl; + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (cancellable != impl->priv->update_current_folder_cancellable) + if (cancellable != priv->update_current_folder_cancellable) goto out; - impl->priv->update_current_folder_cancellable = NULL; - impl->priv->reload_state = RELOAD_EMPTY; + priv->update_current_folder_cancellable = NULL; + priv->reload_state = RELOAD_EMPTY; set_busy_cursor (impl, FALSE); @@ -6490,8 +4545,8 @@ update_current_folder_get_info_cb (GCancellable *cancellable, set_busy_cursor (impl, TRUE); - impl->priv->update_current_folder_cancellable = - _gtk_file_system_mount_enclosing_volume (impl->priv->file_system, data->file, + priv->update_current_folder_cancellable = + _gtk_file_system_mount_enclosing_volume (priv->file_system, data->file, mount_operation, update_current_folder_mount_enclosing_volume_cb, data); @@ -6516,10 +4571,10 @@ update_current_folder_get_info_cb (GCancellable *cancellable, g_object_unref (cancellable); /* restart the update current folder operation */ - impl->priv->reload_state = RELOAD_HAS_FOLDER; + priv->reload_state = RELOAD_HAS_FOLDER; - impl->priv->update_current_folder_cancellable = - _gtk_file_system_get_info (impl->priv->file_system, data->file, + priv->update_current_folder_cancellable = + _gtk_file_system_get_info (priv->file_system, data->file, "standard::type", update_current_folder_get_info_cb, data); @@ -6560,38 +4615,27 @@ update_current_folder_get_info_cb (GCancellable *cancellable, if (! _gtk_file_info_consider_as_directory (info)) goto out; - _gtk_path_bar_set_file (GTK_PATH_BAR (impl->priv->browse_path_bar), data->file, data->keep_trail); + _gtk_path_bar_set_file (GTK_PATH_BAR (priv->browse_path_bar), data->file, data->keep_trail); - if (impl->priv->current_folder != data->file) + if (priv->current_folder != data->file) { - if (impl->priv->current_folder) - g_object_unref (impl->priv->current_folder); + if (priv->current_folder) + g_object_unref (priv->current_folder); - impl->priv->current_folder = g_object_ref (data->file); + priv->current_folder = g_object_ref (data->file); } - impl->priv->reload_state = RELOAD_HAS_FOLDER; - - /* Update the widgets that may trigger a folder change themselves. */ - - if (!impl->priv->changing_folder) - { - impl->priv->changing_folder = TRUE; - - shortcuts_update_current_folder (impl); - - impl->priv->changing_folder = FALSE; - } + priv->reload_state = RELOAD_HAS_FOLDER; /* Set the folder on the save entry */ - if (impl->priv->location_entry) + if (priv->location_entry) { - _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry), - impl->priv->current_folder); + _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), + priv->current_folder); if (data->clear_entry) - gtk_entry_set_text (GTK_ENTRY (impl->priv->location_entry), ""); + gtk_entry_set_text (GTK_ENTRY (priv->location_entry), ""); } /* Create a new list model. This is slightly evil; we store the result value @@ -6602,12 +4646,11 @@ update_current_folder_get_info_cb (GCancellable *cancellable, /* Refresh controls */ - shortcuts_find_current_folder (impl); + gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (priv->places_sidebar), priv->current_folder); g_signal_emit_by_name (impl, "current-folder-changed", 0); check_preview_change (impl); - bookmarks_check_add_sensitivity (impl); g_signal_emit_by_name (impl, "selection-changed", 0); @@ -6626,6 +4669,7 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser, GError **error) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; struct UpdateCurrentFolderData *data; profile_start ("start", NULL); @@ -6634,7 +4678,7 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser, operation_mode_set (impl, OPERATION_MODE_BROWSE); - if (impl->priv->local_only && !_gtk_file_has_native_path (file)) + if (priv->local_only && !_gtk_file_has_native_path (file)) { g_set_error_literal (error, GTK_FILE_CHOOSER_ERROR, @@ -6646,8 +4690,8 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser, return FALSE; } - if (impl->priv->update_current_folder_cancellable) - g_cancellable_cancel (impl->priv->update_current_folder_cancellable); + if (priv->update_current_folder_cancellable) + g_cancellable_cancel (priv->update_current_folder_cancellable); /* Test validity of path here. */ data = g_new0 (struct UpdateCurrentFolderData, 1); @@ -6656,10 +4700,10 @@ gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser, data->keep_trail = keep_trail; data->clear_entry = clear_entry; - impl->priv->reload_state = RELOAD_HAS_FOLDER; + priv->reload_state = RELOAD_HAS_FOLDER; - impl->priv->update_current_folder_cancellable = - _gtk_file_system_get_info (impl->priv->file_system, file, + priv->update_current_folder_cancellable = + _gtk_file_system_get_info (priv->file_system, file, "standard::type", update_current_folder_get_info_cb, data); @@ -6675,13 +4719,14 @@ static GFile * gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH || - impl->priv->operation_mode == OPERATION_MODE_RECENT) + if (priv->operation_mode == OPERATION_MODE_SEARCH || + priv->operation_mode == OPERATION_MODE_RECENT) return NULL; - if (impl->priv->current_folder) - return g_object_ref (impl->priv->current_folder); + if (priv->current_folder) + return g_object_ref (priv->current_folder); return NULL; } @@ -6691,12 +4736,13 @@ gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser, const gchar *name) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; - g_return_if_fail (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER); + g_return_if_fail (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER); pending_select_files_free (impl); - gtk_entry_set_text (GTK_ENTRY (impl->priv->location_entry), name); + gtk_entry_set_text (GTK_ENTRY (priv->location_entry), name); } static gboolean @@ -6705,6 +4751,7 @@ gtk_file_chooser_default_select_file (GtkFileChooser *chooser, GError **error) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; GFile *parent_file; gboolean same_path; @@ -6713,20 +4760,20 @@ gtk_file_chooser_default_select_file (GtkFileChooser *chooser, if (!parent_file) return gtk_file_chooser_set_current_folder_file (chooser, file, error); - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH || - impl->priv->operation_mode == OPERATION_MODE_RECENT || - impl->priv->load_state == LOAD_EMPTY) + if (priv->operation_mode == OPERATION_MODE_SEARCH || + priv->operation_mode == OPERATION_MODE_RECENT || + priv->load_state == LOAD_EMPTY) { same_path = FALSE; } else { - g_assert (impl->priv->current_folder != NULL); + g_assert (priv->current_folder != NULL); - same_path = g_file_equal (parent_file, impl->priv->current_folder); + same_path = g_file_equal (parent_file, priv->current_folder); } - if (same_path && impl->priv->load_state == LOAD_FINISHED) + if (same_path && priv->load_state == LOAD_FINISHED) { gboolean result; GSList files; @@ -6759,13 +4806,14 @@ gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser, GFile *file) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); - GtkTreeView *tree_view = GTK_TREE_VIEW (impl->priv->browse_files_tree_view); + GtkFileChooserDefaultPrivate *priv = impl->priv; + GtkTreeView *tree_view = GTK_TREE_VIEW (priv->browse_files_tree_view); GtkTreeIter iter; - if (!impl->priv->browse_files_model) + if (!priv->browse_files_model) return; - if (!_gtk_file_system_model_get_iter_for_file (impl->priv->browse_files_model, + if (!_gtk_file_system_model_get_iter_for_file (priv->browse_files_model, &iter, file)) return; @@ -6781,11 +4829,12 @@ maybe_select (GtkTreeModel *model, gpointer data) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data); + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; gboolean is_sensitive; gboolean is_folder; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + 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, @@ -6793,8 +4842,8 @@ maybe_select (GtkTreeModel *model, -1); if (is_sensitive && - ((is_folder && impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) || - (!is_folder && impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN))) + ((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); else gtk_tree_selection_unselect_iter (selection, iter); @@ -6806,19 +4855,20 @@ static void gtk_file_chooser_default_select_all (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH || - impl->priv->operation_mode == OPERATION_MODE_RECENT) + if (priv->operation_mode == OPERATION_MODE_SEARCH || + priv->operation_mode == OPERATION_MODE_RECENT) { GtkTreeSelection *selection; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); gtk_tree_selection_select_all (selection); return; } - if (impl->priv->select_multiple) - gtk_tree_model_foreach (GTK_TREE_MODEL (impl->priv->browse_files_model), + if (priv->select_multiple) + gtk_tree_model_foreach (GTK_TREE_MODEL (priv->browse_files_model), maybe_select, impl); } @@ -6826,7 +4876,8 @@ static void gtk_file_chooser_default_unselect_all (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); - GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + GtkFileChooserDefaultPrivate *priv = impl->priv; + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); gtk_tree_selection_unselect_all (selection); pending_select_files_free (impl); @@ -6849,19 +4900,20 @@ check_save_entry (GtkFileChooserDefault *impl, gboolean *is_file_part_empty_ret, gboolean *is_folder) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkFileChooserEntry *chooser_entry; GFile *current_folder; const char *file_part; GFile *file; GError *error; - g_assert (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER - || ((impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) - && impl->priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)); + g_assert (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER + || ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN + || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + && priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)); - chooser_entry = GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry); + chooser_entry = GTK_FILE_CHOOSER_ENTRY (priv->location_entry); if (strlen (gtk_entry_get_text (GTK_ENTRY (chooser_entry))) == 0) { @@ -6951,6 +5003,7 @@ static GSList * gtk_file_chooser_default_get_files (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; struct get_files_closure info; GtkWindow *toplevel; GtkWidget *current_focus; @@ -6960,12 +5013,12 @@ gtk_file_chooser_default_get_files (GtkFileChooser *chooser) info.result = NULL; info.file_from_entry = NULL; - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH) + if (priv->operation_mode == OPERATION_MODE_SEARCH) return search_get_selected_files (impl); - if (impl->priv->operation_mode == OPERATION_MODE_RECENT) + if (priv->operation_mode == OPERATION_MODE_RECENT) { - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { file_list_seen = TRUE; goto file_entry; @@ -6981,14 +5034,14 @@ gtk_file_chooser_default_get_files (GtkFileChooser *chooser) current_focus = NULL; file_list_seen = FALSE; - if (current_focus == impl->priv->browse_files_tree_view) + if (current_focus == priv->browse_files_tree_view) { GtkTreeSelection *selection; file_list: file_list_seen = TRUE; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); gtk_tree_selection_selected_foreach (selection, get_files_foreach, &info); /* If there is no selection in the file list, we probably have this situation: @@ -6999,10 +5052,10 @@ gtk_file_chooser_default_get_files (GtkFileChooser *chooser) * So we want the selection to be "bar/foo.txt". Jump to the case for the * filename entry to see if that is the case. */ - if (info.result == NULL && impl->priv->location_entry) + if (info.result == NULL && priv->location_entry) goto file_entry; } - else if (impl->priv->location_entry && current_focus == impl->priv->location_entry) + else if (priv->location_entry && current_focus == priv->location_entry) { gboolean is_well_formed, is_empty, is_file_part_empty, is_folder; @@ -7016,7 +5069,7 @@ gtk_file_chooser_default_get_files (GtkFileChooser *chooser) if (!is_well_formed) return NULL; - if (is_file_part_empty && impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (is_file_part_empty && priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { g_object_unref (info.file_from_entry); return NULL; @@ -7029,15 +5082,15 @@ gtk_file_chooser_default_get_files (GtkFileChooser *chooser) else return NULL; } - else if (impl->priv->toplevel_last_focus_widget == impl->priv->browse_files_tree_view) + else if (priv->toplevel_last_focus_widget == priv->browse_files_tree_view) goto file_list; - else if (impl->priv->location_entry && impl->priv->toplevel_last_focus_widget == impl->priv->location_entry) + else if (priv->location_entry && priv->toplevel_last_focus_widget == priv->location_entry) goto file_entry; else { /* The focus is on a dialog's action area button or something else */ - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) goto file_entry; else goto file_list; @@ -7047,7 +5100,7 @@ gtk_file_chooser_default_get_files (GtkFileChooser *chooser) /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we * fall back to the current directory */ - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && + if (priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && info.result == NULL) { GFile *current_folder; @@ -7065,9 +5118,10 @@ GFile * gtk_file_chooser_default_get_preview_file (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (impl->priv->preview_file) - return g_object_ref (impl->priv->preview_file); + if (priv->preview_file) + return g_object_ref (priv->preview_file); else return NULL; } @@ -7076,8 +5130,9 @@ static GtkFileSystem * gtk_file_chooser_default_get_file_system (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; - return impl->priv->file_system; + return priv->file_system; } /* Shows or hides the filter widgets */ @@ -7085,10 +5140,12 @@ static void show_filters (GtkFileChooserDefault *impl, gboolean show) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + if (show) - gtk_widget_show (impl->priv->filter_combo_hbox); + gtk_widget_show (priv->filter_combo_hbox); else - gtk_widget_hide (impl->priv->filter_combo_hbox); + gtk_widget_hide (priv->filter_combo_hbox); } static void @@ -7096,24 +5153,25 @@ gtk_file_chooser_default_add_filter (GtkFileChooser *chooser, GtkFileFilter *filter) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; const gchar *name; - if (g_slist_find (impl->priv->filters, filter)) + if (g_slist_find (priv->filters, filter)) { g_warning ("gtk_file_chooser_add_filter() called on filter already in list\n"); return; } g_object_ref_sink (filter); - impl->priv->filters = g_slist_append (impl->priv->filters, filter); + priv->filters = g_slist_append (priv->filters, filter); name = gtk_file_filter_get_name (filter); if (!name) name = "Untitled filter"; /* Place-holder, doesn't need to be marked for translation */ - gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (impl->priv->filter_combo), name); + gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (priv->filter_combo), name); - if (!g_slist_find (impl->priv->filters, impl->priv->current_filter)) + if (!g_slist_find (priv->filters, priv->current_filter)) set_current_filter (impl, filter); show_filters (impl, TRUE); @@ -7124,11 +5182,12 @@ gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser, GtkFileFilter *filter) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeModel *model; GtkTreeIter iter; gint filter_index; - filter_index = g_slist_index (impl->priv->filters, filter); + filter_index = g_slist_index (priv->filters, filter); if (filter_index < 0) { @@ -7136,18 +5195,18 @@ gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser, return; } - impl->priv->filters = g_slist_remove (impl->priv->filters, filter); + priv->filters = g_slist_remove (priv->filters, filter); - if (filter == impl->priv->current_filter) + if (filter == priv->current_filter) { - if (impl->priv->filters) - set_current_filter (impl, impl->priv->filters->data); + if (priv->filters) + set_current_filter (impl, priv->filters->data); else set_current_filter (impl, NULL); } /* Remove row from the combo box */ - model = gtk_combo_box_get_model (GTK_COMBO_BOX (impl->priv->filter_combo)); + model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->filter_combo)); if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, filter_index)) g_assert_not_reached (); @@ -7155,7 +5214,7 @@ gtk_file_chooser_default_remove_filter (GtkFileChooser *chooser, g_object_unref (filter); - if (!impl->priv->filters) + if (!priv->filters) show_filters (impl, FALSE); } @@ -7163,55 +5222,9 @@ static GSList * gtk_file_chooser_default_list_filters (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkFileChooserDefaultPrivate *priv = impl->priv; - return g_slist_copy (impl->priv->filters); -} - -/* Returns the position in the shortcuts tree where the nth specified shortcut would appear */ -static int -shortcuts_get_pos_for_shortcut_folder (GtkFileChooserDefault *impl, - int pos) -{ - return pos + shortcuts_get_index (impl, SHORTCUTS_SHORTCUTS); -} - -struct AddShortcutData -{ - GtkFileChooserDefault *impl; - GFile *file; -}; - -static void -add_shortcut_get_info_cb (GCancellable *cancellable, - GFileInfo *info, - const GError *error, - gpointer user_data) -{ - int pos; - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - struct AddShortcutData *data = user_data; - - if (!g_slist_find (data->impl->priv->loading_shortcuts, cancellable)) - goto out; - - data->impl->priv->loading_shortcuts = g_slist_remove (data->impl->priv->loading_shortcuts, cancellable); - - if (cancelled || error || (! _gtk_file_info_consider_as_directory (info))) - goto out; - - pos = shortcuts_get_pos_for_shortcut_folder (data->impl, data->impl->priv->num_shortcuts); - - shortcuts_insert_file (data->impl, pos, SHORTCUT_TYPE_FILE, NULL, data->file, NULL, FALSE, SHORTCUTS_SHORTCUTS); - - /* need to call shortcuts_add_bookmarks to flush out any duplicates bug #577806 */ - shortcuts_add_bookmarks (data->impl); - -out: - g_object_unref (data->impl); - g_object_unref (data->file); - g_free (data); - - g_object_unref (cancellable); + return g_slist_copy (priv->filters); } static gboolean @@ -7219,66 +5232,10 @@ gtk_file_chooser_default_add_shortcut_folder (GtkFileChooser *chooser, GFile *file, GError **error) { - GCancellable *cancellable; GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); - struct AddShortcutData *data; - GSList *l; - int pos; - - /* Avoid adding duplicates */ - pos = shortcut_find_position (impl, file); - if (pos >= 0 && pos < shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS_SEPARATOR)) - { - gchar *uri; - - uri = g_file_get_uri (file); - /* translators, "Shortcut" means "Bookmark" here */ - g_set_error (error, - GTK_FILE_CHOOSER_ERROR, - GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS, - _("Shortcut %s already exists"), - uri); - g_free (uri); - - return FALSE; - } - - for (l = impl->priv->loading_shortcuts; l; l = l->next) - { - GCancellable *c = l->data; - GFile *f; - - f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key"); - if (f && g_file_equal (file, f)) - { - gchar *uri; - - uri = g_file_get_uri (file); - g_set_error (error, - GTK_FILE_CHOOSER_ERROR, - GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS, - _("Shortcut %s already exists"), - uri); - g_free (uri); - - return FALSE; - } - } - - data = g_new0 (struct AddShortcutData, 1); - data->impl = g_object_ref (impl); - data->file = g_object_ref (file); - - cancellable = _gtk_file_system_get_info (impl->priv->file_system, file, - "standard::type", - add_shortcut_get_info_cb, data); - - if (!cancellable) - return FALSE; - - impl->priv->loading_shortcuts = g_slist_append (impl->priv->loading_shortcuts, cancellable); - g_object_set_data (G_OBJECT (cancellable), "add-shortcut-path-key", data->file); + GtkFileChooserDefaultPrivate *priv = impl->priv; + gtk_places_sidebar_add_shortcut (GTK_PLACES_SIDEBAR (priv->places_sidebar), file); return TRUE; } @@ -7288,114 +5245,19 @@ gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser *chooser, GError **error) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); - int pos; - GtkTreeIter iter; - GSList *l; - char *uri; - int i; - - for (l = impl->priv->loading_shortcuts; l; l = l->next) - { - GCancellable *c = l->data; - GFile *f; - - f = g_object_get_data (G_OBJECT (c), "add-shortcut-path-key"); - if (f && g_file_equal (file, f)) - { - impl->priv->loading_shortcuts = g_slist_remove (impl->priv->loading_shortcuts, c); - g_cancellable_cancel (c); - return TRUE; - } - } - - if (impl->priv->num_shortcuts == 0) - goto out; - - pos = shortcuts_get_pos_for_shortcut_folder (impl, 0); - if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, NULL, pos)) - g_assert_not_reached (); - - for (i = 0; i < impl->priv->num_shortcuts; i++) - { - gpointer col_data; - ShortcutType shortcut_type; - GFile *shortcut; - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - -1); - g_assert (col_data != NULL); - g_assert (shortcut_type == SHORTCUT_TYPE_FILE); - - shortcut = col_data; - if (g_file_equal (shortcut, file)) - { - shortcuts_remove_rows (impl, pos + i, 1); - impl->priv->num_shortcuts--; - return TRUE; - } - - if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter)) - g_assert_not_reached (); - } - - out: + GtkFileChooserDefaultPrivate *priv = impl->priv; - uri = g_file_get_uri (file); - /* translators, "Shortcut" means "Bookmark" here */ - g_set_error (error, - GTK_FILE_CHOOSER_ERROR, - GTK_FILE_CHOOSER_ERROR_NONEXISTENT, - _("Shortcut %s does not exist"), - uri); - g_free (uri); - - return FALSE; + gtk_places_sidebar_remove_shortcut (GTK_PLACES_SIDEBAR (priv->places_sidebar), file); + return TRUE; } static GSList * gtk_file_chooser_default_list_shortcut_folders (GtkFileChooser *chooser) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); - int pos; - GtkTreeIter iter; - int i; - GSList *list; - - if (impl->priv->num_shortcuts == 0) - return NULL; - - pos = shortcuts_get_pos_for_shortcut_folder (impl, 0); - if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, NULL, pos)) - g_assert_not_reached (); - - list = NULL; - - for (i = 0; i < impl->priv->num_shortcuts; i++) - { - gpointer col_data; - ShortcutType shortcut_type; - GFile *shortcut; + GtkFileChooserDefaultPrivate *priv = impl->priv; - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - -1); - g_assert (col_data != NULL); - g_assert (shortcut_type == SHORTCUT_TYPE_FILE); - - shortcut = col_data; - list = g_slist_prepend (list, g_object_ref (shortcut)); - - if (i != impl->priv->num_shortcuts - 1) - { - if (!gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter)) - g_assert_not_reached (); - } - } - - return g_slist_reverse (list); + return gtk_places_sidebar_list_shortcuts (GTK_PLACES_SIDEBAR (priv->places_sidebar)); } /* Guesses a size based upon font sizes */ @@ -7435,13 +5297,12 @@ gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed, gint *default_width, gint *default_height) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed); + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkRequisition req; int x, y, width, height; GSettings *settings; - impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed); - settings = _gtk_file_chooser_get_settings_for_widget (GTK_WIDGET (impl)); g_settings_get (settings, SETTINGS_KEY_WINDOW_POSITION, "(ii)", &x, &y); @@ -7456,19 +5317,19 @@ gtk_file_chooser_default_get_default_size (GtkFileChooserEmbed *chooser_embed, find_good_size_from_style (GTK_WIDGET (chooser_embed), default_width, default_height); - if (impl->priv->preview_widget_active && - impl->priv->preview_widget && - gtk_widget_get_visible (impl->priv->preview_widget)) + if (priv->preview_widget_active && + priv->preview_widget && + gtk_widget_get_visible (priv->preview_widget)) { - gtk_widget_get_preferred_size (impl->priv->preview_box, + gtk_widget_get_preferred_size (priv->preview_box, &req, NULL); *default_width += PREVIEW_HBOX_SPACING + req.width; } - if (impl->priv->extra_widget && - gtk_widget_get_visible (impl->priv->extra_widget)) + if (priv->extra_widget && + gtk_widget_get_visible (priv->extra_widget)) { - gtk_widget_get_preferred_size (impl->priv->extra_align, + gtk_widget_get_preferred_size (priv->extra_align, &req, NULL); *default_height += gtk_box_get_spacing (GTK_BOX (chooser_embed)) + req.height; } @@ -7499,6 +5360,7 @@ switch_folder_foreach_cb (GtkTreeModel *model, static void switch_to_selected_folder (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; struct switch_folder_closure closure; @@ -7510,7 +5372,7 @@ switch_to_selected_folder (GtkFileChooserDefault *impl) closure.file = NULL; closure.num_selected = 0; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + 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); g_assert (closure.file && closure.num_selected == 1); @@ -7525,12 +5387,13 @@ static GFileInfo * get_selected_file_info_from_file_list (GtkFileChooserDefault *impl, gboolean *had_selection) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; GtkTreeIter iter; GFileInfo *info; - g_assert (!impl->priv->select_multiple); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + 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, NULL, &iter)) { *had_selection = FALSE; @@ -7539,7 +5402,7 @@ get_selected_file_info_from_file_list (GtkFileChooserDefault *impl, *had_selection = TRUE; - info = _gtk_file_system_model_get_info (impl->priv->browse_files_model, &iter); + info = _gtk_file_system_model_get_info (priv->browse_files_model, &iter); return info; } @@ -7646,11 +5509,12 @@ confirmation_confirm_get_info_cb (GCancellable *cancellable, gboolean cancelled = g_cancellable_is_cancelled (cancellable); gboolean should_respond = FALSE; struct GetDisplayNameData *data = user_data; + GtkFileChooserDefaultPrivate *priv = data->impl->priv; - if (cancellable != data->impl->priv->should_respond_get_info_cancellable) + if (cancellable != priv->should_respond_get_info_cancellable) goto out; - data->impl->priv->should_respond_get_info_cancellable = NULL; + priv->should_respond_get_info_cancellable = NULL; if (cancelled) goto out; @@ -7681,9 +5545,10 @@ should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl, const gchar *file_part, GFile *parent_file) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkFileChooserConfirmation conf; - if (!impl->priv->do_overwrite_confirmation) + if (!priv->do_overwrite_confirmation) return TRUE; conf = GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM; @@ -7702,11 +5567,11 @@ should_respond_after_confirm_overwrite (GtkFileChooserDefault *impl, data->impl = g_object_ref (impl); data->file_part = g_strdup (file_part); - if (impl->priv->should_respond_get_info_cancellable) - g_cancellable_cancel (impl->priv->should_respond_get_info_cancellable); + if (priv->should_respond_get_info_cancellable) + g_cancellable_cancel (priv->should_respond_get_info_cancellable); - impl->priv->should_respond_get_info_cancellable = - _gtk_file_system_get_info (impl->priv->file_system, parent_file, + priv->should_respond_get_info_cancellable = + _gtk_file_system_get_info (priv->file_system, parent_file, "standard::display-name", confirmation_confirm_get_info_cb, data); @@ -7743,13 +5608,15 @@ name_entry_get_parent_info_cb (GCancellable *cancellable, gboolean parent_is_folder; gboolean cancelled = g_cancellable_is_cancelled (cancellable); struct FileExistsData *data = user_data; + GtkFileChooserDefault *impl = data->impl; + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (cancellable != data->impl->priv->should_respond_get_info_cancellable) + if (cancellable != priv->should_respond_get_info_cancellable) goto out; - data->impl->priv->should_respond_get_info_cancellable = NULL; + priv->should_respond_get_info_cancellable = NULL; - set_busy_cursor (data->impl, FALSE); + set_busy_cursor (impl, FALSE); if (cancelled) goto out; @@ -7761,11 +5628,11 @@ name_entry_get_parent_info_cb (GCancellable *cancellable, if (parent_is_folder) { - if (data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN) { - request_response_and_add_to_recent_list (data->impl); /* even if the file doesn't exist, apps can make good use of that (e.g. Emacs) */ + request_response_and_add_to_recent_list (impl); /* even if the file doesn't exist, apps can make good use of that (e.g. Emacs) */ } - else if (data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { if (data->file_exists_and_is_not_folder) { @@ -7776,18 +5643,18 @@ name_entry_get_parent_info_cb (GCancellable *cancellable, * depending on what clients do in the confirm-overwrite * signal and this corrupts the pointer */ - file_part = g_strdup (_gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (data->impl->priv->location_entry))); - retval = should_respond_after_confirm_overwrite (data->impl, file_part, data->parent_file); + file_part = g_strdup (_gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (priv->location_entry))); + retval = should_respond_after_confirm_overwrite (impl, file_part, data->parent_file); g_free (file_part); if (retval) - request_response_and_add_to_recent_list (data->impl); + request_response_and_add_to_recent_list (impl); } else - request_response_and_add_to_recent_list (data->impl); + request_response_and_add_to_recent_list (impl); } - else if (data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER - || data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { GError *mkdir_error = NULL; @@ -7795,14 +5662,14 @@ name_entry_get_parent_info_cb (GCancellable *cancellable, * "/blah/nonexistent" you *will* want a folder created. */ - set_busy_cursor (data->impl, TRUE); + set_busy_cursor (impl, TRUE); g_file_make_directory (data->file, NULL, &mkdir_error); - set_busy_cursor (data->impl, FALSE); + set_busy_cursor (impl, FALSE); if (!mkdir_error) - request_response_and_add_to_recent_list (data->impl); + request_response_and_add_to_recent_list (impl); else - error_creating_folder_dialog (data->impl, data->file, mkdir_error); + error_creating_folder_dialog (impl, data->file, mkdir_error); } else g_assert_not_reached (); @@ -7812,7 +5679,7 @@ name_entry_get_parent_info_cb (GCancellable *cancellable, if (info) { /* The parent exists, but it's not a folder! Someone probably typed existing_file.txt/subfile.txt */ - error_with_file_under_nonfolder (data->impl, data->parent_file); + error_with_file_under_nonfolder (impl, data->parent_file); } else { @@ -7821,7 +5688,7 @@ name_entry_get_parent_info_cb (GCancellable *cancellable, /* The parent folder is not readable for some reason */ error_copy = g_error_copy (error); - error_changing_folder_dialog (data->impl, data->parent_file, error_copy); + error_changing_folder_dialog (impl, data->parent_file, error_copy); } } @@ -7846,13 +5713,15 @@ file_exists_get_info_cb (GCancellable *cancellable, gboolean is_folder; gboolean needs_parent_check = FALSE; struct FileExistsData *data = user_data; + GtkFileChooserDefault *impl = data->impl; + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (cancellable != data->impl->priv->file_exists_get_info_cancellable) + if (cancellable != priv->file_exists_get_info_cancellable) goto out; - data->impl->priv->file_exists_get_info_cancellable = NULL; + priv->file_exists_get_info_cancellable = NULL; - set_busy_cursor (data->impl, FALSE); + set_busy_cursor (impl, FALSE); if (cancelled) goto out; @@ -7860,26 +5729,26 @@ file_exists_get_info_cb (GCancellable *cancellable, file_exists = (info != NULL); is_folder = (file_exists && _gtk_file_info_consider_as_directory (info)); - if (data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN) { if (is_folder) - change_folder_and_display_error (data->impl, data->file, TRUE); + change_folder_and_display_error (impl, data->file, TRUE); else { if (file_exists) - request_response_and_add_to_recent_list (data->impl); /* user typed an existing filename; we are done */ + request_response_and_add_to_recent_list (impl); /* user typed an existing filename; we are done */ else needs_parent_check = TRUE; /* file doesn't exist; see if its parent exists */ } } - else if (data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { if (file_exists && !is_folder) { /* Oops, the user typed the name of an existing path which is not * a folder */ - error_creating_folder_over_existing_file_dialog (data->impl, data->file, + error_creating_folder_over_existing_file_dialog (impl, data->file, g_error_copy (error)); } else @@ -7887,7 +5756,7 @@ file_exists_get_info_cb (GCancellable *cancellable, needs_parent_check = TRUE; } } - else if (data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { if (!file_exists) { @@ -7898,16 +5767,16 @@ file_exists_get_info_cb (GCancellable *cancellable, if (is_folder) { /* User typed a folder; we are done */ - request_response_and_add_to_recent_list (data->impl); + request_response_and_add_to_recent_list (impl); } else - error_selecting_folder_over_existing_file_dialog (data->impl, data->file); + error_selecting_folder_over_existing_file_dialog (impl, data->file); } } - else if (data->impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { if (is_folder) - change_folder_and_display_error (data->impl, data->file, TRUE); + change_folder_and_display_error (impl, data->file, TRUE); else needs_parent_check = TRUE; } @@ -7922,22 +5791,22 @@ file_exists_get_info_cb (GCancellable *cancellable, data->file_exists_and_is_not_folder = file_exists && !is_folder; data_ownership_taken = TRUE; - if (data->impl->priv->should_respond_get_info_cancellable) - g_cancellable_cancel (data->impl->priv->should_respond_get_info_cancellable); + if (priv->should_respond_get_info_cancellable) + g_cancellable_cancel (priv->should_respond_get_info_cancellable); - data->impl->priv->should_respond_get_info_cancellable = - _gtk_file_system_get_info (data->impl->priv->file_system, + priv->should_respond_get_info_cancellable = + _gtk_file_system_get_info (priv->file_system, data->parent_file, "standard::type", name_entry_get_parent_info_cb, data); - set_busy_cursor (data->impl, TRUE); + set_busy_cursor (impl, TRUE); } out: if (!data_ownership_taken) { - g_object_unref (data->impl); + g_object_unref (impl); g_object_unref (data->file); g_object_unref (data->parent_file); g_free (data); @@ -7979,6 +5848,7 @@ location_popup_on_paste_handler (GtkFileChooserDefault *impl) static void add_selection_to_recent_list (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GSList *files; GSList *l; @@ -7992,7 +5862,7 @@ add_selection_to_recent_list (GtkFileChooserDefault *impl) uri = g_file_get_uri (file); if (uri) { - gtk_recent_manager_add_item (impl->priv->recent_manager, uri); + gtk_recent_manager_add_item (priv->recent_manager, uri); g_free (uri); } } @@ -8004,13 +5874,12 @@ add_selection_to_recent_list (GtkFileChooserDefault *impl) static gboolean gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed); + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *toplevel; GtkWidget *current_focus; gboolean retval; - impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed); - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl)); g_assert (GTK_IS_WINDOW (toplevel)); @@ -8018,9 +5887,9 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) current_focus = gtk_window_get_focus (GTK_WINDOW (toplevel)); - if (current_focus == impl->priv->browse_files_tree_view) + if (current_focus == priv->browse_files_tree_view) { - /* The following array encodes what we do based on the impl->priv->action and the + /* The following array encodes what we do based on the priv->action and the * number of files selected. */ typedef enum { @@ -8047,17 +5916,17 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) file_list: - g_assert (impl->priv->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->priv->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER); + g_assert (priv->action >= GTK_FILE_CHOOSER_ACTION_OPEN && priv->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER); - if (impl->priv->operation_mode == OPERATION_MODE_SEARCH) + if (priv->operation_mode == OPERATION_MODE_SEARCH) { retval = search_should_respond (impl); goto out; } - if (impl->priv->operation_mode == OPERATION_MODE_RECENT) + if (priv->operation_mode == OPERATION_MODE_RECENT) { - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) goto save_entry; else { @@ -8073,7 +5942,7 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) else k = num_selected; - action = what_to_do [impl->priv->action] [k]; + action = what_to_do [priv->action] [k]; switch (action) { @@ -8092,11 +5961,11 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) switch_to_selected_folder (impl); return FALSE; } - else if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { retval = should_respond_after_confirm_overwrite (impl, get_display_name_from_file_list (impl), - impl->priv->current_folder); + priv->current_folder); goto out; } else @@ -8120,7 +5989,7 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) g_assert_not_reached (); } } - else if ((impl->priv->location_entry != NULL) && (current_focus == impl->priv->location_entry)) + else if ((priv->location_entry != NULL) && (current_focus == priv->location_entry)) { GFile *file; gboolean is_well_formed, is_empty, is_file_part_empty; @@ -8130,20 +5999,20 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) save_entry: - g_assert (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER - || ((impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) - && impl->priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)); + g_assert (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER + || ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN + || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + && priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)); - entry = GTK_FILE_CHOOSER_ENTRY (impl->priv->location_entry); + entry = GTK_FILE_CHOOSER_ENTRY (priv->location_entry); check_save_entry (impl, &file, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder); if (!is_well_formed) { if (!is_empty - && impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - && impl->priv->operation_mode == OPERATION_MODE_RECENT) + && priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + && priv->operation_mode == OPERATION_MODE_RECENT) { path_bar_set_mode (impl, PATH_BAR_ERROR_NO_FOLDER); #if 0 @@ -8152,7 +6021,7 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) * a selection causes the error message from path_bar_set_mode() to go away, * but we want the user to see that message! */ - gtk_widget_grab_focus (impl->priv->browse_files_tree_view); + gtk_widget_grab_focus (priv->browse_files_tree_view); #endif } /* FIXME: else show an "invalid filename" error as the pathbar mode? */ @@ -8162,11 +6031,11 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) if (is_empty) { - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { path_bar_set_mode (impl, PATH_BAR_ERROR_NO_FILENAME); - gtk_widget_grab_focus (impl->priv->location_entry); + gtk_widget_grab_focus (priv->location_entry); return FALSE; } @@ -8178,13 +6047,13 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) error = NULL; if (is_folder) { - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { change_folder_and_display_error (impl, file, TRUE); } - else if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { /* The folder already exists, so we do not need to create it. * Just respond to terminate the dialog. @@ -8211,11 +6080,11 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) data->file = g_object_ref (file); data->parent_file = _gtk_file_chooser_entry_get_current_folder (entry); - if (impl->priv->file_exists_get_info_cancellable) - g_cancellable_cancel (impl->priv->file_exists_get_info_cancellable); + if (priv->file_exists_get_info_cancellable) + g_cancellable_cancel (priv->file_exists_get_info_cancellable); - impl->priv->file_exists_get_info_cancellable = - _gtk_file_system_get_info (impl->priv->file_system, file, + priv->file_exists_get_info_cancellable = + _gtk_file_system_get_info (priv->file_system, file, "standard::type", file_exists_get_info_cb, data); @@ -8228,19 +6097,19 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) g_object_unref (file); } - else if (impl->priv->toplevel_last_focus_widget == impl->priv->browse_files_tree_view) + else if (priv->toplevel_last_focus_widget == priv->browse_files_tree_view) { /* The focus is on a dialog's action area button, *and* the widget that * was focused immediately before it is the file list. */ goto file_list; } - else if (impl->priv->operation_mode == OPERATION_MODE_SEARCH && impl->priv->toplevel_last_focus_widget == impl->priv->search_entry) + else if (priv->operation_mode == OPERATION_MODE_SEARCH && priv->toplevel_last_focus_widget == priv->search_entry) { - search_entry_activate_cb (GTK_ENTRY (impl->priv->search_entry), impl); + search_entry_activate_cb (GTK_ENTRY (priv->search_entry), impl); return FALSE; } - else if (impl->priv->location_entry && impl->priv->toplevel_last_focus_widget == impl->priv->location_entry) + else if (priv->location_entry && priv->toplevel_last_focus_widget == priv->location_entry) { /* The focus is on a dialog's action area button, *and* the widget that * was focused immediately before it is the location entry. @@ -8249,8 +6118,8 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) } else /* The focus is on a dialog's action area button or something else */ - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE + || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) goto save_entry; else goto file_list; @@ -8267,23 +6136,22 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed) static void gtk_file_chooser_default_initial_focus (GtkFileChooserEmbed *chooser_embed) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed); + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkWidget *widget; - impl = GTK_FILE_CHOOSER_DEFAULT (chooser_embed); - - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { - if (impl->priv->location_mode == LOCATION_MODE_PATH_BAR - || impl->priv->operation_mode == OPERATION_MODE_RECENT) - widget = impl->priv->browse_files_tree_view; + if (priv->location_mode == LOCATION_MODE_PATH_BAR + || priv->operation_mode == OPERATION_MODE_RECENT) + widget = priv->browse_files_tree_view; else - widget = impl->priv->location_entry; + widget = priv->location_entry; } - else if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - widget = impl->priv->location_entry; + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + widget = priv->location_entry; else { g_assert_not_reached (); @@ -8314,12 +6182,13 @@ search_selected_foreach_get_file_cb (GtkTreeModel *model, static GSList * search_get_selected_files (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GSList *result; GtkTreeSelection *selection; result = NULL; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); gtk_tree_selection_selected_foreach (selection, search_selected_foreach_get_file_cb, &result); result = g_slist_reverse (result); @@ -8332,11 +6201,12 @@ search_get_selected_files (GtkFileChooserDefault *impl) static gboolean search_should_respond (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; - g_assert (impl->priv->operation_mode == OPERATION_MODE_SEARCH); + g_assert (priv->operation_mode == OPERATION_MODE_SEARCH); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); return (gtk_tree_selection_count_selected_rows (selection) != 0); } @@ -8345,6 +6215,7 @@ static void search_add_hit (GtkFileChooserDefault *impl, gchar *uri) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GFile *file; file = g_file_new_for_uri (uri); @@ -8357,7 +6228,7 @@ search_add_hit (GtkFileChooserDefault *impl, return; } - _gtk_file_system_model_add_and_query_file (impl->priv->search_model, + _gtk_file_system_model_add_and_query_file (priv->search_model, file, MODEL_ATTRIBUTES); @@ -8392,8 +6263,8 @@ search_engine_finished_cb (GtkSearchEngine *engine, /* EB: setting the model here will avoid loads of row events, * but it'll make the search look like blocked. */ - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), - GTK_TREE_MODEL (impl->priv->search_model)); + gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), + GTK_TREE_MODEL (impl->search_model)); file_list_set_sort_column_ids (impl); #endif @@ -8434,14 +6305,16 @@ static void search_clear_model (GtkFileChooserDefault *impl, gboolean remove_from_treeview) { - if (!impl->priv->search_model) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (!priv->search_model) return; - g_object_unref (impl->priv->search_model); - impl->priv->search_model = NULL; + g_object_unref (priv->search_model); + priv->search_model = NULL; if (remove_from_treeview) - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), NULL); + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL); } /* Stops any ongoing searches; does not touch the search_model */ @@ -8449,18 +6322,20 @@ static void search_stop_searching (GtkFileChooserDefault *impl, gboolean remove_query) { - if (remove_query && impl->priv->search_query) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (remove_query && priv->search_query) { - g_object_unref (impl->priv->search_query); - impl->priv->search_query = NULL; + g_object_unref (priv->search_query); + priv->search_query = NULL; } - if (impl->priv->search_engine) + if (priv->search_engine) { - _gtk_search_engine_stop (impl->priv->search_engine); + _gtk_search_engine_stop (priv->search_engine); - g_object_unref (impl->priv->search_engine); - impl->priv->search_engine = NULL; + g_object_unref (priv->search_engine); + priv->search_engine = NULL; } } @@ -8468,21 +6343,23 @@ search_stop_searching (GtkFileChooserDefault *impl, static void search_setup_model (GtkFileChooserDefault *impl) { - g_assert (impl->priv->search_model == NULL); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + g_assert (priv->search_model == NULL); - impl->priv->search_model = _gtk_file_system_model_new (file_system_model_set, + priv->search_model = _gtk_file_system_model_new (file_system_model_set, impl, MODEL_COLUMN_TYPES); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->search_model), + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->search_model), MODEL_COL_NAME, name_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->search_model), + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->search_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->search_model), + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->search_model), MODEL_COL_SIZE, size_sort_func, impl, NULL); @@ -8492,8 +6369,8 @@ search_setup_model (GtkFileChooserDefault *impl) * more "alive" than setting the model at the end of the search * run */ - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), - GTK_TREE_MODEL (impl->priv->search_model)); + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), + GTK_TREE_MODEL (priv->search_model)); file_list_set_sort_column_ids (impl); } @@ -8502,37 +6379,39 @@ static void search_start_query (GtkFileChooserDefault *impl, const gchar *query_text) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + search_stop_searching (impl, FALSE); search_clear_model (impl, TRUE); search_setup_model (impl); set_busy_cursor (impl, TRUE); - if (impl->priv->search_engine == NULL) - impl->priv->search_engine = _gtk_search_engine_new (); + if (priv->search_engine == NULL) + priv->search_engine = _gtk_search_engine_new (); - if (!impl->priv->search_engine) + if (!priv->search_engine) { set_busy_cursor (impl, FALSE); search_error_could_not_create_client (impl); /* lame; we don't get an error code or anything */ return; } - if (!impl->priv->search_query) + if (!priv->search_query) { - impl->priv->search_query = _gtk_query_new (); - _gtk_query_set_text (impl->priv->search_query, query_text); + priv->search_query = _gtk_query_new (); + _gtk_query_set_text (priv->search_query, query_text); } - _gtk_search_engine_set_query (impl->priv->search_engine, impl->priv->search_query); + _gtk_search_engine_set_query (priv->search_engine, priv->search_query); - g_signal_connect (impl->priv->search_engine, "hits-added", + g_signal_connect (priv->search_engine, "hits-added", G_CALLBACK (search_engine_hits_added_cb), impl); - g_signal_connect (impl->priv->search_engine, "finished", + g_signal_connect (priv->search_engine, "finished", G_CALLBACK (search_engine_finished_cb), impl); - g_signal_connect (impl->priv->search_engine, "error", + g_signal_connect (priv->search_engine, "error", G_CALLBACK (search_engine_error_cb), impl); - _gtk_search_engine_start (impl->priv->search_engine); + _gtk_search_engine_start (priv->search_engine); } /* Callback used when the user presses Enter while typing on the search @@ -8542,20 +6421,19 @@ static void search_entry_activate_cb (GtkEntry *entry, gpointer data) { - GtkFileChooserDefault *impl; + GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data); + GtkFileChooserDefaultPrivate *priv = impl->priv; const char *text; - impl = GTK_FILE_CHOOSER_DEFAULT (data); - - text = gtk_entry_get_text (GTK_ENTRY (impl->priv->search_entry)); + text = gtk_entry_get_text (GTK_ENTRY (priv->search_entry)); if (strlen (text) == 0) return; /* reset any existing query object */ - if (impl->priv->search_query) + if (priv->search_query) { - g_object_unref (impl->priv->search_query); - impl->priv->search_query = NULL; + g_object_unref (priv->search_query); + priv->search_query = NULL; } search_start_query (impl, text); @@ -8564,13 +6442,15 @@ search_entry_activate_cb (GtkEntry *entry, static gboolean focus_entry_idle_cb (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + gdk_threads_enter (); - g_source_destroy (impl->priv->focus_entry_idle); - impl->priv->focus_entry_idle = NULL; + g_source_destroy (priv->focus_entry_idle); + priv->focus_entry_idle = NULL; - if (impl->priv->search_entry) - gtk_widget_grab_focus (impl->priv->search_entry); + if (priv->search_entry) + gtk_widget_grab_focus (priv->search_entry); gdk_threads_leave (); @@ -8580,6 +6460,8 @@ focus_entry_idle_cb (GtkFileChooserDefault *impl) static void focus_search_entry_in_idle (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + /* bgo#634558 - When the user clicks on the Search entry in the shortcuts * pane, we get a selection-changed signal and we set up the search widgets. * However, gtk_tree_view_button_press() focuses the treeview *after* making @@ -8588,55 +6470,57 @@ focus_search_entry_in_idle (GtkFileChooserDefault *impl) * handler. */ - if (!impl->priv->focus_entry_idle) - impl->priv->focus_entry_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (focus_entry_idle_cb)); + if (!priv->focus_entry_idle) + priv->focus_entry_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (focus_entry_idle_cb)); } /* Hides the path bar and creates the search entry */ static void search_setup_widgets (GtkFileChooserDefault *impl) { - impl->priv->search_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + priv->search_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); path_bar_update (impl); - impl->priv->search_entry = gtk_entry_new (); - g_signal_connect (impl->priv->search_entry, "activate", + priv->search_entry = gtk_entry_new (); + g_signal_connect (priv->search_entry, "activate", G_CALLBACK (search_entry_activate_cb), impl); - gtk_box_pack_start (GTK_BOX (impl->priv->search_hbox), impl->priv->search_entry, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (priv->search_hbox), priv->search_entry, TRUE, TRUE, 0); /* if there already is a query, restart it */ - if (impl->priv->search_query) + if (priv->search_query) { - gchar *query = _gtk_query_get_text (impl->priv->search_query); + gchar *query = _gtk_query_get_text (priv->search_query); if (query) { - gtk_entry_set_text (GTK_ENTRY (impl->priv->search_entry), query); + gtk_entry_set_text (GTK_ENTRY (priv->search_entry), query); search_start_query (impl, query); g_free (query); } else { - g_object_unref (impl->priv->search_query); - impl->priv->search_query = NULL; + g_object_unref (priv->search_query); + priv->search_query = NULL; } } /* Box for search widgets */ - gtk_box_pack_start (GTK_BOX (impl->priv->browse_path_bar_hbox), impl->priv->search_hbox, TRUE, TRUE, 0); - gtk_widget_show_all (impl->priv->search_hbox); - gtk_size_group_add_widget (GTK_SIZE_GROUP (impl->priv->browse_path_bar_size_group), impl->priv->search_hbox); + gtk_box_pack_start (GTK_BOX (priv->browse_path_bar_hbox), priv->search_hbox, TRUE, TRUE, 0); + gtk_widget_show_all (priv->search_hbox); + gtk_size_group_add_widget (GTK_SIZE_GROUP (priv->browse_path_bar_size_group), priv->search_hbox); /* Hide the location widgets temporarily */ - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { - gtk_widget_hide (impl->priv->location_button); - gtk_widget_hide (impl->priv->location_entry_box); + gtk_widget_hide (priv->location_button); + gtk_widget_hide (priv->location_entry_box); } focus_search_entry_in_idle (impl); @@ -8653,14 +6537,16 @@ static void recent_clear_model (GtkFileChooserDefault *impl, gboolean remove_from_treeview) { - if (!impl->priv->recent_model) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (!priv->recent_model) return; if (remove_from_treeview) - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), NULL); + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL); - g_object_unref (impl->priv->recent_model); - impl->priv->recent_model = NULL; + g_object_unref (priv->recent_model); + priv->recent_model = NULL; } /* Stops any ongoing loading of the recent files list; does @@ -8669,33 +6555,37 @@ recent_clear_model (GtkFileChooserDefault *impl, static void recent_stop_loading (GtkFileChooserDefault *impl) { - if (impl->priv->load_recent_id) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->load_recent_id) { - g_source_remove (impl->priv->load_recent_id); - impl->priv->load_recent_id = 0; + g_source_remove (priv->load_recent_id); + priv->load_recent_id = 0; } } static void recent_setup_model (GtkFileChooserDefault *impl) { - g_assert (impl->priv->recent_model == NULL); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + g_assert (priv->recent_model == NULL); - impl->priv->recent_model = _gtk_file_system_model_new (file_system_model_set, + priv->recent_model = _gtk_file_system_model_new (file_system_model_set, impl, MODEL_COLUMN_TYPES); - _gtk_file_system_model_set_filter (impl->priv->recent_model, - impl->priv->current_filter); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->recent_model), + _gtk_file_system_model_set_filter (priv->recent_model, + priv->current_filter); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->recent_model), MODEL_COL_NAME, name_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->recent_model), + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->recent_model), MODEL_COL_SIZE, size_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->priv->recent_model), + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->recent_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL); @@ -8713,15 +6603,16 @@ recent_idle_cleanup (gpointer data) { RecentLoadData *load_data = data; GtkFileChooserDefault *impl = load_data->impl; + GtkFileChooserDefaultPrivate *priv = impl->priv; - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), - GTK_TREE_MODEL (impl->priv->recent_model)); + gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), + GTK_TREE_MODEL (priv->recent_model)); file_list_set_sort_column_ids (impl); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (impl->priv->recent_model), MODEL_COL_MTIME, GTK_SORT_DESCENDING); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->recent_model), MODEL_COL_MTIME, GTK_SORT_DESCENDING); set_busy_cursor (impl, FALSE); - impl->priv->load_recent_id = 0; + priv->load_recent_id = 0; g_free (load_data); } @@ -8746,6 +6637,7 @@ get_recent_files_limit (GtkWidget *widget) static void populate_model_with_recent_items (GtkFileChooserDefault *impl, GList *items) { + GtkFileChooserDefaultPrivate *priv = impl->priv; gint limit; GList *l; int n; @@ -8760,7 +6652,7 @@ populate_model_with_recent_items (GtkFileChooserDefault *impl, GList *items) GFile *file; file = g_file_new_for_uri (gtk_recent_info_get_uri (info)); - _gtk_file_system_model_add_and_query_file (impl->priv->recent_model, + _gtk_file_system_model_add_and_query_file (priv->recent_model, file, MODEL_ATTRIBUTES); g_object_unref (file); @@ -8774,6 +6666,7 @@ populate_model_with_recent_items (GtkFileChooserDefault *impl, GList *items) static void populate_model_with_folders (GtkFileChooserDefault *impl, GList *items) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GList *folders; GList *l; @@ -8783,7 +6676,7 @@ populate_model_with_folders (GtkFileChooserDefault *impl, GList *items) { GFile *folder = l->data; - _gtk_file_system_model_add_and_query_file (impl->priv->recent_model, + _gtk_file_system_model_add_and_query_file (priv->recent_model, folder, MODEL_ATTRIBUTES); } @@ -8797,15 +6690,16 @@ recent_idle_load (gpointer data) { RecentLoadData *load_data = data; GtkFileChooserDefault *impl = load_data->impl; + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (!impl->priv->recent_manager) + if (!priv->recent_manager) return FALSE; - load_data->items = gtk_recent_manager_get_items (impl->priv->recent_manager); + load_data->items = gtk_recent_manager_get_items (priv->recent_manager); if (!load_data->items) return FALSE; - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN) populate_model_with_recent_items (impl, load_data->items); else populate_model_with_folders (impl, load_data->items); @@ -8820,6 +6714,7 @@ recent_idle_load (gpointer data) static void recent_start_loading (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; RecentLoadData *load_data; recent_stop_loading (impl); @@ -8827,14 +6722,14 @@ recent_start_loading (GtkFileChooserDefault *impl) recent_setup_model (impl); set_busy_cursor (impl, TRUE); - g_assert (impl->priv->load_recent_id == 0); + g_assert (priv->load_recent_id == 0); load_data = g_new (RecentLoadData, 1); load_data->impl = impl; load_data->items = NULL; /* begin lazy loading the recent files into the model */ - impl->priv->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30, + priv->load_recent_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30, recent_idle_load, load_data, recent_idle_cleanup); @@ -8859,12 +6754,13 @@ recent_selected_foreach_get_file_cb (GtkTreeModel *model, static GSList * recent_get_selected_files (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GSList *result; GtkTreeSelection *selection; result = NULL; - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); gtk_tree_selection_selected_foreach (selection, recent_selected_foreach_get_file_cb, &result); result = g_slist_reverse (result); @@ -8877,11 +6773,12 @@ recent_get_selected_files (GtkFileChooserDefault *impl) static gboolean recent_should_respond (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeSelection *selection; - g_assert (impl->priv->operation_mode == OPERATION_MODE_RECENT); + g_assert (priv->operation_mode == OPERATION_MODE_RECENT); - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view)); return (gtk_tree_selection_count_selected_rows (selection) != 0); } @@ -8889,44 +6786,46 @@ static void set_current_filter (GtkFileChooserDefault *impl, GtkFileFilter *filter) { - if (impl->priv->current_filter != filter) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->current_filter != filter) { int filter_index; /* NULL filters are allowed to reset to non-filtered status */ - filter_index = g_slist_index (impl->priv->filters, filter); - if (impl->priv->filters && filter && filter_index < 0) + filter_index = g_slist_index (priv->filters, filter); + if (priv->filters && filter && filter_index < 0) return; - if (impl->priv->current_filter) - g_object_unref (impl->priv->current_filter); - impl->priv->current_filter = filter; - if (impl->priv->current_filter) + if (priv->current_filter) + g_object_unref (priv->current_filter); + priv->current_filter = filter; + if (priv->current_filter) { - g_object_ref_sink (impl->priv->current_filter); + g_object_ref_sink (priv->current_filter); } - if (impl->priv->filters) - gtk_combo_box_set_active (GTK_COMBO_BOX (impl->priv->filter_combo), + if (priv->filters) + gtk_combo_box_set_active (GTK_COMBO_BOX (priv->filter_combo), filter_index); - if (impl->priv->browse_files_model) + if (priv->browse_files_model) { - _gtk_file_system_model_set_filter (impl->priv->browse_files_model, impl->priv->current_filter); - _gtk_file_system_model_clear_cache (impl->priv->browse_files_model, MODEL_COL_IS_SENSITIVE); + _gtk_file_system_model_set_filter (priv->browse_files_model, priv->current_filter); + _gtk_file_system_model_clear_cache (priv->browse_files_model, MODEL_COL_IS_SENSITIVE); } - if (impl->priv->search_model) + if (priv->search_model) { - _gtk_file_system_model_set_filter (impl->priv->search_model, filter); - _gtk_file_system_model_clear_cache (impl->priv->search_model, MODEL_COL_IS_SENSITIVE); + _gtk_file_system_model_set_filter (priv->search_model, filter); + _gtk_file_system_model_clear_cache (priv->search_model, MODEL_COL_IS_SENSITIVE); } - if (impl->priv->recent_model) + if (priv->recent_model) { - _gtk_file_system_model_set_filter (impl->priv->recent_model, filter); - _gtk_file_system_model_clear_cache (impl->priv->recent_model, MODEL_COL_IS_SENSITIVE); + _gtk_file_system_model_set_filter (priv->recent_model, filter); + _gtk_file_system_model_clear_cache (priv->recent_model, MODEL_COL_IS_SENSITIVE); } g_object_notify (G_OBJECT (impl), "filter"); @@ -8937,8 +6836,9 @@ static void filter_combo_changed (GtkComboBox *combo_box, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; gint new_index = gtk_combo_box_get_active (combo_box); - GtkFileFilter *new_filter = g_slist_nth_data (impl->priv->filters, new_index); + GtkFileFilter *new_filter = g_slist_nth_data (priv->filters, new_index); set_current_filter (impl, new_filter); } @@ -8946,13 +6846,14 @@ filter_combo_changed (GtkComboBox *combo_box, static void check_preview_change (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreePath *cursor_path; GFile *new_file; char *new_display_name; GtkTreeModel *model; - gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), &cursor_path, NULL); - model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); + gtk_tree_view_get_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), &cursor_path, NULL); + model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)); if (cursor_path) { GtkTreeIter iter; @@ -8971,30 +6872,30 @@ check_preview_change (GtkFileChooserDefault *impl) new_display_name = NULL; } - if (new_file != impl->priv->preview_file && - !(new_file && impl->priv->preview_file && - g_file_equal (new_file, impl->priv->preview_file))) + if (new_file != priv->preview_file && + !(new_file && priv->preview_file && + g_file_equal (new_file, priv->preview_file))) { - if (impl->priv->preview_file) + if (priv->preview_file) { - g_object_unref (impl->priv->preview_file); - g_free (impl->priv->preview_display_name); + g_object_unref (priv->preview_file); + g_free (priv->preview_display_name); } if (new_file) { - impl->priv->preview_file = new_file; - impl->priv->preview_display_name = new_display_name; + priv->preview_file = new_file; + priv->preview_display_name = new_display_name; } else { - impl->priv->preview_file = NULL; - impl->priv->preview_display_name = NULL; + priv->preview_file = NULL; + priv->preview_display_name = NULL; g_free (new_display_name); } - if (impl->priv->use_preview_label && impl->priv->preview_label) - gtk_label_set_text (GTK_LABEL (impl->priv->preview_label), impl->priv->preview_display_name); + if (priv->use_preview_label && priv->preview_label) + gtk_label_set_text (GTK_LABEL (priv->preview_label), priv->preview_display_name); g_signal_emit_by_name (impl, "update-preview"); } @@ -9007,304 +6908,6 @@ check_preview_change (GtkFileChooserDefault *impl) } } -static void -shortcuts_activate_volume_mount_cb (GCancellable *cancellable, - GtkFileSystemVolume *volume, - const GError *error, - gpointer data) -{ - GFile *file; - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - GtkFileChooserDefault *impl = data; - - if (cancellable != impl->priv->shortcuts_activate_iter_cancellable) - goto out; - - impl->priv->shortcuts_activate_iter_cancellable = NULL; - - set_busy_cursor (impl, FALSE); - - if (cancelled) - goto out; - - if (error) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED)) - { - char *msg, *name; - - name = _gtk_file_system_volume_get_display_name (volume); - msg = g_strdup_printf (_("Could not mount %s"), name); - - error_message (impl, msg, error->message); - - g_free (msg); - g_free (name); - } - - goto out; - } - - file = _gtk_file_system_volume_get_root (volume); - if (file != NULL) - { - change_folder_and_display_error (impl, file, FALSE); - g_object_unref (file); - } - -out: - g_object_unref (impl); - g_object_unref (cancellable); -} - - -/* Activates a volume by mounting it if necessary and then switching to its - * base path. - */ -static void -shortcuts_activate_volume (GtkFileChooserDefault *impl, - GtkFileSystemVolume *volume) -{ - GFile *file; - - operation_mode_set (impl, OPERATION_MODE_BROWSE); - - /* We ref the file chooser since volume_mount() may run a main loop, and the - * user could close the file chooser window in the meantime. - */ - g_object_ref (impl); - - if (!_gtk_file_system_volume_is_mounted (volume)) - { - GMountOperation *mount_op; - - set_busy_cursor (impl, TRUE); - - mount_op = gtk_mount_operation_new (get_toplevel (GTK_WIDGET (impl))); - impl->priv->shortcuts_activate_iter_cancellable = - _gtk_file_system_mount_volume (impl->priv->file_system, volume, mount_op, - shortcuts_activate_volume_mount_cb, - g_object_ref (impl)); - g_object_unref (mount_op); - } - else - { - file = _gtk_file_system_volume_get_root (volume); - if (file != NULL) - { - change_folder_and_display_error (impl, file, FALSE); - g_object_unref (file); - } - } - - g_object_unref (impl); -} - -/* Opens the folder or volume at the specified iter in the shortcuts model */ -struct ShortcutsActivateData -{ - GtkFileChooserDefault *impl; - GFile *file; -}; - -static void -shortcuts_activate_get_info_cb (GCancellable *cancellable, - GFileInfo *info, - const GError *error, - gpointer user_data) -{ - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - struct ShortcutsActivateData *data = user_data; - - if (cancellable != data->impl->priv->shortcuts_activate_iter_cancellable) - goto out; - - data->impl->priv->shortcuts_activate_iter_cancellable = NULL; - - if (cancelled) - goto out; - - if (!error && _gtk_file_info_consider_as_directory (info)) - change_folder_and_display_error (data->impl, data->file, FALSE); - else - gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (data->impl), - data->file, - NULL); - -out: - g_object_unref (data->impl); - g_object_unref (data->file); - g_free (data); - - g_object_unref (cancellable); -} - -static void -shortcuts_activate_mount_enclosing_volume (GCancellable *cancellable, - GtkFileSystemVolume *volume, - const GError *error, - gpointer user_data) -{ - struct ShortcutsActivateData *data = user_data; - - if (error) - { - error_changing_folder_dialog (data->impl, data->file, g_error_copy (error)); - - g_object_unref (data->impl); - g_object_unref (data->file); - g_free (data); - - return; - } - - data->impl->priv->shortcuts_activate_iter_cancellable = - _gtk_file_system_get_info (data->impl->priv->file_system, data->file, - "standard::type", - shortcuts_activate_get_info_cb, data); - - if (volume) - _gtk_file_system_volume_unref (volume); -} - -static void -shortcuts_activate_iter (GtkFileChooserDefault *impl, - GtkTreeIter *iter) -{ - gpointer col_data; - ShortcutType shortcut_type; - - /* In the Save modes, we want to preserve what the uesr typed in the filename - * entry, so that he may choose another folder without erasing his typed name. - */ - if (impl->priv->location_entry - && !(impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE - || impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) - gtk_entry_set_text (GTK_ENTRY (impl->priv->location_entry), ""); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->priv->shortcuts_model), iter, - SHORTCUTS_COL_DATA, &col_data, - SHORTCUTS_COL_TYPE, &shortcut_type, - -1); - - if (impl->priv->shortcuts_activate_iter_cancellable) - { - g_cancellable_cancel (impl->priv->shortcuts_activate_iter_cancellable); - impl->priv->shortcuts_activate_iter_cancellable = NULL; - } - - if (shortcut_type == SHORTCUT_TYPE_SEPARATOR) - return; - else if (shortcut_type == SHORTCUT_TYPE_VOLUME) - { - GtkFileSystemVolume *volume; - - volume = col_data; - - operation_mode_set (impl, OPERATION_MODE_BROWSE); - - shortcuts_activate_volume (impl, volume); - } - else if (shortcut_type == SHORTCUT_TYPE_FILE) - { - struct ShortcutsActivateData *data; - GtkFileSystemVolume *volume; - - operation_mode_set (impl, OPERATION_MODE_BROWSE); - - volume = _gtk_file_system_get_volume_for_file (impl->priv->file_system, col_data); - - data = g_new0 (struct ShortcutsActivateData, 1); - data->impl = g_object_ref (impl); - data->file = g_object_ref (col_data); - - if (!volume || !_gtk_file_system_volume_is_mounted (volume)) - { - GMountOperation *mount_operation; - GtkWidget *toplevel; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl)); - - mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel)); - - impl->priv->shortcuts_activate_iter_cancellable = - _gtk_file_system_mount_enclosing_volume (impl->priv->file_system, col_data, - mount_operation, - shortcuts_activate_mount_enclosing_volume, - data); - } - else - { - impl->priv->shortcuts_activate_iter_cancellable = - _gtk_file_system_get_info (impl->priv->file_system, data->file, - "standard::type", - shortcuts_activate_get_info_cb, data); - } - } - else if (shortcut_type == SHORTCUT_TYPE_SEARCH) - { - operation_mode_set (impl, OPERATION_MODE_SEARCH); - } - else if (shortcut_type == SHORTCUT_TYPE_RECENT) - { - operation_mode_set (impl, OPERATION_MODE_RECENT); - } -} - -/* Handler for GtkWidget::key-press-event on the shortcuts list */ -static gboolean -shortcuts_key_press_event_cb (GtkWidget *widget, - GdkEventKey *event, - GtkFileChooserDefault *impl) -{ - guint modifiers; - - modifiers = gtk_accelerator_get_default_mod_mask (); - - if (key_is_left_or_right (event)) - { - gtk_widget_grab_focus (impl->priv->browse_files_tree_view); - return TRUE; - } - - if ((event->keyval == GDK_KEY_BackSpace - || event->keyval == GDK_KEY_Delete - || event->keyval == GDK_KEY_KP_Delete) - && (event->state & modifiers) == 0) - { - remove_selected_bookmarks (impl); - return TRUE; - } - - if ((event->keyval == GDK_KEY_F2) - && (event->state & modifiers) == 0) - { - rename_selected_bookmark (impl); - return TRUE; - } - - return FALSE; -} - -static gboolean -shortcuts_select_func (GtkTreeSelection *selection, - GtkTreeModel *model, - GtkTreePath *path, - gboolean path_currently_selected, - gpointer data) -{ - GtkFileChooserDefault *impl = data; - GtkTreeIter filter_iter; - ShortcutType shortcut_type; - - if (!gtk_tree_model_get_iter (impl->priv->shortcuts_pane_filter_model, &filter_iter, path)) - g_assert_not_reached (); - - gtk_tree_model_get (impl->priv->shortcuts_pane_filter_model, &filter_iter, SHORTCUTS_COL_TYPE, &shortcut_type, -1); - - return shortcut_type != SHORTCUT_TYPE_SEPARATOR; -} - static gboolean list_select_func (GtkTreeSelection *selection, GtkTreeModel *model, @@ -9313,9 +6916,10 @@ list_select_func (GtkTreeSelection *selection, gpointer data) { GtkFileChooserDefault *impl = data; + GtkFileChooserDefaultPrivate *priv = impl->priv; - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { GtkTreeIter iter; gboolean is_sensitive; @@ -9338,9 +6942,11 @@ static void list_selection_changed (GtkTreeSelection *selection, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + /* See if we are in the new folder editable row for Save mode */ - if (impl->priv->operation_mode == OPERATION_MODE_BROWSE && - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (priv->operation_mode == OPERATION_MODE_BROWSE && + priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) { GFileInfo *info; gboolean had_selection; @@ -9355,13 +6961,12 @@ list_selection_changed (GtkTreeSelection *selection, out: - if (impl->priv->location_entry) + if (priv->location_entry) update_chooser_entry (impl); path_bar_update (impl); check_preview_change (impl); - bookmarks_check_add_sensitivity (impl); check_copy_file_location_sensitivity (impl); g_signal_emit_by_name (impl, "selection-changed", 0); @@ -9374,6 +6979,7 @@ list_row_activated (GtkTreeView *tree_view, GtkTreeViewColumn *column, GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GFile *file; GtkTreeIter iter; GtkTreeModel *model; @@ -9397,8 +7003,8 @@ list_row_activated (GtkTreeView *tree_view, goto out; } - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) g_signal_emit_by_name (impl, "file-activated"); out: @@ -9431,6 +7037,7 @@ path_bar_clicked (GtkPathBar *path_bar, static void update_cell_renderer_attributes (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GList *walk, *list; @@ -9438,7 +7045,7 @@ update_cell_renderer_attributes (GtkFileChooserDefault *impl) /* Keep the following column numbers in sync with create_file_list() */ /* name */ - column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), 0); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->browse_files_tree_view), 0); list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); for (walk = list; walk; walk = walk->next) { @@ -9462,7 +7069,7 @@ update_cell_renderer_attributes (GtkFileChooserDefault *impl) g_list_free (list); /* size */ - column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), 1); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->browse_files_tree_view), 1); list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); renderer = list->data; gtk_tree_view_column_set_attributes (column, renderer, @@ -9473,7 +7080,7 @@ update_cell_renderer_attributes (GtkFileChooserDefault *impl) g_list_free (list); /* mtime */ - column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->priv->browse_files_tree_view), 2); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (priv->browse_files_tree_view), 2); list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); renderer = list->data; gtk_tree_view_column_set_attributes (column, renderer, @@ -9493,34 +7100,38 @@ static void location_set_user_text (GtkFileChooserDefault *impl, const gchar *path) { - gtk_entry_set_text (GTK_ENTRY (impl->priv->location_entry), path); - gtk_editable_set_position (GTK_EDITABLE (impl->priv->location_entry), -1); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + gtk_entry_set_text (GTK_ENTRY (priv->location_entry), path); + gtk_editable_set_position (GTK_EDITABLE (priv->location_entry), -1); } static void location_popup_handler (GtkFileChooserDefault *impl, const gchar *path) { - if (impl->priv->operation_mode != OPERATION_MODE_BROWSE) + GtkFileChooserDefaultPrivate *priv = impl->priv; + + if (priv->operation_mode != OPERATION_MODE_BROWSE) { GtkWidget *widget_to_focus; operation_mode_set (impl, OPERATION_MODE_BROWSE); - if (impl->priv->current_folder) - change_folder_and_display_error (impl, impl->priv->current_folder, FALSE); + if (priv->current_folder) + change_folder_and_display_error (impl, priv->current_folder, FALSE); - if (impl->priv->location_mode == LOCATION_MODE_PATH_BAR) - widget_to_focus = impl->priv->browse_files_tree_view; + if (priv->location_mode == LOCATION_MODE_PATH_BAR) + widget_to_focus = priv->browse_files_tree_view; else - widget_to_focus = impl->priv->location_entry; + widget_to_focus = priv->location_entry; gtk_widget_grab_focus (widget_to_focus); return; } - if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) + if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || + priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) { if (!path) return; @@ -9528,10 +7139,10 @@ location_popup_handler (GtkFileChooserDefault *impl, location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE); location_set_user_text (impl, path); } - else if (impl->priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || - impl->priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE || + priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { - gtk_widget_grab_focus (impl->priv->location_entry); + gtk_widget_grab_focus (priv->location_entry); if (path != NULL) location_set_user_text (impl, path); } @@ -9543,162 +7154,107 @@ location_popup_handler (GtkFileChooserDefault *impl, static void up_folder_handler (GtkFileChooserDefault *impl) { - _gtk_path_bar_up (GTK_PATH_BAR (impl->priv->browse_path_bar)); + GtkFileChooserDefaultPrivate *priv = impl->priv; + + _gtk_path_bar_up (GTK_PATH_BAR (priv->browse_path_bar)); } /* Handler for the "down-folder" keybinding signal */ static void down_folder_handler (GtkFileChooserDefault *impl) { - _gtk_path_bar_down (GTK_PATH_BAR (impl->priv->browse_path_bar)); -} - -/* Switches to the shortcut in the specified index */ -static void -switch_to_shortcut (GtkFileChooserDefault *impl, - int pos) -{ - GtkTreeIter iter; - - if (!gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (impl->priv->shortcuts_model), &iter, NULL, pos)) - g_assert_not_reached (); + GtkFileChooserDefaultPrivate *priv = impl->priv; - shortcuts_activate_iter (impl, &iter); + _gtk_path_bar_down (GTK_PATH_BAR (priv->browse_path_bar)); } /* Handler for the "home-folder" keybinding signal */ static void home_folder_handler (GtkFileChooserDefault *impl) { - if (impl->priv->has_home) - switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_HOME)); + switch_to_home_dir (impl); } /* Handler for the "desktop-folder" keybinding signal */ static void desktop_folder_handler (GtkFileChooserDefault *impl) { - if (impl->priv->has_desktop) - switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_DESKTOP)); + const char *name; + + name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); + /* "To disable a directory, point it to the homedir." + * See http://freedesktop.org/wiki/Software/xdg-user-dirs + **/ + if (!g_strcmp0 (name, g_get_home_dir ())) { + return; + } + + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), name); } /* Handler for the "search-shortcut" keybinding signal */ static void search_shortcut_handler (GtkFileChooserDefault *impl) { - if (impl->priv->has_search) - { - switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_SEARCH)); + GtkFileChooserDefaultPrivate *priv = impl->priv; - /* we want the entry widget to grab the focus the first - * time, not the browse_files_tree_view widget. - */ - if (impl->priv->search_entry) - gtk_widget_grab_focus (impl->priv->search_entry); - } + operation_mode_set (impl, OPERATION_MODE_SEARCH); + + /* we want the entry widget to grab the focus the first + * time, not the browse_files_tree_view widget. + */ + if (priv->search_entry) + gtk_widget_grab_focus (priv->search_entry); } /* Handler for the "recent-shortcut" keybinding signal */ static void recent_shortcut_handler (GtkFileChooserDefault *impl) { - switch_to_shortcut (impl, shortcuts_get_index (impl, SHORTCUTS_RECENT)); + operation_mode_set (impl, OPERATION_MODE_RECENT); } static void quick_bookmark_handler (GtkFileChooserDefault *impl, gint bookmark_index) { - int bookmark_pos; - GtkTreePath *path; - - if (bookmark_index < 0 || bookmark_index >= impl->priv->num_bookmarks) - return; - - bookmark_pos = shortcuts_get_index (impl, SHORTCUTS_BOOKMARKS) + bookmark_index; + GtkFileChooserDefaultPrivate *priv = impl->priv; + GFile *file; - path = gtk_tree_path_new_from_indices (bookmark_pos, -1); - gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view), - path, NULL, - FALSE, 0.0, 0.0); - gtk_tree_path_free (path); + file = gtk_places_sidebar_get_nth_bookmark (GTK_PLACES_SIDEBAR (priv->places_sidebar), bookmark_index); - switch_to_shortcut (impl, bookmark_pos); + if (file) + { + change_folder_and_display_error (impl, file, FALSE); + g_object_unref (file); + } } static void show_hidden_handler (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv = impl->priv; + g_object_set (impl, - "show-hidden", !impl->priv->show_hidden, + "show-hidden", !priv->show_hidden, NULL); } -/******************************************************************** - * ShortcutsPaneModelFilter * - ********************************************************************/ static void -_shortcuts_pane_model_filter_class_init (ShortcutsPaneModelFilterClass *class) -{ -} - -static void -_shortcuts_pane_model_filter_init (ShortcutsPaneModelFilter *model) -{ - model->impl = NULL; -} - -/* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */ -static gboolean -shortcuts_pane_model_filter_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - ShortcutsPaneModelFilter *model; - int pos; - int bookmarks_pos; - - model = SHORTCUTS_PANE_MODEL_FILTER (drag_source); - - pos = *gtk_tree_path_get_indices (path); - bookmarks_pos = shortcuts_get_index (model->impl, SHORTCUTS_BOOKMARKS); - - return (pos >= bookmarks_pos && pos < bookmarks_pos + model->impl->priv->num_bookmarks); -} - -/* GtkTreeDragSource::drag_data_get implementation for the shortcuts - * filter model - */ -static gboolean -shortcuts_pane_model_filter_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data) -{ - /* FIXME */ - - return FALSE; -} - -/* Fill the GtkTreeDragSourceIface vtable */ -static void -shortcuts_pane_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface) +add_normal_and_shifted_binding (GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers, + const gchar *signal_name) { - iface->row_draggable = shortcuts_pane_model_filter_row_draggable; - iface->drag_data_get = shortcuts_pane_model_filter_drag_data_get; -} + gtk_binding_entry_add_signal (binding_set, + keyval, modifiers, + signal_name, 0); -#if 0 -/* Fill the GtkTreeDragDestIface vtable */ -static void -shortcuts_pane_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface) -{ - iface->drag_data_received = shortcuts_pane_model_filter_drag_data_received; - iface->row_drop_possible = shortcuts_pane_model_filter_row_drop_possible; + gtk_binding_entry_add_signal (binding_set, + keyval, modifiers | GDK_SHIFT_MASK, + signal_name, 0); } -#endif -/******************************************************************** - * Class/Instance Initializer * - ********************************************************************/ static void _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class) { @@ -9901,21 +7457,18 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class) g_type_class_add_private (gobject_class, sizeof (GtkFileChooserDefaultPrivate)); - /* Bind class to template - */ + /* Bind class to template */ + gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/gtkfilechooserdefault.ui"); /* A *lot* of widgets that we need to handle .... */ - gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_shortcuts_toolbar); - gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_shortcuts_swin); + gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_box); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_hpaned); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_header_box); - gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_shortcuts_tree_view); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_box); - gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_shortcuts_add_button); - gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_shortcuts_remove_button); + gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, places_sidebar); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_files_tree_view); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_new_folder_button); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_path_bar_hbox); @@ -9933,8 +7486,6 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class) gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_button); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_entry_box); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_label); - gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, shortcuts_model); - gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, shortcuts_pane_filter_model); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_name_column); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_pixbuf_renderer); gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_name_renderer); @@ -9942,23 +7493,6 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class) gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_size_column); /* And a *lot* of callbacks to bind ... */ - gtk_widget_class_bind_callback (widget_class, shortcuts_key_press_event_after_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_key_press_event_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_drag_drop_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_drag_data_received_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_drag_data_delete_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_drag_end_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_popup_menu_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_query_tooltip_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_button_press_event_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_drag_motion_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_drag_leave_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_drag_begin_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_selection_changed_cb); - gtk_widget_class_bind_callback (widget_class, shortcuts_editing_canceled); - gtk_widget_class_bind_callback (widget_class, shortcuts_edited); - gtk_widget_class_bind_callback (widget_class, add_bookmark_button_clicked_cb); - gtk_widget_class_bind_callback (widget_class, remove_bookmark_button_clicked_cb); gtk_widget_class_bind_callback (widget_class, browse_files_key_press_event_cb); gtk_widget_class_bind_callback (widget_class, file_list_drag_drop_cb); gtk_widget_class_bind_callback (widget_class, file_list_drag_data_received_cb); @@ -9974,75 +7508,22 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class) gtk_widget_class_bind_callback (widget_class, location_button_toggled_cb); gtk_widget_class_bind_callback (widget_class, new_folder_button_clicked); gtk_widget_class_bind_callback (widget_class, path_bar_clicked); -} - -static void -gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface) -{ - iface->select_file = gtk_file_chooser_default_select_file; - iface->unselect_file = gtk_file_chooser_default_unselect_file; - iface->select_all = gtk_file_chooser_default_select_all; - iface->unselect_all = gtk_file_chooser_default_unselect_all; - iface->get_files = gtk_file_chooser_default_get_files; - iface->get_preview_file = gtk_file_chooser_default_get_preview_file; - iface->get_file_system = gtk_file_chooser_default_get_file_system; - iface->set_current_folder = gtk_file_chooser_default_set_current_folder; - iface->get_current_folder = gtk_file_chooser_default_get_current_folder; - iface->set_current_name = gtk_file_chooser_default_set_current_name; - iface->add_filter = gtk_file_chooser_default_add_filter; - iface->remove_filter = gtk_file_chooser_default_remove_filter; - iface->list_filters = gtk_file_chooser_default_list_filters; - iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder; - iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder; - iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders; -} - -static void -gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface) -{ - iface->get_default_size = gtk_file_chooser_default_get_default_size; - iface->should_respond = gtk_file_chooser_default_should_respond; - iface->initial_focus = gtk_file_chooser_default_initial_focus; + gtk_widget_class_bind_callback (widget_class, places_sidebar_open_location_cb); + gtk_widget_class_bind_callback (widget_class, places_sidebar_show_error_message_cb); } static void post_process_ui (GtkFileChooserDefault *impl) { GtkTreeSelection *selection; - GtkStyleContext *context; GtkCellRenderer *cell; GList *cells; - /* Target types for dragging a row to/from the shortcuts list */ - const GtkTargetEntry tree_model_row_targets[] = { - { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW } - }; - - /* Shortcuts model needs a pointer back to the 'impl' */ - SHORTCUTS_PANE_MODEL_FILTER (impl->priv->shortcuts_pane_filter_model)->impl = impl; - /* Some qdata, qdata can't be set with GtkBuilder */ -#ifdef PROFILE_FILE_CHOOSER - g_object_set_data (G_OBJECT (impl->priv->browse_shortcuts_tree_view), "fmq-name", "shortcuts"); g_object_set_data (G_OBJECT (impl->priv->browse_files_tree_view), "fmq-name", "file_list"); -#endif g_object_set_data (G_OBJECT (impl->priv->browse_files_tree_view), I_("GtkFileChooserDefault"), impl); - /* Setup Places and File List Treeviews - */ - gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view), - GDK_BUTTON1_MASK, - tree_model_row_targets, - G_N_ELEMENTS (tree_model_row_targets), - GDK_ACTION_MOVE); - - gtk_drag_dest_set (impl->priv->browse_shortcuts_tree_view, - GTK_DEST_DEFAULT_ALL, - tree_model_row_targets, - G_N_ELEMENTS (tree_model_row_targets), - GDK_ACTION_COPY | GDK_ACTION_MOVE); - gtk_drag_dest_add_uri_targets (impl->priv->browse_shortcuts_tree_view); - + /* Setup file list treeview */ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view)); gtk_tree_selection_set_select_function (selection, list_select_func, @@ -10053,29 +7534,12 @@ post_process_ui (GtkFileChooserDefault *impl) GDK_ACTION_COPY | GDK_ACTION_MOVE); gtk_drag_source_add_uri_targets (impl->priv->browse_files_tree_view); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view)); - gtk_tree_selection_set_select_function (selection, - shortcuts_select_func, - impl, NULL); - - gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (impl->priv->browse_shortcuts_tree_view), - shortcuts_row_separator_func, - NULL, NULL); - gtk_drag_dest_set (impl->priv->browse_files_tree_view, GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY | GDK_ACTION_MOVE); gtk_drag_dest_add_uri_targets (impl->priv->browse_files_tree_view); - /* GtkStyleContext 'junctions' cannot be set with GtkBuilder */ - context = gtk_widget_get_style_context (impl->priv->browse_shortcuts_swin); - gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM); - - context = gtk_widget_get_style_context (impl->priv->browse_shortcuts_toolbar); - gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP); - /* 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. @@ -10109,43 +7573,43 @@ post_process_ui (GtkFileChooserDefault *impl) static void _gtk_file_chooser_default_init (GtkFileChooserDefault *impl) { + GtkFileChooserDefaultPrivate *priv; + profile_start ("start", NULL); #ifdef PROFILE_FILE_CHOOSER access ("MARK: *** CREATE FILE CHOOSER", F_OK); #endif - impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (impl, GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultPrivate); - - impl->priv->local_only = TRUE; - impl->priv->preview_widget_active = TRUE; - impl->priv->use_preview_label = TRUE; - impl->priv->select_multiple = FALSE; - impl->priv->show_hidden = FALSE; - impl->priv->show_size_column = TRUE; - impl->priv->icon_size = FALLBACK_ICON_SIZE; - impl->priv->load_state = LOAD_EMPTY; - impl->priv->reload_state = RELOAD_EMPTY; - impl->priv->pending_select_files = NULL; - impl->priv->location_mode = LOCATION_MODE_PATH_BAR; - impl->priv->operation_mode = OPERATION_MODE_BROWSE; - impl->priv->sort_column = MODEL_COL_NAME; - impl->priv->sort_order = GTK_SORT_ASCENDING; - impl->priv->recent_manager = gtk_recent_manager_get_default (); - impl->priv->create_folders = TRUE; + priv = impl->priv; + + priv->local_only = TRUE; + priv->preview_widget_active = TRUE; + priv->use_preview_label = TRUE; + priv->select_multiple = FALSE; + priv->show_hidden = FALSE; + priv->show_size_column = TRUE; + priv->icon_size = FALLBACK_ICON_SIZE; + priv->load_state = LOAD_EMPTY; + priv->reload_state = RELOAD_EMPTY; + priv->pending_select_files = NULL; + priv->location_mode = LOCATION_MODE_PATH_BAR; + priv->operation_mode = OPERATION_MODE_BROWSE; + priv->sort_column = MODEL_COL_NAME; + priv->sort_order = GTK_SORT_ASCENDING; + priv->recent_manager = gtk_recent_manager_get_default (); + priv->create_folders = TRUE; /* Ensure GTK+ private types used by the template * definition before calling gtk_widget_init_template() */ - g_type_ensure (SHORTCUTS_PANE_MODEL_FILTER_TYPE); g_type_ensure (GTK_TYPE_PATH_BAR); gtk_widget_init_template (GTK_WIDGET (impl)); set_file_system_backend (impl); - /* Populate the Places sidebar */ - shortcuts_model_populate (impl); + priv->bookmarks_manager = _gtk_bookmarks_manager_new (NULL, NULL); /* Setup various attributes and callbacks in the UI * which cannot be done with GtkBuilder. diff --git a/gtk/gtkfilechooserdefault.ui b/gtk/gtkfilechooserdefault.ui index 44124ca44d..5cf2ef3896 100644 --- a/gtk/gtkfilechooserdefault.ui +++ b/gtk/gtkfilechooserdefault.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <interface domain="gtk30"> - <!-- interface-requires gtkprivate 3.10 --> <!-- interface-requires gtk+ 3.10 --> + <!-- interface-requires gtkprivate 3.10 --> <object class="GtkImage" id="location_button_image"> <property name="visible">True</property> <property name="can_focus">False</property> @@ -31,7 +31,8 @@ <object class="GtkLabel" id="location_label"> <property name="visible">True</property> <property name="can_focus">False</property> - <property name="label" translatable="yes">Location:</property> + <property name="label" translatable="yes">_Location:</property> + <property name="use_underline">True</property> </object> <packing> <property name="expand">False</property> @@ -204,126 +205,13 @@ <property name="visible">True</property> <property name="can_focus">True</property> <child> - <object class="GtkBox" id="browse_shortcuts_box"> + <object class="GtkPlacesSidebar" id="places_sidebar"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <child> - <object class="GtkScrolledWindow" id="browse_shortcuts_swin"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkTreeView" id="browse_shortcuts_tree_view"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="has_tooltip">True</property> - <property name="model">shortcuts_pane_filter_model</property> - <property name="enable_search">False</property> - <child internal-child="accessible"> - <object class="AtkObject" id="browse_shortcuts_tree_view-atkobject"> - <property name="AtkObject::accessible-name" translatable="yes">Places</property> - </object> - </child> - <style> - <class name="sidebar"/> - </style> - <signal name="button-press-event" handler="shortcuts_button_press_event_cb" swapped="no"/> - <signal name="drag-begin" handler="shortcuts_drag_begin_cb" swapped="no"/> - <signal name="drag-data-delete" handler="shortcuts_drag_data_delete_cb" swapped="no"/> - <signal name="drag-data-received" handler="shortcuts_drag_data_received_cb" swapped="no"/> - <signal name="drag-drop" handler="shortcuts_drag_drop_cb" swapped="no"/> - <signal name="drag-end" handler="shortcuts_drag_end_cb" swapped="no"/> - <signal name="drag-leave" handler="shortcuts_drag_leave_cb" swapped="no"/> - <signal name="drag-motion" handler="shortcuts_drag_motion_cb" swapped="no"/> - <signal name="key-press-event" handler="shortcuts_key_press_event_after_cb" after="yes" swapped="no"/> - <signal name="key-press-event" handler="shortcuts_key_press_event_cb" swapped="no"/> - <signal name="popup-menu" handler="shortcuts_popup_menu_cb" swapped="no"/> - <signal name="query-tooltip" handler="shortcuts_query_tooltip_cb" swapped="no"/> - <child internal-child="selection"> - <object class="GtkTreeSelection" id="treeview-selection1"> - <signal name="changed" handler="shortcuts_selection_changed_cb" swapped="no"/> - </object> - </child> - <child> - <object class="GtkTreeViewColumn" id="treeviewcolumn1"> - <property name="title" translatable="yes">Places</property> - <child> - <object class="GtkCellRendererPixbuf" id="cellrendererpixbuf1"/> - <attributes> - <attribute name="visible">5</attribute> - <attribute name="pixbuf">0</attribute> - </attributes> - </child> - <child> - <object class="GtkCellRendererText" id="cellrenderertext1"> - <property name="ellipsize">end</property> - <property name="width_chars">12</property> - <signal name="edited" handler="shortcuts_edited" swapped="no"/> - <signal name="editing-canceled" handler="shortcuts_editing_canceled" swapped="no"/> - </object> - <attributes> - <attribute name="text">1</attribute> - </attributes> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkToolbar" id="browse_shortcuts_toolbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="toolbar_style">icons</property> - <style> - <class name="inline-toolbar"/> - </style> - <property name="icon_size">1</property> - <child> - <object class="GtkToolButton" id="browse_shortcuts_add_button"> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">Add the selected folder to the Bookmarks</property> - <property name="use_underline">True</property> - <property name="icon_name">list-add-symbolic</property> - <signal name="clicked" handler="add_bookmark_button_clicked_cb" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">True</property> - </packing> - </child> - <child> - <object class="GtkToolButton" id="browse_shortcuts_remove_button"> - <property name="visible">True</property> - <property name="sensitive">False</property> - <property name="can_focus">False</property> - <property name="tooltip_text" translatable="yes">Remove the selected bookmark</property> - <property name="use_underline">True</property> - <property name="icon_name">list-remove-symbolic</property> - <signal name="clicked" handler="remove_bookmark_button_clicked_cb" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="homogeneous">True</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">never</property> + <property name="shadow_type">in</property> + <signal name="open-location" handler="places_sidebar_open_location_cb" swapped="no"/> + <signal name="show-error-message" handler="places_sidebar_show_error_message_cb" swapped="no"/> </object> <packing> <property name="resize">False</property> @@ -524,32 +412,4 @@ <widget name="browse_new_folder_button"/> </widgets> </object> - <object class="GtkSizeGroup" id="paned_group"> - <property name="mode">vertical</property> - <widgets> - <widget name="browse_shortcuts_swin"/> - <widget name="browse_files_swin"/> - </widgets> - </object> - <object class="GtkListStore" id="shortcuts_model"> - <columns> - <!-- column-name pixbuf --> - <column type="GdkPixbuf"/> - <!-- column-name name --> - <column type="gchararray"/> - <!-- column-name path-or-volume --> - <column type="gpointer"/> - <!-- column-name shortcut-type --> - <column type="gint"/> - <!-- column-name removable --> - <column type="gboolean"/> - <!-- column-name pixbuf-visible --> - <column type="gboolean"/> - <!-- column-name cancellable --> - <column type="gpointer"/> - </columns> - </object> - <object class="ShortcutsPaneModelFilter" id="shortcuts_pane_filter_model"> - <property name="child_model">shortcuts_model</property> - </object> </interface> diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c index 6d32a98ba4..64d50390ba 100644 --- a/gtk/gtkfilechooserdialog.c +++ b/gtk/gtkfilechooserdialog.c @@ -193,6 +193,14 @@ */ +struct _GtkFileChooserDialogPrivate +{ + GtkWidget *widget; + + /* for use with GtkFileChooserEmbed */ + gboolean response_requested; +}; + #define GTK_FILE_CHOOSER_DIALOG_GET_PRIVATE(o) (GTK_FILE_CHOOSER_DIALOG (o)->priv) static void gtk_file_chooser_dialog_set_property (GObject *object, diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h index 34ff0f0f66..61a3aac6db 100644 --- a/gtk/gtkfilechooserprivate.h +++ b/gtk/gtkfilechooserprivate.h @@ -19,6 +19,7 @@ #ifndef __GTK_FILE_CHOOSER_PRIVATE_H__ #define __GTK_FILE_CHOOSER_PRIVATE_H__ +#include "gtkbookmarksmanager.h" #include "gtkfilechooser.h" #include "gtkfilesystem.h" #include "gtkfilesystemmodel.h" @@ -42,11 +43,11 @@ G_BEGIN_DECLS #define SETTINGS_KEY_WINDOW_POSITION "window-position" #define SETTINGS_KEY_WINDOW_SIZE "window-size" #define SETTINGS_KEY_SIDEBAR_WIDTH "sidebar-width" +#define SETTINGS_KEY_STARTUP_MODE "startup-mode" #define GTK_FILE_CHOOSER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GTK_TYPE_FILE_CHOOSER, GtkFileChooserIface)) typedef struct _GtkFileChooserIface GtkFileChooserIface; -typedef struct _GtkFileChooserDefaultPrivate GtkFileChooserDefaultPrivate; struct _GtkFileChooserIface { @@ -101,58 +102,6 @@ gboolean _gtk_file_chooser_remove_shortcut_folder (GtkFileChooser *cho GError **error); GSList * _gtk_file_chooser_list_shortcut_folder_files (GtkFileChooser *chooser); -/* GtkFileChooserDialog private */ - -struct _GtkFileChooserDialogPrivate -{ - GtkWidget *widget; - - /* for use with GtkFileChooserEmbed */ - gboolean response_requested; -}; - - -/* GtkFileChooserWidget private */ - -struct _GtkFileChooserWidgetPrivate -{ - GtkWidget *impl; - - char *file_system; -}; - - -/* GtkFileChooserDefault private */ - -typedef enum { - LOAD_EMPTY, /* There is no model */ - LOAD_PRELOAD, /* Model is loading and a timer is running; model isn't inserted into the tree yet */ - LOAD_LOADING, /* Timeout expired, model is inserted into the tree, but not fully loaded yet */ - LOAD_FINISHED /* Model is fully loaded and inserted into the tree */ -} LoadState; - -typedef enum { - RELOAD_EMPTY, /* No folder has been set */ - RELOAD_HAS_FOLDER /* We have a folder, although it may not be completely loaded yet; no need to reload */ -} ReloadState; - -typedef enum { - LOCATION_MODE_PATH_BAR, - LOCATION_MODE_FILENAME_ENTRY -} LocationMode; - -typedef enum { - OPERATION_MODE_BROWSE, - OPERATION_MODE_SEARCH, - OPERATION_MODE_RECENT -} OperationMode; - -struct _GtkFileChooserDefault -{ - GtkBox parent_instance; - - GtkFileChooserDefaultPrivate *priv; -}; G_END_DECLS diff --git a/gtk/gtkfilechooserwidget.c b/gtk/gtkfilechooserwidget.c index 011fe6a3a0..bce30977ba 100644 --- a/gtk/gtkfilechooserwidget.c +++ b/gtk/gtkfilechooserwidget.c @@ -45,11 +45,13 @@ * #GtkFileChooser. */ +struct _GtkFileChooserWidgetPrivate +{ + GtkWidget *impl; +}; #define GTK_FILE_CHOOSER_WIDGET_GET_PRIVATE(o) (GTK_FILE_CHOOSER_WIDGET (o)->priv) -static void gtk_file_chooser_widget_finalize (GObject *object); - static GObject* gtk_file_chooser_widget_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params); @@ -76,7 +78,6 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class) gobject_class->constructor = gtk_file_chooser_widget_constructor; gobject_class->set_property = gtk_file_chooser_widget_set_property; gobject_class->get_property = gtk_file_chooser_widget_get_property; - gobject_class->finalize = gtk_file_chooser_widget_finalize; _gtk_file_chooser_install_properties (gobject_class); @@ -94,16 +95,6 @@ gtk_file_chooser_widget_init (GtkFileChooserWidget *chooser_widget) GTK_ORIENTATION_VERTICAL); } -static void -gtk_file_chooser_widget_finalize (GObject *object) -{ - GtkFileChooserWidget *chooser = GTK_FILE_CHOOSER_WIDGET (object); - - g_free (chooser->priv->file_system); - - G_OBJECT_CLASS (gtk_file_chooser_widget_parent_class)->finalize (object); -} - static GObject* gtk_file_chooser_widget_constructor (GType type, guint n_construct_properties, diff --git a/gtk/gtkfilesystem.c b/gtk/gtkfilesystem.c index 2fdfa3df9d..186992ca0e 100644 --- a/gtk/gtkfilesystem.c +++ b/gtk/gtkfilesystem.c @@ -55,7 +55,6 @@ enum { }; enum { - BOOKMARKS_CHANGED, VOLUMES_CHANGED, FS_LAST_SIGNAL }; @@ -81,11 +80,6 @@ struct GtkFileSystemPrivate * of type GDrive, GVolume and GMount */ GSList *volumes; - - /* This list contains GtkFileSystemBookmark structs */ - GSList *bookmarks; - - GFileMonitor *bookmarks_monitor; }; struct AsyncFuncData @@ -99,24 +93,9 @@ struct AsyncFuncData gpointer data; }; -struct GtkFileSystemBookmark -{ - GFile *file; - gchar *label; -}; - G_DEFINE_TYPE (GtkFileSystem, _gtk_file_system, G_TYPE_OBJECT) -/* GtkFileSystemBookmark methods */ -void -_gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark) -{ - g_object_unref (bookmark->file); - g_free (bookmark->label); - g_slice_free (GtkFileSystemBookmark, bookmark); -} - /* GtkFileSystem methods */ static void volumes_changed (GVolumeMonitor *volume_monitor, @@ -158,41 +137,11 @@ gtk_file_system_dispose (GObject *object) } static void -gtk_file_system_finalize (GObject *object) -{ - GtkFileSystem *file_system = GTK_FILE_SYSTEM (object); - GtkFileSystemPrivate *priv = file_system->priv; - - DEBUG ("finalize"); - - if (priv->bookmarks_monitor) - g_object_unref (priv->bookmarks_monitor); - - if (priv->bookmarks) - { - g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL); - g_slist_free (priv->bookmarks); - } - - G_OBJECT_CLASS (_gtk_file_system_parent_class)->finalize (object); -} - -static void _gtk_file_system_class_init (GtkFileSystemClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); object_class->dispose = gtk_file_system_dispose; - object_class->finalize = gtk_file_system_finalize; - - fs_signals[BOOKMARKS_CHANGED] = - g_signal_new ("bookmarks-changed", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileSystemClass, bookmarks_changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); fs_signals[VOLUMES_CHANGED] = g_signal_new ("volumes-changed", @@ -206,155 +155,6 @@ _gtk_file_system_class_init (GtkFileSystemClass *class) g_type_class_add_private (object_class, sizeof (GtkFileSystemPrivate)); } -static GFile * -get_legacy_bookmarks_file (void) -{ - GFile *file; - gchar *filename; - - filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL); - file = g_file_new_for_path (filename); - g_free (filename); - - return file; -} - -static GFile * -get_bookmarks_file (void) -{ - GFile *file; - gchar *filename; - - filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "bookmarks", NULL); - file = g_file_new_for_path (filename); - g_free (filename); - - return file; -} - -static GSList * -read_bookmarks (GFile *file) -{ - gchar *contents; - gchar **lines, *space; - GSList *bookmarks = NULL; - gint i; - - if (!g_file_load_contents (file, NULL, &contents, - NULL, NULL, NULL)) - return NULL; - - lines = g_strsplit (contents, "\n", -1); - - for (i = 0; lines[i]; i++) - { - GtkFileSystemBookmark *bookmark; - - if (!*lines[i]) - continue; - - if (!g_utf8_validate (lines[i], -1, NULL)) - continue; - - bookmark = g_slice_new0 (GtkFileSystemBookmark); - - if ((space = strchr (lines[i], ' ')) != NULL) - { - space[0] = '\0'; - bookmark->label = g_strdup (space + 1); - } - - bookmark->file = g_file_new_for_uri (lines[i]); - bookmarks = g_slist_prepend (bookmarks, bookmark); - } - - bookmarks = g_slist_reverse (bookmarks); - g_strfreev (lines); - g_free (contents); - - return bookmarks; -} - -static void -save_bookmarks (GFile *bookmarks_file, - GSList *bookmarks) -{ - GError *error = NULL; - GString *contents; - GSList *l; - GFile *parent_file; - gchar *path; - - contents = g_string_new (""); - - for (l = bookmarks; l; l = l->next) - { - GtkFileSystemBookmark *bookmark = l->data; - gchar *uri; - - uri = g_file_get_uri (bookmark->file); - if (!uri) - continue; - - g_string_append (contents, uri); - - if (bookmark->label) - g_string_append_printf (contents, " %s", bookmark->label); - - g_string_append_c (contents, '\n'); - g_free (uri); - } - - parent_file = g_file_get_parent (bookmarks_file); - path = g_file_get_path (parent_file); - if (g_mkdir_with_parents (path, 0700) == 0) - { - if (!g_file_replace_contents (bookmarks_file, - contents->str, - strlen (contents->str), - NULL, FALSE, 0, NULL, - NULL, &error)) - { - g_critical ("%s", error->message); - g_error_free (error); - } - } - g_free (path); - g_object_unref (parent_file); - g_string_free (contents, TRUE); -} - -static void -bookmarks_file_changed (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event, - gpointer data) -{ - GtkFileSystem *file_system = GTK_FILE_SYSTEM (data); - GtkFileSystemPrivate *priv = file_system->priv; - - switch (event) - { - case G_FILE_MONITOR_EVENT_CHANGED: - case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_DELETED: - g_slist_foreach (priv->bookmarks, (GFunc) _gtk_file_system_bookmark_free, NULL); - g_slist_free (priv->bookmarks); - - priv->bookmarks = read_bookmarks (file); - - gdk_threads_enter (); - g_signal_emit (data, fs_signals[BOOKMARKS_CHANGED], 0); - gdk_threads_leave (); - break; - default: - /* ignore at the moment */ - break; - } -} - static gboolean mount_referenced_by_volume_activation_root (GList *volumes, GMount *mount) { @@ -533,8 +333,6 @@ static void _gtk_file_system_init (GtkFileSystem *file_system) { GtkFileSystemPrivate *priv; - GFile *bookmarks_file; - GError *error = NULL; DEBUG ("init"); @@ -564,35 +362,6 @@ _gtk_file_system_init (GtkFileSystem *file_system) G_CALLBACK (volumes_changed), file_system); g_signal_connect (priv->volume_monitor, "drive-changed", G_CALLBACK (volumes_changed), file_system); - - /* Bookmarks */ - bookmarks_file = get_bookmarks_file (); - priv->bookmarks = read_bookmarks (bookmarks_file); - if (!priv->bookmarks) - { - GFile *legacy_bookmarks_file; - - /* Read the legacy one and write it to the new one */ - legacy_bookmarks_file = get_legacy_bookmarks_file (); - priv->bookmarks = read_bookmarks (legacy_bookmarks_file); - save_bookmarks (bookmarks_file, priv->bookmarks); - - g_object_unref (legacy_bookmarks_file); - } - - priv->bookmarks_monitor = g_file_monitor_file (bookmarks_file, - G_FILE_MONITOR_NONE, - NULL, &error); - if (error) - { - g_warning ("%s", error->message); - g_error_free (error); - } - else - g_signal_connect (priv->bookmarks_monitor, "changed", - G_CALLBACK (bookmarks_file_changed), file_system); - - g_object_unref (bookmarks_file); } /* GtkFileSystem public methods */ @@ -622,29 +391,6 @@ _gtk_file_system_list_volumes (GtkFileSystem *file_system) return list; } -GSList * -_gtk_file_system_list_bookmarks (GtkFileSystem *file_system) -{ - GtkFileSystemPrivate *priv = file_system->priv; - GSList *bookmarks, *files = NULL; - - DEBUG ("list_bookmarks"); - - bookmarks = priv->bookmarks; - - while (bookmarks) - { - GtkFileSystemBookmark *bookmark; - - bookmark = bookmarks->data; - bookmarks = bookmarks->next; - - files = g_slist_prepend (files, g_object_ref (bookmark->file)); - } - - return g_slist_reverse (files); -} - static void free_async_data (AsyncFuncData *async_data) { @@ -871,185 +617,6 @@ _gtk_file_system_mount_enclosing_volume (GtkFileSystem *file return cancellable; } -gboolean -_gtk_file_system_insert_bookmark (GtkFileSystem *file_system, - GFile *file, - gint position, - GError **error) -{ - GtkFileSystemPrivate *priv = file_system->priv; - GSList *bookmarks; - GtkFileSystemBookmark *bookmark; - gboolean result = TRUE; - GFile *bookmarks_file; - - bookmarks = priv->bookmarks; - - while (bookmarks) - { - bookmark = bookmarks->data; - bookmarks = bookmarks->next; - - if (g_file_equal (bookmark->file, file)) - { - /* File is already in bookmarks */ - result = FALSE; - break; - } - } - - if (!result) - { - gchar *uri = g_file_get_uri (file); - - g_set_error (error, - GTK_FILE_CHOOSER_ERROR, - GTK_FILE_CHOOSER_ERROR_ALREADY_EXISTS, - "%s already exists in the bookmarks list", - uri); - - g_free (uri); - - return FALSE; - } - - bookmark = g_slice_new0 (GtkFileSystemBookmark); - bookmark->file = g_object_ref (file); - - priv->bookmarks = g_slist_insert (priv->bookmarks, bookmark, position); - - bookmarks_file = get_bookmarks_file (); - save_bookmarks (bookmarks_file, priv->bookmarks); - g_object_unref (bookmarks_file); - - g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0); - - return TRUE; -} - -gboolean -_gtk_file_system_remove_bookmark (GtkFileSystem *file_system, - GFile *file, - GError **error) -{ - GtkFileSystemPrivate *priv = file_system->priv; - GtkFileSystemBookmark *bookmark; - GSList *bookmarks; - gboolean result = FALSE; - GFile *bookmarks_file; - - if (!priv->bookmarks) - return FALSE; - - bookmarks = priv->bookmarks; - - while (bookmarks) - { - bookmark = bookmarks->data; - - if (g_file_equal (bookmark->file, file)) - { - result = TRUE; - priv->bookmarks = g_slist_remove_link (priv->bookmarks, bookmarks); - _gtk_file_system_bookmark_free (bookmark); - g_slist_free_1 (bookmarks); - break; - } - - bookmarks = bookmarks->next; - } - - if (!result) - { - gchar *uri = g_file_get_uri (file); - - g_set_error (error, - GTK_FILE_CHOOSER_ERROR, - GTK_FILE_CHOOSER_ERROR_NONEXISTENT, - "%s does not exist in the bookmarks list", - uri); - - g_free (uri); - - return FALSE; - } - - bookmarks_file = get_bookmarks_file (); - save_bookmarks (bookmarks_file, priv->bookmarks); - g_object_unref (bookmarks_file); - - g_signal_emit (file_system, fs_signals[BOOKMARKS_CHANGED], 0); - - return TRUE; -} - -gchar * -_gtk_file_system_get_bookmark_label (GtkFileSystem *file_system, - GFile *file) -{ - GtkFileSystemPrivate *priv = file_system->priv; - GSList *bookmarks; - gchar *label = NULL; - - DEBUG ("get_bookmark_label"); - - bookmarks = priv->bookmarks; - - while (bookmarks) - { - GtkFileSystemBookmark *bookmark; - - bookmark = bookmarks->data; - bookmarks = bookmarks->next; - - if (g_file_equal (file, bookmark->file)) - { - label = g_strdup (bookmark->label); - break; - } - } - - return label; -} - -void -_gtk_file_system_set_bookmark_label (GtkFileSystem *file_system, - GFile *file, - const gchar *label) -{ - GtkFileSystemPrivate *priv = file_system->priv; - gboolean changed = FALSE; - GFile *bookmarks_file; - GSList *bookmarks; - - DEBUG ("set_bookmark_label"); - - bookmarks = priv->bookmarks; - - while (bookmarks) - { - GtkFileSystemBookmark *bookmark; - - bookmark = bookmarks->data; - bookmarks = bookmarks->next; - - if (g_file_equal (file, bookmark->file)) - { - g_free (bookmark->label); - bookmark->label = g_strdup (label); - changed = TRUE; - break; - } - } - - bookmarks_file = get_bookmarks_file (); - save_bookmarks (bookmarks_file, priv->bookmarks); - g_object_unref (bookmarks_file); - - if (changed) - g_signal_emit_by_name (file_system, "bookmarks-changed", 0); -} - GtkFileSystemVolume * _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system, GFile *file) diff --git a/gtk/gtkfilesystem.h b/gtk/gtkfilesystem.h index 03782d6f7f..c24ae29623 100644 --- a/gtk/gtkfilesystem.h +++ b/gtk/gtkfilesystem.h @@ -37,7 +37,6 @@ typedef struct GtkFileSystemClass GtkFileSystemClass; typedef struct GtkFileSystemVolume GtkFileSystemVolume; /* opaque struct */ -typedef struct GtkFileSystemBookmark GtkFileSystemBookmark; /* opaque struct */ struct GtkFileSystem @@ -51,7 +50,6 @@ struct GtkFileSystemClass { GObjectClass parent_class; - void (*bookmarks_changed) (GtkFileSystem *file_system); void (*volumes_changed) (GtkFileSystem *file_system); }; @@ -71,7 +69,6 @@ GType _gtk_file_system_get_type (void) G_GNUC_CONST; GtkFileSystem * _gtk_file_system_new (void); GSList * _gtk_file_system_list_volumes (GtkFileSystem *file_system); -GSList * _gtk_file_system_list_bookmarks (GtkFileSystem *file_system); GCancellable * _gtk_file_system_get_info (GtkFileSystem *file_system, GFile *file, @@ -89,20 +86,6 @@ GCancellable * _gtk_file_system_mount_enclosing_volume (GtkFileSystem GtkFileSystemVolumeMountCallback callback, gpointer data); -gboolean _gtk_file_system_insert_bookmark (GtkFileSystem *file_system, - GFile *file, - gint position, - GError **error); -gboolean _gtk_file_system_remove_bookmark (GtkFileSystem *file_system, - GFile *file, - GError **error); - -gchar * _gtk_file_system_get_bookmark_label (GtkFileSystem *file_system, - GFile *file); -void _gtk_file_system_set_bookmark_label (GtkFileSystem *file_system, - GFile *file, - const gchar *label); - GtkFileSystemVolume * _gtk_file_system_get_volume_for_file (GtkFileSystem *file_system, GFile *file); @@ -118,9 +101,6 @@ GdkPixbuf * _gtk_file_system_volume_render_icon (GtkFileSystemVol GtkFileSystemVolume *_gtk_file_system_volume_ref (GtkFileSystemVolume *volume); void _gtk_file_system_volume_unref (GtkFileSystemVolume *volume); -/* GtkFileSystemBookmark methods */ -void _gtk_file_system_bookmark_free (GtkFileSystemBookmark *bookmark); - /* GFileInfo helper functions */ GdkPixbuf * _gtk_file_info_render_icon (GFileInfo *info, GtkWidget *widget, diff --git a/gtk/gtkgladecatalog.c b/gtk/gtkgladecatalog.c index 310aa2b84c..30e0e6cfef 100644 --- a/gtk/gtkgladecatalog.c +++ b/gtk/gtkgladecatalog.c @@ -30,7 +30,6 @@ /* Some forward declarations of internal types */ GType _gtk_scale_button_scale_get_type (void); -GType _shortcuts_pane_model_filter_get_type (void); /* This function is referred to in gtk/glade/gtk-private-widgets.xml * and is used to ensure the private types for use in Glade while @@ -44,7 +43,6 @@ gtk_glade_catalog_init (const gchar *catalog_name) g_type_ensure (GTK_TYPE_COLOR_PLANE); g_type_ensure (GTK_TYPE_COLOR_SCALE); g_type_ensure (_gtk_scale_button_scale_get_type ()); - g_type_ensure (_shortcuts_pane_model_filter_get_type ()); #ifdef G_OS_UNIX g_type_ensure (GTK_TYPE_PRINTER_OPTION_WIDGET); diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index 14d0519023..31977fe845 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -83,12 +83,15 @@ VOID:OBJECT,BOOLEAN VOID:OBJECT,BOXED,BOXED VOID:OBJECT,BOXED,UINT,UINT VOID:OBJECT,BOXED,BOOLEAN,BOOLEAN +VOID:OBJECT,ENUM +VOID:OBJECT,FLAGS VOID:OBJECT,INT VOID:OBJECT,INT,OBJECT VOID:OBJECT,INT,INT VOID:OBJECT,INT,INT,BOXED,UINT,UINT VOID:OBJECT,OBJECT VOID:OBJECT,POINTER +VOID:OBJECT,POINTER,INT VOID:OBJECT,STRING VOID:OBJECT,STRING,STRING VOID:OBJECT,UINT @@ -97,6 +100,7 @@ VOID:OBJECT,STRING VOID:OBJECT,OBJECT,STRING VOID:OBJECT,OBJECT,OBJECT VOID:OBJECT,OBJECT,BOXED,STRING +VOID:OBJECT,OBJECT,POINTER,POINTER VOID:POINTER VOID:POINTER,INT VOID:POINTER,BOOLEAN @@ -120,3 +124,7 @@ VOID:UINT,UINT VOID:VOID OBJECT:OBJECT,INT,INT VOID:POINTER,POINTER,POINTER,POINTER,STRING +VOID:OBJECT,STRING,POINTER,POINTER +INT:INT +VOID:POINTER,STRING,INT +INT:OBJECT,OBJECT,POINTER diff --git a/gtk/gtkplacessidebar.c b/gtk/gtkplacessidebar.c new file mode 100644 index 0000000000..98cd47f1d8 --- /dev/null +++ b/gtk/gtkplacessidebar.c @@ -0,0 +1,4335 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * GtkPlacesSidebar - sidebar widget for places in the filesystem + * + * This code comes from Nautilus, GNOME's file manager. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk) + * Cosimo Cecchi <cosimoc@gnome.org> + * Federico Mena Quintero <federico@gnome.org> + * + */ + +/* TODO: + * + * * Fix instances of "#if DO_NOT_COMPILE" + * + * * Fix instances of "#if 0" + * + * * Fix FIXMEs + * + * * Grep for "NULL-GError" and see if they should be taken care of + * + * * Although we do g_mount_unmount_with_operation(), Nautilus used to do + * nautilus_file_operations_unmount_mount_full() to unmount a volume. With + * that, Nautilus does the "volume has trash, empty it first?" dance. Cosimo + * suggests that this logic should be part of GtkMountOperation, which can + * have Unix-specific code for emptying trash. + * + * * Sync nautilus commit 17a85b78acc78b573c2e1776b348ed348e19adb7 + * + */ + +#include "config.h" + +#include <gio/gio.h> + +#include "gdk/gdkkeysyms.h" +#include "gtkbookmarksmanager.h" +#include "gtkcelllayout.h" +#include "gtkcellrenderertext.h" +#include "gtkcellrendererpixbuf.h" +#include "gtkfilechooserprivate.h" +#include "gtkicontheme.h" +#include "gtkimagemenuitem.h" +#include "gtkintl.h" +#include "gtkmain.h" +#include "gtkmarshalers.h" +#include "gtkmenuitem.h" +#include "gtkmountoperation.h" +#include "gtkplacessidebar.h" +#include "gtkscrolledwindow.h" +#include "gtkseparatormenuitem.h" +#include "gtksettings.h" +#include "gtkstock.h" +#include "gtktrashmonitor.h" +#include "gtktreeselection.h" +#include "gtktreednd.h" +#include "gtktypebuiltins.h" +#include "gtkwindow.h" + +/** + * SECTION:gtkplacessidebar + * @Short_description: Sidebar that displays frequently-used places in the file system + * @Title: GtkPlacesSidebar + * @See_also: #GtkFileChooser + * + * #GtkPlacesSidebar is a widget that displays a list of frequently-used places in the + * file system: the user's home directory, the user's bookmarks, and volumes and drives. + * This widget is used as a sidebar in #GtkFileChooser and may be used by file managers + * and similar programs. + * + * The places sidebar displays drives and volumes, and will automatically mount + * or unmount them when the user selects them. + * + * Applications can hook to various signals in the places sidebar to customize + * its behavior. For example, they can add extra commands to the context menu + * of the sidebar. + * + * While bookmarks are completely in control of the user, the places sidebar also + * allows individual applications to provide extra shortcut folders that are unique + * to each application. For example, a Paint program may want to add a shortcut + * for a Clipart folder. You can do this with gtk_places_sidebar_add_shortcut(). + * + * To make use of the places sidebar, an application at least needs to connect + * to the #GtkPlacesSidebar::open-location signal. This is emitted when the + * user selects in the sidebar a location to open. The application should also + * call gtk_places_sidebar_set_location() when it changes the currently-viewed + * location. + */ + +#define EJECT_BUTTON_XPAD 6 +#define ICON_CELL_XPAD 6 + +#define DO_NOT_COMPILE 0 + +struct _GtkPlacesSidebar { + GtkScrolledWindow parent; + + GtkTreeView *tree_view; + GtkCellRenderer *eject_icon_cell_renderer; + GtkListStore *store; + GtkBookmarksManager *bookmarks_manager; + GVolumeMonitor *volume_monitor; + GtkTrashMonitor *trash_monitor; + + gulong trash_monitor_changed_id; + + gboolean devices_header_added; + gboolean bookmarks_header_added; + + /* DnD */ + GList *drag_list; /* list of GFile */ + gboolean drag_data_received; + int drag_data_info; + gboolean drop_occured; + + GtkWidget *popup_menu; + + /* volume mounting - delayed open process */ + gboolean mounting; + GtkPlacesOpenFlags go_to_after_mount_open_flags; + + GSList *shortcuts; + + GDBusProxy *hostnamed_proxy; + GCancellable *hostnamed_cancellable; + char *hostname; + + GtkPlacesOpenFlags open_flags; + + guint show_desktop : 1; +}; + +struct _GtkPlacesSidebarClass { + GtkScrolledWindowClass parent; + + void (* open_location) (GtkPlacesSidebar *sidebar, + GFile *location, + GtkPlacesOpenFlags open_flags); + void (* populate_popup) (GtkPlacesSidebar *sidebar, + GtkMenu *menu, + GFile *selected_item); + void (* show_error_message) (GtkPlacesSidebar *sidebar, + const char *primary, + const char *secondary); + GdkDragAction (* drag_action_requested) (GtkPlacesSidebar *sidebar, + GdkDragContext *context, + GFile *dest_file, + GList *source_file_list); + GdkDragAction (* drag_action_ask) (GtkPlacesSidebar *sidebar, + GdkDragAction actions); + void (* drag_perform_drop) (GtkPlacesSidebar *sidebar, + GFile *dest_file, + GList *source_file_list, + GdkDragAction action); +}; + +enum { + PLACES_SIDEBAR_COLUMN_ROW_TYPE, + PLACES_SIDEBAR_COLUMN_URI, + PLACES_SIDEBAR_COLUMN_DRIVE, + PLACES_SIDEBAR_COLUMN_VOLUME, + PLACES_SIDEBAR_COLUMN_MOUNT, + PLACES_SIDEBAR_COLUMN_NAME, + PLACES_SIDEBAR_COLUMN_GICON, + PLACES_SIDEBAR_COLUMN_INDEX, + PLACES_SIDEBAR_COLUMN_EJECT, + PLACES_SIDEBAR_COLUMN_NO_EJECT, + PLACES_SIDEBAR_COLUMN_BOOKMARK, + PLACES_SIDEBAR_COLUMN_TOOLTIP, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, + PLACES_SIDEBAR_COLUMN_HEADING_TEXT, + + PLACES_SIDEBAR_COLUMN_COUNT +}; + +typedef enum { + PLACES_BUILT_IN, + PLACES_XDG_DIR, + PLACES_MOUNTED_VOLUME, + PLACES_BOOKMARK, + PLACES_HEADING, +} PlaceType; + +typedef enum { + SECTION_DEVICES, + SECTION_BOOKMARKS, + SECTION_COMPUTER, + SECTION_NETWORK, +} SectionType; + +enum { + OPEN_LOCATION, + POPULATE_POPUP, + SHOW_ERROR_MESSAGE, + DRAG_ACTION_REQUESTED, + DRAG_ACTION_ASK, + DRAG_PERFORM_DROP, + LAST_SIGNAL, +}; + +enum { + PROP_LOCATION = 1, + PROP_OPEN_FLAGS, + PROP_SHOW_DESKTOP, + NUM_PROPERTIES, +}; + +/* Names for themed icons */ +#define ICON_NAME_HOME "user-home-symbolic" +#define ICON_NAME_DESKTOP "user-desktop" +#define ICON_NAME_FILESYSTEM "drive-harddisk-symbolic" +#define ICON_NAME_EJECT "media-eject-symbolic" +#define ICON_NAME_NETWORK "network-workgroup-symbolic" + +#define ICON_NAME_FOLDER_DESKTOP "user-desktop" +#define ICON_NAME_FOLDER_DOCUMENTS "folder-documents-symbolic" +#define ICON_NAME_FOLDER_DOWNLOAD "folder-download-symbolic" +#define ICON_NAME_FOLDER_MUSIC "folder-music-symbolic" +#define ICON_NAME_FOLDER_PICTURES "folder-pictures-symbolic" +#define ICON_NAME_FOLDER_PUBLIC_SHARE "folder-publicshare-symbolic" +#define ICON_NAME_FOLDER_TEMPLATES "folder-templates-symbolic" +#define ICON_NAME_FOLDER_VIDEOS "folder-videos-symbolic" +#define ICON_NAME_FOLDER_SAVED_SEARCH "folder-saved-search-symbolic" + +static guint places_sidebar_signals [LAST_SIGNAL] = { 0 }; +static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; + +static void open_selected_bookmark (GtkPlacesSidebar *sidebar, + GtkTreeModel *model, + GtkTreeIter *iter, + GtkPlacesOpenFlags open_flags); +static gboolean eject_or_unmount_bookmark (GtkPlacesSidebar *sidebar, + GtkTreePath *path); +static gboolean eject_or_unmount_selection (GtkPlacesSidebar *sidebar); +static void check_unmount_and_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_unmount, + gboolean *show_eject); + +/* Identifiers for target types */ +enum { + GTK_TREE_MODEL_ROW, + TEXT_URI_LIST +}; + +/* Target types for dragging from the shortcuts list */ +static const GtkTargetEntry dnd_source_targets[] = { + { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW } +}; + +/* Target types for dropping into the shortcuts list */ +static const GtkTargetEntry dnd_drop_targets [] = { + { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }, + { "text/uri-list", 0, TEXT_URI_LIST } +}; + +/* Drag and drop interface declarations */ +typedef struct { + GtkListStore parent; + + GtkPlacesSidebar *sidebar; +} ShortcutsModel; + +typedef struct { + GtkListStoreClass parent_class; +} ShortcutsModelClass; + +#define SHORTCUTS_MODEL_TYPE (shortcuts_model_get_type ()) +#define SHORTCUTS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_TYPE, ShortcutsModel)) + +static GType shortcuts_model_get_type (void); +static void shortcuts_model_drag_source_iface_init (GtkTreeDragSourceIface *iface); + +G_DEFINE_TYPE_WITH_CODE (ShortcutsModel, + shortcuts_model, + GTK_TYPE_LIST_STORE, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + shortcuts_model_drag_source_iface_init)); + +static GtkListStore *shortcuts_model_new (GtkPlacesSidebar *sidebar); + +G_DEFINE_TYPE (GtkPlacesSidebar, gtk_places_sidebar, GTK_TYPE_SCROLLED_WINDOW); + +static void +emit_open_location (GtkPlacesSidebar *sidebar, GFile *location, GtkPlacesOpenFlags open_flags) +{ + if ((open_flags & sidebar->open_flags) == 0) + open_flags = GTK_PLACES_OPEN_NORMAL; + + g_signal_emit (sidebar, places_sidebar_signals[OPEN_LOCATION], 0, + location, open_flags); +} + +static void +emit_populate_popup (GtkPlacesSidebar *sidebar, GtkMenu *menu, GFile *selected_item) +{ + g_signal_emit (sidebar, places_sidebar_signals[POPULATE_POPUP], 0, + menu, selected_item); +} + +static void +emit_show_error_message (GtkPlacesSidebar *sidebar, const char *primary, const char *secondary) +{ + g_signal_emit (sidebar, places_sidebar_signals[SHOW_ERROR_MESSAGE], 0, + primary, secondary); +} + +static GdkDragAction +emit_drag_action_requested (GtkPlacesSidebar *sidebar, + GdkDragContext *context, + GFile *dest_file, + GList *source_file_list) +{ + GdkDragAction ret_action; + + ret_action = 0; + + g_signal_emit (sidebar, places_sidebar_signals[DRAG_ACTION_REQUESTED], 0, + context, + dest_file, + source_file_list, + &ret_action); + + return ret_action; +} + +static GdkDragAction +emit_drag_action_ask (GtkPlacesSidebar *sidebar, + GdkDragAction actions) +{ + GdkDragAction ret_action; + + ret_action = 0; + + g_signal_emit (sidebar, places_sidebar_signals[DRAG_ACTION_ASK], 0, + actions, + &ret_action); + return ret_action; +} + +static void +emit_drag_perform_drop (GtkPlacesSidebar *sidebar, + GFile *dest_file, + GList *source_file_list, + GdkDragAction action) +{ + g_signal_emit (sidebar, places_sidebar_signals[DRAG_PERFORM_DROP], 0, + dest_file, + source_file_list, + action); +} + +static gint +get_icon_size (GtkPlacesSidebar *sidebar) +{ + GtkSettings *settings; + gint width, height; + + settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (sidebar))); + + if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height)) + return MAX (width, height); + else + return 16; +} + +static GtkTreeIter +add_heading (GtkPlacesSidebar *sidebar, + SectionType section_type, + const gchar *title) +{ + GtkTreeIter iter; + + gtk_list_store_append (sidebar->store, &iter); + gtk_list_store_set (sidebar->store, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_HEADING, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type, + PLACES_SIDEBAR_COLUMN_HEADING_TEXT, title, + PLACES_SIDEBAR_COLUMN_EJECT, FALSE, + PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE, + -1); + + return iter; +} + +static void +check_heading_for_section (GtkPlacesSidebar *sidebar, + SectionType section_type) +{ + switch (section_type) { + case SECTION_DEVICES: + if (!sidebar->devices_header_added) { + add_heading (sidebar, SECTION_DEVICES, + _("Devices")); + sidebar->devices_header_added = TRUE; + } + + break; + case SECTION_BOOKMARKS: + if (!sidebar->bookmarks_header_added) { + add_heading (sidebar, SECTION_BOOKMARKS, + _("Bookmarks")); + sidebar->bookmarks_header_added = TRUE; + } + + break; + default: + break; + } +} + +static void +add_place (GtkPlacesSidebar *sidebar, + PlaceType place_type, + SectionType section_type, + const char *name, + GIcon *icon, + const char *uri, + GDrive *drive, + GVolume *volume, + GMount *mount, + const int index, + const char *tooltip) +{ + GtkTreeIter iter; + gboolean show_eject, show_unmount; + gboolean show_eject_button; + + check_heading_for_section (sidebar, section_type); + + check_unmount_and_eject (mount, volume, drive, + &show_unmount, &show_eject); + + if (show_unmount || show_eject) { + g_assert (place_type != PLACES_BOOKMARK); + } + + if (mount == NULL) { + show_eject_button = FALSE; + } else { + show_eject_button = (show_unmount || show_eject); + } + + gtk_list_store_append (sidebar->store, &iter); + gtk_list_store_set (sidebar->store, &iter, + PLACES_SIDEBAR_COLUMN_GICON, icon, + PLACES_SIDEBAR_COLUMN_NAME, name, + PLACES_SIDEBAR_COLUMN_URI, uri, + PLACES_SIDEBAR_COLUMN_DRIVE, drive, + PLACES_SIDEBAR_COLUMN_VOLUME, volume, + PLACES_SIDEBAR_COLUMN_MOUNT, mount, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, place_type, + PLACES_SIDEBAR_COLUMN_INDEX, index, + PLACES_SIDEBAR_COLUMN_EJECT, show_eject_button, + PLACES_SIDEBAR_COLUMN_NO_EJECT, !show_eject_button, + PLACES_SIDEBAR_COLUMN_BOOKMARK, place_type != PLACES_BOOKMARK, + PLACES_SIDEBAR_COLUMN_TOOLTIP, tooltip, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type, + -1); +} + +static GIcon * +special_directory_get_gicon (GUserDirectory directory) +{ +#define ICON_CASE(x) \ + case G_USER_DIRECTORY_ ## x: \ + return g_themed_icon_new (ICON_NAME_FOLDER_ ## x); + + switch (directory) { + + ICON_CASE (DOCUMENTS); + ICON_CASE (DOWNLOAD); + ICON_CASE (MUSIC); + ICON_CASE (PICTURES); + ICON_CASE (PUBLIC_SHARE); + ICON_CASE (TEMPLATES); + ICON_CASE (VIDEOS); + + default: + return g_themed_icon_new ("folder-symbolic"); + } + +#undef ICON_CASE +} + +static gboolean +recent_files_setting_is_enabled (GtkPlacesSidebar *sidebar) +{ + GtkSettings *settings; + gboolean enabled; + + if (gtk_widget_has_screen (GTK_WIDGET (sidebar))) + settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (sidebar))); + else + settings = gtk_settings_get_default (); + + g_object_get (settings, "gtk-recent-files-enabled", &enabled, NULL); + return enabled; +} + +static gboolean +recent_scheme_is_supported (void) +{ + const char * const *supported; + int i; + + supported = g_vfs_get_supported_uri_schemes (g_vfs_get_default ()); + if (!supported) { + return FALSE; + } + + for (i = 0; supported[i] != NULL; i++) { + if (strcmp ("recent", supported[i]) == 0) { + return TRUE; + } + } + return FALSE; +} + +static gboolean +should_show_recent (GtkPlacesSidebar *sidebar) +{ + return recent_files_setting_is_enabled (sidebar) && recent_scheme_is_supported (); +} + +static void +add_special_dirs (GtkPlacesSidebar *sidebar) +{ + GList *dirs; + int index; + + dirs = NULL; + for (index = 0; index < G_USER_N_DIRECTORIES; index++) { + const char *path; + GFile *root; + GIcon *icon; + char *name; + char *mount_uri; + char *tooltip; + + if (!_gtk_bookmarks_manager_get_is_xdg_dir_builtin (index)) { + continue; + } + + path = g_get_user_special_dir (index); + + /* xdg resets special dirs to the home directory in case + * it's not finiding what it expects. We don't want the home + * to be added multiple times in that weird configuration. + */ + if (path == NULL + || g_strcmp0 (path, g_get_home_dir ()) == 0 + || g_list_find_custom (dirs, path, (GCompareFunc) g_strcmp0) != NULL) { + continue; + } + + root = g_file_new_for_path (path); + + name = _gtk_bookmarks_manager_get_bookmark_label (sidebar->bookmarks_manager, root); + if (!name) { + name = g_file_get_basename (root); + } + + icon = special_directory_get_gicon (index); + mount_uri = g_file_get_uri (root); + tooltip = g_file_get_parse_name (root); + + add_place (sidebar, PLACES_XDG_DIR, + SECTION_COMPUTER, + name, icon, mount_uri, + NULL, NULL, NULL, 0, + tooltip); + g_free (name); + g_object_unref (root); + g_object_unref (icon); + g_free (mount_uri); + g_free (tooltip); + + dirs = g_list_prepend (dirs, (char *)path); + } + + g_list_free (dirs); +} + +static char * +get_home_directory_uri (void) +{ + const char *home; + + home = g_get_home_dir (); + if (!home) + return NULL; + + return g_strconcat ("file://", home, NULL); +} + +static char * +get_desktop_directory_uri (void) +{ + const char *name; + + name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP); + /* "To disable a directory, point it to the homedir." + * See http://freedesktop.org/wiki/Software/xdg-user-dirs + **/ + if (!g_strcmp0 (name, g_get_home_dir ())) { + return NULL; + } else { + return g_strconcat ("file://", name, NULL); + } +} + +static void +add_application_shortcuts (GtkPlacesSidebar *sidebar) +{ + GSList *l; + + for (l = sidebar->shortcuts; l; l = l->next) { + GFile *file; + GFileInfo *info; + + file = G_FILE (l->data); + + /* FIXME: we are getting file info synchronously. We may want to do it async at some point. */ + info = g_file_query_info (file, + "standard::display-name,standard::symbolic-icon", + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); /* NULL-GError */ + + if (info) { + char *uri; + char *tooltip; + const char *name; + GIcon *icon; + + name = g_file_info_get_display_name (info); + icon = g_file_info_get_symbolic_icon (info); + + uri = g_file_get_uri (file); + tooltip = g_file_get_parse_name (file); + + add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + name, icon, uri, + NULL, NULL, NULL, 0, + tooltip); + + g_free (uri); + g_free (tooltip); + + g_object_unref (info); + } + } +} + +static gboolean +get_selected_iter (GtkPlacesSidebar *sidebar, + GtkTreeIter *iter) +{ + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (sidebar->tree_view); + + return gtk_tree_selection_get_selected (selection, NULL, iter); +} + +static void +update_places (GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GVolumeMonitor *volume_monitor; + GList *mounts, *l, *ll; + GMount *mount; + GList *drives; + GDrive *drive; + GList *volumes; + GVolume *volume; + GSList *bookmarks, *sl; + int index; + char *original_uri, *mount_uri, *name, *identifier; + char *home_uri; + char *bookmark_name; + GIcon *icon; + GFile *root; + char *tooltip; + GList *network_mounts, *network_volumes; + + /* save original selection */ + if (get_selected_iter (sidebar, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), + &iter, + PLACES_SIDEBAR_COLUMN_URI, &original_uri, -1); + } else + original_uri = NULL; + + gtk_list_store_clear (sidebar->store); + + sidebar->devices_header_added = FALSE; + sidebar->bookmarks_header_added = FALSE; + + network_mounts = network_volumes = NULL; + volume_monitor = sidebar->volume_monitor; + + /* add built in bookmarks */ + + add_heading (sidebar, SECTION_COMPUTER, + _("Places")); + + if (should_show_recent (sidebar)) { + mount_uri = "recent:///"; /* No need to strdup */ + icon = g_themed_icon_new ("document-open-recent-symbolic"); + add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Recent"), icon, mount_uri, + NULL, NULL, NULL, 0, + _("Recent files")); + g_object_unref (icon); + } + + /* home folder */ + home_uri = get_home_directory_uri (); + icon = g_themed_icon_new (ICON_NAME_HOME); + add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Home"), icon, + home_uri, NULL, NULL, NULL, 0, + _("Open your personal folder")); + g_object_unref (icon); + g_free (home_uri); + + if (sidebar->show_desktop) { + /* desktop */ + mount_uri = get_desktop_directory_uri (); + icon = g_themed_icon_new (ICON_NAME_DESKTOP); + add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Desktop"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Open the contents of your desktop in a folder")); + g_object_unref (icon); + g_free (mount_uri); + } + + /* XDG directories */ + add_special_dirs (sidebar); + + /* Trash */ + mount_uri = "trash:///"; /* No need to strdup */ + icon = _gtk_trash_monitor_get_icon (sidebar->trash_monitor); + add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Trash"), icon, mount_uri, + NULL, NULL, NULL, 0, + _("Open the trash")); + g_object_unref (icon); + + /* Application-side shortcuts */ + add_application_shortcuts (sidebar); + + /* go through all connected drives */ + drives = g_volume_monitor_get_connected_drives (volume_monitor); + + for (l = drives; l != NULL; l = l->next) { + drive = l->data; + + volumes = g_drive_get_volumes (drive); + if (volumes != NULL) { + for (ll = volumes; ll != NULL; ll = ll->next) { + volume = ll->data; + identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); + + if (g_strcmp0 (identifier, "network") == 0) { + g_free (identifier); + network_volumes = g_list_prepend (network_volumes, volume); + continue; + } + g_free (identifier); + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + /* Show mounted volume in the sidebar */ + icon = g_mount_get_symbolic_icon (mount); + root = g_mount_get_default_location (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + tooltip = g_file_get_parse_name (root); + + add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, mount_uri, + drive, volume, mount, 0, tooltip); + g_object_unref (root); + g_object_unref (mount); + g_object_unref (icon); + g_free (tooltip); + g_free (name); + g_free (mount_uri); + } else { + /* Do show the unmounted volumes in the sidebar; + * this is so the user can mount it (in case automounting + * is off). + * + * Also, even if automounting is enabled, this gives a visual + * cue that the user should remember to yank out the media if + * he just unmounted it. + */ + icon = g_volume_get_symbolic_icon (volume); + name = g_volume_get_name (volume); + tooltip = g_strdup_printf (_("Mount and open %s"), name); + + add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, NULL, + drive, volume, NULL, 0, tooltip); + g_object_unref (icon); + g_free (name); + g_free (tooltip); + } + g_object_unref (volume); + } + g_list_free (volumes); + } else { + if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive)) { + /* If the drive has no mountable volumes and we cannot detect media change.. we + * display the drive in the sidebar so the user can manually poll the drive by + * right clicking and selecting "Rescan..." + * + * This is mainly for drives like floppies where media detection doesn't + * work.. but it's also for human beings who like to turn off media detection + * in the OS to save battery juice. + */ + icon = g_drive_get_symbolic_icon (drive); + name = g_drive_get_name (drive); + tooltip = g_strdup_printf (_("Mount and open %s"), name); + + add_place (sidebar, PLACES_BUILT_IN, + SECTION_DEVICES, + name, icon, NULL, + drive, NULL, NULL, 0, tooltip); + g_object_unref (icon); + g_free (tooltip); + g_free (name); + } + } + g_object_unref (drive); + } + g_list_free (drives); + + /* add all volumes that is not associated with a drive */ + volumes = g_volume_monitor_get_volumes (volume_monitor); + for (l = volumes; l != NULL; l = l->next) { + volume = l->data; + drive = g_volume_get_drive (volume); + if (drive != NULL) { + g_object_unref (volume); + g_object_unref (drive); + continue; + } + + identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); + + if (g_strcmp0 (identifier, "network") == 0) { + g_free (identifier); + network_volumes = g_list_prepend (network_volumes, volume); + continue; + } + g_free (identifier); + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + icon = g_mount_get_symbolic_icon (mount); + root = g_mount_get_default_location (mount); + mount_uri = g_file_get_uri (root); + tooltip = g_file_get_parse_name (root); + g_object_unref (root); + name = g_mount_get_name (mount); + add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, mount_uri, + NULL, volume, mount, 0, tooltip); + g_object_unref (mount); + g_object_unref (icon); + g_free (name); + g_free (tooltip); + g_free (mount_uri); + } else { + /* see comment above in why we add an icon for an unmounted mountable volume */ + icon = g_volume_get_symbolic_icon (volume); + name = g_volume_get_name (volume); + add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, NULL, + NULL, volume, NULL, 0, name); + g_object_unref (icon); + g_free (name); + } + g_object_unref (volume); + } + g_list_free (volumes); + + /* file system root */ + + mount_uri = "file:///"; /* No need to strdup */ + icon = g_themed_icon_new (ICON_NAME_FILESYSTEM); + add_place (sidebar, PLACES_BUILT_IN, + SECTION_DEVICES, + sidebar->hostname, icon, + mount_uri, NULL, NULL, NULL, 0, + _("Open the contents of the File System")); + g_object_unref (icon); + + /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ + mounts = g_volume_monitor_get_mounts (volume_monitor); + + for (l = mounts; l != NULL; l = l->next) { + mount = l->data; + if (g_mount_is_shadowed (mount)) { + g_object_unref (mount); + continue; + } + volume = g_mount_get_volume (mount); + if (volume != NULL) { + g_object_unref (volume); + g_object_unref (mount); + continue; + } + root = g_mount_get_default_location (mount); + + if (!g_file_is_native (root)) { + network_mounts = g_list_prepend (network_mounts, mount); + g_object_unref (root); + continue; + } + + icon = g_mount_get_symbolic_icon (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + tooltip = g_file_get_parse_name (root); + add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_COMPUTER, + name, icon, mount_uri, + NULL, NULL, mount, 0, tooltip); + g_object_unref (root); + g_object_unref (mount); + g_object_unref (icon); + g_free (name); + g_free (mount_uri); + g_free (tooltip); + } + g_list_free (mounts); + + /* add bookmarks */ + + bookmarks = _gtk_bookmarks_manager_list_bookmarks (sidebar->bookmarks_manager); + + for (sl = bookmarks, index = 0; sl; sl = sl->next, index++) { + GFileInfo *info; + + root = sl->data; + +#if 0 + /* FIXME: remove this? If we *do* show bookmarks for nonexistent files, the user will eventually clean them up */ + if (!nautilus_bookmark_get_exists (bookmark)) { + continue; + } +#endif + + if (_gtk_bookmarks_manager_get_is_builtin (sidebar->bookmarks_manager, root)) { + continue; + } + + /* FIXME: we are getting file info synchronously. We may want to do it async at some point. */ + info = g_file_query_info (root, + "standard::display-name,standard::symbolic-icon", + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); /* NULL-GError */ + + if (info) { + bookmark_name = _gtk_bookmarks_manager_get_bookmark_label (sidebar->bookmarks_manager, root); + + if (bookmark_name == NULL) + bookmark_name = g_strdup (g_file_info_get_display_name (info)); + + icon = g_file_info_get_symbolic_icon (info); + + mount_uri = g_file_get_uri (root); + tooltip = g_file_get_parse_name (root); + + add_place (sidebar, PLACES_BOOKMARK, + SECTION_BOOKMARKS, + bookmark_name, icon, mount_uri, + NULL, NULL, NULL, index, + tooltip); + + g_free (mount_uri); + g_free (tooltip); + g_free (bookmark_name); + + g_object_unref (info); + } + } + + g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL); + g_slist_free (bookmarks); + + /* network */ + add_heading (sidebar, SECTION_NETWORK, + _("Network")); + + mount_uri = "network:///"; /* No need to strdup */ + icon = g_themed_icon_new (ICON_NAME_NETWORK); + add_place (sidebar, PLACES_BUILT_IN, + SECTION_NETWORK, + _("Browse Network"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Browse the contents of the network")); + g_object_unref (icon); + + network_volumes = g_list_reverse (network_volumes); + for (l = network_volumes; l != NULL; l = l->next) { + volume = l->data; + mount = g_volume_get_mount (volume); + + if (mount != NULL) { + network_mounts = g_list_prepend (network_mounts, mount); + continue; + } else { + icon = g_volume_get_symbolic_icon (volume); + name = g_volume_get_name (volume); + tooltip = g_strdup_printf (_("Mount and open %s"), name); + + add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_NETWORK, + name, icon, NULL, + NULL, volume, NULL, 0, tooltip); + g_object_unref (icon); + g_free (name); + g_free (tooltip); + } + } + + g_list_free_full (network_volumes, g_object_unref); + + network_mounts = g_list_reverse (network_mounts); + for (l = network_mounts; l != NULL; l = l->next) { + mount = l->data; + root = g_mount_get_default_location (mount); + icon = g_mount_get_symbolic_icon (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + tooltip = g_file_get_parse_name (root); + add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_NETWORK, + name, icon, mount_uri, + NULL, NULL, mount, 0, tooltip); + g_object_unref (root); + g_object_unref (mount); + g_object_unref (icon); + g_free (name); + g_free (mount_uri); + g_free (tooltip); + } + + g_list_free_full (network_mounts, g_object_unref); + + /* restore original selection */ + if (original_uri) { + GFile *restore; + + restore = g_file_new_for_uri (original_uri); + gtk_places_sidebar_set_location (sidebar, restore); + g_object_unref (restore); + + g_free (original_uri); + } +} + +static void +mount_added_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +mount_removed_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +mount_changed_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +volume_added_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +volume_removed_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +volume_changed_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +drive_disconnected_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +drive_connected_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static void +drive_changed_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + +static gboolean +over_eject_button (GtkPlacesSidebar *sidebar, + gint x, + gint y, + GtkTreePath **path) +{ + GtkTreeViewColumn *column; + int width, x_offset, hseparator; + int eject_button_size; + gboolean show_eject; + GtkTreeIter iter; + GtkTreeModel *model; + + *path = NULL; + model = gtk_tree_view_get_model (sidebar->tree_view); + + if (gtk_tree_view_get_path_at_pos (sidebar->tree_view, + x, y, + path, &column, NULL, NULL)) { + + gtk_tree_model_get_iter (model, &iter, *path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_EJECT, &show_eject, + -1); + + if (!show_eject) { + goto out; + } + + + gtk_widget_style_get (GTK_WIDGET (sidebar->tree_view), + "horizontal-separator", &hseparator, + NULL); + + /* Reload cell attributes for this particular row */ + gtk_tree_view_column_cell_set_cell_data (column, + model, &iter, FALSE, FALSE); + + gtk_tree_view_column_cell_get_position (column, + sidebar->eject_icon_cell_renderer, + &x_offset, &width); + + eject_button_size = get_icon_size (sidebar); + + /* This is kinda weird, but we have to do it to workaround gtk+ expanding + * the eject cell renderer (even thought we told it not to) and we then + * had to set it right-aligned */ + x_offset += width - hseparator - EJECT_BUTTON_XPAD - eject_button_size; + + if (x - x_offset >= 0 && + x - x_offset <= eject_button_size) { + return TRUE; + } + } + + out: + if (*path != NULL) { + gtk_tree_path_free (*path); + *path = NULL; + } + + return FALSE; +} + +static gboolean +clicked_eject_button (GtkPlacesSidebar *sidebar, + GtkTreePath **path) +{ + GdkEvent *event = gtk_get_current_event (); + GdkEventButton *button_event = (GdkEventButton *) event; + + if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) && + over_eject_button (sidebar, button_event->x, button_event->y, path)) { + return TRUE; + } + + return FALSE; +} + +static gboolean +pos_is_into_or_before (GtkTreeViewDropPosition pos) +{ + return (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); +} + +/* Computes the appropriate row and position for dropping */ +static gboolean +compute_drop_position (GtkTreeView *tree_view, + int x, + int y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos, + GtkPlacesSidebar *sidebar) +{ + GtkTreeModel *model; + GtkTreeIter iter; + PlaceType place_type; + SectionType section_type; + gboolean drop_possible; + + if (!gtk_tree_view_get_dest_row_at_pos (tree_view, + x, y, + path, pos)) { + return FALSE; + } + + model = gtk_tree_view_get_model (tree_view); + + gtk_tree_model_get_iter (model, &iter, *path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + -1); + + drop_possible = TRUE; + + /* Never drop on headings, but special case the bookmarks heading, + * so we can drop bookmarks in between it and the first bookmark. + */ + if (place_type == PLACES_HEADING + && section_type != SECTION_BOOKMARKS) + drop_possible = FALSE; + + /* Dragging a bookmark? */ + if (sidebar->drag_data_received + && sidebar->drag_data_info == GTK_TREE_MODEL_ROW) { + /* Don't allow reordering bookmarks into non-bookmark areas */ + if (section_type != SECTION_BOOKMARKS) + drop_possible = FALSE; + + /* Bookmarks can only be reordered. Disallow dropping directly into them; only allow dropping between them. */ + if (place_type == PLACES_HEADING) { + if (pos_is_into_or_before (*pos)) + drop_possible = FALSE; + else + *pos = GTK_TREE_VIEW_DROP_AFTER; + } else { + if (pos_is_into_or_before (*pos)) + *pos = GTK_TREE_VIEW_DROP_BEFORE; + else + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + } else { + /* Dragging a file */ + + /* Outside the bookmarks section, URIs can only be dropped + * directly into places items. Inside the bookmarks section, + * they can be dropped between items (to create new bookmarks) + * or in items themselves (to request a move/copy file + * operation). + */ + if (section_type != SECTION_BOOKMARKS) + *pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + else { + if (place_type == PLACES_HEADING) { + if (pos_is_into_or_before (*pos)) + drop_possible = FALSE; + else + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + } + } + + /* Disallow drops on recent:/// */ + if (place_type == PLACES_BUILT_IN) { + char *uri; + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + + if (strcmp (uri, "recent:///") == 0) + drop_possible = FALSE; + + g_free (uri); + } + + if (!drop_possible) { + gtk_tree_path_free (*path); + *path = NULL; + + return FALSE; + } + + return TRUE; +} + +static gboolean +get_drag_data (GtkTreeView *tree_view, + GdkDragContext *context, + unsigned int time) +{ + GdkAtom target; + + target = gtk_drag_dest_find_target (GTK_WIDGET (tree_view), + context, + NULL); + + if (target == GDK_NONE) { + return FALSE; + } + + gtk_drag_get_data (GTK_WIDGET (tree_view), + context, target, time); + + return TRUE; +} + +static void +free_drag_data (GtkPlacesSidebar *sidebar) +{ + sidebar->drag_data_received = FALSE; + + if (sidebar->drag_list) { + g_list_free_full (sidebar->drag_list, g_object_unref); + sidebar->drag_list = NULL; + } +} + +static gboolean +drag_motion_callback (GtkTreeView *tree_view, + GdkDragContext *context, + int x, + int y, + unsigned int time, + GtkPlacesSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeViewDropPosition pos; + int action; + GtkTreeIter iter; + gboolean res; + + action = 0; + + if (!sidebar->drag_data_received) { + if (!get_drag_data (tree_view, context, time)) { + goto out; + } + } + + path = NULL; + res = compute_drop_position (tree_view, x, y, &path, &pos, sidebar); + + if (!res) { + goto out; + } + + if (sidebar->drag_data_received && + sidebar->drag_data_info == GTK_TREE_MODEL_ROW) { + /* Dragging bookmarks always moves them to another position in the bookmarks list */ + action = GDK_ACTION_MOVE; + } else { + /* URIs are being dragged. See if the caller wants to handle a + * file move/copy operation itself, or if we should only try to + * create bookmarks out of the dragged URIs. + */ + if (sidebar->drag_list != NULL) { + SectionType section_type; + PlaceType place_type; + gboolean drop_as_bookmarks; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store), + &iter, path); + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), + &iter, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + -1); + + drop_as_bookmarks = FALSE; + + if (section_type == SECTION_BOOKMARKS) { + if (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER) { + action = GDK_ACTION_COPY; + drop_as_bookmarks = TRUE; + } + } + + if (!drop_as_bookmarks) { + char *uri; + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), + &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + + if (uri != NULL) { + GFile *dest_file = g_file_new_for_uri (uri); + + action = emit_drag_action_requested (sidebar, context, dest_file, sidebar->drag_list); + + g_object_unref (dest_file); + g_free (uri); + } /* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */ + } + } + } + + out: + if (action != 0) + gtk_tree_view_set_drag_dest_row (tree_view, path, pos); + else + gtk_tree_view_set_drag_dest_row (tree_view, NULL, pos); + + if (path != NULL) { + gtk_tree_path_free (path); + } + + g_signal_stop_emission_by_name (tree_view, "drag-motion"); + + gdk_drag_status (context, action, time); + + return TRUE; +} + +static void +drag_leave_callback (GtkTreeView *tree_view, + GdkDragContext *context, + unsigned int time, + GtkPlacesSidebar *sidebar) +{ + free_drag_data (sidebar); + gtk_tree_view_set_drag_dest_row (tree_view, NULL, 0); + g_signal_stop_emission_by_name (tree_view, "drag-leave"); +} + +/* Takes an array of URIs and turns it into a list of GFile */ +static GList * +build_file_list_from_uris (const char **uris) +{ + GList *result; + int i; + + result = NULL; + for (i = 0; uris[i]; i++) { + GFile *file; + + file = g_file_new_for_uri (uris[i]); + result = g_list_prepend (result, file); + } + + return g_list_reverse (result); +} + +/* Reorders the selected bookmark to the specified position */ +static void +reorder_bookmarks (GtkPlacesSidebar *sidebar, + int new_position) +{ + GtkTreeIter iter; + char *uri; + GFile *file; + + /* Get the selected path */ + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + + file = g_file_new_for_uri (uri); + _gtk_bookmarks_manager_reorder_bookmark (sidebar->bookmarks_manager, file, new_position, NULL); /* NULL-GError */ + + g_object_unref (file); + g_free (uri); +} + +/* Creates bookmarks for the specified files at the given position in the bookmarks list */ +static void +drop_files_as_bookmarks (GtkPlacesSidebar *sidebar, + GList *files, + int position) +{ + GList *l; + + for (l = files; l; l = l->next) { + GFile *f = G_FILE (l->data); + + _gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, f, position++, NULL); /* NULL-GError */ + } +} + +static void +drag_data_received_callback (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection_data, + unsigned int info, + unsigned int time, + GtkPlacesSidebar *sidebar) +{ + GtkTreeView *tree_view; + GtkTreePath *tree_path; + GtkTreeViewDropPosition tree_pos; + GtkTreeIter iter; + int position; + GtkTreeModel *model; + PlaceType place_type; + SectionType section_type; + gboolean success; + + tree_view = GTK_TREE_VIEW (widget); + + if (!sidebar->drag_data_received) { + if (gtk_selection_data_get_target (selection_data) != GDK_NONE && + info == TEXT_URI_LIST) { + char **uris; + + uris = gtk_selection_data_get_uris (selection_data); + sidebar->drag_list = build_file_list_from_uris ((const char **) uris); + g_strfreev (uris); + } else { + sidebar->drag_list = NULL; + } + sidebar->drag_data_received = TRUE; + sidebar->drag_data_info = info; + } + + g_signal_stop_emission_by_name (widget, "drag-data-received"); + + if (!sidebar->drop_occured) { + return; + } + + /* Compute position */ + success = compute_drop_position (tree_view, x, y, &tree_path, &tree_pos, sidebar); + if (!success) { + goto out; + } + + success = FALSE; + + if (sidebar->drag_data_info == GTK_TREE_MODEL_ROW) { + /* A bookmark got reordered */ + + model = gtk_tree_view_get_model (tree_view); + + if (!gtk_tree_model_get_iter (model, &iter, tree_path)) { + goto out; + } + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + PLACES_SIDEBAR_COLUMN_INDEX, &position, + -1); + + if (section_type != SECTION_BOOKMARKS) { + goto out; + } + + if (place_type == PLACES_HEADING) + position = 0; + else if (tree_pos == GTK_TREE_VIEW_DROP_AFTER) + position++; + + reorder_bookmarks (sidebar, position); + success = TRUE; + } else { + /* Dropping URIs! */ + + GdkDragAction real_action; + char **uris; + GList *source_file_list; + + /* file transfer requested */ + real_action = gdk_drag_context_get_selected_action (context); + + if (real_action == GDK_ACTION_ASK) + real_action = emit_drag_action_ask (sidebar, gdk_drag_context_get_actions (context)); + + if (real_action > 0) { + char *uri; + GFile *dest_file; + gboolean drop_as_bookmarks; + + model = gtk_tree_view_get_model (tree_view); + + gtk_tree_model_get_iter (model, &iter, tree_path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + PLACES_SIDEBAR_COLUMN_INDEX, &position, + -1); + + drop_as_bookmarks = FALSE; + + uris = gtk_selection_data_get_uris (selection_data); + source_file_list = build_file_list_from_uris ((const char **) uris); + + if (section_type == SECTION_BOOKMARKS) { + if (place_type == PLACES_HEADING) { + position = 0; + tree_pos = GTK_TREE_VIEW_DROP_BEFORE; + } + + if (tree_pos == GTK_TREE_VIEW_DROP_AFTER) + position++; + + if (tree_pos == GTK_TREE_VIEW_DROP_BEFORE + || tree_pos == GTK_TREE_VIEW_DROP_AFTER) { + drop_files_as_bookmarks (sidebar, source_file_list, position); + success = TRUE; + drop_as_bookmarks = TRUE; + } + } + + if (!drop_as_bookmarks) { + gtk_tree_model_get_iter (model, &iter, tree_path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + + dest_file = g_file_new_for_uri (uri); + + emit_drag_perform_drop (sidebar, dest_file, source_file_list, real_action); + success = TRUE; + + g_object_unref (dest_file); + g_free (uri); + } + + g_list_free_full (source_file_list, g_object_unref); + g_strfreev (uris); + } + } + +out: + sidebar->drop_occured = FALSE; + free_drag_data (sidebar); + gtk_drag_finish (context, success, FALSE, time); + + gtk_tree_path_free (tree_path); +} + +static gboolean +drag_drop_callback (GtkTreeView *tree_view, + GdkDragContext *context, + int x, + int y, + unsigned int time, + GtkPlacesSidebar *sidebar) +{ + gboolean retval = FALSE; + sidebar->drop_occured = TRUE; + retval = get_drag_data (tree_view, context, time); + g_signal_stop_emission_by_name (tree_view, "drag-drop"); + return retval; +} + +/* Callback used when the file list's popup menu is detached */ +static void +bookmarks_popup_menu_detach_cb (GtkWidget *attach_widget, + GtkMenu *menu) +{ + GtkPlacesSidebar *sidebar; + + sidebar = GTK_PLACES_SIDEBAR (attach_widget); + + sidebar->popup_menu = NULL; +} +static void +check_unmount_and_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_unmount, + gboolean *show_eject) +{ + *show_unmount = FALSE; + *show_eject = FALSE; + + if (drive != NULL) { + *show_eject = g_drive_can_eject (drive); + } + + if (volume != NULL) { + *show_eject |= g_volume_can_eject (volume); + } + if (mount != NULL) { + *show_eject |= g_mount_can_eject (mount); + *show_unmount = g_mount_can_unmount (mount) && !*show_eject; + } +} + +static void +check_visibility (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_mount, + gboolean *show_unmount, + gboolean *show_eject, + gboolean *show_rescan, + gboolean *show_start, + gboolean *show_stop) +{ + *show_mount = FALSE; + *show_rescan = FALSE; + *show_start = FALSE; + *show_stop = FALSE; + + check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject); + + if (drive != NULL) { + if (g_drive_is_media_removable (drive) && + !g_drive_is_media_check_automatic (drive) && + g_drive_can_poll_for_media (drive)) + *show_rescan = TRUE; + + *show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive); + *show_stop = g_drive_can_stop (drive); + + if (*show_stop) + *show_unmount = FALSE; + } + + if (volume != NULL) { + if (mount == NULL) + *show_mount = g_volume_can_mount (volume); + } +} + +typedef struct { + PlaceType type; + GDrive *drive; + GVolume *volume; + GMount *mount; + char *uri; +} SelectionInfo; + +static void +get_selection_info (GtkPlacesSidebar *sidebar, SelectionInfo *info) +{ + GtkTreeIter iter; + + info->type = PLACES_BUILT_IN; + info->drive = NULL; + info->volume = NULL; + info->mount = NULL; + info->uri = NULL; + + if (get_selected_iter (sidebar, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &info->type, + PLACES_SIDEBAR_COLUMN_DRIVE, &info->drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &info->volume, + PLACES_SIDEBAR_COLUMN_MOUNT, &info->mount, + PLACES_SIDEBAR_COLUMN_URI, &info->uri, + -1); + } +} + +static void +free_selection_info (SelectionInfo *info) +{ + g_clear_object (&info->drive); + g_clear_object (&info->volume); + g_clear_object (&info->mount); + + g_clear_pointer (&info->uri, g_free); +} + +typedef struct { + GtkWidget *add_shortcut_item; + GtkWidget *remove_item; + GtkWidget *rename_item; + GtkWidget *separator_item; + GtkWidget *mount_item; + GtkWidget *unmount_item; + GtkWidget *eject_item; + GtkWidget *rescan_item; + GtkWidget *start_item; + GtkWidget *stop_item; +} PopupMenuData; + +static void +check_popup_sensitivity (GtkPlacesSidebar *sidebar, PopupMenuData *data, SelectionInfo *info) +{ + gboolean show_mount; + gboolean show_unmount; + gboolean show_eject; + gboolean show_rescan; + gboolean show_start; + gboolean show_stop; + + gtk_widget_set_visible (data->add_shortcut_item, (info->type == PLACES_MOUNTED_VOLUME)); + + gtk_widget_set_sensitive (data->remove_item, (info->type == PLACES_BOOKMARK)); + gtk_widget_set_sensitive (data->rename_item, (info->type == PLACES_BOOKMARK || info->type == PLACES_XDG_DIR)); + + check_visibility (info->mount, info->volume, info->drive, + &show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop); + + gtk_widget_set_visible (data->separator_item, show_mount || show_unmount || show_eject); + gtk_widget_set_visible (data->mount_item, show_mount); + gtk_widget_set_visible (data->unmount_item, show_unmount); + gtk_widget_set_visible (data->eject_item, show_eject); + gtk_widget_set_visible (data->rescan_item, show_rescan); + gtk_widget_set_visible (data->start_item, show_start); + gtk_widget_set_visible (data->stop_item, show_stop); + + /* Adjust start/stop items to reflect the type of the drive */ + gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Start")); + gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Stop")); + if ((show_start || show_stop) && info->drive != NULL) { + switch (g_drive_get_start_stop_type (info->drive)) { + case G_DRIVE_START_STOP_TYPE_SHUTDOWN: + /* start() for type G_DRIVE_START_STOP_TYPE_SHUTDOWN is normally not used */ + gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Power On")); + gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Safely Remove Drive")); + break; + case G_DRIVE_START_STOP_TYPE_NETWORK: + gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Connect Drive")); + gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Disconnect Drive")); + break; + case G_DRIVE_START_STOP_TYPE_MULTIDISK: + gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Start Multi-disk Device")); + gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Stop Multi-disk Device")); + break; + case G_DRIVE_START_STOP_TYPE_PASSWORD: + /* stop() for type G_DRIVE_START_STOP_TYPE_PASSWORD is normally not used */ + gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Unlock Drive")); + gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Lock Drive")); + break; + + default: + case G_DRIVE_START_STOP_TYPE_UNKNOWN: + /* uses defaults set above */ + break; + } + } +} + +static void +drive_start_from_bookmark_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar; + GError *error; + char *primary; + char *name; + + sidebar = GTK_PLACES_SIDEBAR (user_data); + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to start %s"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } +} + +/* Callback from g_volume_mount() */ +static void +volume_mount_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) +{ + GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data); + GVolume *volume; + GError *error; + char *primary; + char *name; + GMount *mount; + + volume = G_VOLUME (source_object); + + error = NULL; + if (!g_volume_mount_finish (volume, result, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED && + error->code != G_IO_ERROR_ALREADY_MOUNTED) { + name = g_volume_get_name (G_VOLUME (source_object)); + primary = g_strdup_printf (_("Unable to access “%s”"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + sidebar->mounting = FALSE; + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + GFile *location; + + location = g_mount_get_default_location (mount); + emit_open_location (sidebar, location, sidebar->go_to_after_mount_open_flags); + + g_object_unref (G_OBJECT (location)); + g_object_unref (G_OBJECT (mount)); + } + + g_object_unref (sidebar); +} + +/* This was nautilus_file_operations_mount_volume_full() */ +static void +mount_volume (GtkPlacesSidebar *sidebar, GVolume *volume) +{ + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION); + + g_object_ref (sidebar); + g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, sidebar); +} + +static void +open_selected_bookmark (GtkPlacesSidebar *sidebar, + GtkTreeModel *model, + GtkTreeIter *iter, + GtkPlacesOpenFlags open_flags) +{ + GFile *location; + char *uri; + + if (!iter) { + return; + } + + gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + + if (uri != NULL) { + location = g_file_new_for_uri (uri); + emit_open_location (sidebar, location, open_flags); + + g_object_unref (location); + g_free (uri); + + } else { + GDrive *drive; + GVolume *volume; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + + if (volume != NULL && !sidebar->mounting) { + sidebar->mounting = TRUE; + + sidebar->go_to_after_mount_open_flags = open_flags; + + mount_volume (sidebar, volume); + } else if (volume == NULL && drive != NULL && + (g_drive_can_start (drive) || g_drive_can_start_degraded (drive))) { + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_from_bookmark_cb, sidebar); + g_object_unref (mount_op); + } + + g_clear_object (&drive); + g_clear_object (&volume); + } +} + +static void +open_shortcut_from_menu (GtkPlacesSidebar *sidebar, + GtkPlacesOpenFlags open_flags) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path = NULL; + + model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); + + if (path != NULL && gtk_tree_model_get_iter (model, &iter, path)) { + open_selected_bookmark (sidebar, model, &iter, open_flags); + } + + gtk_tree_path_free (path); +} + +/* Callback used for the "Open" menu item in the context menu */ +static void +open_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, GTK_PLACES_OPEN_NORMAL); +} + +/* Callback used for the "Open in new tab" menu item in the context menu */ +static void +open_shortcut_in_new_tab_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, GTK_PLACES_OPEN_NEW_TAB); +} + +/* Callback used for the "Open in new window" menu item in the context menu */ +static void +open_shortcut_in_new_window_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, GTK_PLACES_OPEN_NEW_WINDOW); +} + +/* Add bookmark for the selected item - just used from mount points */ +static void +add_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + GtkTreeModel *model; + GtkTreeIter iter; + char *uri; + char *name; + GFile *location; + + model = gtk_tree_view_get_model (sidebar->tree_view); + + if (get_selected_iter (sidebar, &iter)) { + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + PLACES_SIDEBAR_COLUMN_NAME, &name, + -1); + + if (uri == NULL) { + return; + } + + location = g_file_new_for_uri (uri); + if (_gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, location, -1, NULL)) + _gtk_bookmarks_manager_set_bookmark_label (sidebar->bookmarks_manager, location, name, NULL); + + g_object_unref (location); + g_free (uri); + g_free (name); + } +} + +/* Rename the selected bookmark */ +static void +rename_selected_bookmark (GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + GList *renderers; + PlaceType type; + + if (get_selected_iter (sidebar, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type != PLACES_BOOKMARK && type != PLACES_XDG_DIR) { + return; + } + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (sidebar->tree_view), 0); + renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); + cell = g_list_nth_data (renderers, 6); + g_list_free (renderers); + g_object_set (cell, "editable", TRUE, NULL); + gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (sidebar->tree_view), + path, column, cell, TRUE); + gtk_tree_path_free (path); + } +} + +static void +rename_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + rename_selected_bookmark (sidebar); +} + +/* Removes the selected bookmarks */ +static void +remove_selected_bookmarks (GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + PlaceType type; + char *uri; + GFile *file; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type != PLACES_BOOKMARK) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + + file = g_file_new_for_uri (uri); + _gtk_bookmarks_manager_remove_bookmark (sidebar->bookmarks_manager, file, NULL); + + g_object_unref (file); + g_free (uri); +} + +static void +remove_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + remove_selected_bookmarks (sidebar); +} + +static void +mount_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GVolume *volume; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + + if (volume != NULL) { + mount_volume (sidebar, volume); + g_object_unref (volume); + } +} + +/* Callback used from g_mount_unmount_with_operation() */ +static void +unmount_mount_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data); + GMount *mount; + GError *error; + + mount = G_MOUNT (source_object); + + error = NULL; + if (!g_mount_unmount_with_operation_finish (mount, result, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + char *name; + char *primary; + + name = g_mount_get_name (mount); + primary = g_strdup_printf (_("Unable to unmount %s"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + + g_error_free (error); + } + + /* FIXME: we need to switch to a path that is available now - $HOME? */ + + g_object_unref (sidebar); +} + +static void +show_unmount_progress_cb (GMountOperation *op, + const gchar *message, + gint64 time_left, + gint64 bytes_left, + gpointer user_data) +{ + /* FIXME: These are just libnotify notifications, but GTK+ doesn't do notifications right now. + * Should we just call D-Bus directly? + */ +#if DO_NOT_COMPILE + NautilusApplication *app = NAUTILUS_APPLICATION (g_application_get_default ()); + + if (bytes_left == 0) { + nautilus_application_notify_unmount_done (app, message); + } else { + nautilus_application_notify_unmount_show (app, message); + } +#endif +} + +static void +show_unmount_progress_aborted_cb (GMountOperation *op, + gpointer user_data) +{ + /* FIXME: These are just libnotify notifications, but GTK+ doesn't do notifications right now. + * Should we just call D-Bus directly? + */ +#if DO_NOT_COMPILE + NautilusApplication *app = NAUTILUS_APPLICATION (g_application_get_default ()); + nautilus_application_notify_unmount_done (app, NULL); +#endif +} + +static GMountOperation * +get_unmount_operation (GtkPlacesSidebar *sidebar) +{ + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + g_signal_connect (mount_op, "show-unmount-progress", + G_CALLBACK (show_unmount_progress_cb), sidebar); + g_signal_connect (mount_op, "aborted", + G_CALLBACK (show_unmount_progress_aborted_cb), sidebar); + + return mount_op; +} + +static void +do_unmount (GMount *mount, + GtkPlacesSidebar *sidebar) +{ + if (mount != NULL) { + GMountOperation *mount_op; + + mount_op = get_unmount_operation (sidebar); + g_mount_unmount_with_operation (mount, + 0, + mount_op, + NULL, + unmount_mount_cb, + g_object_ref (sidebar)); + g_object_unref (mount_op); + } +} + +static void +do_unmount_selection (GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GMount *mount; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + -1); + + if (mount != NULL) { + do_unmount (mount, sidebar); + g_object_unref (mount); + } +} + +static void +unmount_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + do_unmount_selection (sidebar); +} + +static void +drive_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar; + GError *error; + char *primary; + char *name; + + sidebar = user_data; + + error = NULL; + if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + g_object_unref (sidebar); +} + +static void +volume_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar; + GError *error; + char *primary; + char *name; + + sidebar = user_data; + + error = NULL; + if (!g_volume_eject_with_operation_finish (G_VOLUME (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_volume_get_name (G_VOLUME (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + g_object_unref (sidebar); +} + +static void +mount_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar; + GError *error; + char *primary; + char *name; + + sidebar = user_data; + + error = NULL; + if (!g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_mount_get_name (G_MOUNT (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + g_object_unref (sidebar); +} + +static void +do_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + GtkPlacesSidebar *sidebar) +{ + GMountOperation *mount_op; + + mount_op = get_unmount_operation (sidebar); + if (mount != NULL) { + g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb, + g_object_ref (sidebar)); + } else if (volume != NULL) { + g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb, + g_object_ref (sidebar)); + } else if (drive != NULL) { + g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb, + g_object_ref (sidebar)); + } + g_object_unref (mount_op); +} + +static void +eject_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GMount *mount; + GVolume *volume; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + do_eject (mount, volume, drive, sidebar); +} + +static gboolean +eject_or_unmount_bookmark (GtkPlacesSidebar *sidebar, + GtkTreePath *path) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean can_unmount, can_eject; + GMount *mount; + GVolume *volume; + GDrive *drive; + gboolean ret; + + model = GTK_TREE_MODEL (sidebar->store); + + if (!path) { + return FALSE; + } + if (!gtk_tree_model_get_iter (model, &iter, path)) { + return FALSE; + } + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + ret = FALSE; + + check_unmount_and_eject (mount, volume, drive, &can_unmount, &can_eject); + /* if we can eject, it has priority over unmount */ + if (can_eject) { + do_eject (mount, volume, drive, sidebar); + ret = TRUE; + } else if (can_unmount) { + do_unmount (mount, sidebar); + ret = TRUE; + } + + g_clear_object (&mount); + g_clear_object (&volume); + g_clear_object (&drive); + + return ret; +} + +static gboolean +eject_or_unmount_selection (GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GtkTreePath *path; + gboolean ret; + + if (!get_selected_iter (sidebar, &iter)) { + return FALSE; + } + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); + if (path == NULL) { + return FALSE; + } + + ret = eject_or_unmount_bookmark (sidebar, path); + + gtk_tree_path_free (path); + + return ret; +} + +static void +drive_poll_for_media_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar; + GError *error; + char *primary; + char *name; + + sidebar = GTK_PLACES_SIDEBAR (user_data); + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to poll %s for media changes"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + /* FIXME: drive_stop_cb() gets a reffed sidebar, and unrefs it. Do we need to do the same here? */ +} + +static void +rescan_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) { + g_drive_poll_for_media (drive, NULL, drive_poll_for_media_cb, sidebar); + g_object_unref (drive); + } +} + +static void +drive_start_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar; + GError *error; + char *primary; + char *name; + + sidebar = GTK_PLACES_SIDEBAR (user_data); + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to start %s"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + /* FIXME: drive_stop_cb() gets a reffed sidebar, and unrefs it. Do we need to do the same here? */ +} + +static void +start_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) { + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + + g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_cb, sidebar); + + g_object_unref (mount_op); + g_object_unref (drive); + } +} + +static void +drive_stop_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar; + GError *error; + char *primary; + char *name; + + sidebar = user_data; + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to stop %s"), name); + g_free (name); + emit_show_error_message (sidebar, primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + g_object_unref (sidebar); +} + +static void +stop_shortcut_cb (GtkMenuItem *item, + GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) { + GMountOperation *mount_op; + + mount_op = get_unmount_operation (sidebar); + g_drive_stop (drive, G_MOUNT_UNMOUNT_NONE, mount_op, NULL, drive_stop_cb, + g_object_ref (sidebar)); + + g_object_unref (mount_op); + g_object_unref (drive); + } +} + +static gboolean +find_prev_or_next_row (GtkPlacesSidebar *sidebar, + GtkTreeIter *iter, + gboolean go_up) +{ + GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store); + gboolean res; + int place_type; + + if (go_up) { + res = gtk_tree_model_iter_previous (model, iter); + } else { + res = gtk_tree_model_iter_next (model, iter); + } + + if (res) { + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + -1); + if (place_type == PLACES_HEADING) { + if (go_up) { + res = gtk_tree_model_iter_previous (model, iter); + } else { + res = gtk_tree_model_iter_next (model, iter); + } + } + } + + return res; +} + +static gboolean +find_prev_row (GtkPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + return find_prev_or_next_row (sidebar, iter, TRUE); +} + +static gboolean +find_next_row (GtkPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + return find_prev_or_next_row (sidebar, iter, FALSE); +} + +static gboolean +gtk_places_sidebar_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (widget); + GtkTreePath *path; + GtkTreeIter iter; + gboolean res; + + res = get_selected_iter (sidebar, &iter); + + if (!res) { + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store), &iter); + res = find_next_row (sidebar, &iter); + if (res) { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + gtk_tree_path_free (path); + } + } + + return GTK_WIDGET_CLASS (gtk_places_sidebar_parent_class)->focus (widget, direction); +} + +/* Handler for GtkWidget::key-press-event on the shortcuts list */ +static gboolean +bookmarks_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + GtkPlacesSidebar *sidebar) +{ + guint modifiers; + GtkTreeIter selected_iter; + GtkTreePath *path; + + if (!get_selected_iter (sidebar, &selected_iter)) { + return FALSE; + } + + modifiers = gtk_accelerator_get_default_mod_mask (); + + if ((event->keyval == GDK_KEY_Return || + event->keyval == GDK_KEY_KP_Enter || + event->keyval == GDK_KEY_ISO_Enter || + event->keyval == GDK_KEY_space)) { + + GtkPlacesOpenFlags open_flags = GTK_PLACES_OPEN_NORMAL; + + if ((event->state & modifiers) == GDK_SHIFT_MASK) { + open_flags = GTK_PLACES_OPEN_NEW_TAB; + } else if ((event->state & modifiers) == GDK_CONTROL_MASK) { + open_flags = GTK_PLACES_OPEN_NEW_WINDOW; + } + + open_selected_bookmark (sidebar, GTK_TREE_MODEL (sidebar->store), + &selected_iter, open_flags); + + return TRUE; + } + + if (event->keyval == GDK_KEY_Down && + (event->state & modifiers) == GDK_MOD1_MASK) { + return eject_or_unmount_selection (sidebar); + } + + if (event->keyval == GDK_KEY_Up) { + if (find_prev_row (sidebar, &selected_iter)) { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &selected_iter); + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + gtk_tree_path_free (path); + } + return TRUE; + } + + if (event->keyval == GDK_KEY_Down) { + if (find_next_row (sidebar, &selected_iter)) { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &selected_iter); + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + gtk_tree_path_free (path); + } + return TRUE; + } + + if ((event->keyval == GDK_KEY_Delete + || event->keyval == GDK_KEY_KP_Delete) + && (event->state & modifiers) == 0) { + remove_selected_bookmarks (sidebar); + return TRUE; + } + + if ((event->keyval == GDK_KEY_F2) + && (event->state & modifiers) == 0) { + rename_selected_bookmark (sidebar); + return TRUE; + } + + return FALSE; +} + +static GtkMenuItem * +append_menu_separator (GtkMenu *menu) +{ + GtkWidget *menu_item; + + menu_item = gtk_separator_menu_item_new (); + gtk_widget_show (menu_item); + gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menu_item, -1); + + return GTK_MENU_ITEM (menu_item); +} + +/* Constructs the popup menu for the file list if needed */ +static void +bookmarks_build_popup_menu (GtkPlacesSidebar *sidebar) +{ + PopupMenuData menu_data; + GtkWidget *item; + SelectionInfo sel_info; + GFile *file; + + sidebar->popup_menu = gtk_menu_new (); + gtk_menu_attach_to_widget (GTK_MENU (sidebar->popup_menu), + GTK_WIDGET (sidebar), + bookmarks_popup_menu_detach_cb); + + item = gtk_image_menu_item_new_with_mnemonic (_("_Open")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), + gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU)); + g_signal_connect (item, "activate", + G_CALLBACK (open_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + if (sidebar->open_flags & GTK_PLACES_OPEN_NEW_TAB) { + item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab")); + g_signal_connect (item, "activate", + G_CALLBACK (open_shortcut_in_new_tab_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + } + + if (sidebar->open_flags & GTK_PLACES_OPEN_NEW_WINDOW) { + item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window")); + g_signal_connect (item, "activate", + G_CALLBACK (open_shortcut_in_new_window_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + } + + append_menu_separator (GTK_MENU (sidebar->popup_menu)); + + item = gtk_menu_item_new_with_mnemonic (_("_Add Bookmark")); + menu_data.add_shortcut_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (add_shortcut_cb), sidebar); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_image_menu_item_new_with_label (_("Remove")); + menu_data.remove_item = item; + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), + gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU)); + g_signal_connect (item, "activate", + G_CALLBACK (remove_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_label (_("Rename…")); + menu_data.rename_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (rename_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + /* Mount/Unmount/Eject menu items */ + + menu_data.separator_item = GTK_WIDGET (append_menu_separator (GTK_MENU (sidebar->popup_menu))); + + item = gtk_menu_item_new_with_mnemonic (_("_Mount")); + menu_data.mount_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (mount_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Unmount")); + menu_data.unmount_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (unmount_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Eject")); + menu_data.eject_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (eject_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Detect Media")); + menu_data.rescan_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (rescan_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Start")); + menu_data.start_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (start_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + item = gtk_menu_item_new_with_mnemonic (_("_Stop")); + menu_data.stop_item = item; + g_signal_connect (item, "activate", + G_CALLBACK (stop_shortcut_cb), sidebar); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item); + + /* Update everything! */ + + get_selection_info (sidebar, &sel_info); + + check_popup_sensitivity (sidebar, &menu_data, &sel_info); + + /* And let the caller spice things up */ + + if (sel_info.uri) + file = g_file_new_for_uri (sel_info.uri); + else + file = NULL; + + emit_populate_popup (sidebar, GTK_MENU (sidebar->popup_menu), file); + + g_object_unref (file); + + free_selection_info (&sel_info); +} + +static void +bookmarks_popup_menu (GtkPlacesSidebar *sidebar, + GdkEventButton *event) +{ + int button; + + if (sidebar->popup_menu) + gtk_widget_destroy (sidebar->popup_menu); + + bookmarks_build_popup_menu (sidebar); + + /* The event button needs to be 0 if we're popping up this menu from + * a button release, else a 2nd click outside the menu with any button + * other than the one that invoked the menu will be ignored (instead + * of dismissing the menu). This is a subtle fragility of the GTK menu code. + */ + if (event) { + if (event->type == GDK_BUTTON_RELEASE) + button = 0; + else + button = event->button; + } else { + button = 0; + } + + gtk_menu_popup (GTK_MENU (sidebar->popup_menu), /* menu */ + NULL, /* parent_menu_shell */ + NULL, /* parent_menu_item */ + NULL, /* popup_position_func */ + NULL, /* popup_position_user_data */ + button, /* button */ + event ? event->time : gtk_get_current_event_time ()); /* activate_time */ +} + +/* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */ +static gboolean +bookmarks_popup_menu_cb (GtkWidget *widget, + GtkPlacesSidebar *sidebar) +{ + bookmarks_popup_menu (sidebar, NULL); + return TRUE; +} + +static gboolean +bookmarks_button_release_event_cb (GtkWidget *widget, + GdkEventButton *event, + GtkPlacesSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeView *tree_view; + gboolean ret = FALSE; + gboolean res; + + path = NULL; + + if (event->type != GDK_BUTTON_RELEASE) { + return TRUE; + } + + if (clicked_eject_button (sidebar, &path)) { + eject_or_unmount_bookmark (sidebar, path); + gtk_tree_path_free (path); + + return FALSE; + } + + tree_view = GTK_TREE_VIEW (widget); + model = gtk_tree_view_get_model (tree_view); + + if (event->window != gtk_tree_view_get_bin_window (tree_view)) { + return FALSE; + } + + res = gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, + &path, NULL, NULL, NULL); + + if (!res || path == NULL) { + return FALSE; + } + + res = gtk_tree_model_get_iter (model, &iter, path); + if (!res) { + gtk_tree_path_free (path); + return FALSE; + } + + if (event->button == 1) { + open_selected_bookmark (sidebar, model, &iter, 0); + } else if (event->button == 2) { + GtkPlacesOpenFlags open_flags = GTK_PLACES_OPEN_NORMAL; + + open_flags = ((event->state & GDK_CONTROL_MASK) ? + GTK_PLACES_OPEN_NEW_WINDOW : + GTK_PLACES_OPEN_NEW_TAB); + + open_selected_bookmark (sidebar, model, &iter, open_flags); + ret = TRUE; + } else if (event->button == 3) { + PlaceType row_type; + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, + -1); + + if (row_type != PLACES_HEADING) { + bookmarks_popup_menu (sidebar, event); + } + } + + gtk_tree_path_free (path); + + return ret; +} + +static void +bookmarks_edited (GtkCellRenderer *cell, + gchar *path_string, + gchar *new_text, + GtkPlacesSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeIter iter; + char *uri; + GFile *file; + + g_object_set (cell, "editable", FALSE, NULL); + + path = gtk_tree_path_new_from_string (path_string); + gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store), &iter, path); + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + gtk_tree_path_free (path); + + file = g_file_new_for_uri (uri); + if (!_gtk_bookmarks_manager_has_bookmark (sidebar->bookmarks_manager, file)) { + _gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, file, -1, NULL); + } + + _gtk_bookmarks_manager_set_bookmark_label (sidebar->bookmarks_manager, file, new_text, NULL); /* NULL-GError */ + + g_object_unref (file); + g_free (uri); +} + +static void +bookmarks_editing_canceled (GtkCellRenderer *cell, + GtkPlacesSidebar *sidebar) +{ + g_object_set (cell, "editable", FALSE, NULL); +} + +static gboolean +tree_selection_func (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean path_currently_selected, + gpointer user_data) +{ + GtkTreeIter iter; + PlaceType row_type; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, + -1); + + if (row_type == PLACES_HEADING) { + return FALSE; + } + + return TRUE; +} + +static void +icon_cell_renderer_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + PlaceType type; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type == PLACES_HEADING) { + g_object_set (cell, + "visible", FALSE, + NULL); + } else { + g_object_set (cell, + "visible", TRUE, + NULL); + } +} + +static void +padding_cell_renderer_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + PlaceType type; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type == PLACES_HEADING) { + g_object_set (cell, + "visible", FALSE, + "xpad", 0, + "ypad", 0, + NULL); + } else { + g_object_set (cell, + "visible", TRUE, + "xpad", 3, + "ypad", 3, + NULL); + } +} + +static void +heading_cell_renderer_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + PlaceType type; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type == PLACES_HEADING) { + g_object_set (cell, + "visible", TRUE, + NULL); + } else { + g_object_set (cell, + "visible", FALSE, + NULL); + } +} + +static gint +places_sidebar_sort_func (GtkTreeModel *model, + GtkTreeIter *iter_a, + GtkTreeIter *iter_b, + gpointer user_data) +{ + SectionType section_type_a, section_type_b; + PlaceType place_type_a, place_type_b; + gint retval = 0; + + gtk_tree_model_get (model, iter_a, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type_a, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type_a, + -1); + gtk_tree_model_get (model, iter_b, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type_b, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type_b, + -1); + + /* fall back to the default order if we're not in the + * XDG part of the computer section. + */ + if ((section_type_a == section_type_b) && + (section_type_a == SECTION_COMPUTER) && + (place_type_a == place_type_b) && + (place_type_a == PLACES_XDG_DIR)) { + gchar *name_a, *name_b; + + gtk_tree_model_get (model, iter_a, + PLACES_SIDEBAR_COLUMN_NAME, &name_a, + -1); + gtk_tree_model_get (model, iter_b, + PLACES_SIDEBAR_COLUMN_NAME, &name_b, + -1); + + retval = g_utf8_collate (name_a, name_b); + + g_free (name_a); + g_free (name_b); + } + + return retval; +} + +static void +update_hostname (GtkPlacesSidebar *sidebar) +{ + GVariant *variant; + gsize len; + const gchar *hostname; + + if (sidebar->hostnamed_proxy == NULL) + return; + + variant = g_dbus_proxy_get_cached_property (sidebar->hostnamed_proxy, + "PrettyHostname"); + if (variant == NULL) { + return; + } + + hostname = g_variant_get_string (variant, &len); + if (len > 0 && + g_strcmp0 (sidebar->hostname, hostname) != 0) { + g_free (sidebar->hostname); + sidebar->hostname = g_strdup (hostname); + update_places (sidebar); + } + + g_variant_unref (variant); +} + +static void +hostname_proxy_new_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GtkPlacesSidebar *sidebar = user_data; + GError *error = NULL; + + sidebar->hostnamed_proxy = g_dbus_proxy_new_for_bus_finish (res, &error); + g_clear_object (&sidebar->hostnamed_cancellable); + + g_object_unref (sidebar); + + if (error != NULL) { + g_debug ("Failed to create D-Bus proxy: %s", error->message); + g_error_free (error); + return; + } + + g_signal_connect_swapped (sidebar->hostnamed_proxy, + "g-properties-changed", + G_CALLBACK (update_hostname), + sidebar); + update_hostname (sidebar); +} + +static void +create_volume_monitor (GtkPlacesSidebar *sidebar) +{ + g_assert (sidebar->volume_monitor == NULL); + + sidebar->volume_monitor = g_volume_monitor_get (); + + g_signal_connect_object (sidebar->volume_monitor, "volume_added", + G_CALLBACK (volume_added_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "volume_removed", + G_CALLBACK (volume_removed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "volume_changed", + G_CALLBACK (volume_changed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_added", + G_CALLBACK (mount_added_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_removed", + G_CALLBACK (mount_removed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_changed", + G_CALLBACK (mount_changed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_disconnected", + G_CALLBACK (drive_disconnected_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_connected", + G_CALLBACK (drive_connected_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_changed", + G_CALLBACK (drive_changed_callback), sidebar, 0); +} + +static void +bookmarks_changed_cb (gpointer data) +{ + GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (data); + + update_places (sidebar); +} + +static gboolean +tree_view_button_press_callback (GtkWidget *tree_view, + GdkEventButton *event, + gpointer data) +{ + GtkTreePath *path; + GtkTreeViewColumn *column; + + if (event->button == 1 && event->type == GDK_BUTTON_PRESS) { + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), + event->x, event->y, + &path, + &column, + NULL, + NULL)) { + gtk_tree_view_row_activated (GTK_TREE_VIEW (tree_view), path, column); + } + } + + return FALSE; +} + +static void +tree_view_set_activate_on_single_click (GtkTreeView *tree_view) +{ + g_signal_connect (tree_view, "button_press_event", + G_CALLBACK (tree_view_button_press_callback), + NULL); +} + +static void +trash_monitor_trash_state_changed_cb (GtkTrashMonitor *monitor, + GtkPlacesSidebar *sidebar) +{ + update_places (sidebar); +} + + +static void +gtk_places_sidebar_init (GtkPlacesSidebar *sidebar) +{ + GtkTreeView *tree_view; + GtkTreeViewColumn *col; + GtkCellRenderer *cell; + GtkTreeSelection *selection; + GIcon *eject; + + gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (sidebar)), GTK_STYLE_CLASS_SIDEBAR); + + create_volume_monitor (sidebar); + + sidebar->open_flags = GTK_PLACES_OPEN_NORMAL; + + sidebar->bookmarks_manager = _gtk_bookmarks_manager_new (bookmarks_changed_cb, sidebar); + + sidebar->trash_monitor = _gtk_trash_monitor_get (); + sidebar->trash_monitor_changed_id = g_signal_connect (sidebar->trash_monitor, "trash-state-changed", + G_CALLBACK (trash_monitor_trash_state_changed_cb), sidebar); + + sidebar->shortcuts = NULL; + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); + gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN); + + gtk_style_context_set_junction_sides (gtk_widget_get_style_context (GTK_WIDGET (sidebar)), + GTK_JUNCTION_RIGHT | GTK_JUNCTION_LEFT); + + /* tree view */ + tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); + gtk_tree_view_set_headers_visible (tree_view, FALSE); + + col = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ()); + + /* initial padding */ + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, cell, FALSE); + g_object_set (cell, + "xpad", 6, + NULL); + + /* headings */ + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_attributes (col, cell, + "text", PLACES_SIDEBAR_COLUMN_HEADING_TEXT, + NULL); + g_object_set (cell, + "weight", PANGO_WEIGHT_BOLD, + "weight-set", TRUE, + "ypad", 6, + "xpad", 0, + NULL); + gtk_tree_view_column_set_cell_data_func (col, cell, + heading_cell_renderer_func, + sidebar, NULL); + + /* icon padding */ + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_cell_data_func (col, cell, + padding_cell_renderer_func, + sidebar, NULL); + + /* icon renderer */ + cell = gtk_cell_renderer_pixbuf_new (); + g_object_set (cell, "follow-state", TRUE, NULL); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_attributes (col, cell, + "gicon", PLACES_SIDEBAR_COLUMN_GICON, + NULL); + gtk_tree_view_column_set_cell_data_func (col, cell, + icon_cell_renderer_func, + sidebar, NULL); + + /* eject text renderer */ + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, cell, TRUE); + gtk_tree_view_column_set_attributes (col, cell, + "text", PLACES_SIDEBAR_COLUMN_NAME, + "visible", PLACES_SIDEBAR_COLUMN_EJECT, + NULL); + g_object_set (cell, + "ellipsize", PANGO_ELLIPSIZE_END, + "ellipsize-set", TRUE, + NULL); + + /* eject icon renderer */ + cell = gtk_cell_renderer_pixbuf_new (); + sidebar->eject_icon_cell_renderer = cell; + eject = g_themed_icon_new_with_default_fallbacks ("media-eject-symbolic"); + g_object_set (cell, + "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, + "stock-size", GTK_ICON_SIZE_MENU, + "xpad", EJECT_BUTTON_XPAD, + /* align right, because for some reason gtk+ expands + this even though we tell it not to. */ + "xalign", 1.0, + "follow-state", TRUE, + "gicon", eject, + NULL); + gtk_tree_view_column_pack_start (col, cell, FALSE); + gtk_tree_view_column_set_attributes (col, cell, + "visible", PLACES_SIDEBAR_COLUMN_EJECT, + NULL); + g_object_unref (eject); + + /* normal text renderer */ + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, cell, TRUE); + g_object_set (G_OBJECT (cell), "editable", FALSE, NULL); + gtk_tree_view_column_set_attributes (col, cell, + "text", PLACES_SIDEBAR_COLUMN_NAME, + "visible", PLACES_SIDEBAR_COLUMN_NO_EJECT, + "editable-set", PLACES_SIDEBAR_COLUMN_BOOKMARK, + NULL); + g_object_set (cell, + "ellipsize", PANGO_ELLIPSIZE_END, + "ellipsize-set", TRUE, + NULL); + + g_signal_connect (cell, "edited", + G_CALLBACK (bookmarks_edited), sidebar); + g_signal_connect (cell, "editing-canceled", + G_CALLBACK (bookmarks_editing_canceled), sidebar); + + /* this is required to align the eject buttons to the right */ + gtk_tree_view_column_set_max_width (GTK_TREE_VIEW_COLUMN (col), 24); + gtk_tree_view_append_column (tree_view, col); + + sidebar->store = shortcuts_model_new (sidebar); + gtk_tree_view_set_tooltip_column (tree_view, PLACES_SIDEBAR_COLUMN_TOOLTIP); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sidebar->store), + PLACES_SIDEBAR_COLUMN_NAME, + GTK_SORT_ASCENDING); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sidebar->store), + PLACES_SIDEBAR_COLUMN_NAME, + places_sidebar_sort_func, + sidebar, NULL); + + gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (sidebar->store)); + gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view)); + gtk_widget_show (GTK_WIDGET (tree_view)); + gtk_tree_view_set_enable_search (tree_view, FALSE); + + gtk_widget_show (GTK_WIDGET (sidebar)); + sidebar->tree_view = tree_view; + + gtk_tree_view_set_search_column (tree_view, PLACES_SIDEBAR_COLUMN_NAME); + selection = gtk_tree_view_get_selection (tree_view); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + + gtk_tree_selection_set_select_function (selection, + tree_selection_func, + sidebar, + NULL); + + gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree_view), + GDK_BUTTON1_MASK, + dnd_source_targets, G_N_ELEMENTS (dnd_source_targets), + GDK_ACTION_MOVE); + gtk_drag_dest_set (GTK_WIDGET (tree_view), + 0, + dnd_drop_targets, G_N_ELEMENTS (dnd_drop_targets), + GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); + + g_signal_connect (tree_view, "key-press-event", + G_CALLBACK (bookmarks_key_press_event_cb), sidebar); + + g_signal_connect (tree_view, "drag-motion", + G_CALLBACK (drag_motion_callback), sidebar); + g_signal_connect (tree_view, "drag-leave", + G_CALLBACK (drag_leave_callback), sidebar); + g_signal_connect (tree_view, "drag-data-received", + G_CALLBACK (drag_data_received_callback), sidebar); + g_signal_connect (tree_view, "drag-drop", + G_CALLBACK (drag_drop_callback), sidebar); + + g_signal_connect (tree_view, "popup-menu", + G_CALLBACK (bookmarks_popup_menu_cb), sidebar); + g_signal_connect (tree_view, "button-release-event", + G_CALLBACK (bookmarks_button_release_event_cb), sidebar); + + tree_view_set_activate_on_single_click (sidebar->tree_view); + + sidebar->hostname = g_strdup (_("Computer")); + sidebar->hostnamed_cancellable = g_cancellable_new (); + g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + NULL, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + sidebar->hostnamed_cancellable, + hostname_proxy_new_cb, + g_object_ref (sidebar)); +} + +static void +gtk_places_sidebar_set_property (GObject *obj, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (obj); + + switch (property_id) { + case PROP_LOCATION: + gtk_places_sidebar_set_location (sidebar, g_value_get_object (value)); + break; + case PROP_OPEN_FLAGS: + gtk_places_sidebar_set_open_flags (sidebar, g_value_get_flags (value)); + break; + case PROP_SHOW_DESKTOP: + gtk_places_sidebar_set_show_desktop (sidebar, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + break; + } +} + +static void +gtk_places_sidebar_get_property (GObject *obj, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (obj); + + switch (property_id) { + case PROP_LOCATION: + g_value_take_object (value, gtk_places_sidebar_get_location (sidebar)); + break; + case PROP_OPEN_FLAGS: + g_value_set_flags (value, gtk_places_sidebar_get_open_flags (sidebar)); + break; + case PROP_SHOW_DESKTOP: + g_value_set_boolean (value, gtk_places_sidebar_get_show_desktop (sidebar)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); + break; + } +} + +static void +gtk_places_sidebar_dispose (GObject *object) +{ + GtkPlacesSidebar *sidebar; + + sidebar = GTK_PLACES_SIDEBAR (object); + + sidebar->tree_view = NULL; + + free_drag_data (sidebar); + + if (sidebar->bookmarks_manager != NULL) { + _gtk_bookmarks_manager_free (sidebar->bookmarks_manager); + sidebar->bookmarks_manager = NULL; + } + + if (sidebar->popup_menu) { + gtk_widget_destroy (sidebar->popup_menu); + sidebar->popup_menu = NULL; + } + + if (sidebar->trash_monitor) { + g_signal_handler_disconnect (sidebar->trash_monitor, sidebar->trash_monitor_changed_id); + sidebar->trash_monitor_changed_id = 0; + g_clear_object (&sidebar->trash_monitor); + } + + g_clear_object (&sidebar->store); + + g_slist_free_full (sidebar->shortcuts, g_object_unref); + sidebar->shortcuts = NULL; + + if (sidebar->volume_monitor != NULL) { + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + volume_added_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + volume_removed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + volume_changed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + mount_added_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + mount_removed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + mount_changed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + drive_disconnected_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + drive_connected_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + drive_changed_callback, sidebar); + + g_clear_object (&sidebar->volume_monitor); + } + + if (sidebar->hostnamed_cancellable != NULL) { + g_cancellable_cancel (sidebar->hostnamed_cancellable); + g_clear_object (&sidebar->hostnamed_cancellable); + } + + g_clear_object (&sidebar->hostnamed_proxy); + g_free (sidebar->hostname); + sidebar->hostname = NULL; + + G_OBJECT_CLASS (gtk_places_sidebar_parent_class)->dispose (object); +} + +static void +gtk_places_sidebar_class_init (GtkPlacesSidebarClass *class) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) class; + + gobject_class->dispose = gtk_places_sidebar_dispose; + gobject_class->set_property = gtk_places_sidebar_set_property; + gobject_class->get_property = gtk_places_sidebar_get_property; + + GTK_WIDGET_CLASS (class)->focus = gtk_places_sidebar_focus; + + /** + * GtkPlacesSidebar::open-location: + * @sidebar: the object which received the signal. + * @location: #GFile to which the caller should switch. + * @open_flags: a single value from #GtkPlacesOpenFlags specifying how the @location should be opened. + * + * The places sidebar emits this signal when the user selects a location + * in it. The calling application should display the contents of that + * location; for example, a file manager should show a list of files in + * the specified location. + * + * Since: 3.8 + */ + places_sidebar_signals [OPEN_LOCATION] = + g_signal_new (I_("open-location"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkPlacesSidebarClass, open_location), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_FLAGS, + G_TYPE_NONE, 2, + G_TYPE_OBJECT, + GTK_TYPE_PLACES_OPEN_FLAGS); + + /** + * GtkPlacesSidebar::populate-popup: + * @sidebar: the object which received the signal. + * @menu: a #GtkMenu. + * @selected_item: #GFile with the item to which the menu should refer. + * + * The places sidebar emits this signal when the user invokes a contextual + * menu on one of its items. In the signal handler, the application may + * add extra items to the menu as appropriate. For example, a file manager + * may want to add a "Properties" command to the menu. + * + * It is not necessary to store the @selected_item for each menu item; + * during their GtkMenuItem::activate callbacks, the application can use + * gtk_places_sidebar_get_location() to get the file to which the item + * refers. + * + * The @menu and all its menu items are destroyed after the user + * dismisses the menu. The menu is re-created (and thus, this signal is + * emitted) every time the user activates the contextual menu. + * + * Since: 3.8 + */ + places_sidebar_signals [POPULATE_POPUP] = + g_signal_new (I_("populate-popup"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkPlacesSidebarClass, populate_popup), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, + G_TYPE_OBJECT, + G_TYPE_OBJECT); + + /** + * GtkPlacesSidebar::show-error-message: + * @sidebar: the object which received the signal. + * @primary: primary message with a summary of the error to show. + * @secondary: secondary message with details of the error to show. + * + * The places sidebar emits this signal when it needs the calling + * application to present an error message. Most of these messages + * refer to mounting or unmounting media, for example, when a drive + * cannot be started for some reason. + * + * Since: 3.8 + */ + places_sidebar_signals [SHOW_ERROR_MESSAGE] = + g_signal_new (I_("show-error-message"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkPlacesSidebarClass, show_error_message), + NULL, NULL, + _gtk_marshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_STRING); + + /** + * GtkPlacesSidebar::drag-action-requested: + * @sidebar: the object which received the signal. + * @context: #GdkDragContext with information about the drag operation + * @dest_file: #GFile with the tentative location that is being hovered for a drop + * @source_file_list: (element-type GFile) (transfer none): List of #GFile that are being dragged + * + * When the user starts a drag-and-drop operation and the sidebar needs + * to ask the application for which drag action to perform, then the + * sidebar will emit this signal. + * + * The application can evaluate the @context for customary actions, or + * it can check the type of the files indicated by @source_file_list against the + * possible actions for the destination @dest_file. + * + * The drag action to use must be the return value of the signal handler. + * + * Return value: The drag action to use, for example, #GDK_ACTION_COPY + * or #GDK_ACTION_MOVE, or 0 if no action is allowed here (i.e. drops + * are not allowed in the specified @dest_file). + * + * Since: 3.8 + */ + places_sidebar_signals [DRAG_ACTION_REQUESTED] = + g_signal_new (I_("drag-action-requested"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkPlacesSidebarClass, drag_action_requested), + NULL, NULL, + _gtk_marshal_INT__OBJECT_OBJECT_POINTER, + G_TYPE_INT, 3, + GDK_TYPE_DRAG_CONTEXT, + G_TYPE_OBJECT, + G_TYPE_POINTER /* GList of GFile */ ); + + /** + * GtkPlacesSidebar::drag-action-ask: + * @sidebar: the object which received the signal. + * @actions: Possible drag actions that need to be asked for. + * + * The places sidebar emits this signal when it needs to ask the application + * to pop up a menu to ask the user for which drag action to perform. + * + * Return value: the final drag action that the sidebar should pass to the drag side + * of the drag-and-drop operation. + * + * Since: 3.8 + */ + places_sidebar_signals [DRAG_ACTION_ASK] = + g_signal_new (I_("drag-action-ask"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkPlacesSidebarClass, drag_action_ask), + NULL, NULL, + _gtk_marshal_INT__INT, + G_TYPE_INT, 1, + G_TYPE_INT); + + /** + * GtkPlacesSidebar::drag-perform-drop: + * @sidebar: the object which received the signal. + * @dest_file: Destination #GFile. + * @source_file_list: (element-type GFile) (transfer none): #GList of #GFile that got dropped. + * @action: Drop action to perform. + * + * The places sidebar emits this signal when the user completes a + * drag-and-drop operation and one of the sidebar's items is the + * destination. This item is in the @dest_file, and the + * @source_file_list has the list of files that are dropped into it and + * which should be copied/moved/etc. based on the specified @action. + * + * Since: 3.8 + */ + places_sidebar_signals [DRAG_PERFORM_DROP] = + g_signal_new (I_("drag-perform-drop"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkPlacesSidebarClass, drag_perform_drop), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_POINTER_INT, + G_TYPE_NONE, 3, + G_TYPE_OBJECT, + G_TYPE_POINTER, /* GList of GFile */ + G_TYPE_INT); + + properties[PROP_LOCATION] = + g_param_spec_object ("location", + P_("Location to select"), + P_("The location to highlight in the sidebar"), + G_TYPE_FILE, + G_PARAM_READWRITE); + properties[PROP_OPEN_FLAGS] = + g_param_spec_flags ("open-flags", + P_("The open modes supported for this widget"), + P_("The set of open modes supported for this widget"), + GTK_TYPE_PLACES_OPEN_FLAGS, + GTK_PLACES_OPEN_NORMAL, + G_PARAM_READWRITE); + properties[PROP_SHOW_DESKTOP] = + g_param_spec_boolean ("show-desktop", + P_("Whether to show desktop"), + P_("Whether the sidebar includes a builtin shortcut to the desktop folder"), + FALSE, + G_PARAM_READWRITE); + + g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties); +} + +/** + * gtk_places_sidebar_new: + * + * Creates a new #GtkPlacesSidebar widget. The application should connect + * to at least the #GtkPlacesSidebar::open-location signal to be notified + * when the user makes a selection in the sidebar. + */ +GtkWidget * +gtk_places_sidebar_new (void) +{ + return GTK_WIDGET (g_object_new (gtk_places_sidebar_get_type (), NULL)); +} + + + +/* Drag and drop interfaces */ + +/* GtkTreeDragSource::row_draggable implementation for the shortcuts model */ +static gboolean +shortcuts_model_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModel *model; + GtkTreeIter iter; + PlaceType place_type; + SectionType section_type; + + model = GTK_TREE_MODEL (drag_source); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + -1); + + if (place_type != PLACES_HEADING && section_type == SECTION_BOOKMARKS) + return TRUE; + + return FALSE; +} + +/* Fill the GtkTreeDragSourceIface vtable */ +static void +shortcuts_model_class_init (ShortcutsModelClass *klass) +{ +} + +static void +shortcuts_model_init (ShortcutsModel *model) +{ + model->sidebar = NULL; +} + +static void +shortcuts_model_drag_source_iface_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = shortcuts_model_row_draggable; +} + +static GtkListStore * +shortcuts_model_new (GtkPlacesSidebar *sidebar) +{ + ShortcutsModel *model; + GType model_types[PLACES_SIDEBAR_COLUMN_COUNT] = { + G_TYPE_INT, + G_TYPE_STRING, + G_TYPE_DRIVE, + G_TYPE_VOLUME, + G_TYPE_MOUNT, + G_TYPE_STRING, + G_TYPE_ICON, + G_TYPE_INT, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_STRING, + G_TYPE_INT, + G_TYPE_STRING + }; + + model = g_object_new (shortcuts_model_get_type (), NULL); + model->sidebar = sidebar; + + gtk_list_store_set_column_types (GTK_LIST_STORE (model), + PLACES_SIDEBAR_COLUMN_COUNT, + model_types); + + return GTK_LIST_STORE (model); +} + + + +/* Public methods for GtkPlacesSidebar */ + +/** + * gtk_places_sidebar_set_open_flags: + * @sidebar: a places sidebar + * @flags: Bitmask of modes in which the calling application can open locations + * + * Sets the way in which the calling application can open new locations from + * the places sidebar. For example, some applications only open locations + * "directly" into their main view, while others may support opening locations + * in a new notebook tab or a new window. + * + * This function is used to tell the places @sidebar about the ways in which the + * application can open new locations, so that the sidebar can display (or not) + * the "Open in new tab" and "Open in new window" menu items as appropriate. + * + * When the #GtkPlacesSidebar::open-location signal is emitted, its flags + * argument will be set to one of the @flags that was passed in + * gtk_places_sidebar_set_open_flags(). + * + * Passing 0 for @flags will cause #GTK_PLACES_OPEN_NORMAL to always be sent + * to callbacks for the "open-location" signal. + * + * Since: 3.8 + */ +void +gtk_places_sidebar_set_open_flags (GtkPlacesSidebar *sidebar, GtkPlacesOpenFlags flags) +{ + g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar)); + + if (sidebar->open_flags != flags) { + sidebar->open_flags = flags; + g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_OPEN_FLAGS]); + } +} + +GtkPlacesOpenFlags +gtk_places_sidebar_get_open_flags (GtkPlacesSidebar *sidebar) +{ + g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), 0); + + return sidebar->open_flags; +} + +/** + * gtk_places_sidebar_set_location: + * @sidebar: a places sidebar + * @location: (allow-none): location to select, or #NULL for no current path + * + * Sets the location that is being shown in the widgets surrounding the + * @sidebar, for example, in a folder view in a file manager. In turn, the + * @sidebar will highlight that location if it is being shown in the list of + * places, or it will unhighlight everything if the @location is not among the + * places in the list. + * + * Since: 3.8 + */ +void +gtk_places_sidebar_set_location (GtkPlacesSidebar *sidebar, GFile *location) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + gboolean valid; + char *iter_uri; + char *uri; + + g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar)); + + selection = gtk_tree_view_get_selection (sidebar->tree_view); + gtk_tree_selection_unselect_all (selection); + + if (location == NULL) + goto out; + + uri = g_file_get_uri (location); + + valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store), &iter); + while (valid) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_URI, &iter_uri, + -1); + if (iter_uri != NULL) { + if (strcmp (iter_uri, uri) == 0) { + g_free (iter_uri); + gtk_tree_selection_select_iter (selection, &iter); + break; + } + g_free (iter_uri); + } + valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store), &iter); + } + + g_free (uri); + + out: + g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_LOCATION]); +} + +/** + * gtk_places_sidebar_get_location: + * @sidebar: a places sidebar + * + * Gets the currently-selected location in the @sidebar. This can be #NULL when + * nothing is selected, for example, when gtk_places_sidebar_set_location() has + * been called with a location that is not among the sidebar's list of places to + * show. + * + * You can use this function to get the selection in the @sidebar. Also, if you + * connect to the #GtkPlacesSidebar::popup-menu signal, you can use this + * function to get the location that is being referred to during the callbacks + * for your menu items. + * + * Returns: (transfer full): a GFile with the selected location, or #NULL if nothing is visually + * selected. + * + * Since: 3.8 + */ +GFile * +gtk_places_sidebar_get_location (GtkPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + GFile *file; + + g_return_val_if_fail (sidebar != NULL, NULL); + + file = NULL; + + if (get_selected_iter (sidebar, &iter)) { + char *uri; + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + + file = g_file_new_for_uri (uri); + g_free (uri); + } + + return file; +} + +/** + * gtk_places_sidebar_set_show_desktop: + * @sidebar: a places sidebar + * @show_desktop: whether to show an item for the Desktop folder + * + * Sets whether the @sidebar should show an item for the Desktop folder; this is off by default. + * An application may want to turn this on if the desktop environment actually supports the + * notion of a desktop. + * + * Since: 3.8 + */ +void +gtk_places_sidebar_set_show_desktop (GtkPlacesSidebar *sidebar, gboolean show_desktop) +{ + g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar)); + + show_desktop = !!show_desktop; + if (sidebar->show_desktop != show_desktop) { + sidebar->show_desktop = show_desktop; + update_places (sidebar); + g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_DESKTOP]); + } +} + +/** + * gtk_places_sidebar_get_show_desktop: + * @sidebar: a places sidebar + * + * Returns the value previously set with gtk_places_sidebar_set_show_desktop() + * + * Return value: %TRUE if the sidebar will display a builtin shortcut to the desktop folder. + * + * Since: 3.8 + */ +gboolean +gtk_places_sidebar_get_show_desktop (GtkPlacesSidebar *sidebar) +{ + g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), FALSE); + + return sidebar->show_desktop; +} + +static GSList * +find_shortcut_link (GtkPlacesSidebar *sidebar, GFile *location) +{ + GSList *l; + + for (l = sidebar->shortcuts; l; l = l->next) { + GFile *shortcut; + + shortcut = G_FILE (l->data); + if (g_file_equal (shortcut, location)) + return l; + } + + return NULL; +} + +/** + * gtk_places_sidebar_add_shortcut: + * @sidebar: a places sidebar + * @location: location to add as an application-specific shortcut + * + * Applications may want to present some folders in the places sidebar if + * they could be immediately useful to users. For example, a drawing + * program could add a "/usr/share/clipart" location when the sidebar is + * being used in an "Insert Clipart" dialog box. + * + * This function adds the specified @location to a special place for immutable + * shortcuts. The shortcuts are application-specific; they are not shared + * across applications, and they are not persistent. If this function + * is called multiple times with different locations, then they are added + * to the sidebar's list in the same order as the function is called. + * + * Since: 3.8 + */ +void +gtk_places_sidebar_add_shortcut (GtkPlacesSidebar *sidebar, GFile *location) +{ + g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar)); + g_return_if_fail (G_IS_FILE (location)); + + g_object_ref (location); + sidebar->shortcuts = g_slist_append (sidebar->shortcuts, location); + + update_places (sidebar); +} + +/** + * gtk_places_sidebar_remove_shortcut: + * @sidebar: a places sidebar + * @location: location to remove + * + * Removes an application-specific shortcut that has been previously been + * inserted with gtk_places_sidebar_add_shortcut(). If the @location is not a + * shortcut in the sidebar, then nothing is done. + * + * Since: 3.8 + */ +void +gtk_places_sidebar_remove_shortcut (GtkPlacesSidebar *sidebar, GFile *location) +{ + GSList *link; + GFile *shortcut; + + g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar)); + g_return_if_fail (G_IS_FILE (location)); + + link = find_shortcut_link (sidebar, location); + if (!link) + return; + + shortcut = G_FILE (link->data); + g_object_unref (shortcut); + + sidebar->shortcuts = g_slist_delete_link (sidebar->shortcuts, link); + update_places (sidebar); +} + +/** + * gtk_places_sidebar_list_shortcuts: + * @sidebar: a places sidebar + * + * Return value: (element-type GFile) (transfer full): A #GSList of #GFile of the locations + * that have been added as application-specific shortcuts with gtk_places_sidebar_add_shortcut(). + * To free this list, you can use + * |[ + * g_slist_free_full (list, (GDestroyNotify) g_object_unref); + * ]| + * + * Since: 3.8 + */ +GSList * +gtk_places_sidebar_list_shortcuts (GtkPlacesSidebar *sidebar) +{ + g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), FALSE); + + return g_slist_copy_deep (sidebar->shortcuts, (GCopyFunc) g_object_ref, NULL); +} + +/** + * gtk_places_sidebar_get_nth_bookmark: + * @sidebar: a places sidebar + * @n: index of the bookmark to query + * + * This function queries the bookmarks added by the user to the places sidebar, + * and returns one of them. This function is used by #GtkFileChooser to implement + * the "Alt-1", "Alt-2", etc. shortcuts, which activate the cooresponding bookmark. + * + * Return value: (transfer full): The bookmark specified by the index @n, or + * #NULL if no such index exist. Note that the indices start at 0, even though + * the file chooser starts them with the keyboard shortcut "Alt-1". + * + * Since: 3.8 + */ +GFile * +gtk_places_sidebar_get_nth_bookmark (GtkPlacesSidebar *sidebar, int n) +{ + GtkTreeIter iter; + int k; + GFile *file; + + g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), NULL); + + file = NULL; + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store), &iter)) { + k = 0; + + do { + PlaceType place_type; + char *uri; + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + + if (place_type == PLACES_BOOKMARK) { + if (k == n) { + file = g_file_new_for_uri (uri); + g_free (uri); + break; + } + + g_free (uri); + k++; + } + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store), &iter)); + } + + return file; +} diff --git a/gtk/gtkplacessidebar.h b/gtk/gtkplacessidebar.h new file mode 100644 index 0000000000..40e33a36ab --- /dev/null +++ b/gtk/gtkplacessidebar.h @@ -0,0 +1,100 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * GtkPlacesSidebar - sidebar widget for places in the filesystem + * + * This code comes from Nautilus, GNOME's file manager. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk) + * Federico Mena Quintero <federico@gnome.org> + * + */ +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#ifndef __GTK_PLACES_SIDEBAR_H__ +#define __GTK_PLACES_SIDEBAR_H__ + +#include <gtk/gtkwidget.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_PLACES_SIDEBAR (gtk_places_sidebar_get_type ()) +#define GTK_PLACES_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PLACES_SIDEBAR, GtkPlacesSidebar)) +#define GTK_PLACES_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PLACES_SIDEBAR, GtkPlacesSidebarClass)) +#define GTK_IS_PLACES_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PLACES_SIDEBAR)) +#define GTK_IS_PLACES_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PLACES_SIDEBAR)) +#define GTK_PLACES_SIDEBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PLACES_SIDEBAR, GtkPlacesSidebarClass)) + +typedef struct _GtkPlacesSidebar GtkPlacesSidebar; +typedef struct _GtkPlacesSidebarClass GtkPlacesSidebarClass; + +/** + * GtkPlacesOpenFlags: + * @GTK_PLACES_OPEN_NORMAL: This is the default mode that #GtkPlacesSidebar uses if no other flags + * are specified. It indicates that the calling application should open the selected location + * in the normal way, for example, in the folder view beside the sidebar. + * @GTK_PLACES_OPEN_NEW_TAB: When passed to gtk_places_sidebar_set_open_flags(), this indicates + * that the application can open folders selected from the sidebar in new tabs. This value + * will be passed to the #GtkPlacesSidebar::open-location signal when the user selects + * that a location be opened in a new tab instead of in the standard fashion. + * @GTK_PLACES_OPEN_NEW_WINDOW: Similar to @GTK_PLACES_OPEN_NEW_TAB, but indicates that the application + * can open folders in new windows. + * + * These flags serve two purposes. First, the application can call gtk_places_sidebar_set_open_flags() + * using these flags as a bitmask. This tells the sidebar that the application is able to open + * folders selected from the sidebar in various ways, for example, in new tabs or in new windows in + * addition to the normal mode. + * + * Second, when one of these values gets passed back to the application in the + * #GtkPlacesSidebar::open-location signal, it means that the application should + * open the selected location in the normal way, in a new tab, or in a new + * window. The sidebar takes care of determining the desired way to open the location, + * based on the modifier keys that the user is pressing at the time the selection is made. + * + * If the application never calls gtk_places_sidebar_set_open_flags(), then the sidebar will only + * use #GTK_PLACES_OPEN_NORMAL in the #GtkPlacesSidebar::open-location signal. This is the + * default mode of operation. + */ +typedef enum { + GTK_PLACES_OPEN_NORMAL = 1 << 0, + GTK_PLACES_OPEN_NEW_TAB = 1 << 1, + GTK_PLACES_OPEN_NEW_WINDOW = 1 << 2 +} GtkPlacesOpenFlags; + +GType gtk_places_sidebar_get_type (void); +GtkWidget *gtk_places_sidebar_new (void); + +GtkPlacesOpenFlags gtk_places_sidebar_get_open_flags (GtkPlacesSidebar *sidebar); +void gtk_places_sidebar_set_open_flags (GtkPlacesSidebar *sidebar, GtkPlacesOpenFlags flags); + +GFile *gtk_places_sidebar_get_location (GtkPlacesSidebar *sidebar); +void gtk_places_sidebar_set_location (GtkPlacesSidebar *sidebar, GFile *location); + +gboolean gtk_places_sidebar_get_show_desktop (GtkPlacesSidebar *sidebar); +void gtk_places_sidebar_set_show_desktop (GtkPlacesSidebar *sidebar, gboolean show_desktop); + +void gtk_places_sidebar_add_shortcut (GtkPlacesSidebar *sidebar, GFile *location); +void gtk_places_sidebar_remove_shortcut (GtkPlacesSidebar *sidebar, GFile *location); +GSList *gtk_places_sidebar_list_shortcuts (GtkPlacesSidebar *sidebar); + +GFile *gtk_places_sidebar_get_nth_bookmark (GtkPlacesSidebar *sidebar, int n); + +G_END_DECLS + +#endif /* __GTK_PLACES_SIDEBAR_H__ */ diff --git a/gtk/gtktrashmonitor.c b/gtk/gtktrashmonitor.c new file mode 100644 index 0000000000..c61e9db1b1 --- /dev/null +++ b/gtk/gtktrashmonitor.c @@ -0,0 +1,267 @@ +/* GTK - The GIMP Toolkit + * gtktrashmonitor.h: Monitor the trash:/// folder to see if there is trash or not + * Copyright (C) 2011 Suse + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: Federico Mena Quintero <federico@gnome.org> + */ + +#include "config.h" + +#include "gtkintl.h" +#include "gtkmarshalers.h" +#include "gtktrashmonitor.h" + +struct _GtkTrashMonitor +{ + GObject parent; + + GFileMonitor *file_monitor; + gulong file_monitor_changed_id; + + guint has_trash : 1; +}; + +struct _GtkTrashMonitorClass +{ + GObjectClass parent_class; + + void (* trash_state_changed) (GtkTrashMonitor *monitor); +}; + +enum { + TRASH_STATE_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +G_DEFINE_TYPE (GtkTrashMonitor, _gtk_trash_monitor, G_TYPE_OBJECT) + +static GtkTrashMonitor *the_trash_monitor; + +#define ICON_NAME_TRASH_EMPTY "user-trash-symbolic" +#define ICON_NAME_TRASH_FULL "user-trash-full-symbolic" + +static void +gtk_trash_monitor_dispose (GObject *object) +{ + GtkTrashMonitor *monitor; + + monitor = GTK_TRASH_MONITOR (object); + + if (monitor->file_monitor) + { + g_signal_handler_disconnect (monitor->file_monitor, monitor->file_monitor_changed_id); + monitor->file_monitor_changed_id = 0; + + g_clear_object (&monitor->file_monitor); + } + + G_OBJECT_CLASS (_gtk_trash_monitor_parent_class)->dispose (object); +} + +static void +_gtk_trash_monitor_class_init (GtkTrashMonitorClass *class) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) class; + + gobject_class->dispose = gtk_trash_monitor_dispose; + + signals[TRASH_STATE_CHANGED] = + g_signal_new (I_("trash-state-changed"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GtkTrashMonitorClass, trash_state_changed), + NULL, NULL, + _gtk_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +/* Updates the internal has_trash flag and emits the "trash-state-changed" signal */ +static void +update_has_trash_and_notify (GtkTrashMonitor *monitor, + gboolean has_trash) +{ + monitor->has_trash = !!has_trash; + + g_signal_emit (monitor, signals[TRASH_STATE_CHANGED], 0); +} + +static void +trash_enumerate_next_files_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GtkTrashMonitor *monitor = GTK_TRASH_MONITOR (user_data); + GFileEnumerator *enumerator; + GList *infos; + + enumerator = G_FILE_ENUMERATOR (source); + + infos = g_file_enumerator_next_files_finish (enumerator, result, NULL); + if (infos) + { + update_has_trash_and_notify (monitor, TRUE); + g_list_free_full (infos, g_object_unref); + } + else + { + update_has_trash_and_notify (monitor, FALSE); + } + + g_object_unref (monitor); /* was reffed in recompute_trash_state() */ +} + +/* Callback used from g_file_enumerate_children_async() - this is what enumerates "trash:///" */ +static void +trash_enumerate_children_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GtkTrashMonitor *monitor = GTK_TRASH_MONITOR (user_data); + GFileEnumerator *enumerator; + + enumerator = g_file_enumerate_children_finish (G_FILE (source), result, NULL); + if (enumerator) + { + g_file_enumerator_next_files_async (enumerator, + 1, + G_PRIORITY_DEFAULT, + NULL, + trash_enumerate_next_files_cb, + monitor); + g_object_unref (enumerator); + } + else + { + update_has_trash_and_notify (monitor, FALSE); + g_object_unref (monitor); /* was reffed in recompute_trash_state() */ + } +} + +/* Asynchronously recomputes whether there is trash or not */ +static void +recompute_trash_state (GtkTrashMonitor *monitor) +{ + GFile *file; + + g_object_ref (monitor); + + file = g_file_new_for_uri ("trash:///"); + g_file_enumerate_children_async (file, + G_FILE_ATTRIBUTE_STANDARD_TYPE, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + NULL, + trash_enumerate_children_cb, + monitor); + + g_object_unref (file); +} + +/* Callback used when the "trash:///" file monitor changes; we just recompute the trash state + * whenever something happens. + */ +static void +file_monitor_changed_cb (GFileMonitor *file_monitor, + GFile *child, + GFile *other_file, + GFileMonitorEvent event_type, + GtkTrashMonitor *monitor) +{ + recompute_trash_state (monitor); +} + +static void +_gtk_trash_monitor_init (GtkTrashMonitor *monitor) +{ + GFile *file; + + file = g_file_new_for_uri ("trash:///"); + + monitor->file_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL); + + g_object_unref (file); + + if (monitor->file_monitor) + monitor->file_monitor_changed_id = g_signal_connect (monitor->file_monitor, "changed", + G_CALLBACK (file_monitor_changed_cb), monitor); + + recompute_trash_state (monitor); +} + +/** + * _gtk_trash_monitor_get: + * + * Return value: (transfer full): a new reference to the singleton + * #GtkTrashMonitor object. Be sure to call g_object_unref() on it when you are + * done with the trash monitor. + */ +GtkTrashMonitor * +_gtk_trash_monitor_get (void) +{ + if (the_trash_monitor != NULL) + { + g_object_ref (the_trash_monitor); + } + else + { + the_trash_monitor = g_object_new (GTK_TYPE_TRASH_MONITOR, NULL); + g_object_add_weak_pointer (G_OBJECT (the_trash_monitor), (gpointer *) &the_trash_monitor); + } + + return the_trash_monitor; +} + +/** + * _gtk_trash_monitor_get_icon: + * @monitor: a #GtkTrashMonitor + * + * Return value: (transfer full): the #GIcon that should be used to represent + * the state of the trash folder on screen, based on whether there is trash or + * not. + */ +GIcon * +_gtk_trash_monitor_get_icon (GtkTrashMonitor *monitor) +{ + const char *icon_name; + + g_return_val_if_fail (GTK_IS_TRASH_MONITOR (monitor), NULL); + + if (monitor->has_trash) + icon_name = ICON_NAME_TRASH_FULL; + else + icon_name = ICON_NAME_TRASH_EMPTY; + + return g_themed_icon_new (icon_name); +} + +/** + * _gtk_trash_monitor_get_has_trash: + * @monitor: a #GtkTrashMonitor + * + * Return value: #TRUE if there is trash in the trash:/// folder, or #FALSE otherwise. + */ +gboolean +_gtk_trash_monitor_get_has_trash (GtkTrashMonitor *monitor) +{ + g_return_val_if_fail (GTK_IS_TRASH_MONITOR (monitor), FALSE); + + return monitor->has_trash; +} diff --git a/gtk/gtktrashmonitor.h b/gtk/gtktrashmonitor.h new file mode 100644 index 0000000000..f458b2bab3 --- /dev/null +++ b/gtk/gtktrashmonitor.h @@ -0,0 +1,48 @@ +/* GTK - The GIMP Toolkit + * gtktrashmonitor.h: Monitor the trash:/// folder to see if there is trash or not + * Copyright (C) 2011 Suse + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Authors: Federico Mena Quintero <federico@gnome.org> + */ + +#ifndef __GTK_TRASH_MONITOR_H__ +#define __GTK_TRASH_MONITOR_H__ + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_TRASH_MONITOR (_gtk_trash_monitor_get_type ()) +#define GTK_TRASH_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TRASH_MONITOR, GtkTrashMonitor)) +#define GTK_TRASH_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TRASH_MONITOR, GtkTrashMonitorClass)) +#define GTK_IS_TRASH_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TRASH_MONITOR)) +#define GTK_IS_TRASH_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TRASH_MONITOR)) +#define GTK_TRASH_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TRASH_MONITOR, GtkTrashMonitorClass)) + +typedef struct _GtkTrashMonitor GtkTrashMonitor; +typedef struct _GtkTrashMonitorClass GtkTrashMonitorClass; + +GType _gtk_trash_monitor_get_type (void); +GtkTrashMonitor *_gtk_trash_monitor_get (void); + +GIcon *_gtk_trash_monitor_get_icon (GtkTrashMonitor *monitor); + +gboolean _gtk_trash_monitor_get_has_trash (GtkTrashMonitor *monitor); + +G_END_DECLS + +#endif /* __GTK_TRASH_MONITOR_H__ */ diff --git a/gtk/org.gtk.Settings.FileChooser.gschema.xml b/gtk/org.gtk.Settings.FileChooser.gschema.xml index d7d87058af..f2424e5655 100644 --- a/gtk/org.gtk.Settings.FileChooser.gschema.xml +++ b/gtk/org.gtk.Settings.FileChooser.gschema.xml @@ -33,6 +33,11 @@ <value nick='descending' value='1'/> </enum> + <enum id='org.gtk.Settings.FileChooser.StartupMode'> + <value nick='recent' value='0'/> + <value nick='cwd' value='1'/> + </enum> + <schema id='org.gtk.Settings.FileChooser' path='/org/gtk/settings/file-chooser/'> <key name='last-folder-uri' type='s'> <default>""</default> @@ -63,6 +68,9 @@ <key name='window-size' type='(ii)'> <default>(-1, -1)</default> </key> + <key name='startup-mode' enum='org.gtk.Settings.FileChooser.StartupMode'> + <default>'recent'</default> + </key> <key name='sidebar-width' type='i'> <default>148</default> </key> diff --git a/gtk/tests/filechooser.c b/gtk/tests/filechooser.c index 2ca7660bb2..e37babe46d 100644 --- a/gtk/tests/filechooser.c +++ b/gtk/tests/filechooser.c @@ -33,7 +33,6 @@ #include <string.h> #include <glib/gprintf.h> #include <gtk/gtk.h> -#include "gtk/gtkfilechooserprivate.h" #include "gtk/gtkfilechooserdefault.h" #include "gtk/gtkfilechooserentry.h" diff --git a/po/POTFILES.in b/po/POTFILES.in index 763e5c58f2..d97e1bad19 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -57,6 +57,7 @@ gtk/gtkassistant.c gtk/gtkbbox.c gtk/gtkbin.c gtk/gtkbindings.c +gtk/gtkbookmarksmanager.c gtk/gtkbox.c gtk/gtkbubblewindow.c gtk/gtkbuildable.c |