diff options
author | Darin Adler <darin@src.gnome.org> | 2000-08-22 02:11:45 +0000 |
---|---|---|
committer | Darin Adler <darin@src.gnome.org> | 2000-08-22 02:11:45 +0000 |
commit | 7295589544bf5b28024f25ab5783e0f8ec273502 (patch) | |
tree | 203998c60e5bc03a794a5162a8be544efe1bbeaf /libnautilus-extensions/nautilus-merged-directory.c | |
parent | 509e16baa8b3854b9cfab973b7b01cb11652a0ed (diff) | |
download | nautilus-7295589544bf5b28024f25ab5783e0f8ec273502.tar.gz |
Finished task 571 (Make a Trash "virtual directory" with trash
from all volumes). Some loose ends are already reported in bugs
2146, 2243, and 2244.
* libnautilus-extensions/Makefile.am:
* libnautilus-extensions/nautilus-merged-directory.h:
* libnautilus-extensions/nautilus-merged-directory.c:
Moved all the guts of NautilusTrashDirectory that are solely
about having a single directory that's a union of a bunch of
real directories into a separate base class. Also finished the
implementation, leaving only unimportant loose ends.
* libnautilus-extensions/nautilus-trash-directory.h:
* libnautilus-extensions/nautilus-trash-directory.c:
(get_volume_vfs_uri_if_writable), (find_directory_callback),
(add_volume), (remove_trash_volume), (remove_volume),
(add_one_volume), (volume_mounted_callback),
(volume_unmounted_callback), (nautilus_trash_directory_initialize),
(remove_trash_volume_cover), (trash_destroy),
(nautilus_trash_directory_initialize_class):
Added the code to find the trash on all volumes.
* libnautilus-extensions/nautilus-glib-extensions.h:
* libnautilus-extensions/nautilus-glib-extensions.c:
(flatten_hash_table_element),
(nautilus_g_hash_table_safe_for_each): Added a new version of the
hash table iterator that works even if the callback removes items
from the hash table.
* libnautilus-extensions/nautilus-scalable-font.c:
(nautilus_scalable_font_largest_fitting_font_size): Added code to
handle the case of an empty name; the old code worked with NULL,
but not with "".
* src/file-manager/fm-desktop-icon-view.c:
(fm_desktop_icon_view_trash_state_changed_callback),
(find_and_rename_trash_link), (create_or_rename_trash):
* src/file-manager/nautilus-trash-monitor.c:
(nautilus_trash_monitor_initialize):
Changed to use the new "trash:" URL instead of locating one of
the trash folders.
* src/file-manager/fm-directory-view.h:
* src/file-manager/fm-directory-view.c:
(fm_directory_view_initialize_class),
(fm_directory_all_selected_items_in_trash):
* src/file-manager/fm-search-list-view.c:
(fm_search_list_view_initialize_class):
The "share_parent" optimization didn't work for the new trash.
Instead of fixing it, I just removed it.
* src/nautilus-window-manage-views.c: (compute_default_title),
(nautilus_window_update_title): Made two changes to the default
title: 1) Use "" instead of "Nautilus" when there's no title.
We still use "Nautilus" in the window title, but not in the
sidebar any more. 2) Use the scheme part of a URI if there's
nothing after the colon. This works nicely for the trash.
* libnautilus-extensions/nautilus-background-canvas-group.c:
(nautilus_background_canvas_group_render): Removed some unneeded
code and the FIXME that goes with it.
* libnautilus-extensions/nautilus-directory.c:
(nautilus_directory_is_not_empty): Removed an overzealous assert.
Diffstat (limited to 'libnautilus-extensions/nautilus-merged-directory.c')
-rw-r--r-- | libnautilus-extensions/nautilus-merged-directory.c | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/libnautilus-extensions/nautilus-merged-directory.c b/libnautilus-extensions/nautilus-merged-directory.c new file mode 100644 index 000000000..b3c268efd --- /dev/null +++ b/libnautilus-extensions/nautilus-merged-directory.c @@ -0,0 +1,553 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-merged-directory.c: Subclass of NautilusDirectory to implement the + virtual merged directory. + + Copyright (C) 1999, 2000 Eazel, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Darin Adler <darin@eazel.com> +*/ + +#include <config.h> +#include "nautilus-merged-directory.h" + +#include "nautilus-directory-private.h" +#include "nautilus-file.h" +#include "nautilus-glib-extensions.h" +#include "nautilus-gtk-macros.h" +#include <gtk/gtksignal.h> + +struct NautilusMergedDirectoryDetails { + GList *directories; + GHashTable *callbacks; + GHashTable *monitors; +}; + +typedef struct { + /* Basic configuration. */ + NautilusMergedDirectory *merged; + NautilusDirectoryCallback callback; + gpointer callback_data; + + GList *wait_for_attributes; + gboolean wait_for_metadata; + + GList *non_ready_directories; + GList *merged_file_list; +} MergedCallback; + +typedef struct { + NautilusMergedDirectory *merged; + + GList *monitor_attributes; + gboolean monitor_metadata; + gboolean force_reload; +} MergedMonitor; + +static void nautilus_merged_directory_initialize (gpointer object, + gpointer klass); +static void nautilus_merged_directory_initialize_class (gpointer klass); +static void remove_all_real_directories (NautilusMergedDirectory *merged); +static guint merged_callback_hash (gconstpointer merged_callback); +static gboolean merged_callback_equal (gconstpointer merged_callback, + gconstpointer merged_callback_2); + +NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusMergedDirectory, + nautilus_merged_directory, + NAUTILUS_TYPE_DIRECTORY) + +static void +nautilus_merged_directory_initialize (gpointer object, gpointer klass) +{ + NautilusMergedDirectory *merged; + + merged = NAUTILUS_MERGED_DIRECTORY (object); + + merged->details = g_new0 (NautilusMergedDirectoryDetails, 1); + merged->details->callbacks = g_hash_table_new + (merged_callback_hash, merged_callback_equal); + merged->details->monitors = g_hash_table_new + (g_direct_hash, g_direct_equal); +} + +static void +merged_destroy (GtkObject *object) +{ + NautilusMergedDirectory *merged; + + merged = NAUTILUS_MERGED_DIRECTORY (object); + + remove_all_real_directories (merged); + + if (g_hash_table_size (merged->details->callbacks) != 0) { + g_warning ("call_when_ready still pending when merged virtual directory is destroyed"); + } + if (g_hash_table_size (merged->details->monitors) != 0) { + g_warning ("file monitor still active when merged virtual directory is destroyed"); + } + + g_hash_table_destroy (merged->details->callbacks); + g_hash_table_destroy (merged->details->monitors); + g_free (merged->details); + + NAUTILUS_CALL_PARENT_CLASS (GTK_OBJECT_CLASS, destroy, (object)); +} + +static guint +merged_callback_hash (gconstpointer merged_callback_as_pointer) +{ + const MergedCallback *merged_callback; + + merged_callback = merged_callback_as_pointer; + return GPOINTER_TO_UINT (merged_callback->callback) + ^ GPOINTER_TO_UINT (merged_callback->callback_data); +} + +static gboolean +merged_callback_equal (gconstpointer merged_callback_as_pointer, + gconstpointer merged_callback_as_pointer_2) +{ + const MergedCallback *merged_callback, *merged_callback_2; + + merged_callback = merged_callback_as_pointer; + merged_callback_2 = merged_callback_as_pointer_2; + + return merged_callback->callback == merged_callback_2->callback + && merged_callback->callback_data == merged_callback_2->callback_data; +} + +/* Return true if any directory in the list does. */ +static gboolean +merged_contains_file (NautilusDirectory *directory, + NautilusFile *file) +{ + NautilusMergedDirectory *merged; + GList *p; + + merged = NAUTILUS_MERGED_DIRECTORY (directory); + + for (p = merged->details->directories; p != NULL; p = p->next) { + if (nautilus_directory_contains_file (p->data, file)) { + return TRUE; + } + } + return FALSE; +} + +static void +merged_callback_destroy (MergedCallback *merged_callback) +{ + g_assert (merged_callback != NULL); + g_assert (NAUTILUS_IS_MERGED_DIRECTORY (merged_callback->merged)); + + nautilus_g_list_free_deep (merged_callback->wait_for_attributes); + g_list_free (merged_callback->non_ready_directories); + nautilus_file_list_free (merged_callback->merged_file_list); + g_free (merged_callback); +} + +static void +merged_callback_check_done (MergedCallback *merged_callback) +{ + /* Check if we are ready. */ + if (merged_callback->non_ready_directories != NULL) { + return; + } + + /* Remove from the hash table before sending it. */ + g_hash_table_remove (merged_callback->merged->details->callbacks, merged_callback); + + /* We are ready, so do the real callback. */ + (* merged_callback->callback) (NAUTILUS_DIRECTORY (merged_callback->merged), + merged_callback->merged_file_list, + merged_callback->callback_data); + + /* And we are done. */ + merged_callback_destroy (merged_callback); +} + +static void +merged_callback_remove_directory (MergedCallback *merged_callback, + NautilusDirectory *directory) +{ + merged_callback->non_ready_directories = g_list_remove + (merged_callback->non_ready_directories, + directory); + + /* Check if we are ready. */ + merged_callback_check_done (merged_callback); +} + +static void +directory_ready_callback (NautilusDirectory *directory, + GList *files, + gpointer callback_data) +{ + MergedCallback *merged_callback; + + g_assert (NAUTILUS_IS_DIRECTORY (directory)); + g_assert (callback_data != NULL); + + merged_callback = callback_data; + g_assert (g_list_find (merged_callback->non_ready_directories, directory) != NULL); + + /* Update based on this call. */ + merged_callback->merged_file_list = g_list_concat + (merged_callback->merged_file_list, + nautilus_file_list_copy (files)); + + /* Check if we are ready. */ + merged_callback_remove_directory (merged_callback, + directory); +} + +static void +merged_callback_connect_directory (MergedCallback *merged_callback, + NautilusDirectory *real_merged) +{ + nautilus_directory_call_when_ready + (real_merged, + merged_callback->wait_for_attributes, + merged_callback->wait_for_metadata, + directory_ready_callback, merged_callback); +} + +static void +merged_call_when_ready (NautilusDirectory *directory, + GList *file_attributes, + gboolean wait_for_metadata, + NautilusDirectoryCallback callback, + gpointer callback_data) +{ + NautilusMergedDirectory *merged; + MergedCallback search_key, *merged_callback; + GList *p; + + merged = NAUTILUS_MERGED_DIRECTORY (directory); + + /* Check to be sure we aren't overwriting. */ + search_key.callback = callback; + search_key.callback_data = callback_data; + if (g_hash_table_lookup (merged->details->callbacks, &search_key) != NULL) { + g_warning ("tried to add a new callback while an old one was pending"); + return; + } + + /* Create a merged_callback record. */ + merged_callback = g_new0 (MergedCallback, 1); + merged_callback->merged = merged; + merged_callback->callback = callback; + merged_callback->callback_data = callback_data; + merged_callback->wait_for_attributes = nautilus_g_str_list_copy (file_attributes); + merged_callback->wait_for_metadata = wait_for_metadata; + for (p = merged->details->directories; p != NULL; p = p->next) { + merged_callback->non_ready_directories = g_list_prepend + (merged_callback->non_ready_directories, p->data); + } + + /* Put it in the hash table. */ + g_hash_table_insert (merged->details->callbacks, + merged_callback, merged_callback); + + /* Handle the pathological case where there are no directories. */ + if (merged->details->directories == NULL) { + merged_callback_check_done (merged_callback); + } + + /* Now tell all the directories about it. */ + for (p = merged->details->directories; p != NULL; p = p->next) { + merged_callback_connect_directory (merged_callback, p->data); + } +} + +static void +merged_cancel_callback (NautilusDirectory *directory, + NautilusDirectoryCallback callback, + gpointer callback_data) +{ + NautilusMergedDirectory *merged; + MergedCallback search_key, *merged_callback; + GList *p; + + merged = NAUTILUS_MERGED_DIRECTORY (directory); + + /* Find the entry in the table. */ + search_key.callback = callback; + search_key.callback_data = callback_data; + merged_callback = g_hash_table_lookup (merged->details->callbacks, &search_key); + if (merged_callback == NULL) { + return; + } + + /* Remove from the hash table before working with it. */ + g_hash_table_remove (merged_callback->merged->details->callbacks, merged_callback); + + /* Tell all the directories to cancel the call. */ + for (p = merged_callback->non_ready_directories; p != NULL; p = p->next) { + nautilus_directory_cancel_callback + (p->data, + directory_ready_callback, merged_callback); + } + merged_callback_destroy (merged_callback); +} + +/* Create a monitor on each of the directories in the list. */ +static void +merged_file_monitor_add (NautilusDirectory *directory, + gconstpointer client, + GList *file_attributes, + gboolean monitor_metadata, + gboolean force_reload) +{ + NautilusMergedDirectory *merged; + MergedMonitor *monitor; + GList *p; + + merged = NAUTILUS_MERGED_DIRECTORY (directory); + + /* Map the client to a unique value so this doesn't interfere + * with direct monitoring of the directory by the same client. + */ + monitor = g_hash_table_lookup (merged->details->monitors, client); + if (monitor != NULL) { + g_assert (monitor->merged == merged); + nautilus_g_list_free_deep (monitor->monitor_attributes); + } else { + monitor = g_new0 (MergedMonitor, 1); + monitor->merged = merged; + g_hash_table_insert (merged->details->monitors, + (gpointer) client, monitor); + } + monitor->monitor_attributes = nautilus_g_str_list_copy (file_attributes); + monitor->monitor_metadata = monitor_metadata; + monitor->force_reload = force_reload; + + /* Call through to the real directory add calls. */ + for (p = merged->details->directories; p != NULL; p = p->next) { + nautilus_directory_file_monitor_add + (p->data, monitor, + file_attributes, monitor_metadata, force_reload); + } +} + +/* Remove the monitor from each of the directories in the list. */ +static void +merged_file_monitor_remove (NautilusDirectory *directory, + gconstpointer client) +{ + NautilusMergedDirectory *merged; + MergedMonitor *monitor; + GList *p; + + merged = NAUTILUS_MERGED_DIRECTORY (directory); + + /* Map the client to the value used by the earlier add call. */ + monitor = g_hash_table_lookup (merged->details->monitors, client); + if (monitor == NULL) { + return; + } + g_hash_table_remove (merged->details->monitors, client); + + /* Call through to the real directory remove calls. */ + for (p = merged->details->directories; p != NULL; p = p->next) { + nautilus_directory_file_monitor_remove + (p->data, monitor); + } + + nautilus_g_list_free_deep (monitor->monitor_attributes); + g_free (monitor); +} + +/* Return true only if all directories in the list do. */ +static gboolean +merged_are_all_files_seen (NautilusDirectory *directory) +{ + NautilusMergedDirectory *merged; + GList *p; + + merged = NAUTILUS_MERGED_DIRECTORY (directory); + + for (p = merged->details->directories; p != NULL; p = p->next) { + if (!nautilus_directory_are_all_files_seen (p->data)) { + return FALSE; + } + } + return TRUE; +} + +/* Return true if any directory in the list does. */ +static gboolean +merged_is_not_empty (NautilusDirectory *directory) +{ + NautilusMergedDirectory *merged; + GList *p; + + merged = NAUTILUS_MERGED_DIRECTORY (directory); + + for (p = merged->details->directories; p != NULL; p = p->next) { + if (nautilus_directory_is_not_empty (p->data)) { + return TRUE; + } + } + return FALSE; +} + +static void +nautilus_merged_directory_initialize_class (gpointer klass) +{ + GtkObjectClass *object_class; + NautilusDirectoryClass *directory_class; + + object_class = GTK_OBJECT_CLASS (klass); + directory_class = NAUTILUS_DIRECTORY_CLASS (klass); + + object_class->destroy = merged_destroy; + + directory_class->contains_file = merged_contains_file; + directory_class->call_when_ready = merged_call_when_ready; + directory_class->cancel_callback = merged_cancel_callback; + directory_class->file_monitor_add = merged_file_monitor_add; + directory_class->file_monitor_remove = merged_file_monitor_remove; + directory_class->are_all_files_seen = merged_are_all_files_seen; + directory_class->is_not_empty = merged_is_not_empty; +} + +static void +forward_files_added_cover (NautilusDirectory *real_directory, + GList *files, + NautilusMergedDirectory *merged) +{ + nautilus_directory_emit_files_added (NAUTILUS_DIRECTORY (merged), files); +} + +static void +forward_files_changed_cover (NautilusDirectory *real_directory, + GList *files, + NautilusMergedDirectory *merged) +{ + nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (merged), files); +} + +static void +monitor_add_directory (gpointer key, + gpointer value, + gpointer callback_data) +{ + MergedMonitor *monitor; + + monitor = value; + nautilus_directory_file_monitor_add + (NAUTILUS_DIRECTORY (callback_data), monitor, + monitor->monitor_attributes, + monitor->monitor_metadata, + monitor->force_reload); +} + +void +nautilus_merged_directory_add_real_directory (NautilusMergedDirectory *merged, + NautilusDirectory *real_directory) +{ + g_return_if_fail (NAUTILUS_IS_MERGED_DIRECTORY (merged)); + g_return_if_fail (NAUTILUS_IS_DIRECTORY (real_directory)); + g_return_if_fail (!NAUTILUS_IS_MERGED_DIRECTORY (real_directory)); + + /* Quietly do nothing if asked to add something that's already there. */ + if (g_list_find (merged->details->directories, real_directory) != NULL) { + return; + } + + /* Add to our list of directories. */ + nautilus_directory_ref (real_directory); + merged->details->directories = g_list_prepend + (merged->details->directories, real_directory); + + /* Connect signals. */ + gtk_signal_connect (GTK_OBJECT (real_directory), + "files_added", + forward_files_added_cover, + merged); + gtk_signal_connect (GTK_OBJECT (real_directory), + "files_changed", + forward_files_changed_cover, + merged); + /* FIXME: Hook up the done_loading signal too? */ + + /* Add the directory to any extant monitors. */ + g_hash_table_foreach (merged->details->monitors, + monitor_add_directory, + real_directory); + /* FIXME: Do we need to add the directory to callbacks too? */ +} + +static void +merged_callback_remove_directory_cover (gpointer key, + gpointer value, + gpointer callback_data) +{ + merged_callback_remove_directory + (value, NAUTILUS_DIRECTORY (callback_data)); +} + +static void +monitor_remove_directory (gpointer key, + gpointer value, + gpointer callback_data) +{ + nautilus_directory_file_monitor_remove + (NAUTILUS_DIRECTORY (callback_data), value); +} + +void +nautilus_merged_directory_remove_real_directory (NautilusMergedDirectory *merged, + NautilusDirectory *real_directory) +{ + g_return_if_fail (NAUTILUS_IS_MERGED_DIRECTORY (merged)); + g_return_if_fail (NAUTILUS_IS_DIRECTORY (real_directory)); + + /* Quietly do nothing if asked to remove something that's not there. */ + if (g_list_find (merged->details->directories, real_directory) == NULL) { + return; + } + + /* Remove this directory from callbacks and monitors. */ + nautilus_g_hash_table_safe_for_each + (merged->details->callbacks, + merged_callback_remove_directory_cover, + real_directory); + g_hash_table_foreach + (merged->details->monitors, + monitor_remove_directory, + real_directory); + + /* Disconnect all the signals. */ + gtk_signal_disconnect_by_data (GTK_OBJECT (real_directory), merged); + + /* Remove from our list of directories. */ + merged->details->directories = g_list_remove + (merged->details->directories, real_directory); + nautilus_directory_unref (real_directory); +} + +static void +remove_all_real_directories (NautilusMergedDirectory *merged) +{ + while (merged->details->directories != NULL) { + nautilus_merged_directory_remove_real_directory + (merged, merged->details->directories->data); + } +} |