1
0
mirror of https://gitlab.gnome.org/GNOME/nautilus synced 2024-06-30 23:46:35 +00:00

network-directory: Provide recent servers

These used to be displayed in a popover attached to the address entry
in the old Other Locations view.

The new design asks for them to be provided in-line with other items
in the view.

As such, reuse and expand the recent servers code to produce GFileInfo
models for the NautilusNetworkDirectory to create new virtual files
from. Unlike the virtual files aggregated sourced from the network:///
and computer:/// GVFS backends, these virtual NautilusFiles have no
corresponding GVFS presence, and are entirely "nautilus-land" files.

Big credit goes to the people who designed the nautilus directory and
file abstractions with this in mind.
This commit is contained in:
António Fernandes 2024-01-19 21:04:29 +00:00
parent a0a54a72dd
commit 0a78f8cee2
3 changed files with 302 additions and 14 deletions

View File

@ -9,8 +9,10 @@
#include <gio/gio.h>
#include "nautilus-directory-private.h"
#include "nautilus-file-private.h"
#include "nautilus-file-utilities.h"
#include "nautilus-internal-place-file.h"
#include "nautilus-recent-servers.h"
#include "nautilus-scheme.h"
@ -24,6 +26,10 @@ struct _NautilusNetworkDirectory
NautilusDirectory *network_backend_directory;
gboolean network_backend_done_loading;
NautilusRecentServers *recent_servers;
GList *recent_server_files;
gboolean recent_servers_done_loading;
GList /*<owned NetworkCallback>*/ *callback_list;
};
@ -98,7 +104,8 @@ real_are_all_files_seen (NautilusDirectory *directory)
NautilusNetworkDirectory *self = NAUTILUS_NETWORK_DIRECTORY (directory);
return (nautilus_directory_are_all_files_seen (self->computer_backend_directory) &&
nautilus_directory_are_all_files_seen (self->network_backend_directory));
nautilus_directory_are_all_files_seen (self->network_backend_directory) &&
self->recent_servers_done_loading);
}
static gboolean
@ -107,6 +114,11 @@ real_contains_file (NautilusDirectory *directory,
{
NautilusNetworkDirectory *self = NAUTILUS_NETWORK_DIRECTORY (directory);
if (nautilus_file_get_directory (file) == directory)
{
/* Recent server files are directly owned by NautilusNetworkDirectory. */
return TRUE;
}
if (nautilus_directory_contains_file (self->network_backend_directory, file))
{
return TRUE;
@ -135,11 +147,14 @@ on_backend_directory_done_loading (NautilusDirectory *backend_directory,
}
else
{
g_assert_not_reached ();
/* Called from on_recent_servers_loading_changed () */
g_assert (backend_directory == (NautilusDirectory *) self &&
self->recent_servers_done_loading);
}
if (self->computer_backend_done_loading &&
self->network_backend_done_loading)
self->network_backend_done_loading &&
self->recent_servers_done_loading)
{
nautilus_directory_emit_done_loading (NAUTILUS_DIRECTORY (self));
}
@ -155,6 +170,9 @@ real_force_reload (NautilusDirectory *directory)
self->network_backend_done_loading = FALSE;
nautilus_directory_force_reload (self->network_backend_directory);
self->recent_servers_done_loading = FALSE;
nautilus_recent_servers_force_reload (self->recent_servers);
}
static void
@ -175,11 +193,13 @@ on_backend_directory_ready (NautilusDirectory *backend_directory,
}
else
{
g_assert_not_reached ();
g_assert (backend_directory == (NautilusDirectory *) self &&
network_callback->self->recent_servers_done_loading);
}
if (network_callback->computer_backend_ready &&
network_callback->network_backend_ready)
network_callback->network_backend_ready &&
network_callback->self->recent_servers_done_loading)
{
g_autolist (NautilusFile) files = nautilus_directory_get_file_list (NAUTILUS_DIRECTORY (self));
@ -192,6 +212,29 @@ on_backend_directory_ready (NautilusDirectory *backend_directory,
}
}
static void
on_recent_servers_loading_changed (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
NautilusNetworkDirectory *self = NAUTILUS_NETWORK_DIRECTORY (user_data);
NautilusRecentServers *recent_servers = NAUTILUS_RECENT_SERVERS (object);
gboolean is_loading = nautilus_recent_servers_get_loading (recent_servers);
self->recent_servers_done_loading = !is_loading;
if (self->recent_servers_done_loading)
{
NautilusDirectory *self_as_directory = NAUTILUS_DIRECTORY (self);
on_backend_directory_done_loading (self_as_directory, self);
for (GList *l = self->callback_list; l != NULL; l = l->next)
{
on_backend_directory_ready (self_as_directory, NULL, l->data);
}
}
}
static void
on_computer_backend_directory_files_added (NautilusNetworkDirectory *self,
GList *added_files)
@ -220,6 +263,94 @@ on_computer_backend_directory_files_changed (NautilusNetworkDirectory *self,
nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (self), files);
}
static NautilusFile *
get_recent_server_file (NautilusDirectory *directory,
GFileInfo *server_info)
{
g_autofree char *uri = g_strconcat (SCHEME_NETWORK_VIEW ":///",
g_file_info_get_name (server_info),
NULL);
g_autoptr (NautilusFile) file = nautilus_file_get_by_uri (uri);
nautilus_file_update_info (file, server_info);
return g_steal_pointer (&file);
}
static void
on_recent_servers_added (NautilusNetworkDirectory *self,
GList *servers)
{
NautilusDirectory *dir = NAUTILUS_DIRECTORY (self);
g_autolist (NautilusFile) added_files = NULL;
for (GList *l = servers; l != NULL; l = l->next)
{
GFileInfo *server_info = l->data;
g_autoptr (NautilusFile) file = get_recent_server_file (dir, server_info);
self->recent_server_files = g_list_prepend (self->recent_server_files,
g_object_ref (file));
added_files = g_list_prepend (added_files, g_steal_pointer (&file));
}
nautilus_directory_emit_files_added (dir, added_files);
}
static void
on_recent_servers_changed (NautilusNetworkDirectory *self,
GList *servers)
{
g_autolist (NautilusFile) changed_files = NULL;
for (GList *l = servers; l != NULL; l = l->next)
{
GFileInfo *server_info = l->data;
NautilusFile *file = nautilus_directory_find_file_by_name (NAUTILUS_DIRECTORY (self),
g_file_info_get_name (server_info));
if (file == NULL)
{
g_critical ("Notified change on recent server whose random GUID name was not known yet");
continue;
}
nautilus_file_update_info (file, server_info);
changed_files = g_list_prepend (changed_files, g_object_ref (file));
}
nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (self), changed_files);
}
static void
on_recent_servers_removed (NautilusNetworkDirectory *self,
GList *servers)
{
g_autolist (NautilusFile) removed_files = NULL;
for (GList *l = servers; l != NULL; l = l->next)
{
GFileInfo *server_info = l->data;
NautilusFile *file = nautilus_directory_find_file_by_name (NAUTILUS_DIRECTORY (self),
g_file_info_get_name (server_info));
if (file == NULL)
{
g_critical ("Notified removal of recent server whose random GUID name was not known yet");
continue;
}
nautilus_file_mark_gone (file);
/* Steal file from self->recent_server_files */
GList *link = g_list_find (self->recent_server_files, file);
self->recent_server_files = g_list_remove_link (self->recent_server_files, link);
removed_files = g_list_concat (link, removed_files);
}
nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (self), removed_files);
}
static NetworkCallback *
network_callback_find (NautilusNetworkDirectory *self,
NautilusDirectoryCallback callback,
@ -321,6 +452,7 @@ real_file_monitor_add (NautilusDirectory *directory,
monitor_hidden_files,
file_attributes,
NULL, NULL);
if (callback != NULL)
{
g_autolist (NautilusFile) files = nautilus_directory_get_file_list (directory);
@ -345,9 +477,11 @@ real_get_file_list (NautilusDirectory *directory)
NautilusNetworkDirectory *self = NAUTILUS_NETWORK_DIRECTORY (directory);
g_autolist (NautilusFile) computer_list = nautilus_directory_get_file_list (self->computer_backend_directory);
g_autolist (NautilusFile) network_list = nautilus_directory_get_file_list (self->network_backend_directory);
g_autolist (NautilusFile) recent_servers = nautilus_file_list_copy (self->recent_server_files);
return g_list_concat (get_remote_mountables (computer_list),
g_steal_pointer (&network_list));
g_list_concat (g_steal_pointer (&recent_servers),
g_steal_pointer (&network_list)));
}
static gboolean
@ -355,7 +489,8 @@ real_is_not_empty (NautilusDirectory *directory)
{
NautilusNetworkDirectory *self = NAUTILUS_NETWORK_DIRECTORY (directory);
if (nautilus_directory_is_not_empty (self->network_backend_directory))
if (self->recent_server_files != NULL ||
nautilus_directory_is_not_empty (self->network_backend_directory))
{
return TRUE;
}
@ -406,6 +541,7 @@ nautilus_network_directory_dispose (GObject *object)
{
NautilusNetworkDirectory *self = NAUTILUS_NETWORK_DIRECTORY (object);
g_clear_list (&self->recent_server_files, g_object_unref);
g_clear_list (&self->callback_list, g_free);
G_OBJECT_CLASS (nautilus_network_directory_parent_class)->dispose (object);
@ -418,6 +554,7 @@ nautilus_network_directory_finalize (GObject *object)
g_clear_object (&self->computer_backend_directory);
g_clear_object (&self->network_backend_directory);
g_clear_object (&self->recent_servers);
G_OBJECT_CLASS (nautilus_network_directory_parent_class)->finalize (object);
}
@ -444,6 +581,16 @@ nautilus_network_directory_init (NautilusNetworkDirectory *self)
G_CALLBACK (on_backend_directory_done_loading), self, G_CONNECT_DEFAULT);
g_signal_connect_object (self->network_backend_directory, "load-error",
G_CALLBACK (nautilus_directory_emit_load_error), self, G_CONNECT_SWAPPED);
self->recent_servers = nautilus_recent_servers_new ();
g_signal_connect_object (self->recent_servers, "notify::loading",
G_CALLBACK (on_recent_servers_loading_changed), self, G_CONNECT_DEFAULT);
g_signal_connect_object (self->recent_servers, "added",
G_CALLBACK (on_recent_servers_added), self, G_CONNECT_SWAPPED);
g_signal_connect_object (self->recent_servers, "changed",
G_CALLBACK (on_recent_servers_changed), self, G_CONNECT_SWAPPED);
g_signal_connect_object (self->recent_servers, "removed",
G_CALLBACK (on_recent_servers_removed), self, G_CONNECT_SWAPPED);
}
static void
@ -472,5 +619,9 @@ nautilus_network_directory_class_init (NautilusNetworkDirectoryClass *klass)
NautilusNetworkDirectory *
nautilus_network_directory_new (void)
{
return g_object_new (NAUTILUS_TYPE_NETWORK_DIRECTORY, NULL);
g_autoptr (GFile) location = g_file_new_for_uri (SCHEME_NETWORK_VIEW ":///");
return g_object_new (NAUTILUS_TYPE_NETWORK_DIRECTORY,
"location", location,
NULL);
}

