[nautilus/wip/antoniof/gtk4-preparation-placessidebar] general: Use in-tree copy of GtkPlacesSidebar
- From: António Fernandes <antoniof src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [nautilus/wip/antoniof/gtk4-preparation-placessidebar] general: Use in-tree copy of GtkPlacesSidebar
- Date: Wed, 22 Dec 2021 15:03:23 +0000 (UTC)
commit bc9b87eba7fc17cd22771417d9f97b5b4e3a5da0
Author: António Fernandes <antoniof gnome org>
Date: Mon Dec 13 22:18:00 2021 +0000
general: Use in-tree copy of GtkPlacesSidebar
GtkPlacesSidebar is a public GTK 3 widget, but private in GTK 4, so we
need to have the sidebar in our own codebase if we are to keep using it.
Extend the script already in use for the places view and use it to copy
the places sidebar and its dependencies and patch it as necessary.
Also, construct it from code, because this in-tree places sidebar cannot
be used in a GtkBuilder UI template.
po/POTFILES.in | 3 +
src/gtk/gtk-code-generator.sh | 106 +-
src/gtk/nautilusgtkbookmarksmanager.c | 594 +++
src/gtk/nautilusgtkbookmarksmanager.h | 90 +
src/gtk/nautilusgtkplacessidebar.c | 5633 +++++++++++++++++++++++++++++
src/gtk/nautilusgtkplacessidebar.h | 159 +
src/gtk/nautilusgtkplacessidebarprivate.h | 60 +
src/gtk/nautilusgtkplacesview.c | 46 +-
src/gtk/nautilusgtkplacesviewprivate.h | 9 +-
src/gtk/nautilusgtkplacesviewrow.c | 8 -
src/gtk/nautilusgtksidebarrow.c | 667 ++++
src/gtk/nautilusgtksidebarrow.ui | 92 +
src/gtk/nautilusgtksidebarrowprivate.h | 61 +
src/meson.build | 7 +
src/nautilus-places-view.c | 6 +-
src/nautilus-window.c | 115 +-
src/resources/nautilus.gresource.xml | 1 +
src/resources/ui/nautilus-window.ui | 11 -
18 files changed, 7532 insertions(+), 136 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 48e7fd43f..cee23e3f5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -104,3 +104,6 @@ src/gtk/nautilusgtkplacesview.c
src/gtk/nautilusgtkplacesviewrow.c
src/gtk/nautilusgtkplacesviewrow.ui
src/gtk/nautilusgtkplacesview.ui
+src/gtk/nautilusgtkbookmarksmanager.c
+src/gtk/nautilusgtkplacessidebar.c
+src/gtk/nautilusgtksidebarrow.ui
diff --git a/src/gtk/gtk-code-generator.sh b/src/gtk/gtk-code-generator.sh
index 147efb3bd..e6f2ac0d0 100755
--- a/src/gtk/gtk-code-generator.sh
+++ b/src/gtk/gtk-code-generator.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-# Fetch the GtkPlacesView files but rename the symbols to avoid symbol clashes
+# Fetch the GtkPlaces* files but rename the symbols to avoid symbol clashes
# when using the file chooser inside nautilus i.e. when activating the "move to"
# action.
# Also remove/add the neccesary bits to make it work inside nautilus
@@ -13,6 +13,7 @@ SUFIX=?h=gtk-3-24
# by order:
# type substitution
# remove marshalers
+# replace GtkTrashMonitor API with NautilusTrashMonitor API
# add external localization library after the always there config.h
# and remove the gtk internal P_ and I_ localization, we don't actually
# want localization of this in nautilus
@@ -20,6 +21,9 @@ SUFIX=?h=gtk-3-24
# and remove all the other types that get included by the general gtk.h
# remove the error when including gtk.h
# load nautilus resources, not gtk resources
+# use local sidebar header instead of private gtk one
+# in-line replace private gtkfilesystem.h helper function
+# ignore shadowed variable which we would treat as compile error
update_file () {
_source="$1"
@@ -32,62 +36,86 @@ update_file () {
-e 's/GTK_PLACES_VIEW/NAUTILUS_GTK_PLACES_VIEW/g' \
-e 's/GTK_TYPE_PLACES_VIEW/NAUTILUS_TYPE_GTK_PLACES_VIEW/g' \
-e 's/GTK_IS_PLACES_VIEW/NAUTILUS_IS_GTK_PLACES_VIEW/g' \
+ -e 's/G_DECLARE_FINAL_TYPE (NautilusGtkPlacesViewRow, nautilus_gtk_places_view_row, GTK,
PLACES_VIEW_ROW, GtkListBoxRow/ G_DECLARE_FINAL_TYPE (NautilusGtkPlacesViewRow, nautilus_gtk_places_view_row,
NAUTILUS, GTK_PLACES_VIEW_ROW, GtkListBoxRow/g' \
+ -e 's/gtkplacessidebar/nautilusgtkplacessidebar/g' \
+ -e 's/gtk_places_sidebar/nautilus_gtk_places_sidebar/g' \
+ -e 's/GtkPlacesSidebar/NautilusGtkPlacesSidebar/g' \
+ -e 's/GTK_PLACES_SIDEBAR/NAUTILUS_GTK_PLACES_SIDEBAR/g' \
+ -e 's/GTK_TYPE_PLACES_SIDEBAR/NAUTILUS_TYPE_GTK_PLACES_SIDEBAR/g' \
+ -e 's/GTK_IS_PLACES_SIDEBAR/NAUTILUS_IS_GTK_PLACES_SIDEBAR/g' \
+ -e 's/GtkPlacesOpen/NautilusGtkPlacesOpen/g' \
+ -e 's/GTK_PLACES_OPEN/NAUTILUS_GTK_PLACES_OPEN/g' \
+ -e 's/gtkbookmarksmanager/nautilusgtkbookmarksmanager/g' \
+ -e 's/gtk_bookmarks_manager/nautilus_gtk_bookmarks_manager/g' \
+ -e 's/GtkBookmarksManager/NautilusGtkBookmarksManager/g' \
+ -e 's/GTK_BOOKMARKS_MANAGER/NAUTILUS_GTK_BOOKMARKS_MANAGER/g' \
+ -e 's/GTK_TYPE_BOOKMARKS_MANAGER/NAUTILUS_TYPE_GTK_BOOKMARKS_MANAGER/g' \
+ -e 's/GTK_IS_BOOKMARKS_MANAGER/NAUTILUS_IS_GTK_BOOKMARKS_MANAGER/g' \
+ -e 's/gtksidebarrow/nautilusgtksidebarrow/g' \
+ -e 's/gtk_sidebar_row/nautilus_gtk_sidebar_row/g' \
+ -e 's/GtkSidebarRow/NautilusGtkSidebarRow/g' \
+ -e 's/GTK_SIDEBAR_ROW/NAUTILUS_GTK_SIDEBAR_ROW/g' \
+ -e 's/GTK_TYPE_SIDEBAR_ROW/NAUTILUS_TYPE_GTK_SIDEBAR_ROW/g' \
+ -e 's/GTK_IS_SIDEBAR_ROW/NAUTILUS_IS_GTK_SIDEBAR_ROW/g' \
+ -e 's/G_DECLARE_FINAL_TYPE (NautilusGtkSidebarRow, nautilus_gtk_sidebar_row, GTK, SIDEBAR_ROW,
GtkListBoxRow/ G_DECLARE_FINAL_TYPE (NautilusGtkSidebarRow, nautilus_gtk_sidebar_row, NAUTILUS,
GTK_SIDEBAR_ROW, GtkListBoxRow/g' \
-e 's/_gtk_marshal_VOID__STRING_STRING/NULL/g' \
-e '/gtkmarshalers.h/d' \
-e '/g_signal_set_va_marshaller/,+2d' \
-e 's/_gtk_marshal_VOID__OBJECT_FLAGS/NULL/g' \
+ -e 's/_gtk_marshal_VOID__OBJECT_POINTER_INT/NULL/g' \
+ -e 's/_gtk_marshal_VOID__OBJECT_OBJECT_OBJECT/NULL/g' \
+ -e 's/_gtk_marshal_INT__OBJECT_OBJECT_POINTER/NULL/g' \
+ -e 's/_gtk_marshal_INT__INT/NULL/g' \
+ -e 's/gtktrashmonitor.h/nautilus-trash-monitor.h/g' \
+ -e 's/GtkTrashMonitor/NautilusTrashMonitor/g' \
+ -e "s/_gtk_trash_monitor_get_icon (\(.*\))/nautilus_trash_monitor_get_symbolic_icon ()/g" \
+ -e "s/_gtk_trash_monitor_get_has_trash (\(.*\))/!nautilus_trash_monitor_is_empty ()/g" \
+ -e 's/_gtk_trash_monitor_get/nautilus_trash_monitor_get/g' \
-e '/"config.h"/a #include <glib\/gi18n.h>' \
-e "s/P_(\(.*\))/\1/" \
-e "s/I_(\(.*\))/\1/" \
-e '/"config.h"/a #include <gtk\/gtk.h>' \
-e '/gtktypebuiltins.h/d' \
- -e '/gtkplacessidebar.h/d' \
-e '/gtkintl.h/d' \
- -e '/gtkbox.h/d' \
+ -e '/<gtk\/gtkbox.h>/d' \
+ -e '/"gtkbox\(.*\).h"/d' \
+ -e '/"gtkbu\(.*\).h"/d' \
+ -e '/"gtkc\(.*\).h"/d' \
+ -e '/"gtkd\(.*\).h"/d' \
+ -e '/"gtke\(.*\).h"/d' \
+ -e '/"gtkf\(.*\).h"/d' \
+ -e '/"gtkg\(.*\).h"/d' \
+ -e '/"gtki\(.*\).h"/d' \
+ -e '/"gtkl\(.*\).h"/d' \
+ -e '/"gtkm\(.*\).h"/d' \
+ -e '/"gtkpo\(.*\).h"/d' \
+ -e '/"gtkr\(.*\).h"/d' \
+ -e '/"gtksc\(.*\).h"/d' \
+ -e '/"gtkse\(.*\).h"/d' \
+ -e '/"gtksi\(.*\).h"/d' \
+ -e '/"gtksp\(.*\).h"/d' \
+ -e '/"gtkst\(.*\).h"/d' \
+ -e '/"gtkt\(.*\).h"/d' \
+ -e '/"gtkw\(.*\).h"/d' \
-e '/#error/d' \
-e 's/gtk\/libgtk/gnome\/nautilus\/gtk/g' \
+ -e 's/<gtk\/nautilusgtkplacessidebar.h>/"nautilusgtkplacessidebar.h"'/g \
+ -e 's/_gtk_file_info_consider_as_directory (info)/(g_file_info_get_file_type (info) ==
G_FILE_TYPE_DIRECTORY || g_file_info_get_file_type (info) == G_FILE_TYPE_MOUNTABLE ||
g_file_info_get_file_type (info) == G_FILE_TYPE_SHORTCUT)/g' \
+ -e '/#include "nautilus-trash-monitor.h"/a #pragma GCC diagnostic ignored "-Wshadow"' \
> "${_dest}"
}
update_file "${URL}/gtkplacesview.c${SUFIX}" "nautilusgtkplacesview.c"
update_file "${URL}/gtkplacesviewprivate.h${SUFIX}" "nautilusgtkplacesviewprivate.h"
update_file "${URLUI}/gtkplacesview.ui${SUFIX}" "nautilusgtkplacesview.ui"
-
-# Since comments are not allowed inside the sed line, this is what it will do
-# by order:
-# type substitution
-# use the correct prefixes for type definition
-# add external localization library after the always there config.h
-# and remove the gtk internal P_ and I_ localization, we don't actually
-# want localization of this in nautilus
-# include gtk.h library after the always there config.h
-# and remove all the other types that get included by the general gtk.h
-# remove the error when including gtk.h
-# load nautilus resources, not gtk resources
-update_file () {
- _source="$1"
- _dest="$2"
-
- curl "${_source}" | sed \
- -e 's/gtkplacesviewrow/nautilusgtkplacesviewrow/g' \
- -e 's/gtk_places_view_row/nautilus_gtk_places_view_row/g' \
- -e 's/GtkPlacesViewRow/NautilusGtkPlacesViewRow/g' \
- -e 's/GTK_PLACES_VIEW_ROW/NAUTILUS_GTK_PLACES_VIEW_ROW/g' \
- -e 's/GTK_TYPE_PLACES_VIEW_ROW/NAUTILUS_TYPE_GTK_PLACES_VIEW_ROW/g' \
- -e 's/GTK_IS_PLACES_VIEW_ROW/NAUTILUS_IS_GTK_PLACES_VIEW_ROW/g' \
- -e 's/G_DECLARE_FINAL_TYPE (NautilusGtkPlacesViewRow, nautilus_gtk_places_view_row, GTK,
PLACES_VIEW_ROW, GtkListBoxRow/ G_DECLARE_FINAL_TYPE (NautilusGtkPlacesViewRow, nautilus_gtk_places_view_row,
NAUTILUS, GTK_PLACES_VIEW_ROW, GtkListBoxRow/g' \
- -e '/"config.h"/a #include <glib\/gi18n.h>' \
- -e "s/P_(\(.*\))/\1/" \
- -e "s/I_(\(.*\))/\1/" \
- -e '/"config.h"/a #include <gtk\/gtk.h>' \
- -e '/gtksizegroup.h/d' \
- -e '/gtkwidget.h/d' \
- -e '/gtklistbox.h/d' \
- -e '/#error /d' \
- -e 's/gtk\/libgtk/gnome\/nautilus\/gtk/g' \
- > "${_dest}"
-}
-
+update_file "${URL}/gtkplacessidebar.c${SUFIX}" "nautilusgtkplacessidebar.c"
+update_file "${URL}/gtkplacessidebarprivate.h${SUFIX}" "nautilusgtkplacessidebarprivate.h"
+update_file "${URL}/gtkplacessidebar.h${SUFIX}" "nautilusgtkplacessidebar.h"
+update_file "${URL}/gtkbookmarksmanager.c${SUFIX}" "nautilusgtkbookmarksmanager.c"
+update_file "${URL}/gtkbookmarksmanager.h${SUFIX}" "nautilusgtkbookmarksmanager.h"
update_file "${URL}/gtkplacesviewrow.c${SUFIX}" "nautilusgtkplacesviewrow.c"
update_file "${URL}/gtkplacesviewrowprivate.h${SUFIX}" "nautilusgtkplacesviewrowprivate.h"
update_file "${URLUI}/gtkplacesviewrow.ui${SUFIX}" "nautilusgtkplacesviewrow.ui"
+update_file "${URL}/gtksidebarrow.c${SUFIX}" "nautilusgtksidebarrow.c"
+update_file "${URL}/gtksidebarrowprivate.h${SUFIX}" "nautilusgtksidebarrowprivate.h"
+update_file "${URLUI}/gtksidebarrow.ui${SUFIX}" "nautilusgtksidebarrow.ui"
diff --git a/src/gtk/nautilusgtkbookmarksmanager.c b/src/gtk/nautilusgtkbookmarksmanager.c
new file mode 100644
index 000000000..385d58635
--- /dev/null
+++ b/src/gtk/nautilusgtkbookmarksmanager.c
@@ -0,0 +1,594 @@
+/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
+/* GTK - The GIMP Toolkit
+ * nautilusgtkbookmarksmanager.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.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Federico Mena Quintero <federico gnome org>
+ */
+
+#include "config.h"
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nautilusgtkbookmarksmanager.h"
+
+static void
+_gtk_bookmark_free (gpointer data)
+{
+ GtkBookmark *bookmark = data;
+
+ g_object_unref (bookmark->file);
+ g_free (bookmark->label);
+ g_slice_free (GtkBookmark, bookmark);
+}
+
+static void
+set_error_bookmark_doesnt_exist (GFile *file, GError **error)
+{
+ 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);
+}
+
+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;
+ GFile *parent = NULL;
+
+ 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_utf8_validate (bookmark->label, -1, NULL))
+ g_string_append_printf (contents, " %s", bookmark->label);
+
+ g_string_append_c (contents, '\n');
+ g_free (uri);
+ }
+
+ parent = g_file_get_parent (bookmarks_file);
+ if (!g_file_make_directory_with_parents (parent, NULL, &error))
+ {
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ g_clear_error (&error);
+ else
+ goto out;
+ }
+ if (!g_file_replace_contents (bookmarks_file,
+ contents->str,
+ contents->len,
+ NULL, FALSE, 0, NULL,
+ NULL, &error))
+ goto out;
+
+ out:
+ if (error)
+ {
+ g_critical ("%s", error->message);
+ g_error_free (error);
+ }
+ g_clear_object (&parent);
+ g_string_free (contents, TRUE);
+}
+
+static void
+notify_changed (NautilusGtkBookmarksManager *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)
+{
+ NautilusGtkBookmarksManager *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_free_full (manager->bookmarks, _gtk_bookmark_free);
+ manager->bookmarks = read_bookmarks (file);
+
+ gdk_threads_enter ();
+ notify_changed (manager);
+ gdk_threads_leave ();
+ break;
+
+ default:
+ /* ignore at the moment */
+ break;
+ }
+}
+
+NautilusGtkBookmarksManager *
+_nautilus_gtk_bookmarks_manager_new (GtkBookmarksChangedFunc changed_func, gpointer changed_func_data)
+{
+ NautilusGtkBookmarksManager *manager;
+ GFile *bookmarks_file;
+ GError *error;
+
+ manager = g_new0 (NautilusGtkBookmarksManager, 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);
+ if (manager->bookmarks)
+ 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
+_nautilus_gtk_bookmarks_manager_free (NautilusGtkBookmarksManager *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);
+ }
+
+ g_slist_free_full (manager->bookmarks, _gtk_bookmark_free);
+
+ g_free (manager);
+}
+
+GSList *
+_nautilus_gtk_bookmarks_manager_list_bookmarks (NautilusGtkBookmarksManager *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
+_nautilus_gtk_bookmarks_manager_has_bookmark (NautilusGtkBookmarksManager *manager,
+ GFile *file)
+{
+ GSList *link;
+
+ link = find_bookmark_link_for_file (manager->bookmarks, file, NULL);
+ return (link != NULL);
+}
+
+gboolean
+_nautilus_gtk_bookmarks_manager_insert_bookmark (NautilusGtkBookmarksManager *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)
+ {
+ gchar *uri;
+ bookmark = link->data;
+ 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
+_nautilus_gtk_bookmarks_manager_remove_bookmark (NautilusGtkBookmarksManager *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
+ {
+ set_error_bookmark_doesnt_exist (file, error);
+ return FALSE;
+ }
+
+ bookmarks_file = get_bookmarks_file ();
+ save_bookmarks (bookmarks_file, manager->bookmarks);
+ g_object_unref (bookmarks_file);
+
+ notify_changed (manager);
+
+ return TRUE;
+}
+
+gboolean
+_nautilus_gtk_bookmarks_manager_reorder_bookmark (NautilusGtkBookmarksManager *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
+ {
+ set_error_bookmark_doesnt_exist (file, error);
+ return FALSE;
+ }
+
+ bookmarks_file = get_bookmarks_file ();
+ save_bookmarks (bookmarks_file, manager->bookmarks);
+ g_object_unref (bookmarks_file);
+
+ notify_changed (manager);
+
+ return TRUE;
+}
+
+gchar *
+_nautilus_gtk_bookmarks_manager_get_bookmark_label (NautilusGtkBookmarksManager *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
+_nautilus_gtk_bookmarks_manager_set_bookmark_label (NautilusGtkBookmarksManager *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
+ {
+ set_error_bookmark_doesnt_exist (file, error);
+ return FALSE;
+ }
+
+ bookmarks_file = get_bookmarks_file ();
+ save_bookmarks (bookmarks_file, manager->bookmarks);
+ g_object_unref (bookmarks_file);
+
+ notify_changed (manager);
+
+ return TRUE;
+}
+
+gboolean
+_nautilus_gtk_bookmarks_manager_get_xdg_type (NautilusGtkBookmarksManager *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
+_nautilus_gtk_bookmarks_manager_get_is_builtin (NautilusGtkBookmarksManager *manager,
+ GFile *file)
+{
+ GUserDirectory xdg_type;
+
+ /* if this is not an XDG dir, it's never builtin */
+ if (!_nautilus_gtk_bookmarks_manager_get_xdg_type (manager, file, &xdg_type))
+ return FALSE;
+
+ /* exclude XDG locations we don't display by default */
+ return _nautilus_gtk_bookmarks_manager_get_is_xdg_dir_builtin (xdg_type);
+}
+
+gboolean
+_nautilus_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/src/gtk/nautilusgtkbookmarksmanager.h b/src/gtk/nautilusgtkbookmarksmanager.h
new file mode 100644
index 000000000..229c61370
--- /dev/null
+++ b/src/gtk/nautilusgtkbookmarksmanager.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
+/* GTK - The GIMP Toolkit
+ * nautilusgtkbookmarksmanager.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.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Federico Mena Quintero <federico gnome org>
+ */
+
+#ifndef __NAUTILUS_GTK_BOOKMARKS_MANAGER_H__
+#define __NAUTILUS_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;
+} NautilusGtkBookmarksManager;
+
+typedef struct
+{
+ GFile *file;
+ gchar *label;
+} GtkBookmark;
+
+NautilusGtkBookmarksManager *_nautilus_gtk_bookmarks_manager_new (GtkBookmarksChangedFunc changed_func,
+ gpointer changed_func_data);
+
+
+void _nautilus_gtk_bookmarks_manager_free (NautilusGtkBookmarksManager *manager);
+
+GSList *_nautilus_gtk_bookmarks_manager_list_bookmarks (NautilusGtkBookmarksManager *manager);
+
+gboolean _nautilus_gtk_bookmarks_manager_insert_bookmark (NautilusGtkBookmarksManager *manager,
+ GFile *file,
+ gint position,
+ GError **error);
+
+gboolean _nautilus_gtk_bookmarks_manager_remove_bookmark (NautilusGtkBookmarksManager *manager,
+ GFile *file,
+ GError **error);
+
+gboolean _nautilus_gtk_bookmarks_manager_reorder_bookmark (NautilusGtkBookmarksManager *manager,
+ GFile *file,
+ gint new_position,
+ GError **error);
+
+gboolean _nautilus_gtk_bookmarks_manager_has_bookmark (NautilusGtkBookmarksManager *manager,
+ GFile *file);
+
+gchar * _nautilus_gtk_bookmarks_manager_get_bookmark_label (NautilusGtkBookmarksManager *manager,
+ GFile *file);
+
+gboolean _nautilus_gtk_bookmarks_manager_set_bookmark_label (NautilusGtkBookmarksManager *manager,
+ GFile *file,
+ const gchar *label,
+ GError **error);
+
+gboolean _nautilus_gtk_bookmarks_manager_get_xdg_type (NautilusGtkBookmarksManager *manager,
+ GFile *file,
+ GUserDirectory *directory);
+gboolean _nautilus_gtk_bookmarks_manager_get_is_builtin (NautilusGtkBookmarksManager *manager,
+ GFile *file);
+
+gboolean _nautilus_gtk_bookmarks_manager_get_is_xdg_dir_builtin (GUserDirectory xdg_type);
+
+#endif /* __NAUTILUS_GTK_BOOKMARKS_MANAGER_H__ */
diff --git a/src/gtk/nautilusgtkplacessidebar.c b/src/gtk/nautilusgtkplacessidebar.c
new file mode 100644
index 000000000..71c320d7d
--- /dev/null
+++ b/src/gtk/nautilusgtkplacessidebar.c
@@ -0,0 +1,5633 @@
+/* NautilusGtkPlacesSidebar - sidebar widget for places in the filesystem
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code is originally from Nautilus.
+ *
+ * Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk)
+ * Cosimo Cecchi <cosimoc gnome org>
+ * Federico Mena Quintero <federico gnome org>
+ * Carlos Soriano <csoriano gnome org>
+ */
+
+#include "config.h"
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <gio/gio.h>
+#ifdef HAVE_CLOUDPROVIDERS
+#include <cloudproviders/cloudproviderscollector.h>
+#include <cloudproviders/cloudprovidersaccount.h>
+#include <cloudproviders/cloudprovidersprovider.h>
+#endif
+
+#include "nautilusgtkplacessidebarprivate.h"
+#include "nautilusgtksidebarrowprivate.h"
+#include "gdk/gdkkeysyms.h"
+#include "nautilusgtkbookmarksmanager.h"
+#include "nautilusgtkplacessidebar.h"
+#include "nautilus-trash-monitor.h"
+#pragma GCC diagnostic ignored "-Wshadow"
+
+/**
+ * SECTION:nautilusgtkplacessidebar
+ * @Short_description: Sidebar that displays frequently-used places in the file system
+ * @Title: NautilusGtkPlacesSidebar
+ * @See_also: #GtkFileChooser
+ *
+ * #NautilusGtkPlacesSidebar 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 nautilus_gtk_places_sidebar_add_shortcut().
+ *
+ * To make use of the places sidebar, an application at least needs to connect
+ * to the #NautilusGtkPlacesSidebar::open-location signal. This is emitted when the
+ * user selects in the sidebar a location to open. The application should also
+ * call nautilus_gtk_places_sidebar_set_location() when it changes the currently-viewed
+ * location.
+ *
+ * # CSS nodes
+ *
+ * NautilusGtkPlacesSidebar uses a single CSS node with name placessidebar and style
+ * class .sidebar.
+ *
+ * Among the children of the places sidebar, the following style classes can
+ * be used:
+ * - .sidebar-new-bookmark-row for the 'Add new bookmark' row
+ * - .sidebar-placeholder-row for a row that is a placeholder
+ * - .has-open-popup when a popup is open for a row
+ */
+
+/* These are used when a destination-side DND operation is taking place.
+ * Normally, when a common drag action is taking place, the state will be
+ * DROP_STATE_NEW_BOOKMARK_ARMED, however, if the client of NautilusGtkPlacesSidebar
+ * wants to show hints about the valid targets, we sill set it as
+ * DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT, so the sidebar will show drop hints
+ * until the client says otherwise
+ */
+typedef enum {
+ DROP_STATE_NORMAL,
+ DROP_STATE_NEW_BOOKMARK_ARMED,
+ DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT,
+} DropState;
+
+struct _NautilusGtkPlacesSidebar {
+ GtkScrolledWindow parent;
+
+ GtkWidget *list_box;
+ GtkWidget *new_bookmark_row;
+
+ NautilusGtkBookmarksManager *bookmarks_manager;
+
+#ifdef HAVE_CLOUDPROVIDERS
+ CloudProvidersCollector *cloud_manager;
+ GList *unready_accounts;
+#endif
+
+ GVolumeMonitor *volume_monitor;
+ NautilusTrashMonitor *trash_monitor;
+ GtkSettings *gtk_settings;
+ GFile *current_location;
+
+ GtkWidget *rename_popover;
+ GtkWidget *rename_entry;
+ GtkWidget *rename_button;
+ GtkWidget *rename_error;
+ gchar *rename_uri;
+
+ gulong trash_monitor_changed_id;
+ GtkWidget *trash_row;
+
+ /* DND */
+ GList *drag_list; /* list of GFile */
+ gint drag_data_info;
+ gboolean dragging_over;
+ GtkTargetList *source_targets;
+ GtkWidget *drag_row;
+ gint drag_row_height;
+ gint drag_row_x;
+ gint drag_row_y;
+ gint drag_root_x;
+ gint drag_root_y;
+ GtkWidget *row_placeholder;
+ DropState drop_state;
+ GtkGesture *long_press_gesture;
+
+ /* volume mounting - delayed open process */
+ NautilusGtkPlacesOpenFlags go_to_after_mount_open_flags;
+ GCancellable *cancellable;
+
+ GtkWidget *popover;
+ NautilusGtkSidebarRow *context_row;
+ GSList *shortcuts;
+
+ GDBusProxy *hostnamed_proxy;
+ GCancellable *hostnamed_cancellable;
+ gchar *hostname;
+
+ NautilusGtkPlacesOpenFlags open_flags;
+
+ GActionGroup *action_group;
+
+ guint mounting : 1;
+ guint drag_data_received : 1;
+ guint drop_occurred : 1;
+ guint show_recent_set : 1;
+ guint show_recent : 1;
+ guint show_desktop_set : 1;
+ guint show_desktop : 1;
+ guint show_connect_to_server : 1;
+ guint show_enter_location : 1;
+ guint show_other_locations : 1;
+ guint show_trash : 1;
+ guint show_starred_location : 1;
+ guint local_only : 1;
+ guint populate_all : 1;
+};
+
+struct _NautilusGtkPlacesSidebarClass {
+ GtkScrolledWindowClass parent;
+
+ void (* open_location) (NautilusGtkPlacesSidebar *sidebar,
+ GFile *location,
+ NautilusGtkPlacesOpenFlags open_flags);
+ void (* populate_popup) (NautilusGtkPlacesSidebar *sidebar,
+ GtkMenu *menu,
+ GFile *selected_item,
+ GVolume *selected_volume);
+ void (* show_error_message) (NautilusGtkPlacesSidebar *sidebar,
+ const gchar *primary,
+ const gchar *secondary);
+ void (* show_connect_to_server) (NautilusGtkPlacesSidebar *sidebar);
+ GdkDragAction (* drag_action_requested) (NautilusGtkPlacesSidebar *sidebar,
+ GdkDragContext *context,
+ GFile *dest_file,
+ GList *source_file_list);
+ GdkDragAction (* drag_action_ask) (NautilusGtkPlacesSidebar *sidebar,
+ GdkDragAction actions);
+ void (* drag_perform_drop) (NautilusGtkPlacesSidebar *sidebar,
+ GFile *dest_file,
+ GList *source_file_list,
+ GdkDragAction action);
+ void (* show_enter_location) (NautilusGtkPlacesSidebar *sidebar);
+
+ void (* show_other_locations) (NautilusGtkPlacesSidebar *sidebar);
+
+ void (* show_other_locations_with_flags) (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkPlacesOpenFlags open_flags);
+
+ void (* show_starred_location) (NautilusGtkPlacesSidebar *sidebar);
+
+ void (* mount) (NautilusGtkPlacesSidebar *sidebar,
+ GMountOperation *mount_operation);
+ void (* unmount) (NautilusGtkPlacesSidebar *sidebar,
+ GMountOperation *unmount_operation);
+};
+
+enum {
+ OPEN_LOCATION,
+ POPULATE_POPUP,
+ SHOW_ERROR_MESSAGE,
+ SHOW_CONNECT_TO_SERVER,
+ SHOW_ENTER_LOCATION,
+ DRAG_ACTION_REQUESTED,
+ DRAG_ACTION_ASK,
+ DRAG_PERFORM_DROP,
+ SHOW_OTHER_LOCATIONS,
+ SHOW_OTHER_LOCATIONS_WITH_FLAGS,
+ SHOW_STARRED_LOCATION,
+ MOUNT,
+ UNMOUNT,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_LOCATION = 1,
+ PROP_OPEN_FLAGS,
+ PROP_SHOW_RECENT,
+ PROP_SHOW_DESKTOP,
+ PROP_SHOW_CONNECT_TO_SERVER,
+ PROP_SHOW_ENTER_LOCATION,
+ PROP_SHOW_TRASH,
+ PROP_SHOW_STARRED_LOCATION,
+ PROP_LOCAL_ONLY,
+ PROP_SHOW_OTHER_LOCATIONS,
+ PROP_POPULATE_ALL,
+ NUM_PROPERTIES
+};
+
+/* Names for themed icons */
+#define ICON_NAME_HOME "user-home-symbolic"
+#define ICON_NAME_DESKTOP "user-desktop-symbolic"
+#define ICON_NAME_FILESYSTEM "drive-harddisk-symbolic"
+#define ICON_NAME_EJECT "media-eject-symbolic"
+#define ICON_NAME_NETWORK "network-workgroup-symbolic"
+#define ICON_NAME_NETWORK_SERVER "network-server-symbolic"
+#define ICON_NAME_FOLDER_NETWORK "folder-remote-symbolic"
+#define ICON_NAME_OTHER_LOCATIONS "list-add-symbolic"
+
+#define ICON_NAME_FOLDER "folder-symbolic"
+#define ICON_NAME_FOLDER_DESKTOP "user-desktop-symbolic"
+#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 gboolean eject_or_unmount_bookmark (NautilusGtkSidebarRow *row);
+static gboolean eject_or_unmount_selection (NautilusGtkPlacesSidebar *sidebar);
+static void check_unmount_and_eject (GMount *mount,
+ GVolume *volume,
+ GDrive *drive,
+ gboolean *show_unmount,
+ gboolean *show_eject);
+static gboolean on_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ NautilusGtkSidebarRow *sidebar);
+static gboolean on_button_release_event (GtkWidget *widget,
+ GdkEventButton *event,
+ NautilusGtkSidebarRow *sidebar);
+static void popup_menu_cb (NautilusGtkSidebarRow *row);
+static void long_press_cb (GtkGesture *gesture,
+ gdouble x,
+ gdouble y,
+ NautilusGtkPlacesSidebar *sidebar);
+static void stop_drop_feedback (NautilusGtkPlacesSidebar *sidebar);
+static GMountOperation * get_mount_operation (NautilusGtkPlacesSidebar *sidebar);
+static GMountOperation * get_unmount_operation (NautilusGtkPlacesSidebar *sidebar);
+
+
+/* Identifiers for target types */
+enum {
+ DND_UNKNOWN,
+ DND_NAUTILUS_GTK_SIDEBAR_ROW,
+ DND_TEXT_URI_LIST
+};
+
+/* Target types for dragging from the shortcuts list */
+static const GtkTargetEntry dnd_source_targets[] = {
+ { "DND_NAUTILUS_GTK_SIDEBAR_ROW", GTK_TARGET_SAME_WIDGET, DND_NAUTILUS_GTK_SIDEBAR_ROW }
+};
+
+/* Target types for dropping into the shortcuts list */
+static const GtkTargetEntry dnd_drop_targets [] = {
+ { "DND_NAUTILUS_GTK_SIDEBAR_ROW", GTK_TARGET_SAME_WIDGET, DND_NAUTILUS_GTK_SIDEBAR_ROW }
+};
+
+G_DEFINE_TYPE (NautilusGtkPlacesSidebar, nautilus_gtk_places_sidebar, GTK_TYPE_SCROLLED_WINDOW);
+
+static void
+emit_open_location (NautilusGtkPlacesSidebar *sidebar,
+ GFile *location,
+ NautilusGtkPlacesOpenFlags open_flags)
+{
+ if ((open_flags & sidebar->open_flags) == 0)
+ open_flags = NAUTILUS_GTK_PLACES_OPEN_NORMAL;
+
+ g_signal_emit (sidebar, places_sidebar_signals[OPEN_LOCATION], 0,
+ location, open_flags);
+}
+
+static void
+emit_show_error_message (NautilusGtkPlacesSidebar *sidebar,
+ const gchar *primary,
+ const gchar *secondary)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[SHOW_ERROR_MESSAGE], 0,
+ primary, secondary);
+}
+
+static void
+emit_show_connect_to_server (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[SHOW_CONNECT_TO_SERVER], 0);
+}
+
+static void
+emit_show_enter_location (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[SHOW_ENTER_LOCATION], 0);
+}
+
+static void
+emit_show_other_locations (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[SHOW_OTHER_LOCATIONS], 0);
+}
+
+static void
+emit_show_other_locations_with_flags (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkPlacesOpenFlags open_flags)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[SHOW_OTHER_LOCATIONS_WITH_FLAGS],
+ 0, open_flags);
+}
+
+static void
+emit_show_starred_location (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkPlacesOpenFlags open_flags)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[SHOW_STARRED_LOCATION], 0,
+ open_flags);
+}
+
+
+static void
+emit_mount_operation (NautilusGtkPlacesSidebar *sidebar,
+ GMountOperation *mount_op)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[MOUNT], 0, mount_op);
+}
+
+static void
+emit_unmount_operation (NautilusGtkPlacesSidebar *sidebar,
+ GMountOperation *mount_op)
+{
+ g_signal_emit (sidebar, places_sidebar_signals[UNMOUNT], 0, mount_op);
+}
+
+static GdkDragAction
+emit_drag_action_requested (NautilusGtkPlacesSidebar *sidebar,
+ GdkDragContext *context,
+ GFile *dest_file,
+ GList *source_file_list)
+{
+ GdkDragAction 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 (NautilusGtkPlacesSidebar *sidebar,
+ GdkDragAction actions)
+{
+ GdkDragAction 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 (NautilusGtkPlacesSidebar *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 void
+list_box_header_func (GtkListBoxRow *row,
+ GtkListBoxRow *before,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebarSectionType row_section_type;
+ NautilusGtkPlacesSidebarSectionType before_section_type;
+ GtkWidget *separator;
+
+ gtk_list_box_row_set_header (row, NULL);
+
+ g_object_get (row, "section-type", &row_section_type, NULL);
+ if (before)
+ {
+ g_object_get (before, "section-type", &before_section_type, NULL);
+ }
+ else
+ {
+ before_section_type = SECTION_INVALID;
+ gtk_widget_set_margin_top (GTK_WIDGET (row), 4);
+ }
+
+ if (before && before_section_type != row_section_type)
+ {
+ separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+ gtk_widget_set_margin_top (separator, 4);
+ gtk_widget_set_margin_bottom (separator, 4);
+ gtk_list_box_row_set_header (row, separator);
+ }
+}
+
+static GtkWidget*
+add_place (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkPlacesSidebarPlaceType place_type,
+ NautilusGtkPlacesSidebarSectionType section_type,
+ const gchar *name,
+ GIcon *start_icon,
+ GIcon *end_icon,
+ const gchar *uri,
+ GDrive *drive,
+ GVolume *volume,
+ GMount *mount,
+#ifdef HAVE_CLOUDPROVIDERS
+ CloudProvidersAccount *cloud_provider_account,
+#else
+ gpointer *cloud_provider_account,
+#endif
+ const gint index,
+ const gchar *tooltip)
+{
+ gboolean show_eject, show_unmount;
+ gboolean show_eject_button;
+ GtkWidget *row;
+ GtkWidget *eject_button;
+ GtkWidget *event_box;
+
+ check_unmount_and_eject (mount, volume, drive,
+ &show_unmount, &show_eject);
+
+ if (show_unmount || show_eject)
+ g_assert (place_type != PLACES_BOOKMARK);
+
+ show_eject_button = (show_unmount || show_eject);
+
+ row = g_object_new (NAUTILUS_TYPE_GTK_SIDEBAR_ROW,
+ "sidebar", sidebar,
+ "start-icon", start_icon,
+ "end-icon", end_icon,
+ "label", name,
+ "tooltip", tooltip,
+ "ejectable", show_eject_button,
+ "order-index", index,
+ "section-type", section_type,
+ "place-type", place_type,
+ "uri", uri,
+ "drive", drive,
+ "volume", volume,
+ "mount", mount,
+#ifdef HAVE_CLOUDPROVIDERS
+ "cloud-provider-account", cloud_provider_account,
+#endif
+ NULL);
+
+ eject_button = nautilus_gtk_sidebar_row_get_eject_button (NAUTILUS_GTK_SIDEBAR_ROW (row));
+ event_box = nautilus_gtk_sidebar_row_get_event_box (NAUTILUS_GTK_SIDEBAR_ROW (row));
+
+ g_signal_connect_swapped (eject_button, "clicked",
+ G_CALLBACK (eject_or_unmount_bookmark), row);
+ g_signal_connect (event_box, "button-press-event",
+ G_CALLBACK (on_button_press_event), row);
+ g_signal_connect (event_box, "button-release-event",
+ G_CALLBACK (on_button_release_event), row);
+
+ gtk_container_add (GTK_CONTAINER (sidebar->list_box), GTK_WIDGET (row));
+ gtk_widget_show_all (row);
+
+ return row;
+}
+
+static GIcon *
+special_directory_get_gicon (GUserDirectory directory)
+{
+#define ICON_CASE(x) \
+ case G_USER_DIRECTORY_ ## x: \
+ return g_themed_icon_new_with_default_fallbacks (ICON_NAME_FOLDER_ ## x);
+
+ switch (directory)
+ {
+
+ ICON_CASE (DESKTOP);
+ 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_with_default_fallbacks (ICON_NAME_FOLDER);
+ }
+
+#undef ICON_CASE
+}
+
+static gboolean
+recent_files_setting_is_enabled (NautilusGtkPlacesSidebar *sidebar)
+{
+ GtkSettings *settings;
+ gboolean enabled;
+
+ settings = gtk_widget_get_settings (GTK_WIDGET (sidebar));
+ g_object_get (settings, "gtk-recent-files-enabled", &enabled, NULL);
+
+ return enabled;
+}
+
+static gboolean
+recent_scheme_is_supported (void)
+{
+ const gchar * const *supported;
+
+ supported = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
+ if (supported != NULL)
+ return g_strv_contains (supported, "recent");
+
+ return FALSE;
+}
+
+static gboolean
+should_show_recent (NautilusGtkPlacesSidebar *sidebar)
+{
+ return recent_files_setting_is_enabled (sidebar) &&
+ ((sidebar->show_recent_set && sidebar->show_recent) ||
+ (!sidebar->show_recent_set && recent_scheme_is_supported ()));
+}
+
+static gboolean
+path_is_home_dir (const gchar *path)
+{
+ GFile *home_dir;
+ GFile *location;
+ const gchar *home_path;
+ gboolean res;
+
+ home_path = g_get_home_dir ();
+ if (!home_path)
+ return FALSE;
+
+ home_dir = g_file_new_for_path (home_path);
+ location = g_file_new_for_path (path);
+ res = g_file_equal (home_dir, location);
+
+ g_object_unref (home_dir);
+ g_object_unref (location);
+
+ return res;
+}
+
+static void
+open_home (NautilusGtkPlacesSidebar *sidebar)
+{
+ const gchar *home_path;
+ GFile *home_dir;
+
+ home_path = g_get_home_dir ();
+ if (!home_path)
+ return;
+
+ home_dir = g_file_new_for_path (home_path);
+ emit_open_location (sidebar, home_dir, 0);
+
+ g_object_unref (home_dir);
+}
+
+static void
+add_special_dirs (NautilusGtkPlacesSidebar *sidebar)
+{
+ GList *dirs;
+ gint index;
+
+ dirs = NULL;
+ for (index = 0; index < G_USER_N_DIRECTORIES; index++)
+ {
+ const gchar *path;
+ GFile *root;
+ GIcon *start_icon;
+ gchar *name;
+ gchar *mount_uri;
+ gchar *tooltip;
+
+ if (!_nautilus_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 ||
+ path_is_home_dir (path) ||
+ g_list_find_custom (dirs, path, (GCompareFunc) g_strcmp0) != NULL)
+ continue;
+
+ root = g_file_new_for_path (path);
+
+ name = _nautilus_gtk_bookmarks_manager_get_bookmark_label (sidebar->bookmarks_manager, root);
+ if (!name)
+ name = g_file_get_basename (root);
+
+ start_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, start_icon, NULL, mount_uri,
+ NULL, NULL, NULL, NULL, 0,
+ tooltip);
+ g_free (name);
+ g_object_unref (root);
+ g_object_unref (start_icon);
+ g_free (mount_uri);
+ g_free (tooltip);
+
+ dirs = g_list_prepend (dirs, (gchar *)path);
+ }
+
+ g_list_free (dirs);
+}
+
+static gchar *
+get_home_directory_uri (void)
+{
+ const gchar *home;
+
+ home = g_get_home_dir ();
+ if (!home)
+ return NULL;
+
+ return g_filename_to_uri (home, NULL, NULL);
+}
+
+static gchar *
+get_desktop_directory_uri (void)
+{
+ const gchar *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 (path_is_home_dir (name))
+ return NULL;
+
+ return g_filename_to_uri (name, NULL, NULL);
+}
+
+static gboolean
+should_show_file (NautilusGtkPlacesSidebar *sidebar,
+ GFile *file)
+{
+ gchar *path;
+
+ if (!sidebar->local_only)
+ return TRUE;
+
+ path = g_file_get_path (file);
+ if (path)
+ {
+ g_free (path);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+file_is_shown (NautilusGtkPlacesSidebar *sidebar,
+ GFile *file)
+{
+ gchar *uri;
+ GList *rows;
+ GList *l;
+ gboolean found = FALSE;
+
+ rows = gtk_container_get_children (GTK_CONTAINER (sidebar->list_box));
+ l = rows;
+ while (l != NULL && !found)
+ {
+ g_object_get (l->data, "uri", &uri, NULL);
+ if (uri)
+ {
+ GFile *other;
+ other = g_file_new_for_uri (uri);
+ found = g_file_equal (file, other);
+ g_object_unref (other);
+ g_free (uri);
+ }
+ l = l->next;
+ }
+
+ g_list_free (rows);
+
+ return found;
+}
+
+static void
+on_app_shortcuts_query_complete (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ GFile *file = G_FILE (source);
+ GFileInfo *info;
+
+ info = g_file_query_info_finish (file, result, NULL);
+
+ if (info)
+ {
+ gchar *uri;
+ gchar *tooltip;
+ const gchar *name;
+ GIcon *start_icon;
+ int pos = 0;
+
+ name = g_file_info_get_display_name (info);
+ start_icon = g_file_info_get_symbolic_icon (info);
+ uri = g_file_get_uri (file);
+ tooltip = g_file_get_parse_name (file);
+
+ /* XXX: we could avoid this by using an ancillary closure
+ * with the index coming from add_application_shortcuts(),
+ * but in terms of algorithmic overhead, the application
+ * shortcuts is not going to be really big
+ */
+ pos = g_slist_index (sidebar->shortcuts, file);
+
+ add_place (sidebar, PLACES_BUILT_IN,
+ SECTION_COMPUTER,
+ name, start_icon, NULL, uri,
+ NULL, NULL, NULL, NULL,
+ pos,
+ tooltip);
+
+ g_free (uri);
+ g_free (tooltip);
+
+ g_object_unref (info);
+ }
+}
+
+static void
+add_application_shortcuts (NautilusGtkPlacesSidebar *sidebar)
+{
+ GSList *l;
+
+ for (l = sidebar->shortcuts; l; l = l->next)
+ {
+ GFile *file = l->data;
+
+ if (!should_show_file (sidebar, file))
+ continue;
+
+ if (file_is_shown (sidebar, file))
+ continue;
+
+ g_file_query_info_async (file,
+ "standard::display-name,standard::symbolic-icon",
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ sidebar->cancellable,
+ on_app_shortcuts_query_complete,
+ sidebar);
+ }
+}
+
+typedef struct {
+ NautilusGtkPlacesSidebar *sidebar;
+ int index;
+ gboolean is_native;
+} BookmarkQueryClosure;
+
+static void
+on_bookmark_query_info_complete (GObject *source,
+ GAsyncResult *result,
+ gpointer data)
+{
+ BookmarkQueryClosure *clos = data;
+ NautilusGtkPlacesSidebar *sidebar = clos->sidebar;
+ GFile *root = G_FILE (source);
+ GError *error = NULL;
+ GFileInfo *info;
+ gchar *bookmark_name;
+ gchar *mount_uri;
+ gchar *tooltip;
+ GIcon *start_icon;
+
+ info = g_file_query_info_finish (root, result, &error);
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ goto out;
+
+ bookmark_name = _nautilus_gtk_bookmarks_manager_get_bookmark_label (sidebar->bookmarks_manager, root);
+ if (bookmark_name == NULL && info != NULL)
+ bookmark_name = g_strdup (g_file_info_get_display_name (info));
+ else if (bookmark_name == NULL)
+ {
+ /* Don't add non-UTF-8 bookmarks */
+ bookmark_name = g_file_get_basename (root);
+ if (!g_utf8_validate (bookmark_name, -1, NULL))
+ {
+ g_free (bookmark_name);
+ goto out;
+ }
+ }
+
+ if (info)
+ start_icon = g_object_ref (g_file_info_get_symbolic_icon (info));
+ else
+ start_icon = g_themed_icon_new_with_default_fallbacks (clos->is_native ? ICON_NAME_FOLDER :
ICON_NAME_FOLDER_NETWORK);
+
+ mount_uri = g_file_get_uri (root);
+ tooltip = g_file_get_parse_name (root);
+
+ add_place (sidebar, PLACES_BOOKMARK,
+ SECTION_BOOKMARKS,
+ bookmark_name, start_icon, NULL, mount_uri,
+ NULL, NULL, NULL, NULL, clos->index,
+ tooltip);
+
+ g_free (mount_uri);
+ g_free (tooltip);
+ g_free (bookmark_name);
+ g_object_unref (start_icon);
+
+out:
+ g_clear_object (&info);
+ g_clear_error (&error);
+ g_slice_free (BookmarkQueryClosure, clos);
+}
+
+static gboolean
+is_external_volume (GVolume *volume)
+{
+ gboolean is_external;
+ GDrive *drive;
+ gchar *id;
+
+ drive = g_volume_get_drive (volume);
+ id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS);
+
+ is_external = g_volume_can_eject (volume);
+
+ /* NULL volume identifier only happens on removable devices */
+ is_external |= !id;
+
+ if (drive)
+ is_external |= g_drive_is_removable (drive);
+
+ g_clear_object (&drive);
+ g_free (id);
+
+ return is_external;
+}
+
+static void
+update_trash_icon (NautilusGtkPlacesSidebar *sidebar)
+{
+ if (sidebar->trash_row)
+ {
+ GIcon *icon;
+
+ icon = nautilus_trash_monitor_get_symbolic_icon ();
+ nautilus_gtk_sidebar_row_set_start_icon (NAUTILUS_GTK_SIDEBAR_ROW (sidebar->trash_row), icon);
+ g_object_unref (icon);
+ }
+}
+
+#ifdef HAVE_CLOUDPROVIDERS
+
+static gboolean
+create_cloud_provider_account_row (NautilusGtkPlacesSidebar *sidebar,
+ CloudProvidersAccount *account)
+{
+ GIcon *end_icon;
+ GIcon *start_icon;
+ gchar *mount_uri;
+ gchar *name;
+ gchar *tooltip;
+ guint provider_account_status;
+
+ start_icon = cloud_providers_account_get_icon (account);
+ name = cloud_providers_account_get_name (account);
+ provider_account_status = cloud_providers_account_get_status (account);
+ mount_uri = cloud_providers_account_get_path (account);
+ if (start_icon != NULL
+ && name != NULL
+ && provider_account_status != CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID
+ && mount_uri != NULL)
+ {
+ switch (provider_account_status)
+ {
+ case CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE:
+ end_icon = NULL;
+ break;
+
+ case CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING:
+ end_icon = g_themed_icon_new ("emblem-synchronizing-symbolic");
+ break;
+
+ case CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR:
+ end_icon = g_themed_icon_new ("dialog-warning-symbolic");
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ mount_uri = g_strconcat ("file://", mount_uri, NULL);
+
+ /* translators: %s is the name of a cloud provider for files */
+ tooltip = g_strdup_printf (_("Open %s"), name);
+
+ add_place (sidebar, PLACES_BUILT_IN,
+ SECTION_CLOUD,
+ name, start_icon, end_icon, mount_uri,
+ NULL, NULL, NULL, account, 0,
+ tooltip);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static void
+on_account_updated (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ CloudProvidersAccount *account = CLOUD_PROVIDERS_ACCOUNT (object);
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+
+ if (create_cloud_provider_account_row (sidebar, account))
+ {
+ g_signal_handlers_disconnect_by_data (account, sidebar);
+ sidebar->unready_accounts = g_list_remove (sidebar->unready_accounts, account);
+ g_object_unref (account);
+ }
+}
+
+#endif
+
+static void
+update_places (NautilusGtkPlacesSidebar *sidebar)
+{
+ GList *mounts, *l, *ll;
+ GMount *mount;
+ GList *drives;
+ GDrive *drive;
+ GList *volumes;
+ GVolume *volume;
+ GSList *bookmarks, *sl;
+ gint index;
+ gchar *original_uri, *mount_uri, *name, *identifier;
+ GtkListBoxRow *selected;
+ gchar *home_uri;
+ GIcon *start_icon;
+ GFile *root;
+ gchar *tooltip;
+ GList *network_mounts, *network_volumes;
+ GIcon *new_bookmark_icon;
+#ifdef HAVE_CLOUDPROVIDERS
+ GList *cloud_providers;
+ GList *cloud_providers_accounts;
+ CloudProvidersAccount *cloud_provider_account;
+ CloudProvidersProvider *cloud_provider;
+#endif
+ GtkStyleContext *context;
+
+ /* save original selection */
+ selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (sidebar->list_box));
+ if (selected)
+ g_object_get (selected, "uri", &original_uri, NULL);
+ else
+ original_uri = NULL;
+
+ g_cancellable_cancel (sidebar->cancellable);
+
+ g_object_unref (sidebar->cancellable);
+ sidebar->cancellable = g_cancellable_new ();
+
+ /* Reset drag state, just in case we update the places while dragging or
+ * ending a drag */
+ stop_drop_feedback (sidebar);
+ gtk_container_foreach (GTK_CONTAINER (sidebar->list_box),
+ (GtkCallback) gtk_widget_destroy,
+ NULL);
+
+ network_mounts = network_volumes = NULL;
+
+ /* add built-in places */
+ if (should_show_recent (sidebar))
+ {
+ mount_uri = "recent:///";
+ start_icon = g_themed_icon_new_with_default_fallbacks ("document-open-recent-symbolic");
+ add_place (sidebar, PLACES_BUILT_IN,
+ SECTION_COMPUTER,
+ _("Recent"), start_icon, NULL, mount_uri,
+ NULL, NULL, NULL, NULL, 0,
+ _("Recent files"));
+ g_object_unref (start_icon);
+ }
+
+ if (sidebar->show_starred_location)
+ {
+ mount_uri = "starred:///";
+ start_icon = g_themed_icon_new_with_default_fallbacks ("starred-symbolic");
+ add_place (sidebar, PLACES_STARRED_LOCATION,
+ SECTION_COMPUTER,
+ _("Starred"), start_icon, NULL, mount_uri,
+ NULL, NULL, NULL, NULL, 0,
+ /* TODO: Rename to 'Starred files' */
+ _("Favorite files"));
+ g_object_unref (start_icon);
+ }
+
+ /* home folder */
+ home_uri = get_home_directory_uri ();
+ start_icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_HOME);
+ add_place (sidebar, PLACES_BUILT_IN,
+ SECTION_COMPUTER,
+ _("Home"), start_icon, NULL, home_uri,
+ NULL, NULL, NULL, NULL, 0,
+ _("Open your personal folder"));
+ g_object_unref (start_icon);
+ g_free (home_uri);
+
+ /* desktop */
+ if (sidebar->show_desktop)
+ {
+ mount_uri = get_desktop_directory_uri ();
+ if (mount_uri)
+ {
+ start_icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_DESKTOP);
+ add_place (sidebar, PLACES_BUILT_IN,
+ SECTION_COMPUTER,
+ _("Desktop"), start_icon, NULL, mount_uri,
+ NULL, NULL, NULL, NULL, 0,
+ _("Open the contents of your desktop in a folder"));
+ g_object_unref (start_icon);
+ g_free (mount_uri);
+ }
+ }
+
+ /* XDG directories */
+ add_special_dirs (sidebar);
+
+ if (sidebar->show_enter_location)
+ {
+ start_icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_NETWORK_SERVER);
+ add_place (sidebar, PLACES_ENTER_LOCATION,
+ SECTION_COMPUTER,
+ _("Enter Location"), start_icon, NULL, NULL,
+ NULL, NULL, NULL, NULL, 0,
+ _("Manually enter a location"));
+ g_object_unref (start_icon);
+ }
+
+ /* Trash */
+ if (!sidebar->local_only && sidebar->show_trash)
+ {
+ start_icon = nautilus_trash_monitor_get_symbolic_icon ();
+ sidebar->trash_row = add_place (sidebar, PLACES_BUILT_IN,
+ SECTION_COMPUTER,
+ _("Trash"), start_icon, NULL, "trash:///",
+ NULL, NULL, NULL, NULL, 0,
+ _("Open the trash"));
+ g_object_add_weak_pointer (G_OBJECT (sidebar->trash_row),
+ (gpointer *) &sidebar->trash_row);
+ g_object_unref (start_icon);
+ }
+
+ /* Application-side shortcuts */
+ add_application_shortcuts (sidebar);
+
+ /* Cloud providers */
+#ifdef HAVE_CLOUDPROVIDERS
+ cloud_providers = cloud_providers_collector_get_providers (sidebar->cloud_manager);
+ for (l = sidebar->unready_accounts; l != NULL; l = l->next)
+ {
+ g_signal_handlers_disconnect_by_data (l->data, sidebar);
+ }
+ g_list_free_full (sidebar->unready_accounts, g_object_unref);
+ sidebar->unready_accounts = NULL;
+ for (l = cloud_providers; l != NULL; l = l->next)
+ {
+ cloud_provider = CLOUD_PROVIDERS_PROVIDER (l->data);
+ g_signal_connect_swapped (cloud_provider, "accounts-changed",
+ G_CALLBACK (update_places), sidebar);
+ cloud_providers_accounts = cloud_providers_provider_get_accounts (cloud_provider);
+ for (ll = cloud_providers_accounts; ll != NULL; ll = ll->next)
+ {
+ cloud_provider_account = CLOUD_PROVIDERS_ACCOUNT (ll->data);
+ if (!create_cloud_provider_account_row (sidebar, cloud_provider_account))
+ {
+
+ g_signal_connect (cloud_provider_account, "notify::name",
+ G_CALLBACK (on_account_updated), sidebar);
+ g_signal_connect (cloud_provider_account, "notify::status",
+ G_CALLBACK (on_account_updated), sidebar);
+ g_signal_connect (cloud_provider_account, "notify::status-details",
+ G_CALLBACK (on_account_updated), sidebar);
+ g_signal_connect (cloud_provider_account, "notify::path",
+ G_CALLBACK (on_account_updated), sidebar);
+ sidebar->unready_accounts = g_list_append (sidebar->unready_accounts,
+ g_object_ref (cloud_provider_account));
+ continue;
+ }
+
+ }
+ }
+#endif
+
+ /* go through all connected drives */
+ drives = g_volume_monitor_get_connected_drives (sidebar->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);
+
+ if (sidebar->show_other_locations && !is_external_volume (volume))
+ {
+ g_object_unref (volume);
+ continue;
+ }
+
+ mount = g_volume_get_mount (volume);
+ if (mount != NULL)
+ {
+ /* Show mounted volume in the sidebar */
+ start_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_MOUNTS,
+ name, start_icon, NULL, mount_uri,
+ drive, volume, mount, NULL, 0, tooltip);
+ g_object_unref (root);
+ g_object_unref (mount);
+ g_object_unref (start_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.
+ */
+ start_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_MOUNTS,
+ name, start_icon, NULL, NULL,
+ drive, volume, NULL, NULL, 0, tooltip);
+ g_object_unref (start_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.
+ */
+ start_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_MOUNTS,
+ name, start_icon, NULL, NULL,
+ drive, NULL, NULL, NULL, 0, tooltip);
+ g_object_unref (start_icon);
+ g_free (tooltip);
+ g_free (name);
+ }
+ }
+ }
+ g_list_free_full (drives, g_object_unref);
+
+ /* add all network volumes that are not associated with a drive, and
+ * loop devices
+ */
+ volumes = g_volume_monitor_get_volumes (sidebar->volume_monitor);
+ for (l = volumes; l != NULL; l = l->next)
+ {
+ gboolean is_loop = FALSE;
+ 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;
+ }
+ else if (g_strcmp0 (identifier, "loop") == 0)
+ is_loop = TRUE;
+ g_free (identifier);
+
+ if (sidebar->show_other_locations &&
+ !is_external_volume (volume) &&
+ !is_loop)
+ {
+ g_object_unref (volume);
+ continue;
+ }
+
+ mount = g_volume_get_mount (volume);
+ if (mount != NULL)
+ {
+ char *mount_uri;
+ start_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);
+ name = g_mount_get_name (mount);
+ add_place (sidebar, PLACES_MOUNTED_VOLUME,
+ SECTION_MOUNTS,
+ name, start_icon, NULL, mount_uri,
+ NULL, volume, mount, NULL, 0, tooltip);
+ g_object_unref (mount);
+ g_object_unref (root);
+ g_object_unref (start_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 */
+ start_icon = g_volume_get_symbolic_icon (volume);
+ name = g_volume_get_name (volume);
+ add_place (sidebar, PLACES_MOUNTED_VOLUME,
+ SECTION_MOUNTS,
+ name, start_icon, NULL, NULL,
+ NULL, volume, NULL, NULL, 0, name);
+ g_object_unref (start_icon);
+ g_free (name);
+ }
+ g_object_unref (volume);
+ }
+ g_list_free (volumes);
+
+ /* file system root */
+ if (!sidebar->show_other_locations)
+ {
+ mount_uri = "file:///"; /* No need to strdup */
+ start_icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_FILESYSTEM);
+ add_place (sidebar, PLACES_BUILT_IN,
+ SECTION_MOUNTS,
+ sidebar->hostname, start_icon, NULL, mount_uri,
+ NULL, NULL, NULL, NULL, 0,
+ _("Open the contents of the file system"));
+ g_object_unref (start_icon);
+ }
+
+ /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
+ mounts = g_volume_monitor_get_mounts (sidebar->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;
+ }
+
+ start_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, start_icon, NULL, mount_uri,
+ NULL, NULL, mount, NULL, 0, tooltip);
+ g_object_unref (root);
+ g_object_unref (mount);
+ g_object_unref (start_icon);
+ g_free (name);
+ g_free (mount_uri);
+ g_free (tooltip);
+ }
+ g_list_free (mounts);
+
+ /* add bookmarks */
+ bookmarks = _nautilus_gtk_bookmarks_manager_list_bookmarks (sidebar->bookmarks_manager);
+
+ for (sl = bookmarks, index = 0; sl; sl = sl->next, index++)
+ {
+ gboolean is_native;
+ BookmarkQueryClosure *clos;
+
+ root = sl->data;
+ is_native = g_file_is_native (root);
+
+ if (_nautilus_gtk_bookmarks_manager_get_is_builtin (sidebar->bookmarks_manager, root))
+ continue;
+
+ if (sidebar->local_only && !is_native)
+ continue;
+
+ clos = g_slice_new (BookmarkQueryClosure);
+ clos->sidebar = sidebar;
+ clos->index = index;
+ clos->is_native = is_native;
+ g_file_query_info_async (root,
+ "standard::display-name,standard::symbolic-icon",
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ sidebar->cancellable,
+ on_bookmark_query_info_complete,
+ clos);
+ }
+
+ g_slist_free_full (bookmarks, g_object_unref);
+
+ /* Add new bookmark row */
+ new_bookmark_icon = g_themed_icon_new ("bookmark-new-symbolic");
+ sidebar->new_bookmark_row = add_place (sidebar, PLACES_DROP_FEEDBACK,
+ SECTION_BOOKMARKS,
+ _("New bookmark"), new_bookmark_icon, NULL, NULL,
+ NULL, NULL, NULL, NULL, 0,
+ _("Add a new bookmark"));
+ context = gtk_widget_get_style_context (sidebar->new_bookmark_row);
+ gtk_style_context_add_class (context, "sidebar-new-bookmark-row");
+ g_object_unref (new_bookmark_icon);
+
+ /* network */
+ if (!sidebar->local_only)
+ {
+ if (sidebar->show_connect_to_server)
+ {
+ start_icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_NETWORK_SERVER);
+ add_place (sidebar, PLACES_CONNECT_TO_SERVER,
+ SECTION_MOUNTS,
+ _("Connect to Server"), start_icon, NULL,
+ NULL, NULL, NULL, NULL, NULL, 0,
+ _("Connect to a network server address"));
+ g_object_unref (start_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
+ {
+ start_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_MOUNTS,
+ name, start_icon, NULL, NULL,
+ NULL, volume, NULL, NULL, 0, tooltip);
+ g_object_unref (start_icon);
+ g_free (name);
+ g_free (tooltip);
+ }
+ }
+
+ 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);
+ start_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_MOUNTS,
+ name, start_icon, NULL, mount_uri,
+ NULL, NULL, mount, NULL, 0, tooltip);
+ g_object_unref (root);
+ g_object_unref (start_icon);
+ g_free (name);
+ g_free (mount_uri);
+ g_free (tooltip);
+ }
+ }
+
+ g_list_free_full (network_volumes, g_object_unref);
+ g_list_free_full (network_mounts, g_object_unref);
+
+ /* Other locations */
+ if (sidebar->show_other_locations)
+ {
+ start_icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_OTHER_LOCATIONS);
+
+ add_place (sidebar, PLACES_OTHER_LOCATIONS,
+ SECTION_OTHER_LOCATIONS,
+ _("Other Locations"), start_icon, NULL, "other-locations:///",
+ NULL, NULL, NULL, NULL, 0, _("Show other locations"));
+
+ g_object_unref (start_icon);
+ }
+
+ gtk_widget_show_all (GTK_WIDGET (sidebar));
+ /* We want this hidden by default, but need to do it after the show_all call */
+ nautilus_gtk_sidebar_row_hide (NAUTILUS_GTK_SIDEBAR_ROW (sidebar->new_bookmark_row), TRUE);
+
+ /* restore original selection */
+ if (original_uri)
+ {
+ GFile *restore;
+
+ restore = g_file_new_for_uri (original_uri);
+ nautilus_gtk_places_sidebar_set_location (sidebar, restore);
+ g_object_unref (restore);
+ g_free (original_uri);
+ }
+}
+
+static gboolean
+check_valid_drop_target (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkSidebarRow *row,
+ GdkDragContext *context)
+{
+ NautilusGtkPlacesSidebarPlaceType place_type;
+ NautilusGtkPlacesSidebarSectionType section_type;
+ gboolean valid = FALSE;
+ gchar *uri;
+ GFile *dest_file;
+ gint drag_action;
+
+ if (row == NULL)
+ return FALSE;
+
+ g_object_get (row,
+ "place-type", &place_type,
+ "section_type", §ion_type,
+ "uri", &uri,
+ NULL);
+
+ if (place_type == PLACES_STARRED_LOCATION)
+ {
+ g_free (uri);
+ return FALSE;
+ }
+
+ if (place_type == PLACES_CONNECT_TO_SERVER)
+ {
+ g_free (uri);
+ return FALSE;
+ }
+
+ if (place_type == PLACES_DROP_FEEDBACK)
+ {
+ g_free (uri);
+ return TRUE;
+ }
+
+ /* Disallow drops on recent:/// */
+ if (place_type == PLACES_BUILT_IN)
+ {
+ if (g_strcmp0 (uri, "recent:///") == 0)
+ {
+ g_free (uri);
+ return FALSE;
+ }
+ }
+
+ /* Dragging a bookmark? */
+ if (sidebar->drag_data_received &&
+ sidebar->drag_data_info == DND_NAUTILUS_GTK_SIDEBAR_ROW)
+ {
+ /* Don't allow reordering bookmarks into non-bookmark areas */
+ valid = section_type == SECTION_BOOKMARKS;
+ }
+ else
+ {
+ /* Dragging a file */
+ if (context)
+ {
+ if (uri != NULL)
+ {
+ dest_file = g_file_new_for_uri (uri);
+ drag_action = emit_drag_action_requested (sidebar, context, dest_file, sidebar->drag_list);
+ valid = drag_action > 0;
+
+ g_object_unref (dest_file);
+ }
+ else
+ {
+ valid = FALSE;
+ }
+ }
+ else
+ {
+ /* We cannot discern if it is valid or not because there is not drag
+ * context available to ask the client.
+ * Simply make insensitive the drop targets we know are not valid for
+ * files, that are the ones remaining.
+ */
+ valid = TRUE;
+ }
+ }
+
+ g_free (uri);
+ return valid;
+}
+
+static void
+update_possible_drop_targets (NautilusGtkPlacesSidebar *sidebar,
+ gboolean dragging,
+ GdkDragContext *context)
+{
+ GList *rows;
+ GList *l;
+ gboolean sensitive;
+
+ rows = gtk_container_get_children (GTK_CONTAINER (sidebar->list_box));
+
+ for (l = rows; l != NULL; l = l->next)
+ {
+ sensitive = !dragging || check_valid_drop_target (sidebar, NAUTILUS_GTK_SIDEBAR_ROW (l->data),
context);
+ gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive);
+ }
+
+ g_list_free (rows);
+}
+
+static gboolean
+get_drag_data (GtkWidget *list_box,
+ GdkDragContext *context,
+ guint time)
+{
+ GdkAtom target;
+
+ target = gtk_drag_dest_find_target (list_box, context, NULL);
+
+ if (target == GDK_NONE)
+ return FALSE;
+
+ gtk_drag_get_data (list_box, context, target, time);
+
+ return TRUE;
+}
+
+static void
+free_drag_data (NautilusGtkPlacesSidebar *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 void
+start_drop_feedback (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkSidebarRow *row,
+ GdkDragContext *context)
+{
+ if (sidebar->drag_data_info != DND_NAUTILUS_GTK_SIDEBAR_ROW)
+ {
+ nautilus_gtk_sidebar_row_reveal (NAUTILUS_GTK_SIDEBAR_ROW (sidebar->new_bookmark_row));
+ /* If the state is permanent, don't change it. The application controls it. */
+ if (sidebar->drop_state != DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT)
+ sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED;
+ }
+
+ update_possible_drop_targets (sidebar, TRUE, context);
+}
+
+static void
+stop_drop_feedback (NautilusGtkPlacesSidebar *sidebar)
+{
+ update_possible_drop_targets (sidebar, FALSE, NULL);
+
+ free_drag_data (sidebar);
+
+ if (sidebar->drop_state != DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT &&
+ sidebar->new_bookmark_row != NULL)
+ {
+ nautilus_gtk_sidebar_row_hide (NAUTILUS_GTK_SIDEBAR_ROW (sidebar->new_bookmark_row), FALSE);
+ sidebar->drop_state = DROP_STATE_NORMAL;
+ }
+
+ if (sidebar->drag_row != NULL)
+ {
+ gtk_widget_show (sidebar->drag_row);
+ sidebar->drag_row = NULL;
+ }
+
+ if (sidebar->row_placeholder != NULL)
+ {
+ gtk_widget_destroy (sidebar->row_placeholder);
+ sidebar->row_placeholder = NULL;
+ }
+
+ sidebar->dragging_over = FALSE;
+ sidebar->drag_data_info = DND_UNKNOWN;
+}
+
+static gboolean
+on_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+
+ if (sidebar->drag_row == NULL || sidebar->dragging_over)
+ return FALSE;
+
+ if (!(event->state & GDK_BUTTON1_MASK))
+ return FALSE;
+
+ if (gtk_drag_check_threshold (widget,
+ sidebar->drag_root_x, sidebar->drag_root_y,
+ event->x_root, event->y_root))
+ {
+ sidebar->dragging_over = TRUE;
+
+ gtk_drag_begin_with_coordinates (widget, sidebar->source_targets, GDK_ACTION_MOVE,
+ GDK_BUTTON_PRIMARY, (GdkEvent*)event,
+ -1, -1);
+ }
+
+ return FALSE;
+}
+
+static void
+drag_begin_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+ GtkAllocation allocation;
+ GtkWidget *drag_widget;
+ GtkWidget *window;
+
+ gtk_widget_get_allocation (sidebar->drag_row, &allocation);
+ gtk_widget_hide (sidebar->drag_row);
+
+ drag_widget = GTK_WIDGET (nautilus_gtk_sidebar_row_clone (NAUTILUS_GTK_SIDEBAR_ROW (sidebar->drag_row)));
+ window = gtk_window_new (GTK_WINDOW_POPUP);
+ sidebar->drag_row_height = allocation.height;
+ gtk_widget_set_size_request (window, allocation.width, allocation.height);
+
+ gtk_container_add (GTK_CONTAINER (window), drag_widget);
+ gtk_widget_show_all (window);
+ gtk_widget_set_opacity (window, 0.8);
+
+ gtk_drag_set_icon_widget (context,
+ window,
+ sidebar->drag_row_x,
+ sidebar->drag_row_y);
+}
+
+static GtkWidget *
+create_placeholder_row (NautilusGtkPlacesSidebar *sidebar)
+{
+ return g_object_new (NAUTILUS_TYPE_GTK_SIDEBAR_ROW,
+ "placeholder", TRUE,
+ NULL);
+}
+
+static gboolean
+drag_motion_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ gpointer user_data)
+{
+ gint action;
+ GtkListBoxRow *row;
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+ NautilusGtkPlacesSidebarPlaceType place_type;
+ gchar *drop_target_uri = NULL;
+ gint row_index;
+ gint row_placeholder_index;
+
+ sidebar->dragging_over = TRUE;
+ action = 0;
+ row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y);
+
+ gtk_list_box_drag_unhighlight_row (GTK_LIST_BOX (sidebar->list_box));
+
+ /* Nothing to do if no drag data */
+ if (!sidebar->drag_data_received &&
+ !get_drag_data (sidebar->list_box, context, time))
+ goto out;
+
+ /* Nothing to do if the target is not valid drop destination */
+ if (!check_valid_drop_target (sidebar, NAUTILUS_GTK_SIDEBAR_ROW (row), context))
+ goto out;
+
+ if (sidebar->drag_data_received &&
+ sidebar->drag_data_info == DND_NAUTILUS_GTK_SIDEBAR_ROW)
+ {
+ /* Dragging bookmarks always moves them to another position in the bookmarks list */
+ action = GDK_ACTION_MOVE;
+ if (sidebar->row_placeholder == NULL)
+ {
+ sidebar->row_placeholder = create_placeholder_row (sidebar);
+ gtk_widget_show (sidebar->row_placeholder);
+ g_object_ref_sink (sidebar->row_placeholder);
+ }
+ else if (GTK_WIDGET (row) == sidebar->row_placeholder)
+ {
+ goto out;
+ }
+
+ if (gtk_widget_get_parent (sidebar->row_placeholder) != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (sidebar->list_box),
+ sidebar->row_placeholder);
+ }
+
+ if (row != NULL)
+ {
+ gint dest_y, dest_x;
+
+ g_object_get (row, "order-index", &row_index, NULL);
+ g_object_get (sidebar->row_placeholder, "order-index", &row_placeholder_index, NULL);
+ /* We order the bookmarks sections based on the bookmark index that we
+ * set on the row as order-index property, but we have to deal with
+ * the placeholder row wanting to be between two consecutive bookmarks,
+ * with two consecutive order-index values which is the usual case.
+ * For that, in the list box sort func we give priority to the placeholder row,
+ * that means that if the index-order is the same as another bookmark
+ * the placeholder row goes before. However if we want to show it after
+ * the current row, for instance when the cursor is in the lower half
+ * of the row, we need to increase the order-index.
+ */
+ row_placeholder_index = row_index;
+ gtk_widget_translate_coordinates (widget, GTK_WIDGET (row),
+ x, y,
+ &dest_x, &dest_y);
+
+ if (dest_y > sidebar->drag_row_height / 2 && row_index > 0)
+ row_placeholder_index++;
+ }
+ else
+ {
+ /* If the user is dragging over an area that has no row, place the row
+ * placeholder in the last position
+ */
+ row_placeholder_index = G_MAXINT32;
+ }
+
+ g_object_set (sidebar->row_placeholder, "order-index", row_placeholder_index, NULL);
+
+ gtk_list_box_prepend (GTK_LIST_BOX (sidebar->list_box),
+ sidebar->row_placeholder);
+ }
+ else
+ {
+ gtk_list_box_drag_highlight_row (GTK_LIST_BOX (sidebar->list_box), row);
+
+ g_object_get (row,
+ "place-type", &place_type,
+ "uri", &drop_target_uri,
+ NULL);
+ /* 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)
+ {
+ if (place_type == PLACES_DROP_FEEDBACK)
+ {
+ action = GDK_ACTION_COPY;
+ }
+ else
+ {
+ /* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */
+ if (drop_target_uri != NULL)
+ {
+ GFile *dest_file = g_file_new_for_uri (drop_target_uri);
+
+ action = emit_drag_action_requested (sidebar, context, dest_file, sidebar->drag_list);
+
+ g_object_unref (dest_file);
+ }
+ }
+ }
+
+ g_free (drop_target_uri);
+ }
+
+ out:
+ start_drop_feedback (sidebar, NAUTILUS_GTK_SIDEBAR_ROW (row), context);
+
+ g_signal_stop_emission_by_name (sidebar->list_box, "drag-motion");
+
+ gdk_drag_status (context, action, time);
+
+ return TRUE;
+}
+
+/* Takes an array of URIs and turns it into a list of GFile */
+static GList *
+build_file_list_from_uris (const gchar **uris)
+{
+ GList *result;
+ gint i;
+
+ result = NULL;
+ for (i = 0; uris && 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 bookmark to the specified position */
+static void
+reorder_bookmarks (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkSidebarRow *row,
+ gint new_position)
+{
+ gchar *uri;
+ GFile *file;
+
+ g_object_get (row, "uri", &uri, NULL);
+ file = g_file_new_for_uri (uri);
+ _nautilus_gtk_bookmarks_manager_reorder_bookmark (sidebar->bookmarks_manager, file, new_position, NULL);
+
+ 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 (NautilusGtkPlacesSidebar *sidebar,
+ GList *files,
+ gint position)
+{
+ GList *l;
+
+ for (l = files; l; l = l->next)
+ {
+ GFile *f = G_FILE (l->data);
+ GFileInfo *info = g_file_query_info (f,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ NULL,
+ NULL);
+
+ if (info)
+ {
+ if ((g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY || g_file_info_get_file_type (info)
== G_FILE_TYPE_MOUNTABLE || g_file_info_get_file_type (info) == G_FILE_TYPE_SHORTCUT))
+ _nautilus_gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, f, position++,
NULL);
+
+ g_object_unref (info);
+ }
+ }
+}
+
+static void
+drag_data_get_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+ GdkAtom target = gtk_selection_data_get_target (data);
+
+ if (target == gdk_atom_intern_static_string ("DND_NAUTILUS_GTK_SIDEBAR_ROW"))
+ {
+ gtk_selection_data_set (data,
+ target,
+ 8,
+ (void*)&sidebar->drag_row,
+ sizeof (gpointer));
+ }
+}
+
+static void
+drag_data_received_callback (GtkWidget *list_box,
+ GdkDragContext *context,
+ int x,
+ int y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ gpointer user_data)
+{
+ gint target_order_index;
+ NautilusGtkPlacesSidebarPlaceType target_place_type;
+ NautilusGtkPlacesSidebarSectionType target_section_type;
+ gchar *target_uri;
+ gboolean success;
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+ GtkListBoxRow *target_row;
+
+ if (!sidebar->drag_data_received)
+ {
+ if (gtk_selection_data_get_target (selection_data) != GDK_NONE &&
+ info == DND_TEXT_URI_LIST)
+ {
+ gchar **uris;
+
+ uris = gtk_selection_data_get_uris (selection_data);
+ /* Free spurious drag data from previous drags if present */
+ if (sidebar->drag_list != NULL)
+ g_list_free_full (sidebar->drag_list, g_object_unref);
+ 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 (list_box, "drag-data-received");
+
+ if (!sidebar->drop_occurred)
+ return;
+
+ target_row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y);
+
+ if (target_row == NULL)
+ return;
+
+ g_object_get (target_row,
+ "place-type", &target_place_type,
+ "section-type", &target_section_type,
+ "order-index", &target_order_index,
+ "uri", &target_uri,
+ NULL);
+
+ success = FALSE;
+
+ if (!check_valid_drop_target (sidebar, NAUTILUS_GTK_SIDEBAR_ROW (target_row), context))
+ goto out;
+
+ if (sidebar->drag_data_info == DND_NAUTILUS_GTK_SIDEBAR_ROW)
+ {
+ GtkWidget **source_row;
+ /* A bookmark got reordered */
+ if (target_section_type != SECTION_BOOKMARKS)
+ goto out;
+
+ source_row = (void*) gtk_selection_data_get_data (selection_data);
+
+ if (sidebar->row_placeholder != NULL)
+ g_object_get (sidebar->row_placeholder, "order-index", &target_order_index, NULL);
+
+ reorder_bookmarks (sidebar, NAUTILUS_GTK_SIDEBAR_ROW (*source_row), target_order_index);
+ success = TRUE;
+ }
+ else
+ {
+ /* Dropping URIs! */
+ GdkDragAction real_action;
+ gchar **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)
+ {
+ GFile *dest_file;
+
+ uris = gtk_selection_data_get_uris (selection_data);
+ source_file_list = build_file_list_from_uris ((const gchar **) uris);
+
+ if (target_place_type == PLACES_DROP_FEEDBACK)
+ {
+ drop_files_as_bookmarks (sidebar, source_file_list, target_order_index);
+ }
+ else
+ {
+ dest_file = g_file_new_for_uri (target_uri);
+
+ emit_drag_perform_drop (sidebar, dest_file, source_file_list, real_action);
+
+ g_object_unref (dest_file);
+ }
+
+ success = TRUE;
+ g_list_free_full (source_file_list, g_object_unref);
+ g_strfreev (uris);
+ }
+ }
+
+out:
+ sidebar->drop_occurred = FALSE;
+ gtk_drag_finish (context, success, FALSE, time);
+ stop_drop_feedback (sidebar);
+ g_free (target_uri);
+}
+
+static void
+drag_end_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ gpointer user_data)
+{
+ stop_drop_feedback (NAUTILUS_GTK_PLACES_SIDEBAR (user_data));
+}
+
+/* This functions is called every time the drag source leaves
+ * the sidebar widget.
+ * The problem is that, we start showing hints for drop when the source
+ * start being above the sidebar or when the application request so show
+ * drop hints, but at some moment we need to restore to normal
+ * state.
+ * One could think that here we could simply call stop_drop_feedback,
+ * but that's not true, because this function is called also before drag_drop,
+ * which needs the data from the drag so we cannot free the drag data here.
+ * So now one could think we could just do nothing here, and wait for
+ * drag-end or drag-failed signals and just stop_drop_feedback there. But that
+ * is also not true, since when the drag comes from a diferent widget than the
+ * sidebar, when the drag stops the last drag signal we receive is drag-leave.
+ * So here what we will do is restore the state of the sidebar as if no drag
+ * is being done (and if the application didnt request for permanent hints with
+ * nautilus_gtk_places_sidebar_show_drop_hints) and we will free the drag data next time
+ * we build new drag data in drag_data_received.
+ */
+static void
+drag_leave_callback (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+
+ if (sidebar->drop_state != DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT)
+ {
+ update_possible_drop_targets (sidebar, FALSE, context);
+ nautilus_gtk_sidebar_row_hide (NAUTILUS_GTK_SIDEBAR_ROW (sidebar->new_bookmark_row), FALSE);
+ sidebar->drop_state = DROP_STATE_NORMAL;
+ }
+
+ sidebar->drag_data_received = FALSE;
+ sidebar->dragging_over = FALSE;
+ sidebar->drag_data_info = DND_UNKNOWN;
+}
+
+static gboolean
+drag_drop_callback (GtkWidget *list_box,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time,
+ gpointer user_data)
+{
+ gboolean retval = FALSE;
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+
+ sidebar->drop_occurred = TRUE;
+ retval = get_drag_data (sidebar->list_box, context, time);
+ g_signal_stop_emission_by_name (sidebar->list_box, "drag-drop");
+
+ return retval;
+}
+
+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 {
+ 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;
+} PopoverData;
+
+static void
+check_popover_sensitivity (NautilusGtkSidebarRow *row,
+ PopoverData *data)
+{
+ gboolean show_mount;
+ gboolean show_unmount;
+ gboolean show_eject;
+ gboolean show_rescan;
+ gboolean show_start;
+ gboolean show_stop;
+ NautilusGtkPlacesSidebarPlaceType type;
+ GDrive *drive;
+ GVolume *volume;
+ GMount *mount;
+ GtkWidget *sidebar;
+ GActionGroup *actions;
+ GAction *action;
+
+ g_object_get (row,
+ "sidebar", &sidebar,
+ "place-type", &type,
+ "drive", &drive,
+ "volume", &volume,
+ "mount", &mount,
+ NULL);
+
+ gtk_widget_set_visible (data->add_shortcut_item, (type == PLACES_MOUNTED_VOLUME));
+
+ actions = gtk_widget_get_action_group (sidebar, "row");
+ action = g_action_map_lookup_action (G_ACTION_MAP (actions), "remove");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (type == PLACES_BOOKMARK));
+ action = g_action_map_lookup_action (G_ACTION_MAP (actions), "rename");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), (type == PLACES_BOOKMARK ||
+ type == PLACES_XDG_DIR));
+ action = g_action_map_lookup_action (G_ACTION_MAP (actions), "open");
+ g_simple_action_set_enabled (G_SIMPLE_ACTION (action), !gtk_list_box_row_is_selected (GTK_LIST_BOX_ROW
(row)));
+
+ check_visibility (mount, volume, 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 */
+ g_object_set (data->start_item, "text", _("_Start"), NULL);
+ g_object_set (data->stop_item, "text", _("_Stop"), NULL);
+ if ((show_start || show_stop) && drive != NULL)
+ {
+ switch (g_drive_get_start_stop_type (drive))
+ {
+ case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
+ /* start() for type G_DRIVE_START_STOP_TYPE_SHUTDOWN is normally not used */
+ g_object_set (data->start_item, "text", _("_Power On"), NULL);
+ g_object_set (data->stop_item, "text", _("_Safely Remove Drive"), NULL);
+ break;
+
+ case G_DRIVE_START_STOP_TYPE_NETWORK:
+ g_object_set (data->start_item, "text", _("_Connect Drive"), NULL);
+ g_object_set (data->stop_item, "text", _("_Disconnect Drive"), NULL);
+ break;
+
+ case G_DRIVE_START_STOP_TYPE_MULTIDISK:
+ g_object_set (data->start_item, "text", _("_Start Multi-disk Device"), NULL);
+ g_object_set (data->stop_item, "text", _("_Stop Multi-disk Device"), NULL);
+ break;
+
+ case G_DRIVE_START_STOP_TYPE_PASSWORD:
+ /* stop() for type G_DRIVE_START_STOP_TYPE_PASSWORD is normally not used */
+ g_object_set (data->start_item, "text", _("_Unlock Device"), NULL);
+ g_object_set (data->stop_item, "text", _("_Lock Device"), NULL);
+ break;
+
+ default:
+ case G_DRIVE_START_STOP_TYPE_UNKNOWN:
+ /* uses defaults set above */
+ break;
+ }
+ }
+
+ if (drive)
+ g_object_unref (drive);
+ if (volume)
+ g_object_unref (volume);
+ if (mount)
+ g_object_unref (mount);
+
+ g_object_unref (sidebar);
+}
+
+static void
+drive_start_from_bookmark_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GError *error;
+ gchar *primary;
+ gchar *name;
+
+ sidebar = NAUTILUS_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);
+ }
+}
+
+static void
+volume_mount_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ NautilusGtkSidebarRow *row = NAUTILUS_GTK_SIDEBAR_ROW (user_data);
+ NautilusGtkPlacesSidebar *sidebar;
+ GVolume *volume;
+ GError *error;
+ gchar *primary;
+ gchar *name;
+ GMount *mount;
+
+ volume = G_VOLUME (source_object);
+ g_object_get (row, "sidebar", &sidebar, NULL);
+
+ 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));
+ if (g_str_has_prefix (error->message, "Error unlocking"))
+ primary = g_strdup_printf (_("Error unlocking “%s”"), name);
+ else
+ 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;
+ nautilus_gtk_sidebar_row_set_busy (row, 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 (row);
+ g_object_unref (sidebar);
+}
+
+static void
+mount_volume (NautilusGtkSidebarRow *row,
+ GVolume *volume)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GMountOperation *mount_op;
+
+ g_object_get (row, "sidebar", &sidebar, NULL);
+
+ mount_op = get_mount_operation (sidebar);
+ g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
+
+ g_object_ref (row);
+ g_object_ref (sidebar);
+ g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, row);
+}
+
+static void
+open_drive (NautilusGtkSidebarRow *row,
+ GDrive *drive,
+ NautilusGtkPlacesOpenFlags open_flags)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+
+ g_object_get (row, "sidebar", &sidebar, NULL);
+
+ if (drive != NULL &&
+ (g_drive_can_start (drive) || g_drive_can_start_degraded (drive)))
+ {
+ GMountOperation *mount_op;
+
+ nautilus_gtk_sidebar_row_set_busy (row, TRUE);
+ mount_op = get_mount_operation (sidebar);
+ g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_from_bookmark_cb, NULL);
+ g_object_unref (mount_op);
+ }
+}
+
+static void
+open_volume (NautilusGtkSidebarRow *row,
+ GVolume *volume,
+ NautilusGtkPlacesOpenFlags open_flags)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+
+ g_object_get (row, "sidebar", &sidebar, NULL);
+
+ if (volume != NULL && !sidebar->mounting)
+ {
+ sidebar->mounting = TRUE;
+ sidebar->go_to_after_mount_open_flags = open_flags;
+ nautilus_gtk_sidebar_row_set_busy (row, TRUE);
+ mount_volume (row, volume);
+ }
+}
+
+static void
+open_uri (NautilusGtkPlacesSidebar *sidebar,
+ const gchar *uri,
+ NautilusGtkPlacesOpenFlags open_flags)
+{
+ GFile *location;
+
+ location = g_file_new_for_uri (uri);
+ emit_open_location (sidebar, location, open_flags);
+ g_object_unref (location);
+}
+
+static void
+open_row (NautilusGtkSidebarRow *row,
+ NautilusGtkPlacesOpenFlags open_flags)
+{
+ gchar *uri;
+ GDrive *drive;
+ GVolume *volume;
+ NautilusGtkPlacesSidebarPlaceType place_type;
+ NautilusGtkPlacesSidebar *sidebar;
+
+ g_object_get (row,
+ "sidebar", &sidebar,
+ "uri", &uri,
+ "place-type", &place_type,
+ "drive", &drive,
+ "volume", &volume,
+ NULL);
+
+ if (place_type == PLACES_OTHER_LOCATIONS)
+ {
+ emit_show_other_locations (sidebar);
+ emit_show_other_locations_with_flags (sidebar, open_flags);
+ }
+ else if (place_type == PLACES_STARRED_LOCATION)
+ {
+ emit_show_starred_location (sidebar, open_flags);
+ }
+ else if (uri != NULL)
+ {
+ open_uri (sidebar, uri, open_flags);
+ }
+ else if (place_type == PLACES_CONNECT_TO_SERVER)
+ {
+ emit_show_connect_to_server (sidebar);
+ }
+ else if (place_type == PLACES_ENTER_LOCATION)
+ {
+ emit_show_enter_location (sidebar);
+ }
+ else if (volume != NULL)
+ {
+ open_volume (row, volume, open_flags);
+ }
+ else if (drive != NULL)
+ {
+ open_drive (row, drive, open_flags);
+ }
+
+ g_object_unref (sidebar);
+ if (drive)
+ g_object_unref (drive);
+ if (volume)
+ g_object_unref (volume);
+ g_free (uri);
+}
+
+/* Callback used for the "Open" menu items in the context menu */
+static void
+open_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ NautilusGtkPlacesOpenFlags flags;
+
+ flags = (NautilusGtkPlacesOpenFlags)g_variant_get_int32 (parameter);
+ open_row (sidebar->context_row, flags);
+}
+
+/* Add bookmark for the selected item - just used from mount points */
+static void
+add_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ gchar *uri;
+ gchar *name;
+ GFile *location;
+
+ g_object_get (sidebar->context_row,
+ "uri", &uri,
+ "label", &name,
+ NULL);
+
+ if (uri != NULL)
+ {
+ location = g_file_new_for_uri (uri);
+ if (_nautilus_gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, location, -1, NULL))
+ _nautilus_gtk_bookmarks_manager_set_bookmark_label (sidebar->bookmarks_manager, location, name,
NULL);
+ g_object_unref (location);
+ }
+
+ g_free (uri);
+ g_free (name);
+}
+
+static void
+rename_entry_changed (GtkEntry *entry,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ NautilusGtkPlacesSidebarPlaceType type;
+ gchar *name;
+ gchar *uri;
+ const gchar *new_name;
+ gboolean found = FALSE;
+ GList *rows;
+ GList *l;
+
+ new_name = gtk_entry_get_text (GTK_ENTRY (sidebar->rename_entry));
+
+ if (strcmp (new_name, "") == 0)
+ {
+ gtk_widget_set_sensitive (sidebar->rename_button, FALSE);
+ gtk_label_set_label (GTK_LABEL (sidebar->rename_error), "");
+ return;
+ }
+
+ rows = gtk_container_get_children (GTK_CONTAINER (sidebar->list_box));
+ for (l = rows; l && !found; l = l->next)
+ {
+ g_object_get (l->data,
+ "place-type", &type,
+ "uri", &uri,
+ "label", &name,
+ NULL);
+
+ if ((type == PLACES_XDG_DIR || type == PLACES_BOOKMARK) &&
+ strcmp (uri, sidebar->rename_uri) != 0 &&
+ strcmp (new_name, name) == 0)
+ found = TRUE;
+
+ g_free (uri);
+ g_free (name);
+ }
+ g_list_free (rows);
+
+ gtk_widget_set_sensitive (sidebar->rename_button, !found);
+ gtk_label_set_label (GTK_LABEL (sidebar->rename_error),
+ found ? _("This name is already taken") : "");
+}
+
+static void
+do_rename (GtkButton *button,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ gchar *new_text;
+ GFile *file;
+
+ new_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (sidebar->rename_entry)));
+
+ file = g_file_new_for_uri (sidebar->rename_uri);
+ if (!_nautilus_gtk_bookmarks_manager_has_bookmark (sidebar->bookmarks_manager, file))
+ _nautilus_gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, file, -1, NULL);
+
+ _nautilus_gtk_bookmarks_manager_set_bookmark_label (sidebar->bookmarks_manager, file, new_text, NULL);
+
+ g_object_unref (file);
+ g_free (new_text);
+
+ g_clear_pointer (&sidebar->rename_uri, g_free);
+
+ if (sidebar->rename_popover)
+ gtk_popover_popdown (GTK_POPOVER (sidebar->rename_popover));
+}
+
+static void
+on_rename_popover_destroy (GtkWidget *rename_popover,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ if (sidebar)
+ {
+ sidebar->rename_popover = NULL;
+ sidebar->rename_entry = NULL;
+ sidebar->rename_button = NULL;
+ sidebar->rename_error = NULL;
+ }
+}
+
+static void
+create_rename_popover (NautilusGtkPlacesSidebar *sidebar)
+{
+ GtkWidget *popover;
+ GtkWidget *grid;
+ GtkWidget *label;
+ GtkWidget *entry;
+ GtkWidget *button;
+ GtkWidget *error;
+ gchar *str;
+
+ if (sidebar->rename_popover)
+ return;
+
+ popover = gtk_popover_new (GTK_WIDGET (sidebar));
+ /* Clean sidebar pointer when its destroyed, most of the times due to its
+ * relative_to associated row being destroyed */
+ g_signal_connect (popover, "destroy", G_CALLBACK (on_rename_popover_destroy), sidebar);
+ gtk_popover_set_position (GTK_POPOVER (popover), GTK_POS_RIGHT);
+ grid = gtk_grid_new ();
+ gtk_container_add (GTK_CONTAINER (popover), grid);
+ g_object_set (grid,
+ "margin", 10,
+ "row-spacing", 6,
+ "column-spacing", 6,
+ NULL);
+ entry = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ g_signal_connect (entry, "changed", G_CALLBACK (rename_entry_changed), sidebar);
+ str = g_strdup_printf ("<b>%s</b>", _("Name"));
+ label = gtk_label_new (str);
+ gtk_widget_set_halign (label, GTK_ALIGN_START);
+ gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+ g_free (str);
+ button = gtk_button_new_with_mnemonic (_("_Rename"));
+ gtk_widget_set_can_default (button, TRUE);
+ gtk_style_context_add_class (gtk_widget_get_style_context (button), "suggested-action");
+ g_signal_connect (button, "clicked", G_CALLBACK (do_rename), sidebar);
+ error = gtk_label_new ("");
+ gtk_widget_set_halign (error, GTK_ALIGN_START);
+ gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 2, 1);
+ gtk_grid_attach (GTK_GRID (grid), entry, 0, 1, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), button,1, 1, 1, 1);
+ gtk_grid_attach (GTK_GRID (grid), error, 0, 2, 2, 1);
+ gtk_widget_show_all (grid);
+ gtk_popover_set_default_widget (GTK_POPOVER (popover), button);
+
+ sidebar->rename_popover = popover;
+ sidebar->rename_entry = entry;
+ sidebar->rename_button = button;
+ sidebar->rename_error = error;
+}
+
+/* Style the row differently while we show a popover for it.
+ * Otherwise, the popover is 'pointing to nothing'. Since the
+ * main popover and the rename popover interleave their hiding
+ * and showing, we have to count to ensure that we don't loose
+ * the state before the last popover is gone.
+ *
+ * This would be nicer as a state, but reusing hover for this
+ * interferes with the normal handling of this state, so just
+ * use a style class.
+ */
+static void
+update_popover_shadowing (GtkWidget *row,
+ gboolean shown)
+{
+ GtkStyleContext *context;
+ gint count;
+
+ count = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "popover-count"));
+ count = shown ? count + 1 : count - 1;
+ g_object_set_data (G_OBJECT (row), "popover-count", GINT_TO_POINTER (count));
+
+ context = gtk_widget_get_style_context (row);
+ if (count > 0)
+ gtk_style_context_add_class (context, "has-open-popup");
+ else
+ gtk_style_context_remove_class (context, "has-open-popup");
+}
+
+static void
+set_prelight (GtkPopover *popover)
+{
+ update_popover_shadowing (gtk_popover_get_relative_to (popover), TRUE);
+}
+
+static void
+unset_prelight (GtkPopover *popover)
+{
+ update_popover_shadowing (gtk_popover_get_relative_to (popover), FALSE);
+}
+
+static void
+setup_popover_shadowing (GtkWidget *popover)
+{
+ g_signal_connect (popover, "map", G_CALLBACK (set_prelight), NULL);
+ g_signal_connect (popover, "unmap", G_CALLBACK (unset_prelight), NULL);
+}
+
+static void
+show_rename_popover (NautilusGtkSidebarRow *row)
+{
+ gchar *name;
+ gchar *uri;
+ NautilusGtkPlacesSidebar *sidebar;
+
+ g_object_get (row,
+ "sidebar", &sidebar,
+ "label", &name,
+ "uri", &uri,
+ NULL);
+
+ create_rename_popover (sidebar);
+
+ if (sidebar->rename_uri)
+ g_free (sidebar->rename_uri);
+ sidebar->rename_uri = g_strdup (uri);
+
+ gtk_entry_set_text (GTK_ENTRY (sidebar->rename_entry), name);
+ gtk_popover_set_relative_to (GTK_POPOVER (sidebar->rename_popover), GTK_WIDGET (row));
+ setup_popover_shadowing (sidebar->rename_popover);
+
+ gtk_popover_popup (GTK_POPOVER (sidebar->rename_popover));
+ gtk_widget_grab_focus (sidebar->rename_entry);
+
+ g_free (name);
+ g_free (uri);
+ g_object_unref (sidebar);
+}
+
+static void
+rename_bookmark (NautilusGtkSidebarRow *row)
+{
+ NautilusGtkPlacesSidebarPlaceType type;
+
+ g_object_get (row, "place-type", &type, NULL);
+
+ if (type != PLACES_BOOKMARK && type != PLACES_XDG_DIR)
+ return;
+
+ show_rename_popover (row);
+}
+
+static void
+rename_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+
+ rename_bookmark (sidebar->context_row);
+}
+
+static void
+remove_bookmark (NautilusGtkSidebarRow *row)
+{
+ NautilusGtkPlacesSidebarPlaceType type;
+ gchar *uri;
+ GFile *file;
+ NautilusGtkPlacesSidebar *sidebar;
+
+ g_object_get (row,
+ "sidebar", &sidebar,
+ "place-type", &type,
+ "uri", &uri,
+ NULL);
+
+ if (type == PLACES_BOOKMARK)
+ {
+ file = g_file_new_for_uri (uri);
+ _nautilus_gtk_bookmarks_manager_remove_bookmark (sidebar->bookmarks_manager, file, NULL);
+ g_object_unref (file);
+ }
+
+ g_free (uri);
+ g_object_unref (sidebar);
+}
+
+static void
+remove_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+
+ remove_bookmark (sidebar->context_row);
+}
+
+static void
+mount_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ GVolume *volume;
+
+ g_object_get (sidebar->context_row,
+ "volume", &volume,
+ NULL);
+
+ if (volume != NULL)
+ mount_volume (sidebar->context_row, 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)
+{
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_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)
+ {
+ gchar *name;
+ gchar *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);
+ }
+
+ g_object_unref (sidebar);
+}
+
+static GMountOperation *
+get_mount_operation (NautilusGtkPlacesSidebar *sidebar)
+{
+ GMountOperation *mount_op;
+
+ mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
+
+ emit_mount_operation (sidebar, mount_op);
+
+ return mount_op;
+}
+
+static GMountOperation *
+get_unmount_operation (NautilusGtkPlacesSidebar *sidebar)
+{
+ GMountOperation *mount_op;
+
+ mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
+
+ emit_unmount_operation (sidebar, mount_op);
+
+ return mount_op;
+}
+
+/* Returns TRUE if file1 is prefix of file2 or if both files have the
+ * same path
+ */
+static gboolean
+file_prefix_or_same (GFile *file1,
+ GFile *file2)
+{
+ return g_file_has_prefix (file1, file2) ||
+ g_file_equal (file1, file2);
+}
+
+static gboolean
+is_current_location_on_volume (NautilusGtkPlacesSidebar *sidebar,
+ GMount *mount,
+ GVolume *volume,
+ GDrive *drive)
+{
+ gboolean current_location_on_volume;
+ GFile *mount_default_location;
+ GMount *mount_for_volume;
+ GList *volumes_for_drive;
+ GList *volume_for_drive;
+
+ current_location_on_volume = FALSE;
+
+ if (sidebar->current_location != NULL)
+ {
+ if (mount != NULL)
+ {
+ mount_default_location = g_mount_get_default_location (mount);
+ current_location_on_volume = file_prefix_or_same (sidebar->current_location,
+ mount_default_location);
+
+ g_object_unref (mount_default_location);
+ }
+ /* This code path is probably never reached since mount always exists,
+ * and if it doesn't exists we don't offer a way to eject a volume or
+ * drive in the UI. Do it for defensive programming
+ */
+ else if (volume != NULL)
+ {
+ mount_for_volume = g_volume_get_mount (volume);
+ if (mount_for_volume != NULL)
+ {
+ mount_default_location = g_mount_get_default_location (mount_for_volume);
+ current_location_on_volume = file_prefix_or_same (sidebar->current_location,
+ mount_default_location);
+
+ g_object_unref (mount_default_location);
+ g_object_unref (mount_for_volume);
+ }
+ }
+ /* This code path is probably never reached since mount always exists,
+ * and if it doesn't exists we don't offer a way to eject a volume or
+ * drive in the UI. Do it for defensive programming
+ */
+ else if (drive != NULL)
+ {
+ volumes_for_drive = g_drive_get_volumes (drive);
+ for (volume_for_drive = volumes_for_drive; volume_for_drive != NULL; volume_for_drive =
volume_for_drive->next)
+ {
+ mount_for_volume = g_volume_get_mount (volume_for_drive->data);
+ if (mount_for_volume != NULL)
+ {
+ mount_default_location = g_mount_get_default_location (mount_for_volume);
+ current_location_on_volume = file_prefix_or_same (sidebar->current_location,
+ mount_default_location);
+
+ g_object_unref (mount_default_location);
+ g_object_unref (mount_for_volume);
+
+ if (current_location_on_volume)
+ break;
+ }
+ }
+ g_list_free_full (volumes_for_drive, g_object_unref);
+ }
+ }
+
+ return current_location_on_volume;
+}
+
+static void
+do_unmount (GMount *mount,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ if (mount != NULL)
+ {
+ GMountOperation *mount_op;
+
+ if (is_current_location_on_volume (sidebar, mount, NULL, NULL))
+ open_home (sidebar);
+
+ 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
+unmount_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ GMount *mount;
+
+ g_object_get (sidebar->context_row,
+ "mount", &mount,
+ NULL);
+
+ do_unmount (mount, sidebar);
+
+ if (mount)
+ g_object_unref (mount);
+}
+
+static void
+drive_stop_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GError *error;
+ gchar *primary;
+ gchar *name;
+
+ sidebar = user_data;
+
+ error = NULL;
+ if (!g_drive_stop_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
+drive_eject_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GError *error;
+ gchar *primary;
+ gchar *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)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GError *error;
+ gchar *primary;
+ gchar *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)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GError *error;
+ gchar *primary;
+ gchar *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,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ GMountOperation *mount_op;
+
+ mount_op = get_unmount_operation (sidebar);
+
+ if (is_current_location_on_volume (sidebar, mount, volume, drive))
+ open_home (sidebar);
+
+ if (mount != NULL)
+ g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb,
+ g_object_ref (sidebar));
+ /* This code path is probably never reached since mount always exists,
+ * and if it doesn't exists we don't offer a way to eject a volume or
+ * drive in the UI. Do it for defensive programming
+ */
+ else if (volume != NULL)
+ g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb,
+ g_object_ref (sidebar));
+ /* This code path is probably never reached since mount always exists,
+ * and if it doesn't exists we don't offer a way to eject a volume or
+ * drive in the UI. Do it for defensive programming
+ */
+ else if (drive != NULL)
+ {
+ if (g_drive_can_stop (drive))
+ g_drive_stop (drive, 0, mount_op, NULL, drive_stop_cb,
+ g_object_ref (sidebar));
+ else
+ 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 (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ GMount *mount;
+ GVolume *volume;
+ GDrive *drive;
+
+ g_object_get (sidebar->context_row,
+ "mount", &mount,
+ "volume", &volume,
+ "drive", &drive,
+ NULL);
+
+ do_eject (mount, volume, drive, sidebar);
+
+ if (mount)
+ g_object_unref (mount);
+ if (volume)
+ g_object_unref (volume);
+ if (drive)
+ g_object_unref (drive);
+}
+
+static gboolean
+eject_or_unmount_bookmark (NautilusGtkSidebarRow *row)
+{
+ gboolean can_unmount, can_eject;
+ GMount *mount;
+ GVolume *volume;
+ GDrive *drive;
+ gboolean ret;
+ NautilusGtkPlacesSidebar *sidebar;
+
+ g_object_get (row,
+ "sidebar", &sidebar,
+ "mount", &mount,
+ "volume", &volume,
+ "drive", &drive,
+ NULL);
+ 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_object_unref (sidebar);
+ if (mount)
+ g_object_unref (mount);
+ if (volume)
+ g_object_unref (volume);
+ if (drive)
+ g_object_unref (drive);
+
+ return ret;
+}
+
+static gboolean
+eject_or_unmount_selection (NautilusGtkPlacesSidebar *sidebar)
+{
+ gboolean ret;
+ GtkListBoxRow *row;
+
+ row = gtk_list_box_get_selected_row (GTK_LIST_BOX (sidebar->list_box));
+ ret = eject_or_unmount_bookmark (NAUTILUS_GTK_SIDEBAR_ROW (row));
+
+ return ret;
+}
+
+static void
+drive_poll_for_media_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GError *error;
+ gchar *primary;
+ gchar *name;
+
+ sidebar = NAUTILUS_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);
+ }
+
+ g_object_unref (sidebar);
+}
+
+static void
+rescan_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ GDrive *drive;
+
+ g_object_get (sidebar->context_row,
+ "drive", &drive,
+ NULL);
+
+ if (drive != NULL)
+ {
+ g_drive_poll_for_media (drive, NULL, drive_poll_for_media_cb, g_object_ref (sidebar));
+ g_object_unref (drive);
+ }
+}
+
+static void
+drive_start_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ GError *error;
+ gchar *primary;
+ gchar *name;
+
+ sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (user_data);
+
+ error = NULL;
+ if (!g_drive_start_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);
+ }
+
+ g_object_unref (sidebar);
+}
+
+static void
+start_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ GDrive *drive;
+
+ g_object_get (sidebar->context_row,
+ "drive", &drive,
+ NULL);
+
+ if (drive != NULL)
+ {
+ GMountOperation *mount_op;
+
+ mount_op = get_mount_operation (sidebar);
+
+ g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_cb, g_object_ref (sidebar));
+
+ g_object_unref (mount_op);
+ g_object_unref (drive);
+ }
+}
+
+static void
+stop_shortcut_cb (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ NautilusGtkPlacesSidebar *sidebar = data;
+ GDrive *drive;
+
+ g_object_get (sidebar->context_row,
+ "drive", &drive,
+ NULL);
+
+ 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
+on_key_press_event (GtkWidget *widget,
+ GdkEventKey *event,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ guint modifiers;
+ GtkListBoxRow *row;
+
+ if (event)
+ {
+ row = gtk_list_box_get_selected_row (GTK_LIST_BOX (sidebar->list_box));
+ if (row)
+ {
+ 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)
+ {
+ NautilusGtkPlacesOpenFlags open_flags = NAUTILUS_GTK_PLACES_OPEN_NORMAL;
+
+ if ((event->state & modifiers) == GDK_SHIFT_MASK)
+ open_flags = NAUTILUS_GTK_PLACES_OPEN_NEW_TAB;
+ else if ((event->state & modifiers) == GDK_CONTROL_MASK)
+ open_flags = NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW;
+
+ open_row (NAUTILUS_GTK_SIDEBAR_ROW (row), 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_Delete ||
+ event->keyval == GDK_KEY_KP_Delete) &&
+ (event->state & modifiers) == 0)
+ {
+ remove_bookmark (NAUTILUS_GTK_SIDEBAR_ROW (row));
+ return TRUE;
+ }
+
+ if ((event->keyval == GDK_KEY_F2) &&
+ (event->state & modifiers) == 0)
+ {
+ rename_bookmark (NAUTILUS_GTK_SIDEBAR_ROW (row));
+ return TRUE;
+ }
+
+ if ((event->keyval == GDK_KEY_Menu) ||
+ ((event->keyval == GDK_KEY_F10) &&
+ (event->state & modifiers) == GDK_SHIFT_MASK))
+
+ {
+ popup_menu_cb (NAUTILUS_GTK_SIDEBAR_ROW (row));
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+static GActionEntry entries[] = {
+ { "open", open_shortcut_cb, "i", NULL, NULL },
+ { "open-other", open_shortcut_cb, "i", NULL, NULL },
+ { "bookmark", add_shortcut_cb, NULL, NULL, NULL },
+ { "remove", remove_shortcut_cb, NULL, NULL, NULL },
+ { "rename", rename_shortcut_cb, NULL, NULL, NULL },
+ { "mount", mount_shortcut_cb, NULL, NULL, NULL },
+ { "unmount", unmount_shortcut_cb, NULL, NULL, NULL },
+ { "eject", eject_shortcut_cb, NULL, NULL, NULL },
+ { "rescan", rescan_shortcut_cb, NULL, NULL, NULL },
+ { "start", start_shortcut_cb, NULL, NULL, NULL },
+ { "stop", stop_shortcut_cb, NULL, NULL, NULL },
+};
+
+static void
+add_actions (NautilusGtkPlacesSidebar *sidebar)
+{
+ GActionGroup *actions;
+
+ actions = G_ACTION_GROUP (g_simple_action_group_new ());
+ g_action_map_add_action_entries (G_ACTION_MAP (actions),
+ entries, G_N_ELEMENTS (entries),
+ sidebar);
+ gtk_widget_insert_action_group (GTK_WIDGET (sidebar), "row", actions);
+ g_object_unref (actions);
+}
+
+static GtkWidget *
+append_separator (GtkWidget *box)
+{
+ GtkWidget *separator;
+
+ separator = g_object_new (GTK_TYPE_SEPARATOR,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "visible", TRUE,
+ "margin-top", 6,
+ "margin-bottom", 6,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), separator);
+
+ return separator;
+}
+
+static GtkWidget *
+add_button (GtkWidget *box,
+ const gchar *label,
+ const gchar *action)
+{
+ GtkWidget *item;
+
+ item = g_object_new (GTK_TYPE_MODEL_BUTTON,
+ "visible", TRUE,
+ "action-name", action,
+ "text", label,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), item);
+
+ return item;
+}
+
+static GtkWidget *
+add_open_button (GtkWidget *box,
+ const gchar *label,
+ NautilusGtkPlacesOpenFlags flags)
+{
+ GtkWidget *item;
+
+ item = g_object_new (GTK_TYPE_MODEL_BUTTON,
+ "visible", TRUE,
+ "action-name", flags == NAUTILUS_GTK_PLACES_OPEN_NORMAL ? "row.open" :
"row.open-other",
+ "action-target", g_variant_new_int32 (flags),
+ "text", label,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (box), item);
+
+ return item;
+}
+
+static void
+on_row_popover_destroy (GtkWidget *row_popover,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ if (sidebar)
+ sidebar->popover = NULL;
+}
+
+#ifdef HAVE_CLOUDPROVIDERS
+static void
+build_popup_menu_using_gmenu (NautilusGtkSidebarRow *row)
+{
+ CloudProvidersAccount *cloud_provider_account;
+ NautilusGtkPlacesSidebar *sidebar;
+ GMenuModel *cloud_provider_menu;
+ GActionGroup *cloud_provider_action_group;
+
+ g_object_get (row,
+ "sidebar", &sidebar,
+ "cloud-provider-account", &cloud_provider_account,
+ NULL);
+
+ /* Cloud provider account */
+ if (cloud_provider_account)
+ {
+ GMenu *menu = g_menu_new ();
+ GMenuItem *item;
+ item = g_menu_item_new (_("_Open"), "row.open");
+ g_menu_item_set_action_and_target_value (item, "row.open",
g_variant_new_int32(NAUTILUS_GTK_PLACES_OPEN_NORMAL));
+ g_menu_append_item (menu, item);
+ if (sidebar->open_flags & NAUTILUS_GTK_PLACES_OPEN_NEW_TAB)
+ {
+ item = g_menu_item_new (_("Open in New _Tab"), "row.open-other");
+ g_menu_item_set_action_and_target_value (item, "row.open-other",
g_variant_new_int32(NAUTILUS_GTK_PLACES_OPEN_NEW_TAB));
+ g_menu_append_item (menu, item);
+ }
+ if (sidebar->open_flags & NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW)
+ {
+ item = g_menu_item_new (_("Open in New _Window"), "row.open-other");
+ g_menu_item_set_action_and_target_value (item, "row.open-other",
g_variant_new_int32(NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW));
+ g_menu_append_item (menu, item);
+ }
+ cloud_provider_menu = cloud_providers_account_get_menu_model (cloud_provider_account);
+ cloud_provider_action_group = cloud_providers_account_get_action_group (cloud_provider_account);
+ if (cloud_provider_menu != NULL && cloud_provider_action_group != NULL)
+ {
+ g_menu_append_section (menu, NULL, cloud_provider_menu);
+ gtk_widget_insert_action_group (GTK_WIDGET (sidebar),
+ "cloudprovider",
+ G_ACTION_GROUP (cloud_provider_action_group));
+ }
+ add_actions (sidebar);
+ if (sidebar->popover)
+ gtk_widget_destroy (sidebar->popover);
+
+ sidebar->popover = gtk_popover_new_from_model (GTK_WIDGET (sidebar),
+ G_MENU_MODEL (menu));
+ g_signal_connect (sidebar->popover, "destroy",
+ G_CALLBACK (on_row_popover_destroy), sidebar);
+ g_object_unref (sidebar);
+ g_object_unref (cloud_provider_account);
+ }
+}
+#endif
+
+/* Constructs the popover for the sidebar row if needed */
+static void
+create_row_popover (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkSidebarRow *row)
+{
+ PopoverData data;
+ GtkWidget *box;
+
+#ifdef HAVE_CLOUDPROVIDERS
+ CloudProvidersAccount *cloud_provider_account;
+
+ g_object_get (row, "cloud-provider-account", &cloud_provider_account, NULL);
+
+ if (cloud_provider_account)
+ {
+ build_popup_menu_using_gmenu (row);
+ return;
+ }
+#endif
+
+ sidebar->popover = gtk_popover_new (GTK_WIDGET (sidebar));
+ /* Clean sidebar pointer when its destroyed, most of the times due to its
+ * relative_to associated row being destroyed */
+ g_signal_connect (sidebar->popover, "destroy", G_CALLBACK (on_row_popover_destroy), sidebar);
+ setup_popover_shadowing (sidebar->popover);
+ box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ g_object_set (box, "margin", 10, NULL);
+ gtk_widget_show (box);
+ gtk_container_add (GTK_CONTAINER (sidebar->popover), box);
+
+ add_open_button (box, _("_Open"), NAUTILUS_GTK_PLACES_OPEN_NORMAL);
+
+ if (sidebar->open_flags & NAUTILUS_GTK_PLACES_OPEN_NEW_TAB)
+ add_open_button (box, _("Open in New _Tab"), NAUTILUS_GTK_PLACES_OPEN_NEW_TAB);
+
+ if (sidebar->open_flags & NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW)
+ add_open_button (box, _("Open in New _Window"), NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW);
+
+ append_separator (box);
+
+ data.add_shortcut_item = add_button (box, _("_Add Bookmark"), "row.bookmark");
+ data.remove_item = add_button (box, _("_Remove"), "row.remove");
+ data.rename_item = add_button (box, _("Rename…"), "row.rename");
+
+ data.separator_item = append_separator (box);
+
+ data.mount_item = add_button (box, _("_Mount"), "row.mount");
+ data.unmount_item = add_button (box, _("_Unmount"), "row.unmount");
+ data.eject_item = add_button (box, _("_Eject"), "row.eject");
+ data.rescan_item = add_button (box, _("_Detect Media"), "row.rescan");
+ data.start_item = add_button (box, _("_Start"), "row.start");
+ data.stop_item = add_button (box, _("_Stop"), "row.stop");
+
+ /* Update everything! */
+ check_popover_sensitivity (row, &data);
+
+ if (sidebar->populate_all)
+ {
+ gchar *uri;
+ GVolume *volume;
+ GFile *file;
+
+ g_object_get (row,
+ "uri", &uri,
+ "volume", &volume,
+ NULL);
+
+ if (uri)
+ file = g_file_new_for_uri (uri);
+ else
+ file = NULL;
+
+ g_signal_emit (sidebar, places_sidebar_signals[POPULATE_POPUP], 0,
+ box, file, volume);
+
+ if (file)
+ g_object_unref (file);
+
+ g_free (uri);
+ if (volume)
+ g_object_unref (volume);
+ }
+}
+
+static void
+show_row_popover (NautilusGtkSidebarRow *row)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+
+ g_object_get (row, "sidebar", &sidebar, NULL);
+
+ if (sidebar->popover)
+ gtk_widget_destroy (sidebar->popover);
+
+ create_row_popover (sidebar, row);
+
+ gtk_popover_set_relative_to (GTK_POPOVER (sidebar->popover), GTK_WIDGET (row));
+
+ sidebar->context_row = row;
+ gtk_popover_popup (GTK_POPOVER (sidebar->popover));
+
+ g_object_unref (sidebar);
+}
+
+static void
+on_row_activated (GtkListBox *list_box,
+ GtkListBoxRow *row,
+ gpointer user_data)
+{
+ NautilusGtkSidebarRow *selected_row;
+
+ /* Avoid to open a location if the user is dragging. Changing the location
+ * while dragging usually makes clients changing the view of the files, which
+ * is confusing while the user has the attention on the drag
+ */
+ if (NAUTILUS_GTK_PLACES_SIDEBAR (user_data)->dragging_over)
+ return;
+
+ selected_row = NAUTILUS_GTK_SIDEBAR_ROW (gtk_list_box_get_selected_row (list_box));
+ open_row (selected_row, 0);
+}
+
+static gboolean
+on_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ NautilusGtkSidebarRow *row)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+ NautilusGtkPlacesSidebarSectionType section_type;
+
+ g_object_get (NAUTILUS_GTK_SIDEBAR_ROW (row),
+ "sidebar", &sidebar,
+ "section_type", §ion_type,
+ NULL);
+
+ if (section_type == SECTION_BOOKMARKS)
+ {
+ sidebar->drag_row = GTK_WIDGET (row);
+ sidebar->drag_row_x = (gint)event->x;
+ sidebar->drag_row_y = (gint)event->y;
+
+ sidebar->drag_root_x = event->x_root;
+ sidebar->drag_root_y = event->y_root;
+ }
+
+ g_object_unref (sidebar);
+
+ return FALSE;
+}
+
+static gboolean
+on_button_release_event (GtkWidget *widget,
+ GdkEventButton *event,
+ NautilusGtkSidebarRow *row)
+{
+ gboolean ret = FALSE;
+ NautilusGtkPlacesSidebarPlaceType row_type;
+
+ if (event && row)
+ {
+ g_object_get (row, "place-type", &row_type, NULL);
+
+ if (event->button == 1)
+ ret = FALSE;
+ else if (event->button == 2)
+ {
+ NautilusGtkPlacesOpenFlags open_flags = NAUTILUS_GTK_PLACES_OPEN_NORMAL;
+
+ open_flags = (event->state & GDK_CONTROL_MASK) ?
+ NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW :
+ NAUTILUS_GTK_PLACES_OPEN_NEW_TAB;
+
+ open_row (NAUTILUS_GTK_SIDEBAR_ROW (row), open_flags);
+ ret = TRUE;
+ }
+ else if (event->button == 3)
+ {
+ if (row_type != PLACES_CONNECT_TO_SERVER)
+ show_row_popover (NAUTILUS_GTK_SIDEBAR_ROW (row));
+ }
+ }
+
+ return ret;
+}
+
+static void
+popup_menu_cb (NautilusGtkSidebarRow *row)
+{
+ NautilusGtkPlacesSidebarPlaceType row_type;
+
+ g_object_get (row, "place-type", &row_type, NULL);
+
+ if (row_type != PLACES_CONNECT_TO_SERVER)
+ show_row_popover (row);
+}
+
+static void
+long_press_cb (GtkGesture *gesture,
+ gdouble x,
+ gdouble y,
+ NautilusGtkPlacesSidebar *sidebar)
+{
+ GtkWidget *row;
+
+ row = GTK_WIDGET (gtk_list_box_get_row_at_y (GTK_LIST_BOX (sidebar->list_box), y));
+ if (NAUTILUS_IS_GTK_SIDEBAR_ROW (row))
+ popup_menu_cb (NAUTILUS_GTK_SIDEBAR_ROW (row));
+}
+
+static gint
+list_box_sort_func (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebarSectionType section_type_1, section_type_2;
+ NautilusGtkPlacesSidebarPlaceType place_type_1, place_type_2;
+ gchar *label_1, *label_2;
+ gint index_1, index_2;
+ gint retval = 0;
+
+ g_object_get (row1,
+ "label", &label_1,
+ "place-type", &place_type_1,
+ "section-type", §ion_type_1,
+ "order-index", &index_1,
+ NULL);
+ g_object_get (row2,
+ "label", &label_2,
+ "place-type", &place_type_2,
+ "section-type", §ion_type_2,
+ "order-index", &index_2,
+ NULL);
+
+ /* Always last position for "connect to server" */
+ if (place_type_1 == PLACES_CONNECT_TO_SERVER)
+ {
+ retval = 1;
+ }
+ else if (place_type_2 == PLACES_CONNECT_TO_SERVER)
+ {
+ retval = -1;
+ }
+ else
+ {
+ if (section_type_1 == section_type_2)
+ {
+ if ((section_type_1 == SECTION_COMPUTER &&
+ place_type_1 == place_type_2 &&
+ place_type_1 == PLACES_XDG_DIR) ||
+ section_type_1 == SECTION_MOUNTS)
+ {
+ retval = g_utf8_collate (label_1, label_2);
+ }
+ else if ((place_type_1 == PLACES_BOOKMARK || place_type_2 == PLACES_DROP_FEEDBACK) &&
+ (place_type_1 == PLACES_DROP_FEEDBACK || place_type_2 == PLACES_BOOKMARK))
+ {
+ retval = index_1 - index_2;
+ }
+ /* We order the bookmarks sections based on the bookmark index that we
+ * set on the row as a order-index property, but we have to deal with
+ * the placeholder row wanted to be between two consecutive bookmarks,
+ * with two consecutive order-index values which is the usual case.
+ * For that, in the list box sort func we give priority to the placeholder row,
+ * that means that if the index-order is the same as another bookmark
+ * the placeholder row goes before. However if we want to show it after
+ * the current row, for instance when the cursor is in the lower half
+ * of the row, we need to increase the order-index.
+ */
+ else if (place_type_1 == PLACES_BOOKMARK_PLACEHOLDER && place_type_2 == PLACES_BOOKMARK)
+ {
+ if (index_1 == index_2)
+ retval = index_1 - index_2 - 1;
+ else
+ retval = index_1 - index_2;
+ }
+ else if (place_type_1 == PLACES_BOOKMARK && place_type_2 == PLACES_BOOKMARK_PLACEHOLDER)
+ {
+ if (index_1 == index_2)
+ retval = index_1 - index_2 + 1;
+ else
+ retval = index_1 - index_2;
+ }
+ }
+ else
+ {
+ /* Order by section. That means the order in the enum of section types
+ * define the actual order of them in the list */
+ retval = section_type_1 - section_type_2;
+ }
+ }
+
+ g_free (label_1);
+ g_free (label_2);
+
+ return retval;
+}
+
+static void
+update_hostname (NautilusGtkPlacesSidebar *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)
+{
+ NautilusGtkPlacesSidebar *sidebar = user_data;
+ GError *error = NULL;
+ GDBusProxy *proxy;
+
+ proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_error_free (error);
+ return;
+ }
+
+ sidebar->hostnamed_proxy = proxy;
+ g_clear_object (&sidebar->hostnamed_cancellable);
+
+ 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 (NautilusGtkPlacesSidebar *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 (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "volume_removed",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "volume_changed",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "mount_added",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "mount_removed",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "mount_changed",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "drive_disconnected",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "drive_connected",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+ g_signal_connect_object (sidebar->volume_monitor, "drive_changed",
+ G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
+}
+
+static void
+shell_shows_desktop_changed (GtkSettings *settings,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NautilusGtkPlacesSidebar *sidebar = user_data;
+ gboolean show_desktop;
+
+ g_assert (settings == sidebar->gtk_settings);
+
+ /* Check if the user explicitly set this and, if so, don't change it. */
+ if (sidebar->show_desktop_set)
+ return;
+
+ g_object_get (settings, "gtk-shell-shows-desktop", &show_desktop, NULL);
+
+ if (show_desktop != sidebar->show_desktop)
+ {
+ sidebar->show_desktop = show_desktop;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_DESKTOP]);
+ }
+}
+
+static void
+nautilus_gtk_places_sidebar_init (NautilusGtkPlacesSidebar *sidebar)
+{
+ GtkTargetList *target_list;
+ gboolean show_desktop;
+ GtkStyleContext *context;
+
+ sidebar->cancellable = g_cancellable_new ();
+
+ sidebar->show_trash = TRUE;
+
+ create_volume_monitor (sidebar);
+
+ sidebar->open_flags = NAUTILUS_GTK_PLACES_OPEN_NORMAL;
+
+ sidebar->bookmarks_manager = _nautilus_gtk_bookmarks_manager_new ((GtkBookmarksChangedFunc)update_places,
sidebar);
+
+ sidebar->trash_monitor = nautilus_trash_monitor_get ();
+ sidebar->trash_monitor_changed_id = g_signal_connect_swapped (sidebar->trash_monitor,
"trash-state-changed",
+ G_CALLBACK (update_trash_icon), sidebar);
+
+ gtk_widget_set_size_request (GTK_WIDGET (sidebar), 140, 280);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (sidebar));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_SIDEBAR);
+ gtk_style_context_set_junction_sides (context, GTK_JUNCTION_RIGHT | GTK_JUNCTION_LEFT);
+
+ /* list box */
+ sidebar->list_box = gtk_list_box_new ();
+
+ gtk_list_box_set_header_func (GTK_LIST_BOX (sidebar->list_box),
+ list_box_header_func, sidebar, NULL);
+ gtk_list_box_set_sort_func (GTK_LIST_BOX (sidebar->list_box),
+ list_box_sort_func, NULL, NULL);
+ gtk_list_box_set_selection_mode (GTK_LIST_BOX (sidebar->list_box), GTK_SELECTION_SINGLE);
+ gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (sidebar->list_box), TRUE);
+
+ g_signal_connect (sidebar->list_box, "row-activated",
+ G_CALLBACK (on_row_activated), sidebar);
+ g_signal_connect (sidebar->list_box, "key-press-event",
+ G_CALLBACK (on_key_press_event), sidebar);
+
+ sidebar->long_press_gesture = gtk_gesture_long_press_new (GTK_WIDGET (sidebar));
+ gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (sidebar->long_press_gesture), TRUE);
+ g_signal_connect (sidebar->long_press_gesture, "pressed",
+ G_CALLBACK (long_press_cb), sidebar);
+
+ /* DND support */
+ gtk_drag_dest_set (sidebar->list_box,
+ 0,
+ NULL, 0,
+ GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
+ target_list = gtk_target_list_new (dnd_drop_targets, G_N_ELEMENTS (dnd_drop_targets));
+ gtk_target_list_add_uri_targets (target_list, DND_TEXT_URI_LIST);
+ gtk_drag_dest_set_target_list (sidebar->list_box, target_list);
+ gtk_target_list_unref (target_list);
+ sidebar->source_targets = gtk_target_list_new (dnd_source_targets, G_N_ELEMENTS (dnd_source_targets));
+ gtk_target_list_add_text_targets (sidebar->source_targets, 0);
+
+ g_signal_connect (sidebar->list_box, "motion-notify-event",
+ G_CALLBACK (on_motion_notify_event), sidebar);
+ g_signal_connect (sidebar->list_box, "drag-begin",
+ G_CALLBACK (drag_begin_callback), sidebar);
+ g_signal_connect (sidebar->list_box, "drag-motion",
+ G_CALLBACK (drag_motion_callback), sidebar);
+ g_signal_connect (sidebar->list_box, "drag-data-get",
+ G_CALLBACK (drag_data_get_callback), sidebar);
+ g_signal_connect (sidebar->list_box, "drag-data-received",
+ G_CALLBACK (drag_data_received_callback), sidebar);
+ g_signal_connect (sidebar->list_box, "drag-drop",
+ G_CALLBACK (drag_drop_callback), sidebar);
+ g_signal_connect (sidebar->list_box, "drag-end",
+ G_CALLBACK (drag_end_callback), sidebar);
+ g_signal_connect (sidebar->list_box, "drag-leave",
+ G_CALLBACK (drag_leave_callback), sidebar);
+ sidebar->drag_row = NULL;
+ sidebar->row_placeholder = NULL;
+ sidebar->dragging_over = FALSE;
+ sidebar->drag_data_info = DND_UNKNOWN;
+
+ gtk_container_add (GTK_CONTAINER (sidebar), sidebar->list_box);
+
+ 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,
+ sidebar);
+
+ sidebar->drop_state = DROP_STATE_NORMAL;
+
+ /* Don't bother trying to trace this across hierarchy changes... */
+ sidebar->gtk_settings = gtk_settings_get_default ();
+ g_signal_connect (sidebar->gtk_settings, "notify::gtk-shell-shows-desktop",
+ G_CALLBACK (shell_shows_desktop_changed), sidebar);
+ g_object_get (sidebar->gtk_settings, "gtk-shell-shows-desktop", &show_desktop, NULL);
+ sidebar->show_desktop = show_desktop;
+
+ /* Cloud providers */
+#ifdef HAVE_CLOUDPROVIDERS
+ sidebar->cloud_manager = cloud_providers_collector_dup_singleton ();
+ g_signal_connect_swapped (sidebar->cloud_manager,
+ "providers-changed",
+ G_CALLBACK (update_places),
+ sidebar);
+#endif
+
+ /* populate the sidebar */
+ update_places (sidebar);
+
+ add_actions (sidebar);
+}
+
+static void
+nautilus_gtk_places_sidebar_set_property (GObject *obj,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (obj);
+
+ switch (property_id)
+ {
+ case PROP_LOCATION:
+ nautilus_gtk_places_sidebar_set_location (sidebar, g_value_get_object (value));
+ break;
+
+ case PROP_OPEN_FLAGS:
+ nautilus_gtk_places_sidebar_set_open_flags (sidebar, g_value_get_flags (value));
+ break;
+
+ case PROP_SHOW_RECENT:
+ nautilus_gtk_places_sidebar_set_show_recent (sidebar, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_DESKTOP:
+ nautilus_gtk_places_sidebar_set_show_desktop (sidebar, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_CONNECT_TO_SERVER:
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ nautilus_gtk_places_sidebar_set_show_connect_to_server (sidebar, g_value_get_boolean (value));
+G_GNUC_END_IGNORE_DEPRECATIONS
+ break;
+
+ case PROP_SHOW_ENTER_LOCATION:
+ nautilus_gtk_places_sidebar_set_show_enter_location (sidebar, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_OTHER_LOCATIONS:
+ nautilus_gtk_places_sidebar_set_show_other_locations (sidebar, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_TRASH:
+ nautilus_gtk_places_sidebar_set_show_trash (sidebar, g_value_get_boolean (value));
+ break;
+
+ case PROP_SHOW_STARRED_LOCATION:
+ nautilus_gtk_places_sidebar_set_show_starred_location (sidebar, g_value_get_boolean (value));
+ break;
+
+ case PROP_LOCAL_ONLY:
+ nautilus_gtk_places_sidebar_set_local_only (sidebar, g_value_get_boolean (value));
+ break;
+
+ case PROP_POPULATE_ALL:
+ if (sidebar->populate_all != g_value_get_boolean (value))
+ {
+ sidebar->populate_all = g_value_get_boolean (value);
+ g_object_notify_by_pspec (obj, pspec);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+ break;
+ }
+}
+
+static void
+nautilus_gtk_places_sidebar_get_property (GObject *obj,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusGtkPlacesSidebar *sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (obj);
+
+ switch (property_id)
+ {
+ case PROP_LOCATION:
+ g_value_take_object (value, nautilus_gtk_places_sidebar_get_location (sidebar));
+ break;
+
+ case PROP_OPEN_FLAGS:
+ g_value_set_flags (value, nautilus_gtk_places_sidebar_get_open_flags (sidebar));
+ break;
+
+ case PROP_SHOW_RECENT:
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_show_recent (sidebar));
+ break;
+
+ case PROP_SHOW_DESKTOP:
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_show_desktop (sidebar));
+ break;
+
+ case PROP_SHOW_CONNECT_TO_SERVER:
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_show_connect_to_server (sidebar));
+G_GNUC_END_IGNORE_DEPRECATIONS
+ break;
+
+ case PROP_SHOW_ENTER_LOCATION:
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_show_enter_location (sidebar));
+ break;
+
+ case PROP_SHOW_OTHER_LOCATIONS:
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_show_other_locations (sidebar));
+ break;
+
+ case PROP_SHOW_TRASH:
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_show_trash (sidebar));
+ break;
+
+ case PROP_SHOW_STARRED_LOCATION:
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_show_starred_location (sidebar));
+ break;
+
+ case PROP_LOCAL_ONLY:
+ g_value_set_boolean (value, nautilus_gtk_places_sidebar_get_local_only (sidebar));
+ break;
+
+ case PROP_POPULATE_ALL:
+ g_value_set_boolean (value, sidebar->populate_all);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
+ break;
+ }
+}
+
+static void
+nautilus_gtk_places_sidebar_dispose (GObject *object)
+{
+ NautilusGtkPlacesSidebar *sidebar;
+#ifdef HAVE_CLOUDPROVIDERS
+ GList *l;
+#endif
+
+ sidebar = NAUTILUS_GTK_PLACES_SIDEBAR (object);
+
+ if (sidebar->cancellable)
+ {
+ g_cancellable_cancel (sidebar->cancellable);
+ g_object_unref (sidebar->cancellable);
+ sidebar->cancellable = NULL;
+ }
+
+ free_drag_data (sidebar);
+
+ if (sidebar->bookmarks_manager != NULL)
+ {
+ _nautilus_gtk_bookmarks_manager_free (sidebar->bookmarks_manager);
+ sidebar->bookmarks_manager = NULL;
+ }
+
+ if (sidebar->popover)
+ {
+ gtk_widget_destroy (sidebar->popover);
+ sidebar->popover = NULL;
+ }
+
+ if (sidebar->rename_popover)
+ {
+ gtk_widget_destroy (sidebar->rename_popover);
+ sidebar->rename_popover = NULL;
+ sidebar->rename_entry = NULL;
+ sidebar->rename_button = NULL;
+ sidebar->rename_error = 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);
+ }
+
+ if (sidebar->trash_row)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (sidebar->trash_row),
+ (gpointer *) &sidebar->trash_row);
+ sidebar->trash_row = NULL;
+ }
+
+ if (sidebar->volume_monitor != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (sidebar->volume_monitor,
+ update_places, 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;
+
+ if (sidebar->gtk_settings)
+ {
+ g_signal_handlers_disconnect_by_func (sidebar->gtk_settings, shell_shows_desktop_changed, sidebar);
+ sidebar->gtk_settings = NULL;
+ }
+
+ g_clear_object (&sidebar->current_location);
+ g_clear_pointer (&sidebar->rename_uri, g_free);
+
+ g_clear_object (&sidebar->long_press_gesture);
+
+ if (sidebar->source_targets)
+ {
+ gtk_target_list_unref (sidebar->source_targets);
+ sidebar->source_targets = NULL;
+ }
+
+ g_slist_free_full (sidebar->shortcuts, g_object_unref);
+ sidebar->shortcuts = NULL;
+
+#ifdef HAVE_CLOUDPROVIDERS
+ for (l = sidebar->unready_accounts; l != NULL; l = l->next)
+ {
+ g_signal_handlers_disconnect_by_data (l->data, sidebar);
+ }
+ g_list_free_full (sidebar->unready_accounts, g_object_unref);
+ sidebar->unready_accounts = NULL;
+
+ if (sidebar->cloud_manager)
+ {
+ g_signal_handlers_disconnect_by_data (sidebar->cloud_manager, sidebar);
+ for (l = cloud_providers_collector_get_providers (sidebar->cloud_manager);
+ l != NULL; l = l->next)
+ {
+ g_signal_handlers_disconnect_by_data (l->data, sidebar);
+ }
+ g_object_unref (sidebar->cloud_manager);
+ sidebar->cloud_manager = NULL;
+ }
+#endif
+
+ G_OBJECT_CLASS (nautilus_gtk_places_sidebar_parent_class)->dispose (object);
+}
+
+static void
+nautilus_gtk_places_sidebar_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (nautilus_gtk_places_sidebar_parent_class)->finalize (object);
+}
+
+static void
+nautilus_gtk_places_sidebar_class_init (NautilusGtkPlacesSidebarClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+
+ gobject_class->dispose = nautilus_gtk_places_sidebar_dispose;
+ gobject_class->finalize = nautilus_gtk_places_sidebar_finalize;
+ gobject_class->set_property = nautilus_gtk_places_sidebar_set_property;
+ gobject_class->get_property = nautilus_gtk_places_sidebar_get_property;
+
+ /**
+ * NautilusGtkPlacesSidebar::open-location:
+ * @sidebar: the object which received the signal.
+ * @location: (type Gio.File): #GFile to which the caller should switch.
+ * @open_flags: a single value from #NautilusGtkPlacesOpenFlags 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.10
+ */
+ places_sidebar_signals [OPEN_LOCATION] =
+ g_signal_new ("open-location",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, open_location),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_OBJECT,
+ GTK_TYPE_PLACES_OPEN_FLAGS);
+
+ /**
+ * NautilusGtkPlacesSidebar::populate-popup:
+ * @sidebar: the object which received the signal.
+ * @container: (type Gtk.Widget): a #GtkMenu or another #GtkContainer
+ * @selected_item: (type Gio.File) (nullable): #GFile with the item to which
+ * the popup should refer, or %NULL in the case of a @selected_volume.
+ * @selected_volume: (type Gio.Volume) (nullable): #GVolume if the selected
+ * item is a volume, or %NULL if it is a file.
+ *
+ * The places sidebar emits this signal when the user invokes a contextual
+ * popup 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 callbacks, the application can use nautilus_gtk_places_sidebar_get_location()
+ * to get the file to which the item refers.
+ *
+ * The @selected_item argument may be %NULL in case the selection refers to
+ * a volume. In this case, @selected_volume will be non-%NULL. In this case,
+ * the calling application will have to g_object_ref() the @selected_volume and
+ * keep it around to use it in the callback.
+ *
+ * The @container and all its contents are destroyed after the user
+ * dismisses the popup. The popup is re-created (and thus, this signal is
+ * emitted) every time the user activates the contextual menu.
+ *
+ * Before 3.18, the @container always was a #GtkMenu, and you were expected
+ * to add your items as #GtkMenuItems. Since 3.18, the popup may be implemented
+ * as a #GtkPopover, in which case @container will be something else, e.g. a
+ * #GtkBox, to which you may add #GtkModelButtons or other widgets, such as
+ * #GtkEntries, #GtkSpinButtons, etc. If your application can deal with this
+ * situation, you can set #NautilusGtkPlacesSidebar::populate-all to %TRUE to request
+ * that this signal is emitted for populating popovers as well.
+ *
+ * Since: 3.10
+ */
+ places_sidebar_signals [POPULATE_POPUP] =
+ g_signal_new ("populate-popup",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, populate_popup),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 3,
+ GTK_TYPE_WIDGET,
+ G_TYPE_FILE,
+ G_TYPE_VOLUME);
+
+ /**
+ * NautilusGtkPlacesSidebar::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.10
+ */
+ places_sidebar_signals [SHOW_ERROR_MESSAGE] =
+ g_signal_new ("show-error-message",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, show_error_message),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ /**
+ * NautilusGtkPlacesSidebar::show-connect-to-server:
+ * @sidebar: the object which received the signal.
+ *
+ * The places sidebar emits this signal when it needs the calling
+ * application to present an way to connect directly to a network server.
+ * For example, the application may bring up a dialog box asking for
+ * a URL like "sftp://ftp.example.com". It is up to the application to create
+ * the corresponding mount by using, for example, g_file_mount_enclosing_volume().
+ *
+ * Deprecated: 3.18: use the #NautilusGtkPlacesSidebar::show-other-locations signal
+ * to connect to network servers.
+ */
+ places_sidebar_signals [SHOW_CONNECT_TO_SERVER] =
+ g_signal_new ("show-connect-to-server",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, show_connect_to_server),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NautilusGtkPlacesSidebar::show-enter-location:
+ * @sidebar: the object which received the signal.
+ *
+ * The places sidebar emits this signal when it needs the calling
+ * application to present an way to directly enter a location.
+ * For example, the application may bring up a dialog box asking for
+ * a URL like "http://http.example.com".
+ *
+ * Since: 3.14
+ */
+ places_sidebar_signals [SHOW_ENTER_LOCATION] =
+ g_signal_new ("show-enter-location",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, show_enter_location),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NautilusGtkPlacesSidebar::drag-action-requested:
+ * @sidebar: the object which received the signal.
+ * @context: (type Gdk.DragContext): #GdkDragContext with information about the drag operation
+ * @dest_file: (type Gio.File): #GFile with the tentative location that is being hovered for a drop
+ * @source_file_list: (type GLib.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.
+ *
+ * Returns: 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.10
+ */
+ places_sidebar_signals [DRAG_ACTION_REQUESTED] =
+ g_signal_new ("drag-action-requested",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, drag_action_requested),
+ NULL, NULL,
+ NULL,
+ G_TYPE_INT, 3,
+ GDK_TYPE_DRAG_CONTEXT,
+ G_TYPE_OBJECT,
+ G_TYPE_POINTER /* GList of GFile */ );
+
+ /**
+ * NautilusGtkPlacesSidebar::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.
+ *
+ * Returns: the final drag action that the sidebar should pass to the drag side
+ * of the drag-and-drop operation.
+ *
+ * Since: 3.10
+ */
+ places_sidebar_signals [DRAG_ACTION_ASK] =
+ g_signal_new ("drag-action-ask",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, drag_action_ask),
+ NULL, NULL,
+ NULL,
+ G_TYPE_INT, 1,
+ G_TYPE_INT);
+
+ /**
+ * NautilusGtkPlacesSidebar::drag-perform-drop:
+ * @sidebar: the object which received the signal.
+ * @dest_file: (type Gio.File): Destination #GFile.
+ * @source_file_list: (type GLib.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.10
+ */
+ places_sidebar_signals [DRAG_PERFORM_DROP] =
+ g_signal_new ("drag-perform-drop",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, drag_perform_drop),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 3,
+ G_TYPE_OBJECT,
+ G_TYPE_POINTER, /* GList of GFile */
+ G_TYPE_INT);
+
+ /**
+ * NautilusGtkPlacesSidebar::show-other-locations:
+ * @sidebar: the object which received the signal.
+ *
+ * The places sidebar emits this signal when it needs the calling
+ * application to present a way to show other locations e.g. drives
+ * and network access points.
+ * For example, the application may bring up a page showing persistent
+ * volumes and discovered network addresses.
+ *
+ * Deprecated: 3.20: use the #NautilusGtkPlacesSidebar::show-other-locations-with-flags
+ * which includes the open flags in order to allow the user to specify to open
+ * in a new tab or window, in a similar way than #NautilusGtkPlacesSidebar::open-location
+ *
+ * Since: 3.18
+ */
+ places_sidebar_signals [SHOW_OTHER_LOCATIONS] =
+ g_signal_new ("show-other-locations",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_DEPRECATED,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, show_other_locations),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NautilusGtkPlacesSidebar::show-other-locations-with-flags:
+ * @sidebar: the object which received the signal.
+ * @open_flags: a single value from #NautilusGtkPlacesOpenFlags specifying how it should be opened.
+ *
+ * The places sidebar emits this signal when it needs the calling
+ * application to present a way to show other locations e.g. drives
+ * and network access points.
+ * For example, the application may bring up a page showing persistent
+ * volumes and discovered network addresses.
+ *
+ * Since: 3.20
+ */
+ places_sidebar_signals [SHOW_OTHER_LOCATIONS_WITH_FLAGS] =
+ g_signal_new ("show-other-locations-with-flags",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, show_other_locations_with_flags),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_PLACES_OPEN_FLAGS);
+
+ /**
+ * NautilusGtkPlacesSidebar::mount:
+ * @sidebar: the object which received the signal.
+ * @mount_operation: the #GMountOperation that is going to start.
+ *
+ * The places sidebar emits this signal when it starts a new operation
+ * because the user clicked on some location that needs mounting.
+ * In this way the application using the #NautilusGtkPlacesSidebar can track the
+ * progress of the operation and, for example, show a notification.
+ *
+ * Since: 3.20
+ */
+ places_sidebar_signals [MOUNT] =
+ g_signal_new ("mount",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, mount),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_MOUNT_OPERATION);
+ /**
+ * NautilusGtkPlacesSidebar::unmount:
+ * @sidebar: the object which received the signal.
+ * @mount_operation: the #GMountOperation that is going to start.
+ *
+ * The places sidebar emits this signal when it starts a new operation
+ * because the user for example ejected some drive or unmounted a mount.
+ * In this way the application using the #NautilusGtkPlacesSidebar can track the
+ * progress of the operation and, for example, show a notification.
+ *
+ * Since: 3.20
+ */
+ places_sidebar_signals [UNMOUNT] =
+ g_signal_new ("unmount",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, unmount),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_MOUNT_OPERATION);
+
+ /**
+ * NautilusGtkPlacesSidebar::show-starred-location:
+ * @sidebar: the object which received the signal.
+ * @open_flags: a single value from #NautilusGtkPlacesOpenFlags specifying how the
+ * starred file should be opened.
+ *
+ * The places sidebar emits this signal when it needs the calling
+ * application to present a way to show the starred files. In GNOME,
+ * starred files are implemented by setting the nao:predefined-tag-favorite
+ * tag in the tracker database.
+ *
+ * Since: 3.22.26
+ */
+ places_sidebar_signals [SHOW_STARRED_LOCATION] =
+ g_signal_new ("show-starred-location",
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusGtkPlacesSidebarClass, show_starred_location),
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_PLACES_OPEN_FLAGS);
+
+ properties[PROP_LOCATION] =
+ g_param_spec_object ("location",
+ "Location to Select",
+ "The location to highlight in the sidebar",
+ G_TYPE_FILE,
+ G_PARAM_READWRITE);
+ properties[PROP_OPEN_FLAGS] =
+ g_param_spec_flags ("open-flags",
+ "Open Flags",
+ "Modes in which the calling application can open locations selected in the
sidebar",
+ GTK_TYPE_PLACES_OPEN_FLAGS,
+ NAUTILUS_GTK_PLACES_OPEN_NORMAL,
+ G_PARAM_READWRITE);
+ properties[PROP_SHOW_RECENT] =
+ g_param_spec_boolean ("show-recent",
+ "Show recent files",
+ "Whether the sidebar includes a builtin shortcut for recent files",
+ TRUE,
+ G_PARAM_READWRITE);
+ properties[PROP_SHOW_DESKTOP] =
+ g_param_spec_boolean ("show-desktop",
+ "Show 'Desktop'",
+ "Whether the sidebar includes a builtin shortcut to the Desktop folder",
+ TRUE,
+ G_PARAM_READWRITE);
+ properties[PROP_SHOW_CONNECT_TO_SERVER] =
+ g_param_spec_boolean ("show-connect-to-server",
+ "Show 'Connect to Server'",
+ "Whether the sidebar includes a builtin shortcut to a 'Connect to server'
dialog",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_DEPRECATED);
+ properties[PROP_SHOW_ENTER_LOCATION] =
+ g_param_spec_boolean ("show-enter-location",
+ "Show 'Enter Location'",
+ "Whether the sidebar includes a builtin shortcut to manually enter a
location",
+ FALSE,
+ G_PARAM_READWRITE);
+ properties[PROP_LOCAL_ONLY] =
+ g_param_spec_boolean ("local-only",
+ "Local Only",
+ "Whether the sidebar only includes local files",
+ FALSE,
+ G_PARAM_READWRITE);
+ properties[PROP_SHOW_TRASH] =
+ g_param_spec_boolean ("show-trash",
+ "Show 'Trash'",
+ "Whether the sidebar includes a builtin shortcut to the Trash location",
+ TRUE,
+ G_PARAM_READWRITE);
+ properties[PROP_SHOW_OTHER_LOCATIONS] =
+ g_param_spec_boolean ("show-other-locations",
+ "Show 'Other locations'",
+ "Whether the sidebar includes an item to show external locations",
+ FALSE,
+ G_PARAM_READWRITE);
+ properties[PROP_SHOW_STARRED_LOCATION] =
+ g_param_spec_boolean ("show-starred-location",
+ "Show “Starred Location”",
+ "Whether the sidebar includes an item to show starred files",
+ FALSE,
+ G_PARAM_READWRITE);
+
+
+ /**
+ * NautilusGtkPlacesSidebar:populate-all:
+ *
+ * If :populate-all is %TRUE, the #NautilusGtkPlacesSidebar::populate-popup signal
+ * is also emitted for popovers.
+ *
+ * Since: 3.18
+ */
+ properties[PROP_POPULATE_ALL] =
+ g_param_spec_boolean ("populate-all",
+ "Populate all",
+ "Whether to emit ::populate-popup for popups that are not menus",
+ FALSE,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
+
+ gtk_widget_class_set_css_name (widget_class, "placessidebar");
+}
+
+/**
+ * nautilus_gtk_places_sidebar_new:
+ *
+ * Creates a new #NautilusGtkPlacesSidebar widget.
+ *
+ * The application should connect to at least the
+ * #NautilusGtkPlacesSidebar::open-location signal to be notified
+ * when the user makes a selection in the sidebar.
+ *
+ * Returns: a newly created #NautilusGtkPlacesSidebar
+ *
+ * Since: 3.10
+ */
+GtkWidget *
+nautilus_gtk_places_sidebar_new (void)
+{
+ return GTK_WIDGET (g_object_new (nautilus_gtk_places_sidebar_get_type (), NULL));
+}
+
+/* Public methods for NautilusGtkPlacesSidebar */
+
+/**
+ * nautilus_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 #NautilusGtkPlacesSidebar::open-location signal is emitted, its flags
+ * argument will be set to one of the @flags that was passed in
+ * nautilus_gtk_places_sidebar_set_open_flags().
+ *
+ * Passing 0 for @flags will cause #NAUTILUS_GTK_PLACES_OPEN_NORMAL to always be sent
+ * to callbacks for the “open-location” signal.
+ *
+ * Since: 3.10
+ */
+void
+nautilus_gtk_places_sidebar_set_open_flags (NautilusGtkPlacesSidebar *sidebar,
+ NautilusGtkPlacesOpenFlags flags)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ if (sidebar->open_flags != flags)
+ {
+ sidebar->open_flags = flags;
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_OPEN_FLAGS]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_open_flags:
+ * @sidebar: a #NautilusGtkPlacesSidebar
+ *
+ * Gets the open flags.
+ *
+ * Returns: the #NautilusGtkPlacesOpenFlags of @sidebar
+ *
+ * Since: 3.10
+ */
+NautilusGtkPlacesOpenFlags
+nautilus_gtk_places_sidebar_get_open_flags (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), 0);
+
+ return sidebar->open_flags;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_location:
+ * @sidebar: a places sidebar
+ * @location: (nullable): 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.10
+ */
+void
+nautilus_gtk_places_sidebar_set_location (NautilusGtkPlacesSidebar *sidebar,
+ GFile *location)
+{
+ GList *children;
+ GList *child;
+ gchar *row_uri;
+ gchar *uri;
+ gboolean found = FALSE;
+
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ gtk_list_box_unselect_all (GTK_LIST_BOX (sidebar->list_box));
+
+ if (sidebar->current_location != NULL)
+ g_object_unref (sidebar->current_location);
+ sidebar->current_location = location;
+ if (sidebar->current_location != NULL)
+ g_object_ref (sidebar->current_location);
+
+ if (location == NULL)
+ goto out;
+
+ uri = g_file_get_uri (location);
+
+ children = gtk_container_get_children (GTK_CONTAINER (sidebar->list_box));
+ for (child = children; child != NULL && !found; child = child->next)
+ {
+ g_object_get (child->data, "uri", &row_uri, NULL);
+ if (row_uri != NULL && g_strcmp0 (row_uri, uri) == 0)
+ {
+ gtk_list_box_select_row (GTK_LIST_BOX (sidebar->list_box),
+ GTK_LIST_BOX_ROW (child->data));
+ found = TRUE;
+ }
+
+ g_free (row_uri);
+ }
+
+ g_free (uri);
+ g_list_free (children);
+
+ out:
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_LOCATION]);
+}
+
+/**
+ * nautilus_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 nautilus_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 #NautilusGtkPlacesSidebar::populate-popup signal, you can use this
+ * function to get the location that is being referred to during the callbacks
+ * for your menu items.
+ *
+ * Returns: (nullable) (transfer full): a #GFile with the selected location, or
+ * %NULL if nothing is visually selected.
+ *
+ * Since: 3.10
+ */
+GFile *
+nautilus_gtk_places_sidebar_get_location (NautilusGtkPlacesSidebar *sidebar)
+{
+ GtkListBoxRow *selected;
+ GFile *file;
+
+ g_return_val_if_fail (sidebar != NULL, NULL);
+
+ file = NULL;
+ selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (sidebar->list_box));
+
+ if (selected)
+ {
+ gchar *uri;
+
+ g_object_get (selected, "uri", &uri, NULL);
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+ }
+
+ return file;
+}
+
+gchar *
+nautilus_gtk_places_sidebar_get_location_title (NautilusGtkPlacesSidebar *sidebar)
+{
+ GtkListBoxRow *selected;
+ gchar *title;
+
+ g_return_val_if_fail (sidebar != NULL, NULL);
+
+ title = NULL;
+ selected = gtk_list_box_get_selected_row (GTK_LIST_BOX (sidebar->list_box));
+
+ if (selected)
+ g_object_get (selected, "label", &title, NULL);
+
+ return title;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_show_recent:
+ * @sidebar: a places sidebar
+ * @show_recent: whether to show an item for recent files
+ *
+ * Sets whether the @sidebar should show an item for recent files.
+ * The default value for this option is determined by the desktop
+ * environment, but this function can be used to override it on a
+ * per-application basis.
+ *
+ * Since: 3.18
+ */
+void
+nautilus_gtk_places_sidebar_set_show_recent (NautilusGtkPlacesSidebar *sidebar,
+ gboolean show_recent)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ sidebar->show_recent_set = TRUE;
+
+ show_recent = !!show_recent;
+ if (sidebar->show_recent != show_recent)
+ {
+ sidebar->show_recent = show_recent;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_RECENT]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_show_recent:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_show_recent()
+ *
+ * Returns: %TRUE if the sidebar will display a builtin shortcut for recent files
+ *
+ * Since: 3.18
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_show_recent (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), FALSE);
+
+ return sidebar->show_recent;
+}
+
+/**
+ * nautilus_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.
+ * The default value for this option is determined by the desktop
+ * environment and the user’s configuration, but this function can be
+ * used to override it on a per-application basis.
+ *
+ * Since: 3.10
+ */
+void
+nautilus_gtk_places_sidebar_set_show_desktop (NautilusGtkPlacesSidebar *sidebar,
+ gboolean show_desktop)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ /* Don't bother disconnecting from the GtkSettings -- it will just
+ * complicate things. Besides, it's highly unlikely that this will
+ * change while we're running, but we can ignore it if it does.
+ */
+ sidebar->show_desktop_set = TRUE;
+
+ 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]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_show_desktop:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_show_desktop()
+ *
+ * Returns: %TRUE if the sidebar will display a builtin shortcut to the desktop folder.
+ *
+ * Since: 3.10
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_show_desktop (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), FALSE);
+
+ return sidebar->show_desktop;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_show_connect_to_server:
+ * @sidebar: a places sidebar
+ * @show_connect_to_server: whether to show an item for the Connect to Server command
+ *
+ * Sets whether the @sidebar should show an item for connecting to a network server;
+ * this is off by default. An application may want to turn this on if it implements
+ * a way for the user to connect to network servers directly.
+ *
+ * If you enable this, you should connect to the
+ * #NautilusGtkPlacesSidebar::show-connect-to-server signal.
+ *
+ * Since: 3.10
+ *
+ * Deprecated: 3.18: It is recommended to group this functionality with the drives
+ * and network location under the new 'Other Location' item
+ */
+void
+nautilus_gtk_places_sidebar_set_show_connect_to_server (NautilusGtkPlacesSidebar *sidebar,
+ gboolean show_connect_to_server)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ show_connect_to_server = !!show_connect_to_server;
+ if (sidebar->show_connect_to_server != show_connect_to_server)
+ {
+ sidebar->show_connect_to_server = show_connect_to_server;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_CONNECT_TO_SERVER]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_show_connect_to_server:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_show_connect_to_server()
+ *
+ * Returns: %TRUE if the sidebar will display a “Connect to Server” item.
+ *
+ * Deprecated: 3.18: It is recommended to group this functionality with the drives
+ * and network location under the new 'Other Location' item
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_show_connect_to_server (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), FALSE);
+
+ return sidebar->show_connect_to_server;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_show_enter_location:
+ * @sidebar: a places sidebar
+ * @show_enter_location: whether to show an item to enter a location
+ *
+ * Sets whether the @sidebar should show an item for entering a location;
+ * this is off by default. An application may want to turn this on if manually
+ * entering URLs is an expected user action.
+ *
+ * If you enable this, you should connect to the
+ * #NautilusGtkPlacesSidebar::show-enter-location signal.
+ *
+ * Since: 3.14
+ */
+void
+nautilus_gtk_places_sidebar_set_show_enter_location (NautilusGtkPlacesSidebar *sidebar,
+ gboolean show_enter_location)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ show_enter_location = !!show_enter_location;
+ if (sidebar->show_enter_location != show_enter_location)
+ {
+ sidebar->show_enter_location = show_enter_location;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_ENTER_LOCATION]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_show_enter_location:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_show_enter_location()
+ *
+ * Returns: %TRUE if the sidebar will display an “Enter Location” item.
+ *
+ * Since: 3.14
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_show_enter_location (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), FALSE);
+
+ return sidebar->show_enter_location;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_show_other_locations:
+ * @sidebar: a places sidebar
+ * @show_other_locations: whether to show an item for the Other Locations view
+ *
+ * Sets whether the @sidebar should show an item for the application to show
+ * an Other Locations view; this is off by default. When set to %TRUE, persistent
+ * devices such as hard drives are hidden, otherwise they are shown in the sidebar.
+ * An application may want to turn this on if it implements a way for the user to
+ * see and interact with drives and network servers directly.
+ *
+ * If you enable this, you should connect to the
+ * #NautilusGtkPlacesSidebar::show-other-locations signal.
+ *
+ * Since: 3.18
+ */
+void
+nautilus_gtk_places_sidebar_set_show_other_locations (NautilusGtkPlacesSidebar *sidebar,
+ gboolean show_other_locations)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ show_other_locations = !!show_other_locations;
+ if (sidebar->show_other_locations != show_other_locations)
+ {
+ sidebar->show_other_locations = show_other_locations;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_OTHER_LOCATIONS]);
+ }
+ }
+
+/**
+ * nautilus_gtk_places_sidebar_get_show_other_locations:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_show_other_locations()
+ *
+ * Returns: %TRUE if the sidebar will display an “Other Locations” item.
+ *
+ * Since: 3.18
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_show_other_locations (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), FALSE);
+
+ return sidebar->show_other_locations;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_show_trash:
+ * @sidebar: a places sidebar
+ * @show_trash: whether to show an item for the Trash location
+ *
+ * Sets whether the @sidebar should show an item for the Trash location.
+ *
+ * Since: 3.18
+ */
+void
+nautilus_gtk_places_sidebar_set_show_trash (NautilusGtkPlacesSidebar *sidebar,
+ gboolean show_trash)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ show_trash = !!show_trash;
+ if (sidebar->show_trash != show_trash)
+ {
+ sidebar->show_trash = show_trash;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_TRASH]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_show_trash:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_show_trash()
+ *
+ * Returns: %TRUE if the sidebar will display a “Trash” item.
+ *
+ * Since: 3.18
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_show_trash (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), TRUE);
+
+ return sidebar->show_trash;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_local_only:
+ * @sidebar: a places sidebar
+ * @local_only: whether to show only local files
+ *
+ * Sets whether the @sidebar should only show local files.
+ *
+ * Since: 3.12
+ */
+void
+nautilus_gtk_places_sidebar_set_local_only (NautilusGtkPlacesSidebar *sidebar,
+ gboolean local_only)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ local_only = !!local_only;
+ if (sidebar->local_only != local_only)
+ {
+ sidebar->local_only = local_only;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_LOCAL_ONLY]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_local_only:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_local_only().
+ *
+ * Returns: %TRUE if the sidebar will only show local files.
+ *
+ * Since: 3.12
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_local_only (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), FALSE);
+
+ return sidebar->local_only;
+}
+
+static GSList *
+find_shortcut_link (NautilusGtkPlacesSidebar *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;
+}
+
+/**
+ * nautilus_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.10
+ */
+void
+nautilus_gtk_places_sidebar_add_shortcut (NautilusGtkPlacesSidebar *sidebar,
+ GFile *location)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_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);
+}
+
+/**
+ * nautilus_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 nautilus_gtk_places_sidebar_add_shortcut(). If the @location is not a
+ * shortcut in the sidebar, then nothing is done.
+ *
+ * Since: 3.10
+ */
+void
+nautilus_gtk_places_sidebar_remove_shortcut (NautilusGtkPlacesSidebar *sidebar,
+ GFile *location)
+{
+ GSList *link;
+ GFile *shortcut;
+
+ g_return_if_fail (NAUTILUS_IS_GTK_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);
+}
+
+/**
+ * nautilus_gtk_places_sidebar_list_shortcuts:
+ * @sidebar: a places sidebar
+ *
+ * Gets the list of shortcuts.
+ *
+ * Returns: (element-type GFile) (transfer full):
+ * A #GSList of #GFile of the locations that have been added as
+ * application-specific shortcuts with nautilus_gtk_places_sidebar_add_shortcut().
+ * To free this list, you can use
+ * |[<!-- language="C" -->
+ * g_slist_free_full (list, (GDestroyNotify) g_object_unref);
+ * ]|
+ *
+ * Since: 3.10
+ */
+GSList *
+nautilus_gtk_places_sidebar_list_shortcuts (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), NULL);
+
+ return g_slist_copy_deep (sidebar->shortcuts, (GCopyFunc) g_object_ref, NULL);
+}
+
+/**
+ * nautilus_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.
+ *
+ * Returns: (nullable) (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.10
+ */
+GFile *
+nautilus_gtk_places_sidebar_get_nth_bookmark (NautilusGtkPlacesSidebar *sidebar,
+ gint n)
+{
+ GList *rows;
+ GList *l;
+ int k;
+ GFile *file;
+
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), NULL);
+
+ file = NULL;
+ rows = gtk_container_get_children (GTK_CONTAINER (sidebar->list_box));
+ l = rows;
+
+ k = 0;
+ while (l != NULL)
+ {
+ NautilusGtkPlacesSidebarPlaceType place_type;
+ gchar *uri;
+
+ g_object_get (l->data,
+ "place-type", &place_type,
+ "uri", &uri,
+ NULL);
+ if (place_type == PLACES_BOOKMARK)
+ {
+ if (k == n)
+ {
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+ break;
+ }
+ k++;
+ }
+ g_free (uri);
+ l = l->next;
+ }
+
+ g_list_free (rows);
+
+ return file;
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_drop_targets_visible:
+ * @sidebar: a places sidebar.
+ * @visible: whether to show the valid targets or not.
+ * @context: drag context used to ask the source about the action that wants to
+ * perform, so hints are more accurate.
+ *
+ * Make the NautilusGtkPlacesSidebar show drop targets, so it can show the available
+ * drop targets and a "new bookmark" row. This improves the Drag-and-Drop
+ * experience of the user and allows applications to show all available
+ * drop targets at once.
+ *
+ * This needs to be called when the application is aware of an ongoing drag
+ * that might target the sidebar. The drop-targets-visible state will be unset
+ * automatically if the drag finishes in the NautilusGtkPlacesSidebar. You only need
+ * to unset the state when the drag ends on some other widget on your application.
+ *
+ * Since: 3.18
+ */
+void
+nautilus_gtk_places_sidebar_set_drop_targets_visible (NautilusGtkPlacesSidebar *sidebar,
+ gboolean visible,
+ GdkDragContext *context)
+{
+ if (visible)
+ {
+ sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT;
+ start_drop_feedback (sidebar, NULL, context);
+ }
+ else
+ {
+ if (sidebar->drop_state == DROP_STATE_NEW_BOOKMARK_ARMED_PERMANENT ||
+ sidebar->drop_state == DROP_STATE_NEW_BOOKMARK_ARMED)
+ {
+ if (!sidebar->dragging_over)
+ {
+ sidebar->drop_state = DROP_STATE_NORMAL;
+ stop_drop_feedback (sidebar);
+ }
+ else
+ {
+ /* In case this is called while we are dragging we need to mark the
+ * drop state as no permanent so the leave timeout can do its job.
+ * This will only happen in applications that call this in a wrong
+ * time */
+ sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED;
+ }
+ }
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_set_show_starred_location:
+ * @sidebar: a places sidebar
+ * @show_starred_location: whether to show an item for Starred files
+ *
+ * If you enable this, you should connect to the
+ * #NautilusGtkPlacesSidebar::show-starred-location signal.
+ *
+ * Since: 3.22.26
+ */
+void
+nautilus_gtk_places_sidebar_set_show_starred_location (NautilusGtkPlacesSidebar *sidebar,
+ gboolean show_starred_location)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar));
+
+ show_starred_location = !!show_starred_location;
+ if (sidebar->show_starred_location != show_starred_location)
+ {
+ sidebar->show_starred_location = show_starred_location;
+ update_places (sidebar);
+ g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_STARRED_LOCATION]);
+ }
+}
+
+/**
+ * nautilus_gtk_places_sidebar_get_show_starred_location:
+ * @sidebar: a places sidebar
+ *
+ * Returns the value previously set with nautilus_gtk_places_sidebar_set_show_starred_location()
+ *
+ * Returns: %TRUE if the sidebar will display a Starred item.
+ *
+ * Since: 3.22.26
+ */
+gboolean
+nautilus_gtk_places_sidebar_get_show_starred_location (NautilusGtkPlacesSidebar *sidebar)
+{
+ g_return_val_if_fail (NAUTILUS_IS_GTK_PLACES_SIDEBAR (sidebar), FALSE);
+
+ return sidebar->show_starred_location;
+}
diff --git a/src/gtk/nautilusgtkplacessidebar.h b/src/gtk/nautilusgtkplacessidebar.h
new file mode 100644
index 000000000..e7111ecf6
--- /dev/null
+++ b/src/gtk/nautilusgtkplacessidebar.h
@@ -0,0 +1,159 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* NautilusGtkPlacesSidebar - sidebar widget for places in the filesystem
+ *
+ * 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.1 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This code comes from Nautilus, GNOME’s file manager.
+ *
+ * Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk)
+ * Federico Mena Quintero <federico gnome org>
+ */
+
+#ifndef __NAUTILUS_GTK_PLACES_SIDEBAR_H__
+#define __NAUTILUS_GTK_PLACES_SIDEBAR_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#endif
+
+#include <gtk/gtkwidget.h>
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_GTK_PLACES_SIDEBAR (nautilus_gtk_places_sidebar_get_type ())
+#define NAUTILUS_GTK_PLACES_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
NAUTILUS_TYPE_GTK_PLACES_SIDEBAR, NautilusGtkPlacesSidebar))
+#define NAUTILUS_GTK_PLACES_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
NAUTILUS_TYPE_GTK_PLACES_SIDEBAR, NautilusGtkPlacesSidebarClass))
+#define NAUTILUS_IS_GTK_PLACES_SIDEBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
NAUTILUS_TYPE_GTK_PLACES_SIDEBAR))
+#define NAUTILUS_IS_GTK_PLACES_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
NAUTILUS_TYPE_GTK_PLACES_SIDEBAR))
+#define NAUTILUS_GTK_PLACES_SIDEBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
NAUTILUS_TYPE_GTK_PLACES_SIDEBAR, NautilusGtkPlacesSidebarClass))
+
+typedef struct _NautilusGtkPlacesSidebar NautilusGtkPlacesSidebar;
+typedef struct _NautilusGtkPlacesSidebarClass NautilusGtkPlacesSidebarClass;
+
+/**
+ * NautilusGtkPlacesOpenFlags:
+ * @NAUTILUS_GTK_PLACES_OPEN_NORMAL: This is the default mode that #NautilusGtkPlacesSidebar 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.
+ * @NAUTILUS_GTK_PLACES_OPEN_NEW_TAB: When passed to nautilus_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 #NautilusGtkPlacesSidebar::open-location signal when the user selects
+ * that a location be opened in a new tab instead of in the standard fashion.
+ * @NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW: Similar to @NAUTILUS_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
nautilus_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
+ * #NautilusGtkPlacesSidebar::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 nautilus_gtk_places_sidebar_set_open_flags(), then the sidebar will only
+ * use #NAUTILUS_GTK_PLACES_OPEN_NORMAL in the #NautilusGtkPlacesSidebar::open-location signal. This is the
+ * default mode of operation.
+ */
+typedef enum {
+ NAUTILUS_GTK_PLACES_OPEN_NORMAL = 1 << 0,
+ NAUTILUS_GTK_PLACES_OPEN_NEW_TAB = 1 << 1,
+ NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW = 1 << 2
+} NautilusGtkPlacesOpenFlags;
+
+GDK_AVAILABLE_IN_3_10
+GType nautilus_gtk_places_sidebar_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_3_10
+GtkWidget * nautilus_gtk_places_sidebar_new (void);
+
+GDK_AVAILABLE_IN_3_10
+NautilusGtkPlacesOpenFlags nautilus_gtk_places_sidebar_get_open_flags (NautilusGtkPlacesSidebar
*sidebar);
+GDK_AVAILABLE_IN_3_10
+void nautilus_gtk_places_sidebar_set_open_flags (NautilusGtkPlacesSidebar
*sidebar,
+ NautilusGtkPlacesOpenFlags flags);
+
+GDK_AVAILABLE_IN_3_10
+GFile * nautilus_gtk_places_sidebar_get_location (NautilusGtkPlacesSidebar
*sidebar);
+GDK_AVAILABLE_IN_3_10
+void nautilus_gtk_places_sidebar_set_location (NautilusGtkPlacesSidebar
*sidebar,
+ GFile *location);
+
+GDK_AVAILABLE_IN_3_18
+gboolean nautilus_gtk_places_sidebar_get_show_recent (NautilusGtkPlacesSidebar
*sidebar);
+GDK_AVAILABLE_IN_3_18
+void nautilus_gtk_places_sidebar_set_show_recent (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean show_recent);
+
+GDK_AVAILABLE_IN_3_10
+gboolean nautilus_gtk_places_sidebar_get_show_desktop (NautilusGtkPlacesSidebar
*sidebar);
+GDK_AVAILABLE_IN_3_10
+void nautilus_gtk_places_sidebar_set_show_desktop (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean show_desktop);
+
+GDK_DEPRECATED_IN_3_18
+gboolean nautilus_gtk_places_sidebar_get_show_connect_to_server (NautilusGtkPlacesSidebar
*sidebar);
+GDK_DEPRECATED_IN_3_18
+void nautilus_gtk_places_sidebar_set_show_connect_to_server (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean
show_connect_to_server);
+GDK_AVAILABLE_IN_3_14
+gboolean nautilus_gtk_places_sidebar_get_show_enter_location (NautilusGtkPlacesSidebar
*sidebar);
+GDK_AVAILABLE_IN_3_14
+void nautilus_gtk_places_sidebar_set_show_enter_location (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean show_enter_location);
+
+GDK_AVAILABLE_IN_3_12
+void nautilus_gtk_places_sidebar_set_local_only (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean local_only);
+GDK_AVAILABLE_IN_3_12
+gboolean nautilus_gtk_places_sidebar_get_local_only (NautilusGtkPlacesSidebar
*sidebar);
+
+
+GDK_AVAILABLE_IN_3_10
+void nautilus_gtk_places_sidebar_add_shortcut (NautilusGtkPlacesSidebar
*sidebar,
+ GFile *location);
+GDK_AVAILABLE_IN_3_10
+void nautilus_gtk_places_sidebar_remove_shortcut (NautilusGtkPlacesSidebar
*sidebar,
+ GFile *location);
+GDK_AVAILABLE_IN_3_10
+GSList * nautilus_gtk_places_sidebar_list_shortcuts (NautilusGtkPlacesSidebar
*sidebar);
+
+GDK_AVAILABLE_IN_3_10
+GFile * nautilus_gtk_places_sidebar_get_nth_bookmark (NautilusGtkPlacesSidebar
*sidebar,
+ gint n);
+GDK_AVAILABLE_IN_3_18
+void nautilus_gtk_places_sidebar_set_drop_targets_visible (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean visible,
+ GdkDragContext *context);
+GDK_AVAILABLE_IN_3_18
+gboolean nautilus_gtk_places_sidebar_get_show_trash (NautilusGtkPlacesSidebar
*sidebar);
+GDK_AVAILABLE_IN_3_18
+void nautilus_gtk_places_sidebar_set_show_trash (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean show_trash);
+
+GDK_AVAILABLE_IN_3_18
+void nautilus_gtk_places_sidebar_set_show_other_locations (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean show_other_locations);
+GDK_AVAILABLE_IN_3_18
+gboolean nautilus_gtk_places_sidebar_get_show_other_locations (NautilusGtkPlacesSidebar
*sidebar);
+
+GDK_AVAILABLE_IN_3_22
+void nautilus_gtk_places_sidebar_set_show_starred_location (NautilusGtkPlacesSidebar
*sidebar,
+ gboolean
show_starred_location);
+GDK_AVAILABLE_IN_3_22
+gboolean nautilus_gtk_places_sidebar_get_show_starred_location (NautilusGtkPlacesSidebar
*sidebar);
+G_END_DECLS
+
+#endif /* __NAUTILUS_GTK_PLACES_SIDEBAR_H__ */
diff --git a/src/gtk/nautilusgtkplacessidebarprivate.h b/src/gtk/nautilusgtkplacessidebarprivate.h
new file mode 100644
index 000000000..6c6dd3daa
--- /dev/null
+++ b/src/gtk/nautilusgtkplacessidebarprivate.h
@@ -0,0 +1,60 @@
+/* nautilusgtkplacessidebarprivate.h
+ *
+ * Copyright (C) 2015 Red Hat
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Carlos Soriano <csoriano gnome org>
+ */
+
+#ifndef __NAUTILUS_GTK_PLACES_SIDEBAR_PRIVATE_H__
+#define __NAUTILUS_GTK_PLACES_SIDEBAR_PRIVATE_H__
+
+#include <glib.h>
+#include "nautilusgtkplacessidebar.h"
+
+G_BEGIN_DECLS
+
+/* Keep order, since it's used for the sort functions */
+typedef enum {
+ SECTION_INVALID,
+ SECTION_COMPUTER,
+ SECTION_MOUNTS,
+ SECTION_CLOUD,
+ SECTION_BOOKMARKS,
+ SECTION_OTHER_LOCATIONS,
+ N_SECTIONS
+} NautilusGtkPlacesSidebarSectionType;
+
+typedef enum {
+ PLACES_INVALID,
+ PLACES_BUILT_IN,
+ PLACES_XDG_DIR,
+ PLACES_MOUNTED_VOLUME,
+ PLACES_BOOKMARK,
+ PLACES_HEADING,
+ PLACES_CONNECT_TO_SERVER,
+ PLACES_ENTER_LOCATION,
+ PLACES_DROP_FEEDBACK,
+ PLACES_BOOKMARK_PLACEHOLDER,
+ PLACES_OTHER_LOCATIONS,
+ PLACES_STARRED_LOCATION,
+ N_PLACES
+} NautilusGtkPlacesSidebarPlaceType;
+
+gchar *nautilus_gtk_places_sidebar_get_location_title (NautilusGtkPlacesSidebar *sidebar);
+
+G_END_DECLS
+
+#endif /* __NAUTILUS_GTK_PLACES_SIDEBAR_PRIVATE_H__ */
diff --git a/src/gtk/nautilusgtkplacesview.c b/src/gtk/nautilusgtkplacesview.c
index b38777f2a..62505c0e1 100644
--- a/src/gtk/nautilusgtkplacesview.c
+++ b/src/gtk/nautilusgtkplacesview.c
@@ -50,8 +50,8 @@
struct _NautilusGtkPlacesViewPrivate
{
GVolumeMonitor *volume_monitor;
- GtkPlacesOpenFlags open_flags;
- GtkPlacesOpenFlags current_open_flags;
+ NautilusGtkPlacesOpenFlags open_flags;
+ NautilusGtkPlacesOpenFlags current_open_flags;
GFile *server_list_file;
GFileMonitor *server_list_monitor;
@@ -150,14 +150,14 @@ static GParamSpec *properties [LAST_PROP];
static void
emit_open_location (NautilusGtkPlacesView *view,
GFile *location,
- GtkPlacesOpenFlags open_flags)
+ NautilusGtkPlacesOpenFlags open_flags)
{
NautilusGtkPlacesViewPrivate *priv;
priv = nautilus_gtk_places_view_get_instance_private (view);
if ((open_flags & priv->open_flags) == 0)
- open_flags = GTK_PLACES_OPEN_NORMAL;
+ open_flags = NAUTILUS_GTK_PLACES_OPEN_NORMAL;
g_signal_emit (view, places_view_signals[OPEN_LOCATION], 0, location, open_flags);
}
@@ -350,7 +350,7 @@ set_busy_cursor (NautilusGtkPlacesView *view,
static void
activate_row (NautilusGtkPlacesView *view,
NautilusGtkPlacesViewRow *row,
- GtkPlacesOpenFlags flags)
+ NautilusGtkPlacesOpenFlags flags)
{
NautilusGtkPlacesViewPrivate *priv;
GVolume *volume;
@@ -1567,7 +1567,7 @@ open_cb (GtkMenuItem *item,
NautilusGtkPlacesView *self;
self = NAUTILUS_GTK_PLACES_VIEW (gtk_widget_get_ancestor (GTK_WIDGET (row),
NAUTILUS_TYPE_GTK_PLACES_VIEW));
- activate_row (self, row, GTK_PLACES_OPEN_NORMAL);
+ activate_row (self, row, NAUTILUS_GTK_PLACES_OPEN_NORMAL);
}
static void
@@ -1577,7 +1577,7 @@ open_in_new_tab_cb (GtkMenuItem *item,
NautilusGtkPlacesView *self;
self = NAUTILUS_GTK_PLACES_VIEW (gtk_widget_get_ancestor (GTK_WIDGET (row),
NAUTILUS_TYPE_GTK_PLACES_VIEW));
- activate_row (self, row, GTK_PLACES_OPEN_NEW_TAB);
+ activate_row (self, row, NAUTILUS_GTK_PLACES_OPEN_NEW_TAB);
}
static void
@@ -1587,7 +1587,7 @@ open_in_new_window_cb (GtkMenuItem *item,
NautilusGtkPlacesView *self;
self = NAUTILUS_GTK_PLACES_VIEW (gtk_widget_get_ancestor (GTK_WIDGET (row),
NAUTILUS_TYPE_GTK_PLACES_VIEW));
- activate_row (self, row, GTK_PLACES_OPEN_NEW_WINDOW);
+ activate_row (self, row, NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW);
}
static void
@@ -1709,7 +1709,7 @@ build_popup_menu (NautilusGtkPlacesView *view,
gtk_widget_show (item);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
- if (priv->open_flags & GTK_PLACES_OPEN_NEW_TAB)
+ if (priv->open_flags & NAUTILUS_GTK_PLACES_OPEN_NEW_TAB)
{
item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab"));
g_signal_connect (item,
@@ -1720,7 +1720,7 @@ build_popup_menu (NautilusGtkPlacesView *view,
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
}
- if (priv->open_flags & GTK_PLACES_OPEN_NEW_WINDOW)
+ if (priv->open_flags & NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW)
{
item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window"));
g_signal_connect (item,
@@ -1829,7 +1829,7 @@ on_key_press_event (GtkWidget *widget,
GtkWidget *focus_widget;
GtkWindow *toplevel;
- priv->current_open_flags = GTK_PLACES_OPEN_NORMAL;
+ priv->current_open_flags = NAUTILUS_GTK_PLACES_OPEN_NORMAL;
toplevel = get_toplevel (GTK_WIDGET (view));
if (!toplevel)
@@ -1841,9 +1841,9 @@ on_key_press_event (GtkWidget *widget,
return FALSE;
if ((event->state & modifiers) == GDK_SHIFT_MASK)
- priv->current_open_flags = GTK_PLACES_OPEN_NEW_TAB;
+ priv->current_open_flags = NAUTILUS_GTK_PLACES_OPEN_NEW_TAB;
else if ((event->state & modifiers) == GDK_CONTROL_MASK)
- priv->current_open_flags = GTK_PLACES_OPEN_NEW_WINDOW;
+ priv->current_open_flags = NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW;
activate_row (view, NAUTILUS_GTK_PLACES_VIEW_ROW (focus_widget), priv->current_open_flags);
@@ -1981,7 +1981,7 @@ on_listbox_row_activated (NautilusGtkPlacesView *view,
NautilusGtkPlacesViewPrivate *priv;
GdkEvent *event;
guint button;
- GtkPlacesOpenFlags open_flags;
+ NautilusGtkPlacesOpenFlags open_flags;
priv = nautilus_gtk_places_view_get_instance_private (view);
@@ -1989,7 +1989,7 @@ on_listbox_row_activated (NautilusGtkPlacesView *view,
gdk_event_get_button (event, &button);
if (gdk_event_get_event_type (event) == GDK_BUTTON_RELEASE && button == GDK_BUTTON_MIDDLE)
- open_flags = GTK_PLACES_OPEN_NEW_TAB;
+ open_flags = NAUTILUS_GTK_PLACES_OPEN_NEW_TAB;
else
open_flags = priv->current_open_flags;
@@ -2291,7 +2291,7 @@ nautilus_gtk_places_view_class_init (NautilusGtkPlacesViewClass *klass)
* NautilusGtkPlacesView::open-location:
* @view: the object which received the signal.
* @location: (type Gio.File): #GFile to which the caller should switch.
- * @open_flags: a single value from #GtkPlacesOpenFlags specifying how the @location
+ * @open_flags: a single value from #NautilusGtkPlacesOpenFlags specifying how the @location
* should be opened.
*
* The places view emits this signal when the user selects a location
@@ -2362,7 +2362,7 @@ nautilus_gtk_places_view_class_init (NautilusGtkPlacesViewClass *klass)
"Open Flags",
"Modes in which the calling application can open locations selected in the
sidebar",
GTK_TYPE_PLACES_OPEN_FLAGS,
- GTK_PLACES_OPEN_NORMAL,
+ NAUTILUS_GTK_PLACES_OPEN_NORMAL,
G_PARAM_READWRITE);
g_object_class_install_properties (object_class, LAST_PROP, properties);
@@ -2401,7 +2401,7 @@ nautilus_gtk_places_view_init (NautilusGtkPlacesView *self)
priv = nautilus_gtk_places_view_get_instance_private (self);
priv->volume_monitor = g_volume_monitor_get ();
- priv->open_flags = GTK_PLACES_OPEN_NORMAL;
+ priv->open_flags = NAUTILUS_GTK_PLACES_OPEN_NORMAL;
priv->path_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
priv->space_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
@@ -2447,14 +2447,14 @@ nautilus_gtk_places_view_new (void)
* argument will be set to one of the @flags that was passed in
* nautilus_gtk_places_view_set_open_flags().
*
- * Passing 0 for @flags will cause #GTK_PLACES_OPEN_NORMAL to always be sent
+ * Passing 0 for @flags will cause #NAUTILUS_GTK_PLACES_OPEN_NORMAL to always be sent
* to callbacks for the “open-location” signal.
*
* Since: 3.18
*/
void
nautilus_gtk_places_view_set_open_flags (NautilusGtkPlacesView *view,
- GtkPlacesOpenFlags flags)
+ NautilusGtkPlacesOpenFlags flags)
{
NautilusGtkPlacesViewPrivate *priv;
@@ -2471,15 +2471,15 @@ nautilus_gtk_places_view_set_open_flags (NautilusGtkPlacesView *view,
/**
* nautilus_gtk_places_view_get_open_flags:
- * @view: a #GtkPlacesSidebar
+ * @view: a #NautilusGtkPlacesSidebar
*
* Gets the open flags.
*
- * Returns: the #GtkPlacesOpenFlags of @view
+ * Returns: the #NautilusGtkPlacesOpenFlags of @view
*
* Since: 3.18
*/
-GtkPlacesOpenFlags
+NautilusGtkPlacesOpenFlags
nautilus_gtk_places_view_get_open_flags (NautilusGtkPlacesView *view)
{
NautilusGtkPlacesViewPrivate *priv;
diff --git a/src/gtk/nautilusgtkplacesviewprivate.h b/src/gtk/nautilusgtkplacesviewprivate.h
index 92f1dd92f..b821476cf 100644
--- a/src/gtk/nautilusgtkplacesviewprivate.h
+++ b/src/gtk/nautilusgtkplacesviewprivate.h
@@ -22,6 +22,7 @@
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#endif
+#include "nautilusgtkplacessidebar.h"
G_BEGIN_DECLS
@@ -42,9 +43,9 @@ struct _NautilusGtkPlacesViewClass
void (* open_location) (NautilusGtkPlacesView *view,
GFile *location,
- GtkPlacesOpenFlags open_flags);
+ NautilusGtkPlacesOpenFlags open_flags);
- void (* show_error_message) (GtkPlacesSidebar *sidebar,
+ void (* show_error_message) (NautilusGtkPlacesSidebar *sidebar,
const gchar *primary,
const gchar *secondary);
@@ -61,9 +62,9 @@ struct _NautilusGtkPlacesView
GType nautilus_gtk_places_view_get_type (void) G_GNUC_CONST;
-GtkPlacesOpenFlags nautilus_gtk_places_view_get_open_flags (NautilusGtkPlacesView *view);
+NautilusGtkPlacesOpenFlags nautilus_gtk_places_view_get_open_flags (NautilusGtkPlacesView
*view);
void nautilus_gtk_places_view_set_open_flags (NautilusGtkPlacesView *view,
- GtkPlacesOpenFlags flags);
+ NautilusGtkPlacesOpenFlags flags);
const gchar* nautilus_gtk_places_view_get_search_query (NautilusGtkPlacesView *view);
void nautilus_gtk_places_view_set_search_query (NautilusGtkPlacesView *view,
diff --git a/src/gtk/nautilusgtkplacesviewrow.c b/src/gtk/nautilusgtkplacesviewrow.c
index 121c5823b..f07dd4e70 100644
--- a/src/gtk/nautilusgtkplacesviewrow.c
+++ b/src/gtk/nautilusgtkplacesviewrow.c
@@ -29,14 +29,6 @@
* instead of including gtk.h
*/
#ifdef GTK_COMPILATION
-#include "gtkbutton.h"
-#include "gtkeventbox.h"
-#include "gtkimage.h"
-#include "gtkintl.h"
-#include "gtklabel.h"
-#include "gtkspinner.h"
-#include "gtkstack.h"
-#include "gtktypebuiltins.h"
#else
#include <gtk/gtk.h>
#endif
diff --git a/src/gtk/nautilusgtksidebarrow.c b/src/gtk/nautilusgtksidebarrow.c
new file mode 100644
index 000000000..7cc24b09f
--- /dev/null
+++ b/src/gtk/nautilusgtksidebarrow.c
@@ -0,0 +1,667 @@
+/* nautilusgtksidebarrow.c
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "nautilusgtksidebarrowprivate.h"
+/* For section and place type enums */
+#include "nautilusgtkplacessidebarprivate.h"
+#include "nautilusgtkplacessidebar.h"
+
+#ifdef HAVE_CLOUDPROVIDERS
+#include <cloudproviders/cloudprovidersaccount.h>
+#endif
+
+struct _NautilusGtkSidebarRow
+{
+ GtkListBoxRow parent_instance;
+ GIcon *start_icon;
+ GIcon *end_icon;
+ GtkWidget *start_icon_widget;
+ GtkWidget *end_icon_widget;
+ gchar *label;
+ gchar *tooltip;
+ GtkWidget *label_widget;
+ gboolean ejectable;
+ GtkWidget *eject_button;
+ gint order_index;
+ NautilusGtkPlacesSidebarSectionType section_type;
+ NautilusGtkPlacesSidebarPlaceType place_type;
+ gchar *uri;
+ GDrive *drive;
+ GVolume *volume;
+ GMount *mount;
+ GObject *cloud_provider_account;
+ gboolean placeholder;
+ NautilusGtkPlacesSidebar *sidebar;
+ GtkWidget *event_box;
+ GtkWidget *revealer;
+ GtkWidget *busy_spinner;
+};
+
+G_DEFINE_TYPE (NautilusGtkSidebarRow, nautilus_gtk_sidebar_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum
+{
+ PROP_0,
+ PROP_START_ICON,
+ PROP_END_ICON,
+ PROP_LABEL,
+ PROP_TOOLTIP,
+ PROP_EJECTABLE,
+ PROP_SIDEBAR,
+ PROP_ORDER_INDEX,
+ PROP_SECTION_TYPE,
+ PROP_PLACE_TYPE,
+ PROP_URI,
+ PROP_DRIVE,
+ PROP_VOLUME,
+ PROP_MOUNT,
+ PROP_CLOUD_PROVIDER_ACCOUNT,
+ PROP_PLACEHOLDER,
+ LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+#ifdef HAVE_CLOUDPROVIDERS
+
+static void
+cloud_row_update (NautilusGtkSidebarRow *self)
+{
+ CloudProvidersAccount *account;
+ GIcon *end_icon;
+ gint provider_status;
+
+ account = CLOUD_PROVIDERS_ACCOUNT (self->cloud_provider_account);
+ provider_status = cloud_providers_account_get_status (account);
+ switch (provider_status)
+ {
+ case CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE:
+ end_icon = NULL;
+ break;
+
+ case CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING:
+ end_icon = g_themed_icon_new ("emblem-synchronizing-symbolic");
+ break;
+
+ case CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR:
+ end_icon = g_themed_icon_new ("dialog-warning-symbolic");
+ break;
+
+ default:
+ return;
+ }
+
+ g_object_set (self,
+ "label", cloud_providers_account_get_name (account),
+ NULL);
+ g_object_set (self,
+ "tooltip", cloud_providers_account_get_status_details (account),
+ NULL);
+ g_object_set (self,
+ "end-icon", end_icon,
+ NULL);
+
+ if (end_icon != NULL)
+ g_object_unref (end_icon);
+}
+
+#endif
+
+static void
+nautilus_gtk_sidebar_row_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusGtkSidebarRow *self = NAUTILUS_GTK_SIDEBAR_ROW (object);
+
+ switch (prop_id)
+ {
+ case PROP_SIDEBAR:
+ g_value_set_object (value, self->sidebar);
+ break;
+
+ case PROP_START_ICON:
+ g_value_set_object (value, self->start_icon);
+ break;
+
+ case PROP_END_ICON:
+ g_value_set_object (value, self->end_icon);
+ break;
+
+ case PROP_LABEL:
+ g_value_set_string (value, self->label);
+ break;
+
+ case PROP_TOOLTIP:
+ g_value_set_string (value, self->tooltip);
+ break;
+
+ case PROP_EJECTABLE:
+ g_value_set_boolean (value, self->ejectable);
+ break;
+
+ case PROP_ORDER_INDEX:
+ g_value_set_int (value, self->order_index);
+ break;
+
+ case PROP_SECTION_TYPE:
+ g_value_set_int (value, self->section_type);
+ break;
+
+ case PROP_PLACE_TYPE:
+ g_value_set_int (value, self->place_type);
+ break;
+
+ case PROP_URI:
+ g_value_set_string (value, self->uri);
+ break;
+
+ case PROP_DRIVE:
+ g_value_set_object (value, self->drive);
+ break;
+
+ case PROP_VOLUME:
+ g_value_set_object (value, self->volume);
+ break;
+
+ case PROP_MOUNT:
+ g_value_set_object (value, self->mount);
+ break;
+
+ case PROP_CLOUD_PROVIDER_ACCOUNT:
+ g_value_set_object (value, self->cloud_provider_account);
+ break;
+
+ case PROP_PLACEHOLDER:
+ g_value_set_boolean (value, self->placeholder);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+nautilus_gtk_sidebar_row_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusGtkSidebarRow *self = NAUTILUS_GTK_SIDEBAR_ROW (object);
+ GtkStyleContext *context;
+
+ switch (prop_id)
+ {
+ case PROP_SIDEBAR:
+ self->sidebar = g_value_get_object (value);
+ break;
+
+ case PROP_START_ICON:
+ {
+ g_clear_object (&self->start_icon);
+ object = g_value_get_object (value);
+ if (object != NULL)
+ {
+ self->start_icon = G_ICON (g_object_ref (object));
+ gtk_image_set_from_gicon (GTK_IMAGE (self->start_icon_widget),
+ self->start_icon,
+ GTK_ICON_SIZE_MENU);
+ }
+ else
+ {
+ gtk_image_clear (GTK_IMAGE (self->start_icon_widget));
+ }
+ break;
+ }
+
+ case PROP_END_ICON:
+ {
+ g_clear_object (&self->end_icon);
+ object = g_value_get_object (value);
+ if (object != NULL)
+ {
+ self->end_icon = G_ICON (g_object_ref (object));
+ gtk_image_set_from_gicon (GTK_IMAGE (self->end_icon_widget),
+ self->end_icon,
+ GTK_ICON_SIZE_MENU);
+ gtk_widget_show (self->end_icon_widget);
+ }
+ else
+ {
+ gtk_image_clear (GTK_IMAGE (self->end_icon_widget));
+ gtk_widget_hide (self->end_icon_widget);
+ }
+ break;
+ }
+
+ case PROP_LABEL:
+ g_free (self->label);
+ self->label = g_strdup (g_value_get_string (value));
+ gtk_label_set_text (GTK_LABEL (self->label_widget), self->label);
+ break;
+
+ case PROP_TOOLTIP:
+ g_free (self->tooltip);
+ self->tooltip = g_strdup (g_value_get_string (value));
+ gtk_widget_set_tooltip_text (GTK_WIDGET (self), self->tooltip);
+ break;
+
+ case PROP_EJECTABLE:
+ self->ejectable = g_value_get_boolean (value);
+ if (self->ejectable)
+ gtk_widget_show (self->eject_button);
+ else
+ gtk_widget_hide (self->eject_button);
+ break;
+
+ case PROP_ORDER_INDEX:
+ self->order_index = g_value_get_int (value);
+ break;
+
+ case PROP_SECTION_TYPE:
+ self->section_type = g_value_get_int (value);
+ if (self->section_type == SECTION_COMPUTER ||
+ self->section_type == SECTION_OTHER_LOCATIONS)
+ gtk_label_set_ellipsize (GTK_LABEL (self->label_widget), PANGO_ELLIPSIZE_NONE);
+ else
+ gtk_label_set_ellipsize (GTK_LABEL (self->label_widget), PANGO_ELLIPSIZE_END);
+ break;
+
+ case PROP_PLACE_TYPE:
+ self->place_type = g_value_get_int (value);
+ break;
+
+ case PROP_URI:
+ g_free (self->uri);
+ self->uri = g_strdup (g_value_get_string (value));
+ break;
+
+ case PROP_DRIVE:
+ g_set_object (&self->drive, g_value_get_object (value));
+ break;
+
+ case PROP_VOLUME:
+ g_set_object (&self->volume, g_value_get_object (value));
+ break;
+
+ case PROP_MOUNT:
+ g_set_object (&self->mount, g_value_get_object (value));
+ break;
+
+ case PROP_CLOUD_PROVIDER_ACCOUNT:
+#ifdef HAVE_CLOUDPROVIDERS
+ if (self->cloud_provider_account != NULL)
+ g_signal_handlers_disconnect_by_data (self->cloud_provider_account, self);
+
+ self->cloud_provider_account = g_value_dup_object (value);
+
+ if (self->cloud_provider_account != NULL)
+ {
+ g_signal_connect_swapped (self->cloud_provider_account, "notify::name",
+ G_CALLBACK (cloud_row_update), self);
+ g_signal_connect_swapped (self->cloud_provider_account, "notify::status",
+ G_CALLBACK (cloud_row_update), self);
+ g_signal_connect_swapped (self->cloud_provider_account, "notify::status-details",
+ G_CALLBACK (cloud_row_update), self);
+ }
+#endif
+ break;
+
+ case PROP_PLACEHOLDER:
+ {
+ self->placeholder = g_value_get_boolean (value);
+ if (self->placeholder)
+ {
+ g_clear_object (&self->start_icon);
+ g_clear_object (&self->end_icon);
+ g_free (self->label);
+ self->label = NULL;
+ g_free (self->tooltip);
+ self->tooltip = NULL;
+ gtk_widget_set_tooltip_text (GTK_WIDGET (self), NULL);
+ self->ejectable = FALSE;
+ self->section_type = SECTION_BOOKMARKS;
+ self->place_type = PLACES_BOOKMARK_PLACEHOLDER;
+ g_free (self->uri);
+ self->uri = NULL;
+ g_clear_object (&self->drive);
+ g_clear_object (&self->volume);
+ g_clear_object (&self->mount);
+ g_clear_object (&self->cloud_provider_account);
+
+ gtk_container_foreach (GTK_CONTAINER (self),
+ (GtkCallback) gtk_widget_destroy,
+ NULL);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (self));
+ gtk_style_context_add_class (context, "sidebar-placeholder-row");
+ }
+
+ break;
+ }
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+on_child_revealed (GObject *self,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ /* We need to hide the actual widget because if not the GtkListBoxRow will
+ * still allocate the paddings, even if the revealer is not revealed, and
+ * therefore the row will be still somewhat visible. */
+ if (!gtk_revealer_get_reveal_child (GTK_REVEALER (self)))
+ gtk_widget_hide (GTK_WIDGET (NAUTILUS_GTK_SIDEBAR_ROW (user_data)));
+}
+
+void
+nautilus_gtk_sidebar_row_reveal (NautilusGtkSidebarRow *self)
+{
+ gtk_widget_show_all (GTK_WIDGET (self));
+ gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), TRUE);
+}
+
+void
+nautilus_gtk_sidebar_row_hide (NautilusGtkSidebarRow *self,
+ gboolean inmediate)
+{
+ guint transition_duration;
+
+ transition_duration = gtk_revealer_get_transition_duration (GTK_REVEALER (self->revealer));
+ if (inmediate)
+ gtk_revealer_set_transition_duration (GTK_REVEALER (self->revealer), 0);
+
+ gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), FALSE);
+
+ gtk_revealer_set_transition_duration (GTK_REVEALER (self->revealer), transition_duration);
+}
+
+void
+nautilus_gtk_sidebar_row_set_start_icon (NautilusGtkSidebarRow *self,
+ GIcon *icon)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_SIDEBAR_ROW (self));
+
+ if (self->start_icon != icon)
+ {
+ g_set_object (&self->start_icon, icon);
+ if (self->start_icon != NULL)
+ gtk_image_set_from_gicon (GTK_IMAGE (self->start_icon_widget), self->start_icon,
+ GTK_ICON_SIZE_MENU);
+ else
+ gtk_image_clear (GTK_IMAGE (self->start_icon_widget));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_START_ICON]);
+ }
+}
+
+void
+nautilus_gtk_sidebar_row_set_end_icon (NautilusGtkSidebarRow *self,
+ GIcon *icon)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_SIDEBAR_ROW (self));
+
+ if (self->end_icon != icon)
+ {
+ g_set_object (&self->end_icon, icon);
+ if (self->end_icon != NULL)
+ gtk_image_set_from_gicon (GTK_IMAGE (self->end_icon_widget), self->end_icon,
+ GTK_ICON_SIZE_MENU);
+ else
+ if (self->end_icon_widget != NULL)
+ gtk_image_clear (GTK_IMAGE (self->end_icon_widget));
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_END_ICON]);
+ }
+}
+
+static void
+nautilus_gtk_sidebar_row_finalize (GObject *object)
+{
+ NautilusGtkSidebarRow *self = NAUTILUS_GTK_SIDEBAR_ROW (object);
+
+ g_clear_object (&self->start_icon);
+ g_clear_object (&self->end_icon);
+ g_free (self->label);
+ self->label = NULL;
+ g_free (self->tooltip);
+ self->tooltip = NULL;
+ g_free (self->uri);
+ self->uri = NULL;
+ g_clear_object (&self->drive);
+ g_clear_object (&self->volume);
+ g_clear_object (&self->mount);
+#ifdef HAVE_CLOUDPROVIDERS
+ if (self->cloud_provider_account != NULL)
+ g_signal_handlers_disconnect_by_data (self->cloud_provider_account, self);
+ g_clear_object (&self->cloud_provider_account);
+#endif
+
+ G_OBJECT_CLASS (nautilus_gtk_sidebar_row_parent_class)->finalize (object);
+}
+
+static void
+nautilus_gtk_sidebar_row_init (NautilusGtkSidebarRow *self)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+static void
+nautilus_gtk_sidebar_row_class_init (NautilusGtkSidebarRowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->get_property = nautilus_gtk_sidebar_row_get_property;
+ object_class->set_property = nautilus_gtk_sidebar_row_set_property;
+ object_class->finalize = nautilus_gtk_sidebar_row_finalize;
+
+ properties [PROP_SIDEBAR] =
+ g_param_spec_object ("sidebar",
+ "Sidebar",
+ "Sidebar",
+ NAUTILUS_TYPE_GTK_PLACES_SIDEBAR,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_START_ICON] =
+ g_param_spec_object ("start-icon",
+ "start-icon",
+ "The start icon.",
+ G_TYPE_ICON,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_END_ICON] =
+ g_param_spec_object ("end-icon",
+ "end-icon",
+ "The end icon.",
+ G_TYPE_ICON,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_LABEL] =
+ g_param_spec_string ("label",
+ "label",
+ "The label text.",
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_TOOLTIP] =
+ g_param_spec_string ("tooltip",
+ "Tooltip",
+ "Tooltip",
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_EJECTABLE] =
+ g_param_spec_boolean ("ejectable",
+ "Ejectable",
+ "Ejectable",
+ FALSE,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_ORDER_INDEX] =
+ g_param_spec_int ("order-index",
+ "OrderIndex",
+ "Order Index",
+ 0, G_MAXINT, 0,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_SECTION_TYPE] =
+ g_param_spec_int ("section-type",
+ "section type",
+ "The section type.",
+ SECTION_INVALID, N_SECTIONS, SECTION_INVALID,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ properties [PROP_PLACE_TYPE] =
+ g_param_spec_int ("place-type",
+ "place type",
+ "The place type.",
+ PLACES_INVALID, N_PLACES, PLACES_INVALID,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ properties [PROP_URI] =
+ g_param_spec_string ("uri",
+ "Uri",
+ "Uri",
+ NULL,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_DRIVE] =
+ g_param_spec_object ("drive",
+ "Drive",
+ "Drive",
+ G_TYPE_DRIVE,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_VOLUME] =
+ g_param_spec_object ("volume",
+ "Volume",
+ "Volume",
+ G_TYPE_VOLUME,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_MOUNT] =
+ g_param_spec_object ("mount",
+ "Mount",
+ "Mount",
+ G_TYPE_MOUNT,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_CLOUD_PROVIDER_ACCOUNT] =
+ g_param_spec_object ("cloud-provider-account",
+ "CloudProvidersAccount",
+ "CloudProvidersAccount",
+ G_TYPE_OBJECT,
+ (G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ properties [PROP_PLACEHOLDER] =
+ g_param_spec_boolean ("placeholder",
+ "Placeholder",
+ "Placeholder",
+ FALSE,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gnome/nautilus/gtk/ui/nautilusgtksidebarrow.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, NautilusGtkSidebarRow, start_icon_widget);
+ gtk_widget_class_bind_template_child (widget_class, NautilusGtkSidebarRow, end_icon_widget);
+ gtk_widget_class_bind_template_child (widget_class, NautilusGtkSidebarRow, label_widget);
+ gtk_widget_class_bind_template_child (widget_class, NautilusGtkSidebarRow, eject_button);
+ gtk_widget_class_bind_template_child (widget_class, NautilusGtkSidebarRow, event_box);
+ gtk_widget_class_bind_template_child (widget_class, NautilusGtkSidebarRow, revealer);
+ gtk_widget_class_bind_template_child (widget_class, NautilusGtkSidebarRow, busy_spinner);
+
+ gtk_widget_class_bind_template_callback (widget_class, on_child_revealed);
+ gtk_widget_class_set_css_name (widget_class, "row");
+}
+
+NautilusGtkSidebarRow*
+nautilus_gtk_sidebar_row_clone (NautilusGtkSidebarRow *self)
+{
+ return g_object_new (NAUTILUS_TYPE_GTK_SIDEBAR_ROW,
+ "sidebar", self->sidebar,
+ "start-icon", self->start_icon,
+ "end-icon", self->end_icon,
+ "label", self->label,
+ "tooltip", self->tooltip,
+ "ejectable", self->ejectable,
+ "order-index", self->order_index,
+ "section-type", self->section_type,
+ "place-type", self->place_type,
+ "uri", self->uri,
+ "drive", self->drive,
+ "volume", self->volume,
+ "mount", self->mount,
+ "cloud-provider-account", self->cloud_provider_account,
+ NULL);
+}
+
+GtkWidget*
+nautilus_gtk_sidebar_row_get_eject_button (NautilusGtkSidebarRow *self)
+{
+ return self->eject_button;
+}
+
+GtkWidget*
+nautilus_gtk_sidebar_row_get_event_box (NautilusGtkSidebarRow *self)
+{
+ return self->event_box;
+}
+
+void
+nautilus_gtk_sidebar_row_set_busy (NautilusGtkSidebarRow *row,
+ gboolean is_busy)
+{
+ g_return_if_fail (NAUTILUS_IS_GTK_SIDEBAR_ROW (row));
+
+ gtk_widget_set_visible (row->busy_spinner, is_busy);
+}
diff --git a/src/gtk/nautilusgtksidebarrow.ui b/src/gtk/nautilusgtksidebarrow.ui
new file mode 100644
index 000000000..26265fcb3
--- /dev/null
+++ b/src/gtk/nautilusgtksidebarrow.ui
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk30">
+ <template class="NautilusGtkSidebarRow" parent="GtkListBoxRow">
+ <property name="visible">True</property>
+ <property name="margin-top">1</property>
+ <property name="margin-bottom">1</property>
+ <property name="focus-on-click">False</property>
+ <style>
+ <class name="sidebar-row"/>
+ </style>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="visible">1</property>
+ <property name="reveal-child">1</property>
+ <signal name="notify::child-revealed" handler="on_child_revealed"/>
+ <style>
+ <class name="sidebar-revealer"/>
+ </style>
+ <child>
+ <object class="GtkEventBox" id="event_box">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">1</property>
+ <child>
+ <object class="GtkImage" id="start_icon_widget">
+ <property name="visible">True</property>
+ <style>
+ <class name="sidebar-icon"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_widget">
+ <property name="visible">1</property>
+ <property name="hexpand">1</property>
+ <property name="xalign">0</property>
+ <style>
+ <class name="sidebar-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage" id="end_icon_widget">
+ <property name="visible">False</property>
+ <property name="hexpand">True</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <property name="no-show-all">1</property>
+ <style>
+ <class name="sidebar-icon"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="eject_button">
+ <property name="visible">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="margin-start">4px</property>
+ <property name="no-show-all">1</property>
+ <property name="tooltip-text" translatable="yes">Unmount</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">1</property>
+ <property name="icon-name">media-eject-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ <style>
+ <class name="image-button"/>
+ <class name="sidebar-button"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="busy_spinner">
+ <property name="active">1</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="margin-start">4px</property>
+ <property name="no-show-all">1</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/gtk/nautilusgtksidebarrowprivate.h b/src/gtk/nautilusgtksidebarrowprivate.h
new file mode 100644
index 000000000..437384954
--- /dev/null
+++ b/src/gtk/nautilusgtksidebarrowprivate.h
@@ -0,0 +1,61 @@
+/* nautilusgtksidebarrowprivate.h
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef NAUTILUS_GTK_SIDEBAR_ROW_PRIVATE_H
+#define NAUTILUS_GTK_SIDEBAR_ROW_PRIVATE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_GTK_SIDEBAR_ROW (nautilus_gtk_sidebar_row_get_type())
+#define NAUTILUS_GTK_SIDEBAR_ROW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
NAUTILUS_TYPE_GTK_SIDEBAR_ROW, NautilusGtkSidebarRow))
+#define NAUTILUS_GTK_SIDEBAR_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
NAUTILUS_TYPE_GTK_SIDEBAR_ROW, NautilusGtkSidebarRowClass))
+#define NAUTILUS_IS_GTK_SIDEBAR_ROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
NAUTILUS_TYPE_GTK_SIDEBAR_ROW))
+#define NAUTILUS_IS_GTK_SIDEBAR_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
NAUTILUS_TYPE_GTK_SIDEBAR_ROW))
+#define NAUTILUS_GTK_SIDEBAR_ROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
NAUTILUS_TYPE_GTK_SIDEBAR_ROW, NautilusGtkSidebarRowClass))
+
+typedef struct _NautilusGtkSidebarRow NautilusGtkSidebarRow;
+typedef struct _NautilusGtkSidebarRowClass NautilusGtkSidebarRowClass;
+
+struct _NautilusGtkSidebarRowClass
+{
+ GtkListBoxRowClass parent;
+};
+
+GType nautilus_gtk_sidebar_row_get_type (void) G_GNUC_CONST;
+
+NautilusGtkSidebarRow *nautilus_gtk_sidebar_row_new (void);
+NautilusGtkSidebarRow *nautilus_gtk_sidebar_row_clone (NautilusGtkSidebarRow *self);
+
+/* Use these methods instead of gtk_widget_hide/show to use an animation */
+void nautilus_gtk_sidebar_row_hide (NautilusGtkSidebarRow *self,
+ gboolean inmediate);
+void nautilus_gtk_sidebar_row_reveal (NautilusGtkSidebarRow *self);
+
+GtkWidget *nautilus_gtk_sidebar_row_get_eject_button (NautilusGtkSidebarRow *self);
+GtkWidget *nautilus_gtk_sidebar_row_get_event_box (NautilusGtkSidebarRow *self);
+void nautilus_gtk_sidebar_row_set_start_icon (NautilusGtkSidebarRow *self,
+ GIcon *icon);
+void nautilus_gtk_sidebar_row_set_end_icon (NautilusGtkSidebarRow *self,
+ GIcon *icon);
+void nautilus_gtk_sidebar_row_set_busy (NautilusGtkSidebarRow *row,
+ gboolean is_busy);
+
+G_END_DECLS
+
+#endif /* NAUTILUS_GTK_SIDEBAR_ROW_PRIVATE_H */
diff --git a/src/meson.build b/src/meson.build
index 682d6f3a3..fd7d0563a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -61,6 +61,13 @@ libnautilus_sources = [
'animation/ide-box-theatric.h',
'animation/ide-cairo.c',
'animation/ide-cairo.h',
+ 'gtk/nautilusgtkbookmarksmanager.c',
+ 'gtk/nautilusgtkbookmarksmanager.h',
+ 'gtk/nautilusgtkplacessidebar.c',
+ 'gtk/nautilusgtkplacessidebarprivate.h',
+ 'gtk/nautilusgtkplacessidebar.h',
+ 'gtk/nautilusgtksidebarrow.c',
+ 'gtk/nautilusgtksidebarrowprivate.h',
'gtk/nautilusgtkplacesview.c',
'gtk/nautilusgtkplacesviewprivate.h',
'gtk/nautilusgtkplacesviewrow.c',
diff --git a/src/nautilus-places-view.c b/src/nautilus-places-view.c
index a167ce02c..69729d61f 100644
--- a/src/nautilus-places-view.c
+++ b/src/nautilus-places-view.c
@@ -62,9 +62,9 @@ enum
};
static void
-open_location_cb (NautilusPlacesView *view,
- GFile *location,
- GtkPlacesOpenFlags open_flags)
+open_location_cb (NautilusPlacesView *view,
+ GFile *location,
+ NautilusGtkPlacesOpenFlags open_flags)
{
NautilusWindowOpenFlags flags;
GtkWidget *slot;
diff --git a/src/nautilus-window.c b/src/nautilus-window.c
index 940cbeeb4..689707ed1 100644
--- a/src/nautilus-window.c
+++ b/src/nautilus-window.c
@@ -47,6 +47,8 @@
#define DEBUG_FLAG NAUTILUS_DEBUG_WINDOW
#include "nautilus-debug.h"
+#include "gtk/nautilusgtkplacessidebar.h"
+
#include "nautilus-application.h"
#include "nautilus-bookmark-list.h"
#include "nautilus-clipboard.h"
@@ -448,8 +450,8 @@ action_toggle_state_view_button (GSimpleAction *action,
static void
on_location_changed (NautilusWindow *window)
{
- gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (window->places_sidebar),
- nautilus_window_slot_get_location (nautilus_window_get_active_slot
(window)));
+ nautilus_gtk_places_sidebar_set_location (NAUTILUS_GTK_PLACES_SIDEBAR (window->places_sidebar),
+ nautilus_window_slot_get_location
(nautilus_window_get_active_slot (window)));
}
static void
@@ -848,29 +850,29 @@ setup_side_pane_width (NautilusWindow *window)
/* Callback used when the places sidebar changes location; we need to change the displayed folder */
static void
-open_location_cb (NautilusWindow *window,
- GFile *location,
- GtkPlacesOpenFlags open_flags)
+open_location_cb (NautilusWindow *window,
+ GFile *location,
+ NautilusGtkPlacesOpenFlags open_flags)
{
NautilusWindowOpenFlags flags;
NautilusApplication *application;
switch (open_flags)
{
- case GTK_PLACES_OPEN_NEW_TAB:
+ case NAUTILUS_GTK_PLACES_OPEN_NEW_TAB:
{
flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB |
NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE;
}
break;
- case GTK_PLACES_OPEN_NEW_WINDOW:
+ case NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW:
{
flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW;
}
break;
- case GTK_PLACES_OPEN_NORMAL: /* fall-through */
+ case NAUTILUS_GTK_PLACES_OPEN_NORMAL: /* fall-through */
default:
{
flags = 0;
@@ -898,10 +900,10 @@ places_sidebar_unmount_operation_cb (NautilusWindow *window,
/* Callback used when the places sidebar needs us to present an error message */
static void
-places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar,
- const char *primary,
- const char *secondary,
- gpointer user_data)
+places_sidebar_show_error_message_cb (NautilusGtkPlacesSidebar *sidebar,
+ const char *primary,
+ const char *secondary,
+ gpointer user_data)
{
NautilusWindow *window = NAUTILUS_WINDOW (user_data);
@@ -909,8 +911,8 @@ places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar,
}
static void
-places_sidebar_show_other_locations_with_flags (NautilusWindow *window,
- GtkPlacesOpenFlags open_flags)
+places_sidebar_show_other_locations_with_flags (NautilusWindow *window,
+ NautilusGtkPlacesOpenFlags open_flags)
{
GFile *location;
@@ -922,8 +924,8 @@ places_sidebar_show_other_locations_with_flags (NautilusWindow *window,
}
static void
-places_sidebar_show_starred_location (NautilusWindow *window,
- GtkPlacesOpenFlags open_flags)
+places_sidebar_show_starred_location (NautilusWindow *window,
+ NautilusGtkPlacesOpenFlags open_flags)
{
GFile *location;
@@ -964,9 +966,9 @@ nautilus_window_start_dnd (NautilusWindow *window,
{
g_return_if_fail (NAUTILUS_IS_WINDOW (window));
- gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (window->places_sidebar),
- TRUE,
- context);
+ nautilus_gtk_places_sidebar_set_drop_targets_visible (NAUTILUS_GTK_PLACES_SIDEBAR
(window->places_sidebar),
+ TRUE,
+ context);
}
void
@@ -975,18 +977,18 @@ nautilus_window_end_dnd (NautilusWindow *window,
{
g_return_if_fail (NAUTILUS_IS_WINDOW (window));
- gtk_places_sidebar_set_drop_targets_visible (GTK_PLACES_SIDEBAR (window->places_sidebar),
- FALSE,
- context);
+ nautilus_gtk_places_sidebar_set_drop_targets_visible (NAUTILUS_GTK_PLACES_SIDEBAR
(window->places_sidebar),
+ FALSE,
+ context);
}
/* Callback used when the places sidebar needs to know the drag action to suggest */
static GdkDragAction
-places_sidebar_drag_action_requested_cb (GtkPlacesSidebar *sidebar,
- GdkDragContext *context,
- GFile *dest_file,
- GList *source_file_list,
- gpointer user_data)
+places_sidebar_drag_action_requested_cb (NautilusGtkPlacesSidebar *sidebar,
+ GdkDragContext *context,
+ GFile *dest_file,
+ GList *source_file_list,
+ gpointer user_data)
{
GList *items;
char *uri;
@@ -1027,9 +1029,9 @@ out:
/* Callback used when the places sidebar needs us to pop up a menu with possible drag actions */
static GdkDragAction
-places_sidebar_drag_action_ask_cb (GtkPlacesSidebar *sidebar,
- GdkDragAction actions,
- gpointer user_data)
+places_sidebar_drag_action_ask_cb (NautilusGtkPlacesSidebar *sidebar,
+ GdkDragAction actions,
+ gpointer user_data)
{
return nautilus_drag_drop_action_ask (GTK_WIDGET (sidebar), actions);
}
@@ -1056,11 +1058,11 @@ build_uri_list_from_gfile_list (GList *file_list)
/* Callback used when the places sidebar has URIs dropped into it. We do a normal file operation for them.
*/
static void
-places_sidebar_drag_perform_drop_cb (GtkPlacesSidebar *sidebar,
- GFile *dest_file,
- GList *source_file_list,
- GdkDragAction action,
- gpointer user_data)
+places_sidebar_drag_perform_drop_cb (NautilusGtkPlacesSidebar *sidebar,
+ GFile *dest_file,
+ GList *source_file_list,
+ GdkDragAction action,
+ gpointer user_data)
{
char *dest_uri;
GList *source_uri_list;
@@ -1213,11 +1215,11 @@ add_menu_separator (GtkWidget *menu)
}
static void
-places_sidebar_populate_popup_cb (GtkPlacesSidebar *sidebar,
- GtkWidget *menu,
- GFile *selected_file,
- GVolume *selected_volume,
- gpointer user_data)
+places_sidebar_populate_popup_cb (NautilusGtkPlacesSidebar *sidebar,
+ GtkWidget *menu,
+ GFile *selected_file,
+ GVolume *selected_volume,
+ gpointer user_data)
{
NautilusWindow *window = NAUTILUS_WINDOW (user_data);
GFile *trash;
@@ -1294,10 +1296,10 @@ nautilus_window_set_up_sidebar (NautilusWindow *window)
G_CALLBACK (side_pane_size_allocate_callback),
window);
- gtk_places_sidebar_set_open_flags (GTK_PLACES_SIDEBAR (window->places_sidebar),
- (GTK_PLACES_OPEN_NORMAL
- | GTK_PLACES_OPEN_NEW_TAB
- | GTK_PLACES_OPEN_NEW_WINDOW));
+ nautilus_gtk_places_sidebar_set_open_flags (NAUTILUS_GTK_PLACES_SIDEBAR (window->places_sidebar),
+ (NAUTILUS_GTK_PLACES_OPEN_NORMAL
+ | NAUTILUS_GTK_PLACES_OPEN_NEW_TAB
+ | NAUTILUS_GTK_PLACES_OPEN_NEW_WINDOW));
g_signal_connect_swapped (window->places_sidebar, "open-location",
G_CALLBACK (open_location_cb), window);
@@ -2686,6 +2688,27 @@ nautilus_window_init (NautilusWindow *window)
g_type_ensure (NAUTILUS_TYPE_NOTEBOOK);
gtk_widget_init_template (GTK_WIDGET (window));
+ window->places_sidebar = nautilus_gtk_places_sidebar_new ();
+ g_object_set (window->places_sidebar,
+ "vexpand", TRUE,
+ "visible", TRUE,
+ "populate-all", TRUE,
+ "show-other-locations", TRUE,
+ "show-starred-location", TRUE,
+ NULL);
+ gtk_box_append (GTK_BOX (window->sidebar), window->places_sidebar);
+
+ g_signal_connect_object (window->places_sidebar,
+ "show-other-locations-with-flags",
+ G_CALLBACK (places_sidebar_show_other_locations_with_flags),
+ window,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (window->places_sidebar,
+ "show-starred-location",
+ G_CALLBACK (places_sidebar_show_starred_location),
+ window,
+ G_CONNECT_SWAPPED);
+
g_signal_connect (window, "notify::is-maximized",
G_CALLBACK (on_is_maximized_changed), NULL);
@@ -2750,7 +2773,6 @@ nautilus_window_class_init (NautilusWindowClass *class)
gtk_widget_class_bind_template_child (wclass, NautilusWindow, toolbar);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, content_paned);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, sidebar);
- gtk_widget_class_bind_template_child (wclass, NautilusWindow, places_sidebar);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, main_view);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, notebook);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, in_app_notification_undo);
@@ -2762,9 +2784,6 @@ nautilus_window_class_init (NautilusWindowClass *class)
gtk_widget_class_bind_template_child (wclass, NautilusWindow, notification_operation_open);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, notification_operation_close);
- gtk_widget_class_bind_template_callback (wclass, places_sidebar_show_other_locations_with_flags);
- gtk_widget_class_bind_template_callback (wclass, places_sidebar_show_starred_location);
-
signals[SLOT_ADDED] =
g_signal_new ("slot-added",
G_TYPE_FROM_CLASS (class),
diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml
index a0cd28876..57f5b81f2 100644
--- a/src/resources/nautilus.gresource.xml
+++ b/src/resources/nautilus.gresource.xml
@@ -26,6 +26,7 @@
<file>ui/nautilus-file-conflict-dialog.ui</file>
<file>ui/nautilus-files-view-select-items.ui</file>
<file>ui/nautilus-operations-ui-manager-request-passphrase.ui</file>
+ <file alias="gtk/ui/nautilusgtksidebarrow.ui">../gtk/nautilusgtksidebarrow.ui</file>
<file alias="gtk/ui/nautilusgtkplacesview.ui">../gtk/nautilusgtkplacesview.ui</file>
<file alias="gtk/ui/nautilusgtkplacesviewrow.ui">../gtk/nautilusgtkplacesviewrow.ui</file>
<file alias="icons/thumbnail_frame.png">../../icons/thumbnail_frame.png</file>
diff --git a/src/resources/ui/nautilus-window.ui b/src/resources/ui/nautilus-window.ui
index 853a693ca..407014ecc 100644
--- a/src/resources/ui/nautilus-window.ui
+++ b/src/resources/ui/nautilus-window.ui
@@ -20,17 +20,6 @@
<property name="visible">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
- <child>
- <object class="GtkPlacesSidebar" id="places_sidebar">
- <property name="vexpand">True</property>
- <property name="visible">True</property>
- <property name="populate-all">True</property>
- <property name="show-other-locations">True</property>
- <property name="show-starred-location">True</property>
- <signal name="show-other-locations-with-flags"
handler="places_sidebar_show_other_locations_with_flags" object="NautilusWindow" swapped="yes" />
- <signal name="show-starred-location" handler="places_sidebar_show_starred_location"
object="NautilusWindow" swapped="yes" />
- </object>
- </child>
</object>
<packing>
<property name="resize">False</property>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]