[nautilus/wip/gbsneto/files-view: 3/4] places-view: implement a view for Other Locations



commit bce0f35c2a82b6c34e8f1cc5794cf5a6be637d50
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Tue Aug 18 15:23:02 2015 -0300

    places-view: implement a view for Other Locations
    
    GtkFileChooser received a Other Locations view that lists
    persistent devices, as well as networks and the root location
    for the computer's hard drive.
    
    Since Nautilus is a file management tool too, it should keep
    consistency between Gtk+ file chooser, something that doesn't
    happen since it doesn't display Other Locations.
    
    To fix that, add NautilusPlacesView, a NautilusView implementation
    that displays the GtkPlacesView widget. In order to implement that,
    update window-slot to correctly display the places-view whenever
    Other Locations is clicked.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=753777

 libnautilus-private/nautilus-bookmark.c       |    4 +-
 libnautilus-private/nautilus-file-utilities.c |   14 +-
 libnautilus-private/nautilus-file.c           |   25 +
 libnautilus-private/nautilus-file.h           |    1 +
 src/Makefile.am                               |    6 +
 src/gtk/gtkplacesview.c                       | 2379 +++++++++++++++++++++++++
 src/gtk/gtkplacesview.ui                      |  332 ++++
 src/gtk/gtkplacesviewprivate.h                |   88 +
 src/gtk/gtkplacesviewrow.c                    |  326 ++++
 src/gtk/gtkplacesviewrow.ui                   |  109 ++
 src/gtk/gtkplacesviewrowprivate.h             |   53 +
 src/nautilus-application.c                    |    5 +-
 src/nautilus-pathbar.c                        |   23 +-
 src/nautilus-places-view.c                    |  323 ++++
 src/nautilus-places-view.h                    |   37 +
 src/nautilus-window-slot.c                    |  282 ++--
 src/nautilus-window.c                         |   15 +-
 src/nautilus-window.ui                        |    2 +
 src/nautilus.gresource.xml                    |    2 +
 19 files changed, 3902 insertions(+), 124 deletions(-)
