network-address-bar: Recycle widgetry (step 2/2)

The new design for Network view uses an address entry bar which is
similar to the one from Other Locations view. Instead of reinventing
the wheel, let's salvage some code.

In addition to formatting the code to nautilus style, this also makes
a couple of visual changes to match the design requests.

https://gitlab.gnome.org/GNOME/nautilus/-/issues/2785
This commit is contained in:
António Fernandes 2024-01-18 16:48:15 +00:00
parent 2e59a031c1
commit 62c4a979ba
9 changed files with 456 additions and 526 deletions

View file

@ -42,6 +42,7 @@ src/nautilus-location-banner.c
src/nautilus-location-entry.c
src/nautilus-main.c
src/nautilus-mime-actions.c
src/nautilus-network-address-bar.c
src/nautilus-new-folder-dialog.c
src/nautilus-operations-ui-manager.c
src/nautilus-pathbar.c
@ -80,6 +81,7 @@ src/resources/ui/nautilus-files-view.ui
src/resources/ui/nautilus-grid-cell.ui
src/resources/ui/nautilus-history-controls.ui
src/resources/ui/nautilus-name-cell.ui
src/resources/ui/nautilus-network-address-bar.ui
src/resources/ui/nautilus-operations-ui-manager-request-passphrase.ui
src/resources/ui/nautilus-pathbar-context-menu.ui
src/resources/ui/nautilus-preferences-window.ui

View file

