From 349b7392a5ac05e4145d3c060bf0e2b05dfe5064 Mon Sep 17 00:00:00 2001 From: Carlos Soriano Date: Wed, 9 Mar 2016 01:36:44 +0100 Subject: placesview: autogenerate from gtk+ repo Instead of manual copying and replacing symbols, use a script to copy from gtk+ repo and replace any symbol or problematic part. --- src/Makefile.am | 8 +- src/gtk/gtkplacesview.c | 2528 ----------------------------- src/gtk/gtkplacesview.ui | 287 ---- src/gtk/gtkplacesviewprivate.h | 82 - src/gtk/gtkplacesviewrow.c | 467 ------ src/gtk/gtkplacesviewrow.ui | 121 -- src/gtk/gtkplacesviewrowprivate.h | 59 - src/gtk/nautilusgtkplacesview.c | 2512 ++++++++++++++++++++++++++++ src/gtk/nautilusgtkplacesview.ui | 290 ++++ src/gtk/nautilusgtkplacesviewprivate.h | 87 + src/gtk/nautilusgtkplacesviewrow.c | 490 ++++++ src/gtk/nautilusgtkplacesviewrow.ui | 100 ++ src/gtk/nautilusgtkplacesviewrowprivate.h | 64 + src/gtk/symbolconverter.sh | 61 + src/nautilus-places-view.c | 10 +- src/resources/nautilus.gresource.xml | 4 +- 16 files changed, 3615 insertions(+), 3555 deletions(-) delete mode 100644 src/gtk/gtkplacesview.c delete mode 100644 src/gtk/gtkplacesview.ui delete mode 100644 src/gtk/gtkplacesviewprivate.h delete mode 100644 src/gtk/gtkplacesviewrow.c delete mode 100644 src/gtk/gtkplacesviewrow.ui delete mode 100644 src/gtk/gtkplacesviewrowprivate.h create mode 100644 src/gtk/nautilusgtkplacesview.c create mode 100644 src/gtk/nautilusgtkplacesview.ui create mode 100644 src/gtk/nautilusgtkplacesviewprivate.h create mode 100644 src/gtk/nautilusgtkplacesviewrow.c create mode 100644 src/gtk/nautilusgtkplacesviewrow.ui create mode 100644 src/gtk/nautilusgtkplacesviewrowprivate.h create mode 100755 src/gtk/symbolconverter.sh diff --git a/src/Makefile.am b/src/Makefile.am index 40329ceb2..608e6a675 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -135,10 +135,10 @@ nautilus_built_sources = \ $(NULL) nautilus_SOURCES = \ - gtk/gtkplacesview.c \ - gtk/gtkplacesviewprivate.h \ - gtk/gtkplacesviewrow.c \ - gtk/gtkplacesviewrowprivate.h \ + gtk/nautilusgtkplacesview.c \ + gtk/nautilusgtkplacesviewprivate.h \ + gtk/nautilusgtkplacesviewrow.c \ + gtk/nautilusgtkplacesviewrowprivate.h \ nautilus-application.c \ nautilus-application.h \ nautilus-bookmark-list.c \ diff --git a/src/gtk/gtkplacesview.c b/src/gtk/gtkplacesview.c deleted file mode 100644 index e1f27b238..000000000 --- a/src/gtk/gtkplacesview.c +++ /dev/null @@ -1,2528 +0,0 @@ -/* gtkplacesview.c - * - * Copyright (C) 2015 Georges Basile Stavracas Neto - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include "config.h" - -#include -#include -#include - -#include "gtkplacesviewprivate.h" -#include "gtkplacesviewrowprivate.h" - -/** - * SECTION:gtkplacesview - * @Short_description: Widget that displays persistent drives and manages mounted networks - * @Title: GtkNautilusPlacesView - * @See_also: #GtkFileChooser - * - * #GtkNautilusPlacesView is a stock widget that displays a list of persistent drives - * such as harddisk partitions and networks. #GtkNautilusPlacesView 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 #GtkNautilusPlacesView::open-location signal. This is emitted when the user - * selects a location to open in the view. - */ - -struct _GtkNautilusPlacesViewPrivate -{ - 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_placeholder; - GtkWidget *network_placeholder_label; - - GtkSizeGroup *path_size_group; - GtkSizeGroup *space_size_group; - - GtkEntryCompletion *address_entry_completion; - GtkListStore *completion_store; - - 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 mounting_volume : 1; - guint unmounting_mount : 1; - guint fetching_networks : 1; - guint loading : 1; -}; - -static void mount_volume (GtkNautilusPlacesView *view, - GVolume *volume); - -static gboolean on_button_press_event (GtkNautilusPlacesViewRow *row, - GdkEventButton *event); - -static void on_eject_button_clicked (GtkWidget *widget, - GtkNautilusPlacesViewRow *row); - -static gboolean on_row_popup_menu (GtkNautilusPlacesViewRow *row); - -static void populate_servers (GtkNautilusPlacesView *view); - -static gboolean gtk_nautilus_places_view_get_fetching_networks (GtkNautilusPlacesView *view); - -static void gtk_nautilus_places_view_set_fetching_networks (GtkNautilusPlacesView *view, - gboolean fetching_networks); - -static void gtk_nautilus_places_view_set_loading (GtkNautilusPlacesView *view, - gboolean loading); - -static void update_loading (GtkNautilusPlacesView *view); - -G_DEFINE_TYPE_WITH_PRIVATE (GtkNautilusPlacesView, gtk_nautilus_places_view, GTK_TYPE_BOX) - -/* GtkNautilusPlacesView properties & signals */ -enum { - PROP_0, - PROP_LOCAL_ONLY, - PROP_OPEN_FLAGS, - PROP_FETCHING_NETWORKS, - PROP_LOADING, - 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 nautilus_places_view_signals [LAST_SIGNAL] = { 0 }; -static GParamSpec *properties [LAST_PROP]; - -static void -emit_open_location (GtkNautilusPlacesView *view, - GFile *location, - GtkPlacesOpenFlags open_flags) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_places_view_get_instance_private (view); - - if ((open_flags & priv->open_flags) == 0) - open_flags = GTK_PLACES_OPEN_NORMAL; - - g_signal_emit (view, nautilus_places_view_signals[OPEN_LOCATION], 0, location, open_flags); -} - -static void -emit_show_error_message (GtkNautilusPlacesView *view, - gchar *primary_message, - gchar *secondary_message) -{ - g_signal_emit (view, nautilus_places_view_signals[SHOW_ERROR_MESSAGE], - 0, primary_message, secondary_message); -} - -static void -server_file_changed_cb (GtkNautilusPlacesView *view) -{ - populate_servers (view); -} - -static GBookmarkFile * -server_list_load (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - GBookmarkFile *bookmarks; - GError *error = NULL; - gchar *datadir; - gchar *filename; - - priv = gtk_nautilus_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 (GtkNautilusPlacesView *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 (GtkNautilusPlacesView *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 (GtkNautilusPlacesView *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, "progress"); - 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 (GtkNautilusPlacesView *view, - GtkNautilusPlacesViewRow *row, - GtkPlacesOpenFlags flags) -{ - GtkNautilusPlacesViewPrivate *priv; - GVolume *volume; - GMount *mount; - GFile *file; - - priv = gtk_nautilus_places_view_get_instance_private (view); - mount = gtk_nautilus_places_view_row_get_mount (row); - volume = gtk_nautilus_places_view_row_get_volume (row); - file = gtk_nautilus_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_nautilus_places_view_row_set_busy (row, TRUE); - mount_volume (view, volume); - } -} - -static void update_places (GtkNautilusPlacesView *view); - -static void -gtk_nautilus_places_view_finalize (GObject *object) -{ - GtkNautilusPlacesView *self = (GtkNautilusPlacesView *)object; - GtkNautilusPlacesViewPrivate *priv = gtk_nautilus_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_clear_object (&priv->path_size_group); - g_clear_object (&priv->space_size_group); - - G_OBJECT_CLASS (gtk_nautilus_places_view_parent_class)->finalize (object); -} - -static void -gtk_nautilus_places_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkNautilusPlacesView *self = GTK_NAUTILUS_PLACES_VIEW (object); - - switch (prop_id) - { - case PROP_LOCAL_ONLY: - g_value_set_boolean (value, gtk_nautilus_places_view_get_local_only (self)); - break; - - case PROP_LOADING: - g_value_set_boolean (value, gtk_nautilus_places_view_get_loading (self)); - break; - - case PROP_FETCHING_NETWORKS: - g_value_set_boolean (value, gtk_nautilus_places_view_get_fetching_networks (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_nautilus_places_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkNautilusPlacesView *self = GTK_NAUTILUS_PLACES_VIEW (object); - - switch (prop_id) - { - case PROP_LOCAL_ONLY: - gtk_nautilus_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_external_volume (GVolume *volume) -{ - gboolean is_external; - GDrive *drive; - gchar *id; - - drive = g_volume_get_drive (volume); - id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); - - is_external = g_volume_can_eject (volume); - - /* NULL volume identifier only happens on removable devices */ - is_external |= !id; - - if (drive) - is_external |= g_drive_can_eject (drive) || - g_drive_is_media_removable (drive) || - g_drive_can_stop (drive); - - g_clear_object (&drive); - g_free (id); - - return is_external; -} - -typedef struct -{ - gchar *uri; - GtkNautilusPlacesView *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 (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - GBookmarkFile *server_list; - GList *children; - gchar **uris; - gsize num_uris; - gint i; - - priv = gtk_nautilus_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", 3, - 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 (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - GList *children; - GList *l; - gboolean show_listbox; - - priv = gtk_nautilus_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 (GtkNautilusPlacesView *view, - GtkWidget *row, - gboolean is_network) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_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_nautilus_places_view_row_get_event_box (GTK_NAUTILUS_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_nautilus_places_view_row_get_eject_button (GTK_NAUTILUS_PLACES_VIEW_ROW (row)), - "clicked", - G_CALLBACK (on_eject_button_clicked), - row); - - gtk_nautilus_places_view_row_set_path_size_group (GTK_NAUTILUS_PLACES_VIEW_ROW (row), priv->path_size_group); - gtk_nautilus_places_view_row_set_space_size_group (GTK_NAUTILUS_PLACES_VIEW_ROW (row), priv->space_size_group); - - gtk_container_add (GTK_CONTAINER (priv->listbox), row); -} - -static void -add_volume (GtkNautilusPlacesView *view, - GVolume *volume) -{ - gboolean is_network; - GMount *mount; - GFile *root; - GIcon *icon; - gchar *identifier; - gchar *name; - gchar *path; - - if (is_external_volume (volume)) - 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_NAUTILUS_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 (GtkNautilusPlacesView *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_NAUTILUS_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 (GtkNautilusPlacesView *view, - GDrive *drive) -{ - GList *volumes; - GList *l; - - 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 (GtkNautilusPlacesView *view, - GFile *file, - GIcon *icon, - const gchar *display_name, - const gchar *path, - gboolean is_network) -{ - GtkWidget *row; - row = g_object_new (GTK_TYPE_NAUTILUS_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 (GtkNautilusPlacesView *view) -{ - GList *l; - GtkNautilusPlacesViewPrivate *priv; - GList *children; - gboolean has_network = FALSE; - - priv = gtk_nautilus_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 (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_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); - } - - if (gtk_nautilus_places_view_get_fetching_networks (view)) - { - /* 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_widget_show_all (priv->network_placeholder); - gtk_label_set_text (GTK_LABEL (priv->network_placeholder_label), - _("No network locations found")); - } - else - { - gtk_widget_hide (priv->network_placeholder); - } -} - -static void -populate_networks (GtkNautilusPlacesView *view, - GFileEnumerator *enumerator, - GList *detected_networks) -{ - GList *l; - GFile *file; - GFile *activatable_file; - gchar *uri; - GFileType type; - GIcon *icon; - gchar *display_name; - - for (l = detected_networks; l != NULL; l = l->next) - { - file = g_file_enumerator_get_child (enumerator, l->data); - type = g_file_info_get_file_type (l->data); - if (type == G_FILE_TYPE_SHORTCUT || type == G_FILE_TYPE_MOUNTABLE) - uri = g_file_info_get_attribute_as_string (l->data, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); - else - uri = g_file_get_uri (file); - activatable_file = g_file_new_for_uri (uri); - 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, activatable_file, icon, display_name, NULL, TRUE); - - g_free (uri); - g_free (display_name); - g_clear_object (&file); - g_clear_object (&activatable_file); - } -} - -static void -network_enumeration_next_files_finished (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GtkNautilusPlacesViewPrivate *priv; - GtkNautilusPlacesView *view; - GList *detected_networks; - GError *error; - - view = GTK_NAUTILUS_PLACES_VIEW (user_data); - priv = gtk_nautilus_places_view_get_instance_private (view); - error = NULL; - - detected_networks = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source_object), - res, &error); - - if (error) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("Failed to fetch network locations: %s", error->message); - - g_clear_error (&error); - } - else - { - gtk_nautilus_places_view_set_fetching_networks (view, FALSE); - populate_networks (view, G_FILE_ENUMERATOR (source_object), detected_networks); - - g_list_free_full (detected_networks, g_object_unref); - } - - /* avoid to update widgets if the operation was cancelled in finalize */ - if (priv->listbox != NULL) - { - update_network_state (view); - update_loading (view); - } -} - -static void -network_enumeration_finished (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GtkNautilusPlacesViewPrivate *priv; - GFileEnumerator *enumerator; - GError *error; - - error = NULL; - enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, &error); - - if (error) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && - !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) - g_warning ("Failed to fetch network locations: %s", error->message); - - g_clear_error (&error); - } - else - { - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_PLACES_VIEW (user_data)); - 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 (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - GFile *network_file; - - priv = gtk_nautilus_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 (); - gtk_nautilus_places_view_set_fetching_networks (view, TRUE); - update_network_state (view); - - g_file_enumerate_children_async (network_file, - "standard::type,standard::target-uri,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 (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - GList *children; - GList *mounts; - GList *volumes; - GList *drives; - GList *l; - GIcon *icon; - GFile *file; - - priv = gtk_nautilus_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; - /* Inform clients that we started loading */ - gtk_nautilus_places_view_set_loading (view, TRUE); - - /* 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 */ - if (!gtk_nautilus_places_view_get_local_only (view)) - fetch_networks (view); - - update_view_mode (view); - /* Check whether we still are in a loading state */ - update_loading (view); -} - -static void -server_mount_ready_cb (GObject *source_file, - GAsyncResult *res, - gpointer user_data) -{ - GtkNautilusPlacesViewPrivate *priv; - GtkNautilusPlacesView *view; - gboolean should_show; - GError *error; - GFile *location; - - location = G_FILE (source_file); - should_show = TRUE; - error = NULL; - - g_file_mount_enclosing_volume_finish (location, res, &error); - 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)) - { - view = GTK_NAUTILUS_PLACES_VIEW (user_data); - /* if it wasn't cancelled show a dialog */ - emit_show_error_message (view, _("Unable to access location"), error->message); - should_show = FALSE; - } - else - { - /* it was cancelled, so probably it was called during finalize, bail out. */ - g_clear_error (&error); - return; - } - g_clear_error (&error); - } - - view = GTK_NAUTILUS_PLACES_VIEW (user_data); - priv = gtk_nautilus_places_view_get_instance_private (view); - priv->should_pulse_entry = FALSE; - - /* 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; - update_loading (view); - - 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; - - 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; - } - - emit_open_location (view, location, priv->open_flags); - } - } - -out: - update_places (view); -} - -static void -volume_mount_ready_cb (GObject *source_volume, - GAsyncResult *res, - gpointer user_data) -{ - GtkNautilusPlacesViewPrivate *priv; - GtkNautilusPlacesView *view; - gboolean should_show; - GVolume *volume; - GError *error; - - volume = G_VOLUME (source_volume); - should_show = TRUE; - error = NULL; - - 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_NAUTILUS_PLACES_VIEW (user_data), _("Unable to access location"), error->message); - should_show = FALSE; - } - else - { - /* it was cancelled, so probably it was called during finalize, bail out. */ - g_clear_error (&error); - return; - } - - g_clear_error (&error); - } - - view = GTK_NAUTILUS_PLACES_VIEW (user_data); - priv = gtk_nautilus_places_view_get_instance_private (view); - priv->mounting_volume = FALSE; - update_loading (view); - - 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_NAUTILUS_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) -{ - GtkNautilusPlacesView *view; - GtkNautilusPlacesViewPrivate *priv; - GMount *mount; - GError *error; - - view = GTK_NAUTILUS_PLACES_VIEW (user_data); - mount = G_MOUNT (source_mount); - error = NULL; - - priv = gtk_nautilus_places_view_get_instance_private (view); - priv->unmounting_mount = FALSE; - update_loading (view); - - 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) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_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 (GtkNautilusPlacesView *view, - GMount *mount) -{ - GtkNautilusPlacesViewPrivate *priv; - GMountOperation *operation; - GtkWidget *toplevel; - - priv = gtk_nautilus_places_view_get_instance_private (view); - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view)); - operation = gtk_mount_operation_new (GTK_WINDOW (toplevel)); - - g_cancellable_cancel (priv->cancellable); - g_clear_object (&priv->cancellable); - priv->cancellable = g_cancellable_new (); - - priv->unmounting_mount = TRUE; - update_loading (view); - - 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 (GtkNautilusPlacesView *view, - GFile *location) -{ - GtkNautilusPlacesViewPrivate *priv; - GMountOperation *operation; - GtkWidget *toplevel; - - priv = gtk_nautilus_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)); - - 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; - update_loading (view); - - 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 (GtkNautilusPlacesView *view, - GVolume *volume) -{ - GtkNautilusPlacesViewPrivate *priv; - GMountOperation *operation; - GtkWidget *toplevel; - - priv = gtk_nautilus_places_view_get_instance_private (view); - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view)); - operation = gtk_mount_operation_new (GTK_WINDOW (toplevel)); - - g_cancellable_cancel (priv->cancellable); - g_clear_object (&priv->cancellable); - priv->cancellable = g_cancellable_new (); - - priv->mounting_volume = TRUE; - update_loading (view); - - 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) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_PLACES_VIEW (attach_widget)); - priv->popup_menu = NULL; -} - -static void -get_view_and_file (GtkNautilusPlacesViewRow *row, - GtkWidget **view, - GFile **file) -{ - if (view) - *view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_NAUTILUS_PLACES_VIEW); - - if (file) - { - GVolume *volume; - GMount *mount; - - volume = gtk_nautilus_places_view_row_get_volume (row); - mount = gtk_nautilus_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, - GtkNautilusPlacesViewRow *row) -{ - GtkWidget *view; - GFile *file; - - get_view_and_file (row, &view, &file); - - if (file) - emit_open_location (GTK_NAUTILUS_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NORMAL); - - g_clear_object (&file); -} - -static void -open_in_new_tab_cb (GtkMenuItem *item, - GtkNautilusPlacesViewRow *row) -{ - GtkWidget *view; - GFile *file; - - get_view_and_file (row, &view, &file); - - if (file) - emit_open_location (GTK_NAUTILUS_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_TAB); - - g_clear_object (&file); -} - -static void -open_in_new_window_cb (GtkMenuItem *item, - GtkNautilusPlacesViewRow *row) -{ - GtkWidget *view; - GFile *file; - - get_view_and_file (row, &view, &file); - - if (file) - emit_open_location (GTK_NAUTILUS_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_WINDOW); - - g_clear_object (&file); -} - -static void -mount_cb (GtkMenuItem *item, - GtkNautilusPlacesViewRow *row) -{ - GtkNautilusPlacesViewPrivate *priv; - GtkWidget *view; - GVolume *volume; - - view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_NAUTILUS_PLACES_VIEW); - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_PLACES_VIEW (view)); - volume = gtk_nautilus_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_nautilus_places_view_row_set_busy (row, TRUE); - mount_volume (GTK_NAUTILUS_PLACES_VIEW (view), volume); -} - -static void -unmount_cb (GtkMenuItem *item, - GtkNautilusPlacesViewRow *row) -{ - GtkWidget *view; - GMount *mount; - - view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_NAUTILUS_PLACES_VIEW); - mount = gtk_nautilus_places_view_row_get_mount (row); - - gtk_nautilus_places_view_row_set_busy (row, TRUE); - - unmount_mount (GTK_NAUTILUS_PLACES_VIEW (view), mount); -} - -/* Constructs the popup menu if needed */ -static void -build_popup_menu (GtkNautilusPlacesView *view, - GtkNautilusPlacesViewRow *row) -{ - GtkNautilusPlacesViewPrivate *priv; - GtkWidget *item; - GMount *mount; - GFile *file; - gboolean is_network; - - priv = gtk_nautilus_places_view_get_instance_private (view); - mount = gtk_nautilus_places_view_row_get_mount (row); - file = gtk_nautilus_places_view_row_get_file (row); - is_network = gtk_nautilus_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 (GtkNautilusPlacesViewRow *row, - GdkEventButton *event) -{ - GtkNautilusPlacesViewPrivate *priv; - GtkWidget *view; - gint button; - - view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_NAUTILUS_PLACES_VIEW); - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_PLACES_VIEW (view)); - - g_clear_pointer (&priv->popup_menu, gtk_widget_destroy); - - build_popup_menu (GTK_NAUTILUS_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 (GtkNautilusPlacesViewRow *row) -{ - popup_menu (row, NULL); - return TRUE; -} - -static gboolean -on_button_press_event (GtkNautilusPlacesViewRow *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, - GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_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_NAUTILUS_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_NAUTILUS_PLACES_VIEW_ROW (focus_widget), priv->current_open_flags); - - return TRUE; - } - } - - return FALSE; -} - -static void -on_eject_button_clicked (GtkWidget *widget, - GtkNautilusPlacesViewRow *row) -{ - if (row) - { - GtkWidget *view = gtk_widget_get_ancestor (GTK_WIDGET (row), GTK_TYPE_NAUTILUS_PLACES_VIEW); - - unmount_mount (GTK_NAUTILUS_PLACES_VIEW (view), gtk_nautilus_places_view_row_get_mount (row)); - } -} - -static void -on_connect_button_clicked (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - const gchar *uri; - GFile *file; - - priv = gtk_nautilus_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 (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - const gchar* const *supported_protocols; - gchar *address, *scheme; - gboolean supported; - - priv = gtk_nautilus_places_view_get_instance_private (view); - supported = FALSE; - supported_protocols = g_vfs_get_supported_uri_schemes (g_vfs_get_default ()); - address = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->address_entry))); - scheme = g_uri_parse_scheme (address); - - if (strlen (address) > 0) - gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->address_entry), - GTK_ENTRY_ICON_SECONDARY, - "edit-clear-symbolic"); - else - gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->address_entry), - GTK_ENTRY_ICON_SECONDARY, - NULL); - - if (!supported_protocols) - goto out; - - 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); - g_free (scheme); -} - -static void -on_address_entry_clear_pressed (GtkNautilusPlacesView *view, - GtkEntryIconPosition icon_pos, - GdkEvent *event, - GtkEntry *entry) -{ - gtk_entry_set_text (entry, ""); -} - -static void -on_recent_servers_listbox_row_activated (GtkNautilusPlacesView *view, - GtkNautilusPlacesViewRow *row, - GtkWidget *listbox) -{ - GtkNautilusPlacesViewPrivate *priv; - gchar *uri; - - priv = gtk_nautilus_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 (GtkNautilusPlacesView *view, - GtkNautilusPlacesViewRow *row, - GtkWidget *listbox) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_places_view_get_instance_private (view); - - activate_row (view, row, priv->current_open_flags); -} - -static gboolean -listbox_filter_func (GtkListBoxRow *row, - gpointer user_data) -{ - GtkNautilusPlacesViewPrivate *priv; - gboolean is_network; - gboolean is_placeholder; - gboolean retval; - gboolean searching; - gchar *name; - gchar *path; - - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_PLACES_VIEW (user_data)); - retval = FALSE; - searching = priv->search_query && priv->search_query[0] != '\0'; - - is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-network")); - is_placeholder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-placeholder")); - - if (is_network && priv->local_only) - return FALSE; - - if (is_placeholder && searching) - return FALSE; - - if (!searching) - 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) -{ - gboolean row_is_network; - gchar *text; - - text = NULL; - row_is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-network")); - - if (!before) - { - text = g_strdup_printf ("%s", 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 ("%s", row_is_network ? _("Networks") : _("On This Computer")); - } - - if (text) - { - GtkWidget *header; - GtkWidget *label; - GtkWidget *separator; - - header = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - gtk_widget_set_margin_top (header, 6); - - separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); - - label = g_object_new (GTK_TYPE_LABEL, - "use_markup", TRUE, - "margin-start", 12, - "label", text, - "xalign", 0.0f, - NULL); - if (row_is_network) - { - GtkWidget *header_name; - GtkWidget *network_header_spinner; - - g_object_set (label, - "margin-end", 6, - NULL); - - header_name = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - network_header_spinner = gtk_spinner_new (); - g_object_set (network_header_spinner, - "margin-end", 12, - NULL); - g_object_bind_property (GTK_NAUTILUS_PLACES_VIEW (user_data), - "fetching-networks", - network_header_spinner, - "active", - G_BINDING_SYNC_CREATE); - - gtk_container_add (GTK_CONTAINER (header_name), label); - gtk_container_add (GTK_CONTAINER (header_name), 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 *path1; - gchar *path2; - 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", &path1, NULL); - g_object_get (row2, "path", &path2, NULL); - - retval = g_utf8_collate (path1, path2); - - g_free (path1); - g_free (path2); - - return retval; -} - -static void -gtk_nautilus_places_view_constructed (GObject *object) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_PLACES_VIEW (object)); - - G_OBJECT_CLASS (gtk_nautilus_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_NAUTILUS_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_nautilus_places_view_map (GtkWidget *widget) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_places_view_get_instance_private (GTK_NAUTILUS_PLACES_VIEW (widget)); - - gtk_entry_set_text (GTK_ENTRY (priv->address_entry), ""); - - GTK_WIDGET_CLASS (gtk_nautilus_places_view_parent_class)->map (widget); -} - -static void -gtk_nautilus_places_view_class_init (GtkNautilusPlacesViewClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = gtk_nautilus_places_view_finalize; - object_class->constructed = gtk_nautilus_places_view_constructed; - object_class->get_property = gtk_nautilus_places_view_get_property; - object_class->set_property = gtk_nautilus_places_view_set_property; - - widget_class->map = gtk_nautilus_places_view_map; - - /** - * GtkNautilusPlacesView::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 - */ - nautilus_places_view_signals [OPEN_LOCATION] = - g_signal_new ("open-location", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - G_STRUCT_OFFSET (GtkNautilusPlacesViewClass, open_location), - NULL, NULL, NULL, - G_TYPE_NONE, 2, - G_TYPE_OBJECT, - GTK_TYPE_PLACES_OPEN_FLAGS); - - /** - * GtkNautilusPlacesView::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 - */ - nautilus_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 (GtkNautilusPlacesViewClass, 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_LOADING] = - g_param_spec_boolean ("loading", - "Loading", - "Whether the view is loading locations", - FALSE, - G_PARAM_READABLE); - - properties[PROP_FETCHING_NETWORKS] = - g_param_spec_boolean ("fetching-networks", - "Fetching networks", - "Whether the view is fetching networks", - FALSE, - G_PARAM_READABLE); - - 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/gnome/nautilus/gtk/gtkplacesview.ui"); - - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, actionbar); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, address_entry); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, address_entry_completion); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, completion_store); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, connect_button); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, listbox); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, recent_servers_listbox); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, recent_servers_popover); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, recent_servers_stack); - gtk_widget_class_bind_template_child_private (widget_class, GtkNautilusPlacesView, stack); - - gtk_widget_class_bind_template_callback (widget_class, on_address_entry_text_changed); - gtk_widget_class_bind_template_callback (widget_class, on_address_entry_clear_pressed); - 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); - - gtk_widget_class_set_css_name (widget_class, "placesview"); -} - -static void -gtk_nautilus_places_view_init (GtkNautilusPlacesView *self) -{ - GtkNautilusPlacesViewPrivate *priv; - - priv = gtk_nautilus_places_view_get_instance_private (self); - - priv->volume_monitor = g_volume_monitor_get (); - priv->open_flags = GTK_PLACES_OPEN_NORMAL; - priv->path_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); - priv->space_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); - - gtk_widget_init_template (GTK_WIDGET (self)); -} - -/** - * gtk_nautilus_places_view_new: - * - * Creates a new #GtkNautilusPlacesView widget. - * - * The application should connect to at least the - * #GtkNautilusPlacesView::open-location signal to be notified - * when the user makes a selection in the view. - * - * Returns: a newly created #GtkNautilusPlacesView - * - * Since: 3.18 - */ -GtkWidget * -gtk_nautilus_places_view_new (void) -{ - return g_object_new (GTK_TYPE_NAUTILUS_PLACES_VIEW, NULL); -} - -/** - * gtk_nautilus_places_view_set_open_flags: - * @view: a #GtkNautilusPlacesView - * @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 #GtkNautilusPlacesView::open-location signal is emitted, its flags - * argument will be set to one of the @flags that was passed in - * gtk_nautilus_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_nautilus_places_view_set_open_flags (GtkNautilusPlacesView *view, - GtkPlacesOpenFlags flags) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view)); - - priv = gtk_nautilus_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_nautilus_places_view_get_open_flags: - * @view: a #GtkPlacesSidebar - * - * Gets the open flags. - * - * Returns: the #GtkPlacesOpenFlags of @view - * - * Since: 3.18 - */ -GtkPlacesOpenFlags -gtk_nautilus_places_view_get_open_flags (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view), 0); - - priv = gtk_nautilus_places_view_get_instance_private (view); - - return priv->open_flags; -} - -/** - * gtk_nautilus_places_view_get_search_query: - * @view: a #GtkNautilusPlacesView - * - * Retrieves the current search query from @view. - * - * Returns: (transfer none): the current search query. - */ -const gchar* -gtk_nautilus_places_view_get_search_query (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view), NULL); - - priv = gtk_nautilus_places_view_get_instance_private (view); - - return priv->search_query; -} - -/** - * gtk_nautilus_places_view_set_search_query: - * @view: a #GtkNautilusPlacesView - * @query_text: the query, or NULL. - * - * Sets the search query of @view. The search is immediately performed - * once the query is set. - */ -void -gtk_nautilus_places_view_set_search_query (GtkNautilusPlacesView *view, - const gchar *query_text) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view)); - - priv = gtk_nautilus_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_nautilus_places_view_get_loading: - * @view: a #GtkNautilusPlacesView - * - * Returns %TRUE if the view is loading locations. - * - * Since: 3.18 - */ -gboolean -gtk_nautilus_places_view_get_loading (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view), FALSE); - - priv = gtk_nautilus_places_view_get_instance_private (view); - - return priv->loading; -} - -static void -update_loading (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - gboolean loading; - - g_return_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view)); - - priv = gtk_nautilus_places_view_get_instance_private (view); - loading = priv->fetching_networks || priv->connecting_to_server || - priv->mounting_volume || priv->unmounting_mount; - - set_busy_cursor (view, loading); - gtk_nautilus_places_view_set_loading (view, loading); -} - -static void -gtk_nautilus_places_view_set_loading (GtkNautilusPlacesView *view, - gboolean loading) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view)); - - priv = gtk_nautilus_places_view_get_instance_private (view); - - if (priv->loading != loading) - { - priv->loading = loading; - g_object_notify_by_pspec (G_OBJECT (view), properties [PROP_LOADING]); - } -} - -static gboolean -gtk_nautilus_places_view_get_fetching_networks (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view), FALSE); - - priv = gtk_nautilus_places_view_get_instance_private (view); - - return priv->fetching_networks; -} - -static void -gtk_nautilus_places_view_set_fetching_networks (GtkNautilusPlacesView *view, - gboolean fetching_networks) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view)); - - priv = gtk_nautilus_places_view_get_instance_private (view); - - if (priv->fetching_networks != fetching_networks) - { - priv->fetching_networks = fetching_networks; - g_object_notify_by_pspec (G_OBJECT (view), properties [PROP_FETCHING_NETWORKS]); - } -} - -/** - * gtk_nautilus_places_view_get_local_only: - * @view: a #GtkNautilusPlacesView - * - * 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_nautilus_places_view_get_local_only (GtkNautilusPlacesView *view) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view), FALSE); - - priv = gtk_nautilus_places_view_get_instance_private (view); - - return priv->local_only; -} - -/** - * gtk_nautilus_places_view_set_local_only: - * @view: a #GtkNautilusPlacesView - * @local_only: %TRUE to hide remote locations, %FALSE to show. - * - * Sets the #GtkNautilusPlacesView::local-only property to @local_only. - * - * Since: 3.18 - */ -void -gtk_nautilus_places_view_set_local_only (GtkNautilusPlacesView *view, - gboolean local_only) -{ - GtkNautilusPlacesViewPrivate *priv; - - g_return_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW (view)); - - priv = gtk_nautilus_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); - update_places (view); - - 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 deleted file mode 100644 index 00296eb84..000000000 --- a/src/gtk/gtkplacesview.ui +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - - - - - - - - completion_store - 1 - 1 - 0 - - - - - 1 - - - 1 - 1 - center - vertical - 18 - - - 1 - 48 - network-server-symbolic - - - - - - 1 - No recent servers found - - - - 1 - - - - - empty - - - - - 1 - 12 - vertical - 12 - - - 1 - Recent Servers - - - - - - - - 1 - 1 - 1 - in - 250 - 200 - - - 1 - none - - - 1 - 1 - none - - - - - - - - 1 - - - - - list - - - - - - - diff --git a/src/gtk/gtkplacesviewprivate.h b/src/gtk/gtkplacesviewprivate.h deleted file mode 100644 index d0e308669..000000000 --- a/src/gtk/gtkplacesviewprivate.h +++ /dev/null @@ -1,82 +0,0 @@ -/* gtkplacesview.h - * - * Copyright (C) 2015 Georges Basile Stavracas Neto - * - * 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 . - */ - - -#ifndef GTK_NAUTILUS_PLACES_VIEW_H -#define GTK_NAUTILUS_PLACES_VIEW_H - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_NAUTILUS_PLACES_VIEW (gtk_nautilus_places_view_get_type ()) -#define GTK_NAUTILUS_PLACES_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_NAUTILUS_PLACES_VIEW, GtkNautilusPlacesView)) -#define GTK_NAUTILUS_PLACES_VIEW_CLASS(klass)(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_NAUTILUS_PLACES_VIEW, GtkNautilusPlacesViewClass)) -#define GTK_IS_NAUTILUS_PLACES_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_NAUTILUS_PLACES_VIEW)) -#define GTK_IS_NAUTILUS_PLACES_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_NAUTILUS_PLACES_VIEW)) -#define GTK_NAUTILUS_PLACES_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_NAUTILUS_PLACES_VIEW, GtkNautilusPlacesViewClass)) - -typedef struct _GtkNautilusPlacesView GtkNautilusPlacesView; -typedef struct _GtkNautilusPlacesViewClass GtkNautilusPlacesViewClass; -typedef struct _GtkNautilusPlacesViewPrivate GtkNautilusPlacesViewPrivate; - -struct _GtkNautilusPlacesViewClass -{ - GtkBoxClass parent_class; - - void (* open_location) (GtkNautilusPlacesView *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 _GtkNautilusPlacesView -{ - GtkBox parent_instance; -}; - -GType gtk_nautilus_places_view_get_type (void) G_GNUC_CONST; - -GtkPlacesOpenFlags gtk_nautilus_places_view_get_open_flags (GtkNautilusPlacesView *view); -void gtk_nautilus_places_view_set_open_flags (GtkNautilusPlacesView *view, - GtkPlacesOpenFlags flags); - -const gchar* gtk_nautilus_places_view_get_search_query (GtkNautilusPlacesView *view); -void gtk_nautilus_places_view_set_search_query (GtkNautilusPlacesView *view, - const gchar *query_text); - -gboolean gtk_nautilus_places_view_get_local_only (GtkNautilusPlacesView *view); - -void gtk_nautilus_places_view_set_local_only (GtkNautilusPlacesView *view, - gboolean local_only); - -gboolean gtk_nautilus_places_view_get_loading (GtkNautilusPlacesView *view); - -GtkWidget * gtk_nautilus_places_view_new (void); - -G_END_DECLS - -#endif /* GTK_NAUTILUS_PLACES_VIEW_H */ diff --git a/src/gtk/gtkplacesviewrow.c b/src/gtk/gtkplacesviewrow.c deleted file mode 100644 index 015d55ba3..000000000 --- a/src/gtk/gtkplacesviewrow.c +++ /dev/null @@ -1,467 +0,0 @@ -/* gtkplacesviewrow.c - * - * Copyright (C) 2015 Georges Basile Stavracas Neto - * - * 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 . - */ - -#include "config.h" - -#include -#include -#include - -#include "gtkplacesviewrowprivate.h" - -struct _GtkNautilusPlacesViewRow -{ - GtkListBoxRow parent_instance; - - GtkLabel *available_space_label; - 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; - - GCancellable *cancellable; - - gint is_network : 1; -}; - -G_DEFINE_TYPE (GtkNautilusPlacesViewRow, gtk_nautilus_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 -measure_available_space_finished (GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - GtkNautilusPlacesViewRow *row = user_data; - GFileInfo *info; - GError *error; - guint64 free_space; - guint64 total_space; - gchar *formatted_free_size; - gchar *formatted_total_size; - gchar *label; - - error = NULL; - - info = g_file_query_filesystem_info_finish (G_FILE (object), - res, - &error); - - if (error) - { - if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && - !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) - { - g_warning ("Failed to measure available space: %s", error->message); - } - - g_clear_error (&error); - goto out; - } - - if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE) || - !g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE)) - { - g_object_unref (info); - goto out; - } - - free_space = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); - total_space = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); - - formatted_free_size = g_format_size (free_space); - formatted_total_size = g_format_size (total_space); - /* Translators: respectively, free and total space of the drive */ - label = g_strdup_printf (_("%s / %s available"), formatted_free_size, formatted_total_size); - - gtk_label_set_label (row->available_space_label, label); - - g_object_unref (info); - g_free (formatted_total_size); - g_free (formatted_free_size); - g_free (label); -out: - g_object_unref (object); -} - -static void -measure_available_space (GtkNautilusPlacesViewRow *row) -{ - gboolean should_measure; - - should_measure = (!row->is_network && (row->volume || row->mount || row->file)); - - gtk_label_set_label (row->available_space_label, ""); - gtk_widget_set_visible (GTK_WIDGET (row->available_space_label), should_measure); - - if (should_measure) - { - GFile *file = NULL; - - if (row->file) - { - file = g_object_ref (row->file); - } - else if (row->mount) - { - file = g_mount_get_root (row->mount); - } - else if (row->volume) - { - GMount *mount; - - mount = g_volume_get_mount (row->volume); - - if (mount) - file = g_mount_get_root (row->mount); - - g_clear_object (&mount); - } - - if (file) - { - g_cancellable_cancel (row->cancellable); - g_clear_object (&row->cancellable); - row->cancellable = g_cancellable_new (); - - g_file_query_filesystem_info_async (file, - G_FILE_ATTRIBUTE_FILESYSTEM_FREE "," G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, - G_PRIORITY_DEFAULT, - row->cancellable, - measure_available_space_finished, - row); - } - } -} - -static void -gtk_nautilus_places_view_row_finalize (GObject *object) -{ - GtkNautilusPlacesViewRow *self = GTK_NAUTILUS_PLACES_VIEW_ROW (object); - - g_cancellable_cancel (self->cancellable); - - g_clear_object (&self->volume); - g_clear_object (&self->mount); - g_clear_object (&self->file); - g_clear_object (&self->cancellable); - - G_OBJECT_CLASS (gtk_nautilus_places_view_row_parent_class)->finalize (object); -} - -static void -gtk_nautilus_places_view_row_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkNautilusPlacesViewRow *self; - GIcon *icon; - - self = GTK_NAUTILUS_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_nautilus_places_view_row_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkNautilusPlacesViewRow *self = GTK_NAUTILUS_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)); - - /* - * When we hide the eject button, no size is allocated for it. Since - * we want to have alignment between rows, it needs an empty space - * when the eject button is not available. So, call then - * gtk_widget_set_child_visible(), which makes the button allocate the - * size but it stays hidden when needed. - */ - gtk_widget_set_child_visible (GTK_WIDGET (self->eject_button), self->mount != NULL); - measure_available_space (self); - break; - - case PROP_FILE: - g_set_object (&self->file, g_value_get_object (value)); - measure_available_space (self); - break; - - case PROP_IS_NETWORK: - gtk_nautilus_places_view_row_set_is_network (self, g_value_get_boolean (value)); - measure_available_space (self); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_nautilus_places_view_row_class_init (GtkNautilusPlacesViewRowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = gtk_nautilus_places_view_row_finalize; - object_class->get_property = gtk_nautilus_places_view_row_get_property; - object_class->set_property = gtk_nautilus_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, GtkNautilusPlacesViewRow, available_space_label); - gtk_widget_class_bind_template_child (widget_class, GtkNautilusPlacesViewRow, busy_spinner); - gtk_widget_class_bind_template_child (widget_class, GtkNautilusPlacesViewRow, eject_button); - gtk_widget_class_bind_template_child (widget_class, GtkNautilusPlacesViewRow, eject_icon); - gtk_widget_class_bind_template_child (widget_class, GtkNautilusPlacesViewRow, event_box); - gtk_widget_class_bind_template_child (widget_class, GtkNautilusPlacesViewRow, icon_image); - gtk_widget_class_bind_template_child (widget_class, GtkNautilusPlacesViewRow, name_label); - gtk_widget_class_bind_template_child (widget_class, GtkNautilusPlacesViewRow, path_label); -} - -static void -gtk_nautilus_places_view_row_init (GtkNautilusPlacesViewRow *self) -{ - gtk_widget_init_template (GTK_WIDGET (self)); -} - -GtkWidget* -gtk_nautilus_places_view_row_new (GVolume *volume, - GMount *mount) -{ - return g_object_new (GTK_TYPE_NAUTILUS_PLACES_VIEW_ROW, - "volume", volume, - "mount", mount, - NULL); -} - -GMount* -gtk_nautilus_places_view_row_get_mount (GtkNautilusPlacesViewRow *row) -{ - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW_ROW (row), NULL); - - return row->mount; -} - -GVolume* -gtk_nautilus_places_view_row_get_volume (GtkNautilusPlacesViewRow *row) -{ - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW_ROW (row), NULL); - - return row->volume; -} - -GFile* -gtk_nautilus_places_view_row_get_file (GtkNautilusPlacesViewRow *row) -{ - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW_ROW (row), NULL); - - return row->file; -} - -GtkWidget* -gtk_nautilus_places_view_row_get_eject_button (GtkNautilusPlacesViewRow *row) -{ - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW_ROW (row), NULL); - - return GTK_WIDGET (row->eject_button); -} - -GtkWidget* -gtk_nautilus_places_view_row_get_event_box (GtkNautilusPlacesViewRow *row) -{ - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW_ROW (row), NULL); - - return GTK_WIDGET (row->event_box); -} - -void -gtk_nautilus_places_view_row_set_busy (GtkNautilusPlacesViewRow *row, - gboolean is_busy) -{ - g_return_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW_ROW (row)); - - gtk_widget_set_visible (GTK_WIDGET (row->busy_spinner), is_busy); -} - -gboolean -gtk_nautilus_places_view_row_get_is_network (GtkNautilusPlacesViewRow *row) -{ - g_return_val_if_fail (GTK_IS_NAUTILUS_PLACES_VIEW_ROW (row), FALSE); - - return row->is_network; -} - -void -gtk_nautilus_places_view_row_set_is_network (GtkNautilusPlacesViewRow *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")); - } -} - -void -gtk_nautilus_places_view_row_set_path_size_group (GtkNautilusPlacesViewRow *row, - GtkSizeGroup *group) -{ - if (group) - gtk_size_group_add_widget (group, GTK_WIDGET (row->path_label)); -} - -void -gtk_nautilus_places_view_row_set_space_size_group (GtkNautilusPlacesViewRow *row, - GtkSizeGroup *group) -{ - if (group) - gtk_size_group_add_widget (group, GTK_WIDGET (row->available_space_label)); -} diff --git a/src/gtk/gtkplacesviewrow.ui b/src/gtk/gtkplacesviewrow.ui deleted file mode 100644 index 90e9ed05d..000000000 --- a/src/gtk/gtkplacesviewrow.ui +++ /dev/null @@ -1,121 +0,0 @@ - - - - - diff --git a/src/gtk/gtkplacesviewrowprivate.h b/src/gtk/gtkplacesviewrowprivate.h deleted file mode 100644 index d5f28db0a..000000000 --- a/src/gtk/gtkplacesviewrowprivate.h +++ /dev/null @@ -1,59 +0,0 @@ -/* gtkplacesviewrow.h - * - * Copyright (C) 2015 Georges Basile Stavracas Neto - * - * 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 . - */ - -#ifndef GTK_NAUTILUS_PLACES_VIEW_ROW_H -#define GTK_NAUTILUS_PLACES_VIEW_ROW_H - -#include - -G_BEGIN_DECLS - -#define GTK_TYPE_NAUTILUS_PLACES_VIEW_ROW (gtk_nautilus_places_view_row_get_type()) - -G_DECLARE_FINAL_TYPE (GtkNautilusPlacesViewRow, gtk_nautilus_places_view_row, GTK, NAUTILUS_PLACES_VIEW_ROW, GtkListBoxRow) - -GtkWidget* gtk_nautilus_places_view_row_new (GVolume *volume, - GMount *mount); - -GtkWidget* gtk_nautilus_places_view_row_get_eject_button (GtkNautilusPlacesViewRow *row); - -GtkWidget* gtk_nautilus_places_view_row_get_event_box (GtkNautilusPlacesViewRow *row); - -GMount* gtk_nautilus_places_view_row_get_mount (GtkNautilusPlacesViewRow *row); - -GVolume* gtk_nautilus_places_view_row_get_volume (GtkNautilusPlacesViewRow *row); - -GFile* gtk_nautilus_places_view_row_get_file (GtkNautilusPlacesViewRow *row); - -void gtk_nautilus_places_view_row_set_busy (GtkNautilusPlacesViewRow *row, - gboolean is_busy); - -gboolean gtk_nautilus_places_view_row_get_is_network (GtkNautilusPlacesViewRow *row); - -void gtk_nautilus_places_view_row_set_is_network (GtkNautilusPlacesViewRow *row, - gboolean is_network); - -void gtk_nautilus_places_view_row_set_path_size_group (GtkNautilusPlacesViewRow *row, - GtkSizeGroup *group); - -void gtk_nautilus_places_view_row_set_space_size_group (GtkNautilusPlacesViewRow *row, - GtkSizeGroup *group); - -G_END_DECLS - -#endif /* GTK_NAUTILUS_PLACES_VIEW_ROW_H */ diff --git a/src/gtk/nautilusgtkplacesview.c b/src/gtk/nautilusgtkplacesview.c new file mode 100644 index 000000000..aac7ff9e2 --- /dev/null +++ b/src/gtk/nautilusgtkplacesview.c @@ -0,0 +1,2512 @@ +/* Do not edit: this file is generated from https://git.gnome.org/browse/gtk+/plain/gtk//gtkplacesview.c */ + +/* nautilusgtkplacesview.c + * + * Copyright (C) 2015 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include +#include + +#include "nautilusgtkplacesviewprivate.h" +#include "nautilusnautilusgtkplacesviewrowprivate.h" + +/** + * SECTION:nautilusgtkplacesview + * @Short_description: Widget that displays persistent drives and manages mounted networks + * @Title: NautilusGtkPlacesView + * @See_also: #GtkFileChooser + * + * #NautilusGtkPlacesView is a stock widget that displays a list of persistent drives + * such as harddisk partitions and networks. #NautilusGtkPlacesView 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 #NautilusGtkPlacesView::open-location signal. This is emitted when the user + * selects a location to open in the view. + */ + +struct _NautilusGtkPlacesViewPrivate +{ + 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_placeholder; + GtkWidget *network_placeholder_label; + + GtkSizeGroup *path_size_group; + GtkSizeGroup *space_size_group; + + GtkEntryCompletion *address_entry_completion; + GtkListStore *completion_store; + + 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 mounting_volume : 1; + guint unmounting_mount : 1; + guint fetching_networks : 1; + guint loading : 1; +}; + +static void mount_volume (NautilusGtkPlacesView *view, + GVolume *volume); + +static gboolean on_button_press_event (NautilusGtkPlacesViewRow *row, + GdkEventButton *event); + +static void on_eject_button_clicked (GtkWidget *widget, + NautilusGtkPlacesViewRow *row); + +static gboolean on_row_popup_menu (NautilusGtkPlacesViewRow *row); + +static void populate_servers (NautilusGtkPlacesView *view); + +static gboolean nautilus_gtk_places_view_get_fetching_networks (NautilusGtkPlacesView *view); + +static void nautilus_gtk_places_view_set_fetching_networks (NautilusGtkPlacesView *view, + gboolean fetching_networks); + +static void nautilus_gtk_places_view_set_loading (NautilusGtkPlacesView *view, + gboolean loading); + +static void update_loading (NautilusGtkPlacesView *view); + +G_DEFINE_TYPE_WITH_PRIVATE (NautilusGtkPlacesView, nautilus_gtk_places_view, GTK_TYPE_BOX) + +/* NautilusGtkPlacesView properties & signals */ +enum { + PROP_0, + PROP_LOCAL_ONLY, + PROP_OPEN_FLAGS, + PROP_FETCHING_NETWORKS, + PROP_LOADING, + 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 (NautilusGtkPlacesView *view, + GFile *location, + GtkPlacesOpenFlags open_flags) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_gtk_places_view_get_instance_private (view); + + if ((open_flags & priv->open_flags) == 0) + open_flags = GTK_PLACES_OPEN_NORMAL; + + g_signal_emit (view, places_view_signals[OPEN_LOCATION], 0, location, open_flags); +} + +static void +emit_show_error_message (NautilusGtkPlacesView *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 (NautilusGtkPlacesView *view) +{ + populate_servers (view); +} + +static GBookmarkFile * +server_list_load (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + GBookmarkFile *bookmarks; + GError *error = NULL; + gchar *datadir; + gchar *filename; + + priv = nautilus_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 (NautilusGtkPlacesView *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 (NautilusGtkPlacesView *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 (NautilusGtkPlacesView *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, "progress"); + 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 (NautilusGtkPlacesView *view, + NautilusGtkPlacesViewRow *row, + GtkPlacesOpenFlags flags) +{ + NautilusGtkPlacesViewPrivate *priv; + GVolume *volume; + GMount *mount; + GFile *file; + + priv = nautilus_gtk_places_view_get_instance_private (view); + mount = nautilus_gtk_places_view_row_get_mount (row); + volume = nautilus_gtk_places_view_row_get_volume (row); + file = nautilus_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; + + nautilus_gtk_places_view_row_set_busy (row, TRUE); + mount_volume (view, volume); + } +} + +static void update_places (NautilusGtkPlacesView *view); + +static void +nautilus_gtk_places_view_finalize (GObject *object) +{ + NautilusGtkPlacesView *self = (NautilusGtkPlacesView *)object; + NautilusGtkPlacesViewPrivate *priv = nautilus_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_clear_object (&priv->path_size_group); + g_clear_object (&priv->space_size_group); + + G_OBJECT_CLASS (nautilus_gtk_places_view_parent_class)->finalize (object); +} + +static void +nautilus_gtk_places_view_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusGtkPlacesView *self = NAUTILUS_GTK_PLACES_VIEW (object); + + switch (prop_id) + { + case PROP_LOCAL_ONLY: + g_value_set_boolean (value, nautilus_gtk_places_view_get_local_only (self)); + break; + + case PROP_LOADING: + g_value_set_boolean (value, nautilus_gtk_places_view_get_loading (self)); + break; + + case PROP_FETCHING_NETWORKS: + g_value_set_boolean (value, nautilus_gtk_places_view_get_fetching_networks (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +nautilus_gtk_places_view_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusGtkPlacesView *self = NAUTILUS_GTK_PLACES_VIEW (object); + + switch (prop_id) + { + case PROP_LOCAL_ONLY: + nautilus_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_external_volume (GVolume *volume) +{ + gboolean is_external; + GDrive *drive; + gchar *id; + + drive = g_volume_get_drive (volume); + id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); + + is_external = g_volume_can_eject (volume); + + /* NULL volume identifier only happens on removable devices */ + is_external |= !id; + + if (drive) + is_external |= g_drive_can_eject (drive) || + g_drive_is_media_removable (drive) || + g_drive_can_stop (drive); + + g_clear_object (&drive); + g_free (id); + + return is_external; +} + +typedef struct +{ + gchar *uri; + NautilusGtkPlacesView *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 (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + GBookmarkFile *server_list; + GList *children; + gchar **uris; + gsize num_uris; + gint i; + + priv = nautilus_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", 3, + 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 (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + GList *children; + GList *l; + gboolean show_listbox; + + priv = nautilus_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 (NautilusGtkPlacesView *view, + GtkWidget *row, + gboolean is_network) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_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 (nautilus_gtk_places_view_row_get_event_box (NAUTILUS_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 (nautilus_gtk_places_view_row_get_eject_button (NAUTILUS_GTK_PLACES_VIEW_ROW (row)), + "clicked", + G_CALLBACK (on_eject_button_clicked), + row); + + nautilus_gtk_places_view_row_set_path_size_group (NAUTILUS_GTK_PLACES_VIEW_ROW (row), priv->path_size_group); + nautilus_gtk_places_view_row_set_space_size_group (NAUTILUS_GTK_PLACES_VIEW_ROW (row), priv->space_size_group); + + gtk_container_add (GTK_CONTAINER (priv->listbox), row); +} + +static void +add_volume (NautilusGtkPlacesView *view, + GVolume *volume) +{ + gboolean is_network; + GMount *mount; + GFile *root; + GIcon *icon; + gchar *identifier; + gchar *name; + gchar *path; + + if (is_external_volume (volume)) + 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 (NAUTILUS_TYPE_GTK_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 (NautilusGtkPlacesView *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 (NAUTILUS_TYPE_GTK_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 (NautilusGtkPlacesView *view, + GDrive *drive) +{ + GList *volumes; + GList *l; + + 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 (NautilusGtkPlacesView *view, + GFile *file, + GIcon *icon, + const gchar *display_name, + const gchar *path, + gboolean is_network) +{ + GtkWidget *row; + row = g_object_new (NAUTILUS_TYPE_GTK_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 (NautilusGtkPlacesView *view) +{ + GList *l; + NautilusGtkPlacesViewPrivate *priv; + GList *children; + gboolean has_network = FALSE; + + priv = nautilus_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 (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_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); + } + + if (nautilus_gtk_places_view_get_fetching_networks (view)) + { + /* 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_widget_show_all (priv->network_placeholder); + gtk_label_set_text (GTK_LABEL (priv->network_placeholder_label), + _("No network locations found")); + } + else + { + gtk_widget_hide (priv->network_placeholder); + } +} + +static void +populate_networks (NautilusGtkPlacesView *view, + GFileEnumerator *enumerator, + GList *detected_networks) +{ + GList *l; + GFile *file; + GFile *activatable_file; + gchar *uri; + GFileType type; + GIcon *icon; + gchar *display_name; + + for (l = detected_networks; l != NULL; l = l->next) + { + file = g_file_enumerator_get_child (enumerator, l->data); + type = g_file_info_get_file_type (l->data); + if (type == G_FILE_TYPE_SHORTCUT || type == G_FILE_TYPE_MOUNTABLE) + uri = g_file_info_get_attribute_as_string (l->data, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); + else + uri = g_file_get_uri (file); + activatable_file = g_file_new_for_uri (uri); + 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, activatable_file, icon, display_name, NULL, TRUE); + + g_free (uri); + g_free (display_name); + g_clear_object (&file); + g_clear_object (&activatable_file); + } +} + +static void +network_enumeration_next_files_finished (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusGtkPlacesViewPrivate *priv; + NautilusGtkPlacesView *view; + GList *detected_networks; + GError *error; + + view = NAUTILUS_GTK_PLACES_VIEW (user_data); + priv = nautilus_gtk_places_view_get_instance_private (view); + error = NULL; + + detected_networks = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source_object), + res, &error); + + if (error) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("Failed to fetch network locations: %s", error->message); + + g_clear_error (&error); + } + else + { + nautilus_gtk_places_view_set_fetching_networks (view, FALSE); + populate_networks (view, G_FILE_ENUMERATOR (source_object), detected_networks); + + g_list_free_full (detected_networks, g_object_unref); + } + + /* avoid to update widgets if the operation was cancelled in finalize */ + if (priv->listbox != NULL) + { + update_network_state (view); + update_loading (view); + } +} + +static void +network_enumeration_finished (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusGtkPlacesViewPrivate *priv; + GFileEnumerator *enumerator; + GError *error; + + error = NULL; + enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, &error); + + if (error) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) + g_warning ("Failed to fetch network locations: %s", error->message); + + g_clear_error (&error); + } + else + { + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_GTK_PLACES_VIEW (user_data)); + 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 (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + GFile *network_file; + + priv = nautilus_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 (); + nautilus_gtk_places_view_set_fetching_networks (view, TRUE); + update_network_state (view); + + g_file_enumerate_children_async (network_file, + "standard::type,standard::target-uri,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 (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + GList *children; + GList *mounts; + GList *volumes; + GList *drives; + GList *l; + GIcon *icon; + GFile *file; + + priv = nautilus_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; + /* Inform clients that we started loading */ + nautilus_gtk_places_view_set_loading (view, TRUE); + + /* 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 */ + if (!nautilus_gtk_places_view_get_local_only (view)) + fetch_networks (view); + + update_view_mode (view); + /* Check whether we still are in a loading state */ + update_loading (view); +} + +static void +server_mount_ready_cb (GObject *source_file, + GAsyncResult *res, + gpointer user_data) +{ + NautilusGtkPlacesViewPrivate *priv; + NautilusGtkPlacesView *view; + gboolean should_show; + GError *error; + GFile *location; + + location = G_FILE (source_file); + should_show = TRUE; + error = NULL; + + g_file_mount_enclosing_volume_finish (location, res, &error); + 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)) + { + view = NAUTILUS_GTK_PLACES_VIEW (user_data); + /* if it wasn't cancelled show a dialog */ + emit_show_error_message (view, _("Unable to access location"), error->message); + should_show = FALSE; + } + else + { + /* it was cancelled, so probably it was called during finalize, bail out. */ + g_clear_error (&error); + return; + } + g_clear_error (&error); + } + + view = NAUTILUS_GTK_PLACES_VIEW (user_data); + priv = nautilus_gtk_places_view_get_instance_private (view); + priv->should_pulse_entry = FALSE; + + /* 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 (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) + emit_open_location (view, location, priv->open_flags); + } + + update_places (view); +} + +static void +volume_mount_ready_cb (GObject *source_volume, + GAsyncResult *res, + gpointer user_data) +{ + NautilusGtkPlacesViewPrivate *priv; + NautilusGtkPlacesView *view; + gboolean should_show; + GVolume *volume; + GError *error; + + volume = G_VOLUME (source_volume); + should_show = TRUE; + error = NULL; + + 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 (NAUTILUS_GTK_PLACES_VIEW (user_data), _("Unable to access location"), error->message); + should_show = FALSE; + } + else + { + /* it was cancelled, so probably it was called during finalize, bail out. */ + g_clear_error (&error); + return; + } + + g_clear_error (&error); + } + + view = NAUTILUS_GTK_PLACES_VIEW (user_data); + priv = nautilus_gtk_places_view_get_instance_private (view); + priv->mounting_volume = FALSE; + update_loading (view); + + 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 (NAUTILUS_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) +{ + NautilusGtkPlacesView *view; + NautilusGtkPlacesViewPrivate *priv; + GMount *mount; + GError *error; + + view = NAUTILUS_GTK_PLACES_VIEW (user_data); + mount = G_MOUNT (source_mount); + error = NULL; + + priv = nautilus_gtk_places_view_get_instance_private (view); + priv->unmounting_mount = FALSE; + update_loading (view); + + 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) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_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 (NautilusGtkPlacesView *view, + GMount *mount) +{ + NautilusGtkPlacesViewPrivate *priv; + GMountOperation *operation; + GtkWidget *toplevel; + + priv = nautilus_gtk_places_view_get_instance_private (view); + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view)); + operation = gtk_mount_operation_new (GTK_WINDOW (toplevel)); + + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + priv->cancellable = g_cancellable_new (); + + priv->unmounting_mount = TRUE; + update_loading (view); + + 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 (NautilusGtkPlacesView *view, + GFile *location) +{ + NautilusGtkPlacesViewPrivate *priv; + GMountOperation *operation; + GtkWidget *toplevel; + + priv = nautilus_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)); + + 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; + update_loading (view); + + 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 (NautilusGtkPlacesView *view, + GVolume *volume) +{ + NautilusGtkPlacesViewPrivate *priv; + GMountOperation *operation; + GtkWidget *toplevel; + + priv = nautilus_gtk_places_view_get_instance_private (view); + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (view)); + operation = gtk_mount_operation_new (GTK_WINDOW (toplevel)); + + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + priv->cancellable = g_cancellable_new (); + + priv->mounting_volume = TRUE; + update_loading (view); + + 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) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_GTK_PLACES_VIEW (attach_widget)); + priv->popup_menu = NULL; +} + +static void +get_view_and_file (NautilusGtkPlacesViewRow *row, + GtkWidget **view, + GFile **file) +{ + if (view) + *view = gtk_widget_get_ancestor (GTK_WIDGET (row), NAUTILUS_TYPE_GTK_PLACES_VIEW); + + if (file) + { + GVolume *volume; + GMount *mount; + + volume = nautilus_gtk_places_view_row_get_volume (row); + mount = nautilus_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, + NautilusGtkPlacesViewRow *row) +{ + GtkWidget *view; + GFile *file; + + get_view_and_file (row, &view, &file); + + if (file) + emit_open_location (NAUTILUS_GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NORMAL); + + g_clear_object (&file); +} + +static void +open_in_new_tab_cb (GtkMenuItem *item, + NautilusGtkPlacesViewRow *row) +{ + GtkWidget *view; + GFile *file; + + get_view_and_file (row, &view, &file); + + if (file) + emit_open_location (NAUTILUS_GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_TAB); + + g_clear_object (&file); +} + +static void +open_in_new_window_cb (GtkMenuItem *item, + NautilusGtkPlacesViewRow *row) +{ + GtkWidget *view; + GFile *file; + + get_view_and_file (row, &view, &file); + + if (file) + emit_open_location (NAUTILUS_GTK_PLACES_VIEW (view), file, GTK_PLACES_OPEN_NEW_WINDOW); + + g_clear_object (&file); +} + +static void +mount_cb (GtkMenuItem *item, + NautilusGtkPlacesViewRow *row) +{ + NautilusGtkPlacesViewPrivate *priv; + GtkWidget *view; + GVolume *volume; + + view = gtk_widget_get_ancestor (GTK_WIDGET (row), NAUTILUS_TYPE_GTK_PLACES_VIEW); + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_GTK_PLACES_VIEW (view)); + volume = nautilus_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; + + nautilus_gtk_places_view_row_set_busy (row, TRUE); + mount_volume (NAUTILUS_GTK_PLACES_VIEW (view), volume); +} + +static void +unmount_cb (GtkMenuItem *item, + NautilusGtkPlacesViewRow *row) +{ + GtkWidget *view; + GMount *mount; + + view = gtk_widget_get_ancestor (GTK_WIDGET (row), NAUTILUS_TYPE_GTK_PLACES_VIEW); + mount = nautilus_gtk_places_view_row_get_mount (row); + + nautilus_gtk_places_view_row_set_busy (row, TRUE); + + unmount_mount (NAUTILUS_GTK_PLACES_VIEW (view), mount); +} + +/* Constructs the popup menu if needed */ +static void +build_popup_menu (NautilusGtkPlacesView *view, + NautilusGtkPlacesViewRow *row) +{ + NautilusGtkPlacesViewPrivate *priv; + GtkWidget *item; + GMount *mount; + GFile *file; + gboolean is_network; + + priv = nautilus_gtk_places_view_get_instance_private (view); + mount = nautilus_gtk_places_view_row_get_mount (row); + file = nautilus_gtk_places_view_row_get_file (row); + is_network = nautilus_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 (NautilusGtkPlacesViewRow *row, + GdkEventButton *event) +{ + NautilusGtkPlacesViewPrivate *priv; + GtkWidget *view; + gint button; + + view = gtk_widget_get_ancestor (GTK_WIDGET (row), NAUTILUS_TYPE_GTK_PLACES_VIEW); + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_GTK_PLACES_VIEW (view)); + + g_clear_pointer (&priv->popup_menu, gtk_widget_destroy); + + build_popup_menu (NAUTILUS_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 (NautilusGtkPlacesViewRow *row) +{ + popup_menu (row, NULL); + return TRUE; +} + +static gboolean +on_button_press_event (NautilusGtkPlacesViewRow *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, + NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_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, NAUTILUS_GTK_PLACES_VIEW_ROW (focus_widget), priv->current_open_flags); + + return TRUE; + } + } + + return FALSE; +} + +static void +on_eject_button_clicked (GtkWidget *widget, + NautilusGtkPlacesViewRow *row) +{ + if (row) + { + GtkWidget *view = gtk_widget_get_ancestor (GTK_WIDGET (row), NAUTILUS_TYPE_GTK_PLACES_VIEW); + + unmount_mount (NAUTILUS_GTK_PLACES_VIEW (view), nautilus_gtk_places_view_row_get_mount (row)); + } +} + +static void +on_connect_button_clicked (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + const gchar *uri; + GFile *file; + + priv = nautilus_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 (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + const gchar* const *supported_protocols; + gchar *address, *scheme; + gboolean supported; + + priv = nautilus_gtk_places_view_get_instance_private (view); + supported = FALSE; + supported_protocols = g_vfs_get_supported_uri_schemes (g_vfs_get_default ()); + address = g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->address_entry))); + scheme = g_uri_parse_scheme (address); + + if (strlen (address) > 0) + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->address_entry), + GTK_ENTRY_ICON_SECONDARY, + "edit-clear-symbolic"); + else + gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->address_entry), + GTK_ENTRY_ICON_SECONDARY, + NULL); + + if (!supported_protocols) + goto out; + + 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); + g_free (scheme); +} + +static void +on_address_entry_clear_pressed (NautilusGtkPlacesView *view, + GtkEntryIconPosition icon_pos, + GdkEvent *event, + GtkEntry *entry) +{ + gtk_entry_set_text (entry, ""); +} + +static void +on_recent_servers_listbox_row_activated (NautilusGtkPlacesView *view, + NautilusGtkPlacesViewRow *row, + GtkWidget *listbox) +{ + NautilusGtkPlacesViewPrivate *priv; + gchar *uri; + + priv = nautilus_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 (NautilusGtkPlacesView *view, + NautilusGtkPlacesViewRow *row, + GtkWidget *listbox) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_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) +{ + NautilusGtkPlacesViewPrivate *priv; + gboolean is_network; + gboolean is_placeholder; + gboolean retval; + gboolean searching; + gchar *name; + gchar *path; + + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_GTK_PLACES_VIEW (user_data)); + retval = FALSE; + searching = priv->search_query && priv->search_query[0] != '\0'; + + is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-network")); + is_placeholder = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-placeholder")); + + if (is_network && priv->local_only) + return FALSE; + + if (is_placeholder && searching) + return FALSE; + + if (!searching) + 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) +{ + gboolean row_is_network; + gchar *text; + + text = NULL; + row_is_network = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "is-network")); + + if (!before) + { + text = g_strdup_printf ("%s", 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 ("%s", row_is_network ? _("Networks") : _("On This Computer")); + } + + if (text) + { + GtkWidget *header; + GtkWidget *label; + GtkWidget *separator; + + header = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_widget_set_margin_top (header, 6); + + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + + label = g_object_new (GTK_TYPE_LABEL, + "use_markup", TRUE, + "margin-start", 12, + "label", text, + "xalign", 0.0f, + NULL); + if (row_is_network) + { + GtkWidget *header_name; + GtkWidget *network_header_spinner; + + g_object_set (label, + "margin-end", 6, + NULL); + + header_name = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + network_header_spinner = gtk_spinner_new (); + g_object_set (network_header_spinner, + "margin-end", 12, + NULL); + g_object_bind_property (NAUTILUS_GTK_PLACES_VIEW (user_data), + "fetching-networks", + network_header_spinner, + "active", + G_BINDING_SYNC_CREATE); + + gtk_container_add (GTK_CONTAINER (header_name), label); + gtk_container_add (GTK_CONTAINER (header_name), 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 *path1; + gchar *path2; + 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", &path1, NULL); + g_object_get (row2, "path", &path2, NULL); + + retval = g_utf8_collate (path1, path2); + + g_free (path1); + g_free (path2); + + return retval; +} + +static void +nautilus_gtk_places_view_constructed (GObject *object) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_GTK_PLACES_VIEW (object)); + + G_OBJECT_CLASS (nautilus_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 (NAUTILUS_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 +nautilus_gtk_places_view_map (GtkWidget *widget) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_gtk_places_view_get_instance_private (NAUTILUS_GTK_PLACES_VIEW (widget)); + + gtk_entry_set_text (GTK_ENTRY (priv->address_entry), ""); + + GTK_WIDGET_CLASS (nautilus_gtk_places_view_parent_class)->map (widget); +} + +static void +nautilus_gtk_places_view_class_init (NautilusGtkPlacesViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = nautilus_gtk_places_view_finalize; + object_class->constructed = nautilus_gtk_places_view_constructed; + object_class->get_property = nautilus_gtk_places_view_get_property; + object_class->set_property = nautilus_gtk_places_view_set_property; + + widget_class->map = nautilus_gtk_places_view_map; + + /** + * NautilusGtkPlacesView::open-location: + * @view: the object which received the signal. + * @location: (type Gio.File): #GFile to which the caller should switch. + * @open_flags: a single value from #GtkPlacesOpenFlags specifying how the @location + * 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 (NautilusGtkPlacesViewClass, open_location), + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_OBJECT, + GTK_TYPE_PLACES_OPEN_FLAGS); + + /** + * NautilusGtkPlacesView::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 (NautilusGtkPlacesViewClass, 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_LOADING] = + g_param_spec_boolean ("loading", + "Loading", + "Whether the view is loading locations", + FALSE, + G_PARAM_READABLE); + + properties[PROP_FETCHING_NETWORKS] = + g_param_spec_boolean ("fetching-networks", + "Fetching networks", + "Whether the view is fetching networks", + FALSE, + G_PARAM_READABLE); + + 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/nautilusgtkplacesview.ui"); + + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, actionbar); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, address_entry); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, address_entry_completion); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, completion_store); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, connect_button); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, listbox); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, recent_servers_listbox); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, recent_servers_popover); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, recent_servers_stack); + gtk_widget_class_bind_template_child_private (widget_class, NautilusGtkPlacesView, stack); + + gtk_widget_class_bind_template_callback (widget_class, on_address_entry_text_changed); + gtk_widget_class_bind_template_callback (widget_class, on_address_entry_clear_pressed); + 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); + + gtk_widget_class_set_css_name (widget_class, "placesview"); +} + +static void +nautilus_gtk_places_view_init (NautilusGtkPlacesView *self) +{ + NautilusGtkPlacesViewPrivate *priv; + + priv = nautilus_gtk_places_view_get_instance_private (self); + + priv->volume_monitor = g_volume_monitor_get (); + priv->open_flags = GTK_PLACES_OPEN_NORMAL; + priv->path_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + priv->space_size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + gtk_widget_init_template (GTK_WIDGET (self)); +} + +/** + * nautilus_gtk_places_view_new: + * + * Creates a new #NautilusGtkPlacesView widget. + * + * The application should connect to at least the + * #NautilusGtkPlacesView::open-location signal to be notified + * when the user makes a selection in the view. + * + * Returns: a newly created #NautilusGtkPlacesView + * + * Since: 3.18 + */ +GtkWidget * +nautilus_gtk_places_view_new (void) +{ + return g_object_new (NAUTILUS_TYPE_GTK_PLACES_VIEW, NULL); +} + +/** + * nautilus_gtk_places_view_set_open_flags: + * @view: a #NautilusGtkPlacesView + * @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 #NautilusGtkPlacesView::open-location signal is emitted, its flags + * argument will be set to one of the @flags that was passed in + * nautilus_gtk_places_view_set_open_flags(). + * + * Passing 0 for @flags will cause #GTK_PLACES_OPEN_NORMAL to always be sent + * to callbacks for the “open-location” signal. + * + * Since: 3.18 + */ +void +nautilus_gtk_places_view_set_open_flags (NautilusGtkPlacesView *view, + GtkPlacesOpenFlags flags) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_if_fail (GTK_IS_PLACES_VIEW (view)); + + priv = nautilus_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]); + } +} + +/** + * nautilus_gtk_places_view_get_open_flags: + * @view: a #GtkPlacesSidebar + * + * Gets the open flags. + * + * Returns: the #GtkPlacesOpenFlags of @view + * + * Since: 3.18 + */ +GtkPlacesOpenFlags +nautilus_gtk_places_view_get_open_flags (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), 0); + + priv = nautilus_gtk_places_view_get_instance_private (view); + + return priv->open_flags; +} + +/** + * nautilus_gtk_places_view_get_search_query: + * @view: a #NautilusGtkPlacesView + * + * Retrieves the current search query from @view. + * + * Returns: (transfer none): the current search query. + */ +const gchar* +nautilus_gtk_places_view_get_search_query (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), NULL); + + priv = nautilus_gtk_places_view_get_instance_private (view); + + return priv->search_query; +} + +/** + * nautilus_gtk_places_view_set_search_query: + * @view: a #NautilusGtkPlacesView + * @query_text: the query, or NULL. + * + * Sets the search query of @view. The search is immediately performed + * once the query is set. + */ +void +nautilus_gtk_places_view_set_search_query (NautilusGtkPlacesView *view, + const gchar *query_text) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_if_fail (GTK_IS_PLACES_VIEW (view)); + + priv = nautilus_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); + } +} + +/** + * nautilus_gtk_places_view_get_loading: + * @view: a #NautilusGtkPlacesView + * + * Returns %TRUE if the view is loading locations. + * + * Since: 3.18 + */ +gboolean +nautilus_gtk_places_view_get_loading (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), FALSE); + + priv = nautilus_gtk_places_view_get_instance_private (view); + + return priv->loading; +} + +static void +update_loading (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + gboolean loading; + + g_return_if_fail (GTK_IS_PLACES_VIEW (view)); + + priv = nautilus_gtk_places_view_get_instance_private (view); + loading = priv->fetching_networks || priv->connecting_to_server || + priv->mounting_volume || priv->unmounting_mount; + + set_busy_cursor (view, loading); + nautilus_gtk_places_view_set_loading (view, loading); +} + +static void +nautilus_gtk_places_view_set_loading (NautilusGtkPlacesView *view, + gboolean loading) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_if_fail (GTK_IS_PLACES_VIEW (view)); + + priv = nautilus_gtk_places_view_get_instance_private (view); + + if (priv->loading != loading) + { + priv->loading = loading; + g_object_notify_by_pspec (G_OBJECT (view), properties [PROP_LOADING]); + } +} + +static gboolean +nautilus_gtk_places_view_get_fetching_networks (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), FALSE); + + priv = nautilus_gtk_places_view_get_instance_private (view); + + return priv->fetching_networks; +} + +static void +nautilus_gtk_places_view_set_fetching_networks (NautilusGtkPlacesView *view, + gboolean fetching_networks) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_if_fail (GTK_IS_PLACES_VIEW (view)); + + priv = nautilus_gtk_places_view_get_instance_private (view); + + if (priv->fetching_networks != fetching_networks) + { + priv->fetching_networks = fetching_networks; + g_object_notify_by_pspec (G_OBJECT (view), properties [PROP_FETCHING_NETWORKS]); + } +} + +/** + * nautilus_gtk_places_view_get_local_only: + * @view: a #NautilusGtkPlacesView + * + * 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 +nautilus_gtk_places_view_get_local_only (NautilusGtkPlacesView *view) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_val_if_fail (GTK_IS_PLACES_VIEW (view), FALSE); + + priv = nautilus_gtk_places_view_get_instance_private (view); + + return priv->local_only; +} + +/** + * nautilus_gtk_places_view_set_local_only: + * @view: a #NautilusGtkPlacesView + * @local_only: %TRUE to hide remote locations, %FALSE to show. + * + * Sets the #NautilusGtkPlacesView::local-only property to @local_only. + * + * Since: 3.18 + */ +void +nautilus_gtk_places_view_set_local_only (NautilusGtkPlacesView *view, + gboolean local_only) +{ + NautilusGtkPlacesViewPrivate *priv; + + g_return_if_fail (GTK_IS_PLACES_VIEW (view)); + + priv = nautilus_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); + update_places (view); + + update_view_mode (view); + + g_object_notify_by_pspec (G_OBJECT (view), properties [PROP_LOCAL_ONLY]); + } +} diff --git a/src/gtk/nautilusgtkplacesview.ui b/src/gtk/nautilusgtkplacesview.ui new file mode 100644 index 000000000..5d7eb326d --- /dev/null +++ b/src/gtk/nautilusgtkplacesview.ui @@ -0,0 +1,290 @@ +/* Do not edit: this file is generated from https://git.gnome.org/browse/gtk+/plain/gtk/ui//gtkplacesview.ui */ + + + + + + + + + + + + + + completion_store + 1 + 1 + 0 + + + + + 1 + + + 1 + 1 + center + vertical + 18 + + + 1 + 48 + network-server-symbolic + + + + + + 1 + No recent servers found + + + + 1 + + + + + empty + + + + + 1 + 12 + vertical + 12 + + + 1 + Recent Servers + + + + + + + + 1 + 1 + 1 + in + 250 + 200 + + + 1 + none + + + 1 + 1 + none + + + + + + + + 1 + + + + + list + + + + + + + diff --git a/src/gtk/nautilusgtkplacesviewprivate.h b/src/gtk/nautilusgtkplacesviewprivate.h new file mode 100644 index 000000000..5ac8c6e29 --- /dev/null +++ b/src/gtk/nautilusgtkplacesviewprivate.h @@ -0,0 +1,87 @@ +/* Do not edit: this file is generated from https://git.gnome.org/browse/gtk+/plain/gtk//gtkplacesviewprivate.h */ + +/* nautilusgtkplacesview.h + * + * Copyright (C) 2015 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef NAUTILUS_GTK_PLACES_VIEW_H +#define NAUTILUS_GTK_PLACES_VIEW_H + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_GTK_PLACES_VIEW (nautilus_gtk_places_view_get_type ()) +#define NAUTILUS_GTK_PLACES_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_GTK_PLACES_VIEW, NautilusGtkPlacesView)) +#define NAUTILUS_GTK_PLACES_VIEW_CLASS(klass)(G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_GTK_PLACES_VIEW, NautilusGtkPlacesViewClass)) +#define GTK_IS_PLACES_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_GTK_PLACES_VIEW)) +#define GTK_IS_PLACES_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_GTK_PLACES_VIEW)) +#define NAUTILUS_GTK_PLACES_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_GTK_PLACES_VIEW, NautilusGtkPlacesViewClass)) + +typedef struct _NautilusGtkPlacesView NautilusGtkPlacesView; +typedef struct _NautilusGtkPlacesViewClass NautilusGtkPlacesViewClass; +typedef struct _NautilusGtkPlacesViewPrivate NautilusGtkPlacesViewPrivate; + +struct _NautilusGtkPlacesViewClass +{ + GtkBoxClass parent_class; + + void (* open_location) (NautilusGtkPlacesView *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 _NautilusGtkPlacesView +{ + GtkBox parent_instance; +}; + +GType nautilus_gtk_places_view_get_type (void) G_GNUC_CONST; + +GtkPlacesOpenFlags nautilus_gtk_places_view_get_open_flags (NautilusGtkPlacesView *view); +void nautilus_gtk_places_view_set_open_flags (NautilusGtkPlacesView *view, + GtkPlacesOpenFlags flags); + +const gchar* nautilus_gtk_places_view_get_search_query (NautilusGtkPlacesView *view); +void nautilus_gtk_places_view_set_search_query (NautilusGtkPlacesView *view, + const gchar *query_text); + +gboolean nautilus_gtk_places_view_get_local_only (NautilusGtkPlacesView *view); + +void nautilus_gtk_places_view_set_local_only (NautilusGtkPlacesView *view, + gboolean local_only); + +gboolean nautilus_gtk_places_view_get_loading (NautilusGtkPlacesView *view); + +GtkWidget * nautilus_gtk_places_view_new (void); + +G_END_DECLS + +#endif /* NAUTILUS_GTK_PLACES_VIEW_H */ diff --git a/src/gtk/nautilusgtkplacesviewrow.c b/src/gtk/nautilusgtkplacesviewrow.c new file mode 100644 index 000000000..6c308a94d --- /dev/null +++ b/src/gtk/nautilusgtkplacesviewrow.c @@ -0,0 +1,490 @@ +/* Do not edit: this file is generated from https://git.gnome.org/browse/gtk+/plain/gtk//gtkplacesviewrow.c */ + +/* nautilusgtkplacesviewrow.c + * + * Copyright (C) 2015 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include + +#include "nautilusgtkplacesviewrowprivate.h" + +/* As this widget is shared with Nautilus, we use this guard to + * ensure that internally we only include the files that we need + * instead of including gtk.h + */ +#ifdef GTK_COMPILATION +#include "gtkbutton.h" +#include "gtkeventbox.h" +#include "gtkimage.h" +#include "gtkintl.h" +#include "gtklabel.h" +#include "gtkspinner.h" +#include "gtktypebuiltins.h" +#else +#include +#endif + +struct _NautilusGtkPlacesViewRow +{ + GtkListBoxRow parent_instance; + + GtkLabel *available_space_label; + 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; + + GCancellable *cancellable; + + gint is_network : 1; +}; + +G_DEFINE_TYPE (NautilusGtkPlacesViewRow, nautilus_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 +measure_available_space_finished (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + NautilusGtkPlacesViewRow *row = user_data; + GFileInfo *info; + GError *error; + guint64 free_space; + guint64 total_space; + gchar *formatted_free_size; + gchar *formatted_total_size; + gchar *label; + guint plural_form; + + error = NULL; + + info = g_file_query_filesystem_info_finish (G_FILE (object), + res, + &error); + + if (error) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) && + !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) + { + g_warning ("Failed to measure available space: %s", error->message); + } + + g_clear_error (&error); + goto out; + } + + if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE) || + !g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE)) + { + g_object_unref (info); + goto out; + } + + free_space = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); + total_space = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); + + formatted_free_size = g_format_size (free_space); + formatted_total_size = g_format_size (total_space); + + /* read g_format_size code in glib for further understanding */ + plural_form = free_space < 1000 ? free_space : free_space % 1000 + 1000; + + /* Translators: respectively, free and total space of the drive. The plural form + * should be based on the free space available. + * i.e. 1 GB / 24 GB available. + */ + label = g_strdup_printf (ngettext ("%s / %s available", "%s / %s available", plural_form), + formatted_free_size, formatted_total_size); + + gtk_label_set_label (row->available_space_label, label); + + g_object_unref (info); + g_free (formatted_total_size); + g_free (formatted_free_size); + g_free (label); +out: + g_object_unref (object); +} + +static void +measure_available_space (NautilusGtkPlacesViewRow *row) +{ + gboolean should_measure; + + should_measure = (!row->is_network && (row->volume || row->mount || row->file)); + + gtk_label_set_label (row->available_space_label, ""); + gtk_widget_set_visible (GTK_WIDGET (row->available_space_label), should_measure); + + if (should_measure) + { + GFile *file = NULL; + + if (row->file) + { + file = g_object_ref (row->file); + } + else if (row->mount) + { + file = g_mount_get_root (row->mount); + } + else if (row->volume) + { + GMount *mount; + + mount = g_volume_get_mount (row->volume); + + if (mount) + file = g_mount_get_root (row->mount); + + g_clear_object (&mount); + } + + if (file) + { + g_cancellable_cancel (row->cancellable); + g_clear_object (&row->cancellable); + row->cancellable = g_cancellable_new (); + + g_file_query_filesystem_info_async (file, + G_FILE_ATTRIBUTE_FILESYSTEM_FREE "," G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, + G_PRIORITY_DEFAULT, + row->cancellable, + measure_available_space_finished, + row); + } + } +} + +static void +nautilus_gtk_places_view_row_finalize (GObject *object) +{ + NautilusGtkPlacesViewRow *self = NAUTILUS_GTK_PLACES_VIEW_ROW (object); + + g_cancellable_cancel (self->cancellable); + + g_clear_object (&self->volume); + g_clear_object (&self->mount); + g_clear_object (&self->file); + g_clear_object (&self->cancellable); + + G_OBJECT_CLASS (nautilus_gtk_places_view_row_parent_class)->finalize (object); +} + +static void +nautilus_gtk_places_view_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusGtkPlacesViewRow *self; + GIcon *icon; + + self = NAUTILUS_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 +nautilus_gtk_places_view_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusGtkPlacesViewRow *self = NAUTILUS_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)); + + /* + * When we hide the eject button, no size is allocated for it. Since + * we want to have alignment between rows, it needs an empty space + * when the eject button is not available. So, call then + * gtk_widget_set_child_visible(), which makes the button allocate the + * size but it stays hidden when needed. + */ + gtk_widget_set_child_visible (GTK_WIDGET (self->eject_button), self->mount != NULL); + measure_available_space (self); + break; + + case PROP_FILE: + g_set_object (&self->file, g_value_get_object (value)); + measure_available_space (self); + break; + + case PROP_IS_NETWORK: + nautilus_gtk_places_view_row_set_is_network (self, g_value_get_boolean (value)); + measure_available_space (self); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +nautilus_gtk_places_view_row_class_init (NautilusGtkPlacesViewRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = nautilus_gtk_places_view_row_finalize; + object_class->get_property = nautilus_gtk_places_view_row_get_property; + object_class->set_property = nautilus_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/gtk/libgtk/ui/nautilusgtkplacesviewrow.ui"); + + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, available_space_label); + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, busy_spinner); + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, eject_button); + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, eject_icon); + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, event_box); + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, icon_image); + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, name_label); + gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesViewRow, path_label); +} + +static void +nautilus_gtk_places_view_row_init (NautilusGtkPlacesViewRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} + +GtkWidget* +nautilus_gtk_places_view_row_new (GVolume *volume, + GMount *mount) +{ + return g_object_new (NAUTILUS_TYPE_GTK_PLACES_VIEW_ROW, + "volume", volume, + "mount", mount, + NULL); +} + +GMount* +nautilus_gtk_places_view_row_get_mount (NautilusGtkPlacesViewRow *row) +{ + g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL); + + return row->mount; +} + +GVolume* +nautilus_gtk_places_view_row_get_volume (NautilusGtkPlacesViewRow *row) +{ + g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL); + + return row->volume; +} + +GFile* +nautilus_gtk_places_view_row_get_file (NautilusGtkPlacesViewRow *row) +{ + g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL); + + return row->file; +} + +GtkWidget* +nautilus_gtk_places_view_row_get_eject_button (NautilusGtkPlacesViewRow *row) +{ + g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL); + + return GTK_WIDGET (row->eject_button); +} + +GtkWidget* +nautilus_gtk_places_view_row_get_event_box (NautilusGtkPlacesViewRow *row) +{ + g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), NULL); + + return GTK_WIDGET (row->event_box); +} + +void +nautilus_gtk_places_view_row_set_busy (NautilusGtkPlacesViewRow *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 +nautilus_gtk_places_view_row_get_is_network (NautilusGtkPlacesViewRow *row) +{ + g_return_val_if_fail (GTK_IS_PLACES_VIEW_ROW (row), FALSE); + + return row->is_network; +} + +void +nautilus_gtk_places_view_row_set_is_network (NautilusGtkPlacesViewRow *row, + gboolean is_network) +{ + if (row->is_network != is_network) + { + row->is_network = is_network; + + gtk_image_set_from_icon_name (row->eject_icon, "media-eject-symbolic", GTK_ICON_SIZE_BUTTON); + gtk_widget_set_tooltip_text (GTK_WIDGET (row->eject_button), is_network ? _("Disconnect") : _("Unmount")); + } +} + +void +nautilus_gtk_places_view_row_set_path_size_group (NautilusGtkPlacesViewRow *row, + GtkSizeGroup *group) +{ + if (group) + gtk_size_group_add_widget (group, GTK_WIDGET (row->path_label)); +} + +void +nautilus_gtk_places_view_row_set_space_size_group (NautilusGtkPlacesViewRow *row, + GtkSizeGroup *group) +{ + if (group) + gtk_size_group_add_widget (group, GTK_WIDGET (row->available_space_label)); +} diff --git a/src/gtk/nautilusgtkplacesviewrow.ui b/src/gtk/nautilusgtkplacesviewrow.ui new file mode 100644 index 000000000..f1a78c601 --- /dev/null +++ b/src/gtk/nautilusgtkplacesviewrow.ui @@ -0,0 +1,100 @@ +/* Do not edit: this file is generated from https://git.gnome.org/browse/gtk+/plain/gtk/ui//gtkplacesviewrow.ui */ + + + + + + diff --git a/src/gtk/nautilusgtkplacesviewrowprivate.h b/src/gtk/nautilusgtkplacesviewrowprivate.h new file mode 100644 index 000000000..854a712cf --- /dev/null +++ b/src/gtk/nautilusgtkplacesviewrowprivate.h @@ -0,0 +1,64 @@ +/* Do not edit: this file is generated from https://git.gnome.org/browse/gtk+/plain/gtk//gtkplacesviewrowprivate.h */ + +/* nautilusgtkplacesviewrow.h + * + * Copyright (C) 2015 Georges Basile Stavracas Neto + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef NAUTILUS_GTK_PLACES_VIEW_ROW_H +#define NAUTILUS_GTK_PLACES_VIEW_ROW_H + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#endif + +#include + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_GTK_PLACES_VIEW_ROW (nautilus_gtk_places_view_row_get_type()) + + G_DECLARE_FINAL_TYPE (NautilusGtkPlacesViewRow, nautilus_gtk_places_view_row, NAUTILUS, GTK_PLACES_VIEW_ROW, GtkListBoxRow) + +GtkWidget* nautilus_gtk_places_view_row_new (GVolume *volume, + GMount *mount); + +GtkWidget* nautilus_gtk_places_view_row_get_eject_button (NautilusGtkPlacesViewRow *row); + +GtkWidget* nautilus_gtk_places_view_row_get_event_box (NautilusGtkPlacesViewRow *row); + +GMount* nautilus_gtk_places_view_row_get_mount (NautilusGtkPlacesViewRow *row); + +GVolume* nautilus_gtk_places_view_row_get_volume (NautilusGtkPlacesViewRow *row); + +GFile* nautilus_gtk_places_view_row_get_file (NautilusGtkPlacesViewRow *row); + +void nautilus_gtk_places_view_row_set_busy (NautilusGtkPlacesViewRow *row, + gboolean is_busy); + +gboolean nautilus_gtk_places_view_row_get_is_network (NautilusGtkPlacesViewRow *row); + +void nautilus_gtk_places_view_row_set_is_network (NautilusGtkPlacesViewRow *row, + gboolean is_network); + +void nautilus_gtk_places_view_row_set_path_size_group (NautilusGtkPlacesViewRow *row, + GtkSizeGroup *group); + +void nautilus_gtk_places_view_row_set_space_size_group (NautilusGtkPlacesViewRow *row, + GtkSizeGroup *group); + +G_END_DECLS + +#endif /* NAUTILUS_GTK_PLACES_VIEW_ROW_H */ diff --git a/src/gtk/symbolconverter.sh b/src/gtk/symbolconverter.sh new file mode 100755 index 000000000..66b2b33d9 --- /dev/null +++ b/src/gtk/symbolconverter.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +# Fetch the GtkPlacesView files but rename the symbols to avoid symbol clashes + +URL=https://git.gnome.org/browse/gtk+/plain/gtk/ +URLUI=https://git.gnome.org/browse/gtk+/plain/gtk/ui/ + +update_file () { + _source="$1" + _dest="$2" + + echo "/* Do not edit: this file is generated from ${_source} */" > "${_dest}" + echo >> "${_dest}" + + curl "${_source}" | sed \ + -e 's/gtkplacesview/nautilusgtkplacesview/g' \ + -e 's/gtkplacesviewrow/nautilusgtkplacesviewrow/g' \ + -e 's/gtk_places_view/nautilus_gtk_places_view/g' \ + -e 's/GtkPlacesView/NautilusGtkPlacesView/g' \ + -e 's/GTK_PLACES_VIEW/NAUTILUS_GTK_PLACES_VIEW/g' \ + -e 's/GTK_TYPE_PLACES_VIEW/NAUTILUS_TYPE_GTK_PLACES_VIEW/g' \ + -e 's/_gtk_marshal_VOID__STRING_STRING/NULL/g' \ + -e '/gtkintl.h/d' \ + -e '/gtkmarshalers.h/d' \ + -e '/gtktypebuiltins.h/d' \ + -e 's///' \ + -e '/gtkplacessidebar.h/d' \ + -e '/#error /d' \ + -e "s/P_(\(.*\))/\1/" \ + -e "s/I_(\(.*\))/\1/" >> "${_dest}" +} + +update_file "${URL}/gtkplacesview.c" "nautilusgtkplacesview.c" +update_file "${URL}/gtkplacesviewprivate.h" "nautilusgtkplacesviewprivate.h" +update_file "${URLUI}/gtkplacesviewrow.ui" "nautilusgtkplacesviewrow.ui" + +update_file () { + _source="$1" + _dest="$2" + + echo "/* Do not edit: this file is generated from ${_source} */" > "${_dest}" + echo >> "${_dest}" + + curl "${_source}" | sed \ + -e 's/gtkplacesviewrow/nautilusgtkplacesviewrow/g' \ + -e 's/gtk_places_view_row/nautilus_gtk_places_view_row/g' \ + -e 's/GtkPlacesViewRow/NautilusGtkPlacesViewRow/g' \ + -e 's/GTK_PLACES_VIEW_ROW/NAUTILUS_GTK_PLACES_VIEW_ROW/g' \ + -e 's/GTK_TYPE_PLACES_VIEW_ROW/NAUTILUS_TYPE_GTK_PLACES_VIEW_ROW/g' \ + -e 's/G_DECLARE_FINAL_TYPE (NautilusGtkPlacesViewRow, nautilus_gtk_places_view_row, GTK, PLACES_VIEW_ROW, GtkListBoxRow/ G_DECLARE_FINAL_TYPE (NautilusGtkPlacesViewRow, nautilus_gtk_places_view_row, NAUTILUS, GTK_PLACES_VIEW_ROW, GtkListBoxRow/g' \ + -e 's/"gtkwidget.h"//' \ + -e '/gtksizegroup.h/d' \ + -e '/gtklistbox.h/d' \ + -e '/#error /d' \ + -e "s/P_(\(.*\))/\1/" \ + -e "s/I_(\(.*\))/\1/" >> "${_dest}" +} + +update_file "${URL}/gtkplacesviewrow.c" "nautilusgtkplacesviewrow.c" +update_file "${URL}/gtkplacesviewrowprivate.h" "nautilusgtkplacesviewrowprivate.h" +update_file "${URLUI}/gtkplacesview.ui" "nautilusgtkplacesview.ui" diff --git a/src/nautilus-places-view.c b/src/nautilus-places-view.c index 9a7cd1feb..b80549023 100644 --- a/src/nautilus-places-view.c +++ b/src/nautilus-places-view.c @@ -20,7 +20,7 @@ #include "nautilus-places-view.h" #include "nautilus-window-slot.h" #include "nautilus-application.h" -#include "gtk/gtkplacesviewprivate.h" +#include "gtk/nautilusgtkplacesviewprivate.h" typedef struct { @@ -251,7 +251,7 @@ nautilus_places_view_set_search_query (NautilusView *view, text = query ? nautilus_query_get_text (query) : NULL; - gtk_nautilus_places_view_set_search_query (GTK_NAUTILUS_PLACES_VIEW (priv->places_view), text); + nautilus_gtk_places_view_set_search_query (NAUTILUS_GTK_PLACES_VIEW (priv->places_view), text); g_free (text); } @@ -270,7 +270,7 @@ nautilus_places_view_is_loading (NautilusView *view) priv = nautilus_places_view_get_instance_private (NAUTILUS_PLACES_VIEW (view)); - return gtk_nautilus_places_view_get_loading (GTK_NAUTILUS_PLACES_VIEW (priv->places_view)); + return nautilus_gtk_places_view_get_loading (NAUTILUS_GTK_PLACES_VIEW (priv->places_view)); } static gboolean @@ -329,8 +329,8 @@ nautilus_places_view_init (NautilusPlacesView *self) priv->location = g_file_new_for_uri ("other-locations:///"); /* Places view */ - priv->places_view = gtk_nautilus_places_view_new (); - gtk_nautilus_places_view_set_open_flags (GTK_NAUTILUS_PLACES_VIEW (priv->places_view), + priv->places_view = nautilus_gtk_places_view_new (); + nautilus_gtk_places_view_set_open_flags (NAUTILUS_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); diff --git a/src/resources/nautilus.gresource.xml b/src/resources/nautilus.gresource.xml index 9463849a2..3104160cd 100644 --- a/src/resources/nautilus.gresource.xml +++ b/src/resources/nautilus.gresource.xml @@ -17,8 +17,8 @@ ui/nautilus-no-search-results.ui ui/nautilus-folder-is-empty.ui gtk/help-overlay.ui - ../gtk/gtkplacesview.ui - ../gtk/gtkplacesviewrow.ui + ../gtk/nautilusgtkplacesview.ui + ../gtk/nautilusgtkplacesviewrow.ui ../../icons/thumbnail_frame.png ../../icons/filmholes.png ../../icons/knob.png -- cgit v1.2.1