diff options
33 files changed, 2240 insertions, 2106 deletions
@@ -1,3 +1,73 @@ +2001-03-26 Michael Engber <engber@eazel.com> + + reviewed by: Darin Adler <darin@eazel.com> + + * libnautilus-extensions/nautilus-directory-async.c: + (is_anyone_waiting_for_metafile), (call_ready_callbacks), + (start_or_stop_io), (nautilus_directory_cancel): + * libnautilus-extensions/nautilus-directory-metafile-monitor.c: + (corba_metafile_ready): + * libnautilus-extensions/nautilus-directory-metafile.c: + (get_metafile), (nautilus_directory_rename_file_metadata), + (nautilus_directory_rename_directory_metadata): + * libnautilus-extensions/nautilus-directory-metafile.h: + * libnautilus-extensions/nautilus-directory-private.h: + * libnautilus-extensions/nautilus-directory.c: + (nautilus_directory_initialize), (nautilus_directory_destroy), + (set_directory_uri), (change_directory_uri), + (nautilus_self_check_directory): + * libnautilus-extensions/nautilus-directory.h: + * libnautilus-extensions/nautilus-file-utilities.c: + (nautilus_file_name_matches_backup_pattern), + (nautilus_file_name_matches_metafile_pattern): + * libnautilus-extensions/nautilus-file-utilities.h: + * libnautilus-extensions/nautilus-file.c: + (nautilus_file_is_metafile): + * libnautilus-extensions/nautilus-file.h: + * libnautilus-extensions/nautilus-metafile-factory.c: (corba_open): + * libnautilus-extensions/nautilus-metafile-server.idl: + * libnautilus-extensions/nautilus-metafile.c: + (nautilus_metafile_class_init), (nautilus_metafile_init), + (destroy), (construct_private_metafile_vfs_uri), + (nautilus_metafile_set_directory_uri), (nautilus_metafile_new), + (nautilus_metafile_get), (schedule_next_read), (async_read_start), + (async_read_done), (async_read_cancel), (corba_is_read), + (corba_get), (corba_get_list), (corba_set), (corba_set_list), + (corba_copy), (corba_remove), (corba_rename), + (corba_rename_directory), (corba_register_monitor), + (corba_unregister_monitor), + (nautilus_metafile_notify_metafile_ready), (call_metafile_changed), + (call_metafile_changed_for_all_files_mentioned_in_metafile), + (call_metafile_changed_for_one_file), (create_metafile_root), + (get_file_node), (get_metadata_string_from_metafile), + (get_metadata_list_from_metafile), + (set_metadata_string_in_metafile), (set_metadata_list_in_metafile), + (set_metadata_in_metafile), (get_metadata_string_from_table), + (get_metadata_list_from_table), (set_metadata_eat_value), + (metafile_free_metadata), (get_file_metadata), + (get_file_metadata_list), (set_file_metadata), + (set_file_metadata_list), (metafile_get_file_uri), + (rename_file_metadata), (apply_one_change), (apply_file_changes), + (nautilus_metafile_apply_pending_changes), (copy_file_metadata), + (remove_file_metadata), (nautilus_metafile_set_metafile_contents), + (metafile_read_cancel), (can_use_public_metafile), + (metafile_read_mark_done), (metafile_read_done), + (metafile_read_try_public_metafile), + (metafile_read_check_for_directory_callback), + (metafile_read_check_for_directory), (metafile_read_failed), + (metafile_read_done_callback), (metafile_read_restart), + (allow_metafile), (metafile_read_start), (metafile_write_done), + (metafile_write_failed), (metafile_write_failure_close_callback), + (metafile_write_success_close_callback), (metafile_write_callback), + (metafile_write_create_callback), (metafile_write_start), + (metafile_write), (metafile_write_idle_callback), + (directory_request_write_metafile): + * libnautilus-extensions/nautilus-metafile.h: + * libnautilus-extensions/nautilus-vfs-directory.c: + (vfs_is_not_empty): + Factored the metadata reading/writing code out of nautilus-directory + - bug 6661. + 2001-03-26 Eskil Heyn Olsen <eskil@eazel.com> Initial commit of machine cloning. diff --git a/libnautilus-extensions/nautilus-directory-async.c b/libnautilus-extensions/nautilus-directory-async.c index f8dc8538d..868ebc5fe 100644 --- a/libnautilus-extensions/nautilus-directory-async.c +++ b/libnautilus-extensions/nautilus-directory-async.c @@ -57,29 +57,12 @@ #define DEBUG_START_STOP #endif -#define METAFILE_PERMISSIONS (GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE \ - | GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE \ - | GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE) #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 32 /* Keep async. jobs down to this number for all directories. */ #define MAX_ASYNC_JOBS 10 -struct MetafileReadState { - gboolean use_public_metafile; - NautilusReadFileHandle *handle; - GnomeVFSAsyncHandle *get_file_info_handle; -}; - -struct MetafileWriteState { - gboolean use_public_metafile; - GnomeVFSAsyncHandle *handle; - xmlChar *buffer; - GnomeVFSFileSize size; - gboolean write_again; -}; - struct TopLeftTextReadState { NautilusFile *file; NautilusReadFileHandle *handle; @@ -121,7 +104,6 @@ static GHashTable *async_jobs; /* Forward declarations for functions that need them. */ static void deep_count_load (NautilusDirectory *directory, const char *uri); -static void metafile_read_restart (NautilusDirectory *directory); static gboolean request_is_satisfied (NautilusDirectory *directory, NautilusFile *file, Request *request); @@ -417,539 +399,6 @@ file_info_cancel (NautilusDirectory *directory) } } -static void -metafile_read_cancel (NautilusDirectory *directory) -{ - if (directory->details->metafile_read_state != NULL) { - if (directory->details->metafile_read_state->handle != NULL) { - nautilus_read_file_cancel (directory->details->metafile_read_state->handle); - } - if (directory->details->metafile_read_state->get_file_info_handle != NULL) { - gnome_vfs_async_cancel (directory->details->metafile_read_state->get_file_info_handle); - } - g_free (directory->details->metafile_read_state); - directory->details->metafile_read_state = NULL; - - async_job_end (directory, "metafile read"); - } -} - -static gboolean -can_use_public_metafile (NautilusDirectory *directory) -{ - NautilusSpeedTradeoffValue preference_value; - - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE); - - if (directory->details->public_metafile_vfs_uri == NULL) { - return FALSE; - } - - preference_value = nautilus_preferences_get_integer (NAUTILUS_PREFERENCES_USE_PUBLIC_METADATA); - - if (preference_value == NAUTILUS_SPEED_TRADEOFF_ALWAYS) { - return TRUE; - } - - if (preference_value == NAUTILUS_SPEED_TRADEOFF_NEVER) { - return FALSE; - } - - g_assert (preference_value == NAUTILUS_SPEED_TRADEOFF_LOCAL_ONLY); - return nautilus_directory_is_local (directory); -} - -static void -metafile_read_mark_done (NautilusDirectory *directory) -{ - g_free (directory->details->metafile_read_state); - directory->details->metafile_read_state = NULL; - - directory->details->metafile_read = TRUE; - - /* Move over the changes to the metafile that were in the hash table. */ - nautilus_metafile_apply_pending_changes (directory); - - /* Tell change-watchers that we have update information. */ - nautilus_metafile_notify_metafile_ready (directory); - - /* Let the callers that were waiting for the metafile know. */ - nautilus_directory_async_state_changed (directory); -} - -static void -metafile_read_done (NautilusDirectory *directory) -{ - async_job_end (directory, "metafile read"); - metafile_read_mark_done (directory); -} - -static void -metafile_read_try_public_metafile (NautilusDirectory *directory) -{ - directory->details->metafile_read_state->use_public_metafile = TRUE; - metafile_read_restart (directory); -} - -static void -metafile_read_check_for_directory_callback (GnomeVFSAsyncHandle *handle, - GList *results, - gpointer callback_data) -{ - NautilusDirectory *directory; - GnomeVFSGetFileInfoResult *result; - - directory = NAUTILUS_DIRECTORY (callback_data); - - g_assert (directory->details->metafile_read_state->get_file_info_handle == handle); - g_assert (nautilus_g_list_exactly_one_item (results)); - - nautilus_directory_ref (directory); - - directory->details->metafile_read_state->get_file_info_handle = NULL; - - result = results->data; - - if (result->result == GNOME_VFS_OK - && ((result->file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE) != 0) - && result->file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) { - /* Is a directory. */ - metafile_read_try_public_metafile (directory); - } else { - /* Not a directory. */ - metafile_read_done (directory); - } - - nautilus_directory_unref (directory); -} - -static void -metafile_read_check_for_directory (NautilusDirectory *directory) -{ - GList fake_list; - - /* We only get here if the public metafile is in question, - * which in turn only happens if the URI is one that gnome-vfs - * can handle. - */ - g_assert (directory->details->vfs_uri != NULL); - - /* We have to do a get_info call to check if this a directory. */ - fake_list.data = directory->details->vfs_uri; - fake_list.next = NULL; - fake_list.prev = NULL; - gnome_vfs_async_get_file_info - (&directory->details->metafile_read_state->get_file_info_handle, - &fake_list, - GNOME_VFS_FILE_INFO_DEFAULT, - metafile_read_check_for_directory_callback, - directory); -} - -static void -metafile_read_failed (NautilusDirectory *directory) -{ - NautilusFile *file; - gboolean need_directory_check, is_directory; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - g_assert (directory->details->metafile == NULL); - - directory->details->metafile_read_state->handle = NULL; - - if (!directory->details->metafile_read_state->use_public_metafile - && can_use_public_metafile (directory)) { - /* The goal here is to read the real metafile, but - * only if the directory is actually a directory. - */ - - /* First, check if we already know if it a directory. */ - file = nautilus_file_get (directory->details->uri); - if (file == NULL || file->details->is_gone) { - need_directory_check = FALSE; - is_directory = FALSE; - } else if (file->details->info == NULL) { - need_directory_check = TRUE; - is_directory = TRUE; - } else { - need_directory_check = FALSE; - is_directory = nautilus_file_is_directory (file); - } - nautilus_file_unref (file); - - /* Do the directory check if we don't know. */ - if (need_directory_check) { - metafile_read_check_for_directory (directory); - return; - } - - /* Try for the public metafile if it is a directory. */ - if (is_directory) { - metafile_read_try_public_metafile (directory); - return; - } - } - - metafile_read_done (directory); -} - -static void -metafile_read_done_callback (GnomeVFSResult result, - GnomeVFSFileSize file_size, - char *file_contents, - gpointer callback_data) -{ - NautilusDirectory *directory; - int size; - char *buffer; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile == NULL); - - nautilus_directory_ref (directory); - - if (result != GNOME_VFS_OK) { - g_assert (file_contents == NULL); - metafile_read_failed (directory); - nautilus_directory_unref (directory); - return; - } - - size = file_size; - if ((GnomeVFSFileSize) size != file_size) { - g_free (file_contents); - metafile_read_failed (directory); - nautilus_directory_unref (directory); - return; - } - - /* The gnome-xml parser requires a zero-terminated array. */ - buffer = g_realloc (file_contents, size + 1); - buffer[size] = '\0'; - nautilus_metafile_set_metafile_contents (directory, - xmlParseMemory (buffer, size)); - g_free (buffer); - - metafile_read_done (directory); - - nautilus_directory_unref (directory); -} - -static void -metafile_read_restart (NautilusDirectory *directory) -{ - char *text_uri; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - text_uri = gnome_vfs_uri_to_string - (directory->details->metafile_read_state->use_public_metafile - ? directory->details->public_metafile_vfs_uri - : directory->details->private_metafile_vfs_uri, - GNOME_VFS_URI_HIDE_NONE); - - directory->details->metafile_read_state->handle = nautilus_read_entire_file_async - (text_uri, metafile_read_done_callback, directory); - - g_free (text_uri); -} - -static gboolean -allow_metafile (NautilusDirectory *directory) -{ - const char *uri; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - /* Note that this inhibits both reading and writing metadata - * completely. In the future we may want to inhibit writing to - * the real directory while allowing parallel-directory - * metadata. - */ - - /* For now, hard-code these schemes. Perhaps we should - * hardcode the schemes that are good for metadata instead of - * the schemes that are bad for it. - */ - /* FIXME bugzilla.eazel.com 2434: - * We need to handle this in a better way. Perhaps a - * better way can wait until we have support for metadata - * access inside gnome-vfs. - */ - uri = directory->details->uri; - if (nautilus_is_search_uri (uri) - || nautilus_istr_has_prefix (uri, "ghelp:") - || nautilus_istr_has_prefix (uri, "gnome-help:") - || nautilus_istr_has_prefix (uri, "help:") - || nautilus_istr_has_prefix (uri, "info:") - || nautilus_istr_has_prefix (uri, "man:") - || nautilus_istr_has_prefix (uri, "pipe:") - ) { - return FALSE; - } - - return TRUE; -} - -/* This checks if there's a request for the metafile contents. */ -static gboolean -is_anyone_waiting_for_metafile (NautilusDirectory *directory) -{ - GList *node; - ReadyCallback *callback; - Monitor *monitor; - - for (node = directory->details->call_when_ready_list; node != NULL; node = node->next) { - callback = node->data; - if (callback->request.metafile) { - return TRUE; - } - } - - for (node = directory->details->monitor_list; node != NULL; node = node->next) { - monitor = node->data; - if (monitor->request.metafile) { - return TRUE; - } - } - - return FALSE; -} - -static void -metafile_read_start (NautilusDirectory *directory) -{ - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - if (directory->details->metafile_read - || directory->details->metafile_read_state != NULL) { - return; - } - - g_assert (directory->details->metafile == NULL); - - if (!directory->details->load_metafile_for_server) { - return; - } - - if (!allow_metafile (directory)) { - metafile_read_mark_done (directory); - } else { - if (!async_job_start (directory, "metafile read")) { - return; - } - directory->details->metafile_read_state = g_new0 (MetafileReadState, 1); - metafile_read_restart (directory); - } -} - -static void -metafile_write_done (NautilusDirectory *directory) -{ - if (directory->details->metafile_write_state->write_again) { - nautilus_metafile_write_start (directory); - return; - } - - xmlFree (directory->details->metafile_write_state->buffer); - g_free (directory->details->metafile_write_state); - directory->details->metafile_write_state = NULL; - nautilus_directory_unref (directory); -} - -static void -metafile_write_failed (NautilusDirectory *directory) -{ - if (directory->details->metafile_write_state->use_public_metafile) { - directory->details->metafile_write_state->use_public_metafile = FALSE; - nautilus_metafile_write_start (directory); - return; - } - - metafile_write_done (directory); -} - -static void -metafile_write_failure_close_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - - nautilus_directory_ref (directory); - metafile_write_failed (directory); - nautilus_directory_unref (directory); -} - -static void -metafile_write_success_close_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile_write_state->handle == NULL); - - nautilus_directory_ref (directory); - - if (result != GNOME_VFS_OK) { - metafile_write_failed (directory); - nautilus_directory_unref (directory); - return; - } - - /* Now that we have finished writing, it is time to delete the - * private file if we wrote the public one. - */ - if (directory->details->metafile_write_state->use_public_metafile) { - /* A synchronous unlink is OK here because the private - * metafiles are local, so an unlink is very fast. - */ - gnome_vfs_unlink_from_uri (directory->details->private_metafile_vfs_uri); - } - - metafile_write_done (directory); - - nautilus_directory_unref (directory); -} - -static void -metafile_write_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gconstpointer buffer, - GnomeVFSFileSize bytes_requested, - GnomeVFSFileSize bytes_read, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile_write_state->handle == handle); - g_assert (directory->details->metafile_write_state->buffer == buffer); - g_assert (directory->details->metafile_write_state->size == bytes_requested); - - g_assert (directory->details->metafile_write_state->handle != NULL); - gnome_vfs_async_close (directory->details->metafile_write_state->handle, - result == GNOME_VFS_OK - ? metafile_write_success_close_callback - : metafile_write_failure_close_callback, - directory); - directory->details->metafile_write_state->handle = NULL; -} - -static void -metafile_write_create_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile_write_state->handle == handle); - - if (result != GNOME_VFS_OK) { - nautilus_directory_ref (directory); - metafile_write_failed (directory); - nautilus_directory_unref (directory); - return; - } - - gnome_vfs_async_write (directory->details->metafile_write_state->handle, - directory->details->metafile_write_state->buffer, - directory->details->metafile_write_state->size, - metafile_write_callback, - directory); -} - -void -nautilus_metafile_write_start (NautilusDirectory *directory) -{ - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - directory->details->metafile_write_state->write_again = FALSE; - - /* Open the file. */ - gnome_vfs_async_create_uri - (&directory->details->metafile_write_state->handle, - directory->details->metafile_write_state->use_public_metafile - ? directory->details->public_metafile_vfs_uri - : directory->details->private_metafile_vfs_uri, - GNOME_VFS_OPEN_WRITE, FALSE, METAFILE_PERMISSIONS, - metafile_write_create_callback, directory); -} - -static void -metafile_write (NautilusDirectory *directory) -{ - int xml_doc_size; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - nautilus_directory_ref (directory); - - /* If we are already writing, then just remember to do it again. */ - if (directory->details->metafile_write_state != NULL) { - nautilus_directory_unref (directory); - directory->details->metafile_write_state->write_again = TRUE; - return; - } - - /* Don't write anything if there's nothing to write. - * At some point, we might want to change this to actually delete - * the metafile in this case. - */ - if (directory->details->metafile == NULL) { - nautilus_directory_unref (directory); - return; - } - - /* Create the write state. */ - directory->details->metafile_write_state = g_new0 (MetafileWriteState, 1); - directory->details->metafile_write_state->use_public_metafile - = can_use_public_metafile (directory); - xmlDocDumpMemory (directory->details->metafile, - &directory->details->metafile_write_state->buffer, - &xml_doc_size); - directory->details->metafile_write_state->size = xml_doc_size; - nautilus_metafile_write_start (directory); -} - -static gboolean -metafile_write_idle_callback (gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - - directory->details->write_metafile_idle_id = 0; - metafile_write (directory); - - nautilus_directory_unref (directory); - - return FALSE; -} - -void -nautilus_directory_request_write_metafile (NautilusDirectory *directory) -{ - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - if (!allow_metafile (directory)) { - return; - } - - /* Set up an idle task that will write the metafile. */ - if (directory->details->write_metafile_idle_id == 0) { - nautilus_directory_ref (directory); - directory->details->write_metafile_idle_id = - gtk_idle_add (metafile_write_idle_callback, - directory); - } -} - static int monitor_key_compare (gconstpointer a, gconstpointer data) @@ -1526,6 +975,31 @@ directory_load_callback (GnomeVFSAsyncHandle *handle, nautilus_directory_unref (directory); } +/* This checks if there's a request for the metafile contents. */ +static gboolean +is_anyone_waiting_for_metafile (NautilusDirectory *directory) +{ + GList *node; + ReadyCallback *callback; + Monitor *monitor; + + for (node = directory->details->call_when_ready_list; node != NULL; node = node->next) { + callback = node->data; + if (callback->request.metafile) { + return TRUE; + } + } + + for (node = directory->details->monitor_list; node != NULL; node = node->next) { + monitor = node->data; + if (monitor->request.metafile) { + return TRUE; + } + } + + return FALSE; +} + static void update_metadata_monitors (NautilusDirectory *directory) { @@ -2104,7 +1578,6 @@ call_ready_callbacks (NautilusDirectory *directory) node != NULL; node = next) { next = node->next; callback = node->data; - if (request_is_satisfied (directory, callback->file, &callback->request)) { break; } @@ -3299,9 +2772,6 @@ start_or_stop_io (NautilusDirectory *directory) /* Start or stop getting file info. */ file_info_start (directory); - /* Start or stop reading the metafile. */ - metafile_read_start (directory); - /* Start or stop reading files. */ file_list_start (directory); @@ -3367,7 +2837,6 @@ nautilus_directory_cancel (NautilusDirectory *directory) directory_count_cancel (directory); file_info_cancel (directory); file_list_cancel (directory); - metafile_read_cancel (directory); mime_list_cancel (directory); top_left_cancel (directory); diff --git a/libnautilus-extensions/nautilus-directory-metafile-monitor.c b/libnautilus-extensions/nautilus-directory-metafile-monitor.c index c90a640a7..47251d0b3 100644 --- a/libnautilus-extensions/nautilus-directory-metafile-monitor.c +++ b/libnautilus-extensions/nautilus-directory-metafile-monitor.c @@ -126,6 +126,10 @@ corba_metafile_ready (PortableServer_Servant servant, NautilusMetafileMonitor *monitor; monitor = NAUTILUS_METAFILE_MONITOR (bonobo_object_from_servant (servant)); - emit_change_signals_for_all_files (monitor->details->directory); + nautilus_idle_queue_add (monitor->details->directory->details->idle_queue, + (GFunc) nautilus_directory_async_state_changed, + monitor->details->directory, + NULL, + NULL); } diff --git a/libnautilus-extensions/nautilus-directory-metafile.c b/libnautilus-extensions/nautilus-directory-metafile.c index d9100c802..8cb75bdfa 100644 --- a/libnautilus-extensions/nautilus-directory-metafile.c +++ b/libnautilus-extensions/nautilus-directory-metafile.c @@ -34,9 +34,6 @@ #include <liboaf/liboaf.h> #include <stdio.h> -/* FIXME: remove this when evilness is removed */ -#include <libnautilus-extensions/nautilus-metafile.h> - static Nautilus_MetafileFactory factory = CORBA_OBJECT_NIL; static gboolean get_factory_from_oaf = TRUE; @@ -73,25 +70,11 @@ get_factory (void) return factory; } -/* FIXME: Remove this code. It's ORBit dependent and generally evil */ -static gboolean -corba_object_is_local (CORBA_Object obj) -{ - return obj->vepv != NULL; -} -static PortableServer_Servant -corba_object_get_servant (CORBA_Object obj) -{ - g_assert (corba_object_is_local (obj)); - return obj->servant; -} - static Nautilus_Metafile get_metafile (NautilusDirectory *directory) { char *uri; CORBA_Environment ev; - NautilusMetafile *metafile; uri = nautilus_directory_get_uri (directory); @@ -99,15 +82,6 @@ get_metafile (NautilusDirectory *directory) if (directory->details->metafile_corba_object == CORBA_OBJECT_NIL) { directory->details->metafile_corba_object = Nautilus_MetafileFactory_open (get_factory (), uri, &ev); - - /* FIXME: remove this cycle-breaking when no longer needed */ - if (corba_object_is_local (directory->details->metafile_corba_object)) { - metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (corba_object_get_servant (directory->details->metafile_corba_object))); - if (!metafile->details->directory_ref_is_gone) { - nautilus_directory_unref (metafile->details->directory); - metafile->details->directory_ref_is_gone = TRUE; - } - } } /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ @@ -470,6 +444,26 @@ nautilus_directory_rename_file_metadata (NautilusDirectory *directory, } void +nautilus_directory_rename_directory_metadata (NautilusDirectory *directory, + const char *new_directory_uri) +{ + CORBA_Environment ev; + Nautilus_Metafile metafile; + + g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (new_directory_uri != NULL); + + metafile = get_metafile (directory); + CORBA_exception_init (&ev); + + Nautilus_Metafile_rename_directory (metafile, new_directory_uri, &ev); + + /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ + CORBA_exception_free (&ev); + bonobo_object_release_unref (metafile, &ev); +} + +void nautilus_directory_register_metadata_monitor (NautilusDirectory *directory) { CORBA_Environment ev; diff --git a/libnautilus-extensions/nautilus-directory-metafile.h b/libnautilus-extensions/nautilus-directory-metafile.h index 511355a9b..ffe6eb4a5 100644 --- a/libnautilus-extensions/nautilus-directory-metafile.h +++ b/libnautilus-extensions/nautilus-directory-metafile.h @@ -74,6 +74,8 @@ void nautilus_directory_remove_file_metadata (NautilusDirectory *d void nautilus_directory_rename_file_metadata (NautilusDirectory *directory, const char *old_file_name, const char *new_file_name); +void nautilus_directory_rename_directory_metadata (NautilusDirectory *directory, + const char *new_directory_uri); void nautilus_directory_register_metadata_monitor (NautilusDirectory *directory); void nautilus_directory_unregister_metadata_monitor (NautilusDirectory *directory); diff --git a/libnautilus-extensions/nautilus-directory-private.h b/libnautilus-extensions/nautilus-directory-private.h index 46a56bf57..36a8750d4 100644 --- a/libnautilus-extensions/nautilus-directory-private.h +++ b/libnautilus-extensions/nautilus-directory-private.h @@ -34,10 +34,9 @@ #include "nautilus-file-utilities.h" #include "nautilus-directory-metafile-monitor.h" #include "nautilus-metafile-server.h" +#include "nautilus-idle-queue.h" typedef struct ActivationURIReadState ActivationURIReadState; -typedef struct MetafileReadState MetafileReadState; -typedef struct MetafileWriteState MetafileWriteState; typedef struct TopLeftTextReadState TopLeftTextReadState; struct NautilusDirectoryDetails @@ -45,33 +44,20 @@ struct NautilusDirectoryDetails /* The location. */ char *uri; GnomeVFSURI *vfs_uri; - GnomeVFSURI *private_metafile_vfs_uri; - GnomeVFSURI *public_metafile_vfs_uri; /* The file objects. */ NautilusFile *as_file; GList *file_list; GHashTable *file_hash; - /* The metadata. */ - gboolean metafile_read; - xmlDoc *metafile; - GHashTable *metafile_node_hash; - GHashTable *metadata_changes; - - /* State for reading and writing metadata. */ - MetafileReadState *metafile_read_state; - guint write_metafile_idle_id; - MetafileWriteState *metafile_write_state; - /* These lists are going to be pretty short. If we think they * are going to get big, we can use hash tables instead. */ GList *call_when_ready_list; GList *monitor_list; + NautilusIdleQueue *idle_queue; NautilusMetafileMonitor *metafile_monitor; - gboolean load_metafile_for_server; Nautilus_Metafile metafile_corba_object; gboolean in_async_service_loop; @@ -161,11 +147,9 @@ gboolean nautilus_directory_is_file_list_monitored (NautilusD gboolean nautilus_directory_is_anyone_monitoring_file_list (NautilusDirectory *directory); void nautilus_directory_remove_file_monitor_link (NautilusDirectory *directory, GList *link); -void nautilus_directory_request_write_metafile (NautilusDirectory *directory); void nautilus_directory_schedule_dequeue_pending (NautilusDirectory *directory); void nautilus_directory_stop_monitoring_file_list (NautilusDirectory *directory); void nautilus_directory_cancel (NautilusDirectory *directory); -void nautilus_metafile_write_start (NautilusDirectory *directory); void nautilus_async_destroying_file (NautilusFile *file); void nautilus_directory_force_reload_internal (NautilusDirectory *directory, GList *file_attributes); diff --git a/libnautilus-extensions/nautilus-directory.c b/libnautilus-extensions/nautilus-directory.c index aca258afd..15185b8b2 100644 --- a/libnautilus-extensions/nautilus-directory.c +++ b/libnautilus-extensions/nautilus-directory.c @@ -52,9 +52,6 @@ enum { static guint signals[LAST_SIGNAL]; -/* Specifications for in-directory metafile. */ -#define METAFILE_NAME ".nautilus-metafile.xml" - /* Specifications for parallel-directory metafile. */ #define METAFILES_DIRECTORY_NAME "metafiles" #define METAFILE_SUFFIX ".xml" @@ -130,7 +127,7 @@ nautilus_directory_initialize (gpointer object, gpointer klass) directory->details = g_new0 (NautilusDirectoryDetails, 1); directory->details->file_hash = g_hash_table_new (g_str_hash, g_str_equal); - directory->details->metafile_node_hash = g_hash_table_new (g_str_hash, g_str_equal); + directory->details->idle_queue = nautilus_idle_queue_new (); } void @@ -164,9 +161,7 @@ nautilus_directory_destroy (GtkObject *object) directory = NAUTILUS_DIRECTORY (object); - g_assert (directory->details->metafile_write_state == NULL); nautilus_directory_cancel (directory); - g_assert (directory->details->metafile_read_state == NULL); g_assert (directory->details->count_in_progress == NULL); g_assert (directory->details->top_left_read_state == NULL); @@ -189,26 +184,17 @@ nautilus_directory_destroy (GtkObject *object) gtk_idle_remove (directory->details->dequeue_pending_idle_id); } - nautilus_metafile_destroy (directory); - g_free (directory->details->uri); if (directory->details->vfs_uri != NULL) { gnome_vfs_uri_unref (directory->details->vfs_uri); } - if (directory->details->public_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->public_metafile_vfs_uri); - } - if (directory->details->private_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->private_metafile_vfs_uri); - } g_assert (directory->details->file_list == NULL); g_hash_table_destroy (directory->details->file_hash); - g_hash_table_destroy (directory->details->metafile_node_hash); + nautilus_idle_queue_destroy (directory->details->idle_queue); g_assert (directory->details->directory_load_in_progress == NULL); g_assert (directory->details->count_in_progress == NULL); g_assert (directory->details->dequeue_pending_idle_id == 0); gnome_vfs_file_info_list_unref (directory->details->pending_file_info); - g_assert (directory->details->write_metafile_idle_id == 0); g_free (directory->details); @@ -316,7 +302,7 @@ remove_preferences_callbacks (void) NULL); } -static char * +char * nautilus_directory_make_uri_canonical (const char *uri) { char *canonical_maybe_trailing_slash; @@ -495,41 +481,6 @@ nautilus_directory_get_uri (NautilusDirectory *directory) return g_strdup (directory->details->uri); } -static GnomeVFSURI * -construct_private_metafile_vfs_uri (const char *uri) -{ - GnomeVFSResult result; - char *user_directory; - GnomeVFSURI *user_directory_uri, *metafiles_directory_uri, *alternate_uri; - char *escaped_uri, *file_name; - - /* Ensure that the metafiles directory exists. */ - user_directory = nautilus_get_user_directory (); - user_directory_uri = gnome_vfs_uri_new (user_directory); - g_free (user_directory); - - metafiles_directory_uri = gnome_vfs_uri_append_file_name (user_directory_uri, - METAFILES_DIRECTORY_NAME); - gnome_vfs_uri_unref (user_directory_uri); - result = nautilus_make_directory_and_parents (metafiles_directory_uri, - METAFILES_DIRECTORY_PERMISSIONS); - if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) { - gnome_vfs_uri_unref (metafiles_directory_uri); - return NULL; - } - - /* Construct a file name from the URI. */ - escaped_uri = gnome_vfs_escape_slashes (uri); - file_name = g_strconcat (escaped_uri, ".xml", NULL); - g_free (escaped_uri); - - /* Construct a URI for something in the "metafiles" directory. */ - alternate_uri = gnome_vfs_uri_append_file_name (metafiles_directory_uri, file_name); - gnome_vfs_uri_unref (metafiles_directory_uri); - g_free (file_name); - - return alternate_uri; -} static NautilusDirectory * nautilus_directory_new (const char *uri) @@ -1026,13 +977,8 @@ set_directory_uri (NautilusDirectory *directory, const char *new_uri) { GnomeVFSURI *new_vfs_uri; - GnomeVFSURI *new_public_metafile_vfs_uri; - GnomeVFSURI *new_private_metafile_vfs_uri; new_vfs_uri = gnome_vfs_uri_new (new_uri); - new_public_metafile_vfs_uri = new_vfs_uri == NULL ? NULL - : gnome_vfs_uri_append_file_name (new_vfs_uri, METAFILE_NAME); - new_private_metafile_vfs_uri = construct_private_metafile_vfs_uri (new_uri); g_free (directory->details->uri); directory->details->uri = g_strdup (new_uri); @@ -1041,58 +987,18 @@ set_directory_uri (NautilusDirectory *directory, gnome_vfs_uri_unref (directory->details->vfs_uri); } directory->details->vfs_uri = new_vfs_uri; - - if (directory->details->public_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->public_metafile_vfs_uri); - } - directory->details->public_metafile_vfs_uri = - new_public_metafile_vfs_uri; - - if (directory->details->private_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->private_metafile_vfs_uri); - } - directory->details->private_metafile_vfs_uri - = new_private_metafile_vfs_uri; -} - -static char * -get_path_from_vfs_uri (GnomeVFSURI *vfs_uri) -{ - char *uri, *path; - - if (vfs_uri == NULL) { - return NULL; - } - - uri = gnome_vfs_uri_to_string (vfs_uri, GNOME_VFS_URI_HIDE_NONE); - if (uri == NULL) { - return NULL; - } - path = gnome_vfs_get_local_path_from_uri (uri); - g_free (uri); - return path; -} - -static char * -get_private_metafile_path (NautilusDirectory *directory) -{ - return get_path_from_vfs_uri (directory->details->private_metafile_vfs_uri); } static void change_directory_uri (NautilusDirectory *directory, const char *new_uri) { - char *old_metafile_path, *new_metafile_path; - /* I believe it's impossible for a self-owned file/directory * to be moved. But if that did somehow happen, this function * wouldn't do enough to handle it. */ g_return_if_fail (directory->details->as_file == NULL); - old_metafile_path = get_private_metafile_path (directory); - g_hash_table_remove (directories, directory->details->uri); @@ -1102,14 +1008,7 @@ change_directory_uri (NautilusDirectory *directory, directory->details->uri, directory); - new_metafile_path = get_private_metafile_path (directory); - - if (old_metafile_path != NULL && new_metafile_path != NULL) { - rename (old_metafile_path, new_metafile_path); - } - - g_free (old_metafile_path); - g_free (new_metafile_path); + nautilus_directory_rename_directory_metadata (directory, new_uri); } typedef struct { @@ -1684,7 +1583,7 @@ nautilus_self_check_directory (void) gtk_main_iteration (); } - NAUTILUS_CHECK_BOOLEAN_RESULT (directory->details->metafile != NULL, TRUE); + NAUTILUS_CHECK_BOOLEAN_RESULT (nautilus_directory_is_metadata_read (directory), TRUE); got_files_flag = FALSE; diff --git a/libnautilus-extensions/nautilus-directory.h b/libnautilus-extensions/nautilus-directory.h index efd122c9f..7ec226dac 100644 --- a/libnautilus-extensions/nautilus-directory.h +++ b/libnautilus-extensions/nautilus-directory.h @@ -202,4 +202,6 @@ gboolean nautilus_directory_is_local (NautilusDirector gboolean nautilus_directory_is_not_empty (NautilusDirectory *directory); gboolean nautilus_directory_file_list_length_reached (NautilusDirectory *directory); +char * nautilus_directory_make_uri_canonical (const char *uri); + #endif /* NAUTILUS_DIRECTORY_H */ diff --git a/libnautilus-extensions/nautilus-file-utilities.c b/libnautilus-extensions/nautilus-file-utilities.c index 6d8e50494..7b2960067 100644 --- a/libnautilus-extensions/nautilus-file-utilities.c +++ b/libnautilus-extensions/nautilus-file-utilities.c @@ -30,6 +30,7 @@ #include "nautilus-lib-self-check-functions.h" #include "nautilus-link-set.h" #include "nautilus-metadata.h" +#include "nautilus-metafile.h" #include "nautilus-string.h" #include <ctype.h> #include <libgnome/gnome-defs.h> @@ -859,6 +860,14 @@ nautilus_file_name_matches_backup_pattern (const char *name_or_relative_uri) return nautilus_str_has_suffix (name_or_relative_uri, "~"); } +gboolean +nautilus_file_name_matches_metafile_pattern (const char *name_or_relative_uri) +{ + g_return_val_if_fail (name_or_relative_uri != NULL, FALSE); + + return nautilus_str_has_suffix (name_or_relative_uri, NAUTILUS_METAFILE_NAME_SUFFIX); +} + /** * nautilus_make_path: * diff --git a/libnautilus-extensions/nautilus-file-utilities.h b/libnautilus-extensions/nautilus-file-utilities.h index 22266fd82..37149623e 100644 --- a/libnautilus-extensions/nautilus-file-utilities.h +++ b/libnautilus-extensions/nautilus-file-utilities.h @@ -59,6 +59,7 @@ char * nautilus_uri_make_full_from_relative (const char /* Recognizing special file names. */ gboolean nautilus_file_name_matches_hidden_pattern (const char *name_or_relative_uri); gboolean nautilus_file_name_matches_backup_pattern (const char *name_or_relative_uri); +gboolean nautilus_file_name_matches_metafile_pattern(const char *name_or_relative_uri); /* FIXME bugzilla.eazel.com 2424: diff --git a/libnautilus-extensions/nautilus-file.c b/libnautilus-extensions/nautilus-file.c index 0a796bcfb..84994fd23 100644 --- a/libnautilus-extensions/nautilus-file.c +++ b/libnautilus-extensions/nautilus-file.c @@ -1843,6 +1843,13 @@ nautilus_file_is_backup_file (NautilusFile *file) } gboolean +nautilus_file_is_metafile (NautilusFile *file) +{ + return nautilus_file_name_matches_metafile_pattern + (file->details->relative_uri); +} + +gboolean nautilus_file_should_show (NautilusFile *file, gboolean show_hidden, gboolean show_backup) diff --git a/libnautilus-extensions/nautilus-file.h b/libnautilus-extensions/nautilus-file.h index 43810f0ad..f1a3fa364 100644 --- a/libnautilus-extensions/nautilus-file.h +++ b/libnautilus-extensions/nautilus-file.h @@ -264,6 +264,7 @@ int nautilus_file_compare_name (Nautilu /* filtering functions for use by various directory views */ gboolean nautilus_file_is_hidden_file (NautilusFile *file); gboolean nautilus_file_is_backup_file (NautilusFile *file); +gboolean nautilus_file_is_metafile (NautilusFile *file); gboolean nautilus_file_should_show (NautilusFile *file, gboolean show_hidden, gboolean show_backup); diff --git a/libnautilus-extensions/nautilus-metafile-factory.c b/libnautilus-extensions/nautilus-metafile-factory.c index 8ca0628bd..2686f278c 100644 --- a/libnautilus-extensions/nautilus-metafile-factory.c +++ b/libnautilus-extensions/nautilus-metafile-factory.c @@ -103,6 +103,6 @@ corba_open (PortableServer_Servant servant, CORBA_Environment *ev) { BonoboObject *object; - object = BONOBO_OBJECT (nautilus_metafile_new (directory)); + object = BONOBO_OBJECT (nautilus_metafile_get (directory)); return CORBA_Object_duplicate (bonobo_object_corba_objref (object), ev); } diff --git a/libnautilus-extensions/nautilus-metafile-server.idl b/libnautilus-extensions/nautilus-metafile-server.idl index 5d0500399..afa1a6617 100644 --- a/libnautilus-extensions/nautilus-metafile-server.idl +++ b/libnautilus-extensions/nautilus-metafile-server.idl @@ -70,12 +70,13 @@ module Nautilus { in MetadataList list); /* calls to keep metadata in sync with file operations */ - void copy (in string source_file_name, - in URI destination_directory_uri, - in string destination_file_name); - void remove (in string file_name); - void rename (in string old_file_name, - in string new_file_name); + void copy (in string source_file_name, + in URI destination_directory_uri, + in string destination_file_name); + void remove (in string file_name); + void rename (in string old_file_name, + in string new_file_name); + void rename_directory (in string new_directory_uri); /* calls for registering a MetafileMonitor with a Metafile */ void register_monitor (in MetafileMonitor monitor); diff --git a/libnautilus-extensions/nautilus-metafile.c b/libnautilus-extensions/nautilus-metafile.c index 2850a442c..939f0c82b 100644 --- a/libnautilus-extensions/nautilus-metafile.c +++ b/libnautilus-extensions/nautilus-metafile.c @@ -28,18 +28,38 @@ #include <libnautilus-extensions/nautilus-bonobo-extensions.h> #include <libnautilus/nautilus-bonobo-workarounds.h> +#include <libgnomevfs/gnome-vfs-types.h> +#include <libgnomevfs/gnome-vfs-uri.h> +#include <libgnomevfs/gnome-vfs-file-info.h> + #include "nautilus-string.h" #include "nautilus-metadata.h" #include "nautilus-thumbnails.h" #include "nautilus-xml-extensions.h" #include "nautilus-glib-extensions.h" -#include "nautilus-directory-private.h" +#include "nautilus-directory.h" +#include "nautilus-global-preferences.h" +#include "nautilus-file-private.h" +#include "nautilus-search-uri.h" #include <stdlib.h> +#include <gnome-xml/parser.h> #include <gnome-xml/xmlmemory.h> +#include <gtk/gtkmain.h> #define METAFILE_XML_VERSION "1.0" +#define METAFILE_PERMISSIONS (GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE \ + | GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE \ + | GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE) + +#define METAFILES_DIRECTORY_NAME "metafiles" + +#define METAFILES_DIRECTORY_PERMISSIONS \ + (GNOME_VFS_PERM_USER_ALL \ + | GNOME_VFS_PERM_GROUP_ALL \ + | GNOME_VFS_PERM_OTHER_ALL) + static void nautilus_metafile_init (NautilusMetafile *metafile); static void nautilus_metafile_class_init (NautilusMetafileClass *klass); @@ -72,18 +92,21 @@ static void corba_set_list (PortableServer_Servant servant, const Nautilus_MetadataList *list, CORBA_Environment *ev); -static void corba_copy (PortableServer_Servant servant, - const CORBA_char *source_file_name, - const Nautilus_URI destination_directory_uri, - const CORBA_char *destination_file_name, - CORBA_Environment *ev); -static void corba_remove (PortableServer_Servant servant, - const CORBA_char *file_name, - CORBA_Environment *ev); -static void corba_rename (PortableServer_Servant servant, - const CORBA_char *old_file_name, - const CORBA_char *new_file_name, - CORBA_Environment *ev); +static void corba_copy (PortableServer_Servant servant, + const CORBA_char *source_file_name, + const Nautilus_URI destination_directory_uri, + const CORBA_char *destination_file_name, + CORBA_Environment *ev); +static void corba_remove (PortableServer_Servant servant, + const CORBA_char *file_name, + CORBA_Environment *ev); +static void corba_rename (PortableServer_Servant servant, + const CORBA_char *old_file_name, + const CORBA_char *new_file_name, + CORBA_Environment *ev); +static void corba_rename_directory (PortableServer_Servant servant, + const CORBA_char *new_directory_uri, + CORBA_Environment *ev); static void corba_register_monitor (PortableServer_Servant servant, const Nautilus_MetafileMonitor monitor, @@ -92,40 +115,88 @@ static void corba_unregister_monitor (PortableServer_Servant servant, const Nautilus_MetafileMonitor monitor, CORBA_Environment *ev); -static char *get_file_metadata (NautilusDirectory *directory, - const char *file_name, - const char *key, - const char *default_metadata); -static GList *get_file_metadata_list (NautilusDirectory *directory, - const char *file_name, - const char *list_key, - const char *list_subkey); -static gboolean set_file_metadata (NautilusDirectory *directory, - const char *file_name, - const char *key, - const char *default_metadata, - const char *metadata); -static gboolean set_file_metadata_list (NautilusDirectory *directory, - const char *file_name, - const char *list_key, - const char *list_subkey, +static char *get_file_metadata (NautilusMetafile *metafile, + const char *file_name, + const char *key, + const char *default_metadata); +static GList *get_file_metadata_list (NautilusMetafile *metafile, + const char *file_name, + const char *list_key, + const char *list_subkey); +static gboolean set_file_metadata (NautilusMetafile *metafile, + const char *file_name, + const char *key, + const char *default_metadata, + const char *metadata); +static gboolean set_file_metadata_list (NautilusMetafile *metafile, + const char *file_name, + const char *list_key, + const char *list_subkey, GList *list); -static void rename_file_metadata (NautilusDirectory *directory, - const char *old_file_name, - const char *new_file_name); -static void copy_file_metadata (NautilusDirectory *source_directory, - const char *source_file_name, - NautilusDirectory *destination_directory, - const char *destination_file_name); -static void remove_file_metadata (NautilusDirectory *directory, - const char *file_name); - -static void call_metafile_changed_for_one_file (NautilusDirectory *directory, +static void rename_file_metadata (NautilusMetafile *metafile, + const char *old_file_name, + const char *new_file_name); +static void copy_file_metadata (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name); +static void remove_file_metadata (NautilusMetafile *metafile, + const char *file_name); + +static void call_metafile_changed_for_one_file (NautilusMetafile *metafile, const CORBA_char *file_name); +static void metafile_read_restart (NautilusMetafile *metafile); +static void metafile_read_start (NautilusMetafile *metafile); +static void metafile_write_start (NautilusMetafile *metafile); +static void directory_request_write_metafile (NautilusMetafile *metafile); +static void metafile_free_metadata (NautilusMetafile *metafile); +static void metafile_read_cancel (NautilusMetafile *metafile); +static void async_read_cancel (NautilusMetafile *metafile); + +static void nautilus_metafile_set_metafile_contents (NautilusMetafile *metafile, + xmlDocPtr metafile_contents); + NAUTILUS_BONOBO_X_BOILERPLATE (NautilusMetafile, Nautilus_Metafile, BONOBO_X_OBJECT_TYPE, nautilus_metafile) +typedef struct MetafileReadState { + gboolean use_public_metafile; + NautilusReadFileHandle *handle; + GnomeVFSAsyncHandle *get_file_info_handle; +} MetafileReadState; + +typedef struct MetafileWriteState { + gboolean use_public_metafile; + GnomeVFSAsyncHandle *handle; + xmlChar *buffer; + GnomeVFSFileSize size; + gboolean write_again; +} MetafileWriteState; + +struct NautilusMetafileDetails { + gboolean is_read; + + xmlDoc *xml; + GHashTable *node_hash; + GHashTable *changes; + + /* State for reading and writing metadata. */ + MetafileReadState *read_state; + guint write_idle_id; + MetafileWriteState *write_state; + + GList *monitors; + + GnomeVFSURI *private_vfs_uri; + GnomeVFSURI *public_vfs_uri; + + char *directory_uri; + GnomeVFSURI *directory_vfs_uri; +}; + +static GHashTable *metafiles; + static void nautilus_metafile_class_init (NautilusMetafileClass *klass) { @@ -139,6 +210,7 @@ nautilus_metafile_class_init (NautilusMetafileClass *klass) klass->epv.copy = corba_copy; klass->epv.remove = corba_remove; klass->epv.rename = corba_rename; + klass->epv.rename_directory = corba_rename_directory; klass->epv.register_monitor = corba_register_monitor; klass->epv.unregister_monitor = corba_unregister_monitor; } @@ -147,47 +219,246 @@ static void nautilus_metafile_init (NautilusMetafile *metafile) { metafile->details = g_new0 (NautilusMetafileDetails, 1); + + metafile->details->node_hash = g_hash_table_new (g_str_hash, g_str_equal); + } static void destroy (GtkObject *object) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (object); - directory = metafile->details->directory; - /* FIXME: remove this conditional when the field goes away */ - if (!metafile->details->directory_ref_is_gone) { - nautilus_directory_unref (directory); + g_assert (metafile->details->write_state == NULL); + async_read_cancel (metafile); + g_assert (metafile->details->read_state == NULL); + + if (metafile->details->public_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->public_vfs_uri); } + if (metafile->details->private_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->private_vfs_uri); + } + + g_hash_table_remove (metafiles, metafile->details->directory_uri); + + metafile_free_metadata (metafile); + g_hash_table_destroy (metafile->details->node_hash); + + g_assert (metafile->details->write_idle_id == 0); g_free (metafile->details); NAUTILUS_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); } -NautilusMetafile * +static GnomeVFSURI * +construct_private_metafile_vfs_uri (const char *uri) +{ + GnomeVFSResult result; + char *user_directory; + GnomeVFSURI *user_directory_uri, *metafiles_directory_uri, *alternate_uri; + char *escaped_uri, *file_name; + + /* Ensure that the metafiles directory exists. */ + user_directory = nautilus_get_user_directory (); + user_directory_uri = gnome_vfs_uri_new (user_directory); + g_free (user_directory); + + metafiles_directory_uri = gnome_vfs_uri_append_file_name (user_directory_uri, + METAFILES_DIRECTORY_NAME); + gnome_vfs_uri_unref (user_directory_uri); + result = nautilus_make_directory_and_parents (metafiles_directory_uri, + METAFILES_DIRECTORY_PERMISSIONS); + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) { + gnome_vfs_uri_unref (metafiles_directory_uri); + return NULL; + } + + /* Construct a file name from the URI. */ + escaped_uri = gnome_vfs_escape_slashes (uri); + file_name = g_strconcat (escaped_uri, ".xml", NULL); + g_free (escaped_uri); + + /* Construct a URI for something in the "metafiles" directory. */ + alternate_uri = gnome_vfs_uri_append_file_name (metafiles_directory_uri, file_name); + gnome_vfs_uri_unref (metafiles_directory_uri); + g_free (file_name); + + return alternate_uri; +} + +static void +nautilus_metafile_set_directory_uri (NautilusMetafile *metafile, const char *directory_uri) +{ + GnomeVFSURI *new_vfs_uri; + + if (nautilus_strcmp (metafile->details->directory_uri, directory_uri) == 0) { + return; + } + + g_free (metafile->details->directory_uri); + metafile->details->directory_uri = g_strdup (directory_uri); + + new_vfs_uri = gnome_vfs_uri_new (directory_uri); + + if (metafile->details->directory_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->directory_vfs_uri); + } + metafile->details->directory_vfs_uri = new_vfs_uri; + + if (metafile->details->public_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->public_vfs_uri); + } + metafile->details->public_vfs_uri = new_vfs_uri == NULL ? NULL + : gnome_vfs_uri_append_file_name (new_vfs_uri, NAUTILUS_METAFILE_NAME_SUFFIX); + + if (metafile->details->private_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->private_vfs_uri); + } + metafile->details->private_vfs_uri + = construct_private_metafile_vfs_uri (directory_uri); + +} + +static NautilusMetafile * nautilus_metafile_new (const char *directory_uri) { NautilusMetafile *metafile; + metafile = NAUTILUS_METAFILE (gtk_object_new (NAUTILUS_TYPE_METAFILE, NULL)); - metafile->details->directory = nautilus_directory_get (directory_uri); + + nautilus_metafile_set_directory_uri (metafile, directory_uri); + return metafile; } +NautilusMetafile * +nautilus_metafile_get (const char *directory_uri) +{ + NautilusMetafile *metafile; + char *canonical_uri; + + g_return_val_if_fail (directory_uri != NULL, NULL); + + if (metafiles == NULL) { + metafiles = nautilus_g_hash_table_new_free_at_exit + (g_str_hash, g_str_equal, __FILE__ ": metafiles"); + } + + canonical_uri = nautilus_directory_make_uri_canonical (directory_uri); + + metafile = g_hash_table_lookup (metafiles, canonical_uri); + + if (metafile != NULL) { + bonobo_object_ref (BONOBO_OBJECT (metafile)); + } else { + metafile = nautilus_metafile_new (canonical_uri); + + g_assert (strcmp (metafile->details->directory_uri, canonical_uri) == 0); + + g_hash_table_insert (metafiles, + metafile->details->directory_uri, + metafile); + } + + g_free (canonical_uri); + + return metafile; +} + +/* FIXME + * Right now we only limit the number of conccurrent reads. + * We may want to consider limiting writes as well. + */ + +int num_reads_in_progress; +GList *pending_reads; + +#if 0 +#define DEBUG_METADATA_IO +#endif + +static void +schedule_next_read () +{ + const int kMaxAsyncReads = 10; + + GList* node; + +#ifdef DEBUG_METADATA_IO + g_message ("schedule_next_read: %d pending reads, %d reads in progress", + g_list_length (pending_reads), num_reads_in_progress); +#endif + + if (pending_reads != NULL && num_reads_in_progress <= kMaxAsyncReads) { + node = pending_reads; + pending_reads = g_list_remove_link (pending_reads, node); +#ifdef DEBUG_METADATA_IO + g_message ("schedule_next_read: %s", NAUTILUS_METAFILE (node->data)->details->directory_uri); +#endif + metafile_read_start (node->data); + g_list_free_1 (node); + ++num_reads_in_progress; + } +} + +static void +async_read_start (NautilusMetafile *metafile) +{ + if (metafile->details->is_read + || metafile->details->read_state != NULL) { + return; + } +#ifdef DEBUG_METADATA_IO + g_message ("async_read_start: %s", metafile->details->directory_uri); +#endif + pending_reads = g_list_prepend (pending_reads, metafile); + schedule_next_read (); +} + +static void +async_read_done (NautilusMetafile *metafile) +{ +#ifdef DEBUG_METADATA_IO + g_message ("async_read_done: %s", metafile->details->directory_uri); +#endif + --num_reads_in_progress; + schedule_next_read (); +} + +static void +async_read_cancel (NautilusMetafile *metafile) +{ + GList* node; + +#ifdef DEBUG_METADATA_IO + g_message ("async_read_cancel: %s", metafile->details->directory_uri); +#endif + node = g_list_find (pending_reads, metafile); + + if (node != NULL) { + pending_reads = g_list_remove_link (pending_reads, node); + g_list_free_1 (node); + } + + if (metafile->details->read_state != NULL) { + metafile_read_cancel (metafile); + async_read_done (metafile); + } + +} + static CORBA_boolean corba_is_read (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - - return directory->details->metafile_read ? CORBA_TRUE : CORBA_FALSE; + return metafile->details->is_read ? CORBA_TRUE : CORBA_FALSE; } static CORBA_char * @@ -198,15 +469,13 @@ corba_get (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; char *metadata; CORBA_char *result; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - metadata = get_file_metadata (directory, file_name, key, default_value); + metadata = get_file_metadata (metafile, file_name, key, default_value); result = CORBA_string_dup (metadata != NULL ? metadata : ""); @@ -223,7 +492,6 @@ corba_get_list (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; GList *metadata_list; Nautilus_MetadataList *result; @@ -232,9 +500,8 @@ corba_get_list (PortableServer_Servant servant, GList *list_ptr; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - metadata_list = get_file_metadata_list (directory, file_name, list_key, list_subkey); + metadata_list = get_file_metadata_list (metafile, file_name, list_key, list_subkey); len = g_list_length (metadata_list); result = Nautilus_MetadataList__alloc (); @@ -269,7 +536,6 @@ corba_set (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; if (nautilus_str_is_empty (default_value)) { default_value = NULL; @@ -279,10 +545,9 @@ corba_set (PortableServer_Servant servant, } metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - if (set_file_metadata (directory, file_name, key, default_value, metadata)) { - call_metafile_changed_for_one_file (directory, file_name); + if (set_file_metadata (metafile, file_name, key, default_value, metadata)) { + call_metafile_changed_for_one_file (metafile, file_name); } } @@ -295,13 +560,11 @@ corba_set_list (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; GList *metadata_list; CORBA_unsigned_long buf_pos; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; metadata_list = NULL; for (buf_pos = 0; buf_pos < list->_length; ++buf_pos) { @@ -309,8 +572,8 @@ corba_set_list (PortableServer_Servant servant, } metadata_list = g_list_reverse (metadata_list); - if (set_file_metadata_list (directory, file_name, list_key, list_subkey, metadata_list)) { - call_metafile_changed_for_one_file (directory, file_name); + if (set_file_metadata_list (metafile, file_name, list_key, list_subkey, metadata_list)) { + call_metafile_changed_for_one_file (metafile, file_name); } g_list_free (metadata_list); @@ -323,19 +586,17 @@ corba_copy (PortableServer_Servant servant, const CORBA_char *destination_file_name, CORBA_Environment *ev) { - NautilusMetafile *source_metafile; - NautilusDirectory *source_directory; - NautilusDirectory *destination_directory; + NautilusMetafile *source_metafile; + NautilusMetafile *destination_metafile; source_metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - source_directory = source_metafile->details->directory; - destination_directory = nautilus_directory_get (destination_directory_uri); + destination_metafile = nautilus_metafile_get (destination_directory_uri); - copy_file_metadata (source_directory, source_file_name, - destination_directory, destination_file_name); + copy_file_metadata (source_metafile, source_file_name, + destination_metafile, destination_file_name); - nautilus_directory_unref (destination_directory); + bonobo_object_unref (BONOBO_OBJECT (destination_metafile)); } static void @@ -344,12 +605,10 @@ corba_remove (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - remove_file_metadata (directory, file_name); + remove_file_metadata (metafile, file_name); } static void @@ -359,59 +618,22 @@ corba_rename (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - rename_file_metadata (directory, old_file_name, new_file_name); -} - -typedef struct { - GList *monitors; - NautilusDirectory *directory; -} DirectoryMonitorListEntry; - -static GHashTable *directory_monitor_lists; - -static DirectoryMonitorListEntry * -get_or_add_directory_monitor_list_entry (NautilusDirectory *directory) -{ - DirectoryMonitorListEntry *entry; - - if (directory_monitor_lists == NULL) { - directory_monitor_lists = nautilus_g_hash_table_new_free_at_exit (g_direct_hash, g_direct_equal, __FILE__ ": metadata monitors"); - } - - entry = g_hash_table_lookup (directory_monitor_lists, directory); - - if (entry == NULL) { - entry = g_new0 (DirectoryMonitorListEntry, 1); - nautilus_directory_ref (directory); - entry->directory = directory; - g_hash_table_insert (directory_monitor_lists, directory, entry); - } - - return entry; + rename_file_metadata (metafile, old_file_name, new_file_name); } static void -remove_directory_monitor_list_entry (NautilusDirectory *directory) +corba_rename_directory (PortableServer_Servant servant, + const CORBA_char *new_directory_uri, + CORBA_Environment *ev) { - DirectoryMonitorListEntry *entry; - - entry = g_hash_table_lookup (directory_monitor_lists, directory); + NautilusMetafile *metafile; - if (entry != NULL) { - /* This fn only handles removal when there are no monitors left. - * It makes no attempt to free the monitors. - */ - g_return_if_fail (entry->monitors == NULL); - - g_hash_table_remove (directory_monitor_lists, directory); - nautilus_directory_unref (directory); - g_free (entry); - } + metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); + + nautilus_metafile_set_directory_uri (metafile, new_directory_uri); } static GList * @@ -443,21 +665,14 @@ corba_register_monitor (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; - DirectoryMonitorListEntry *monitor_list; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - - monitor_list = get_or_add_directory_monitor_list_entry (directory); - g_return_if_fail (find_monitor_node (monitor_list->monitors, monitor) == NULL); + g_return_if_fail (find_monitor_node (metafile->details->monitors, monitor) == NULL); - monitor_list->monitors = g_list_prepend (monitor_list->monitors, (gpointer) CORBA_Object_duplicate (monitor, ev)); + metafile->details->monitors = g_list_prepend (metafile->details->monitors, (gpointer) CORBA_Object_duplicate (monitor, ev)); - /* cause metafile to be read */ - directory->details->load_metafile_for_server = TRUE; - nautilus_directory_async_state_changed (directory); + async_read_start (metafile); } static void @@ -466,77 +681,57 @@ corba_unregister_monitor (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; - DirectoryMonitorListEntry *entry; GList *node; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - entry = g_hash_table_lookup (directory_monitor_lists, directory); - - g_return_if_fail (entry != NULL); - - node = find_monitor_node (entry->monitors, monitor); + node = find_monitor_node (metafile->details->monitors, monitor); g_return_if_fail (node != NULL); - entry->monitors = g_list_remove_link (entry->monitors, node); + metafile->details->monitors = g_list_remove_link (metafile->details->monitors, node); CORBA_Object_release (node->data, ev); g_list_free_1 (node); - - if (entry->monitors == NULL) { - remove_directory_monitor_list_entry (directory); - } } -void -nautilus_metafile_notify_metafile_ready (NautilusDirectory *directory) +static void +nautilus_metafile_notify_metafile_ready (NautilusMetafile *metafile) { GList *node; CORBA_Environment ev; Nautilus_MetafileMonitor monitor; - DirectoryMonitorListEntry *entry; - entry = g_hash_table_lookup (directory_monitor_lists, directory); + CORBA_exception_init (&ev); - if (entry != NULL) { - CORBA_exception_init (&ev); - - for (node = entry->monitors; node != NULL; node = node->next) { - monitor = node->data; - Nautilus_MetafileMonitor_metafile_ready (monitor, &ev); - /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ - } - - CORBA_exception_free (&ev); + for (node = metafile->details->monitors; node != NULL; node = node->next) { + monitor = node->data; + Nautilus_MetafileMonitor_metafile_ready (monitor, &ev); + /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ } + + CORBA_exception_free (&ev); } static void -call_metafile_changed (NautilusDirectory *directory, - const Nautilus_FileNameList *file_names) +call_metafile_changed (NautilusMetafile *metafile, + const Nautilus_FileNameList *file_names) { GList *node; CORBA_Environment ev; Nautilus_MetafileMonitor monitor; - DirectoryMonitorListEntry *entry; - entry = g_hash_table_lookup (directory_monitor_lists, directory); + CORBA_exception_init (&ev); - if (entry != NULL) { - CORBA_exception_init (&ev); - - for (node = entry->monitors; node != NULL; node = node->next) { - monitor = node->data; - Nautilus_MetafileMonitor_metafile_changed (monitor, file_names, &ev); - /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ - } - - CORBA_exception_free (&ev); + for (node = metafile->details->monitors; node != NULL; node = node->next) { + monitor = node->data; + Nautilus_MetafileMonitor_metafile_changed (monitor, file_names, &ev); + /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ } + + CORBA_exception_free (&ev); } +#if 0 static void file_list_filler_ghfunc (gpointer key, @@ -553,31 +748,32 @@ file_list_filler_ghfunc (gpointer key, } void -call_metafile_changed_for_all_files_mentioned_in_metafile (NautilusDirectory *directory) +call_metafile_changed_for_all_files_mentioned_in_metafile (NautilusMetafile *metafile) { CORBA_unsigned_long len; Nautilus_FileNameList file_names; - len = g_hash_table_size (directory->details->metafile_node_hash); + len = g_hash_table_size (metafile->details->node_hash); if (len > 0) { file_names._maximum = len; file_names._length = 0; file_names._buffer = g_new (CORBA_char *, len); - g_hash_table_foreach (directory->details->metafile_node_hash, + g_hash_table_foreach (metafile->details->node_hash, file_list_filler_ghfunc, &file_names); - call_metafile_changed (directory, &file_names); + call_metafile_changed (metafile, &file_names); g_free (file_names._buffer); } } +#endif static void -call_metafile_changed_for_one_file (NautilusDirectory *directory, - const CORBA_char *file_name) +call_metafile_changed_for_one_file (NautilusMetafile *metafile, + const CORBA_char *file_name) { Nautilus_FileNameList file_names = {0}; @@ -585,7 +781,7 @@ call_metafile_changed_for_one_file (NautilusDirectory *directory, file_names._length = 1; file_names._buffer = (CORBA_char **) &file_name; - call_metafile_changed (directory, &file_names); + call_metafile_changed (metafile, &file_names); } typedef struct { @@ -629,40 +825,40 @@ get_metadata_list_from_node (xmlNode *node, } static xmlNode * -create_metafile_root (NautilusDirectory *directory) +create_metafile_root (NautilusMetafile *metafile) { xmlNode *root; - if (directory->details->metafile == NULL) { - nautilus_metafile_set_metafile_contents (directory, xmlNewDoc (METAFILE_XML_VERSION)); + if (metafile->details->xml == NULL) { + nautilus_metafile_set_metafile_contents (metafile, xmlNewDoc (METAFILE_XML_VERSION)); } - root = xmlDocGetRootElement (directory->details->metafile); + root = xmlDocGetRootElement (metafile->details->xml); if (root == NULL) { - root = xmlNewDocNode (directory->details->metafile, NULL, "directory", NULL); - xmlDocSetRootElement (directory->details->metafile, root); + root = xmlNewDocNode (metafile->details->xml, NULL, "directory", NULL); + xmlDocSetRootElement (metafile->details->xml, root); } return root; } static xmlNode * -get_file_node (NautilusDirectory *directory, +get_file_node (NautilusMetafile *metafile, const char *file_name, gboolean create) { GHashTable *hash; xmlNode *root, *node; - g_assert (NAUTILUS_IS_DIRECTORY (directory)); + g_assert (NAUTILUS_IS_METAFILE (metafile)); - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; node = g_hash_table_lookup (hash, file_name); if (node != NULL) { return node; } if (create) { - root = create_metafile_root (directory); + root = create_metafile_root (metafile); node = xmlNewChild (root, NULL, "file", NULL); xmlSetProp (node, "name", file_name); g_hash_table_insert (hash, xmlMemStrdup (file_name), node); @@ -673,31 +869,31 @@ get_file_node (NautilusDirectory *directory, } static char * -get_metadata_string_from_metafile (NautilusDirectory *directory, +get_metadata_string_from_metafile (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata) { xmlNode *node; - node = get_file_node (directory, file_name, FALSE); + node = get_file_node (metafile, file_name, FALSE); return get_metadata_from_node (node, key, default_metadata); } static GList * -get_metadata_list_from_metafile (NautilusDirectory *directory, +get_metadata_list_from_metafile (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey) { xmlNode *node; - node = get_file_node (directory, file_name, FALSE); + node = get_file_node (metafile, file_name, FALSE); return get_metadata_list_from_node (node, list_key, list_subkey); } static gboolean -set_metadata_string_in_metafile (NautilusDirectory *directory, +set_metadata_string_in_metafile (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata, @@ -711,7 +907,7 @@ set_metadata_string_in_metafile (NautilusDirectory *directory, /* If the data in the metafile is already correct, do nothing. */ old_metadata = get_file_metadata - (directory, file_name, key, default_metadata); + (metafile, file_name, key, default_metadata); old_metadata_matches = nautilus_strcmp (old_metadata, metadata) == 0; g_free (old_metadata); @@ -729,7 +925,7 @@ set_metadata_string_in_metafile (NautilusDirectory *directory, } /* Get or create the node. */ - node = get_file_node (directory, file_name, value != NULL); + node = get_file_node (metafile, file_name, value != NULL); /* Add or remove a property node. */ if (node != NULL) { @@ -740,12 +936,12 @@ set_metadata_string_in_metafile (NautilusDirectory *directory, } /* Since we changed the tree, arrange for it to be written. */ - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); return TRUE; } static gboolean -set_metadata_list_in_metafile (NautilusDirectory *directory, +set_metadata_list_in_metafile (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey, @@ -757,7 +953,7 @@ set_metadata_list_in_metafile (NautilusDirectory *directory, xmlChar *property; /* Get or create the node. */ - node = get_file_node (directory, file_name, list != NULL); + node = get_file_node (metafile, file_name, list != NULL); /* Work with the list. */ changed = FALSE; @@ -798,7 +994,7 @@ set_metadata_list_in_metafile (NautilusDirectory *directory, return FALSE; } - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); return TRUE; } @@ -868,7 +1064,7 @@ metadata_value_equal (const MetadataValue *value_a, } static gboolean -set_metadata_in_metafile (NautilusDirectory *directory, +set_metadata_in_metafile (NautilusMetafile *metafile, const char *file_name, const char *key, const char *subkey, @@ -879,13 +1075,13 @@ set_metadata_in_metafile (NautilusDirectory *directory, if (!value->is_list) { g_assert (subkey == NULL); changed = set_metadata_string_in_metafile - (directory, file_name, key, + (metafile, file_name, key, value->default_value, value->value.string); } else { g_assert (value->default_value == NULL); changed = set_metadata_list_in_metafile - (directory, file_name, key, subkey, + (metafile, file_name, key, subkey, value->value.string_list); } @@ -893,7 +1089,7 @@ set_metadata_in_metafile (NautilusDirectory *directory, } static char * -get_metadata_string_from_table (NautilusDirectory *directory, +get_metadata_string_from_table (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata) @@ -902,7 +1098,7 @@ get_metadata_string_from_table (NautilusDirectory *directory, MetadataValue *value; /* Get the value from the hash table. */ - directory_table = directory->details->metadata_changes; + directory_table = metafile->details->changes; file_table = directory_table == NULL ? NULL : g_hash_table_lookup (directory_table, file_name); value = file_table == NULL ? NULL @@ -920,7 +1116,7 @@ get_metadata_string_from_table (NautilusDirectory *directory, } static GList * -get_metadata_list_from_table (NautilusDirectory *directory, +get_metadata_list_from_table (NautilusMetafile *metafile, const char *file_name, const char *key, const char *subkey) @@ -930,7 +1126,7 @@ get_metadata_list_from_table (NautilusDirectory *directory, MetadataValue *value; /* Get the value from the hash table. */ - directory_table = directory->details->metadata_changes; + directory_table = metafile->details->changes; file_table = directory_table == NULL ? NULL : g_hash_table_lookup (directory_table, file_name); if (file_table == NULL) { @@ -967,7 +1163,7 @@ str_or_null_equal (gconstpointer str_a, gconstpointer str_b) } static gboolean -set_metadata_eat_value (NautilusDirectory *directory, +set_metadata_eat_value (NautilusMetafile *metafile, const char *file_name, const char *key, const char *subkey, @@ -978,19 +1174,19 @@ set_metadata_eat_value (NautilusDirectory *directory, char *combined_key; MetadataValue *old_value; - if (directory->details->metafile_read) { + if (metafile->details->is_read) { changed = set_metadata_in_metafile - (directory, file_name, key, subkey, value); + (metafile, file_name, key, subkey, value); metadata_value_destroy (value); } else { /* Create hash table only when we need it. * We'll destroy it when we finish reading the metafile. */ - directory_table = directory->details->metadata_changes; + directory_table = metafile->details->changes; if (directory_table == NULL) { directory_table = g_hash_table_new (str_or_null_hash, str_or_null_equal); - directory->details->metadata_changes = directory_table; + metafile->details->changes = directory_table; } file_table = g_hash_table_lookup (directory_table, file_name); if (file_table == NULL) { @@ -1065,58 +1261,58 @@ destroy_xml_string_key (gpointer key, gpointer value, gpointer user_data) xmlFree (key); } -void -nautilus_metafile_destroy (NautilusDirectory *directory) +static void +metafile_free_metadata (NautilusMetafile *metafile) { - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); - g_hash_table_foreach (directory->details->metafile_node_hash, + g_hash_table_foreach (metafile->details->node_hash, destroy_xml_string_key, NULL); - xmlFreeDoc (directory->details->metafile); - destroy_metadata_changes_hash_table (directory->details->metadata_changes); + xmlFreeDoc (metafile->details->xml); + destroy_metadata_changes_hash_table (metafile->details->changes); } static char * -get_file_metadata (NautilusDirectory *directory, +get_file_metadata (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata) { - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), NULL); g_return_val_if_fail (!nautilus_str_is_empty (file_name), NULL); g_return_val_if_fail (!nautilus_str_is_empty (key), NULL); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { return get_metadata_string_from_metafile - (directory, file_name, key, default_metadata); + (metafile, file_name, key, default_metadata); } else { return get_metadata_string_from_table - (directory, file_name, key, default_metadata); + (metafile, file_name, key, default_metadata); } } static GList * -get_file_metadata_list (NautilusDirectory *directory, +get_file_metadata_list (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey) { - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), NULL); g_return_val_if_fail (!nautilus_str_is_empty (file_name), NULL); g_return_val_if_fail (!nautilus_str_is_empty (list_key), NULL); g_return_val_if_fail (!nautilus_str_is_empty (list_subkey), NULL); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { return get_metadata_list_from_metafile - (directory, file_name, list_key, list_subkey); + (metafile, file_name, list_key, list_subkey); } else { return get_metadata_list_from_table - (directory, file_name, list_key, list_subkey); + (metafile, file_name, list_key, list_subkey); } } static gboolean -set_file_metadata (NautilusDirectory *directory, +set_file_metadata (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata, @@ -1124,22 +1320,22 @@ set_file_metadata (NautilusDirectory *directory, { MetadataValue *value; - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (file_name), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (key), FALSE); - if (directory->details->metafile_read) { - return set_metadata_string_in_metafile (directory, file_name, key, + if (metafile->details->is_read) { + return set_metadata_string_in_metafile (metafile, file_name, key, default_metadata, metadata); } else { value = metadata_value_new (default_metadata, metadata); - return set_metadata_eat_value (directory, file_name, + return set_metadata_eat_value (metafile, file_name, key, NULL, value); } } static gboolean -set_file_metadata_list (NautilusDirectory *directory, +set_file_metadata_list (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey, @@ -1147,23 +1343,47 @@ set_file_metadata_list (NautilusDirectory *directory, { MetadataValue *value; - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (file_name), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (list_key), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (list_subkey), FALSE); - if (directory->details->metafile_read) { - return set_metadata_list_in_metafile (directory, file_name, + if (metafile->details->is_read) { + return set_metadata_list_in_metafile (metafile, file_name, list_key, list_subkey, list); } else { value = metadata_value_new_list (list); - return set_metadata_eat_value (directory, file_name, + return set_metadata_eat_value (metafile, file_name, list_key, list_subkey, value); } } +static char * +metafile_get_file_uri (NautilusMetafile *metafile, + const char *file_name) +{ + GnomeVFSURI *file_uri; + char *result; + + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), NULL); + g_return_val_if_fail (file_name != NULL, NULL); + + result = NULL; + + g_assert (metafile->details->directory_vfs_uri != NULL); + + file_uri = gnome_vfs_uri_append_string (metafile->details->directory_vfs_uri, file_name); + + if (file_uri != NULL) { + result = gnome_vfs_uri_to_string (file_uri, GNOME_VFS_URI_HIDE_NONE); + gnome_vfs_uri_unref (file_uri); + } + + return result; +} + static void -rename_file_metadata (NautilusDirectory *directory, +rename_file_metadata (NautilusMetafile *metafile, const char *old_file_name, const char *new_file_name) { @@ -1173,15 +1393,15 @@ rename_file_metadata (NautilusDirectory *directory, GHashTable *hash; char *old_file_uri, *new_file_uri; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); g_return_if_fail (old_file_name != NULL); g_return_if_fail (new_file_name != NULL); - remove_file_metadata (directory, new_file_name); + remove_file_metadata (metafile, new_file_name); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { /* Move data in XML document if present. */ - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; found = g_hash_table_lookup_extended (hash, old_file_name, &key, &value); if (found) { @@ -1193,7 +1413,7 @@ rename_file_metadata (NautilusDirectory *directory, g_hash_table_insert (hash, xmlMemStrdup (new_file_name), value); xmlSetProp (file_node, "name", new_file_name); - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); } } else { /* Move data in hash table. */ @@ -1201,7 +1421,7 @@ rename_file_metadata (NautilusDirectory *directory, * metafile on disk, this doesn't arrange for that * data to be moved to the new name. */ - hash = directory->details->metadata_changes; + hash = metafile->details->changes; found = g_hash_table_lookup_extended (hash, old_file_name, &key, &value); if (found) { @@ -1212,15 +1432,15 @@ rename_file_metadata (NautilusDirectory *directory, } /* Rename the thumbnails for the file, if any. */ - old_file_uri = nautilus_directory_get_file_uri (directory, old_file_name); - new_file_uri = nautilus_directory_get_file_uri (directory, new_file_name); + old_file_uri = metafile_get_file_uri (metafile, old_file_name); + new_file_uri = metafile_get_file_uri (metafile, new_file_name); nautilus_update_thumbnail_file_renamed (old_file_uri, new_file_uri); g_free (old_file_uri); g_free (new_file_uri); } typedef struct { - NautilusDirectory *directory; + NautilusMetafile *metafile; const char *file_name; } ChangeContext; @@ -1251,23 +1471,23 @@ apply_one_change (gpointer key, gpointer value, gpointer callback_data) } /* Set the metadata. */ - set_metadata_in_metafile (context->directory, context->file_name, + set_metadata_in_metafile (context->metafile, context->file_name, metadata_key, subkey, value); g_free (key_prefix); } static void -apply_file_changes (NautilusDirectory *directory, +apply_file_changes (NautilusMetafile *metafile, const char *file_name, GHashTable *changes) { ChangeContext context; - g_assert (NAUTILUS_IS_DIRECTORY (directory)); + g_assert (NAUTILUS_IS_METAFILE (metafile)); g_assert (file_name != NULL); g_assert (changes != NULL); - context.directory = directory; + context.metafile = metafile; context.file_name = file_name; g_hash_table_foreach (changes, apply_one_change, &context); @@ -1280,30 +1500,30 @@ apply_one_file_changes (gpointer key, gpointer value, gpointer callback_data) g_hash_table_destroy (value); } -void -nautilus_metafile_apply_pending_changes (NautilusDirectory *directory) +static void +nautilus_metafile_apply_pending_changes (NautilusMetafile *metafile) { - if (directory->details->metadata_changes == NULL) { + if (metafile->details->changes == NULL) { return; } - g_hash_table_foreach (directory->details->metadata_changes, - apply_one_file_changes, directory); - g_hash_table_destroy (directory->details->metadata_changes); - directory->details->metadata_changes = NULL; + g_hash_table_foreach (metafile->details->changes, + apply_one_file_changes, metafile); + g_hash_table_destroy (metafile->details->changes); + metafile->details->changes = NULL; } static void -copy_file_metadata (NautilusDirectory *source_directory, - const char *source_file_name, - NautilusDirectory *destination_directory, - const char *destination_file_name) +copy_file_metadata (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name) { xmlNodePtr source_node, node, root; GHashTable *hash, *changes; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (source_directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (source_metafile)); g_return_if_fail (source_file_name != NULL); - g_return_if_fail (NAUTILUS_IS_DIRECTORY (destination_directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (destination_metafile)); g_return_if_fail (destination_file_name != NULL); /* FIXME bugzilla.eazel.com 3343: This does not properly @@ -1312,17 +1532,17 @@ copy_file_metadata (NautilusDirectory *source_directory, */ remove_file_metadata - (destination_directory, destination_file_name); - g_assert (get_file_node (destination_directory, destination_file_name, FALSE) == NULL); + (destination_metafile, destination_file_name); + g_assert (get_file_node (destination_metafile, destination_file_name, FALSE) == NULL); - source_node = get_file_node (source_directory, source_file_name, FALSE); + source_node = get_file_node (source_metafile, source_file_name, FALSE); if (source_node != NULL) { - if (destination_directory->details->metafile_read) { + if (destination_metafile->details->is_read) { node = xmlCopyNode (source_node, TRUE); - root = create_metafile_root (destination_directory); + root = create_metafile_root (destination_metafile); xmlAddChild (root, node); xmlSetProp (node, "name", destination_file_name); - g_hash_table_insert (destination_directory->details->metafile_node_hash, + g_hash_table_insert (destination_metafile->details->node_hash, xmlMemStrdup (destination_file_name), node); } else { /* FIXME bugzilla.eazel.com 6526: Copying data into a destination @@ -1332,11 +1552,11 @@ copy_file_metadata (NautilusDirectory *source_directory, } } - hash = source_directory->details->metadata_changes; + hash = source_metafile->details->changes; if (hash != NULL) { changes = g_hash_table_lookup (hash, source_file_name); if (changes != NULL) { - apply_file_changes (destination_directory, + apply_file_changes (destination_metafile, destination_file_name, changes); } @@ -1348,8 +1568,8 @@ copy_file_metadata (NautilusDirectory *source_directory, } static void -remove_file_metadata (NautilusDirectory *directory, - const char *file_name) +remove_file_metadata (NautilusMetafile *metafile, + const char *file_name) { gboolean found; gpointer key, value; @@ -1357,12 +1577,12 @@ remove_file_metadata (NautilusDirectory *directory, GHashTable *hash; char *file_uri; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); g_return_if_fail (file_name != NULL); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { /* Remove data in XML document if present. */ - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; found = g_hash_table_lookup_extended (hash, file_name, &key, &value); if (found) { @@ -1373,7 +1593,7 @@ remove_file_metadata (NautilusDirectory *directory, xmlFree (key); nautilus_xml_remove_node (file_node); xmlFreeNode (file_node); - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); } } else { /* Remove data from hash table. */ @@ -1381,7 +1601,7 @@ remove_file_metadata (NautilusDirectory *directory, * metafile on disk, this does not arrange for it to * be removed when the metafile is later read. */ - hash = directory->details->metadata_changes; + hash = metafile->details->changes; if (hash != NULL) { found = g_hash_table_lookup_extended (hash, file_name, &key, &value); @@ -1394,30 +1614,30 @@ remove_file_metadata (NautilusDirectory *directory, } /* Delete the thumbnails for the file, if any. */ - file_uri = nautilus_directory_get_file_uri (directory, file_name); + file_uri = metafile_get_file_uri (metafile, file_name); nautilus_remove_thumbnail_for_file (file_uri); g_free (file_uri); } -void -nautilus_metafile_set_metafile_contents (NautilusDirectory *directory, - xmlDocPtr metafile_contents) +static void +nautilus_metafile_set_metafile_contents (NautilusMetafile *metafile, + xmlDocPtr metafile_contents) { GHashTable *hash; xmlNodePtr node; xmlChar *name; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); - g_return_if_fail (directory->details->metafile == NULL); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); + g_return_if_fail (metafile->details->xml == NULL); if (metafile_contents == NULL) { return; } - directory->details->metafile = metafile_contents; + metafile->details->xml = metafile_contents; /* Populate the node hash table. */ - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; for (node = nautilus_xml_get_root_children (metafile_contents); node != NULL; node = node->next) { if (strcmp (node->name, "file") == 0) { @@ -1432,3 +1652,478 @@ nautilus_metafile_set_metafile_contents (NautilusDirectory *directory, } } + +static void +metafile_read_cancel (NautilusMetafile *metafile) +{ + if (metafile->details->read_state != NULL) { + if (metafile->details->read_state->handle != NULL) { + nautilus_read_file_cancel (metafile->details->read_state->handle); + } + if (metafile->details->read_state->get_file_info_handle != NULL) { + gnome_vfs_async_cancel (metafile->details->read_state->get_file_info_handle); + } + g_free (metafile->details->read_state); + metafile->details->read_state = NULL; + } +} + +static gboolean +can_use_public_metafile (NautilusMetafile *metafile) +{ + NautilusSpeedTradeoffValue preference_value; + + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), FALSE); + + if (metafile->details->public_vfs_uri == NULL) { + return FALSE; + } + + preference_value = nautilus_preferences_get_integer (NAUTILUS_PREFERENCES_USE_PUBLIC_METADATA); + + if (preference_value == NAUTILUS_SPEED_TRADEOFF_ALWAYS) { + return TRUE; + } + + if (preference_value == NAUTILUS_SPEED_TRADEOFF_NEVER) { + return FALSE; + } + + g_assert (preference_value == NAUTILUS_SPEED_TRADEOFF_LOCAL_ONLY); + + return metafile->details->directory_vfs_uri == NULL || + gnome_vfs_uri_is_local (metafile->details->directory_vfs_uri); +} + +static void +metafile_read_mark_done (NautilusMetafile *metafile) +{ + g_free (metafile->details->read_state); + metafile->details->read_state = NULL; + + metafile->details->is_read = TRUE; + + /* Move over the changes to the metafile that were in the hash table. */ + nautilus_metafile_apply_pending_changes (metafile); + + /* Tell change-watchers that we have update information. */ + nautilus_metafile_notify_metafile_ready (metafile); + + async_read_done (metafile); +} + +static void +metafile_read_done (NautilusMetafile *metafile) +{ + metafile_read_mark_done (metafile); +} + +static void +metafile_read_try_public_metafile (NautilusMetafile *metafile) +{ + metafile->details->read_state->use_public_metafile = TRUE; + metafile_read_restart (metafile); +} + +static void +metafile_read_check_for_directory_callback (GnomeVFSAsyncHandle *handle, + GList *results, + gpointer callback_data) +{ + NautilusMetafile *metafile; + GnomeVFSGetFileInfoResult *result; + + metafile = NAUTILUS_METAFILE (callback_data); + + g_assert (metafile->details->read_state->get_file_info_handle == handle); + g_assert (nautilus_g_list_exactly_one_item (results)); + + metafile->details->read_state->get_file_info_handle = NULL; + + result = results->data; + + if (result->result == GNOME_VFS_OK + && ((result->file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE) != 0) + && result->file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) { + /* Is a directory. */ + metafile_read_try_public_metafile (metafile); + } else { + /* Not a directory. */ + metafile_read_done (metafile); + } +} + +static void +metafile_read_check_for_directory (NautilusMetafile *metafile) +{ + GList fake_list; + + /* We only get here if the public metafile is in question, + * which in turn only happens if the URI is one that gnome-vfs + * can handle. + */ + g_assert (metafile->details->directory_vfs_uri != NULL); + + /* We have to do a get_info call to check if this a directory. */ + fake_list.data = metafile->details->directory_vfs_uri; + fake_list.next = NULL; + fake_list.prev = NULL; + gnome_vfs_async_get_file_info + (&metafile->details->read_state->get_file_info_handle, + &fake_list, + GNOME_VFS_FILE_INFO_DEFAULT, + metafile_read_check_for_directory_callback, + metafile); +} + +static void +metafile_read_failed (NautilusMetafile *metafile) +{ + NautilusFile *file; + gboolean need_directory_check, is_directory; + + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + metafile->details->read_state->handle = NULL; + + if (!metafile->details->read_state->use_public_metafile + && can_use_public_metafile (metafile)) { + /* The goal here is to read the real metafile, but + * only if the directory is actually a directory. + */ + + /* First, check if we already know if it a directory. */ + file = nautilus_file_get (metafile->details->directory_uri); + if (file == NULL || file->details->is_gone) { + need_directory_check = FALSE; + is_directory = FALSE; + } else if (file->details->info == NULL) { + need_directory_check = TRUE; + is_directory = TRUE; + } else { + need_directory_check = FALSE; + is_directory = nautilus_file_is_directory (file); + } + nautilus_file_unref (file); + + /* Do the directory check if we don't know. */ + if (need_directory_check) { + metafile_read_check_for_directory (metafile); + return; + } + + /* Try for the public metafile if it is a directory. */ + if (is_directory) { + metafile_read_try_public_metafile (metafile); + return; + } + } + + metafile_read_done (metafile); +} + +static void +metafile_read_done_callback (GnomeVFSResult result, + GnomeVFSFileSize file_size, + char *file_contents, + gpointer callback_data) +{ + NautilusMetafile *metafile; + int size; + char *buffer; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->xml == NULL); + + if (result != GNOME_VFS_OK) { + g_assert (file_contents == NULL); + metafile_read_failed (metafile); + return; + } + + size = file_size; + if ((GnomeVFSFileSize) size != file_size) { + g_free (file_contents); + metafile_read_failed (metafile); + return; + } + + /* The gnome-xml parser requires a zero-terminated array. */ + buffer = g_realloc (file_contents, size + 1); + buffer[size] = '\0'; + nautilus_metafile_set_metafile_contents (metafile, + xmlParseMemory (buffer, size)); + g_free (buffer); + + metafile_read_done (metafile); +} + +static void +metafile_read_restart (NautilusMetafile *metafile) +{ + char *text_uri; + + text_uri = gnome_vfs_uri_to_string + (metafile->details->read_state->use_public_metafile + ? metafile->details->public_vfs_uri + : metafile->details->private_vfs_uri, + GNOME_VFS_URI_HIDE_NONE); + + metafile->details->read_state->handle = nautilus_read_entire_file_async + (text_uri, metafile_read_done_callback, metafile); + + g_free (text_uri); +} + +static gboolean +allow_metafile (NautilusMetafile *metafile) +{ + const char *uri; + + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + /* Note that this inhibits both reading and writing metadata + * completely. In the future we may want to inhibit writing to + * the real directory while allowing parallel-directory + * metadata. + */ + + /* For now, hard-code these schemes. Perhaps we should + * hardcode the schemes that are good for metadata instead of + * the schemes that are bad for it. + */ + /* FIXME bugzilla.eazel.com 2434: + * We need to handle this in a better way. Perhaps a + * better way can wait until we have support for metadata + * access inside gnome-vfs. + */ + uri = metafile->details->directory_uri; + if (nautilus_is_search_uri (uri) + || nautilus_istr_has_prefix (uri, "ghelp:") + || nautilus_istr_has_prefix (uri, "gnome-help:") + || nautilus_istr_has_prefix (uri, "help:") + || nautilus_istr_has_prefix (uri, "info:") + || nautilus_istr_has_prefix (uri, "man:") + || nautilus_istr_has_prefix (uri, "pipe:") + ) { + return FALSE; + } + + return TRUE; +} + +static void +metafile_read_start (NautilusMetafile *metafile) +{ + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + if (metafile->details->is_read + || metafile->details->read_state != NULL) { + return; + } + + if (!allow_metafile (metafile)) { + metafile_read_mark_done (metafile); + } else { + metafile->details->read_state = g_new0 (MetafileReadState, 1); + metafile_read_restart (metafile); + } +} + +static void +metafile_write_done (NautilusMetafile *metafile) +{ + if (metafile->details->write_state->write_again) { + metafile_write_start (metafile); + return; + } + + xmlFree (metafile->details->write_state->buffer); + g_free (metafile->details->write_state); + metafile->details->write_state = NULL; + bonobo_object_unref (BONOBO_OBJECT (metafile)); +} + +static void +metafile_write_failed (NautilusMetafile *metafile) +{ + if (metafile->details->write_state->use_public_metafile) { + metafile->details->write_state->use_public_metafile = FALSE; + metafile_write_start (metafile); + return; + } + + metafile_write_done (metafile); +} + +static void +metafile_write_failure_close_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + + metafile_write_failed (metafile); +} + +static void +metafile_write_success_close_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->write_state->handle == NULL); + + if (result != GNOME_VFS_OK) { + metafile_write_failed (metafile); + return; + } + + /* Now that we have finished writing, it is time to delete the + * private file if we wrote the public one. + */ + if (metafile->details->write_state->use_public_metafile) { + /* A synchronous unlink is OK here because the private + * metafiles are local, so an unlink is very fast. + */ + gnome_vfs_unlink_from_uri (metafile->details->private_vfs_uri); + } + + metafile_write_done (metafile); +} + +static void +metafile_write_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gconstpointer buffer, + GnomeVFSFileSize bytes_requested, + GnomeVFSFileSize bytes_read, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->write_state->handle == handle); + g_assert (metafile->details->write_state->buffer == buffer); + g_assert (metafile->details->write_state->size == bytes_requested); + + g_assert (metafile->details->write_state->handle != NULL); + gnome_vfs_async_close (metafile->details->write_state->handle, + result == GNOME_VFS_OK + ? metafile_write_success_close_callback + : metafile_write_failure_close_callback, + metafile); + metafile->details->write_state->handle = NULL; +} + +static void +metafile_write_create_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->write_state->handle == handle); + + if (result != GNOME_VFS_OK) { + metafile_write_failed (metafile); + return; + } + + gnome_vfs_async_write (metafile->details->write_state->handle, + metafile->details->write_state->buffer, + metafile->details->write_state->size, + metafile_write_callback, + metafile); +} + +static void +metafile_write_start (NautilusMetafile *metafile) +{ + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + metafile->details->write_state->write_again = FALSE; + + /* Open the file. */ + gnome_vfs_async_create_uri + (&metafile->details->write_state->handle, + metafile->details->write_state->use_public_metafile + ? metafile->details->public_vfs_uri + : metafile->details->private_vfs_uri, + GNOME_VFS_OPEN_WRITE, FALSE, METAFILE_PERMISSIONS, + metafile_write_create_callback, metafile); +} + +static void +metafile_write (NautilusMetafile *metafile) +{ + int xml_doc_size; + + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + bonobo_object_ref (BONOBO_OBJECT (metafile)); + + /* If we are already writing, then just remember to do it again. */ + if (metafile->details->write_state != NULL) { + bonobo_object_unref (BONOBO_OBJECT (metafile)); + metafile->details->write_state->write_again = TRUE; + return; + } + + /* Don't write anything if there's nothing to write. + * At some point, we might want to change this to actually delete + * the metafile in this case. + */ + if (metafile->details->xml == NULL) { + bonobo_object_unref (BONOBO_OBJECT (metafile)); + return; + } + + /* Create the write state. */ + metafile->details->write_state = g_new0 (MetafileWriteState, 1); + metafile->details->write_state->use_public_metafile + = can_use_public_metafile (metafile); + xmlDocDumpMemory (metafile->details->xml, + &metafile->details->write_state->buffer, + &xml_doc_size); + metafile->details->write_state->size = xml_doc_size; + metafile_write_start (metafile); +} + +static gboolean +metafile_write_idle_callback (gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + + metafile->details->write_idle_id = 0; + metafile_write (metafile); + + bonobo_object_unref (BONOBO_OBJECT (metafile)); + + return FALSE; +} + +static void +directory_request_write_metafile (NautilusMetafile *metafile) +{ + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + if (!allow_metafile (metafile)) { + return; + } + + /* Set up an idle task that will write the metafile. */ + if (metafile->details->write_idle_id == 0) { + bonobo_object_ref (BONOBO_OBJECT (metafile)); + metafile->details->write_idle_id = + gtk_idle_add (metafile_write_idle_callback, + metafile); + } +} diff --git a/libnautilus-extensions/nautilus-metafile.h b/libnautilus-extensions/nautilus-metafile.h index 94e2c3a44..7e22486b6 100644 --- a/libnautilus-extensions/nautilus-metafile.h +++ b/libnautilus-extensions/nautilus-metafile.h @@ -30,6 +30,7 @@ #include <gnome-xml/tree.h> #include "nautilus-directory.h" +#include "nautilus-file-utilities.h" #define NAUTILUS_TYPE_METAFILE (nautilus_metafile_get_type ()) #define NAUTILUS_METAFILE(obj) (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_METAFILE, NautilusMetafile)) @@ -44,16 +45,6 @@ typedef struct { NautilusMetafileDetails *details; } NautilusMetafile; -/* FIXME: move this back to nautilus-metafile.c after changes to - * nautilus-directory-metafile.c mean that it doesn't need to access it. - */ -struct NautilusMetafileDetails { - NautilusDirectory *directory; - - /* FIXME: remove this field when gratuitous ORBit hacks are removed */ - gboolean directory_ref_is_gone; -}; - typedef struct { BonoboXObjectClass parent_slot; POA_Nautilus_Metafile__epv epv; @@ -61,18 +52,9 @@ typedef struct { GtkType nautilus_metafile_get_type (void); -NautilusMetafile *nautilus_metafile_new (const char *directory_uri); - -void call_metafile_changed_for_all_files_mentioned_in_metafile (NautilusDirectory *directory); - -void nautilus_metafile_notify_metafile_ready (NautilusDirectory *directory); - -void nautilus_metafile_apply_pending_changes (NautilusDirectory *directory); - -void nautilus_metafile_destroy (NautilusDirectory *directory); - -void nautilus_metafile_set_metafile_contents (NautilusDirectory *directory, - xmlDocPtr metafile_contents); +NautilusMetafile *nautilus_metafile_get (const char *directory_uri); +/* Specifications for in-directory metafile. */ +#define NAUTILUS_METAFILE_NAME_SUFFIX ".nautilus-metafile.xml" #endif /* NAUTILUS_METAFILE_H */ diff --git a/libnautilus-extensions/nautilus-vfs-directory.c b/libnautilus-extensions/nautilus-vfs-directory.c index 2b29388c4..27678d939 100644 --- a/libnautilus-extensions/nautilus-vfs-directory.c +++ b/libnautilus-extensions/nautilus-vfs-directory.c @@ -148,40 +148,22 @@ vfs_are_all_files_seen (NautilusDirectory *directory) return directory->details->directory_loaded; } -static int -any_non_metafile_item (gconstpointer item, gconstpointer callback_data) -{ - /* A metafile is exactly what we are not looking for, anything else is a match. */ - return nautilus_file_matches_uri - (NAUTILUS_FILE (item), (const char *) callback_data) - ? 1 : 0; -} - static gboolean vfs_is_not_empty (NautilusDirectory *directory) { - char *public_metafile_uri; - gboolean not_empty; - + GList *node; + g_return_val_if_fail (NAUTILUS_IS_VFS_DIRECTORY (directory), FALSE); g_return_val_if_fail (nautilus_directory_is_anyone_monitoring_file_list (directory), FALSE); - - if (directory->details->public_metafile_vfs_uri == NULL) { - not_empty = directory->details->file_list != NULL; - } else { - public_metafile_uri = gnome_vfs_uri_to_string - (directory->details->public_metafile_vfs_uri, - GNOME_VFS_URI_HIDE_NONE); - - /* Return TRUE if the directory contains anything besides a metafile. */ - not_empty = g_list_find_custom (directory->details->file_list, - public_metafile_uri, - any_non_metafile_item) != NULL; - - g_free (public_metafile_uri); + + for (node = directory->details->file_list; node != NULL; node = node->next) { + if (!nautilus_file_is_metafile (NAUTILUS_FILE (node->data))) { + /* Return TRUE if the directory contains anything besides a metafile. */ + return TRUE; + } } - - return not_empty; + + return FALSE; } static void diff --git a/libnautilus-private/nautilus-directory-async.c b/libnautilus-private/nautilus-directory-async.c index f8dc8538d..868ebc5fe 100644 --- a/libnautilus-private/nautilus-directory-async.c +++ b/libnautilus-private/nautilus-directory-async.c @@ -57,29 +57,12 @@ #define DEBUG_START_STOP #endif -#define METAFILE_PERMISSIONS (GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE \ - | GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE \ - | GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE) #define DIRECTORY_LOAD_ITEMS_PER_CALLBACK 32 /* Keep async. jobs down to this number for all directories. */ #define MAX_ASYNC_JOBS 10 -struct MetafileReadState { - gboolean use_public_metafile; - NautilusReadFileHandle *handle; - GnomeVFSAsyncHandle *get_file_info_handle; -}; - -struct MetafileWriteState { - gboolean use_public_metafile; - GnomeVFSAsyncHandle *handle; - xmlChar *buffer; - GnomeVFSFileSize size; - gboolean write_again; -}; - struct TopLeftTextReadState { NautilusFile *file; NautilusReadFileHandle *handle; @@ -121,7 +104,6 @@ static GHashTable *async_jobs; /* Forward declarations for functions that need them. */ static void deep_count_load (NautilusDirectory *directory, const char *uri); -static void metafile_read_restart (NautilusDirectory *directory); static gboolean request_is_satisfied (NautilusDirectory *directory, NautilusFile *file, Request *request); @@ -417,539 +399,6 @@ file_info_cancel (NautilusDirectory *directory) } } -static void -metafile_read_cancel (NautilusDirectory *directory) -{ - if (directory->details->metafile_read_state != NULL) { - if (directory->details->metafile_read_state->handle != NULL) { - nautilus_read_file_cancel (directory->details->metafile_read_state->handle); - } - if (directory->details->metafile_read_state->get_file_info_handle != NULL) { - gnome_vfs_async_cancel (directory->details->metafile_read_state->get_file_info_handle); - } - g_free (directory->details->metafile_read_state); - directory->details->metafile_read_state = NULL; - - async_job_end (directory, "metafile read"); - } -} - -static gboolean -can_use_public_metafile (NautilusDirectory *directory) -{ - NautilusSpeedTradeoffValue preference_value; - - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE); - - if (directory->details->public_metafile_vfs_uri == NULL) { - return FALSE; - } - - preference_value = nautilus_preferences_get_integer (NAUTILUS_PREFERENCES_USE_PUBLIC_METADATA); - - if (preference_value == NAUTILUS_SPEED_TRADEOFF_ALWAYS) { - return TRUE; - } - - if (preference_value == NAUTILUS_SPEED_TRADEOFF_NEVER) { - return FALSE; - } - - g_assert (preference_value == NAUTILUS_SPEED_TRADEOFF_LOCAL_ONLY); - return nautilus_directory_is_local (directory); -} - -static void -metafile_read_mark_done (NautilusDirectory *directory) -{ - g_free (directory->details->metafile_read_state); - directory->details->metafile_read_state = NULL; - - directory->details->metafile_read = TRUE; - - /* Move over the changes to the metafile that were in the hash table. */ - nautilus_metafile_apply_pending_changes (directory); - - /* Tell change-watchers that we have update information. */ - nautilus_metafile_notify_metafile_ready (directory); - - /* Let the callers that were waiting for the metafile know. */ - nautilus_directory_async_state_changed (directory); -} - -static void -metafile_read_done (NautilusDirectory *directory) -{ - async_job_end (directory, "metafile read"); - metafile_read_mark_done (directory); -} - -static void -metafile_read_try_public_metafile (NautilusDirectory *directory) -{ - directory->details->metafile_read_state->use_public_metafile = TRUE; - metafile_read_restart (directory); -} - -static void -metafile_read_check_for_directory_callback (GnomeVFSAsyncHandle *handle, - GList *results, - gpointer callback_data) -{ - NautilusDirectory *directory; - GnomeVFSGetFileInfoResult *result; - - directory = NAUTILUS_DIRECTORY (callback_data); - - g_assert (directory->details->metafile_read_state->get_file_info_handle == handle); - g_assert (nautilus_g_list_exactly_one_item (results)); - - nautilus_directory_ref (directory); - - directory->details->metafile_read_state->get_file_info_handle = NULL; - - result = results->data; - - if (result->result == GNOME_VFS_OK - && ((result->file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE) != 0) - && result->file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) { - /* Is a directory. */ - metafile_read_try_public_metafile (directory); - } else { - /* Not a directory. */ - metafile_read_done (directory); - } - - nautilus_directory_unref (directory); -} - -static void -metafile_read_check_for_directory (NautilusDirectory *directory) -{ - GList fake_list; - - /* We only get here if the public metafile is in question, - * which in turn only happens if the URI is one that gnome-vfs - * can handle. - */ - g_assert (directory->details->vfs_uri != NULL); - - /* We have to do a get_info call to check if this a directory. */ - fake_list.data = directory->details->vfs_uri; - fake_list.next = NULL; - fake_list.prev = NULL; - gnome_vfs_async_get_file_info - (&directory->details->metafile_read_state->get_file_info_handle, - &fake_list, - GNOME_VFS_FILE_INFO_DEFAULT, - metafile_read_check_for_directory_callback, - directory); -} - -static void -metafile_read_failed (NautilusDirectory *directory) -{ - NautilusFile *file; - gboolean need_directory_check, is_directory; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - g_assert (directory->details->metafile == NULL); - - directory->details->metafile_read_state->handle = NULL; - - if (!directory->details->metafile_read_state->use_public_metafile - && can_use_public_metafile (directory)) { - /* The goal here is to read the real metafile, but - * only if the directory is actually a directory. - */ - - /* First, check if we already know if it a directory. */ - file = nautilus_file_get (directory->details->uri); - if (file == NULL || file->details->is_gone) { - need_directory_check = FALSE; - is_directory = FALSE; - } else if (file->details->info == NULL) { - need_directory_check = TRUE; - is_directory = TRUE; - } else { - need_directory_check = FALSE; - is_directory = nautilus_file_is_directory (file); - } - nautilus_file_unref (file); - - /* Do the directory check if we don't know. */ - if (need_directory_check) { - metafile_read_check_for_directory (directory); - return; - } - - /* Try for the public metafile if it is a directory. */ - if (is_directory) { - metafile_read_try_public_metafile (directory); - return; - } - } - - metafile_read_done (directory); -} - -static void -metafile_read_done_callback (GnomeVFSResult result, - GnomeVFSFileSize file_size, - char *file_contents, - gpointer callback_data) -{ - NautilusDirectory *directory; - int size; - char *buffer; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile == NULL); - - nautilus_directory_ref (directory); - - if (result != GNOME_VFS_OK) { - g_assert (file_contents == NULL); - metafile_read_failed (directory); - nautilus_directory_unref (directory); - return; - } - - size = file_size; - if ((GnomeVFSFileSize) size != file_size) { - g_free (file_contents); - metafile_read_failed (directory); - nautilus_directory_unref (directory); - return; - } - - /* The gnome-xml parser requires a zero-terminated array. */ - buffer = g_realloc (file_contents, size + 1); - buffer[size] = '\0'; - nautilus_metafile_set_metafile_contents (directory, - xmlParseMemory (buffer, size)); - g_free (buffer); - - metafile_read_done (directory); - - nautilus_directory_unref (directory); -} - -static void -metafile_read_restart (NautilusDirectory *directory) -{ - char *text_uri; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - text_uri = gnome_vfs_uri_to_string - (directory->details->metafile_read_state->use_public_metafile - ? directory->details->public_metafile_vfs_uri - : directory->details->private_metafile_vfs_uri, - GNOME_VFS_URI_HIDE_NONE); - - directory->details->metafile_read_state->handle = nautilus_read_entire_file_async - (text_uri, metafile_read_done_callback, directory); - - g_free (text_uri); -} - -static gboolean -allow_metafile (NautilusDirectory *directory) -{ - const char *uri; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - /* Note that this inhibits both reading and writing metadata - * completely. In the future we may want to inhibit writing to - * the real directory while allowing parallel-directory - * metadata. - */ - - /* For now, hard-code these schemes. Perhaps we should - * hardcode the schemes that are good for metadata instead of - * the schemes that are bad for it. - */ - /* FIXME bugzilla.eazel.com 2434: - * We need to handle this in a better way. Perhaps a - * better way can wait until we have support for metadata - * access inside gnome-vfs. - */ - uri = directory->details->uri; - if (nautilus_is_search_uri (uri) - || nautilus_istr_has_prefix (uri, "ghelp:") - || nautilus_istr_has_prefix (uri, "gnome-help:") - || nautilus_istr_has_prefix (uri, "help:") - || nautilus_istr_has_prefix (uri, "info:") - || nautilus_istr_has_prefix (uri, "man:") - || nautilus_istr_has_prefix (uri, "pipe:") - ) { - return FALSE; - } - - return TRUE; -} - -/* This checks if there's a request for the metafile contents. */ -static gboolean -is_anyone_waiting_for_metafile (NautilusDirectory *directory) -{ - GList *node; - ReadyCallback *callback; - Monitor *monitor; - - for (node = directory->details->call_when_ready_list; node != NULL; node = node->next) { - callback = node->data; - if (callback->request.metafile) { - return TRUE; - } - } - - for (node = directory->details->monitor_list; node != NULL; node = node->next) { - monitor = node->data; - if (monitor->request.metafile) { - return TRUE; - } - } - - return FALSE; -} - -static void -metafile_read_start (NautilusDirectory *directory) -{ - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - if (directory->details->metafile_read - || directory->details->metafile_read_state != NULL) { - return; - } - - g_assert (directory->details->metafile == NULL); - - if (!directory->details->load_metafile_for_server) { - return; - } - - if (!allow_metafile (directory)) { - metafile_read_mark_done (directory); - } else { - if (!async_job_start (directory, "metafile read")) { - return; - } - directory->details->metafile_read_state = g_new0 (MetafileReadState, 1); - metafile_read_restart (directory); - } -} - -static void -metafile_write_done (NautilusDirectory *directory) -{ - if (directory->details->metafile_write_state->write_again) { - nautilus_metafile_write_start (directory); - return; - } - - xmlFree (directory->details->metafile_write_state->buffer); - g_free (directory->details->metafile_write_state); - directory->details->metafile_write_state = NULL; - nautilus_directory_unref (directory); -} - -static void -metafile_write_failed (NautilusDirectory *directory) -{ - if (directory->details->metafile_write_state->use_public_metafile) { - directory->details->metafile_write_state->use_public_metafile = FALSE; - nautilus_metafile_write_start (directory); - return; - } - - metafile_write_done (directory); -} - -static void -metafile_write_failure_close_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - - nautilus_directory_ref (directory); - metafile_write_failed (directory); - nautilus_directory_unref (directory); -} - -static void -metafile_write_success_close_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile_write_state->handle == NULL); - - nautilus_directory_ref (directory); - - if (result != GNOME_VFS_OK) { - metafile_write_failed (directory); - nautilus_directory_unref (directory); - return; - } - - /* Now that we have finished writing, it is time to delete the - * private file if we wrote the public one. - */ - if (directory->details->metafile_write_state->use_public_metafile) { - /* A synchronous unlink is OK here because the private - * metafiles are local, so an unlink is very fast. - */ - gnome_vfs_unlink_from_uri (directory->details->private_metafile_vfs_uri); - } - - metafile_write_done (directory); - - nautilus_directory_unref (directory); -} - -static void -metafile_write_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gconstpointer buffer, - GnomeVFSFileSize bytes_requested, - GnomeVFSFileSize bytes_read, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile_write_state->handle == handle); - g_assert (directory->details->metafile_write_state->buffer == buffer); - g_assert (directory->details->metafile_write_state->size == bytes_requested); - - g_assert (directory->details->metafile_write_state->handle != NULL); - gnome_vfs_async_close (directory->details->metafile_write_state->handle, - result == GNOME_VFS_OK - ? metafile_write_success_close_callback - : metafile_write_failure_close_callback, - directory); - directory->details->metafile_write_state->handle = NULL; -} - -static void -metafile_write_create_callback (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - g_assert (directory->details->metafile_write_state->handle == handle); - - if (result != GNOME_VFS_OK) { - nautilus_directory_ref (directory); - metafile_write_failed (directory); - nautilus_directory_unref (directory); - return; - } - - gnome_vfs_async_write (directory->details->metafile_write_state->handle, - directory->details->metafile_write_state->buffer, - directory->details->metafile_write_state->size, - metafile_write_callback, - directory); -} - -void -nautilus_metafile_write_start (NautilusDirectory *directory) -{ - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - directory->details->metafile_write_state->write_again = FALSE; - - /* Open the file. */ - gnome_vfs_async_create_uri - (&directory->details->metafile_write_state->handle, - directory->details->metafile_write_state->use_public_metafile - ? directory->details->public_metafile_vfs_uri - : directory->details->private_metafile_vfs_uri, - GNOME_VFS_OPEN_WRITE, FALSE, METAFILE_PERMISSIONS, - metafile_write_create_callback, directory); -} - -static void -metafile_write (NautilusDirectory *directory) -{ - int xml_doc_size; - - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - nautilus_directory_ref (directory); - - /* If we are already writing, then just remember to do it again. */ - if (directory->details->metafile_write_state != NULL) { - nautilus_directory_unref (directory); - directory->details->metafile_write_state->write_again = TRUE; - return; - } - - /* Don't write anything if there's nothing to write. - * At some point, we might want to change this to actually delete - * the metafile in this case. - */ - if (directory->details->metafile == NULL) { - nautilus_directory_unref (directory); - return; - } - - /* Create the write state. */ - directory->details->metafile_write_state = g_new0 (MetafileWriteState, 1); - directory->details->metafile_write_state->use_public_metafile - = can_use_public_metafile (directory); - xmlDocDumpMemory (directory->details->metafile, - &directory->details->metafile_write_state->buffer, - &xml_doc_size); - directory->details->metafile_write_state->size = xml_doc_size; - nautilus_metafile_write_start (directory); -} - -static gboolean -metafile_write_idle_callback (gpointer callback_data) -{ - NautilusDirectory *directory; - - directory = NAUTILUS_DIRECTORY (callback_data); - - directory->details->write_metafile_idle_id = 0; - metafile_write (directory); - - nautilus_directory_unref (directory); - - return FALSE; -} - -void -nautilus_directory_request_write_metafile (NautilusDirectory *directory) -{ - g_assert (NAUTILUS_IS_DIRECTORY (directory)); - - if (!allow_metafile (directory)) { - return; - } - - /* Set up an idle task that will write the metafile. */ - if (directory->details->write_metafile_idle_id == 0) { - nautilus_directory_ref (directory); - directory->details->write_metafile_idle_id = - gtk_idle_add (metafile_write_idle_callback, - directory); - } -} - static int monitor_key_compare (gconstpointer a, gconstpointer data) @@ -1526,6 +975,31 @@ directory_load_callback (GnomeVFSAsyncHandle *handle, nautilus_directory_unref (directory); } +/* This checks if there's a request for the metafile contents. */ +static gboolean +is_anyone_waiting_for_metafile (NautilusDirectory *directory) +{ + GList *node; + ReadyCallback *callback; + Monitor *monitor; + + for (node = directory->details->call_when_ready_list; node != NULL; node = node->next) { + callback = node->data; + if (callback->request.metafile) { + return TRUE; + } + } + + for (node = directory->details->monitor_list; node != NULL; node = node->next) { + monitor = node->data; + if (monitor->request.metafile) { + return TRUE; + } + } + + return FALSE; +} + static void update_metadata_monitors (NautilusDirectory *directory) { @@ -2104,7 +1578,6 @@ call_ready_callbacks (NautilusDirectory *directory) node != NULL; node = next) { next = node->next; callback = node->data; - if (request_is_satisfied (directory, callback->file, &callback->request)) { break; } @@ -3299,9 +2772,6 @@ start_or_stop_io (NautilusDirectory *directory) /* Start or stop getting file info. */ file_info_start (directory); - /* Start or stop reading the metafile. */ - metafile_read_start (directory); - /* Start or stop reading files. */ file_list_start (directory); @@ -3367,7 +2837,6 @@ nautilus_directory_cancel (NautilusDirectory *directory) directory_count_cancel (directory); file_info_cancel (directory); file_list_cancel (directory); - metafile_read_cancel (directory); mime_list_cancel (directory); top_left_cancel (directory); diff --git a/libnautilus-private/nautilus-directory-metafile-monitor.c b/libnautilus-private/nautilus-directory-metafile-monitor.c index c90a640a7..47251d0b3 100644 --- a/libnautilus-private/nautilus-directory-metafile-monitor.c +++ b/libnautilus-private/nautilus-directory-metafile-monitor.c @@ -126,6 +126,10 @@ corba_metafile_ready (PortableServer_Servant servant, NautilusMetafileMonitor *monitor; monitor = NAUTILUS_METAFILE_MONITOR (bonobo_object_from_servant (servant)); - emit_change_signals_for_all_files (monitor->details->directory); + nautilus_idle_queue_add (monitor->details->directory->details->idle_queue, + (GFunc) nautilus_directory_async_state_changed, + monitor->details->directory, + NULL, + NULL); } diff --git a/libnautilus-private/nautilus-directory-metafile.c b/libnautilus-private/nautilus-directory-metafile.c index d9100c802..8cb75bdfa 100644 --- a/libnautilus-private/nautilus-directory-metafile.c +++ b/libnautilus-private/nautilus-directory-metafile.c @@ -34,9 +34,6 @@ #include <liboaf/liboaf.h> #include <stdio.h> -/* FIXME: remove this when evilness is removed */ -#include <libnautilus-extensions/nautilus-metafile.h> - static Nautilus_MetafileFactory factory = CORBA_OBJECT_NIL; static gboolean get_factory_from_oaf = TRUE; @@ -73,25 +70,11 @@ get_factory (void) return factory; } -/* FIXME: Remove this code. It's ORBit dependent and generally evil */ -static gboolean -corba_object_is_local (CORBA_Object obj) -{ - return obj->vepv != NULL; -} -static PortableServer_Servant -corba_object_get_servant (CORBA_Object obj) -{ - g_assert (corba_object_is_local (obj)); - return obj->servant; -} - static Nautilus_Metafile get_metafile (NautilusDirectory *directory) { char *uri; CORBA_Environment ev; - NautilusMetafile *metafile; uri = nautilus_directory_get_uri (directory); @@ -99,15 +82,6 @@ get_metafile (NautilusDirectory *directory) if (directory->details->metafile_corba_object == CORBA_OBJECT_NIL) { directory->details->metafile_corba_object = Nautilus_MetafileFactory_open (get_factory (), uri, &ev); - - /* FIXME: remove this cycle-breaking when no longer needed */ - if (corba_object_is_local (directory->details->metafile_corba_object)) { - metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (corba_object_get_servant (directory->details->metafile_corba_object))); - if (!metafile->details->directory_ref_is_gone) { - nautilus_directory_unref (metafile->details->directory); - metafile->details->directory_ref_is_gone = TRUE; - } - } } /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ @@ -470,6 +444,26 @@ nautilus_directory_rename_file_metadata (NautilusDirectory *directory, } void +nautilus_directory_rename_directory_metadata (NautilusDirectory *directory, + const char *new_directory_uri) +{ + CORBA_Environment ev; + Nautilus_Metafile metafile; + + g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (new_directory_uri != NULL); + + metafile = get_metafile (directory); + CORBA_exception_init (&ev); + + Nautilus_Metafile_rename_directory (metafile, new_directory_uri, &ev); + + /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ + CORBA_exception_free (&ev); + bonobo_object_release_unref (metafile, &ev); +} + +void nautilus_directory_register_metadata_monitor (NautilusDirectory *directory) { CORBA_Environment ev; diff --git a/libnautilus-private/nautilus-directory-metafile.h b/libnautilus-private/nautilus-directory-metafile.h index 511355a9b..ffe6eb4a5 100644 --- a/libnautilus-private/nautilus-directory-metafile.h +++ b/libnautilus-private/nautilus-directory-metafile.h @@ -74,6 +74,8 @@ void nautilus_directory_remove_file_metadata (NautilusDirectory *d void nautilus_directory_rename_file_metadata (NautilusDirectory *directory, const char *old_file_name, const char *new_file_name); +void nautilus_directory_rename_directory_metadata (NautilusDirectory *directory, + const char *new_directory_uri); void nautilus_directory_register_metadata_monitor (NautilusDirectory *directory); void nautilus_directory_unregister_metadata_monitor (NautilusDirectory *directory); diff --git a/libnautilus-private/nautilus-directory-private.h b/libnautilus-private/nautilus-directory-private.h index 46a56bf57..36a8750d4 100644 --- a/libnautilus-private/nautilus-directory-private.h +++ b/libnautilus-private/nautilus-directory-private.h @@ -34,10 +34,9 @@ #include "nautilus-file-utilities.h" #include "nautilus-directory-metafile-monitor.h" #include "nautilus-metafile-server.h" +#include "nautilus-idle-queue.h" typedef struct ActivationURIReadState ActivationURIReadState; -typedef struct MetafileReadState MetafileReadState; -typedef struct MetafileWriteState MetafileWriteState; typedef struct TopLeftTextReadState TopLeftTextReadState; struct NautilusDirectoryDetails @@ -45,33 +44,20 @@ struct NautilusDirectoryDetails /* The location. */ char *uri; GnomeVFSURI *vfs_uri; - GnomeVFSURI *private_metafile_vfs_uri; - GnomeVFSURI *public_metafile_vfs_uri; /* The file objects. */ NautilusFile *as_file; GList *file_list; GHashTable *file_hash; - /* The metadata. */ - gboolean metafile_read; - xmlDoc *metafile; - GHashTable *metafile_node_hash; - GHashTable *metadata_changes; - - /* State for reading and writing metadata. */ - MetafileReadState *metafile_read_state; - guint write_metafile_idle_id; - MetafileWriteState *metafile_write_state; - /* These lists are going to be pretty short. If we think they * are going to get big, we can use hash tables instead. */ GList *call_when_ready_list; GList *monitor_list; + NautilusIdleQueue *idle_queue; NautilusMetafileMonitor *metafile_monitor; - gboolean load_metafile_for_server; Nautilus_Metafile metafile_corba_object; gboolean in_async_service_loop; @@ -161,11 +147,9 @@ gboolean nautilus_directory_is_file_list_monitored (NautilusD gboolean nautilus_directory_is_anyone_monitoring_file_list (NautilusDirectory *directory); void nautilus_directory_remove_file_monitor_link (NautilusDirectory *directory, GList *link); -void nautilus_directory_request_write_metafile (NautilusDirectory *directory); void nautilus_directory_schedule_dequeue_pending (NautilusDirectory *directory); void nautilus_directory_stop_monitoring_file_list (NautilusDirectory *directory); void nautilus_directory_cancel (NautilusDirectory *directory); -void nautilus_metafile_write_start (NautilusDirectory *directory); void nautilus_async_destroying_file (NautilusFile *file); void nautilus_directory_force_reload_internal (NautilusDirectory *directory, GList *file_attributes); diff --git a/libnautilus-private/nautilus-directory.c b/libnautilus-private/nautilus-directory.c index aca258afd..15185b8b2 100644 --- a/libnautilus-private/nautilus-directory.c +++ b/libnautilus-private/nautilus-directory.c @@ -52,9 +52,6 @@ enum { static guint signals[LAST_SIGNAL]; -/* Specifications for in-directory metafile. */ -#define METAFILE_NAME ".nautilus-metafile.xml" - /* Specifications for parallel-directory metafile. */ #define METAFILES_DIRECTORY_NAME "metafiles" #define METAFILE_SUFFIX ".xml" @@ -130,7 +127,7 @@ nautilus_directory_initialize (gpointer object, gpointer klass) directory->details = g_new0 (NautilusDirectoryDetails, 1); directory->details->file_hash = g_hash_table_new (g_str_hash, g_str_equal); - directory->details->metafile_node_hash = g_hash_table_new (g_str_hash, g_str_equal); + directory->details->idle_queue = nautilus_idle_queue_new (); } void @@ -164,9 +161,7 @@ nautilus_directory_destroy (GtkObject *object) directory = NAUTILUS_DIRECTORY (object); - g_assert (directory->details->metafile_write_state == NULL); nautilus_directory_cancel (directory); - g_assert (directory->details->metafile_read_state == NULL); g_assert (directory->details->count_in_progress == NULL); g_assert (directory->details->top_left_read_state == NULL); @@ -189,26 +184,17 @@ nautilus_directory_destroy (GtkObject *object) gtk_idle_remove (directory->details->dequeue_pending_idle_id); } - nautilus_metafile_destroy (directory); - g_free (directory->details->uri); if (directory->details->vfs_uri != NULL) { gnome_vfs_uri_unref (directory->details->vfs_uri); } - if (directory->details->public_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->public_metafile_vfs_uri); - } - if (directory->details->private_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->private_metafile_vfs_uri); - } g_assert (directory->details->file_list == NULL); g_hash_table_destroy (directory->details->file_hash); - g_hash_table_destroy (directory->details->metafile_node_hash); + nautilus_idle_queue_destroy (directory->details->idle_queue); g_assert (directory->details->directory_load_in_progress == NULL); g_assert (directory->details->count_in_progress == NULL); g_assert (directory->details->dequeue_pending_idle_id == 0); gnome_vfs_file_info_list_unref (directory->details->pending_file_info); - g_assert (directory->details->write_metafile_idle_id == 0); g_free (directory->details); @@ -316,7 +302,7 @@ remove_preferences_callbacks (void) NULL); } -static char * +char * nautilus_directory_make_uri_canonical (const char *uri) { char *canonical_maybe_trailing_slash; @@ -495,41 +481,6 @@ nautilus_directory_get_uri (NautilusDirectory *directory) return g_strdup (directory->details->uri); } -static GnomeVFSURI * -construct_private_metafile_vfs_uri (const char *uri) -{ - GnomeVFSResult result; - char *user_directory; - GnomeVFSURI *user_directory_uri, *metafiles_directory_uri, *alternate_uri; - char *escaped_uri, *file_name; - - /* Ensure that the metafiles directory exists. */ - user_directory = nautilus_get_user_directory (); - user_directory_uri = gnome_vfs_uri_new (user_directory); - g_free (user_directory); - - metafiles_directory_uri = gnome_vfs_uri_append_file_name (user_directory_uri, - METAFILES_DIRECTORY_NAME); - gnome_vfs_uri_unref (user_directory_uri); - result = nautilus_make_directory_and_parents (metafiles_directory_uri, - METAFILES_DIRECTORY_PERMISSIONS); - if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) { - gnome_vfs_uri_unref (metafiles_directory_uri); - return NULL; - } - - /* Construct a file name from the URI. */ - escaped_uri = gnome_vfs_escape_slashes (uri); - file_name = g_strconcat (escaped_uri, ".xml", NULL); - g_free (escaped_uri); - - /* Construct a URI for something in the "metafiles" directory. */ - alternate_uri = gnome_vfs_uri_append_file_name (metafiles_directory_uri, file_name); - gnome_vfs_uri_unref (metafiles_directory_uri); - g_free (file_name); - - return alternate_uri; -} static NautilusDirectory * nautilus_directory_new (const char *uri) @@ -1026,13 +977,8 @@ set_directory_uri (NautilusDirectory *directory, const char *new_uri) { GnomeVFSURI *new_vfs_uri; - GnomeVFSURI *new_public_metafile_vfs_uri; - GnomeVFSURI *new_private_metafile_vfs_uri; new_vfs_uri = gnome_vfs_uri_new (new_uri); - new_public_metafile_vfs_uri = new_vfs_uri == NULL ? NULL - : gnome_vfs_uri_append_file_name (new_vfs_uri, METAFILE_NAME); - new_private_metafile_vfs_uri = construct_private_metafile_vfs_uri (new_uri); g_free (directory->details->uri); directory->details->uri = g_strdup (new_uri); @@ -1041,58 +987,18 @@ set_directory_uri (NautilusDirectory *directory, gnome_vfs_uri_unref (directory->details->vfs_uri); } directory->details->vfs_uri = new_vfs_uri; - - if (directory->details->public_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->public_metafile_vfs_uri); - } - directory->details->public_metafile_vfs_uri = - new_public_metafile_vfs_uri; - - if (directory->details->private_metafile_vfs_uri != NULL) { - gnome_vfs_uri_unref (directory->details->private_metafile_vfs_uri); - } - directory->details->private_metafile_vfs_uri - = new_private_metafile_vfs_uri; -} - -static char * -get_path_from_vfs_uri (GnomeVFSURI *vfs_uri) -{ - char *uri, *path; - - if (vfs_uri == NULL) { - return NULL; - } - - uri = gnome_vfs_uri_to_string (vfs_uri, GNOME_VFS_URI_HIDE_NONE); - if (uri == NULL) { - return NULL; - } - path = gnome_vfs_get_local_path_from_uri (uri); - g_free (uri); - return path; -} - -static char * -get_private_metafile_path (NautilusDirectory *directory) -{ - return get_path_from_vfs_uri (directory->details->private_metafile_vfs_uri); } static void change_directory_uri (NautilusDirectory *directory, const char *new_uri) { - char *old_metafile_path, *new_metafile_path; - /* I believe it's impossible for a self-owned file/directory * to be moved. But if that did somehow happen, this function * wouldn't do enough to handle it. */ g_return_if_fail (directory->details->as_file == NULL); - old_metafile_path = get_private_metafile_path (directory); - g_hash_table_remove (directories, directory->details->uri); @@ -1102,14 +1008,7 @@ change_directory_uri (NautilusDirectory *directory, directory->details->uri, directory); - new_metafile_path = get_private_metafile_path (directory); - - if (old_metafile_path != NULL && new_metafile_path != NULL) { - rename (old_metafile_path, new_metafile_path); - } - - g_free (old_metafile_path); - g_free (new_metafile_path); + nautilus_directory_rename_directory_metadata (directory, new_uri); } typedef struct { @@ -1684,7 +1583,7 @@ nautilus_self_check_directory (void) gtk_main_iteration (); } - NAUTILUS_CHECK_BOOLEAN_RESULT (directory->details->metafile != NULL, TRUE); + NAUTILUS_CHECK_BOOLEAN_RESULT (nautilus_directory_is_metadata_read (directory), TRUE); got_files_flag = FALSE; diff --git a/libnautilus-private/nautilus-directory.h b/libnautilus-private/nautilus-directory.h index efd122c9f..7ec226dac 100644 --- a/libnautilus-private/nautilus-directory.h +++ b/libnautilus-private/nautilus-directory.h @@ -202,4 +202,6 @@ gboolean nautilus_directory_is_local (NautilusDirector gboolean nautilus_directory_is_not_empty (NautilusDirectory *directory); gboolean nautilus_directory_file_list_length_reached (NautilusDirectory *directory); +char * nautilus_directory_make_uri_canonical (const char *uri); + #endif /* NAUTILUS_DIRECTORY_H */ diff --git a/libnautilus-private/nautilus-file-utilities.c b/libnautilus-private/nautilus-file-utilities.c index 6d8e50494..7b2960067 100644 --- a/libnautilus-private/nautilus-file-utilities.c +++ b/libnautilus-private/nautilus-file-utilities.c @@ -30,6 +30,7 @@ #include "nautilus-lib-self-check-functions.h" #include "nautilus-link-set.h" #include "nautilus-metadata.h" +#include "nautilus-metafile.h" #include "nautilus-string.h" #include <ctype.h> #include <libgnome/gnome-defs.h> @@ -859,6 +860,14 @@ nautilus_file_name_matches_backup_pattern (const char *name_or_relative_uri) return nautilus_str_has_suffix (name_or_relative_uri, "~"); } +gboolean +nautilus_file_name_matches_metafile_pattern (const char *name_or_relative_uri) +{ + g_return_val_if_fail (name_or_relative_uri != NULL, FALSE); + + return nautilus_str_has_suffix (name_or_relative_uri, NAUTILUS_METAFILE_NAME_SUFFIX); +} + /** * nautilus_make_path: * diff --git a/libnautilus-private/nautilus-file-utilities.h b/libnautilus-private/nautilus-file-utilities.h index 22266fd82..37149623e 100644 --- a/libnautilus-private/nautilus-file-utilities.h +++ b/libnautilus-private/nautilus-file-utilities.h @@ -59,6 +59,7 @@ char * nautilus_uri_make_full_from_relative (const char /* Recognizing special file names. */ gboolean nautilus_file_name_matches_hidden_pattern (const char *name_or_relative_uri); gboolean nautilus_file_name_matches_backup_pattern (const char *name_or_relative_uri); +gboolean nautilus_file_name_matches_metafile_pattern(const char *name_or_relative_uri); /* FIXME bugzilla.eazel.com 2424: diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c index 0a796bcfb..84994fd23 100644 --- a/libnautilus-private/nautilus-file.c +++ b/libnautilus-private/nautilus-file.c @@ -1843,6 +1843,13 @@ nautilus_file_is_backup_file (NautilusFile *file) } gboolean +nautilus_file_is_metafile (NautilusFile *file) +{ + return nautilus_file_name_matches_metafile_pattern + (file->details->relative_uri); +} + +gboolean nautilus_file_should_show (NautilusFile *file, gboolean show_hidden, gboolean show_backup) diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h index 43810f0ad..f1a3fa364 100644 --- a/libnautilus-private/nautilus-file.h +++ b/libnautilus-private/nautilus-file.h @@ -264,6 +264,7 @@ int nautilus_file_compare_name (Nautilu /* filtering functions for use by various directory views */ gboolean nautilus_file_is_hidden_file (NautilusFile *file); gboolean nautilus_file_is_backup_file (NautilusFile *file); +gboolean nautilus_file_is_metafile (NautilusFile *file); gboolean nautilus_file_should_show (NautilusFile *file, gboolean show_hidden, gboolean show_backup); diff --git a/libnautilus-private/nautilus-metafile-factory.c b/libnautilus-private/nautilus-metafile-factory.c index 8ca0628bd..2686f278c 100644 --- a/libnautilus-private/nautilus-metafile-factory.c +++ b/libnautilus-private/nautilus-metafile-factory.c @@ -103,6 +103,6 @@ corba_open (PortableServer_Servant servant, CORBA_Environment *ev) { BonoboObject *object; - object = BONOBO_OBJECT (nautilus_metafile_new (directory)); + object = BONOBO_OBJECT (nautilus_metafile_get (directory)); return CORBA_Object_duplicate (bonobo_object_corba_objref (object), ev); } diff --git a/libnautilus-private/nautilus-metafile-server.idl b/libnautilus-private/nautilus-metafile-server.idl index 5d0500399..afa1a6617 100644 --- a/libnautilus-private/nautilus-metafile-server.idl +++ b/libnautilus-private/nautilus-metafile-server.idl @@ -70,12 +70,13 @@ module Nautilus { in MetadataList list); /* calls to keep metadata in sync with file operations */ - void copy (in string source_file_name, - in URI destination_directory_uri, - in string destination_file_name); - void remove (in string file_name); - void rename (in string old_file_name, - in string new_file_name); + void copy (in string source_file_name, + in URI destination_directory_uri, + in string destination_file_name); + void remove (in string file_name); + void rename (in string old_file_name, + in string new_file_name); + void rename_directory (in string new_directory_uri); /* calls for registering a MetafileMonitor with a Metafile */ void register_monitor (in MetafileMonitor monitor); diff --git a/libnautilus-private/nautilus-metafile.c b/libnautilus-private/nautilus-metafile.c index 2850a442c..939f0c82b 100644 --- a/libnautilus-private/nautilus-metafile.c +++ b/libnautilus-private/nautilus-metafile.c @@ -28,18 +28,38 @@ #include <libnautilus-extensions/nautilus-bonobo-extensions.h> #include <libnautilus/nautilus-bonobo-workarounds.h> +#include <libgnomevfs/gnome-vfs-types.h> +#include <libgnomevfs/gnome-vfs-uri.h> +#include <libgnomevfs/gnome-vfs-file-info.h> + #include "nautilus-string.h" #include "nautilus-metadata.h" #include "nautilus-thumbnails.h" #include "nautilus-xml-extensions.h" #include "nautilus-glib-extensions.h" -#include "nautilus-directory-private.h" +#include "nautilus-directory.h" +#include "nautilus-global-preferences.h" +#include "nautilus-file-private.h" +#include "nautilus-search-uri.h" #include <stdlib.h> +#include <gnome-xml/parser.h> #include <gnome-xml/xmlmemory.h> +#include <gtk/gtkmain.h> #define METAFILE_XML_VERSION "1.0" +#define METAFILE_PERMISSIONS (GNOME_VFS_PERM_USER_READ | GNOME_VFS_PERM_USER_WRITE \ + | GNOME_VFS_PERM_GROUP_READ | GNOME_VFS_PERM_GROUP_WRITE \ + | GNOME_VFS_PERM_OTHER_READ | GNOME_VFS_PERM_OTHER_WRITE) + +#define METAFILES_DIRECTORY_NAME "metafiles" + +#define METAFILES_DIRECTORY_PERMISSIONS \ + (GNOME_VFS_PERM_USER_ALL \ + | GNOME_VFS_PERM_GROUP_ALL \ + | GNOME_VFS_PERM_OTHER_ALL) + static void nautilus_metafile_init (NautilusMetafile *metafile); static void nautilus_metafile_class_init (NautilusMetafileClass *klass); @@ -72,18 +92,21 @@ static void corba_set_list (PortableServer_Servant servant, const Nautilus_MetadataList *list, CORBA_Environment *ev); -static void corba_copy (PortableServer_Servant servant, - const CORBA_char *source_file_name, - const Nautilus_URI destination_directory_uri, - const CORBA_char *destination_file_name, - CORBA_Environment *ev); -static void corba_remove (PortableServer_Servant servant, - const CORBA_char *file_name, - CORBA_Environment *ev); -static void corba_rename (PortableServer_Servant servant, - const CORBA_char *old_file_name, - const CORBA_char *new_file_name, - CORBA_Environment *ev); +static void corba_copy (PortableServer_Servant servant, + const CORBA_char *source_file_name, + const Nautilus_URI destination_directory_uri, + const CORBA_char *destination_file_name, + CORBA_Environment *ev); +static void corba_remove (PortableServer_Servant servant, + const CORBA_char *file_name, + CORBA_Environment *ev); +static void corba_rename (PortableServer_Servant servant, + const CORBA_char *old_file_name, + const CORBA_char *new_file_name, + CORBA_Environment *ev); +static void corba_rename_directory (PortableServer_Servant servant, + const CORBA_char *new_directory_uri, + CORBA_Environment *ev); static void corba_register_monitor (PortableServer_Servant servant, const Nautilus_MetafileMonitor monitor, @@ -92,40 +115,88 @@ static void corba_unregister_monitor (PortableServer_Servant servant, const Nautilus_MetafileMonitor monitor, CORBA_Environment *ev); -static char *get_file_metadata (NautilusDirectory *directory, - const char *file_name, - const char *key, - const char *default_metadata); -static GList *get_file_metadata_list (NautilusDirectory *directory, - const char *file_name, - const char *list_key, - const char *list_subkey); -static gboolean set_file_metadata (NautilusDirectory *directory, - const char *file_name, - const char *key, - const char *default_metadata, - const char *metadata); -static gboolean set_file_metadata_list (NautilusDirectory *directory, - const char *file_name, - const char *list_key, - const char *list_subkey, +static char *get_file_metadata (NautilusMetafile *metafile, + const char *file_name, + const char *key, + const char *default_metadata); +static GList *get_file_metadata_list (NautilusMetafile *metafile, + const char *file_name, + const char *list_key, + const char *list_subkey); +static gboolean set_file_metadata (NautilusMetafile *metafile, + const char *file_name, + const char *key, + const char *default_metadata, + const char *metadata); +static gboolean set_file_metadata_list (NautilusMetafile *metafile, + const char *file_name, + const char *list_key, + const char *list_subkey, GList *list); -static void rename_file_metadata (NautilusDirectory *directory, - const char *old_file_name, - const char *new_file_name); -static void copy_file_metadata (NautilusDirectory *source_directory, - const char *source_file_name, - NautilusDirectory *destination_directory, - const char *destination_file_name); -static void remove_file_metadata (NautilusDirectory *directory, - const char *file_name); - -static void call_metafile_changed_for_one_file (NautilusDirectory *directory, +static void rename_file_metadata (NautilusMetafile *metafile, + const char *old_file_name, + const char *new_file_name); +static void copy_file_metadata (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name); +static void remove_file_metadata (NautilusMetafile *metafile, + const char *file_name); + +static void call_metafile_changed_for_one_file (NautilusMetafile *metafile, const CORBA_char *file_name); +static void metafile_read_restart (NautilusMetafile *metafile); +static void metafile_read_start (NautilusMetafile *metafile); +static void metafile_write_start (NautilusMetafile *metafile); +static void directory_request_write_metafile (NautilusMetafile *metafile); +static void metafile_free_metadata (NautilusMetafile *metafile); +static void metafile_read_cancel (NautilusMetafile *metafile); +static void async_read_cancel (NautilusMetafile *metafile); + +static void nautilus_metafile_set_metafile_contents (NautilusMetafile *metafile, + xmlDocPtr metafile_contents); + NAUTILUS_BONOBO_X_BOILERPLATE (NautilusMetafile, Nautilus_Metafile, BONOBO_X_OBJECT_TYPE, nautilus_metafile) +typedef struct MetafileReadState { + gboolean use_public_metafile; + NautilusReadFileHandle *handle; + GnomeVFSAsyncHandle *get_file_info_handle; +} MetafileReadState; + +typedef struct MetafileWriteState { + gboolean use_public_metafile; + GnomeVFSAsyncHandle *handle; + xmlChar *buffer; + GnomeVFSFileSize size; + gboolean write_again; +} MetafileWriteState; + +struct NautilusMetafileDetails { + gboolean is_read; + + xmlDoc *xml; + GHashTable *node_hash; + GHashTable *changes; + + /* State for reading and writing metadata. */ + MetafileReadState *read_state; + guint write_idle_id; + MetafileWriteState *write_state; + + GList *monitors; + + GnomeVFSURI *private_vfs_uri; + GnomeVFSURI *public_vfs_uri; + + char *directory_uri; + GnomeVFSURI *directory_vfs_uri; +}; + +static GHashTable *metafiles; + static void nautilus_metafile_class_init (NautilusMetafileClass *klass) { @@ -139,6 +210,7 @@ nautilus_metafile_class_init (NautilusMetafileClass *klass) klass->epv.copy = corba_copy; klass->epv.remove = corba_remove; klass->epv.rename = corba_rename; + klass->epv.rename_directory = corba_rename_directory; klass->epv.register_monitor = corba_register_monitor; klass->epv.unregister_monitor = corba_unregister_monitor; } @@ -147,47 +219,246 @@ static void nautilus_metafile_init (NautilusMetafile *metafile) { metafile->details = g_new0 (NautilusMetafileDetails, 1); + + metafile->details->node_hash = g_hash_table_new (g_str_hash, g_str_equal); + } static void destroy (GtkObject *object) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (object); - directory = metafile->details->directory; - /* FIXME: remove this conditional when the field goes away */ - if (!metafile->details->directory_ref_is_gone) { - nautilus_directory_unref (directory); + g_assert (metafile->details->write_state == NULL); + async_read_cancel (metafile); + g_assert (metafile->details->read_state == NULL); + + if (metafile->details->public_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->public_vfs_uri); } + if (metafile->details->private_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->private_vfs_uri); + } + + g_hash_table_remove (metafiles, metafile->details->directory_uri); + + metafile_free_metadata (metafile); + g_hash_table_destroy (metafile->details->node_hash); + + g_assert (metafile->details->write_idle_id == 0); g_free (metafile->details); NAUTILUS_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object)); } -NautilusMetafile * +static GnomeVFSURI * +construct_private_metafile_vfs_uri (const char *uri) +{ + GnomeVFSResult result; + char *user_directory; + GnomeVFSURI *user_directory_uri, *metafiles_directory_uri, *alternate_uri; + char *escaped_uri, *file_name; + + /* Ensure that the metafiles directory exists. */ + user_directory = nautilus_get_user_directory (); + user_directory_uri = gnome_vfs_uri_new (user_directory); + g_free (user_directory); + + metafiles_directory_uri = gnome_vfs_uri_append_file_name (user_directory_uri, + METAFILES_DIRECTORY_NAME); + gnome_vfs_uri_unref (user_directory_uri); + result = nautilus_make_directory_and_parents (metafiles_directory_uri, + METAFILES_DIRECTORY_PERMISSIONS); + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_FILE_EXISTS) { + gnome_vfs_uri_unref (metafiles_directory_uri); + return NULL; + } + + /* Construct a file name from the URI. */ + escaped_uri = gnome_vfs_escape_slashes (uri); + file_name = g_strconcat (escaped_uri, ".xml", NULL); + g_free (escaped_uri); + + /* Construct a URI for something in the "metafiles" directory. */ + alternate_uri = gnome_vfs_uri_append_file_name (metafiles_directory_uri, file_name); + gnome_vfs_uri_unref (metafiles_directory_uri); + g_free (file_name); + + return alternate_uri; +} + +static void +nautilus_metafile_set_directory_uri (NautilusMetafile *metafile, const char *directory_uri) +{ + GnomeVFSURI *new_vfs_uri; + + if (nautilus_strcmp (metafile->details->directory_uri, directory_uri) == 0) { + return; + } + + g_free (metafile->details->directory_uri); + metafile->details->directory_uri = g_strdup (directory_uri); + + new_vfs_uri = gnome_vfs_uri_new (directory_uri); + + if (metafile->details->directory_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->directory_vfs_uri); + } + metafile->details->directory_vfs_uri = new_vfs_uri; + + if (metafile->details->public_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->public_vfs_uri); + } + metafile->details->public_vfs_uri = new_vfs_uri == NULL ? NULL + : gnome_vfs_uri_append_file_name (new_vfs_uri, NAUTILUS_METAFILE_NAME_SUFFIX); + + if (metafile->details->private_vfs_uri != NULL) { + gnome_vfs_uri_unref (metafile->details->private_vfs_uri); + } + metafile->details->private_vfs_uri + = construct_private_metafile_vfs_uri (directory_uri); + +} + +static NautilusMetafile * nautilus_metafile_new (const char *directory_uri) { NautilusMetafile *metafile; + metafile = NAUTILUS_METAFILE (gtk_object_new (NAUTILUS_TYPE_METAFILE, NULL)); - metafile->details->directory = nautilus_directory_get (directory_uri); + + nautilus_metafile_set_directory_uri (metafile, directory_uri); + return metafile; } +NautilusMetafile * +nautilus_metafile_get (const char *directory_uri) +{ + NautilusMetafile *metafile; + char *canonical_uri; + + g_return_val_if_fail (directory_uri != NULL, NULL); + + if (metafiles == NULL) { + metafiles = nautilus_g_hash_table_new_free_at_exit + (g_str_hash, g_str_equal, __FILE__ ": metafiles"); + } + + canonical_uri = nautilus_directory_make_uri_canonical (directory_uri); + + metafile = g_hash_table_lookup (metafiles, canonical_uri); + + if (metafile != NULL) { + bonobo_object_ref (BONOBO_OBJECT (metafile)); + } else { + metafile = nautilus_metafile_new (canonical_uri); + + g_assert (strcmp (metafile->details->directory_uri, canonical_uri) == 0); + + g_hash_table_insert (metafiles, + metafile->details->directory_uri, + metafile); + } + + g_free (canonical_uri); + + return metafile; +} + +/* FIXME + * Right now we only limit the number of conccurrent reads. + * We may want to consider limiting writes as well. + */ + +int num_reads_in_progress; +GList *pending_reads; + +#if 0 +#define DEBUG_METADATA_IO +#endif + +static void +schedule_next_read () +{ + const int kMaxAsyncReads = 10; + + GList* node; + +#ifdef DEBUG_METADATA_IO + g_message ("schedule_next_read: %d pending reads, %d reads in progress", + g_list_length (pending_reads), num_reads_in_progress); +#endif + + if (pending_reads != NULL && num_reads_in_progress <= kMaxAsyncReads) { + node = pending_reads; + pending_reads = g_list_remove_link (pending_reads, node); +#ifdef DEBUG_METADATA_IO + g_message ("schedule_next_read: %s", NAUTILUS_METAFILE (node->data)->details->directory_uri); +#endif + metafile_read_start (node->data); + g_list_free_1 (node); + ++num_reads_in_progress; + } +} + +static void +async_read_start (NautilusMetafile *metafile) +{ + if (metafile->details->is_read + || metafile->details->read_state != NULL) { + return; + } +#ifdef DEBUG_METADATA_IO + g_message ("async_read_start: %s", metafile->details->directory_uri); +#endif + pending_reads = g_list_prepend (pending_reads, metafile); + schedule_next_read (); +} + +static void +async_read_done (NautilusMetafile *metafile) +{ +#ifdef DEBUG_METADATA_IO + g_message ("async_read_done: %s", metafile->details->directory_uri); +#endif + --num_reads_in_progress; + schedule_next_read (); +} + +static void +async_read_cancel (NautilusMetafile *metafile) +{ + GList* node; + +#ifdef DEBUG_METADATA_IO + g_message ("async_read_cancel: %s", metafile->details->directory_uri); +#endif + node = g_list_find (pending_reads, metafile); + + if (node != NULL) { + pending_reads = g_list_remove_link (pending_reads, node); + g_list_free_1 (node); + } + + if (metafile->details->read_state != NULL) { + metafile_read_cancel (metafile); + async_read_done (metafile); + } + +} + static CORBA_boolean corba_is_read (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - - return directory->details->metafile_read ? CORBA_TRUE : CORBA_FALSE; + return metafile->details->is_read ? CORBA_TRUE : CORBA_FALSE; } static CORBA_char * @@ -198,15 +469,13 @@ corba_get (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; char *metadata; CORBA_char *result; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - metadata = get_file_metadata (directory, file_name, key, default_value); + metadata = get_file_metadata (metafile, file_name, key, default_value); result = CORBA_string_dup (metadata != NULL ? metadata : ""); @@ -223,7 +492,6 @@ corba_get_list (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; GList *metadata_list; Nautilus_MetadataList *result; @@ -232,9 +500,8 @@ corba_get_list (PortableServer_Servant servant, GList *list_ptr; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - metadata_list = get_file_metadata_list (directory, file_name, list_key, list_subkey); + metadata_list = get_file_metadata_list (metafile, file_name, list_key, list_subkey); len = g_list_length (metadata_list); result = Nautilus_MetadataList__alloc (); @@ -269,7 +536,6 @@ corba_set (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; if (nautilus_str_is_empty (default_value)) { default_value = NULL; @@ -279,10 +545,9 @@ corba_set (PortableServer_Servant servant, } metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - if (set_file_metadata (directory, file_name, key, default_value, metadata)) { - call_metafile_changed_for_one_file (directory, file_name); + if (set_file_metadata (metafile, file_name, key, default_value, metadata)) { + call_metafile_changed_for_one_file (metafile, file_name); } } @@ -295,13 +560,11 @@ corba_set_list (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; GList *metadata_list; CORBA_unsigned_long buf_pos; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; metadata_list = NULL; for (buf_pos = 0; buf_pos < list->_length; ++buf_pos) { @@ -309,8 +572,8 @@ corba_set_list (PortableServer_Servant servant, } metadata_list = g_list_reverse (metadata_list); - if (set_file_metadata_list (directory, file_name, list_key, list_subkey, metadata_list)) { - call_metafile_changed_for_one_file (directory, file_name); + if (set_file_metadata_list (metafile, file_name, list_key, list_subkey, metadata_list)) { + call_metafile_changed_for_one_file (metafile, file_name); } g_list_free (metadata_list); @@ -323,19 +586,17 @@ corba_copy (PortableServer_Servant servant, const CORBA_char *destination_file_name, CORBA_Environment *ev) { - NautilusMetafile *source_metafile; - NautilusDirectory *source_directory; - NautilusDirectory *destination_directory; + NautilusMetafile *source_metafile; + NautilusMetafile *destination_metafile; source_metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - source_directory = source_metafile->details->directory; - destination_directory = nautilus_directory_get (destination_directory_uri); + destination_metafile = nautilus_metafile_get (destination_directory_uri); - copy_file_metadata (source_directory, source_file_name, - destination_directory, destination_file_name); + copy_file_metadata (source_metafile, source_file_name, + destination_metafile, destination_file_name); - nautilus_directory_unref (destination_directory); + bonobo_object_unref (BONOBO_OBJECT (destination_metafile)); } static void @@ -344,12 +605,10 @@ corba_remove (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - remove_file_metadata (directory, file_name); + remove_file_metadata (metafile, file_name); } static void @@ -359,59 +618,22 @@ corba_rename (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - rename_file_metadata (directory, old_file_name, new_file_name); -} - -typedef struct { - GList *monitors; - NautilusDirectory *directory; -} DirectoryMonitorListEntry; - -static GHashTable *directory_monitor_lists; - -static DirectoryMonitorListEntry * -get_or_add_directory_monitor_list_entry (NautilusDirectory *directory) -{ - DirectoryMonitorListEntry *entry; - - if (directory_monitor_lists == NULL) { - directory_monitor_lists = nautilus_g_hash_table_new_free_at_exit (g_direct_hash, g_direct_equal, __FILE__ ": metadata monitors"); - } - - entry = g_hash_table_lookup (directory_monitor_lists, directory); - - if (entry == NULL) { - entry = g_new0 (DirectoryMonitorListEntry, 1); - nautilus_directory_ref (directory); - entry->directory = directory; - g_hash_table_insert (directory_monitor_lists, directory, entry); - } - - return entry; + rename_file_metadata (metafile, old_file_name, new_file_name); } static void -remove_directory_monitor_list_entry (NautilusDirectory *directory) +corba_rename_directory (PortableServer_Servant servant, + const CORBA_char *new_directory_uri, + CORBA_Environment *ev) { - DirectoryMonitorListEntry *entry; - - entry = g_hash_table_lookup (directory_monitor_lists, directory); + NautilusMetafile *metafile; - if (entry != NULL) { - /* This fn only handles removal when there are no monitors left. - * It makes no attempt to free the monitors. - */ - g_return_if_fail (entry->monitors == NULL); - - g_hash_table_remove (directory_monitor_lists, directory); - nautilus_directory_unref (directory); - g_free (entry); - } + metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); + + nautilus_metafile_set_directory_uri (metafile, new_directory_uri); } static GList * @@ -443,21 +665,14 @@ corba_register_monitor (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; - DirectoryMonitorListEntry *monitor_list; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - - monitor_list = get_or_add_directory_monitor_list_entry (directory); - g_return_if_fail (find_monitor_node (monitor_list->monitors, monitor) == NULL); + g_return_if_fail (find_monitor_node (metafile->details->monitors, monitor) == NULL); - monitor_list->monitors = g_list_prepend (monitor_list->monitors, (gpointer) CORBA_Object_duplicate (monitor, ev)); + metafile->details->monitors = g_list_prepend (metafile->details->monitors, (gpointer) CORBA_Object_duplicate (monitor, ev)); - /* cause metafile to be read */ - directory->details->load_metafile_for_server = TRUE; - nautilus_directory_async_state_changed (directory); + async_read_start (metafile); } static void @@ -466,77 +681,57 @@ corba_unregister_monitor (PortableServer_Servant servant, CORBA_Environment *ev) { NautilusMetafile *metafile; - NautilusDirectory *directory; - DirectoryMonitorListEntry *entry; GList *node; metafile = NAUTILUS_METAFILE (bonobo_object_from_servant (servant)); - directory = metafile->details->directory; - entry = g_hash_table_lookup (directory_monitor_lists, directory); - - g_return_if_fail (entry != NULL); - - node = find_monitor_node (entry->monitors, monitor); + node = find_monitor_node (metafile->details->monitors, monitor); g_return_if_fail (node != NULL); - entry->monitors = g_list_remove_link (entry->monitors, node); + metafile->details->monitors = g_list_remove_link (metafile->details->monitors, node); CORBA_Object_release (node->data, ev); g_list_free_1 (node); - - if (entry->monitors == NULL) { - remove_directory_monitor_list_entry (directory); - } } -void -nautilus_metafile_notify_metafile_ready (NautilusDirectory *directory) +static void +nautilus_metafile_notify_metafile_ready (NautilusMetafile *metafile) { GList *node; CORBA_Environment ev; Nautilus_MetafileMonitor monitor; - DirectoryMonitorListEntry *entry; - entry = g_hash_table_lookup (directory_monitor_lists, directory); + CORBA_exception_init (&ev); - if (entry != NULL) { - CORBA_exception_init (&ev); - - for (node = entry->monitors; node != NULL; node = node->next) { - monitor = node->data; - Nautilus_MetafileMonitor_metafile_ready (monitor, &ev); - /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ - } - - CORBA_exception_free (&ev); + for (node = metafile->details->monitors; node != NULL; node = node->next) { + monitor = node->data; + Nautilus_MetafileMonitor_metafile_ready (monitor, &ev); + /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ } + + CORBA_exception_free (&ev); } static void -call_metafile_changed (NautilusDirectory *directory, - const Nautilus_FileNameList *file_names) +call_metafile_changed (NautilusMetafile *metafile, + const Nautilus_FileNameList *file_names) { GList *node; CORBA_Environment ev; Nautilus_MetafileMonitor monitor; - DirectoryMonitorListEntry *entry; - entry = g_hash_table_lookup (directory_monitor_lists, directory); + CORBA_exception_init (&ev); - if (entry != NULL) { - CORBA_exception_init (&ev); - - for (node = entry->monitors; node != NULL; node = node->next) { - monitor = node->data; - Nautilus_MetafileMonitor_metafile_changed (monitor, file_names, &ev); - /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ - } - - CORBA_exception_free (&ev); + for (node = metafile->details->monitors; node != NULL; node = node->next) { + monitor = node->data; + Nautilus_MetafileMonitor_metafile_changed (monitor, file_names, &ev); + /* FIXME bugzilla.eazel.com 6664: examine ev for errors */ } + + CORBA_exception_free (&ev); } +#if 0 static void file_list_filler_ghfunc (gpointer key, @@ -553,31 +748,32 @@ file_list_filler_ghfunc (gpointer key, } void -call_metafile_changed_for_all_files_mentioned_in_metafile (NautilusDirectory *directory) +call_metafile_changed_for_all_files_mentioned_in_metafile (NautilusMetafile *metafile) { CORBA_unsigned_long len; Nautilus_FileNameList file_names; - len = g_hash_table_size (directory->details->metafile_node_hash); + len = g_hash_table_size (metafile->details->node_hash); if (len > 0) { file_names._maximum = len; file_names._length = 0; file_names._buffer = g_new (CORBA_char *, len); - g_hash_table_foreach (directory->details->metafile_node_hash, + g_hash_table_foreach (metafile->details->node_hash, file_list_filler_ghfunc, &file_names); - call_metafile_changed (directory, &file_names); + call_metafile_changed (metafile, &file_names); g_free (file_names._buffer); } } +#endif static void -call_metafile_changed_for_one_file (NautilusDirectory *directory, - const CORBA_char *file_name) +call_metafile_changed_for_one_file (NautilusMetafile *metafile, + const CORBA_char *file_name) { Nautilus_FileNameList file_names = {0}; @@ -585,7 +781,7 @@ call_metafile_changed_for_one_file (NautilusDirectory *directory, file_names._length = 1; file_names._buffer = (CORBA_char **) &file_name; - call_metafile_changed (directory, &file_names); + call_metafile_changed (metafile, &file_names); } typedef struct { @@ -629,40 +825,40 @@ get_metadata_list_from_node (xmlNode *node, } static xmlNode * -create_metafile_root (NautilusDirectory *directory) +create_metafile_root (NautilusMetafile *metafile) { xmlNode *root; - if (directory->details->metafile == NULL) { - nautilus_metafile_set_metafile_contents (directory, xmlNewDoc (METAFILE_XML_VERSION)); + if (metafile->details->xml == NULL) { + nautilus_metafile_set_metafile_contents (metafile, xmlNewDoc (METAFILE_XML_VERSION)); } - root = xmlDocGetRootElement (directory->details->metafile); + root = xmlDocGetRootElement (metafile->details->xml); if (root == NULL) { - root = xmlNewDocNode (directory->details->metafile, NULL, "directory", NULL); - xmlDocSetRootElement (directory->details->metafile, root); + root = xmlNewDocNode (metafile->details->xml, NULL, "directory", NULL); + xmlDocSetRootElement (metafile->details->xml, root); } return root; } static xmlNode * -get_file_node (NautilusDirectory *directory, +get_file_node (NautilusMetafile *metafile, const char *file_name, gboolean create) { GHashTable *hash; xmlNode *root, *node; - g_assert (NAUTILUS_IS_DIRECTORY (directory)); + g_assert (NAUTILUS_IS_METAFILE (metafile)); - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; node = g_hash_table_lookup (hash, file_name); if (node != NULL) { return node; } if (create) { - root = create_metafile_root (directory); + root = create_metafile_root (metafile); node = xmlNewChild (root, NULL, "file", NULL); xmlSetProp (node, "name", file_name); g_hash_table_insert (hash, xmlMemStrdup (file_name), node); @@ -673,31 +869,31 @@ get_file_node (NautilusDirectory *directory, } static char * -get_metadata_string_from_metafile (NautilusDirectory *directory, +get_metadata_string_from_metafile (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata) { xmlNode *node; - node = get_file_node (directory, file_name, FALSE); + node = get_file_node (metafile, file_name, FALSE); return get_metadata_from_node (node, key, default_metadata); } static GList * -get_metadata_list_from_metafile (NautilusDirectory *directory, +get_metadata_list_from_metafile (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey) { xmlNode *node; - node = get_file_node (directory, file_name, FALSE); + node = get_file_node (metafile, file_name, FALSE); return get_metadata_list_from_node (node, list_key, list_subkey); } static gboolean -set_metadata_string_in_metafile (NautilusDirectory *directory, +set_metadata_string_in_metafile (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata, @@ -711,7 +907,7 @@ set_metadata_string_in_metafile (NautilusDirectory *directory, /* If the data in the metafile is already correct, do nothing. */ old_metadata = get_file_metadata - (directory, file_name, key, default_metadata); + (metafile, file_name, key, default_metadata); old_metadata_matches = nautilus_strcmp (old_metadata, metadata) == 0; g_free (old_metadata); @@ -729,7 +925,7 @@ set_metadata_string_in_metafile (NautilusDirectory *directory, } /* Get or create the node. */ - node = get_file_node (directory, file_name, value != NULL); + node = get_file_node (metafile, file_name, value != NULL); /* Add or remove a property node. */ if (node != NULL) { @@ -740,12 +936,12 @@ set_metadata_string_in_metafile (NautilusDirectory *directory, } /* Since we changed the tree, arrange for it to be written. */ - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); return TRUE; } static gboolean -set_metadata_list_in_metafile (NautilusDirectory *directory, +set_metadata_list_in_metafile (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey, @@ -757,7 +953,7 @@ set_metadata_list_in_metafile (NautilusDirectory *directory, xmlChar *property; /* Get or create the node. */ - node = get_file_node (directory, file_name, list != NULL); + node = get_file_node (metafile, file_name, list != NULL); /* Work with the list. */ changed = FALSE; @@ -798,7 +994,7 @@ set_metadata_list_in_metafile (NautilusDirectory *directory, return FALSE; } - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); return TRUE; } @@ -868,7 +1064,7 @@ metadata_value_equal (const MetadataValue *value_a, } static gboolean -set_metadata_in_metafile (NautilusDirectory *directory, +set_metadata_in_metafile (NautilusMetafile *metafile, const char *file_name, const char *key, const char *subkey, @@ -879,13 +1075,13 @@ set_metadata_in_metafile (NautilusDirectory *directory, if (!value->is_list) { g_assert (subkey == NULL); changed = set_metadata_string_in_metafile - (directory, file_name, key, + (metafile, file_name, key, value->default_value, value->value.string); } else { g_assert (value->default_value == NULL); changed = set_metadata_list_in_metafile - (directory, file_name, key, subkey, + (metafile, file_name, key, subkey, value->value.string_list); } @@ -893,7 +1089,7 @@ set_metadata_in_metafile (NautilusDirectory *directory, } static char * -get_metadata_string_from_table (NautilusDirectory *directory, +get_metadata_string_from_table (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata) @@ -902,7 +1098,7 @@ get_metadata_string_from_table (NautilusDirectory *directory, MetadataValue *value; /* Get the value from the hash table. */ - directory_table = directory->details->metadata_changes; + directory_table = metafile->details->changes; file_table = directory_table == NULL ? NULL : g_hash_table_lookup (directory_table, file_name); value = file_table == NULL ? NULL @@ -920,7 +1116,7 @@ get_metadata_string_from_table (NautilusDirectory *directory, } static GList * -get_metadata_list_from_table (NautilusDirectory *directory, +get_metadata_list_from_table (NautilusMetafile *metafile, const char *file_name, const char *key, const char *subkey) @@ -930,7 +1126,7 @@ get_metadata_list_from_table (NautilusDirectory *directory, MetadataValue *value; /* Get the value from the hash table. */ - directory_table = directory->details->metadata_changes; + directory_table = metafile->details->changes; file_table = directory_table == NULL ? NULL : g_hash_table_lookup (directory_table, file_name); if (file_table == NULL) { @@ -967,7 +1163,7 @@ str_or_null_equal (gconstpointer str_a, gconstpointer str_b) } static gboolean -set_metadata_eat_value (NautilusDirectory *directory, +set_metadata_eat_value (NautilusMetafile *metafile, const char *file_name, const char *key, const char *subkey, @@ -978,19 +1174,19 @@ set_metadata_eat_value (NautilusDirectory *directory, char *combined_key; MetadataValue *old_value; - if (directory->details->metafile_read) { + if (metafile->details->is_read) { changed = set_metadata_in_metafile - (directory, file_name, key, subkey, value); + (metafile, file_name, key, subkey, value); metadata_value_destroy (value); } else { /* Create hash table only when we need it. * We'll destroy it when we finish reading the metafile. */ - directory_table = directory->details->metadata_changes; + directory_table = metafile->details->changes; if (directory_table == NULL) { directory_table = g_hash_table_new (str_or_null_hash, str_or_null_equal); - directory->details->metadata_changes = directory_table; + metafile->details->changes = directory_table; } file_table = g_hash_table_lookup (directory_table, file_name); if (file_table == NULL) { @@ -1065,58 +1261,58 @@ destroy_xml_string_key (gpointer key, gpointer value, gpointer user_data) xmlFree (key); } -void -nautilus_metafile_destroy (NautilusDirectory *directory) +static void +metafile_free_metadata (NautilusMetafile *metafile) { - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); - g_hash_table_foreach (directory->details->metafile_node_hash, + g_hash_table_foreach (metafile->details->node_hash, destroy_xml_string_key, NULL); - xmlFreeDoc (directory->details->metafile); - destroy_metadata_changes_hash_table (directory->details->metadata_changes); + xmlFreeDoc (metafile->details->xml); + destroy_metadata_changes_hash_table (metafile->details->changes); } static char * -get_file_metadata (NautilusDirectory *directory, +get_file_metadata (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata) { - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), NULL); g_return_val_if_fail (!nautilus_str_is_empty (file_name), NULL); g_return_val_if_fail (!nautilus_str_is_empty (key), NULL); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { return get_metadata_string_from_metafile - (directory, file_name, key, default_metadata); + (metafile, file_name, key, default_metadata); } else { return get_metadata_string_from_table - (directory, file_name, key, default_metadata); + (metafile, file_name, key, default_metadata); } } static GList * -get_file_metadata_list (NautilusDirectory *directory, +get_file_metadata_list (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey) { - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), NULL); g_return_val_if_fail (!nautilus_str_is_empty (file_name), NULL); g_return_val_if_fail (!nautilus_str_is_empty (list_key), NULL); g_return_val_if_fail (!nautilus_str_is_empty (list_subkey), NULL); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { return get_metadata_list_from_metafile - (directory, file_name, list_key, list_subkey); + (metafile, file_name, list_key, list_subkey); } else { return get_metadata_list_from_table - (directory, file_name, list_key, list_subkey); + (metafile, file_name, list_key, list_subkey); } } static gboolean -set_file_metadata (NautilusDirectory *directory, +set_file_metadata (NautilusMetafile *metafile, const char *file_name, const char *key, const char *default_metadata, @@ -1124,22 +1320,22 @@ set_file_metadata (NautilusDirectory *directory, { MetadataValue *value; - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (file_name), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (key), FALSE); - if (directory->details->metafile_read) { - return set_metadata_string_in_metafile (directory, file_name, key, + if (metafile->details->is_read) { + return set_metadata_string_in_metafile (metafile, file_name, key, default_metadata, metadata); } else { value = metadata_value_new (default_metadata, metadata); - return set_metadata_eat_value (directory, file_name, + return set_metadata_eat_value (metafile, file_name, key, NULL, value); } } static gboolean -set_file_metadata_list (NautilusDirectory *directory, +set_file_metadata_list (NautilusMetafile *metafile, const char *file_name, const char *list_key, const char *list_subkey, @@ -1147,23 +1343,47 @@ set_file_metadata_list (NautilusDirectory *directory, { MetadataValue *value; - g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE); + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (file_name), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (list_key), FALSE); g_return_val_if_fail (!nautilus_str_is_empty (list_subkey), FALSE); - if (directory->details->metafile_read) { - return set_metadata_list_in_metafile (directory, file_name, + if (metafile->details->is_read) { + return set_metadata_list_in_metafile (metafile, file_name, list_key, list_subkey, list); } else { value = metadata_value_new_list (list); - return set_metadata_eat_value (directory, file_name, + return set_metadata_eat_value (metafile, file_name, list_key, list_subkey, value); } } +static char * +metafile_get_file_uri (NautilusMetafile *metafile, + const char *file_name) +{ + GnomeVFSURI *file_uri; + char *result; + + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), NULL); + g_return_val_if_fail (file_name != NULL, NULL); + + result = NULL; + + g_assert (metafile->details->directory_vfs_uri != NULL); + + file_uri = gnome_vfs_uri_append_string (metafile->details->directory_vfs_uri, file_name); + + if (file_uri != NULL) { + result = gnome_vfs_uri_to_string (file_uri, GNOME_VFS_URI_HIDE_NONE); + gnome_vfs_uri_unref (file_uri); + } + + return result; +} + static void -rename_file_metadata (NautilusDirectory *directory, +rename_file_metadata (NautilusMetafile *metafile, const char *old_file_name, const char *new_file_name) { @@ -1173,15 +1393,15 @@ rename_file_metadata (NautilusDirectory *directory, GHashTable *hash; char *old_file_uri, *new_file_uri; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); g_return_if_fail (old_file_name != NULL); g_return_if_fail (new_file_name != NULL); - remove_file_metadata (directory, new_file_name); + remove_file_metadata (metafile, new_file_name); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { /* Move data in XML document if present. */ - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; found = g_hash_table_lookup_extended (hash, old_file_name, &key, &value); if (found) { @@ -1193,7 +1413,7 @@ rename_file_metadata (NautilusDirectory *directory, g_hash_table_insert (hash, xmlMemStrdup (new_file_name), value); xmlSetProp (file_node, "name", new_file_name); - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); } } else { /* Move data in hash table. */ @@ -1201,7 +1421,7 @@ rename_file_metadata (NautilusDirectory *directory, * metafile on disk, this doesn't arrange for that * data to be moved to the new name. */ - hash = directory->details->metadata_changes; + hash = metafile->details->changes; found = g_hash_table_lookup_extended (hash, old_file_name, &key, &value); if (found) { @@ -1212,15 +1432,15 @@ rename_file_metadata (NautilusDirectory *directory, } /* Rename the thumbnails for the file, if any. */ - old_file_uri = nautilus_directory_get_file_uri (directory, old_file_name); - new_file_uri = nautilus_directory_get_file_uri (directory, new_file_name); + old_file_uri = metafile_get_file_uri (metafile, old_file_name); + new_file_uri = metafile_get_file_uri (metafile, new_file_name); nautilus_update_thumbnail_file_renamed (old_file_uri, new_file_uri); g_free (old_file_uri); g_free (new_file_uri); } typedef struct { - NautilusDirectory *directory; + NautilusMetafile *metafile; const char *file_name; } ChangeContext; @@ -1251,23 +1471,23 @@ apply_one_change (gpointer key, gpointer value, gpointer callback_data) } /* Set the metadata. */ - set_metadata_in_metafile (context->directory, context->file_name, + set_metadata_in_metafile (context->metafile, context->file_name, metadata_key, subkey, value); g_free (key_prefix); } static void -apply_file_changes (NautilusDirectory *directory, +apply_file_changes (NautilusMetafile *metafile, const char *file_name, GHashTable *changes) { ChangeContext context; - g_assert (NAUTILUS_IS_DIRECTORY (directory)); + g_assert (NAUTILUS_IS_METAFILE (metafile)); g_assert (file_name != NULL); g_assert (changes != NULL); - context.directory = directory; + context.metafile = metafile; context.file_name = file_name; g_hash_table_foreach (changes, apply_one_change, &context); @@ -1280,30 +1500,30 @@ apply_one_file_changes (gpointer key, gpointer value, gpointer callback_data) g_hash_table_destroy (value); } -void -nautilus_metafile_apply_pending_changes (NautilusDirectory *directory) +static void +nautilus_metafile_apply_pending_changes (NautilusMetafile *metafile) { - if (directory->details->metadata_changes == NULL) { + if (metafile->details->changes == NULL) { return; } - g_hash_table_foreach (directory->details->metadata_changes, - apply_one_file_changes, directory); - g_hash_table_destroy (directory->details->metadata_changes); - directory->details->metadata_changes = NULL; + g_hash_table_foreach (metafile->details->changes, + apply_one_file_changes, metafile); + g_hash_table_destroy (metafile->details->changes); + metafile->details->changes = NULL; } static void -copy_file_metadata (NautilusDirectory *source_directory, - const char *source_file_name, - NautilusDirectory *destination_directory, - const char *destination_file_name) +copy_file_metadata (NautilusMetafile *source_metafile, + const char *source_file_name, + NautilusMetafile *destination_metafile, + const char *destination_file_name) { xmlNodePtr source_node, node, root; GHashTable *hash, *changes; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (source_directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (source_metafile)); g_return_if_fail (source_file_name != NULL); - g_return_if_fail (NAUTILUS_IS_DIRECTORY (destination_directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (destination_metafile)); g_return_if_fail (destination_file_name != NULL); /* FIXME bugzilla.eazel.com 3343: This does not properly @@ -1312,17 +1532,17 @@ copy_file_metadata (NautilusDirectory *source_directory, */ remove_file_metadata - (destination_directory, destination_file_name); - g_assert (get_file_node (destination_directory, destination_file_name, FALSE) == NULL); + (destination_metafile, destination_file_name); + g_assert (get_file_node (destination_metafile, destination_file_name, FALSE) == NULL); - source_node = get_file_node (source_directory, source_file_name, FALSE); + source_node = get_file_node (source_metafile, source_file_name, FALSE); if (source_node != NULL) { - if (destination_directory->details->metafile_read) { + if (destination_metafile->details->is_read) { node = xmlCopyNode (source_node, TRUE); - root = create_metafile_root (destination_directory); + root = create_metafile_root (destination_metafile); xmlAddChild (root, node); xmlSetProp (node, "name", destination_file_name); - g_hash_table_insert (destination_directory->details->metafile_node_hash, + g_hash_table_insert (destination_metafile->details->node_hash, xmlMemStrdup (destination_file_name), node); } else { /* FIXME bugzilla.eazel.com 6526: Copying data into a destination @@ -1332,11 +1552,11 @@ copy_file_metadata (NautilusDirectory *source_directory, } } - hash = source_directory->details->metadata_changes; + hash = source_metafile->details->changes; if (hash != NULL) { changes = g_hash_table_lookup (hash, source_file_name); if (changes != NULL) { - apply_file_changes (destination_directory, + apply_file_changes (destination_metafile, destination_file_name, changes); } @@ -1348,8 +1568,8 @@ copy_file_metadata (NautilusDirectory *source_directory, } static void -remove_file_metadata (NautilusDirectory *directory, - const char *file_name) +remove_file_metadata (NautilusMetafile *metafile, + const char *file_name) { gboolean found; gpointer key, value; @@ -1357,12 +1577,12 @@ remove_file_metadata (NautilusDirectory *directory, GHashTable *hash; char *file_uri; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); g_return_if_fail (file_name != NULL); - if (directory->details->metafile_read) { + if (metafile->details->is_read) { /* Remove data in XML document if present. */ - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; found = g_hash_table_lookup_extended (hash, file_name, &key, &value); if (found) { @@ -1373,7 +1593,7 @@ remove_file_metadata (NautilusDirectory *directory, xmlFree (key); nautilus_xml_remove_node (file_node); xmlFreeNode (file_node); - nautilus_directory_request_write_metafile (directory); + directory_request_write_metafile (metafile); } } else { /* Remove data from hash table. */ @@ -1381,7 +1601,7 @@ remove_file_metadata (NautilusDirectory *directory, * metafile on disk, this does not arrange for it to * be removed when the metafile is later read. */ - hash = directory->details->metadata_changes; + hash = metafile->details->changes; if (hash != NULL) { found = g_hash_table_lookup_extended (hash, file_name, &key, &value); @@ -1394,30 +1614,30 @@ remove_file_metadata (NautilusDirectory *directory, } /* Delete the thumbnails for the file, if any. */ - file_uri = nautilus_directory_get_file_uri (directory, file_name); + file_uri = metafile_get_file_uri (metafile, file_name); nautilus_remove_thumbnail_for_file (file_uri); g_free (file_uri); } -void -nautilus_metafile_set_metafile_contents (NautilusDirectory *directory, - xmlDocPtr metafile_contents) +static void +nautilus_metafile_set_metafile_contents (NautilusMetafile *metafile, + xmlDocPtr metafile_contents) { GHashTable *hash; xmlNodePtr node; xmlChar *name; - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); - g_return_if_fail (directory->details->metafile == NULL); + g_return_if_fail (NAUTILUS_IS_METAFILE (metafile)); + g_return_if_fail (metafile->details->xml == NULL); if (metafile_contents == NULL) { return; } - directory->details->metafile = metafile_contents; + metafile->details->xml = metafile_contents; /* Populate the node hash table. */ - hash = directory->details->metafile_node_hash; + hash = metafile->details->node_hash; for (node = nautilus_xml_get_root_children (metafile_contents); node != NULL; node = node->next) { if (strcmp (node->name, "file") == 0) { @@ -1432,3 +1652,478 @@ nautilus_metafile_set_metafile_contents (NautilusDirectory *directory, } } + +static void +metafile_read_cancel (NautilusMetafile *metafile) +{ + if (metafile->details->read_state != NULL) { + if (metafile->details->read_state->handle != NULL) { + nautilus_read_file_cancel (metafile->details->read_state->handle); + } + if (metafile->details->read_state->get_file_info_handle != NULL) { + gnome_vfs_async_cancel (metafile->details->read_state->get_file_info_handle); + } + g_free (metafile->details->read_state); + metafile->details->read_state = NULL; + } +} + +static gboolean +can_use_public_metafile (NautilusMetafile *metafile) +{ + NautilusSpeedTradeoffValue preference_value; + + g_return_val_if_fail (NAUTILUS_IS_METAFILE (metafile), FALSE); + + if (metafile->details->public_vfs_uri == NULL) { + return FALSE; + } + + preference_value = nautilus_preferences_get_integer (NAUTILUS_PREFERENCES_USE_PUBLIC_METADATA); + + if (preference_value == NAUTILUS_SPEED_TRADEOFF_ALWAYS) { + return TRUE; + } + + if (preference_value == NAUTILUS_SPEED_TRADEOFF_NEVER) { + return FALSE; + } + + g_assert (preference_value == NAUTILUS_SPEED_TRADEOFF_LOCAL_ONLY); + + return metafile->details->directory_vfs_uri == NULL || + gnome_vfs_uri_is_local (metafile->details->directory_vfs_uri); +} + +static void +metafile_read_mark_done (NautilusMetafile *metafile) +{ + g_free (metafile->details->read_state); + metafile->details->read_state = NULL; + + metafile->details->is_read = TRUE; + + /* Move over the changes to the metafile that were in the hash table. */ + nautilus_metafile_apply_pending_changes (metafile); + + /* Tell change-watchers that we have update information. */ + nautilus_metafile_notify_metafile_ready (metafile); + + async_read_done (metafile); +} + +static void +metafile_read_done (NautilusMetafile *metafile) +{ + metafile_read_mark_done (metafile); +} + +static void +metafile_read_try_public_metafile (NautilusMetafile *metafile) +{ + metafile->details->read_state->use_public_metafile = TRUE; + metafile_read_restart (metafile); +} + +static void +metafile_read_check_for_directory_callback (GnomeVFSAsyncHandle *handle, + GList *results, + gpointer callback_data) +{ + NautilusMetafile *metafile; + GnomeVFSGetFileInfoResult *result; + + metafile = NAUTILUS_METAFILE (callback_data); + + g_assert (metafile->details->read_state->get_file_info_handle == handle); + g_assert (nautilus_g_list_exactly_one_item (results)); + + metafile->details->read_state->get_file_info_handle = NULL; + + result = results->data; + + if (result->result == GNOME_VFS_OK + && ((result->file_info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE) != 0) + && result->file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) { + /* Is a directory. */ + metafile_read_try_public_metafile (metafile); + } else { + /* Not a directory. */ + metafile_read_done (metafile); + } +} + +static void +metafile_read_check_for_directory (NautilusMetafile *metafile) +{ + GList fake_list; + + /* We only get here if the public metafile is in question, + * which in turn only happens if the URI is one that gnome-vfs + * can handle. + */ + g_assert (metafile->details->directory_vfs_uri != NULL); + + /* We have to do a get_info call to check if this a directory. */ + fake_list.data = metafile->details->directory_vfs_uri; + fake_list.next = NULL; + fake_list.prev = NULL; + gnome_vfs_async_get_file_info + (&metafile->details->read_state->get_file_info_handle, + &fake_list, + GNOME_VFS_FILE_INFO_DEFAULT, + metafile_read_check_for_directory_callback, + metafile); +} + +static void +metafile_read_failed (NautilusMetafile *metafile) +{ + NautilusFile *file; + gboolean need_directory_check, is_directory; + + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + metafile->details->read_state->handle = NULL; + + if (!metafile->details->read_state->use_public_metafile + && can_use_public_metafile (metafile)) { + /* The goal here is to read the real metafile, but + * only if the directory is actually a directory. + */ + + /* First, check if we already know if it a directory. */ + file = nautilus_file_get (metafile->details->directory_uri); + if (file == NULL || file->details->is_gone) { + need_directory_check = FALSE; + is_directory = FALSE; + } else if (file->details->info == NULL) { + need_directory_check = TRUE; + is_directory = TRUE; + } else { + need_directory_check = FALSE; + is_directory = nautilus_file_is_directory (file); + } + nautilus_file_unref (file); + + /* Do the directory check if we don't know. */ + if (need_directory_check) { + metafile_read_check_for_directory (metafile); + return; + } + + /* Try for the public metafile if it is a directory. */ + if (is_directory) { + metafile_read_try_public_metafile (metafile); + return; + } + } + + metafile_read_done (metafile); +} + +static void +metafile_read_done_callback (GnomeVFSResult result, + GnomeVFSFileSize file_size, + char *file_contents, + gpointer callback_data) +{ + NautilusMetafile *metafile; + int size; + char *buffer; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->xml == NULL); + + if (result != GNOME_VFS_OK) { + g_assert (file_contents == NULL); + metafile_read_failed (metafile); + return; + } + + size = file_size; + if ((GnomeVFSFileSize) size != file_size) { + g_free (file_contents); + metafile_read_failed (metafile); + return; + } + + /* The gnome-xml parser requires a zero-terminated array. */ + buffer = g_realloc (file_contents, size + 1); + buffer[size] = '\0'; + nautilus_metafile_set_metafile_contents (metafile, + xmlParseMemory (buffer, size)); + g_free (buffer); + + metafile_read_done (metafile); +} + +static void +metafile_read_restart (NautilusMetafile *metafile) +{ + char *text_uri; + + text_uri = gnome_vfs_uri_to_string + (metafile->details->read_state->use_public_metafile + ? metafile->details->public_vfs_uri + : metafile->details->private_vfs_uri, + GNOME_VFS_URI_HIDE_NONE); + + metafile->details->read_state->handle = nautilus_read_entire_file_async + (text_uri, metafile_read_done_callback, metafile); + + g_free (text_uri); +} + +static gboolean +allow_metafile (NautilusMetafile *metafile) +{ + const char *uri; + + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + /* Note that this inhibits both reading and writing metadata + * completely. In the future we may want to inhibit writing to + * the real directory while allowing parallel-directory + * metadata. + */ + + /* For now, hard-code these schemes. Perhaps we should + * hardcode the schemes that are good for metadata instead of + * the schemes that are bad for it. + */ + /* FIXME bugzilla.eazel.com 2434: + * We need to handle this in a better way. Perhaps a + * better way can wait until we have support for metadata + * access inside gnome-vfs. + */ + uri = metafile->details->directory_uri; + if (nautilus_is_search_uri (uri) + || nautilus_istr_has_prefix (uri, "ghelp:") + || nautilus_istr_has_prefix (uri, "gnome-help:") + || nautilus_istr_has_prefix (uri, "help:") + || nautilus_istr_has_prefix (uri, "info:") + || nautilus_istr_has_prefix (uri, "man:") + || nautilus_istr_has_prefix (uri, "pipe:") + ) { + return FALSE; + } + + return TRUE; +} + +static void +metafile_read_start (NautilusMetafile *metafile) +{ + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + if (metafile->details->is_read + || metafile->details->read_state != NULL) { + return; + } + + if (!allow_metafile (metafile)) { + metafile_read_mark_done (metafile); + } else { + metafile->details->read_state = g_new0 (MetafileReadState, 1); + metafile_read_restart (metafile); + } +} + +static void +metafile_write_done (NautilusMetafile *metafile) +{ + if (metafile->details->write_state->write_again) { + metafile_write_start (metafile); + return; + } + + xmlFree (metafile->details->write_state->buffer); + g_free (metafile->details->write_state); + metafile->details->write_state = NULL; + bonobo_object_unref (BONOBO_OBJECT (metafile)); +} + +static void +metafile_write_failed (NautilusMetafile *metafile) +{ + if (metafile->details->write_state->use_public_metafile) { + metafile->details->write_state->use_public_metafile = FALSE; + metafile_write_start (metafile); + return; + } + + metafile_write_done (metafile); +} + +static void +metafile_write_failure_close_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + + metafile_write_failed (metafile); +} + +static void +metafile_write_success_close_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->write_state->handle == NULL); + + if (result != GNOME_VFS_OK) { + metafile_write_failed (metafile); + return; + } + + /* Now that we have finished writing, it is time to delete the + * private file if we wrote the public one. + */ + if (metafile->details->write_state->use_public_metafile) { + /* A synchronous unlink is OK here because the private + * metafiles are local, so an unlink is very fast. + */ + gnome_vfs_unlink_from_uri (metafile->details->private_vfs_uri); + } + + metafile_write_done (metafile); +} + +static void +metafile_write_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gconstpointer buffer, + GnomeVFSFileSize bytes_requested, + GnomeVFSFileSize bytes_read, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->write_state->handle == handle); + g_assert (metafile->details->write_state->buffer == buffer); + g_assert (metafile->details->write_state->size == bytes_requested); + + g_assert (metafile->details->write_state->handle != NULL); + gnome_vfs_async_close (metafile->details->write_state->handle, + result == GNOME_VFS_OK + ? metafile_write_success_close_callback + : metafile_write_failure_close_callback, + metafile); + metafile->details->write_state->handle = NULL; +} + +static void +metafile_write_create_callback (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + g_assert (metafile->details->write_state->handle == handle); + + if (result != GNOME_VFS_OK) { + metafile_write_failed (metafile); + return; + } + + gnome_vfs_async_write (metafile->details->write_state->handle, + metafile->details->write_state->buffer, + metafile->details->write_state->size, + metafile_write_callback, + metafile); +} + +static void +metafile_write_start (NautilusMetafile *metafile) +{ + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + metafile->details->write_state->write_again = FALSE; + + /* Open the file. */ + gnome_vfs_async_create_uri + (&metafile->details->write_state->handle, + metafile->details->write_state->use_public_metafile + ? metafile->details->public_vfs_uri + : metafile->details->private_vfs_uri, + GNOME_VFS_OPEN_WRITE, FALSE, METAFILE_PERMISSIONS, + metafile_write_create_callback, metafile); +} + +static void +metafile_write (NautilusMetafile *metafile) +{ + int xml_doc_size; + + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + bonobo_object_ref (BONOBO_OBJECT (metafile)); + + /* If we are already writing, then just remember to do it again. */ + if (metafile->details->write_state != NULL) { + bonobo_object_unref (BONOBO_OBJECT (metafile)); + metafile->details->write_state->write_again = TRUE; + return; + } + + /* Don't write anything if there's nothing to write. + * At some point, we might want to change this to actually delete + * the metafile in this case. + */ + if (metafile->details->xml == NULL) { + bonobo_object_unref (BONOBO_OBJECT (metafile)); + return; + } + + /* Create the write state. */ + metafile->details->write_state = g_new0 (MetafileWriteState, 1); + metafile->details->write_state->use_public_metafile + = can_use_public_metafile (metafile); + xmlDocDumpMemory (metafile->details->xml, + &metafile->details->write_state->buffer, + &xml_doc_size); + metafile->details->write_state->size = xml_doc_size; + metafile_write_start (metafile); +} + +static gboolean +metafile_write_idle_callback (gpointer callback_data) +{ + NautilusMetafile *metafile; + + metafile = NAUTILUS_METAFILE (callback_data); + + metafile->details->write_idle_id = 0; + metafile_write (metafile); + + bonobo_object_unref (BONOBO_OBJECT (metafile)); + + return FALSE; +} + +static void +directory_request_write_metafile (NautilusMetafile *metafile) +{ + g_assert (NAUTILUS_IS_METAFILE (metafile)); + + if (!allow_metafile (metafile)) { + return; + } + + /* Set up an idle task that will write the metafile. */ + if (metafile->details->write_idle_id == 0) { + bonobo_object_ref (BONOBO_OBJECT (metafile)); + metafile->details->write_idle_id = + gtk_idle_add (metafile_write_idle_callback, + metafile); + } +} diff --git a/libnautilus-private/nautilus-metafile.h b/libnautilus-private/nautilus-metafile.h index 94e2c3a44..7e22486b6 100644 --- a/libnautilus-private/nautilus-metafile.h +++ b/libnautilus-private/nautilus-metafile.h @@ -30,6 +30,7 @@ #include <gnome-xml/tree.h> #include "nautilus-directory.h" +#include "nautilus-file-utilities.h" #define NAUTILUS_TYPE_METAFILE (nautilus_metafile_get_type ()) #define NAUTILUS_METAFILE(obj) (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_METAFILE, NautilusMetafile)) @@ -44,16 +45,6 @@ typedef struct { NautilusMetafileDetails *details; } NautilusMetafile; -/* FIXME: move this back to nautilus-metafile.c after changes to - * nautilus-directory-metafile.c mean that it doesn't need to access it. - */ -struct NautilusMetafileDetails { - NautilusDirectory *directory; - - /* FIXME: remove this field when gratuitous ORBit hacks are removed */ - gboolean directory_ref_is_gone; -}; - typedef struct { BonoboXObjectClass parent_slot; POA_Nautilus_Metafile__epv epv; @@ -61,18 +52,9 @@ typedef struct { GtkType nautilus_metafile_get_type (void); -NautilusMetafile *nautilus_metafile_new (const char *directory_uri); - -void call_metafile_changed_for_all_files_mentioned_in_metafile (NautilusDirectory *directory); - -void nautilus_metafile_notify_metafile_ready (NautilusDirectory *directory); - -void nautilus_metafile_apply_pending_changes (NautilusDirectory *directory); - -void nautilus_metafile_destroy (NautilusDirectory *directory); - -void nautilus_metafile_set_metafile_contents (NautilusDirectory *directory, - xmlDocPtr metafile_contents); +NautilusMetafile *nautilus_metafile_get (const char *directory_uri); +/* Specifications for in-directory metafile. */ +#define NAUTILUS_METAFILE_NAME_SUFFIX ".nautilus-metafile.xml" #endif /* NAUTILUS_METAFILE_H */ diff --git a/libnautilus-private/nautilus-vfs-directory.c b/libnautilus-private/nautilus-vfs-directory.c index 2b29388c4..27678d939 100644 --- a/libnautilus-private/nautilus-vfs-directory.c +++ b/libnautilus-private/nautilus-vfs-directory.c @@ -148,40 +148,22 @@ vfs_are_all_files_seen (NautilusDirectory *directory) return directory->details->directory_loaded; } -static int -any_non_metafile_item (gconstpointer item, gconstpointer callback_data) -{ - /* A metafile is exactly what we are not looking for, anything else is a match. */ - return nautilus_file_matches_uri - (NAUTILUS_FILE (item), (const char *) callback_data) - ? 1 : 0; -} - static gboolean vfs_is_not_empty (NautilusDirectory *directory) { - char *public_metafile_uri; - gboolean not_empty; - + GList *node; + g_return_val_if_fail (NAUTILUS_IS_VFS_DIRECTORY (directory), FALSE); g_return_val_if_fail (nautilus_directory_is_anyone_monitoring_file_list (directory), FALSE); - - if (directory->details->public_metafile_vfs_uri == NULL) { - not_empty = directory->details->file_list != NULL; - } else { - public_metafile_uri = gnome_vfs_uri_to_string - (directory->details->public_metafile_vfs_uri, - GNOME_VFS_URI_HIDE_NONE); - - /* Return TRUE if the directory contains anything besides a metafile. */ - not_empty = g_list_find_custom (directory->details->file_list, - public_metafile_uri, - any_non_metafile_item) != NULL; - - g_free (public_metafile_uri); + + for (node = directory->details->file_list; node != NULL; node = node->next) { + if (!nautilus_file_is_metafile (NAUTILUS_FILE (node->data))) { + /* Return TRUE if the directory contains anything besides a metafile. */ + return TRUE; + } } - - return not_empty; + + return FALSE; } static void |