@ -79,8 +79,6 @@ struct _NautilusGtkPlacesView
char *search_query;
GtkWidget *actionbar;
GtkWidget *address_entry;
GtkWidget *connect_button;
GtkWidget *listbox;
GtkWidget *popup_menu;
GtkWidget *recent_servers_listbox;
@ -88,7 +86,6 @@ struct _NautilusGtkPlacesView
GtkWidget *recent_servers_stack;
GtkWidget *stack;
GtkWidget *server_adresses_popover;
GtkWidget *available_protocols_grid;
GtkWidget *network_placeholder;
GtkWidget *network_placeholder_label;
@ -102,10 +99,6 @@ struct _NautilusGtkPlacesView
NautilusGtkPlacesViewRow *row_for_action;
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;
@ -158,14 +151,6 @@ enum {
LAST_SIGNAL
};
const char *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];
@ -180,15 +165,6 @@ emit_open_location (NautilusGtkPlacesView *view,
g_signal_emit (view, places_view_signals[OPEN_LOCATION], 0, location, open_flags);
}
static void
emit_show_error_message (NautilusGtkPlacesView *view,
char *primary_message,
char *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)
{
@ -401,9 +377,6 @@ nautilus_gtk_places_view_finalize (GObject *object)
{
NautilusGtkPlacesView *view = (NautilusGtkPlacesView *)object;
if (view->entry_pulse_timeout_id > 0)
g_source_remove (view->entry_pulse_timeout_id);
g_clear_pointer (&view->search_query, g_free);
g_clear_object (&view->server_list_file);
g_clear_object (&view->server_list_monitor);
@ -1180,102 +1153,6 @@ update_places (NautilusGtkPlacesView *view)
update_loading (view);
}
static void
server_mount_ready_cb (GObject *source_file,
GAsyncResult *res,
gpointer user_data)
{
NautilusGtkPlacesView *view = NAUTILUS_GTK_PLACES_VIEW (user_data);
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))
{
/* if it wasn't cancelled show a dialog */
emit_show_error_message (view, _("Unable to access location"), error->message);
}
/* The operation got cancelled by the user and or the error
has been handled already. */
g_clear_error (&error);
}
if (view->destroyed)
{
g_object_unref (view);
return;
}
view->should_pulse_entry = FALSE;
gtk_entry_set_progress_fraction (GTK_ENTRY (view->address_entry), 0);
/* Restore from Cancel to Connect */
gtk_button_set_label (GTK_BUTTON (view->connect_button), _("Con_nect"));
gtk_widget_set_sensitive (view->address_entry, TRUE);
view->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_editable_set_text (GTK_EDITABLE (view->address_entry), "");
if (view->should_open_location)
{
GMount *mount;
GFile *root;
/*
* If the mount is not found at this point, it is probably user-
* invisible, which happens e.g for smb-browse, but the location
* should be opened anyway...
*/
mount = g_file_find_enclosing_mount (location, view->cancellable, NULL);
if (mount)
{
root = g_mount_get_default_location (mount);
emit_open_location (view, root, view->open_flags);
g_object_unref (root);
g_object_unref (mount);
}
else
{
emit_open_location (view, location, view->open_flags);
}
}
}
update_places (view);
g_object_unref (view);
}
static void
volume_mount_ready_cb (GObject *source_volume,
GAsyncResult *res,
@ -1385,32 +1262,6 @@ unmount_ready_cb (GObject *source_mount,
g_object_unref (view);
}
static gboolean
pulse_entry_cb (gpointer user_data)
{
NautilusGtkPlacesView *view = NAUTILUS_GTK_PLACES_VIEW (user_data);
if (view->destroyed)
{
view->entry_pulse_timeout_id = 0;
return G_SOURCE_REMOVE;
}
else if (view->should_pulse_entry)
{
gtk_entry_progress_pulse (GTK_ENTRY (view->address_entry));
return G_SOURCE_CONTINUE;
}
else
{
gtk_entry_set_progress_fraction (GTK_ENTRY (view->address_entry), 0);
view->entry_pulse_timeout_id = 0;
return G_SOURCE_REMOVE;
}
}
static void
unmount_mount (NautilusGtkPlacesView *view,
GMount *mount)
@ -1439,51 +1290,6 @@ unmount_mount (NautilusGtkPlacesView *view,
g_object_unref (operation);
}
static void
mount_server (NautilusGtkPlacesView *view,
GFile *location)
{
GMountOperation *operation;
GtkWidget *toplevel;
g_cancellable_cancel (view->cancellable);
g_clear_object (&view->cancellable);
/* User cliked when the operation was ongoing, so wanted to cancel it */
if (view->connecting_to_server)
return;
view->cancellable = g_cancellable_new ();
toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (view)));
operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
view->should_pulse_entry = TRUE;
gtk_entry_set_progress_pulse_step (GTK_ENTRY (view->address_entry), 0.1);
gtk_entry_set_progress_fraction (GTK_ENTRY (view->address_entry), 0.1);
/* Allow to cancel the operation */
gtk_button_set_label (GTK_BUTTON (view->connect_button), _("Cance_l"));
gtk_widget_set_sensitive (view->address_entry, FALSE);
view->connecting_to_server = TRUE;
update_loading (view);
if (view->entry_pulse_timeout_id == 0)
view->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);
/* make sure we keep the view around for as long as we are running */
g_object_ref (view);
g_file_mount_enclosing_volume (location,
0,
operation,
view->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)
@ -1612,77 +1418,6 @@ unmount_cb (GtkWidget *widget,
unmount_mount (view, mount);
}
static void
attach_protocol_row_to_grid (GtkGrid *grid,
const char *protocol_name,
const char *protocol_prefix)
{
GtkWidget *name_label;
GtkWidget *prefix_label;
name_label = gtk_label_new (protocol_name);
gtk_widget_set_halign (name_label, GTK_ALIGN_START);
gtk_grid_attach_next_to (grid, name_label, NULL, GTK_POS_BOTTOM, 1, 1);
prefix_label = gtk_label_new (protocol_prefix);
gtk_widget_set_halign (prefix_label, GTK_ALIGN_START);
gtk_grid_attach_next_to (grid, prefix_label, name_label, GTK_POS_RIGHT, 1, 1);
}
static void
populate_available_protocols_grid (GtkGrid *grid)
{
const char * const *supported_protocols;
gboolean has_any = FALSE;
supported_protocols = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
if (g_strv_contains (supported_protocols, "afp"))
{
attach_protocol_row_to_grid (grid, _("AppleTalk"), "afp://");
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "ftp"))
{
attach_protocol_row_to_grid (grid, _("File Transfer Protocol"),
/* Translators: do not translate ftp:// and ftps:// */
_("ftp:// or ftps://"));
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "nfs"))
{
attach_protocol_row_to_grid (grid, _("Network File System"), "nfs://");
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "smb"))
{
attach_protocol_row_to_grid (grid, _("Samba"), "smb://");
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "ssh"))
{
attach_protocol_row_to_grid (grid, _("SSH File Transfer Protocol"),
/* Translators: do not translate sftp:// and ssh:// */
_("sftp:// or ssh://"));
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "dav"))
{
attach_protocol_row_to_grid (grid, _("WebDAV"),
/* Translators: do not translate dav:// and davs:// */
_("dav:// or davs://"));
has_any = TRUE;
}
if (!has_any)
gtk_widget_set_visible (GTK_WIDGET (grid), FALSE);
}
static GMenuModel *
get_menu_model (void)
{
@ -1920,71 +1655,6 @@ on_eject_button_clicked (GtkWidget *widget,
}
}
static void
on_connect_button_clicked (NautilusGtkPlacesView *view)
{
const char *uri;
GFile *file;
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 (view->connect_button))
return;
uri = gtk_editable_get_text (GTK_EDITABLE (view->address_entry));
if (uri != NULL && uri[0] != '\0')
file = g_file_new_for_commandline_arg (uri);
if (file)
{
view->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)
{
const char * const *supported_protocols;
char *address, *scheme;
gboolean supported;
supported = FALSE;
supported_protocols = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
address = g_strdup (gtk_editable_get_text (GTK_EDITABLE (view->address_entry)));
scheme = g_uri_parse_scheme (address);
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 (view->connect_button, supported);
if (scheme && !supported)
gtk_widget_add_css_class (view->address_entry, "error");
else
gtk_widget_remove_css_class (view->address_entry, "error");
g_free (address);
g_free (scheme);
}
static void
on_address_entry_show_help_pressed (NautilusGtkPlacesView *view,
GtkEntryIconPosition icon_pos,
@ -2254,16 +1924,6 @@ nautilus_gtk_places_view_constructed (GObject *object)
object);
}
static void
nautilus_gtk_places_view_map (GtkWidget *widget)
{
NautilusGtkPlacesView *view = NAUTILUS_GTK_PLACES_VIEW (widget);
gtk_editable_set_text (GTK_EDITABLE (view->address_entry), "");
GTK_WIDGET_CLASS (nautilus_gtk_places_view_parent_class)->map (widget);
}
static void
nautilus_gtk_places_view_class_init (NautilusGtkPlacesViewClass *klass)
{
@ -2276,8 +1936,6 @@ nautilus_gtk_places_view_class_init (NautilusGtkPlacesViewClass *klass)
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.
@ -2351,21 +2009,15 @@ nautilus_gtk_places_view_class_init (NautilusGtkPlacesViewClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/gtk/ui/nautilusgtkplacesview.ui");
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, actionbar);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, address_entry);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, address_entry_completion);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, completion_store);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, connect_button);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, listbox);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, recent_servers_listbox);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, recent_servers_popover);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, recent_servers_stack);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, stack);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, server_adresses_popover);
gtk_widget_class_bind_template_child (widget_class, NautilusGtkPlacesView, available_protocols_grid);
gtk_widget_class_bind_template_callback (widget_class, on_address_entry_text_changed);
gtk_widget_class_bind_template_callback (widget_class, on_address_entry_show_help_pressed);
gtk_widget_class_bind_template_callback (widget_class, on_connect_button_clicked);
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);
@ -2458,8 +2110,6 @@ nautilus_gtk_places_view_init (NautilusGtkPlacesView *self)
g_signal_connect (controller, "released",
G_CALLBACK (on_middle_click_row_event), self);
gtk_widget_add_controller (self->listbox, controller);
populate_available_protocols_grid (GTK_GRID (self->available_protocols_grid));
}
/*

View file

@ -165,6 +165,8 @@ libnautilus_sources = [
'nautilus-filename-validator.h',
'nautilus-rename-file-popover.c',
'nautilus-rename-file-popover.h',
'nautilus-network-address-bar.c',
'nautilus-network-address-bar.h',
'nautilus-network-directory.c',
'nautilus-network-directory.h',
'nautilus-new-folder-dialog.c',

View file

@ -0,0 +1,377 @@
/*
* Copyright (C) 2015 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
* Copyright (C) 2022 António Fernandes <antoniof@gnome.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "nautilus-network-address-bar.h"
#include <glib/gi18n.h>
#include <adwaita.h>
struct _NautilusNetworkAddressBar
{
GtkBox parent_instance;
GtkWidget *address_entry;
GtkWidget *connect_button;
GtkWidget *available_protocols_grid;
gboolean should_open_location;
gboolean should_pulse_entry;
gboolean connecting_to_server;
guint entry_pulse_timeout_id;
GCancellable *cancellable;
};
G_DEFINE_TYPE (NautilusNetworkAddressBar, nautilus_network_address_bar, GTK_TYPE_BOX)
const char *unsupported_protocols[] =
{
"afc", "archive", "burn", "computer", "file", "http", "localtest", "obex", "recent", "trash", NULL
};
static void
show_error_message (NautilusNetworkAddressBar *self,
const gchar *primary,
const gchar *secondary)
{
GtkRoot *window = gtk_widget_get_root (GTK_WIDGET (self));
GtkWidget *dialog = adw_message_dialog_new (GTK_WINDOW (window), primary, secondary);
adw_message_dialog_add_response (ADW_MESSAGE_DIALOG (dialog), "close", _("_Close"));
gtk_window_present (GTK_WINDOW (dialog));
}
static void
server_mount_ready_cb (GObject *source_file,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (NautilusNetworkAddressBar) self = NAUTILUS_NETWORK_ADDRESS_BAR (user_data);
gboolean should_show = TRUE;
g_autoptr (GError) error = NULL;
GFile *location = G_FILE (source_file);
g_file_mount_enclosing_volume_finish (location, res, &error);
if (error != NULL)
{
should_show = FALSE;
if (error->code == G_IO_ERROR_ALREADY_MOUNTED)
{
/*
* Already mounted volume is not a critical error
* and we can still continue with the operation.
*/
should_show = TRUE;
}
else if (error->domain != G_IO_ERROR ||
(error->code != G_IO_ERROR_CANCELLED &&
error->code != G_IO_ERROR_FAILED_HANDLED))
{
/* if it wasn't cancelled show a dialog */
show_error_message (self, _("Unable to access location"), error->message);
}
/* The operation got cancelled by the user or dispose() and or the error
* has been handled already. */
}
self->should_pulse_entry = FALSE;
gtk_entry_set_progress_fraction (GTK_ENTRY (self->address_entry), 0);
/* Restore from Cancel to Connect */
gtk_button_set_label (GTK_BUTTON (self->connect_button), _("Con_nect"));
gtk_widget_set_sensitive (self->address_entry, TRUE);
self->connecting_to_server = FALSE;
if (should_show)
{
#if 0
server_list_add_server (self, location);
#endif
/*
* 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_editable_set_text (GTK_EDITABLE (self->address_entry), "");
if (self->should_open_location)
{
g_autofree char *uri_to_open = NULL;
g_autoptr (GMount) mount = g_file_find_enclosing_mount (location, self->cancellable, NULL);
/*
* If the mount is not found at this point, it is probably user-
* invisible, which happens e.g for smb-browse, but the location
* should be opened anyway...
*/
if (mount != NULL)
{
g_autoptr (GFile) root = g_mount_get_default_location (mount);
uri_to_open = g_file_get_uri (root);
}
else
{
uri_to_open = g_file_get_uri (location);
}
gtk_widget_activate_action (GTK_WIDGET (self),
"win.open-location", "s", uri_to_open);
}
}
}
static gboolean
pulse_entry_cb (gpointer user_data)
{
NautilusNetworkAddressBar *self = NAUTILUS_NETWORK_ADDRESS_BAR (user_data);
if (self->should_pulse_entry)
{
gtk_entry_progress_pulse (GTK_ENTRY (self->address_entry));
return G_SOURCE_CONTINUE;
}
else
{
gtk_entry_set_progress_fraction (GTK_ENTRY (self->address_entry), 0);
self->entry_pulse_timeout_id = 0;
return G_SOURCE_REMOVE;
}
}
static void
mount_server (NautilusNetworkAddressBar *self,
GFile *location)
{
GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (self)));
g_autoptr (GMountOperation) operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
/* User cliked when the operation was ongoing, so wanted to cancel it */
if (self->connecting_to_server)
{
return;
}
self->cancellable = g_cancellable_new ();
self->should_pulse_entry = TRUE;
gtk_entry_set_progress_pulse_step (GTK_ENTRY (self->address_entry), 0.1);
gtk_entry_set_progress_fraction (GTK_ENTRY (self->address_entry), 0.1);
/* Allow to cancel the operation */
gtk_button_set_label (GTK_BUTTON (self->connect_button), _("Cance_l"));
gtk_widget_set_sensitive (self->address_entry, FALSE);
self->connecting_to_server = TRUE;
if (self->entry_pulse_timeout_id == 0)
{
self->entry_pulse_timeout_id = g_timeout_add (100, (GSourceFunc) pulse_entry_cb, self);
}
g_mount_operation_set_password_save (operation, G_PASSWORD_SAVE_FOR_SESSION);
/* make sure we keep the view around for as long as we are running */
g_file_mount_enclosing_volume (location,
0,
operation,
self->cancellable,
server_mount_ready_cb,
g_object_ref (self));
}
static void
on_connect_button_clicked (NautilusNetworkAddressBar *self)
{
/* 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 (self->connect_button))
{
return;
}
const char *uri = gtk_editable_get_text (GTK_EDITABLE (self->address_entry));
if (uri != NULL && uri[0] != '\0')
{
g_autoptr (GFile) file = g_file_new_for_commandline_arg (uri);
self->should_open_location = TRUE;
mount_server (self, file);
}
else
{
show_error_message (self, _("Unable to get remote server location"), NULL);
}
}
static void
on_address_entry_text_changed (NautilusNetworkAddressBar *self)
{
const char * const *supported_protocols = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
const char *address = gtk_editable_get_text (GTK_EDITABLE (self->address_entry));
g_autofree char *scheme = g_uri_parse_scheme (address);
gboolean supported = FALSE;
if (supported_protocols != NULL && scheme != NULL)
{
supported = g_strv_contains (supported_protocols, scheme) &&
!g_strv_contains (unsupported_protocols, scheme);
}
gtk_widget_set_sensitive (self->connect_button, supported);
if (scheme != NULL && !supported)
{
gtk_widget_add_css_class (self->address_entry, "error");
}
else
{
gtk_widget_remove_css_class (self->address_entry, "error");
}
}
static void
nautilus_network_address_bar_map (GtkWidget *widget)
{
NautilusNetworkAddressBar *self = NAUTILUS_NETWORK_ADDRESS_BAR (widget);
gtk_editable_set_text (GTK_EDITABLE (self->address_entry), "");
GTK_WIDGET_CLASS (nautilus_network_address_bar_parent_class)->map (widget);
}
static void
attach_protocol_row_to_grid (GtkGrid *grid,
const char *protocol_name,
const char *protocol_prefix)
{
GtkWidget *name_label = gtk_label_new (protocol_name);
GtkWidget *prefix_label = gtk_label_new (protocol_prefix);
gtk_widget_set_halign (name_label, GTK_ALIGN_START);
gtk_grid_attach_next_to (grid, name_label, NULL, GTK_POS_BOTTOM, 1, 1);
gtk_widget_set_halign (prefix_label, GTK_ALIGN_START);
gtk_grid_attach_next_to (grid, prefix_label, name_label, GTK_POS_RIGHT, 1, 1);
}
static void
populate_available_protocols_grid (GtkGrid *grid)
{
const char * const *supported_protocols = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
gboolean has_any = FALSE;
if (g_strv_contains (supported_protocols, "afp"))
{
attach_protocol_row_to_grid (grid, _("AppleTalk"), "afp://");
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "ftp"))
{
attach_protocol_row_to_grid (grid, _("File Transfer Protocol"),
/* Translators: do not translate ftp:// and ftps:// */
_("ftp:// or ftps://"));
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "nfs"))
{
attach_protocol_row_to_grid (grid, _("Network File System"), "nfs://");
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "smb"))
{
attach_protocol_row_to_grid (grid, _("Samba"), "smb://");
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "ssh"))
{
attach_protocol_row_to_grid (grid, _("SSH File Transfer Protocol"),
/* Translators: do not translate sftp:// and ssh:// */
_("sftp:// or ssh://"));
has_any = TRUE;
}
if (g_strv_contains (supported_protocols, "dav"))
{
attach_protocol_row_to_grid (grid, _("WebDAV"),
/* Translators: do not translate dav:// and davs:// */
_("dav:// or davs://"));
has_any = TRUE;
}
if (!has_any)
{
gtk_widget_set_visible (GTK_WIDGET (grid), FALSE);
}
}
static void
nautilus_network_address_bar_dispose (GObject *object)
{
NautilusNetworkAddressBar *self = NAUTILUS_NETWORK_ADDRESS_BAR (object);
g_cancellable_cancel (self->cancellable);
g_clear_object (&self->cancellable);
g_clear_handle_id (&self->entry_pulse_timeout_id, g_source_remove);
G_OBJECT_CLASS (nautilus_network_address_bar_parent_class)->dispose (object);
}
static void
nautilus_network_address_bar_finalize (GObject *object)
{
G_OBJECT_CLASS (nautilus_network_address_bar_parent_class)->finalize (object);
}
static void
nautilus_network_address_bar_init (NautilusNetworkAddressBar *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
populate_available_protocols_grid (GTK_GRID (self->available_protocols_grid));
}
static void
nautilus_network_address_bar_class_init (NautilusNetworkAddressBarClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->finalize = nautilus_network_address_bar_finalize;
object_class->dispose = nautilus_network_address_bar_dispose;
widget_class->map = nautilus_network_address_bar_map;
/* Bind class to template */
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-network-address-bar.ui");
gtk_widget_class_bind_template_child (widget_class, NautilusNetworkAddressBar, address_entry);
gtk_widget_class_bind_template_child (widget_class, NautilusNetworkAddressBar, connect_button);
gtk_widget_class_bind_template_child (widget_class, NautilusNetworkAddressBar, available_protocols_grid);
gtk_widget_class_bind_template_callback (widget_class, on_address_entry_text_changed);
gtk_widget_class_bind_template_callback (widget_class, on_connect_button_clicked);
}
NautilusNetworkAddressBar *
nautilus_network_address_bar_new (void)
{
return g_object_new (NAUTILUS_TYPE_NETWORK_ADDRESS_BAR, NULL);
}