View File

@ -16,6 +16,8 @@ struct _NautilusRecentServers
{
GObject parent_instance;
GHashTable *server_infos;
GFile *server_list_file;
GFileMonitor *server_list_monitor;
@ -36,7 +38,16 @@ enum
LAST_PROP
};
enum
{
ADDED,
CHANGED,
REMOVED,
LAST_SIGNAL
};
static GParamSpec *properties[LAST_PROP];
static guint signals[LAST_SIGNAL];
static void
server_file_changed_cb (NautilusRecentServers *self)
@ -185,6 +196,7 @@ nautilus_recent_servers_finalize (GObject *object)
g_clear_object (&self->server_list_file);
g_clear_object (&self->server_list_monitor);
g_clear_pointer (&self->server_infos, g_hash_table_destroy);
G_OBJECT_CLASS (nautilus_recent_servers_parent_class)->finalize (object);
}
@ -225,6 +237,40 @@ nautilus_recent_servers_get_property (GObject *object,
}
}
static GFileInfo *
server_file_info_new (const char *uri)
{
GFileInfo *info = g_file_info_new ();
g_autofree char *random_name = g_dbus_generate_guid ();
g_autoptr (GIcon) icon = g_themed_icon_new ("folder-remote");
g_autoptr (GIcon) symbolic_icon = g_themed_icon_new ("folder-remote-symbolic");
g_file_info_set_name (info, random_name);
g_file_info_set_icon (info, icon);
g_file_info_set_symbolic_icon (info, symbolic_icon);
g_file_info_set_content_type (info, "inode/directory");
g_file_info_set_file_type (info, G_FILE_TYPE_SHORTCUT);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL, TRUE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, uri);
return info;
}
static int
_date_time_equal_steal_1st (GDateTime *date_time1__to_be_stolen,
GDateTime *date_time2)
{
g_autoptr (GDateTime) date_time1 = date_time1__to_be_stolen;
return ((date_time1 == NULL && date_time2 == NULL) ||
g_date_time_equal (date_time1, date_time2));
}
static void
populate_servers (NautilusRecentServers *self)
{
@ -239,6 +285,8 @@ populate_servers (NautilusRecentServers *self)
return;
}
nautilus_recent_servers_set_loading (self, TRUE);
uris = g_bookmark_file_get_uris (server_list, &num_uris);
if (!uris)
@ -248,17 +296,69 @@ populate_servers (NautilusRecentServers *self)
return;
}
/* clear previous items */
g_autoptr (GList) old_infos = g_hash_table_get_values (self->server_infos);
g_autoptr (GList) new_infos = NULL;
g_autoptr (GList) changed_infos = NULL;
for (gsize i = 0; i < num_uris; i++)
{
char *name;
char *dup_uri;
const gchar *uri = uris[i];
GFileInfo *info = g_hash_table_lookup (self->server_infos, uri);
gboolean new = FALSE;
name = g_bookmark_file_get_title (server_list, uris[i], NULL);
dup_uri = g_strdup (uris[i]);
if (info != NULL)
{
old_infos = g_list_remove (old_infos, info);
}
else
{
new = TRUE;
info = server_file_info_new (uri);
g_hash_table_insert (self->server_infos, g_strdup (uri), info);
new_infos = g_list_prepend (new_infos, info);
}
g_free (name);
g_autofree char *name = g_bookmark_file_get_title (server_list, uri, NULL);
GDateTime *added = g_bookmark_file_get_added_date_time (server_list, uri, NULL);
GDateTime *visited = g_bookmark_file_get_visited_date_time (server_list, uri, NULL);
GDateTime *modified = g_bookmark_file_get_modified_date_time (server_list, uri, NULL);
if (new ||
g_strcmp0 (g_file_info_get_display_name (info), name) != 0 ||
!_date_time_equal_steal_1st (g_file_info_get_creation_date_time (info), added) ||
!_date_time_equal_steal_1st (g_file_info_get_access_date_time (info), visited) ||
!_date_time_equal_steal_1st (g_file_info_get_modification_date_time (info), modified))
{
g_file_info_set_display_name (info, name);
g_file_info_set_creation_date_time (info, added);
g_file_info_set_access_date_time (info, visited);
g_file_info_set_modification_date_time (info, modified);
if (!new)
{
changed_infos = g_list_prepend (changed_infos, info);
}
}
}
if (old_infos != NULL)
{
g_signal_emit (self, signals[REMOVED], 0, old_infos);
}
if (changed_infos != NULL)
{
g_signal_emit (self, signals[CHANGED], 0, changed_infos);
}
if (new_infos != NULL)
{
g_signal_emit (self, signals[ADDED], 0, new_infos);
}
for (GList *l = old_infos; l != NULL; l = l->next)
{
const char *old_uri = g_file_info_get_attribute_string (l->data, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
g_hash_table_remove (self->server_infos, old_uri);
}
nautilus_recent_servers_set_loading (self, FALSE);
@ -284,11 +384,28 @@ nautilus_recent_servers_class_init (NautilusRecentServersClass *klass)
G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
g_object_class_install_properties (object_class, LAST_PROP, properties);
signals[ADDED] = g_signal_new ("added",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
signals[CHANGED] = g_signal_new ("changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
signals[REMOVED] = g_signal_new ("removed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
}
static void
nautilus_recent_servers_init (NautilusRecentServers *self)
{
self->server_infos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}
NautilusRecentServers *
@ -297,6 +414,24 @@ nautilus_recent_servers_new (void)
return g_object_new (NAUTILUS_TYPE_RECENT_SERVERS, NULL);
}
void
nautilus_recent_servers_force_reload (NautilusRecentServers *self)
{
populate_servers (self);
}
/* Returns: (transfer full): List of server infos. */
GList *
nautilus_recent_servers_get_infos (NautilusRecentServers *self)
{
g_return_val_if_fail (NAUTILUS_IS_RECENT_SERVERS (self), FALSE);
GList *server_infos = g_hash_table_get_values (self->server_infos);
g_list_foreach (server_infos, (GFunc) g_object_ref, NULL);
return server_infos;
}
gboolean
nautilus_recent_servers_get_loading (NautilusRecentServers *self)
{

View File

@ -16,6 +16,8 @@ G_DECLARE_FINAL_TYPE (NautilusRecentServers, nautilus_recent_servers, NAUTILUS,
NautilusRecentServers* nautilus_recent_servers_new (void);
void nautilus_recent_servers_force_reload (NautilusRecentServers *self);
GList * nautilus_recent_servers_get_infos (NautilusRecentServers *self);
gboolean nautilus_recent_servers_get_loading (NautilusRecentServers *self);
G_END_DECLS