---
diff --git a/libnautilus-private/nautilus-bookmark.c b/libnautilus-private/nautilus-bookmark.c
index f7bcda8..db9d01e 100644
--- a/libnautilus-private/nautilus-bookmark.c
+++ b/libnautilus-private/nautilus-bookmark.c
@@ -103,7 +103,9 @@ bookmark_set_name_from_ready_file (NautilusBookmark *self,
 
        display_name = nautilus_file_get_display_name (self->details->file);
 
-       if (nautilus_file_is_home (self->details->file)) {
+        if (nautilus_file_is_in_other_locations (self->details->file)) {
+                nautilus_bookmark_set_name_internal (self, _("Other Locations"));
+        } else if (nautilus_file_is_home (self->details->file)) {
                nautilus_bookmark_set_name_internal (self, _("Home"));
        } else if (g_strcmp0 (self->details->name, display_name) != 0) {
                nautilus_bookmark_set_name_internal (self, display_name);
diff --git a/libnautilus-private/nautilus-file-utilities.c b/libnautilus-private/nautilus-file-utilities.c
index 30d9305..7481648 100644
--- a/libnautilus-private/nautilus-file-utilities.c
+++ b/libnautilus-private/nautilus-file-utilities.c
@@ -268,10 +268,16 @@ nautilus_compute_title_for_location (GFile *location)
        title = NULL;
        if (location) {
                file = nautilus_file_get (location);
-               title = nautilus_file_get_description (file);
-               if (title == NULL) {
-                       title = nautilus_file_get_display_name (file);
-               }
+
+                if (nautilus_file_is_in_other_locations (file)) {
+                        title = g_strdup (_("Other Locations"));
+                } else {
+                        title = nautilus_file_get_description (file);
+
+                        if (title == NULL) {
+                                title = nautilus_file_get_display_name (file);
+                        }
+                }
                nautilus_file_unref (file);
        }
 
diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c
index e12e03f..2c8285c 100644
--- a/libnautilus-private/nautilus-file.c
+++ b/libnautilus-private/nautilus-file.c
@@ -7015,6 +7015,31 @@ nautilus_file_is_in_network (NautilusFile *file)
        return nautilus_directory_is_in_network (file->details->directory);
 }
 
+/**
+ * nautilus_file_is_in_other_locations
+ *
+ * Check if this file is a file in Other Locations.
+ * @file: NautilusFile representing the file in question.
+ *
+ * Returns: TRUE if @file is in Other Locations.
+ *
+ **/
+gboolean
+nautilus_file_is_in_other_locations (NautilusFile *file)
+{
+        gboolean is_other_locations;
+        gchar *uri;
+
+        g_assert (NAUTILUS_IS_FILE (file));
+
+        uri = nautilus_file_get_uri (file);
+        is_other_locations = g_str_has_prefix (uri, "other-locations://");
+
+        g_free (uri);
+
+        return is_other_locations;
+}
+
 GError *
 nautilus_file_get_file_info_error (NautilusFile *file)
 {
diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h
index 52e10d0..4f95f99 100644
--- a/libnautilus-private/nautilus-file.h
+++ b/libnautilus-private/nautilus-file.h
@@ -191,6 +191,7 @@ gboolean                nautilus_file_is_in_search                      (Nautilu
 gboolean                nautilus_file_is_in_trash                       (NautilusFile                   
*file);
 gboolean                nautilus_file_is_in_recent                      (NautilusFile                   
*file);
 gboolean                nautilus_file_is_in_network                     (NautilusFile                   
*file);
+gboolean                nautilus_file_is_in_other_locations             (NautilusFile                   
*file);
 gboolean               nautilus_file_is_home                           (NautilusFile                   
*file);
 gboolean                nautilus_file_is_desktop_directory              (NautilusFile                   
*file);
 GError *                nautilus_file_get_file_info_error               (NautilusFile                   
*file);
diff --git a/src/Makefile.am b/src/Makefile.am
index 2b01996..5697224 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -139,6 +139,10 @@ nautilus_built_sources = \
        $(NULL)
 
 nautilus_SOURCES = \
+       gtk/gtkplacesview.c                     \
+       gtk/gtkplacesviewprivate.h              \
+       gtk/gtkplacesviewrow.c                  \
+       gtk/gtkplacesviewrowprivate.h           \
        nautilus-application.c                  \
        nautilus-application.h                  \
        nautilus-application-actions.c          \
@@ -189,6 +193,8 @@ nautilus_SOURCES = \
        nautilus-notebook.h                     \
        nautilus-pathbar.c                      \
        nautilus-pathbar.h                      \
+       nautilus-places-view.c                  \
+       nautilus-places-view.h                  \
        nautilus-previewer.c                    \
        nautilus-previewer.h                    \
        nautilus-progress-info-widget.c         \
diff --git a/src/gtk/gtkplacesview.c b/src/gtk/gtkplacesview.c
new file mode 100644
index 0000000..55737b7
--- /dev/null
+++ b/src/gtk/gtkplacesview.c
@@ -0,0 +1,2379 @@
+/* gtkplacesview.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "gtkplacesviewprivate.h"
+#include "gtkplacesviewrowprivate.h"
+
+/**
+ * SECTION:gtkplacesview
+ * @Short_description: Widget that displays persistent drives and manages mounted networks
+ * @Title: GtkPlacesView
+ * @See_also: #GtkFileChooser
+ *
+ * #GtkPlacesView is a stock widget that displays a list of persistent drives
+ * such as harddisk partitions and networks.  #GtkPlacesView does not monitor
+ * removable devices.
+ *
+ * The places view displays drives and networks, and will automatically mount
+ * them when the user activates. Network addresses are stored even if they fail
+ * to connect. When the connection is successful, the connected network is
+ * shown at the network list.
+ *
+ * To make use of the places view, an application at least needs to connect
+ * to the #GtkPlacesView::open-location signal. This is emitted when the user
+ * selects a location to open in the view.
+ */
+
+struct _GtkPlacesViewPrivate
+{
+  GVolumeMonitor                *volume_monitor;
+  GtkPlacesOpenFlags             open_flags;
+  GtkPlacesOpenFlags             current_open_flags;
+
+  GFile                         *server_list_file;
+  GFileMonitor                  *server_list_monitor;
+
+  GCancellable                  *cancellable;
+
+  gchar                         *search_query;
+
+  GtkWidget                     *actionbar;
+  GtkWidget                     *address_entry;
+  GtkWidget                     *connect_button;
+  GtkWidget                     *listbox;
+  GtkWidget                     *popup_menu;
+  GtkWidget                     *recent_servers_listbox;
+  GtkWidget                     *recent_servers_popover;
+  GtkWidget                     *recent_servers_stack;
+  GtkWidget                     *stack;
+  GtkWidget                     *network_header_spinner;
+  GtkWidget                     *network_placeholder;
+  GtkWidget                     *network_placeholder_label;
+
+  GtkEntryCompletion            *address_entry_completion;
+  GtkListStore                  *completion_store;
+
+  GList                         *detected_networks;
+  GFileEnumerator               *detected_networks_enumerator;
+  GCancellable                  *networks_fetching_cancellable;
+
+  guint                          local_only : 1;
+  guint                          should_open_location : 1;
+  guint                          should_pulse_entry : 1;
+  guint                          entry_pulse_timeout_id;
+  guint                          connecting_to_server : 1;
+  guint                          fetching_networks : 1;
+};
+
+static void        mount_volume                                  (GtkPlacesView *view,
+                                                                  GVolume       *volume);
+
+static gboolean    on_button_press_event                         (GtkPlacesViewRow *row,
+                                                                  GdkEventButton   *event);
+
+static void        on_eject_button_clicked                       (GtkWidget        *widget,
+                                                                  GtkPlacesViewRow *row);
+
+static gboolean    on_row_popup_menu                             (GtkPlacesViewRow *row);
+
+static void        populate_servers                              (GtkPlacesView *view);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkPlacesView, gtk_places_view, GTK_TYPE_BOX)
+
+/* GtkPlacesView properties & signals */
+enum {
+  PROP_0,
+  PROP_LOCAL_ONLY,
+  PROP_OPEN_FLAGS,
+  LAST_PROP
+};
+
+enum {
+  OPEN_LOCATION,
+  SHOW_ERROR_MESSAGE,
+  LAST_SIGNAL
+};
+
+const gchar *unsupported_protocols [] =
+{
+  "file", "afc", "obex", "http",
+  "trash", "burn", "computer",
+  "archive", "recent", "localtest",
+  NULL
+};
+
+static guint places_view_signals [LAST_SIGNAL] = { 0 };
+static GParamSpec *properties [LAST_PROP];
+
+static void
+emit_open_location (GtkPlacesView      *view,
+                    GFile              *location,
+                    GtkPlacesOpenFlags  open_flags)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  if ((open_flags & priv->open_flags) == 0)
+    open_flags = GTK_PLACES_OPEN_NORMAL;
+
+  g_signal_emit (view, places_view_signals[OPEN_LOCATION], 0, location, open_flags);
+}
+
+static void
+emit_show_error_message (GtkPlacesView *view,
+                         gchar         *primary_message,
+                         gchar         *secondary_message)
+{
+  g_signal_emit (view, places_view_signals[SHOW_ERROR_MESSAGE],
+                         0, primary_message, secondary_message);
+}
+
+static void
+server_file_changed_cb (GtkPlacesView *view)
+{
+  populate_servers (view);
+}
+
+static GBookmarkFile *
+server_list_load (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+  GBookmarkFile *bookmarks;
+  GError *error = NULL;
+  gchar *datadir;
+  gchar *filename;
+
+  priv = gtk_places_view_get_instance_private (view);
+  bookmarks = g_bookmark_file_new ();
+  datadir = g_build_filename (g_get_user_config_dir (), "gtk-3.0", NULL);
+  filename = g_build_filename (datadir, "servers", NULL);
+
+  g_mkdir_with_parents (datadir, 0700);
+  g_bookmark_file_load_from_file (bookmarks, filename, &error);
+
+  if (error)
+    {
+      if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+        {
+          /* only warn if the file exists */
+          g_warning ("Unable to open server bookmarks: %s", error->message);
+          g_clear_pointer (&bookmarks, g_bookmark_file_free);
+        }
+
+      g_clear_error (&error);
+    }
+
+  /* Monitor the file in case it's modified outside this code */
+  if (!priv->server_list_monitor)
+    {
+      priv->server_list_file = g_file_new_for_path (filename);
+
+      if (priv->server_list_file)
+        {
+          priv->server_list_monitor = g_file_monitor_file (priv->server_list_file,
+                                                           G_FILE_MONITOR_NONE,
+                                                           NULL,
+                                                           &error);
+
+          if (error)
+            {
+              g_warning ("Cannot monitor server file: %s", error->message);
+              g_clear_error (&error);
+            }
+          else
+            {
+              g_signal_connect_swapped (priv->server_list_monitor,
+                                        "changed",
+                                        G_CALLBACK (server_file_changed_cb),
+                                        view);
+            }
+        }
+
+      g_clear_object (&priv->server_list_file);
+    }
+
+  g_free (datadir);
+  g_free (filename);
+
+  return bookmarks;
+}
+
+static void
+server_list_save (GBookmarkFile *bookmarks)
+{
+  gchar *filename;
+
+  filename = g_build_filename (g_get_user_config_dir (), "gtk-3.0", "servers", NULL);
+  g_bookmark_file_to_file (bookmarks, filename, NULL);
+  g_free (filename);
+}
+
+static void
+server_list_add_server (GtkPlacesView *view,
+                        GFile         *file)
+{
+  GBookmarkFile *bookmarks;
+  GFileInfo *info;
+  GError *error;
+  gchar *title;
+  gchar *uri;
+
+  error = NULL;
+  bookmarks = server_list_load (view);
+
+  if (!bookmarks)
+    return;
+
+  uri = g_file_get_uri (file);
+
+  info = g_file_query_info (file,
+                            G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+                            G_FILE_QUERY_INFO_NONE,
+                            NULL,
+                            &error);
+  title = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
+
+  g_bookmark_file_set_title (bookmarks, uri, title);
+  g_bookmark_file_set_visited (bookmarks, uri, -1);
+  g_bookmark_file_add_application (bookmarks, uri, NULL, NULL);
+
+  server_list_save (bookmarks);
+
+  g_bookmark_file_free (bookmarks);
+  g_clear_object (&info);
+  g_free (title);
+  g_free (uri);
+}
+
+static void
+server_list_remove_server (GtkPlacesView *view,
+                           const gchar   *uri)
+{
+  GBookmarkFile *bookmarks;
+
+  bookmarks = server_list_load (view);
+
+  if (!bookmarks)
+    return;
+
+  g_bookmark_file_remove_item (bookmarks, uri, NULL);
+  server_list_save (bookmarks);
+
+  g_bookmark_file_free (bookmarks);
+}
+
+/* Returns a toplevel GtkWindow, or NULL if none */
+static GtkWindow *
+get_toplevel (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (!gtk_widget_is_toplevel (toplevel))
+    return NULL;
+  else
+    return GTK_WINDOW (toplevel);
+}
+
+static void
+set_busy_cursor (GtkPlacesView *view,
+                 gboolean       busy)
+{
+  GtkWidget *widget;
+  GtkWindow *toplevel;
+  GdkDisplay *display;
+  GdkCursor *cursor;
+
+  toplevel = get_toplevel (GTK_WIDGET (view));
+  widget = GTK_WIDGET (toplevel);
+  if (!toplevel || !gtk_widget_get_realized (widget))
+    return;
+
+  display = gtk_widget_get_display (widget);
+
+  if (busy)
+    {
+      cursor = gdk_cursor_new_from_name (display, "left_ptr_watch");
+      if (cursor == NULL)
+        cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+    }
+  else
+    cursor = NULL;
+
+  gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+  gdk_display_flush (display);
+
+  if (cursor)
+    g_object_unref (cursor);
+}
+
+/* Activates the given row, with the given flags as parameter */
+static void
+activate_row (GtkPlacesView      *view,
+              GtkPlacesViewRow   *row,
+              GtkPlacesOpenFlags  flags)
+{
+  GtkPlacesViewPrivate *priv;
+  GVolume *volume;
+  GMount *mount;
+  GFile *file;
+
+  priv = gtk_places_view_get_instance_private (view);
+  mount = gtk_places_view_row_get_mount (row);
+  volume = gtk_places_view_row_get_volume (row);
+  file = gtk_places_view_row_get_file (row);
+
+  if (file)
+    {
+      emit_open_location (view, file, flags);
+    }
+  else if (mount)
+    {
+      GFile *location = g_mount_get_default_location (mount);
+
+      emit_open_location (view, location, flags);
+
+      g_object_unref (location);
+    }
+  else if (volume && g_volume_can_mount (volume))
+    {
+      /*
+       * When the row is activated, the unmounted volume shall
+       * be mounted and opened right after.
+       */
+      priv->should_open_location = TRUE;
+
+      gtk_places_view_row_set_busy (row, TRUE);
+      mount_volume (view, volume);
+    }
+}
+
+static void update_places (GtkPlacesView *view);
+
+static void
+gtk_places_view_finalize (GObject *object)
+{
+  GtkPlacesView *self = (GtkPlacesView *)object;
+  GtkPlacesViewPrivate *priv = gtk_places_view_get_instance_private (self);
+
+  g_signal_handlers_disconnect_by_func (priv->volume_monitor, update_places, object);
+
+  if (priv->entry_pulse_timeout_id > 0)
+    g_source_remove (priv->entry_pulse_timeout_id);
+
+  g_cancellable_cancel (priv->cancellable);
+  g_cancellable_cancel (priv->networks_fetching_cancellable);
+
+  g_clear_pointer (&priv->search_query, g_free);
+  g_clear_object (&priv->server_list_file);
+  g_clear_object (&priv->server_list_monitor);
+  g_clear_object (&priv->volume_monitor);
+  g_clear_object (&priv->cancellable);
+  g_clear_object (&priv->networks_fetching_cancellable);
+
+  G_OBJECT_CLASS (gtk_places_view_parent_class)->finalize (object);
+}
+
+static void
+gtk_places_view_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  GtkPlacesView *self = GTK_PLACES_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_LOCAL_ONLY:
+      g_value_set_boolean (value, gtk_places_view_get_local_only (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_places_view_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  GtkPlacesView *self = GTK_PLACES_VIEW (object);
+
+  switch (prop_id)
+    {
+    case PROP_LOCAL_ONLY:
+      gtk_places_view_set_local_only (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static gboolean
+is_removable_volume (GVolume *volume)
+{
+  gboolean is_removable;
+  GDrive *drive;
+  GMount *mount;
+  gchar *id;
+
+  drive = g_volume_get_drive (volume);
+  mount = g_volume_get_mount (volume);
+  id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS);
+
+  is_removable = g_volume_can_eject (volume);
+
+  /* NULL volume identifier only happens on removable devices */
+  is_removable |= !id;
+
+  if (drive)
+    is_removable |= g_drive_can_eject (drive);
+
+  if (mount)
+    is_removable |= (g_mount_can_eject (mount) && !g_mount_can_unmount (mount));
+
+  g_clear_object (&drive);
+  g_clear_object (&mount);
+  g_free (id);
+
+  return is_removable;
+}
+
+typedef struct
+{
+  gchar         *uri;
+  GtkPlacesView *view;
+} RemoveServerData;
+
+static void
+on_remove_server_button_clicked (RemoveServerData *data)
+{
+  server_list_remove_server (data->view, data->uri);
+
+  populate_servers (data->view);
+}
+
+static void
+populate_servers (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+  GBookmarkFile *server_list;
+  GList *children;
+  gchar **uris;
+  gsize num_uris;
+  gint i;
+
+  priv = gtk_places_view_get_instance_private (view);
+  server_list = server_list_load (view);
+
+  if (!server_list)
+    return;
+
+  uris = g_bookmark_file_get_uris (server_list, &num_uris);
+
+  gtk_stack_set_visible_child_name (GTK_STACK (priv->recent_servers_stack),
+                                    num_uris > 0 ? "list" : "empty");
+
+  if (!uris)
+    {
+      g_bookmark_file_free (server_list);
+      return;
+    }
+
+  /* clear previous items */
+  children = gtk_container_get_children (GTK_CONTAINER (priv->recent_servers_listbox));
+  g_list_free_full (children, (GDestroyNotify) gtk_widget_destroy);
+
+  gtk_list_store_clear (priv->completion_store);
+
+  for (i = 0; i < num_uris; i++)
+    {
+      RemoveServerData *data;
+      GtkTreeIter iter;
+      GtkWidget *row;
+      GtkWidget *grid;
+      GtkWidget *button;
+      GtkWidget *label;
+      gchar *name;
+      gchar *dup_uri;
+
+      name = g_bookmark_file_get_title (server_list, uris[i], NULL);
+      dup_uri = g_strdup (uris[i]);
+
+      /* add to the completion list */
+      gtk_list_store_append (priv->completion_store, &iter);
+      gtk_list_store_set (priv->completion_store,
+                          &iter,
+                          0, name,
+                          1, uris[i],
+                          -1);
+
+      /* add to the recent servers listbox */
+      row = gtk_list_box_row_new ();
+
+      grid = g_object_new (GTK_TYPE_GRID,
+                           "orientation", GTK_ORIENTATION_VERTICAL,
+                           "border-width", 6,
+                           NULL);
+
+      /* name of the connected uri, if any */
+      label = gtk_label_new (name);
+      gtk_widget_set_hexpand (label, TRUE);
+      gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+      gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+      gtk_container_add (GTK_CONTAINER (grid), label);
+
+      /* the uri itself */
+      label = gtk_label_new (uris[i]);
+      gtk_widget_set_hexpand (label, TRUE);
+      gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+      gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+      gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
+      gtk_container_add (GTK_CONTAINER (grid), label);
+
+      /* remove button */
+      button = gtk_button_new ();
+      gtk_widget_set_halign (button, GTK_ALIGN_END);
+      gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
+      gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
+      gtk_style_context_add_class (gtk_widget_get_style_context (button), "image-button");
+      gtk_style_context_add_class (gtk_widget_get_style_context (button), "sidebar-button");
+      gtk_grid_attach (GTK_GRID (grid), button, 1, 0, 1, 2);
+      gtk_container_add (GTK_CONTAINER (button),
+                         gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_BUTTON));
+
+      gtk_container_add (GTK_CONTAINER (row), grid);
+      gtk_container_add (GTK_CONTAINER (priv->recent_servers_listbox), row);
+
+      /* custom data */
+      data = g_new0 (RemoveServerData, 1);
+      data->view = view;
+      data->uri = dup_uri;
+
+      g_object_set_data_full (G_OBJECT (row), "uri", dup_uri, g_free);
+      g_object_set_data_full (G_OBJECT (row), "remove-server-data", data, g_free);
+
+      g_signal_connect_swapped (button,
+                                "clicked",
+                                G_CALLBACK (on_remove_server_button_clicked),
+                                data);
+
+      gtk_widget_show_all (row);
+
+      g_free (name);
+    }
+
+  g_strfreev (uris);
+  g_bookmark_file_free (server_list);
+}
+
+static void
+update_view_mode (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+  GList *children;
+  GList *l;
+  gboolean show_listbox;
+
+  priv = gtk_places_view_get_instance_private (view);
+  show_listbox = FALSE;
+
+  /* drives */
+  children = gtk_container_get_children (GTK_CONTAINER (priv->listbox));
+
+  for (l = children; l; l = l->next)
+    {
+      /* GtkListBox filter rows by changing their GtkWidget::child-visible property */
+      if (gtk_widget_get_child_visible (l->data))
+        {
+          show_listbox = TRUE;
+          break;
+        }
+    }
+
+  g_list_free (children);
+
+  if (!show_listbox &&
+      priv->search_query &&
+      priv->search_query[0] != '\0')
+    {
+        gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "empty-search");
+    }
+  else
+    {
+      gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "browse");
+    }
+}
+
+static void
+insert_row (GtkPlacesView *view,
+            GtkWidget     *row,
+            gboolean       is_network)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  g_object_set_data (G_OBJECT (row), "is-network", GINT_TO_POINTER (is_network));
+
+  g_signal_connect_swapped (gtk_places_view_row_get_event_box (GTK_PLACES_VIEW_ROW (row)),
+                            "button-press-event",
+                            G_CALLBACK (on_button_press_event),
+                            row);
+
+  g_signal_connect (row,
+                    "popup-menu",
+                    G_CALLBACK (on_row_popup_menu),
+                    row);
+
+  g_signal_connect (gtk_places_view_row_get_eject_button (GTK_PLACES_VIEW_ROW (row)),
+                    "clicked",
+                    G_CALLBACK (on_eject_button_clicked),
+                    row);
+
+  gtk_container_add (GTK_CONTAINER (priv->listbox), row);
+}
+
+static void
+add_volume (GtkPlacesView *view,
+            GVolume       *volume)
+{
+  gboolean is_network;
+  GDrive *drive;
+  GMount *mount;
+  GFile *root;
+  GIcon *icon;
+  gchar *identifier;
+  gchar *name;
+  gchar *path;
+
+  if (is_removable_volume (volume))
+    return;
+
+  drive = g_volume_get_drive (volume);
+
+  if (drive)
+    {
+      gboolean is_removable;
+
+      is_removable = g_drive_is_media_removable (drive) ||
+                     g_volume_can_eject (volume);
+      g_object_unref (drive);
+
+      if (is_removable)
+        return;
+    }
+
+  identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS);
+  is_network = g_strcmp0 (identifier, "network") == 0;
+
+  mount = g_volume_get_mount (volume);
+  root = mount ? g_mount_get_default_location (mount) : NULL;
+  icon = g_volume_get_icon (volume);
+  name = g_volume_get_name (volume);
+  path = !is_network ? g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) : NULL;
+
+  if (!mount ||
+      (mount && !g_mount_is_shadowed (mount)))
+    {
+      GtkWidget *row;
+
+      row = g_object_new (GTK_TYPE_PLACES_VIEW_ROW,
+                          "icon", icon,
+                          "name", name,
+                          "path", path ? path : "",
+                          "volume", volume,
+                          "mount", mount,
+                          "file", NULL,
+                          "is-network", is_network,
+                          NULL);
+
+      insert_row (view, row, is_network);
+    }
+
+  g_clear_object (&root);
+  g_clear_object (&icon);
+  g_clear_object (&mount);
+  g_free (identifier);
+  g_free (name);
+  g_free (path);
+}
+
+static void
+add_mount (GtkPlacesView *view,
+           GMount        *mount)
+{
+  gboolean is_network;
+  GFile *root;
+  GIcon *icon;
+  gchar *name;
+  gchar *path;
+  gchar *uri;
+  gchar *schema;
+
+  icon = g_mount_get_icon (mount);
+  name = g_mount_get_name (mount);
+  root = g_mount_get_default_location (mount);
+  path = root ? g_file_get_parse_name (root) : NULL;
+  uri = g_file_get_uri (root);
+  schema = g_uri_parse_scheme (uri);
+  is_network = g_strcmp0 (schema, "file") != 0;
+
+  if (is_network)
+    g_clear_pointer (&path, g_free);
+
+  if (!g_mount_is_shadowed (mount))
+    {
+      GtkWidget *row;
+
+      row = g_object_new (GTK_TYPE_PLACES_VIEW_ROW,
+                          "icon", icon,
+                          "name", name,
+                          "path", path ? path : "",
+                          "volume", NULL,
+                          "mount", mount,
+                          "file", NULL,
+                          "is-network", is_network,
+                          NULL);
+
+      insert_row (view, row, is_network);
+    }
+
+  g_clear_object (&root);
+  g_clear_object (&icon);
+  g_free (name);
+  g_free (path);
+  g_free (uri);
+  g_free (schema);
+}
+
+static void
+add_drive (GtkPlacesView *view,
+           GDrive        *drive)
+{
+  GList *volumes;
+  GList *l;
+
+  /* Removable devices won't appear here */
+  if (g_drive_can_eject (drive))
+    return;
+
+  volumes = g_drive_get_volumes (drive);
+
+  for (l = volumes; l != NULL; l = l->next)
+    add_volume (view, l->data);
+
+  g_list_free_full (volumes, g_object_unref);
+}
+
+static void
+add_file (GtkPlacesView *view,
+          GFile         *file,
+          GIcon         *icon,
+          const gchar   *display_name,
+          const gchar   *path,
+          gboolean       is_network)
+{
+  GtkWidget *row;
+  row = g_object_new (GTK_TYPE_PLACES_VIEW_ROW,
+                      "icon", icon,
+                      "name", display_name,
+                      "path", path,
+                      "volume", NULL,
+                      "mount", NULL,
+                      "file", file,
+                      "is_network", is_network,
+                      NULL);
+
+  insert_row (view, row, is_network);
+}
+
+static gboolean
+has_networks (GtkPlacesView *view)
+{
+  GList *l;
+  GtkPlacesViewPrivate *priv;
+  GList *children;
+  gboolean has_network = FALSE;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  children = gtk_container_get_children (GTK_CONTAINER (priv->listbox));
+  for (l = children; l != NULL; l = l->next)
+    {
+      if (GPOINTER_TO_INT (g_object_get_data (l->data, "is-network")) == TRUE &&
+          g_object_get_data (l->data, "is-placeholder") == NULL)
+      {
+        has_network = TRUE;
+        break;
+      }
+    }
+
+  g_list_free (children);
+
+  return has_network;
+}
+
+static void
+update_network_state (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  if (priv->network_placeholder == NULL)
+    {
+      priv->network_placeholder = gtk_list_box_row_new ();
+      priv->network_placeholder_label = gtk_label_new ("");
+      gtk_label_set_xalign (GTK_LABEL (priv->network_placeholder_label), 0.0);
+      gtk_widget_set_margin_start (priv->network_placeholder_label, 12);
+      gtk_widget_set_margin_end (priv->network_placeholder_label, 12);
+      gtk_widget_set_margin_top (priv->network_placeholder_label, 6);
+      gtk_widget_set_margin_bottom (priv->network_placeholder_label, 6);
+      gtk_widget_set_hexpand (priv->network_placeholder_label, TRUE);
+      gtk_widget_set_sensitive (priv->network_placeholder, FALSE);
+      gtk_container_add (GTK_CONTAINER (priv->network_placeholder),
+                         priv->network_placeholder_label);
+      g_object_set_data (G_OBJECT (priv->network_placeholder),
+                         "is-network", GINT_TO_POINTER (TRUE));
+      /* mark the row as placeholder, so it always goes first */
+      g_object_set_data (G_OBJECT (priv->network_placeholder),
+                         "is-placeholder", GINT_TO_POINTER (TRUE));
+      gtk_container_add (GTK_CONTAINER (priv->listbox), priv->network_placeholder);
+
+      gtk_widget_show_all (GTK_WIDGET (priv->network_placeholder));
+      gtk_list_box_invalidate_headers (GTK_LIST_BOX (priv->listbox));
+    }
+
+  if (priv->fetching_networks)
+    {
+      gtk_spinner_start (GTK_SPINNER (priv->network_header_spinner));
+      /* only show a placeholder with a message if the list is empty.
+       * otherwise just show the spinner in the header */
+      if (!has_networks (view))
+        {
+          gtk_widget_show_all (priv->network_placeholder);
+          gtk_label_set_text (GTK_LABEL (priv->network_placeholder_label),
+                              _("Searching for network locations"));
+        }
+    }
+  else if (!has_networks (view))
+    {
+      gtk_spinner_stop (GTK_SPINNER (priv->network_header_spinner));
+      gtk_widget_show_all (priv->network_placeholder);
+      gtk_label_set_text (GTK_LABEL (priv->network_placeholder_label),
+                          _("No networks locations found"));
+    }
+  else
+    {
+      gtk_spinner_stop (GTK_SPINNER (priv->network_header_spinner));
+      gtk_widget_hide (priv->network_placeholder);
+    }
+}
+
+static void
+populate_networks (GtkPlacesView *view)
+{
+  GList *l;
+  GtkPlacesViewPrivate *priv;
+  GFile *file;
+  GIcon *icon;
+  gchar *display_name;
+
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  for (l = priv->detected_networks; l != NULL; l = l->next)
+    {
+      file = g_file_enumerator_get_child (priv->detected_networks_enumerator, l->data);
+      display_name = g_file_info_get_attribute_as_string (l->data, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
+      icon = g_file_info_get_icon (l->data);
+
+      add_file (view, file, icon, display_name, NULL, TRUE);
+    }
+}
+
+static void
+network_enumeration_next_files_finished (GObject      *source_object,
+                                         GAsyncResult *res,
+                                         gpointer      user_data)
+{
+  GtkPlacesViewPrivate *priv;
+  GtkPlacesView *view;
+  GError *error;
+
+  view = GTK_PLACES_VIEW (user_data);
+  priv = gtk_places_view_get_instance_private (view);
+  error = NULL;
+
+  /* clean previous fetched networks */
+  g_list_free_full (priv->detected_networks, g_object_unref);
+  priv->fetching_networks = FALSE;
+  g_clear_object (&priv->detected_networks_enumerator);
+
+  priv->detected_networks_enumerator = G_FILE_ENUMERATOR (source_object);
+  priv->detected_networks = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source_object),
+                                                                 res, &error);
+  if (error)
+    {
+      if (error->code != G_IO_ERROR_CANCELLED)
+        g_warning ("Failed to fetch network locations: %s", error->message);
+
+      g_clear_error (&error);
+    }
+  else
+    {
+      populate_networks (view);
+    }
+
+    /* avoid to update widgets if the operation was cancelled in finalize */
+    if (priv->listbox != NULL)
+      update_network_state (view);
+}
+
+static void
+network_enumeration_finished (GObject      *source_object,
+                              GAsyncResult *res,
+                              gpointer      user_data)
+{
+  GtkPlacesViewPrivate *priv;
+  GFileEnumerator *enumerator;
+  GtkPlacesView *view;
+  GError *error;
+
+  view = GTK_PLACES_VIEW (user_data);
+  priv = gtk_places_view_get_instance_private (view);
+  error = NULL;
+  enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, &error);
+
+  if (error)
+    {
+      if (error->code != G_IO_ERROR_CANCELLED)
+        g_warning ("Failed to fetch network locations: %s", error->message);
+
+      g_clear_error (&error);
+    }
+  else
+    {
+      g_clear_object (&priv->networks_fetching_cancellable);
+      priv->networks_fetching_cancellable = g_cancellable_new ();
+      g_file_enumerator_next_files_async (enumerator,
+                                          G_MAXINT32,
+                                          G_PRIORITY_DEFAULT,
+                                          priv->networks_fetching_cancellable,
+                                          network_enumeration_next_files_finished,
+                                          user_data);
+    }
+}
+
+static void
+fetch_networks (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+  GFile *network_file;
+
+  priv = gtk_places_view_get_instance_private (view);
+  network_file = g_file_new_for_uri ("network:///");
+
+  g_cancellable_cancel (priv->networks_fetching_cancellable);
+  g_clear_object (&priv->networks_fetching_cancellable);
+  priv->networks_fetching_cancellable = g_cancellable_new ();
+  priv->fetching_networks = TRUE;
+
+  update_network_state (view);
+
+  g_file_enumerate_children_async (network_file,
+                                   "standard::name,standard::display-name,standard::icon",
+                                   G_FILE_QUERY_INFO_NONE,
+                                   G_PRIORITY_DEFAULT,
+                                   priv->networks_fetching_cancellable,
+                                   network_enumeration_finished,
+                                   view);
+
+  g_clear_object (&network_file);
+}
+
+static void
+update_places (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+  GList *children;
+  GList *mounts;
+  GList *volumes;
+  GList *drives;
+  GList *l;
+  GIcon *icon;
+  GFile *file;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  /* Clear all previously added items */
+  children = gtk_container_get_children (GTK_CONTAINER (priv->listbox));
+  g_list_free_full (children, (GDestroyNotify) gtk_widget_destroy);
+  priv->network_placeholder = NULL;
+  priv->network_header_spinner = NULL;
+
+  /* Add "Computer" row */
+  file = g_file_new_for_path ("/");
+  icon = g_themed_icon_new_with_default_fallbacks ("drive-harddisk");
+
+  add_file (view, file, icon, _("Computer"), "/", FALSE);
+
+  g_clear_object (&file);
+  g_clear_object (&icon);
+
+  /* Add currently connected drives */
+  drives = g_volume_monitor_get_connected_drives (priv->volume_monitor);
+
+  for (l = drives; l != NULL; l = l->next)
+    add_drive (view, l->data);
+
+  g_list_free_full (drives, g_object_unref);
+
+  /*
+   * Since all volumes with an associated GDrive were already added with
+   * add_drive before, add all volumes that aren't associated with a
+   * drive.
+   */
+  volumes = g_volume_monitor_get_volumes (priv->volume_monitor);
+
+  for (l = volumes; l != NULL; l = l->next)
+    {
+      GVolume *volume;
+      GDrive *drive;
+
+      volume = l->data;
+      drive = g_volume_get_drive (volume);
+
+      if (drive)
+        {
+          g_object_unref (drive);
+          continue;
+        }
+
+      add_volume (view, volume);
+    }
+
+  g_list_free_full (volumes, g_object_unref);
+
+  /*
+   * Now that all necessary drives and volumes were already added, add mounts
+   * that have no volume, such as /etc/mtab mounts, ftp, sftp, etc.
+   */
+  mounts = g_volume_monitor_get_mounts (priv->volume_monitor);
+
+  for (l = mounts; l != NULL; l = l->next)
+    {
+      GMount *mount;
+      GVolume *volume;
+
+      mount = l->data;
+      volume = g_mount_get_volume (mount);
+
+      if (volume)
+        {
+          g_object_unref (volume);
+          continue;
+        }
+
+      add_mount (view, mount);
+    }
+
+  g_list_free_full (mounts, g_object_unref);
+
+  /* load saved servers */
+  populate_servers (view);
+
+  /* fetch networks and add them asynchronously */
+  fetch_networks (view);
+
+  update_view_mode (view);
+}
+
+static void
+server_mount_ready_cb (GObject      *source_file,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  GtkPlacesViewPrivate *priv;
+  GtkPlacesView *view;
+  gboolean should_show;
+  GError *error;
+  GFile *location;
+
+  view = GTK_PLACES_VIEW (user_data);
+  priv = gtk_places_view_get_instance_private (view);
+  location = G_FILE (source_file);
+  should_show = TRUE;
+  error = NULL;
+
+  priv->should_pulse_entry = FALSE;
+  set_busy_cursor (view, FALSE);
+
+  g_file_mount_enclosing_volume_finish (location, res, &error);
+  /* Restore from Cancel to Connect */
+  gtk_button_set_label (GTK_BUTTON (priv->connect_button), _("Con_nect"));
+  gtk_widget_set_sensitive (priv->address_entry, TRUE);
+  priv->connecting_to_server = FALSE;
+
+  if (error)
+    {
+      should_show = FALSE;
+
+      if (error->code == G_IO_ERROR_ALREADY_MOUNTED)
+        {
+          /*
+           * Already mounted volume is not a critical error
+           * and we can still continue with the operation.
+           */
+          should_show = TRUE;
+        }
+      else if (error->domain != G_IO_ERROR ||
+               (error->code != G_IO_ERROR_CANCELLED &&
+                error->code != G_IO_ERROR_FAILED_HANDLED))
+        {
+          /* if it wasn't cancelled show a dialog */
+          emit_show_error_message (view, _("Unable to access location"), error->message);
+          should_show = FALSE;
+        }
+
+      g_clear_error (&error);
+    }
+
+  if (should_show)
+    {
+      server_list_add_server (view, location);
+
+      /*
+       * Only clear the entry if it successfully connects to the server.
+       * Otherwise, the user would lost the typed address even if it fails
+       * to connect.
+       */
+      gtk_entry_set_text (GTK_ENTRY (priv->address_entry), "");
+
+      if (priv->should_open_location)
+        {
+          GMount *mount_point;
+          GError *error;
+          GFile *enclosing_location;
+
+          error = NULL;
+          mount_point = g_file_find_enclosing_mount (location, NULL, &error);
+
+          if (error)
+            {
+              emit_show_error_message (view, _("Unable to access location"), error->message);
+              g_clear_error (&error);
+              goto out;
+            }
+
+          enclosing_location = g_mount_get_default_location (mount_point);
+
+          emit_open_location (view, enclosing_location, priv->open_flags);
+
+          g_object_unref (enclosing_location);
+        }
+    }
+
+out:
+  update_places (view);
+}
+
+static void
+volume_mount_ready_cb (GObject      *source_volume,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  GtkPlacesViewPrivate *priv;
+  GtkPlacesView *view;
+  gboolean should_show;
+  GVolume *volume;
+  GError *error;
+
+  view = GTK_PLACES_VIEW (user_data);
+  priv = gtk_places_view_get_instance_private (view);
+  volume = G_VOLUME (source_volume);
+  should_show = TRUE;
+  error = NULL;
+
+  set_busy_cursor (view, FALSE);
+
+  g_volume_mount_finish (volume, res, &error);
+
+  if (error)
+    {
+      should_show = FALSE;
+
+      if (error->code == G_IO_ERROR_ALREADY_MOUNTED)
+        {
+          /*
+           * If the volume was already mounted, it's not a hard error
+           * and we can still continue with the operation.
+           */
+          should_show = TRUE;
+        }
+      else if (error->domain != G_IO_ERROR ||
+               (error->code != G_IO_ERROR_CANCELLED &&
+                error->code != G_IO_ERROR_FAILED_HANDLED))
+        {
+          /* if it wasn't cancelled show a dialog */
+          emit_show_error_message (GTK_PLACES_VIEW (user_data), _("Unable to access location"), 
error->message);
+          should_show = FALSE;
+        }
+
+      g_clear_error (&error);
+    }
+
+  if (should_show)
+    {
+      GMount *mount;
+      GFile *root;
+
+      mount = g_volume_get_mount (volume);
+      root = g_mount_get_default_location (mount);
+
+      if (priv->should_open_location)
+        emit_open_location (GTK_PLACES_VIEW (user_data), root, priv->open_flags);
+
+      g_object_unref (mount);
+      g_object_unref (root);
+    }
+
+  update_places (view);
+}
+
+static void
+unmount_ready_cb (GObject      *source_mount,
+                  GAsyncResult *res,
+                  gpointer      user_data)
+{
+  GtkPlacesView *view;
+  GMount *mount;
+  GError *error;
+
+  view = GTK_PLACES_VIEW (user_data);
+  mount = G_MOUNT (source_mount);
+  error = NULL;
+
+  set_busy_cursor (view, FALSE);
+
+  g_mount_unmount_with_operation_finish (mount, res, &error);
+
+  if (error)
+    {
+      if (error->domain != G_IO_ERROR ||
+          (error->code != G_IO_ERROR_CANCELLED &&
+           error->code != G_IO_ERROR_FAILED_HANDLED))
+        {
+          /* if it wasn't cancelled show a dialog */
+          emit_show_error_message (view, _("Unable to unmount volume"), error->message);
+        }
+
+      g_clear_error (&error);
+    }
+}
+
+static gboolean
+pulse_entry_cb (gpointer user_data)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (user_data));
+
+  if (priv->should_pulse_entry)
+    {
+      gtk_entry_progress_pulse (GTK_ENTRY (priv->address_entry));
+
+      return G_SOURCE_CONTINUE;
+    }
+  else
+    {
+      gtk_entry_set_progress_pulse_step (GTK_ENTRY (priv->address_entry), 0.0);
+      gtk_entry_set_progress_fraction (GTK_ENTRY (priv->address_entry), 0.0);
+      priv->entry_pulse_timeout_id = 0;
+
+      return G_SOURCE_REMOVE;
+    }
+}
+
+static void
+unmount_mount (GtkPlacesView *view,
+               GMount        *mount)
+{
+  GtkPlacesViewPrivate *priv;
+  GMountOperation *operation;
+  GtkWidget *toplevel;
+
+  priv = gtk_places_view_get_instance_private (view);
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+  operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
+
+  set_busy_cursor (GTK_PLACES_VIEW (view), TRUE);
+
+  g_cancellable_cancel (priv->cancellable);
+  g_clear_object (&priv->cancellable);
+  priv->cancellable = g_cancellable_new ();
+
+  operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
+  g_mount_unmount_with_operation (mount,
+                                  0,
+                                  operation,
+                                  priv->cancellable,
+                                  unmount_ready_cb,
+                                  view);
+  g_object_unref (operation);
+}
+
+static void
+mount_server (GtkPlacesView *view,
+              GFile         *location)
+{
+  GtkPlacesViewPrivate *priv;
+  GMountOperation *operation;
+  GtkWidget *toplevel;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  g_cancellable_cancel (priv->cancellable);
+  g_clear_object (&priv->cancellable);
+  /* User cliked when the operation was ongoing, so wanted to cancel it */
+  if (priv->connecting_to_server)
+    return;
+
+  priv->cancellable = g_cancellable_new ();
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+  operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
+
+  set_busy_cursor (view, TRUE);
+  priv->should_pulse_entry = TRUE;
+  gtk_entry_set_progress_pulse_step (GTK_ENTRY (priv->address_entry), 0.1);
+  /* Allow to cancel the operation */
+  gtk_button_set_label (GTK_BUTTON (priv->connect_button), _("Cance_l"));
+  gtk_widget_set_sensitive (priv->address_entry, FALSE);
+  priv->connecting_to_server = TRUE;
+
+  if (priv->entry_pulse_timeout_id == 0)
+    priv->entry_pulse_timeout_id = g_timeout_add (100, (GSourceFunc) pulse_entry_cb, view);
+
+  g_mount_operation_set_password_save (operation, G_PASSWORD_SAVE_FOR_SESSION);
+
+  g_file_mount_enclosing_volume (location,
+                                 0,
+                                 operation,
+                                 priv->cancellable,
+                                 server_mount_ready_cb,
+                                 view);
+
+  /* unref operation here - g_file_mount_enclosing_volume() does ref for itself */
+  g_object_unref (operation);
+}
+
+static void
+mount_volume (GtkPlacesView *view,
+              GVolume       *volume)
+{
+  GtkPlacesViewPrivate *priv;
+  GMountOperation *operation;
+  GtkWidget *toplevel;
+
+  priv = gtk_places_view_get_instance_private (view);
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view));
+  operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
+
+  set_busy_cursor (view, TRUE);
+
+  g_cancellable_cancel (priv->cancellable);
+  g_clear_object (&priv->cancellable);
+  priv->cancellable = g_cancellable_new ();
+
+  g_mount_operation_set_password_save (operation, G_PASSWORD_SAVE_FOR_SESSION);
+
+  g_volume_mount (volume,
+                  0,
+                  operation,
+                  priv->cancellable,
+                  volume_mount_ready_cb,
+                  view);
+
+  /* unref operation here - g_file_mount_enclosing_volume() does ref for itself */
+  g_object_unref (operation);
+}
+
+/* Callback used when the file list's popup menu is detached */
+static void
+popup_menu_detach_cb (GtkWidget *attach_widget,
+                      GtkMenu   *menu)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (attach_widget));
+  priv->popup_menu = NULL;
+}
+
+static void
+get_view_and_file (GtkPlacesViewRow  *row,
+                   GtkWidget        **view,
+                   GFile            **file)
+{
+  if (view)
+    *view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+
+  if (file)
+    {
+      GVolume *volume;
+      GMount *mount;
+
+      volume = gtk_places_view_row_get_volume (row);
+      mount = gtk_places_view_row_get_mount (row);
+
+      if (mount)
+        *file = g_mount_get_default_location (mount);
+      else if (volume)
+        *file = g_volume_get_activation_root (volume);
+      else
+        *file = NULL;
+    }
+}
+
+static void
+open_cb (GtkMenuItem      *item,
+         GtkPlacesViewRow *row)
+{
+  GtkWidget *view;
+  GFile *file;
+
+  get_view_and_file (row, &view, &file);
+
+  if (file)
+    emit_open_location (GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NORMAL);
+
+  g_clear_object (&file);
+}
+
+static void
+open_in_new_tab_cb (GtkMenuItem      *item,
+                    GtkPlacesViewRow *row)
+{
+  GtkWidget *view;
+  GFile *file;
+
+  get_view_and_file (row, &view, &file);
+
+  if (file)
+    emit_open_location (GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_TAB);
+
+  g_clear_object (&file);
+}
+
+static void
+open_in_new_window_cb (GtkMenuItem      *item,
+                       GtkPlacesViewRow *row)
+{
+  GtkWidget *view;
+  GFile *file;
+
+  get_view_and_file (row, &view, &file);
+
+  if (file)
+    emit_open_location (GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_WINDOW);
+
+  g_clear_object (&file);
+}
+
+static void
+mount_cb (GtkMenuItem      *item,
+          GtkPlacesViewRow *row)
+{
+  GtkPlacesViewPrivate *priv;
+  GtkWidget *view;
+  GVolume *volume;
+
+  view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (view));
+  volume = gtk_places_view_row_get_volume (row);
+
+  /*
+   * When the mount item is activated, it's expected that
+   * the volume only gets mounted, without opening it after
+   * the operation is complete.
+   */
+  priv->should_open_location = FALSE;
+
+  gtk_places_view_row_set_busy (row, TRUE);
+  mount_volume (GTK_PLACES_VIEW (view), volume);
+}
+
+static void
+unmount_cb (GtkMenuItem      *item,
+            GtkPlacesViewRow *row)
+{
+  GtkWidget *view;
+  GMount *mount;
+
+  view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+  mount = gtk_places_view_row_get_mount (row);
+
+  gtk_places_view_row_set_busy (row, TRUE);
+
+  unmount_mount (GTK_PLACES_VIEW (view), mount);
+}
+
+/* Constructs the popup menu if needed */
+static void
+build_popup_menu (GtkPlacesView    *view,
+                  GtkPlacesViewRow *row)
+{
+  GtkPlacesViewPrivate *priv;
+  GtkWidget *item;
+  GMount *mount;
+  GFile *file;
+  gboolean is_network;
+
+  priv = gtk_places_view_get_instance_private (view);
+  mount = gtk_places_view_row_get_mount (row);
+  file = gtk_places_view_row_get_file (row);
+  is_network = gtk_places_view_row_get_is_network (row);
+
+  priv->popup_menu = gtk_menu_new ();
+  gtk_style_context_add_class (gtk_widget_get_style_context (priv->popup_menu),
+                               GTK_STYLE_CLASS_CONTEXT_MENU);
+
+  gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
+                             GTK_WIDGET (view),
+                             popup_menu_detach_cb);
+
+  /* Open item is always present */
+  item = gtk_menu_item_new_with_mnemonic (_("_Open"));
+  g_signal_connect (item,
+                    "activate",
+                    G_CALLBACK (open_cb),
+                    row);
+  gtk_widget_show (item);
+  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+
+  if (priv->open_flags & GTK_PLACES_OPEN_NEW_TAB)
+    {
+      item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab"));
+      g_signal_connect (item,
+                        "activate",
+                        G_CALLBACK (open_in_new_tab_cb),
+                        row);
+      gtk_widget_show (item);
+      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+    }
+
+  if (priv->open_flags & GTK_PLACES_OPEN_NEW_WINDOW)
+    {
+      item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window"));
+      g_signal_connect (item,
+                        "activate",
+                        G_CALLBACK (open_in_new_window_cb),
+                        row);
+      gtk_widget_show (item);
+      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+    }
+
+  /*
+   * The only item that contains a file up to now is the Computer
+   * item, which cannot be mounted or unmounted.
+   */
+  if (file)
+    return;
+
+  /* Separator */
+  item = gtk_separator_menu_item_new ();
+  gtk_widget_show (item);
+  gtk_menu_shell_insert (GTK_MENU_SHELL (priv->popup_menu), item, -1);
+
+  /* Mount/Unmount items */
+  if (mount)
+    {
+      item = gtk_menu_item_new_with_mnemonic (is_network ? _("_Disconnect") : _("_Unmount"));
+      g_signal_connect (item,
+                        "activate",
+                        G_CALLBACK (unmount_cb),
+                        row);
+      gtk_widget_show (item);
+      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+    }
+  else
+    {
+      item = gtk_menu_item_new_with_mnemonic (is_network ? _("_Connect") : _("_Mount"));
+      g_signal_connect (item,
+                        "activate",
+                        G_CALLBACK (mount_cb),
+                        row);
+      gtk_widget_show (item);
+      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
+    }
+}
+
+static void
+popup_menu (GtkPlacesViewRow *row,
+            GdkEventButton   *event)
+{
+  GtkPlacesViewPrivate *priv;
+  GtkWidget *view;
+  gint button;
+
+  view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (view));
+
+  g_clear_pointer (&priv->popup_menu, gtk_widget_destroy);
+
+  build_popup_menu (GTK_PLACES_VIEW (view), row);
+
+  /* The event button needs to be 0 if we're popping up this menu from
+   * a button release, else a 2nd click outside the menu with any button
+   * other than the one that invoked the menu will be ignored (instead
+   * of dismissing the menu). This is a subtle fragility of the GTK menu code.
+   */
+  if (event)
+    {
+      if (event->type == GDK_BUTTON_PRESS)
+        button = 0;
+      else
+        button = event->button;
+    }
+  else
+    {
+      button = 0;
+    }
+
+  gtk_menu_popup (GTK_MENU (priv->popup_menu),
+                  NULL,
+                  NULL,
+                  NULL,
+                  NULL,
+                  button,
+                  event ? event->time : gtk_get_current_event_time ());
+}
+
+static gboolean
+on_row_popup_menu (GtkPlacesViewRow *row)
+{
+  popup_menu (row, NULL);
+  return TRUE;
+}
+
+static gboolean
+on_button_press_event (GtkPlacesViewRow *row,
+                       GdkEventButton   *event)
+{
+  if (row &&
+      gdk_event_triggers_context_menu ((GdkEvent*) event) &&
+      event->type == GDK_BUTTON_PRESS)
+    {
+      popup_menu (row, event);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+on_key_press_event (GtkWidget     *widget,
+                    GdkEventKey   *event,
+                    GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  if (event)
+    {
+      guint modifiers;
+
+      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)
+        {
+          GtkWidget *focus_widget;
+          GtkWindow *toplevel;
+
+          priv->current_open_flags = GTK_PLACES_OPEN_NORMAL;
+          toplevel = get_toplevel (GTK_WIDGET (view));
+
+          if (!toplevel)
+            return FALSE;
+
+          focus_widget = gtk_window_get_focus (toplevel);
+
+          if (!GTK_IS_PLACES_VIEW_ROW (focus_widget))
+            return FALSE;
+
+          if ((event->state & modifiers) == GDK_SHIFT_MASK)
+            priv->current_open_flags = GTK_PLACES_OPEN_NEW_TAB;
+          else if ((event->state & modifiers) == GDK_CONTROL_MASK)
+            priv->current_open_flags = GTK_PLACES_OPEN_NEW_WINDOW;
+
+          activate_row (view, GTK_PLACES_VIEW_ROW (focus_widget), priv->current_open_flags);
+
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static void
+on_eject_button_clicked (GtkWidget        *widget,
+                         GtkPlacesViewRow *row)
+{
+  if (row)
+    {
+      GtkWidget *view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_PLACES_VIEW);
+
+      unmount_mount (GTK_PLACES_VIEW (view), gtk_places_view_row_get_mount (row));
+    }
+}
+
+static void
+on_connect_button_clicked (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+  const gchar *uri;
+  GFile *file;
+
+  priv = gtk_places_view_get_instance_private (view);
+  file = NULL;
+
+  /*
+   * Since the 'Connect' button is updated whenever the typed
+   * address changes, it is sufficient to check if it's sensitive
+   * or not, in order to determine if the given address is valid.
+   */
+  if (!gtk_widget_get_sensitive (priv->connect_button))
+    return;
+
+  uri = gtk_entry_get_text (GTK_ENTRY (priv->address_entry));
+
+  if (uri != NULL && uri[0] != '\0')
+    file = g_file_new_for_commandline_arg (uri);
+
+  if (file)
+    {
+      priv->should_open_location = TRUE;
+
+      mount_server (view, file);
+    }
+  else
+    {
+      emit_show_error_message (view, _("Unable to get remote server location"), NULL);
+    }
+}
+
+static void
+on_address_entry_text_changed (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+  const gchar* const *supported_protocols;
+  gchar *address, *scheme;
+  gboolean supported;
+
+  priv = gtk_places_view_get_instance_private (view);
+  supported = FALSE;
+  supported_protocols = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
+
+  if (!supported_protocols)
+    return;
+
+  address = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->address_entry)));
+  scheme = g_uri_parse_scheme (address);
+
+  if (!scheme)
+    goto out;
+
+  supported = g_strv_contains (supported_protocols, scheme) &&
+              !g_strv_contains (unsupported_protocols, scheme);
+
+out:
+  gtk_widget_set_sensitive (priv->connect_button, supported);
+  g_free (address);
+}
+
+static void
+on_recent_servers_listbox_row_activated (GtkPlacesView    *view,
+                                         GtkPlacesViewRow *row,
+                                         GtkWidget        *listbox)
+{
+  GtkPlacesViewPrivate *priv;
+  gchar *uri;
+
+  priv = gtk_places_view_get_instance_private (view);
+  uri = g_object_get_data (G_OBJECT (row), "uri");
+
+  gtk_entry_set_text (GTK_ENTRY (priv->address_entry), uri);
+
+  gtk_widget_hide (priv->recent_servers_popover);
+}
+
+static void
+on_listbox_row_activated (GtkPlacesView    *view,
+                          GtkPlacesViewRow *row,
+                          GtkWidget        *listbox)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  activate_row (view, row, priv->current_open_flags);
+}
+
+static gboolean
+listbox_filter_func (GtkListBoxRow *row,
+                     gpointer       user_data)
+{
+  GtkPlacesViewPrivate *priv;
+  gboolean is_network;
+  gboolean retval;
+  gchar *name;
+  gchar *path;
+
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (user_data));
+  retval = FALSE;
+
+  is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-network"));
+
+  if (is_network && priv->local_only)
+    return FALSE;
+
+  if (!priv->search_query || priv->search_query[0] == '\0')
+    return TRUE;
+
+  g_object_get (row,
+                "name", &name,
+                "path", &path,
+                NULL);
+
+  if (name)
+    retval |= strstr (name, priv->search_query) != NULL;
+
+  if (path)
+    retval |= strstr (path, priv->search_query) != NULL;
+
+  g_free (name);
+  g_free (path);
+
+  return retval;
+}
+
+static void
+listbox_header_func (GtkListBoxRow *row,
+                     GtkListBoxRow *before,
+                     gpointer       user_data)
+{
+  GtkPlacesViewPrivate *priv;
+  gboolean row_is_network;
+  gchar *text;
+
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (user_data));
+  text = NULL;
+  row_is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-network"));
+
+  if (!before)
+    {
+      text = g_strdup_printf ("<b>%s</b>", row_is_network ? _("Networks") : _("On This Computer"));
+    }
+  else
+    {
+      gboolean before_is_network;
+
+      before_is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (before), "is-network"));
+
+      if (before_is_network != row_is_network)
+        text = g_strdup_printf ("<b>%s</b>", row_is_network ? _("Networks") : _("On This Computer"));
+    }
+
+  if (text)
+    {
+      GtkWidget *header;
+      GtkWidget *label;
+      GtkWidget *separator;
+
+      header = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+
+      separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+
+      label = g_object_new (GTK_TYPE_LABEL,
+                            "use_markup", TRUE,
+                            "margin-top", 6,
+                            "margin-start", 12,
+                            "label", text,
+                            "xalign", 0.0f,
+                            NULL);
+      if (row_is_network)
+        {
+          GtkWidget *header_name;
+
+          g_object_set (label,
+                        "margin-end", 6,
+                        NULL);
+
+          header_name = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+          priv->network_header_spinner = gtk_spinner_new ();
+          g_object_set (priv->network_header_spinner,
+                        "margin-end", 12,
+                        NULL);
+          gtk_container_add (GTK_CONTAINER (header_name), label);
+          gtk_container_add (GTK_CONTAINER (header_name), priv->network_header_spinner);
+          gtk_container_add (GTK_CONTAINER (header), header_name);
+        }
+      else
+        {
+          g_object_set (label,
+                        "hexpand", TRUE,
+                        "margin-end", 12,
+                        NULL);
+          gtk_container_add (GTK_CONTAINER (header), label);
+        }
+
+      gtk_container_add (GTK_CONTAINER (header), separator);
+      gtk_widget_show_all (header);
+
+      gtk_list_box_row_set_header (row, header);
+
+      g_free (text);
+    }
+  else
+    {
+      gtk_list_box_row_set_header (row, NULL);
+    }
+}
+
+static gint
+listbox_sort_func (GtkListBoxRow *row1,
+                   GtkListBoxRow *row2,
+                   gpointer       user_data)
+{
+  gboolean row1_is_network;
+  gboolean row2_is_network;
+  gchar *location1;
+  gchar *location2;
+  gboolean *is_placeholder1;
+  gboolean *is_placeholder2;
+  gint retval;
+
+  row1_is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row1), "is-network"));
+  row2_is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row2), "is-network"));
+
+  retval = row1_is_network - row2_is_network;
+
+  if (retval != 0)
+    return retval;
+
+  is_placeholder1 = g_object_get_data (G_OBJECT (row1), "is-placeholder");
+  is_placeholder2 = g_object_get_data (G_OBJECT (row2), "is-placeholder");
+
+  /* we can't have two placeholders for the same section */
+  g_assert (!(is_placeholder1 != NULL && is_placeholder2 != NULL));
+
+  if (is_placeholder1)
+    return -1;
+  if (is_placeholder2)
+    return 1;
+
+  g_object_get (row1, "path", &location1, NULL);
+  g_object_get (row2, "path", &location2, NULL);
+
+  retval = g_strcmp0 (location1, location2);
+
+  g_free (location1);
+  g_free (location2);
+
+  return retval;
+}
+
+static void
+gtk_places_view_constructed (GObject *object)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (object));
+
+  G_OBJECT_CLASS (gtk_places_view_parent_class)->constructed (object);
+
+  gtk_list_box_set_sort_func (GTK_LIST_BOX (priv->listbox),
+                              listbox_sort_func,
+                              object,
+                              NULL);
+  gtk_list_box_set_filter_func (GTK_LIST_BOX (priv->listbox),
+                                listbox_filter_func,
+                                object,
+                                NULL);
+  gtk_list_box_set_header_func (GTK_LIST_BOX (priv->listbox),
+                                listbox_header_func,
+                                object,
+                                NULL);
+
+  /* load drives */
+  update_places (GTK_PLACES_VIEW (object));
+
+  g_signal_connect_swapped (priv->volume_monitor,
+                            "mount-added",
+                            G_CALLBACK (update_places),
+                            object);
+  g_signal_connect_swapped (priv->volume_monitor,
+                            "mount-changed",
+                            G_CALLBACK (update_places),
+                            object);
+  g_signal_connect_swapped (priv->volume_monitor,
+                            "mount-removed",
+                            G_CALLBACK (update_places),
+                            object);
+  g_signal_connect_swapped (priv->volume_monitor,
+                            "volume-added",
+                            G_CALLBACK (update_places),
+                            object);
+  g_signal_connect_swapped (priv->volume_monitor,
+                            "volume-changed",
+                            G_CALLBACK (update_places),
+                            object);
+  g_signal_connect_swapped (priv->volume_monitor,
+                            "volume-removed",
+                            G_CALLBACK (update_places),
+                            object);
+}
+
+static void
+gtk_places_view_map (GtkWidget *widget)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (GTK_PLACES_VIEW (widget));
+
+  gtk_entry_set_text (GTK_ENTRY (priv->address_entry), "");
+
+  GTK_WIDGET_CLASS (gtk_places_view_parent_class)->map (widget);
+}
+
+static void
+gtk_places_view_class_init (GtkPlacesViewClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gtk_places_view_finalize;
+  object_class->constructed = gtk_places_view_constructed;
+  object_class->get_property = gtk_places_view_get_property;
+  object_class->set_property = gtk_places_view_set_property;
+
+  widget_class->map = gtk_places_view_map;
+
+  /**
+   * GtkPlacesView::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
+   * should be opened.
+   *
+   * The places view 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.18
+   */
+  places_view_signals [OPEN_LOCATION] =
+          g_signal_new (_("open-location"),
+                        G_OBJECT_CLASS_TYPE (object_class),
+                        G_SIGNAL_RUN_FIRST,
+                        G_STRUCT_OFFSET (GtkPlacesViewClass, open_location),
+                        NULL, NULL, NULL,
+                        G_TYPE_NONE, 2,
+                        G_TYPE_OBJECT,
+                        GTK_TYPE_PLACES_OPEN_FLAGS);
+
+  /**
+   * GtkPlacesView::show-error-message:
+   * @view: 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 view 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.18
+   */
+  places_view_signals [SHOW_ERROR_MESSAGE] =
+          g_signal_new (_("show-error-message"),
+                        G_OBJECT_CLASS_TYPE (object_class),
+                        G_SIGNAL_RUN_FIRST,
+                        G_STRUCT_OFFSET (GtkPlacesViewClass, show_error_message),
+                        NULL, NULL,
+                        NULL,
+                        G_TYPE_NONE, 2,
+                        G_TYPE_STRING,
+                        G_TYPE_STRING);
+
+  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_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,
+                              GTK_PLACES_OPEN_NORMAL,
+                              G_PARAM_READWRITE);
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  /* Bind class to template */
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkplacesview.ui");
+
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, actionbar);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, address_entry);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, address_entry_completion);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, completion_store);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, connect_button);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, listbox);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, recent_servers_listbox);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, recent_servers_popover);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, recent_servers_stack);
+  gtk_widget_class_bind_template_child_private (widget_class, GtkPlacesView, stack);
+
+  gtk_widget_class_bind_template_callback (widget_class, on_address_entry_text_changed);
+  gtk_widget_class_bind_template_callback (widget_class, on_connect_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
+  gtk_widget_class_bind_template_callback (widget_class, on_listbox_row_activated);
+  gtk_widget_class_bind_template_callback (widget_class, on_recent_servers_listbox_row_activated);
+}
+
+static void
+gtk_places_view_init (GtkPlacesView *self)
+{
+  GtkPlacesViewPrivate *priv;
+
+  priv = gtk_places_view_get_instance_private (self);
+
+  priv->volume_monitor = g_volume_monitor_get ();
+  priv->open_flags = GTK_PLACES_OPEN_NORMAL;
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+/**
+ * gtk_places_view_new:
+ *
+ * Creates a new #GtkPlacesView widget.
+ *
+ * The application should connect to at least the
+ * #GtkPlacesView::open-location signal to be notified
+ * when the user makes a selection in the view.
+ *
+ * Returns: a newly created #GtkPlacesView
+ *
+ * Since: 3.18
+ */
+GtkWidget *
+gtk_places_view_new (void)
+{
+  return g_object_new (GTK_TYPE_PLACES_VIEW, NULL);
+}
+
+/**
+ * gtk_places_view_set_open_flags:
+ * @view: a #GtkPlacesView
+ * @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 view.  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 @view about the ways in which the
+ * application can open new locations, so that the view can display (or not)
+ * the “Open in new tab” and “Open in new window” menu items as appropriate.
+ *
+ * When the #GtkPlacesView::open-location signal is emitted, its flags
+ * argument will be set to one of the @flags that was passed in
+ * gtk_places_view_set_open_flags().
+ *
+ * Passing 0 for @flags will cause #GTK_PLACES_OPEN_NORMAL to always be sent
+ * to callbacks for the “open-location” signal.
+ *
+ * Since: 3.18
+ */
+void
+gtk_places_view_set_open_flags (GtkPlacesView      *view,
+                                GtkPlacesOpenFlags  flags)
+{
+  GtkPlacesViewPrivate *priv;
+
+  g_return_if_fail (GTK_IS_PLACES_VIEW (view));
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  if (priv->open_flags != flags)
+    {
+      priv->open_flags = flags;
+      g_object_notify_by_pspec (G_OBJECT (view), properties[PROP_OPEN_FLAGS]);
+    }
+}
+
+/**
+ * gtk_places_view_get_open_flags:
+ * @view: a #GtkPlacesSidebar
+ *
+ * Gets the open flags.
+ *
+ * Returns: the #GtkPlacesOpenFlags of @view
+ *
+ * Since: 3.18
+ */
+GtkPlacesOpenFlags
+gtk_places_view_get_open_flags (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), 0);
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  return priv->open_flags;
+}
+
+/**
+ * gtk_places_view_get_search_query:
+ * @view: a #GtkPlacesView
+ *
+ * Retrieves the current search query from @view.
+ *
+ * Returns: (transfer none): the current search query.
+ */
+const gchar*
+gtk_places_view_get_search_query (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), NULL);
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  return priv->search_query;
+}
+
+/**
+ * gtk_places_view_set_search_query:
+ * @view: a #GtkPlacesView
+ * @query_text: the query, or NULL.
+ *
+ * Sets the search query of @view. The search is immediately performed
+ * once the query is set.
+ *
+ * Returns:
+ */
+void
+gtk_places_view_set_search_query (GtkPlacesView *view,
+                                  const gchar   *query_text)
+{
+  GtkPlacesViewPrivate *priv;
+
+  g_return_if_fail (GTK_IS_PLACES_VIEW (view));
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  if (g_strcmp0 (priv->search_query, query_text) != 0)
+    {
+      g_clear_pointer (&priv->search_query, g_free);
+      priv->search_query = g_strdup (query_text);
+
+      gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->listbox));
+      gtk_list_box_invalidate_headers (GTK_LIST_BOX (priv->listbox));
+
+      update_view_mode (view);
+    }
+}
+
+/**
+ * gtk_places_view_get_local_only:
+ * @view: a #GtkPlacesView
+ *
+ * Returns %TRUE if only local volumes are shown, i.e. no networks
+ * are displayed.
+ *
+ * Returns: %TRUE if only local volumes are shown, %FALSE otherwise.
+ *
+ * Since: 3.18
+ */
+gboolean
+gtk_places_view_get_local_only (GtkPlacesView *view)
+{
+  GtkPlacesViewPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), FALSE);
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  return priv->local_only;
+}
+
+/**
+ * gtk_places_view_set_local_only:
+ * @view: a #GtkPlacesView
+ * @local_only: %TRUE to hide remote locations, %FALSE to show.
+ *
+ * Sets the #GtkPlacesView::local-only property to @local_only.
+ *
+ * Returns:
+ *
+ * Since: 3.18
+ */
+void
+gtk_places_view_set_local_only (GtkPlacesView *view,
+                                gboolean       local_only)
+{
+  GtkPlacesViewPrivate *priv;
+
+  g_return_if_fail (GTK_IS_PLACES_VIEW (view));
+
+  priv = gtk_places_view_get_instance_private (view);
+
+  if (priv->local_only != local_only)
+    {
+      priv->local_only = local_only;
+
+      gtk_widget_set_visible (priv->actionbar, !local_only);
+      gtk_list_box_invalidate_filter (GTK_LIST_BOX (priv->listbox));
+
+      update_view_mode (view);
+
+      g_object_notify_by_pspec (G_OBJECT (view), properties [PROP_LOCAL_ONLY]);
+    }
+}
diff --git a/src/gtk/gtkplacesview.ui b/src/gtk/gtkplacesview.ui
new file mode 100644
index 0000000..5ee523e
--- /dev/null
+++ b/src/gtk/gtkplacesview.ui
@@ -0,0 +1,332 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk30">
+  <requires lib="gtk+" version="3.16"/>
+    <object class="GtkListStore" id="completion_store">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name uri -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkEntryCompletion" id="address_entry_completion">
+    <property name="model">completion_store</property>
+    <property name="minimum_key_length">1</property>
+    <property name="text_column">1</property>
+    <property name="inline_completion">True</property>
+    <property name="popup_completion">False</property>
+  </object>
+  <object class="GtkPopover" id="recent_servers_popover">
+    <child>
+      <object class="GtkStack" id="recent_servers_stack">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="vexpand">True</property>
+            <property name="valign">center</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">18</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="pixel-size">48</property>
+                <property name="icon_name">network-server-symbolic</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes" comments="Translators: Server as any successfully 
connected network address">No recent servers found</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">empty</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="border_width">12</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Recent Servers</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="vexpand">True</property>
+                <property name="shadow_type">in</property>
+                <property name="min_content_width">250</property>
+                <property name="min_content_height">150</property>
+                <child>
+                  <object class="GtkViewport">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkListBox" id="recent_servers_listbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="selection_mode">none</property>
+                        <signal name="row-activated" handler="on_recent_servers_listbox_row_activated" 
object="GtkPlacesView" swapped="yes" />
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">list</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <template class="GtkPlacesView" parent="GtkBox">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="orientation">vertical</property>
+    <signal name="key-press-event" handler="on_key_press_event" object="GtkPlacesView" swapped="no" />
+    <child>
+      <object class="GtkStack" id="stack">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="vhomogeneous">False</property>
+        <property name="transition_type">crossfade</property>
+        <child>
+          <object class="GtkFrame">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="visible">True</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+                <child>
+                  <object class="GtkViewport">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkListBox" id="listbox">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="selection_mode">none</property>
+                        <signal name="row-activated" handler="on_listbox_row_activated" 
object="GtkPlacesView" swapped="yes" />
+                      </object>
+                    </child>
+                    <style>
+                      <class name="view"/>
+                    </style>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="name">browse</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="halign">center</property>
+            <property name="valign">center</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkImage">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="pixel_size">72</property>
+                <property name="icon_name">edit-find-symbolic</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">No results found</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                  <attribute name="scale" value="1.44"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Try a different search</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="name">empty-search</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkActionBar" id="actionbar">
+        <property name="visible">True</property>
+        <property name="hexpand">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkLabel">
+            <property name="visible">True</property>
+            <property name="hexpand">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
+            <property name="label" translatable="yes">Connect to _Server</property>
+            <property name="mnemonic_widget">address_entry</property>
+            <property name="use_underline">True</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+        </child>
+        <child>
+          <object class="GtkButton" id="connect_button">
+            <property name="label" translatable="yes">Con_nect</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="sensitive">False</property>
+            <property name="receives_default">True</property>
+            <signal name="clicked" handler="on_connect_button_clicked" object="GtkPlacesView" swapped="yes" 
/>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="hexpand">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkEntry" id="address_entry">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="width_chars">20</property>
+                <property name="placeholder_text" translatable="yes">Enter server address…</property>
+                <property name="completion">address_entry_completion</property>
+                <signal name="notify::text" handler="on_address_entry_text_changed" object="GtkPlacesView" 
swapped="yes" />
+                <signal name="activate" handler="on_connect_button_clicked" object="GtkPlacesView" 
swapped="yes" />
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkMenuButton" id="server_list_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="direction">up</property>
+                <property name="popover">recent_servers_popover</property>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">pan-up-symbolic</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+              </packing>
+            </child>
+            <style>
+              <class name="linked"/>
+            </style>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </template>
+</interface>
diff --git a/src/gtk/gtkplacesviewprivate.h b/src/gtk/gtkplacesviewprivate.h
new file mode 100644
index 0000000..d61574a
--- /dev/null
+++ b/src/gtk/gtkplacesviewprivate.h
@@ -0,0 +1,88 @@
+/* gtkplacesview.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef GTK_PLACES_VIEW_H
+#define GTK_PLACES_VIEW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PLACES_VIEW        (gtk_places_view_get_type ())
+#define GTK_PLACES_VIEW(obj)        (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_PLACES_VIEW, GtkPlacesView))
+#define GTK_PLACES_VIEW_CLASS(klass)(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PLACES_VIEW, 
GtkPlacesViewClass))
+#define GTK_IS_PLACES_VIEW(obj)     (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_PLACES_VIEW))
+#define GTK_IS_PLACES_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PLACES_VIEW))
+#define GTK_PLACES_VIEW_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PLACES_VIEW, 
GtkPlacesViewClass))
+
+typedef struct _GtkPlacesView GtkPlacesView;
+typedef struct _GtkPlacesViewClass GtkPlacesViewClass;
+typedef struct _GtkPlacesViewPrivate GtkPlacesViewPrivate;
+
+struct _GtkPlacesViewClass
+{
+  GtkBoxClass parent_class;
+
+  void     (* open_location)        (GtkPlacesView          *view,
+                                     GFile                  *location,
+                                     GtkPlacesOpenFlags  open_flags);
+
+  void    (* show_error_message)     (GtkPlacesSidebar      *sidebar,
+                                      const gchar           *primary,
+                                      const gchar           *secondary);
+
+  /*< private >*/
+
+  /* Padding for future expansion */
+  gpointer reserved[10];
+};
+
+struct _GtkPlacesView
+{
+  GtkBox parent_instance;
+};
+
+GDK_AVAILABLE_IN_3_18
+GType              gtk_places_view_get_type                      (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_18
+GtkPlacesOpenFlags gtk_places_view_get_open_flags                (GtkPlacesView      *view);
+GDK_AVAILABLE_IN_3_18
+void               gtk_places_view_set_open_flags                (GtkPlacesView      *view,
+                                                                  GtkPlacesOpenFlags  flags);
+
+GDK_AVAILABLE_IN_3_18
+const gchar*       gtk_places_view_get_search_query              (GtkPlacesView      *view);
+GDK_AVAILABLE_IN_3_18
+void               gtk_places_view_set_search_query              (GtkPlacesView      *view,
+                                                                  const gchar        *query_text);
+
+GDK_AVAILABLE_IN_3_18
+gboolean           gtk_places_view_get_local_only                (GtkPlacesView         *view);
+
+GDK_AVAILABLE_IN_3_18
+void               gtk_places_view_set_local_only                (GtkPlacesView         *view,
+                                                                  gboolean               local_only);
+
+GDK_AVAILABLE_IN_3_18
+GtkWidget *        gtk_places_view_new                           (void);
+
+G_END_DECLS
+
+#endif /* GTK_PLACES_VIEW_H */
diff --git a/src/gtk/gtkplacesviewrow.c b/src/gtk/gtkplacesviewrow.c
new file mode 100644
index 0000000..dea6488
--- /dev/null
+++ b/src/gtk/gtkplacesviewrow.c
@@ -0,0 +1,326 @@
+/* gtkplacesviewrow.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "gtkplacesviewrowprivate.h"
+
+struct _GtkPlacesViewRow
+{
+  GtkListBoxRow  parent_instance;
+
+  GtkSpinner    *busy_spinner;
+  GtkButton     *eject_button;
+  GtkImage      *eject_icon;
+  GtkEventBox   *event_box;
+  GtkImage      *icon_image;
+  GtkLabel      *name_label;
+  GtkLabel      *path_label;
+
+  GVolume       *volume;
+  GMount        *mount;
+  GFile         *file;
+
+  gint           is_network : 1;
+};
+
+G_DEFINE_TYPE (GtkPlacesViewRow, gtk_places_view_row, GTK_TYPE_LIST_BOX_ROW)
+
+enum {
+  PROP_0,
+  PROP_ICON,
+  PROP_NAME,
+  PROP_PATH,
+  PROP_VOLUME,
+  PROP_MOUNT,
+  PROP_FILE,
+  PROP_IS_NETWORK,
+  LAST_PROP
+};
+
+static GParamSpec *properties [LAST_PROP];
+
+static void
+gtk_places_view_row_finalize (GObject *object)
+{
+  GtkPlacesViewRow *self = GTK_PLACES_VIEW_ROW (object);
+
+  g_clear_object (&self->volume);
+  g_clear_object (&self->mount);
+  g_clear_object (&self->file);
+
+  G_OBJECT_CLASS (gtk_places_view_row_parent_class)->finalize (object);
+}
+
+static void
+gtk_places_view_row_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GtkPlacesViewRow *self;
+  GIcon *icon;
+
+  self = GTK_PLACES_VIEW_ROW (object);
+  icon = NULL;
+
+  switch (prop_id)
+    {
+    case PROP_ICON:
+      gtk_image_get_gicon (self->icon_image, &icon, NULL);
+      g_value_set_object (value, icon);
+      break;
+
+    case PROP_NAME:
+      g_value_set_string (value, gtk_label_get_label (self->name_label));
+      break;
+
+    case PROP_PATH:
+      g_value_set_string (value, gtk_label_get_label (self->path_label));
+      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_FILE:
+      g_value_set_object (value, self->file);
+      break;
+
+    case PROP_IS_NETWORK:
+      g_value_set_boolean (value, self->is_network);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_places_view_row_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GtkPlacesViewRow *self = GTK_PLACES_VIEW_ROW (object);
+
+  switch (prop_id)
+    {
+    case PROP_ICON:
+      gtk_image_set_from_gicon (self->icon_image,
+                                g_value_get_object (value),
+                                GTK_ICON_SIZE_LARGE_TOOLBAR);
+      break;
+
+    case PROP_NAME:
+      gtk_label_set_label (self->name_label, g_value_get_string (value));
+      break;
+
+    case PROP_PATH:
+      gtk_label_set_label (self->path_label, g_value_get_string (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));
+      gtk_widget_set_visible (GTK_WIDGET (self->eject_button), self->mount != NULL);
+      break;
+
+    case PROP_FILE:
+      g_set_object (&self->file, g_value_get_object (value));
+      break;
+
+    case PROP_IS_NETWORK:
+      gtk_places_view_row_set_is_network (self, g_value_get_boolean (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_places_view_row_class_init (GtkPlacesViewRowClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gtk_places_view_row_finalize;
+  object_class->get_property = gtk_places_view_row_get_property;
+  object_class->set_property = gtk_places_view_row_set_property;
+
+  properties[PROP_ICON] =
+          g_param_spec_object ("icon",
+                               _("Icon of the row"),
+                               _("The icon representing the volume"),
+                               G_TYPE_ICON,
+                               G_PARAM_READWRITE);
+
+  properties[PROP_NAME] =
+          g_param_spec_string ("name",
+                               _("Name of the volume"),
+                               _("The name of the volume"),
+                               "",
+                               G_PARAM_READWRITE);
+
+  properties[PROP_PATH] =
+          g_param_spec_string ("path",
+                               _("Path of the volume"),
+                               _("The path of the volume"),
+                               "",
+                               G_PARAM_READWRITE);
+
+  properties[PROP_VOLUME] =
+          g_param_spec_object ("volume",
+                               _("Volume represented by the row"),
+                               _("The volume represented by the row"),
+                               G_TYPE_VOLUME,
+                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  properties[PROP_MOUNT] =
+          g_param_spec_object ("mount",
+                               _("Mount represented by the row"),
+                               _("The mount point represented by the row, if any"),
+                               G_TYPE_MOUNT,
+                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  properties[PROP_FILE] =
+          g_param_spec_object ("file",
+                               _("File represented by the row"),
+                               _("The file represented by the row, if any"),
+                               G_TYPE_FILE,
+                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  properties[PROP_IS_NETWORK] =
+          g_param_spec_boolean ("is-network",
+                                _("Whether the row represents a network location"),
+                                _("Whether the row represents a network location"),
+                                FALSE,
+                                G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+  g_object_class_install_properties (object_class, LAST_PROP, properties);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/gtk/gtkplacesviewrow.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, busy_spinner);
+  gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, eject_button);
+  gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, eject_icon);
+  gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, event_box);
+  gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, icon_image);
+  gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, name_label);
+  gtk_widget_class_bind_template_child (widget_class, GtkPlacesViewRow, path_label);
+}
+
+static void
+gtk_places_view_row_init (GtkPlacesViewRow *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+GtkWidget*
+gtk_places_view_row_new (GVolume *volume,
+                         GMount  *mount)
+{
+  return g_object_new (GTK_TYPE_PLACES_VIEW_ROW,
+                       "volume", volume,
+                       "mount", mount,
+                       NULL);
+}
+
+GMount*
+gtk_places_view_row_get_mount (GtkPlacesViewRow *row)
+{
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL);
+
+  return row->mount;
+}
+
+GVolume*
+gtk_places_view_row_get_volume (GtkPlacesViewRow *row)
+{
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL);
+
+  return row->volume;
+}
+
+GFile*
+gtk_places_view_row_get_file (GtkPlacesViewRow *row)
+{
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL);
+
+  return row->file;
+}
+
+GtkWidget*
+gtk_places_view_row_get_eject_button (GtkPlacesViewRow *row)
+{
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL);
+
+  return GTK_WIDGET (row->eject_button);
+}
+
+GtkWidget*
+gtk_places_view_row_get_event_box (GtkPlacesViewRow *row)
+{
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL);
+
+  return GTK_WIDGET (row->event_box);
+}
+
+void
+gtk_places_view_row_set_busy (GtkPlacesViewRow *row,
+                              gboolean          is_busy)
+{
+  g_return_if_fail (GTK_IS_PLACES_VIEW_ROW (row));
+
+  gtk_widget_set_visible (GTK_WIDGET (row->busy_spinner), is_busy);
+}
+
+gboolean
+gtk_places_view_row_get_is_network (GtkPlacesViewRow *row)
+{
+  g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), FALSE);
+
+  return row->is_network;
+}
+
+void
+gtk_places_view_row_set_is_network (GtkPlacesViewRow *row,
+                                    gboolean          is_network)
+{
+  if (row->is_network != is_network)
+    {
+      row->is_network = is_network;
+
+      gtk_image_set_from_icon_name (row->eject_icon,
+                                    is_network ? "network-offline-symbolic" : "media-eject-symbolic",
+                                    GTK_ICON_SIZE_BUTTON);
+      gtk_widget_set_tooltip_text (GTK_WIDGET (row->eject_button), is_network ? _("Disconnect") : 
_("Unmount"));
+    }
+}
diff --git a/src/gtk/gtkplacesviewrow.ui b/src/gtk/gtkplacesviewrow.ui
new file mode 100644
index 0000000..d1e923b
--- /dev/null
+++ b/src/gtk/gtkplacesviewrow.ui
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface domain="gtk30">
+  <requires lib="gtk+" version="3.16"/>
+  <template class="GtkPlacesViewRow" parent="GtkListBoxRow">
+    <property name="width_request">100</property>
+    <property name="visible">True</property>
+    <property name="can_focus">True</property>
+    <style>
+      <class name="volume-row" />
+    </style>
+    <child>
+      <object class="GtkEventBox" id="event_box">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkBox" id="box">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_start">12</property>
+            <property name="margin_end">12</property>
+            <property name="margin_top">6</property>
+            <property name="margin_bottom">6</property>
+            <property name="border_width">0</property>
+            <property name="spacing">18</property>
+            <child>
+              <object class="GtkImage" id="icon_image">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="pixel_size">32</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="name_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="xalign">0</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="path_label">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="justify">right</property>
+                <property name="ellipsize">middle</property>
+                <property name="xalign">1</property>
+                <property name="width_chars">15</property>
+                <property name="max_width_chars">15</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="eject_button">
+                <property name="visible">False</property>
+                <property name="halign">end</property>
+                <property name="valign">center</property>
+                <property name="tooltip-text" translatable="yes">Unmount</property>
+                <child>
+                  <object class="GtkImage" id="eject_icon">
+                    <property name="visible">True</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>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinner" id="busy_spinner">
+                <property name="visible">False</property>
+                <property name="can_focus">False</property>
+                <property name="active">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/gtk/gtkplacesviewrowprivate.h b/src/gtk/gtkplacesviewrowprivate.h
new file mode 100644
index 0000000..59294de
--- /dev/null
+++ b/src/gtk/gtkplacesviewrowprivate.h
@@ -0,0 +1,53 @@
+/* gtkplacesviewrow.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTK_PLACES_VIEW_ROW_H
+#define GTK_PLACES_VIEW_ROW_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_PLACES_VIEW_ROW (gtk_places_view_row_get_type())
+
+G_DECLARE_FINAL_TYPE (GtkPlacesViewRow, gtk_places_view_row, GTK, PLACES_VIEW_ROW, GtkListBoxRow)
+
+GtkWidget*         gtk_places_view_row_new                       (GVolume            *volume,
+                                                                  GMount             *mount);
+
+GtkWidget*         gtk_places_view_row_get_eject_button          (GtkPlacesViewRow   *row);
+
+GtkWidget*         gtk_places_view_row_get_event_box             (GtkPlacesViewRow   *row);
+
+GMount*            gtk_places_view_row_get_mount                 (GtkPlacesViewRow   *row);
+
+GVolume*           gtk_places_view_row_get_volume                (GtkPlacesViewRow   *row);
+
+GFile*             gtk_places_view_row_get_file                  (GtkPlacesViewRow   *row);
+
+void               gtk_places_view_row_set_busy                  (GtkPlacesViewRow   *row,
+                                                                  gboolean            is_busy);
+
+gboolean           gtk_places_view_row_get_is_network            (GtkPlacesViewRow   *row);
+
+void               gtk_places_view_row_set_is_network            (GtkPlacesViewRow   *row,
+                                                                  gboolean            is_network);
+
+G_END_DECLS
+
+#endif /* GTK_PLACES_VIEW_ROW_H */
diff --git a/src/nautilus-application.c b/src/nautilus-application.c
index 19ce99a..a4bb9fc 100644
--- a/src/nautilus-application.c
+++ b/src/nautilus-application.c
@@ -383,11 +383,13 @@ get_window_slot_for_location (NautilusApplication *application, GFile *location)
 {
        NautilusWindowSlot *slot;
        NautilusWindow *window;
+        NautilusFile *file;
        GList *l, *sl;
 
        slot = NULL;
+        file = nautilus_file_get (location);
 
-       if (g_file_query_file_type (location, G_FILE_QUERY_INFO_NONE, NULL) != G_FILE_TYPE_DIRECTORY) {
+       if (!nautilus_file_is_in_other_locations (file) && nautilus_file_is_directory (file)) {
                location = g_file_get_parent (location);
        } else {
                g_object_ref (location);
@@ -411,6 +413,7 @@ get_window_slot_for_location (NautilusApplication *application, GFile *location)
                }
        }
 
+        nautilus_file_unref (file);
        g_object_unref (location);
 
        return slot;
diff --git a/src/nautilus-pathbar.c b/src/nautilus-pathbar.c
index 19ff073..a489583 100644
--- a/src/nautilus-pathbar.c
+++ b/src/nautilus-pathbar.c
@@ -43,6 +43,7 @@ enum {
 
 typedef enum {
         NORMAL_BUTTON,
+        OTHER_LOCATIONS_BUTTON,
         ROOT_BUTTON,
         HOME_BUTTON,
        MOUNT_BUTTON
@@ -400,11 +401,14 @@ nautilus_path_bar_dispose (GObject *object)
 static const char *
 get_dir_name (ButtonData *button_data)
 {
-       if (button_data->type == HOME_BUTTON) {
-               return _("Home");
-       } else {
-               return button_data->dir_name;
-       }
+        switch (button_data->type) {
+        case HOME_BUTTON:
+                return _("Home");
+        case OTHER_LOCATIONS_BUTTON:
+                return _("Other Locations");
+        default:
+                return button_data->dir_name;
+        }
 }
 
 /* We always want to request the same size for the label, whether
@@ -1629,8 +1633,13 @@ setup_button_type (ButtonData       *button_data,
                   GFile *location)
 {
        GMount *mount;
+        gchar *uri;
+
+        uri = g_file_get_uri (location);
 
-       if (nautilus_is_root_directory (location)) {
+        if (g_str_has_prefix (uri, "other-locations://")) {
+                button_data->type = OTHER_LOCATIONS_BUTTON;
+        } else if (nautilus_is_root_directory (location)) {
                button_data->type = ROOT_BUTTON;
        } else if (nautilus_is_home_directory (location)) {
                button_data->type = HOME_BUTTON;
@@ -1644,6 +1653,8 @@ setup_button_type (ButtonData       *button_data,
        } else {
                button_data->type = NORMAL_BUTTON;
        }
+
+        g_free (uri);
 }
 
 static void
diff --git a/src/nautilus-places-view.c b/src/nautilus-places-view.c
new file mode 100644
index 0000000..6bccbc0
--- /dev/null
+++ b/src/nautilus-places-view.c
@@ -0,0 +1,323 @@
+/* nautilus-places-view.c
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "nautilus-places-view.h"
+#include "nautilus-window-slot.h"
+#include "gtk/gtkplacesviewprivate.h"
+
+typedef struct
+{
+        GFile                  *location;
+        GIcon                  *icon;
+        NautilusQuery          *search_query;
+
+        GtkWidget              *places_view;
+} NautilusPlacesViewPrivate;
+
+struct _NautilusPlacesView
+{
+        GtkFrameClass parent;
+};
+
+static void          nautilus_places_view_iface_init             (NautilusViewInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (NautilusPlacesView, nautilus_places_view, GTK_TYPE_BOX,
+                         G_ADD_PRIVATE (NautilusPlacesView)
+                         G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_VIEW, nautilus_places_view_iface_init));
+
+enum {
+        PROP_0,
+        PROP_ICON,
+        PROP_LOCATION,
+        PROP_SEARCH_QUERY,
+        PROP_VIEW_WIDGET,
+        PROP_IS_LOADING,
+        PROP_IS_SEARCHING,
+        LAST_PROP
+};
+
+static void
+open_location_cb (NautilusPlacesView *view,
+                  GFile              *location,
+                  GtkPlacesOpenFlags  open_flags)
+{
+        NautilusWindowOpenFlags flags;
+        GtkWidget *slot;
+
+        slot = gtk_widget_get_ancestor (GTK_WIDGET (view), NAUTILUS_TYPE_WINDOW_SLOT);
+
+        switch (open_flags) {
+        case GTK_PLACES_OPEN_NEW_TAB:
+                flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB;
+                break;
+
+        case GTK_PLACES_OPEN_NEW_WINDOW:
+                flags = NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW;
+                break;
+
+        case GTK_PLACES_OPEN_NORMAL: /* fall-through */
+        default:
+                flags = 0;
+                break;
+        }
+
+        if (slot) {
+                nautilus_window_slot_open_location_full (NAUTILUS_WINDOW_SLOT (slot),
+                                                         location,
+                                                         flags,
+                                                         NULL,
+                                                         NULL,
+                                                         NULL);
+        }
+}
+
+static void
+nautilus_places_view_finalize (GObject *object)
+{
+        NautilusPlacesView *self = (NautilusPlacesView *)object;
+        NautilusPlacesViewPrivate *priv = nautilus_places_view_get_instance_private (self);
+
+        g_clear_object (&priv->icon);
+        g_clear_object (&priv->location);
+        g_clear_object (&priv->search_query);
+
+        G_OBJECT_CLASS (nautilus_places_view_parent_class)->finalize (object);
+}
+
+static void
+nautilus_places_view_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+        NautilusView *view = NAUTILUS_VIEW (object);
+
+        switch (prop_id) {
+        case PROP_ICON:
+                g_value_set_object (value, nautilus_view_get_icon (view));
+                break;
+
+        case PROP_LOCATION:
+                g_value_set_object (value, nautilus_view_get_location (view));
+                break;
+
+        case PROP_SEARCH_QUERY:
+                g_value_set_object (value, nautilus_view_get_search_query (view));
+                break;
+
+        case PROP_VIEW_WIDGET:
+                g_value_set_object (value, nautilus_view_get_view_widget (view));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+}
+
+static void
+nautilus_places_view_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+        NautilusView *view = NAUTILUS_VIEW (object);
+
+        switch (prop_id) {
+        case PROP_LOCATION:
+                nautilus_view_set_location (view, g_value_get_object (value));
+                break;
+
+        case PROP_SEARCH_QUERY:
+                nautilus_view_set_search_query (view, g_value_get_object (value));
+                break;
+
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+}
+
+static GIcon*
+nautilus_places_view_get_icon (NautilusView *view)
+{
+        NautilusPlacesViewPrivate *priv;
+
+        priv = nautilus_places_view_get_instance_private (NAUTILUS_PLACES_VIEW (view));
+
+        return priv->icon;
+}
+
+static GFile*
+nautilus_places_view_get_location (NautilusView *view)
+{
+        NautilusPlacesViewPrivate *priv;
+
+        priv = nautilus_places_view_get_instance_private (NAUTILUS_PLACES_VIEW (view));
+
+        return priv->location;
+}
+
+static void
+nautilus_places_view_set_location (NautilusView *view,
+                                   GFile        *location)
+{
+        if (location) {
+                NautilusPlacesViewPrivate *priv;
+                gchar *uri;
+
+                priv = nautilus_places_view_get_instance_private (NAUTILUS_PLACES_VIEW (view));
+                uri = g_file_get_uri (location);
+
+                /*
+                 * If it's not trying to open the places view itself, simply
+                 * delegates the location to window-slot, which takes care of
+                 * selecting the appropriate view.
+                 */
+                if (!g_str_has_prefix (uri, "other-locations://")) {
+                        GtkWidget *slot;
+
+                        slot = gtk_widget_get_ancestor (GTK_WIDGET (view), NAUTILUS_TYPE_WINDOW_SLOT);
+
+                        if (slot) {
+                                nautilus_window_slot_open_location (NAUTILUS_WINDOW_SLOT (slot), location, 
0);
+                        }
+                } else {
+                        g_set_object (&priv->location, location);
+                }
+
+                g_free (uri);
+        }
+}
+
+static NautilusQuery*
+nautilus_places_view_get_search_query (NautilusView *view)
+{
+        NautilusPlacesViewPrivate *priv;
+
+        priv = nautilus_places_view_get_instance_private (NAUTILUS_PLACES_VIEW (view));
+
+        return priv->search_query;
+}
+
+static void
+nautilus_places_view_set_search_query (NautilusView  *view,
+                                       NautilusQuery *query)
+{
+        NautilusPlacesViewPrivate *priv;
+        gchar *text;
+
+        priv = nautilus_places_view_get_instance_private (NAUTILUS_PLACES_VIEW (view));
+
+        g_set_object (&priv->search_query, query);
+
+        text = query ? nautilus_query_get_text (query) : NULL;
+
+        gtk_places_view_set_search_query (GTK_PLACES_VIEW (priv->places_view), text);
+
+        g_free (text);
+}
+
+static GtkWidget*
+nautilus_places_view_get_view_widget (NautilusView *view)
+{
+        /* By returning NULL, the view menu button turns insensitive */
+        return NULL;
+}
+
+static gboolean
+nautilus_places_view_is_loading (NautilusView *view)
+{
+        /* TODO: it should return TRUE when we're searching for networks.
+         *      Needs support for it at GtkPlacesView widget.
+         */
+        return FALSE;
+}
+
+static gboolean
+nautilus_places_view_is_searching (NautilusView *view)
+{
+        NautilusPlacesViewPrivate *priv;
+
+        priv = nautilus_places_view_get_instance_private (NAUTILUS_PLACES_VIEW (view));
+
+        return priv->search_query != NULL;
+}
+
+static void
+nautilus_places_view_iface_init (NautilusViewInterface *iface)
+{
+        iface->get_icon = nautilus_places_view_get_icon;
+        iface->get_location = nautilus_places_view_get_location;
+        iface->set_location = nautilus_places_view_set_location;
+        iface->get_search_query = nautilus_places_view_get_search_query;
+        iface->set_search_query = nautilus_places_view_set_search_query;
+        iface->get_view_widget = nautilus_places_view_get_view_widget;
+        iface->is_loading = nautilus_places_view_is_loading;
+        iface->is_searching = nautilus_places_view_is_searching;
+}
+
+static void
+nautilus_places_view_class_init (NautilusPlacesViewClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = nautilus_places_view_finalize;
+        object_class->get_property = nautilus_places_view_get_property;
+        object_class->set_property = nautilus_places_view_set_property;
+
+        g_object_class_override_property (object_class, PROP_ICON, "icon");
+        g_object_class_override_property (object_class, PROP_IS_LOADING, "is-loading");
+        g_object_class_override_property (object_class, PROP_IS_SEARCHING, "is-searching");
+        g_object_class_override_property (object_class, PROP_LOCATION, "location");
+        g_object_class_override_property (object_class, PROP_SEARCH_QUERY, "search-query");
+        g_object_class_override_property (object_class, PROP_VIEW_WIDGET, "view-widget");
+}
+
+static void
+nautilus_places_view_init (NautilusPlacesView *self)
+{
+        NautilusPlacesViewPrivate *priv;
+
+        priv = nautilus_places_view_get_instance_private (self);
+
+        /* Icon */
+        priv->icon = g_themed_icon_new_with_default_fallbacks ("view-list-symbolic");
+
+        /* Location */
+        priv->location = g_file_new_for_uri ("other-locations:///");
+
+        /* Places view */
+        priv->places_view = gtk_places_view_new ();
+        gtk_places_view_set_open_flags (GTK_PLACES_VIEW (priv->places_view),
+                                        GTK_PLACES_OPEN_NEW_TAB | GTK_PLACES_OPEN_NEW_WINDOW | 
GTK_PLACES_OPEN_NORMAL);
+        gtk_widget_set_hexpand (priv->places_view, TRUE);
+        gtk_widget_set_vexpand (priv->places_view, TRUE);
+        gtk_widget_show (priv->places_view);
+        gtk_container_add (GTK_CONTAINER (self), priv->places_view);
+
+        g_signal_connect_swapped (priv->places_view,
+                                  "open-location",
+                                  G_CALLBACK (open_location_cb),
+                                  self);
+
+}
+
+NautilusPlacesView *
+nautilus_places_view_new (void)
+{
+        return g_object_new (NAUTILUS_TYPE_PLACES_VIEW, NULL);
+}
diff --git a/src/nautilus-places-view.h b/src/nautilus-places-view.h
new file mode 100644
index 0000000..5193910
--- /dev/null
+++ b/src/nautilus-places-view.h
@@ -0,0 +1,37 @@
+/* nautilus-places-view.h
+ *
+ * Copyright (C) 2015 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NAUTILUS_PLACES_VIEW_H
+#define NAUTILUS_PLACES_VIEW_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "nautilus-view.h"
+
+G_BEGIN_DECLS
+
+#define NAUTILUS_TYPE_PLACES_VIEW (nautilus_places_view_get_type())
+
+G_DECLARE_FINAL_TYPE (NautilusPlacesView, nautilus_places_view, NAUTILUS, PLACES_VIEW, GtkBox)
+
+NautilusPlacesView*  nautilus_places_view_new                    (void);
+
+G_END_DECLS
+
+#endif /* NAUTILUS_PLACES_VIEW_H */
diff --git a/src/nautilus-window-slot.c b/src/nautilus-window-slot.c
index 16e062e..15be076 100644
--- a/src/nautilus-window-slot.c
+++ b/src/nautilus-window-slot.c
@@ -28,6 +28,7 @@
 #include "nautilus-canvas-view.h"
 #include "nautilus-desktop-window.h"
 #include "nautilus-list-view.h"
+#include "nautilus-places-view.h"
 #include "nautilus-special-location-bar.h"
 #include "nautilus-trash-bar.h"
 #include "nautilus-view.h"
@@ -117,6 +118,7 @@ struct NautilusWindowSlotDetails {
        NautilusWindowGoToCallback open_callback;
        gpointer open_callback_user_data;
         gchar *view_mode_before_search;
+        gchar *view_mode_before_places;
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -127,15 +129,108 @@ static void location_has_really_changed (NautilusWindowSlot *slot);
 static void nautilus_window_slot_connect_new_content_view (NautilusWindowSlot *slot);
 static void nautilus_window_slot_disconnect_content_view (NautilusWindowSlot *slot);
 static void nautilus_window_slot_emit_location_change (NautilusWindowSlot *slot, GFile *from, GFile *to);
+static gboolean nautilus_window_slot_content_view_matches (NautilusWindowSlot *slot, const char *iid);
+static NautilusView* nautilus_window_slot_get_view_for_location (NautilusWindowSlot *slot, GFile *location);
+
+static NautilusView*
+nautilus_window_slot_get_view_for_location (NautilusWindowSlot *slot,
+                                            GFile              *location)
+{
+        NautilusWindow *window;
+        NautilusView *view;
+        NautilusFile *file;
+
+        window = nautilus_window_slot_get_window (slot);
+        file = nautilus_file_get (location);
+        view = NULL;
+
+        /* FIXME bugzilla.gnome.org 41243:
+        * We should use inheritance instead of these special cases
+        * for the desktop window.
+        */
+        if (NAUTILUS_IS_DESKTOP_WINDOW (window)) {
+                view = NAUTILUS_VIEW (nautilus_files_view_new (NAUTILUS_DESKTOP_ICON_VIEW_IID, slot));
+        } else if (nautilus_file_is_in_other_locations (file)) {
+                view = NAUTILUS_VIEW (nautilus_places_view_new ());
+
+                /* Save the current view, so we can go back after places view */
+                if (slot->details->content_view && NAUTILUS_IS_FILES_VIEW (slot->details->content_view)) {
+                        g_clear_pointer (&slot->details->view_mode_before_places, g_free);
+                        slot->details->view_mode_before_places = g_strdup (nautilus_files_view_get_view_id 
(NAUTILUS_FILES_VIEW (slot->details->content_view)));
+                }
+        } else {
+                gchar *view_id;
+
+                view_id = NULL;
+
+                /* If we are in search, try to use by default list view. This will be deactivated
+                 * if the user manually switch to a diferent view mode */
+                if (nautilus_file_is_in_search (file)) {
+                        if (g_settings_get_boolean (nautilus_preferences, 
NAUTILUS_PREFERENCES_LIST_VIEW_ON_SEARCH)) {
+                                /* If it's already set, is because we already made the change to search mode,
+                                 * so the view mode of the current view will be the one search is using,
+                                 * which is not the one we are interested in */
+                                if (slot->details->view_mode_before_search == NULL) {
+                                        slot->details->view_mode_before_search = g_strdup 
(nautilus_files_view_get_view_id (NAUTILUS_FILES_VIEW (slot->details->content_view)));
+                                }
+                                view_id = g_strdup (NAUTILUS_LIST_VIEW_IID);
+                        } else {
+                                g_free (slot->details->view_mode_before_search);
+                                slot->details->view_mode_before_search = NULL;
+                        }
+                }
+
+                /* If there is already a view, just use the view mode that it's currently using, or
+                 * if we were on search before, use what we were using before entering
+                 * search mode */
+               if (slot->details->content_view != NULL && view_id == NULL) {
+                        if (slot->details->view_mode_before_search != NULL) {
+                                view_id = slot->details->view_mode_before_search;
+                                slot->details->view_mode_before_search = NULL;
+                        } else if (NAUTILUS_IS_PLACES_VIEW (slot->details->content_view)) {
+                                view_id = slot->details->view_mode_before_places;
+                                slot->details->view_mode_before_places = NULL;
+                        } else {
+                               view_id = g_strdup (nautilus_files_view_get_view_id (NAUTILUS_FILES_VIEW 
(slot->details->content_view)));
+                        }
+               }
+
+                /* If there is not previous view in this slot, use the default view mode
+                 * from preferences */
+               if (view_id == NULL) {
+                       view_id = nautilus_global_preferences_get_default_folder_viewer_preference_as_iid ();
+               }
+
+                /* Try to reuse the current view */
+                if (nautilus_window_slot_content_view_matches (slot, view_id)) {
+                        view = slot->details->content_view;
+                } else {
+                        view = NAUTILUS_VIEW (nautilus_files_view_new (view_id, slot));
+                }
+
+                g_free (view_id);
+        }
+
+        nautilus_file_unref (file);
+
+        return g_object_ref (view);
+}
 
 static gboolean
-nautilus_window_slot_content_view_matches_iid (NautilusWindowSlot *slot,
-                                              const char *iid)
+nautilus_window_slot_content_view_matches (NautilusWindowSlot *slot,
+                                           const char         *iid)
 {
        if (slot->details->content_view == NULL) {
                return FALSE;
        }
-       return g_strcmp0 (nautilus_files_view_get_view_id (NAUTILUS_FILES_VIEW 
(slot->details->content_view)), iid) == 0;
+
+        if (!iid && NAUTILUS_IS_PLACES_VIEW (slot->details->content_view)) {
+                return TRUE;
+        } else if (iid && NAUTILUS_IS_FILES_VIEW (slot->details->content_view)){
+                return g_strcmp0 (nautilus_files_view_get_view_id (NAUTILUS_FILES_VIEW 
(slot->details->content_view)), iid) == 0;
+        } else {
+                return FALSE;
+        }
 }
 
 void
@@ -152,10 +247,11 @@ nautilus_window_slot_sync_view_mode (NautilusWindowSlot *slot)
        }
 
        action = g_action_map_lookup_action (G_ACTION_MAP (slot->details->window), "view-mode");
+        g_simple_action_set_enabled (G_SIMPLE_ACTION (action), TRUE);
 
-       if (nautilus_window_slot_content_view_matches_iid (slot, NAUTILUS_LIST_VIEW_ID)) {
+       if (nautilus_window_slot_content_view_matches (slot, NAUTILUS_LIST_VIEW_ID)) {
                g_action_change_state (action, g_variant_new_string ("list"));
-       } else if (nautilus_window_slot_content_view_matches_iid (slot, NAUTILUS_CANVAS_VIEW_ID)) {
+       } else if (nautilus_window_slot_content_view_matches (slot, NAUTILUS_CANVAS_VIEW_ID)) {
                g_action_change_state (action, g_variant_new_string ("grid"));
        } else {
                g_simple_action_set_enabled (G_SIMPLE_ACTION (action), FALSE);
@@ -486,8 +582,8 @@ static void end_location_change                       (NautilusWindowSlot
 static void cancel_location_change                    (NautilusWindowSlot         *slot);
 static void got_file_info_for_view_selection_callback (NautilusFile               *file,
                                                       gpointer                    callback_data);
-static gboolean create_content_view                   (NautilusWindowSlot         *slot,
-                                                      const char                 *view_id,
+static gboolean setup_view                            (NautilusWindowSlot         *slot,
+                                                       NautilusView               *view,
                                                       GError                    **error);
 static void load_new_location                         (NautilusWindowSlot         *slot,
                                                       GFile                      *location,
@@ -621,7 +717,7 @@ nautilus_window_slot_open_location_full (NautilusWindowSlot *slot,
        }
 
        old_selection = NULL;
-       if (slot->details->content_view != NULL) {
+        if (slot->details->content_view && NAUTILUS_IS_FILES_VIEW (slot->details->content_view)) {
                 old_selection = nautilus_files_view_get_selection (NAUTILUS_FILES_VIEW 
(slot->details->content_view));
        }
 
@@ -707,13 +803,11 @@ begin_location_change (NautilusWindowSlot *slot,
 
        nautilus_profile_start (NULL);
 
-        directory = nautilus_directory_get (location);
-
         /* Avoid to update status from the current view in our async calls */
         nautilus_window_slot_disconnect_content_view (slot);
         /* We are going to change the location, so make sure we stop any loading
          * or searching of the previous view, so we avoid to be slow */
-        if (slot->details->content_view) {
+        if (slot->details->content_view && NAUTILUS_IS_FILES_VIEW (slot->details->content_view)) {
                 nautilus_files_view_stop_loading (NAUTILUS_FILES_VIEW (slot->details->content_view));
         }
 
@@ -761,6 +855,8 @@ begin_location_change (NautilusWindowSlot *slot,
         * after determining an initial view (in the components), then
         * we end up fetching things twice.
         */
+        directory = nautilus_directory_get (location);
+
        if (type == NAUTILUS_LOCATION_CHANGE_RELOAD) {
                force_reload = TRUE;
        } else if (!nautilus_monitor_active ()) {
@@ -784,7 +880,8 @@ begin_location_change (NautilusWindowSlot *slot,
 
         /* Set current_bookmark scroll pos */
         if (slot->details->current_location_bookmark != NULL &&
-            slot->details->content_view != NULL) {
+            slot->details->content_view != NULL &&
+            NAUTILUS_IS_FILES_VIEW (slot->details->content_view)) {
                 current_pos = nautilus_files_view_get_first_visible_file (NAUTILUS_FILES_VIEW 
(slot->details->content_view));
                 nautilus_bookmark_set_scroll_pos (slot->details->current_location_bookmark, current_pos);
                 g_free (current_pos);
@@ -979,10 +1076,10 @@ got_file_info_for_view_selection_callback (NautilusFile *file,
                                           gpointer callback_data)
 {
         GError *error = NULL;
-       char *view_id;
        NautilusWindow *window;
        NautilusWindowSlot *slot;
        NautilusFile *viewed_file, *parent_file;
+        NautilusView *view;
        GFile *location, *default_location;
        GMountOperation *mount_op;
        MountNotMountedData *data;
@@ -1079,54 +1176,16 @@ got_file_info_for_view_selection_callback (NautilusFile *file,
        nautilus_file_unref (parent_file);
        location = slot->details->pending_location;
 
-       view_id = NULL;
-
-        if (error == NULL ||
-           (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_SUPPORTED)) {
-               /* We got the information we need, now pick what view to use: */
+        view = NULL;
 
-                /* If we are in search, try to use by default list view. This will be deactivated
-                 * if the user manually switch to a diferent view mode */
-                if (nautilus_file_is_in_search (nautilus_file_get (location))) {
-                        if (g_settings_get_boolean (nautilus_preferences, 
NAUTILUS_PREFERENCES_LIST_VIEW_ON_SEARCH)) {
-                                /* If it's already set, is because we already made the change to search mode,
-                                 * so the view mode of the current view will be the one search is using,
-                                 * which is not the one we are interested in */
-                                if (slot->details->view_mode_before_search == NULL) {
-                                        slot->details->view_mode_before_search = g_strdup 
(nautilus_files_view_get_view_id (NAUTILUS_FILES_VIEW (slot->details->content_view)));
-                                }
-                                view_id = g_strdup (NAUTILUS_LIST_VIEW_IID);
-                        } else {
-                                g_free (slot->details->view_mode_before_search);
-                                slot->details->view_mode_before_search = NULL;
-                        }
-                }
-
-                /* If there is already a view, just use the view mode that it's currently using, or
-                 * if we were on search before, use what we were using before entering
-                 * search mode */
-               if (slot->details->content_view != NULL && view_id == NULL) {
-                        if (slot->details->view_mode_before_search != NULL) {
-                                view_id = g_strdup (slot->details->view_mode_before_search);
-                                g_free (slot->details->view_mode_before_search);
-                                slot->details->view_mode_before_search = NULL;
-                        } else {
-                               view_id = g_strdup (nautilus_files_view_get_view_id (NAUTILUS_FILES_VIEW 
(slot->details->content_view)));
-                        }
-               }
-
-                /* If there is not previous view in this slot, use the default view mode
-                 * from preferences */
-               if (view_id == NULL) {
-                       view_id = nautilus_global_preferences_get_default_folder_viewer_preference_as_iid ();
-               }
+        if (!error || (error && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_SUPPORTED)) {
+                view = nautilus_window_slot_get_view_for_location (slot, location);
        }
 
-       if (view_id != NULL) {
+        if (view != NULL) {
                GError *err = NULL;
 
-               create_content_view (slot, view_id, &err);
-               g_free (view_id);
+                setup_view (slot, view, &err);
 
                report_callback (slot, err);
                g_clear_error (&err);
@@ -1203,7 +1262,6 @@ got_file_info_for_view_selection_callback (NautilusFile *file,
        g_clear_error (&error);
 
        nautilus_file_unref (file);
-
        nautilus_profile_end (NULL);
 }
 
@@ -1215,52 +1273,26 @@ got_file_info_for_view_selection_callback (NautilusFile *file,
  * view, and the current location will be used.
  */
 static gboolean
-create_content_view (NautilusWindowSlot *slot,
-                    const char *view_id,
-                    GError **error_out)
+setup_view (NautilusWindowSlot  *slot,
+            NautilusView        *view,
+            GError             **error_out)
 {
-       NautilusWindow *window;
-        NautilusView *view;
-       GList *selection;
        gboolean ret = TRUE;
        GError *error = NULL;
        GFile *old_location;
 
-       window = nautilus_window_slot_get_window (slot);
-
        nautilus_profile_start (NULL);
 
-       /* FIXME bugzilla.gnome.org 41243:
-        * We should use inheritance instead of these special cases
-        * for the desktop window.
-        */
-       if (NAUTILUS_IS_DESKTOP_WINDOW (window)) {
-               /* We force the desktop to use a desktop_icon_view. It's simpler
-                * to fix it here than trying to make it pick the right view in
-                * the first place.
-                */
-               view_id = NAUTILUS_DESKTOP_ICON_VIEW_IID;
-       }
-
         nautilus_window_slot_disconnect_content_view (slot);
-        if (nautilus_window_slot_content_view_matches_iid (slot, view_id)) {
-                /* reuse existing content view */
-                view = slot->details->content_view;
-                slot->details->new_content_view = view;
-               g_object_ref (view);
-        } else {
-                /* create a new content view */
-               view = NAUTILUS_VIEW (nautilus_files_view_new (view_id, slot));
 
-                slot->details->new_content_view = view;
-        }
+        slot->details->new_content_view = view;
+
         nautilus_window_slot_connect_new_content_view (slot);
 
        /* Forward search selection and state before loading the new model */
         old_location = slot->details->content_view ? nautilus_view_get_location 
(slot->details->content_view) : NULL;
 
        /* Actually load the pending location and selection: */
-
         if (slot->details->pending_location != NULL) {
                load_new_location (slot,
                                   slot->details->pending_location,
@@ -1271,7 +1303,14 @@ create_content_view (NautilusWindowSlot *slot,
                g_list_free_full (slot->details->pending_selection, g_object_unref);
                slot->details->pending_selection = NULL;
        } else if (old_location != NULL) {
-               selection = nautilus_files_view_get_selection (NAUTILUS_FILES_VIEW 
(slot->details->content_view));
+                GList *selection;
+
+                selection = NULL;
+
+                if (NAUTILUS_IS_FILES_VIEW (slot->details->content_view)) {
+                        selection = nautilus_files_view_get_selection (NAUTILUS_FILES_VIEW 
(slot->details->content_view));
+                }
+
                load_new_location (slot,
                                   old_location,
                                   selection,
@@ -1302,13 +1341,11 @@ load_new_location (NautilusWindowSlot *slot,
                   gboolean tell_current_content_view,
                   gboolean tell_new_content_view)
 {
-       GList *selection_copy;
        NautilusView *view;
 
        g_assert (slot != NULL);
        g_assert (location != NULL);
 
-       selection_copy = g_list_copy_deep (selection, (GCopyFunc) g_object_ref, NULL);
        view = NULL;
 
        nautilus_profile_start (NULL);
@@ -1324,13 +1361,17 @@ load_new_location (NautilusWindowSlot *slot,
                view = slot->details->new_content_view;
                nautilus_view_set_location (slot->details->new_content_view, location);
         }
-       if (view != NULL) {
+        if (view && NAUTILUS_IS_FILES_VIEW (view)) {
+                GList *selection_copy;
+
+                selection_copy = g_list_copy_deep (selection, (GCopyFunc) g_object_ref, NULL);
+
                /* new_content_view might have changed here if
                   report_load_underway was called from load_location */
                nautilus_files_view_set_selection (NAUTILUS_FILES_VIEW (view), selection_copy);
-       }
 
-       g_list_free_full (selection_copy, g_object_unref);
+                g_list_free_full (selection_copy, g_object_unref);
+       }
 
        nautilus_profile_end (NULL);
 }
@@ -1402,7 +1443,8 @@ cancel_location_change (NautilusWindowSlot *slot)
 
         if (slot->details->pending_location != NULL
             && location != NULL
-            && slot->details->content_view != NULL) {
+            && slot->details->content_view != NULL
+            && NAUTILUS_IS_FILES_VIEW (slot->details->content_view)) {
 
                 /* No need to tell the new view - either it is the
                  * same as the old view, in which case it will already
@@ -1492,7 +1534,7 @@ void
 nautilus_window_slot_set_content_view (NautilusWindowSlot *slot,
                                       const char *id)
 {
-        GList *selection;
+        NautilusFilesView *view;
        char *uri;
 
        g_assert (slot != NULL);
@@ -1502,29 +1544,36 @@ nautilus_window_slot_set_content_view (NautilusWindowSlot *slot,
        DEBUG ("Change view of window %s to %s", uri, id);
        g_free (uri);
 
-       if (nautilus_window_slot_content_view_matches_iid (slot, id)) {
+       if (nautilus_window_slot_content_view_matches (slot, id)) {
                return;
         }
 
+        view = nautilus_files_view_new (id, slot);
+
         end_location_change (slot);
 
         nautilus_window_slot_set_allow_stop (slot, TRUE);
 
-        selection = nautilus_files_view_get_selection (NAUTILUS_FILES_VIEW (slot->details->content_view));
+        if (NAUTILUS_IS_FILES_VIEW (slot->details->content_view)) {
+                GList *selection;
+
+                selection = nautilus_files_view_get_selection (NAUTILUS_FILES_VIEW 
(slot->details->content_view));
 
-        if (g_list_length (selection) == 0) {
-                /* If there is no selection, queue a scroll to the same icon that
-                 * is currently visible */
-                slot->details->pending_scroll_to = nautilus_files_view_get_first_visible_file 
(NAUTILUS_FILES_VIEW (slot->details->content_view));
+                if (g_list_length (selection) == 0) {
+                        /* If there is no selection, queue a scroll to the same icon that
+                         * is currently visible */
+                        slot->details->pending_scroll_to = nautilus_files_view_get_first_visible_file 
(NAUTILUS_FILES_VIEW (slot->details->content_view));
+                }
+
+                nautilus_file_list_free (selection);
         }
+
        slot->details->location_change_type = NAUTILUS_LOCATION_CHANGE_RELOAD;
 
-        if (!create_content_view (slot, id, NULL)) {
+        if (!setup_view (slot, NAUTILUS_VIEW (view), NULL)) {
                /* Just load the homedir. */
                nautilus_window_slot_go_home (slot, FALSE);
        }
-
-        nautilus_file_list_free (selection);
 }
 
 void
@@ -1599,7 +1648,7 @@ nautilus_window_slot_force_reload (NautilusWindowSlot *slot)
        g_object_ref (location);
        current_pos = NULL;
        selection = NULL;
-       if (slot->details->content_view != NULL) {
+        if (slot->details->new_content_view && NAUTILUS_IS_FILES_VIEW (slot->details->new_content_view)) {
                current_pos = nautilus_files_view_get_first_visible_file (NAUTILUS_FILES_VIEW 
(slot->details->content_view));
                selection = nautilus_files_view_get_selection (NAUTILUS_FILES_VIEW 
(slot->details->content_view));
        }
@@ -2105,16 +2154,27 @@ nautilus_window_slot_setup_extra_location_widgets (NautilusWindowSlot *slot)
 static void
 nautilus_window_slot_connect_new_content_view (NautilusWindowSlot *slot)
 {
-       if (slot->details->new_content_view != NULL) {
-               g_signal_connect (slot->details->new_content_view, "begin-loading", G_CALLBACK 
(view_begin_loading_cb), slot);
-               g_signal_connect (slot->details->new_content_view, "end-loading", G_CALLBACK 
(view_end_loading_cb), slot);
-       }
+        if (slot->details->new_content_view) {
+                if (NAUTILUS_IS_FILES_VIEW (slot->details->new_content_view)) {
+                        g_signal_connect (slot->details->new_content_view, "begin-loading", G_CALLBACK 
(view_begin_loading_cb), slot);
+                        g_signal_connect (slot->details->new_content_view, "end-loading", G_CALLBACK 
(view_end_loading_cb), slot);
+                } else {
+                        /*
+                         * When it's not a files-view, we never call view_begin_loading_cb, which
+                         * causes the window to never be shown, the cursor never gets updated and
+                         * stuff, so we have to simulate it here.
+                         */
+                        gtk_widget_show (GTK_WIDGET (slot->details->window));
+                        location_has_really_changed (slot);
+                        end_location_change (slot);
+                }
+        }
 }
 
 static void
 nautilus_window_slot_disconnect_content_view (NautilusWindowSlot *slot)
 {
-       if (slot->details->content_view != NULL) {
+        if (slot->details->new_content_view && NAUTILUS_IS_FILES_VIEW (slot->details->new_content_view)) {
                /* disconnect old view */
                g_signal_handlers_disconnect_by_func (slot->details->content_view, G_CALLBACK 
(view_end_loading_cb), slot);
                g_signal_handlers_disconnect_by_func (slot->details->content_view, G_CALLBACK 
(view_begin_loading_cb), slot);
diff --git a/src/nautilus-window.c b/src/nautilus-window.c
index 4ccc96d..0432d57 100644
--- a/src/nautilus-window.c
+++ b/src/nautilus-window.c
@@ -986,6 +986,18 @@ places_sidebar_show_connect_to_server_cb (GtkPlacesSidebar *sidebar,
        gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
 }
 
+static void
+places_sidebar_show_other_locations (NautilusWindow   *window)
+{
+        GFile *location;
+
+        location = g_file_new_for_uri ("other-locations://");
+
+        open_location_cb (window, location, GTK_PLACES_OPEN_NORMAL);
+
+        g_object_unref (location);
+}
+
 static GList *
 build_selection_list_from_gfile_list (GList *gfile_list)
 {
@@ -1271,7 +1283,6 @@ nautilus_window_set_up_sidebar (NautilusWindow *window)
                                           (GTK_PLACES_OPEN_NORMAL
                                            | GTK_PLACES_OPEN_NEW_TAB
                                            | GTK_PLACES_OPEN_NEW_WINDOW));
-       gtk_places_sidebar_set_show_connect_to_server (GTK_PLACES_SIDEBAR (window->priv->places_sidebar), 
TRUE);
 
         g_signal_connect_swapped (window->priv->places_sidebar, "open-location",
                                   G_CALLBACK (open_location_cb), window);
@@ -2534,6 +2545,8 @@ nautilus_window_class_init (NautilusWindowClass *class)
        gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_operation_open);
        gtk_widget_class_bind_template_child_private (wclass, NautilusWindow, notification_operation_close);
 
+        gtk_widget_class_bind_template_callback (wclass, places_sidebar_show_other_locations);
+
        properties[PROP_DISABLE_CHROME] =
                g_param_spec_boolean ("disable-chrome",
                                      "Disable chrome",
diff --git a/src/nautilus-window.ui b/src/nautilus-window.ui
index 7e7a7f2..b23e3a5 100644
--- a/src/nautilus-window.ui
+++ b/src/nautilus-window.ui
@@ -33,6 +33,8 @@
                   <object class="GtkPlacesSidebar" id="places_sidebar">
                     <property name="visible">True</property>
                     <property name="populate-all">True</property>
+                    <property name="show-other-locations">True</property>
+                    <signal name="show-other-locations" handler="places_sidebar_show_other_locations" 
object="NautilusWindow" swapped="yes" />
                   </object>
                   <packing>
                     <property name="pack_type">start</property>
diff --git a/src/nautilus.gresource.xml b/src/nautilus.gresource.xml
index 5a3e324..c663e73 100644
--- a/src/nautilus.gresource.xml
+++ b/src/nautilus.gresource.xml
@@ -16,6 +16,8 @@
     <file>nautilus-window.ui</file>
     <file>nautilus-no-search-results.ui</file>
     <file>nautilus-folder-is-empty.ui</file>
+    <file>gtk/gtkplacesview.ui</file>
+    <file>gtk/gtkplacesviewrow.ui</file>
     <file alias="icons/thumbnail_frame.png">../icons/thumbnail_frame.png</file>
     <file alias="icons/filmholes.png">../icons/filmholes.png</file>
     <file alias="icons/knob.png">../icons/knob.png</file>


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