View file

@ -0,0 +1,19 @@
/*
* Copyright (C) 2022 António Fernandes <antoniof@gnome.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define NAUTILUS_TYPE_NETWORK_ADDRESS_BAR (nautilus_network_address_bar_get_type())
G_DECLARE_FINAL_TYPE (NautilusNetworkAddressBar, nautilus_network_address_bar, NAUTILUS, NETWORK_ADDRESS_BAR, GtkBox)
NautilusNetworkAddressBar * nautilus_network_address_bar_new (void);
G_END_DECLS

View file

@ -55,6 +55,7 @@
#include "nautilus-global-preferences.h"
#include "nautilus-location-entry.h"
#include "nautilus-metadata.h"
#include "nautilus-network-address-bar.h"
#include "nautilus-mime-actions.h"
#include "nautilus-module.h"
#include "nautilus-pathbar.h"
@ -118,6 +119,8 @@ struct _NautilusWindow
GtkWidget *toolbar;
gboolean temporary_navigation_bar;
GtkWidget *network_address_bar;
/* focus widget before the location bar has been shown temporarily */
GtkWidget *last_focus_widget;
@ -1152,6 +1155,9 @@ nautilus_window_sync_location_widgets (NautilusWindow *window)
path_bar = nautilus_toolbar_get_path_bar (NAUTILUS_TOOLBAR (window->toolbar));
nautilus_path_bar_set_path (NAUTILUS_PATH_BAR (path_bar), location);
gtk_widget_set_visible (window->network_address_bar,
g_file_has_uri_scheme (location, SCHEME_NETWORK_VIEW));
}
enabled = (nautilus_window_slot_get_back_history (slot) != NULL &&
@ -2280,6 +2286,7 @@ nautilus_window_init (NautilusWindow *window)
GtkEventController *controller;
GtkPadController *pad_controller;
g_type_ensure (NAUTILUS_TYPE_NETWORK_ADDRESS_BAR);
g_type_ensure (NAUTILUS_TYPE_TOOLBAR);
g_type_ensure (NAUTILUS_TYPE_GTK_PLACES_SIDEBAR);
g_type_ensure (NAUTILUS_TYPE_PROGRESS_INDICATOR);
@ -2421,6 +2428,7 @@ nautilus_window_class_init (NautilusWindowClass *class)
gtk_widget_class_bind_template_child (wclass, NautilusWindow, toast_overlay);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, tab_view);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, tab_bar);
gtk_widget_class_bind_template_child (wclass, NautilusWindow, network_address_bar);
gtk_widget_class_bind_template_callback (wclass, window_set_back_forward_accelerators);

View file

@ -27,6 +27,7 @@
<file>ui/nautilus-operations-ui-manager-request-passphrase.ui</file>
<file>ui/nautilus-grid-cell.ui</file>
<file>ui/nautilus-name-cell.ui</file>
<file>ui/nautilus-network-address-bar.ui</file>
<file alias="gtk/ui/nautilusgtksidebarrow.ui">../gtk/nautilusgtksidebarrow.ui</file>
<file alias="icons/filmholes.png">../../icons/filmholes.png</file>
<file>style.css</file>

View file

@ -1,33 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkListStore" id="completion_store">
<columns>
<column type="gchararray"/>
<column type="gchararray"/>
</columns>
</object>
<object class="GtkEntryCompletion" id="address_entry_completion">
<property name="model">completion_store</property>
<property name="text-column">1</property>
<property name="inline-completion">1</property>
<property name="popup-completion">0</property>
</object>
<object class="GtkPopover" id="server_adresses_popover">
<property name="position">2</property>
<property name="position">top</property>
<child>
<object class="GtkBox">
<property name="orientation">1</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<property name="margin-start">18</property>
<property name="margin-end">18</property>
<property name="margin-top">18</property>
<property name="margin-bottom">18</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<child>
<object class="GtkLabel">
<property name="hexpand">1</property>
<property name="hexpand">true</property>
<property name="label" translatable="yes">Server Addresses</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="weight" value="bold"></attribute>
</attributes>
<style>
<class name="dim-label"/>
@ -36,9 +24,9 @@
</child>
<child>
<object class="GtkLabel">
<property name="hexpand">1</property>
<property name="hexpand">true</property>
<property name="label" translatable="yes">Server addresses are made up of a protocol prefix and an address. Examples:</property>
<property name="wrap">1</property>
<property name="wrap">true</property>
<property name="width-chars">40</property>
<property name="max-width-chars">40</property>
<property name="xalign">0</property>
@ -46,9 +34,9 @@
</child>
<child>
<object class="GtkLabel">
<property name="hexpand">1</property>
<property name="hexpand">true</property>
<property name="label">smb://gnome.org, ssh://192.168.0.1, ftp://[2001:db8::1]</property>
<property name="wrap">1</property>
<property name="wrap">true</property>
<property name="width-chars">40</property>
<property name="max-width-chars">40</property>
<property name="xalign">0</property>
@ -57,16 +45,16 @@
<child>
<object class="GtkGrid" id="available_protocols_grid">
<property name="margin-top">12</property>
<property name="hexpand">1</property>
<property name="hexpand">true</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel">
<property name="hexpand">1</property>
<property name="hexpand">true</property>
<property name="label" translatable="yes">Available Protocols</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="weight" value="bold"></attribute>
</attributes>
<layout>
<property name="column">0</property>
@ -79,7 +67,7 @@
<property name="label" translatable="yes">Prefix</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="weight" value="bold"></attribute>
</attributes>
<layout>
<property name="column">1</property>
@ -92,162 +80,40 @@
</object>
</child>
</object>
<object class="GtkPopover" id="recent_servers_popover">
<template class="NautilusNetworkAddressBar" parent="GtkBox">
<property name="hexpand">true</property>
<style>
<class name="toolbar"/>
</style>
<child>
<object class="GtkStack" id="recent_servers_stack">
<child>
<object class="GtkStackPage">
<property name="name">empty</property>
<property name="child">
<object class="AdwStatusPage">
<property name="icon-name">network-server-symbolic</property>
<property name="title" translatable="yes" comments="Translators: Server as any successfully connected network address">No Recent Servers</property>
<style>
<class name="compact"/>
</style>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">list</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">1</property>
<property name="spacing">12</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Recent Servers</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="vexpand">1</property>
<property name="has-frame">1</property>
<property name="min-content-width">250</property>
<property name="min-content-height">200</property>
<child>
<object class="GtkViewport">
<child>
<object class="GtkListBox" id="recent_servers_listbox">
<property name="selection-mode">0</property>
<signal name="row-activated" handler="on_recent_servers_listbox_row_activated" object="NautilusGtkPlacesView" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
<template class="NautilusGtkPlacesView" parent="GtkBox">
<accessibility>
<property name="label" translatable="yes">Other Locations</property>
<property name="description" translatable="yes">List of common local and remote mountpoints.</property>
</accessibility>
<property name="orientation">1</property>
<child>
<object class="GtkStack" id="stack">
<property name="vhomogeneous">0</property>
<property name="transition-type">1</property>
<child>
<object class="GtkStackPage">
<property name="name">browse</property>
<property name="child">
<object class="GtkScrolledWindow">
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<child>
<object class="GtkViewport">
<child>
<object class="GtkListBox" id="listbox">
<property name="selection-mode">0</property>
<signal name="row-activated" handler="on_listbox_row_activated" object="NautilusGtkPlacesView" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">empty-search</property>
<property name="child">
<object class="AdwStatusPage">
<property name="icon-name">edit-find-symbolic</property>
<property name="title" translatable="yes">No Results Found</property>
<property name="description" translatable="yes">Try a different search.</property>
</object>
</property>
</object>
</child>
<object class="GtkEntry" id="address_entry">
<property name="input-purpose">url</property>
<property name="input-hints">no-spellcheck | no-emoji</property>
<property name="width-chars">10</property>
<property name="max-width-chars">34</property>
<property name="placeholder-text" translatable="yes">Server address</property>
<signal name="notify::text" handler="on_address_entry_text_changed" object="NautilusNetworkAddressBar" swapped="yes"/>
<signal name="activate" handler="on_connect_button_clicked" object="NautilusNetworkAddressBar" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkSeparator"/>
<object class="GtkButton" id="connect_button">
<property name="label" translatable="yes">Con_nect</property>
<property name="use-underline">true</property>
<property name="can-shrink">true</property>
<property name="sensitive">false</property>
<property name="valign">center</property>
<signal name="clicked" handler="on_connect_button_clicked" object="NautilusNetworkAddressBar" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkBox" id="actionbar">
<property name="hexpand">1</property>
<object class="GtkMenuButton">
<property name="popover">server_adresses_popover</property>
<property name="icon-name">dialog-question-symbolic</property>
<style>
<class name="toolbar"/>
<class name="flat"/>
<class name="circular"/>
</style>
<child>
<object class="GtkBox">
<child>
<object class="GtkEntry" id="address_entry">
<property name="width-chars">10</property>
<property name="max-width-chars">34</property>
<property name="placeholder-text" translatable="yes">Enter server address…</property>
<property name="secondary-icon-name">dialog-question-symbolic</property>
<property name="completion">address_entry_completion</property>
<property name="input-purpose">url</property>
<property name="input-hints">no-spellcheck | no-emoji</property>
<signal name="notify::text" handler="on_address_entry_text_changed" object="NautilusGtkPlacesView" swapped="yes"/>
<signal name="activate" handler="on_connect_button_clicked" object="NautilusGtkPlacesView" swapped="yes"/>
<signal name="icon-press" handler="on_address_entry_show_help_pressed" object="NautilusGtkPlacesView" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkMenuButton" id="server_list_button">
<property name="direction">0</property>
<property name="popover">recent_servers_popover</property>
<property name="icon-name">pan-down-symbolic</property>
<style>
<class name="server-list-button"/>
</style>
</object>
</child>
<style>
<class name="linked"/>
</style>
</object>
</child>
<child>
<object class="GtkButton" id="connect_button">
<property name="label" translatable="yes">Con_nect</property>
<property name="use-underline">1</property>
<property name="can-shrink">true</property>
<property name="sensitive">0</property>
<property name="valign">3</property>
<signal name="clicked" handler="on_connect_button_clicked" object="NautilusGtkPlacesView" swapped="yes"/>
</object>
</child>
</object>
</child>
</template>

View file

@ -207,6 +207,11 @@
<property name="menu-model">tab_menu_model</property>
</object>
</property>
<child type="bottom">
<object class="NautilusNetworkAddressBar" id="network_address_bar">
<property name="visible">False</property>
</object>
</child>
<child type="bottom">
<object class="GtkActionBar" id="action_bar">
<property name="revealed">False</property>