diff options
author | Dave Camp <campd@src.gnome.org> | 2004-01-06 21:52:39 +0000 |
---|---|---|
committer | Dave Camp <campd@src.gnome.org> | 2004-01-06 21:52:39 +0000 |
commit | ebee7d61a54cc79965c0133c4e63d6a7a00eb1d2 (patch) | |
tree | 35ddcddb0bcf9c1d0ea0e425fc5c0d5a1f494d9e /src | |
parent | bd6e1847ced6fc5f041f5a05c108290a450c8c05 (diff) | |
download | nautilus-ebee7d61a54cc79965c0133c4e63d6a7a00eb1d2.tar.gz |
merged from HEADNAUTILUS_EXTENSIONS_BRANCH_AFTER_MERGE_1
Diffstat (limited to 'src')
44 files changed, 4675 insertions, 455 deletions
diff --git a/src/Nautilus_shell.server.in b/src/Nautilus_shell.server.in index 51b7ef937..a9c8d9d62 100644 --- a/src/Nautilus_shell.server.in +++ b/src/Nautilus_shell.server.in @@ -109,4 +109,15 @@ <oaf_attribute name="description" type="string" _value="Produces metafile objects for accessing Nautilus metadata"/> </oaf_server> +<oaf_server iid="OAFIID:Nautilus_File_Manager_Tree_View" type="factory" location="OAFIID:Nautilus_Factory"> + <oaf_attribute name="repo_ids" type="stringv"> + <item value="IDL:Bonobo/Unknown:1.0"/> + <item value="IDL:Bonobo/Control:1.0"/> + <item value="IDL:Nautilus/View:1.0"/> + </oaf_attribute> + <oaf_attribute name="name" type="string" _value="Nautilus Tree View"/> + <oaf_attribute name="description" type="string" _value="Nautilus Tree side pane"/> + <oaf_attribute name="nautilus:sidebar_panel_name" type="string" _value="Tree"/> +</oaf_server> + </oaf_info> diff --git a/src/file-manager/Makefile.am b/src/file-manager/Makefile.am index f96ea8ea1..03640c88a 100644 --- a/src/file-manager/Makefile.am +++ b/src/file-manager/Makefile.am @@ -27,6 +27,8 @@ libnautilus_file_manager_la_SOURCES= \ fm-search-list-view.c \ nautilus-indexing-info.c \ fm-bonobo-provider.h \ + fm-tree-model.c \ + fm-tree-view.c \ fm-desktop-icon-view.h \ fm-directory-view.h \ fm-error-reporting.h \ @@ -38,6 +40,8 @@ libnautilus_file_manager_la_SOURCES= \ fm-properties-window.h \ fm-search-list-view.h \ nautilus-indexing-info.h \ + fm-tree-model.h \ + fm-tree-view.h \ $(NULL) uidir = $(datadir)/gnome-2.0/ui diff --git a/src/file-manager/fm-directory-view.c b/src/file-manager/fm-directory-view.c index b757e08a6..336a8fad7 100644 --- a/src/file-manager/fm-directory-view.c +++ b/src/file-manager/fm-directory-view.c @@ -141,6 +141,8 @@ #define FM_DIRECTORY_VIEW_COMMAND_MEDIA_PROPERTIES_VOLUME_CONDITIONAL "/commands/Media Properties Conditional" #define FM_DIRECTORY_VIEW_MENU_PATH_OPEN_WITH "/menu/File/Open Placeholder/Open With" +#define FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS "/menu/File/New Items Placeholder/New Documents" +#define FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_NO_TEMPLATES "/menu/File/New Items Placeholder/New Documents/No Templates" #define FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS "/menu/File/Open Placeholder/Scripts" #define FM_DIRECTORY_VIEW_MENU_PATH_TRASH "/menu/Edit/Dangerous File Items Placeholder/Trash" #define FM_DIRECTORY_VIEW_MENU_PATH_DELETE "/menu/Edit/Dangerous File Items Placeholder/Delete" @@ -153,6 +155,8 @@ #define FM_DIRECTORY_VIEW_MENU_PATH_OTHER_VIEWER "/menu/File/Open Placeholder/Open With/OtherViewer" #define FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER "/menu/File/Open Placeholder/Scripts/Scripts Placeholder" #define FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_SEPARATOR "/menu/File/Open Placeholder/Scripts/After Scripts" +#define FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER "/menu/File/New Items Placeholder/New Documents/New Documents Placeholder" +#define FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_SEPARATOR "/menu/File/New Items Placeholder/New Documents/After New Documents" #define FM_DIRECTORY_VIEW_MENU_PATH_CUT_FILES "/menu/Edit/Cut" #define FM_DIRECTORY_VIEW_MENU_PATH_COPY_FILES "/menu/Edit/Copy" #define FM_DIRECTORY_VIEW_MENU_PATH_PASTE_FILES "/menu/Edit/Paste" @@ -164,6 +168,10 @@ #define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS "/popups/background/Before Zoom Items/Scripts" #define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER "/popups/background/Before Zoom Items/Scripts/Scripts Placeholder" #define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_SEPARATOR "/popups/background/Before Zoom Items/Scripts/After Scripts" +#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS "/popups/background/Before Zoom Items/New Documents" +#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_NO_TEMPLATES "/popups/background/Before Zoom Items/New Documents/No Templates" +#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER "/popups/background/Before Zoom Items/New Documents/New Documents Placeholder" +#define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_SEPARATOR "/popups/background/Before Zoom Items/New Documents/After New Documents" #define FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER "/popups/selection/Open Placeholder/Open With/Applications Placeholder" #define FM_DIRECTORY_VIEW_POPUP_PATH_BEFORE_VIEWERS_SEPARATOR "/popups/selection/Open Placeholder/Open With/Before Viewers" @@ -201,6 +209,9 @@ static gboolean confirm_trash_auto_value; static char *scripts_directory_uri; static int scripts_directory_uri_length; +static char *templates_directory_uri; +static int templates_directory_uri_length; + struct FMDirectoryViewDetails { NautilusView *nautilus_view; @@ -211,9 +222,11 @@ struct FMDirectoryViewDetails BonoboUIComponent *ui; GList *scripts_directory_list; + GList *templates_directory_list; guint display_selection_idle_id; guint update_menus_timeout_id; + guint update_status_idle_id; guint display_pending_idle_id; @@ -237,6 +250,7 @@ struct FMDirectoryViewDetails gboolean menus_merged; gboolean menu_states_untrustworthy; gboolean scripts_invalid; + gboolean templates_invalid; gboolean reported_load_error; gboolean sort_directories_first; @@ -331,6 +345,8 @@ static void zoomable_zoom_to_fit_callback (BonoboZoomable static void schedule_update_menus (FMDirectoryView *view); static void schedule_update_menus_callback (gpointer callback_data); static void remove_update_menus_timeout_callback (FMDirectoryView *view); +static void schedule_update_status (FMDirectoryView *view); +static void remove_update_status_idle_callback (FMDirectoryView *view); static void schedule_idle_display_of_pending_files (FMDirectoryView *view); static void unschedule_idle_display_of_pending_files (FMDirectoryView *view); static void unschedule_display_of_pending_files (FMDirectoryView *view); @@ -362,6 +378,7 @@ EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, clear) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, file_changed) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_background_widget) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection) +EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_item_count) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, is_empty) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, reset_to_defaults) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, restore_default_zoom_level) @@ -386,6 +403,12 @@ typedef struct { FMDirectoryView *directory_view; } ScriptLaunchParameters; +typedef struct { + NautilusFile *file; + FMDirectoryView *directory_view; +} CreateTemplateParameters; + + static ApplicationLaunchParameters * application_launch_parameters_new (GnomeVFSMimeApplication *application, NautilusFile *file, @@ -460,6 +483,30 @@ script_launch_parameters_free (ScriptLaunchParameters *parameters) g_free (parameters); } +static CreateTemplateParameters * +create_template_parameters_new (NautilusFile *file, + FMDirectoryView *directory_view) +{ + CreateTemplateParameters *result; + + result = g_new0 (CreateTemplateParameters, 1); + g_object_ref (directory_view); + result->directory_view = directory_view; + nautilus_file_ref (file); + result->file = file; + + return result; +} + +static void +create_templates_parameters_free (CreateTemplateParameters *parameters) +{ + g_object_unref (parameters->directory_view); + nautilus_file_unref (parameters->file); + g_free (parameters); +} + + /* Returns the GtkWindow that this directory view occupies, or NULL * if at the moment this directory view is not in a GtkWindow or the * GtkWindow cannot be determined. Primarily used for parenting dialogs. @@ -485,19 +532,20 @@ fm_directory_view_confirm_multiple_windows (FMDirectoryView *view, int count) GtkDialog *dialog; char *prompt; char *title; + char *detail; int response; if (count <= SILENT_WINDOW_OPEN_LIMIT) { return TRUE; } - prompt = g_strdup_printf (_("This will open %d separate windows. " - "Are you sure you want to do this?"), count); title = g_strdup_printf (_("Open %d Windows?"), count); - dialog = eel_show_yes_no_dialog (prompt, title, + prompt = _("Are you sure you want to open all files?"); + detail = g_strdup_printf (_("This will open %d separate windows."), count); + dialog = eel_show_yes_no_dialog (prompt, detail, title, GTK_STOCK_OK, GTK_STOCK_CANCEL, fm_directory_view_get_containing_window (view)); - g_free (prompt); + g_free (detail); g_free (title); response = gtk_dialog_run (dialog); @@ -552,10 +600,28 @@ open_callback (BonoboUIComponent *component, gpointer callback_data, const char view = FM_DIRECTORY_VIEW (callback_data); selection = fm_directory_view_get_selection (view); - fm_directory_view_activate_files (view, selection, Nautilus_ViewFrame_OPEN_ACCORDING_TO_MODE, 0); + fm_directory_view_activate_files (view, selection, + Nautilus_ViewFrame_OPEN_ACCORDING_TO_MODE, + 0); + nautilus_file_list_free (selection); +} + +static void +open_close_parent_callback (BonoboUIComponent *component, gpointer callback_data, const char *verb) +{ + GList *selection; + FMDirectoryView *view; + + view = FM_DIRECTORY_VIEW (callback_data); + + selection = fm_directory_view_get_selection (view); + fm_directory_view_activate_files (view, selection, + Nautilus_ViewFrame_OPEN_ACCORDING_TO_MODE, + Nautilus_ViewFrame_OPEN_FLAG_CLOSE_BEHIND); nautilus_file_list_free (selection); } + static void open_alternate_callback (BonoboUIComponent *component, gpointer callback_data, const char *verb) { @@ -840,6 +906,7 @@ confirm_delete_directly (FMDirectoryView *view, dialog = eel_show_yes_no_dialog (prompt, + _("If you delete an item, it is permanently lost."), _("Delete?"), GTK_STOCK_DELETE, GTK_STOCK_CANCEL, fm_directory_view_get_containing_window (view)); @@ -1049,6 +1116,14 @@ new_folder_callback (BonoboUIComponent *component, gpointer callback_data, const } static void +new_empty_file_callback (BonoboUIComponent *component, gpointer callback_data, const char *verb) +{ + g_assert (FM_IS_DIRECTORY_VIEW (callback_data)); + + fm_directory_view_new_file (FM_DIRECTORY_VIEW (callback_data), NULL); +} + +static void new_launcher_callback (BonoboUIComponent *component, gpointer callback_data, const char *verb) { char *parent_uri; @@ -1080,7 +1155,7 @@ open_properties_window_callback (BonoboUIComponent *component, gpointer callback view = FM_DIRECTORY_VIEW (callback_data); selection = fm_directory_view_get_selection (view); - fm_properties_window_present (selection, view); + fm_properties_window_present (selection, GTK_WIDGET (view)); nautilus_file_list_free (selection); } @@ -1262,6 +1337,17 @@ set_up_scripts_directory_global (void) } static void +set_up_templates_directory_global (void) +{ + if (templates_directory_uri != NULL) { + return; + } + + templates_directory_uri = nautilus_get_templates_directory_uri (); + templates_directory_uri_length = strlen (templates_directory_uri); +} + +static void create_scripts_directory (void) { char *gnome1_path, *gnome1_uri_str; @@ -1307,6 +1393,19 @@ scripts_added_or_changed_callback (NautilusDirectory *directory, } static void +templates_added_or_changed_callback (NautilusDirectory *directory, + GList *files, + gpointer callback_data) +{ + FMDirectoryView *view; + + view = FM_DIRECTORY_VIEW (callback_data); + + view->details->templates_invalid = TRUE; + schedule_update_menus (view); +} + +static void icons_changed_callback (gpointer callback_data) { FMDirectoryView *view; @@ -1314,57 +1413,100 @@ icons_changed_callback (gpointer callback_data) view = FM_DIRECTORY_VIEW (callback_data); view->details->scripts_invalid = TRUE; + view->details->templates_invalid = TRUE; schedule_update_menus (view); } static void -add_directory_to_scripts_directory_list (FMDirectoryView *view, - NautilusDirectory *directory) +add_directory_to_directory_list (FMDirectoryView *view, + NautilusDirectory *directory, + GList **directory_list, + GCallback changed_callback) { NautilusFileAttributes attributes; - if (g_list_find (view->details->scripts_directory_list, directory) == NULL) { + if (g_list_find (*directory_list, directory) == NULL) { nautilus_directory_ref (directory); attributes = nautilus_icon_factory_get_required_file_attributes (); attributes |= NAUTILUS_FILE_ATTRIBUTE_CAPABILITIES | NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT; - nautilus_directory_file_monitor_add (directory, &view->details->scripts_directory_list, + nautilus_directory_file_monitor_add (directory, directory_list, FALSE, FALSE, attributes, - scripts_added_or_changed_callback, view); + (NautilusDirectoryCallback)changed_callback, view); g_signal_connect_object (directory, "files_added", - G_CALLBACK (scripts_added_or_changed_callback), view, 0); + G_CALLBACK (changed_callback), view, 0); g_signal_connect_object (directory, "files_changed", - G_CALLBACK (scripts_added_or_changed_callback), view, 0); + G_CALLBACK (changed_callback), view, 0); - view->details->scripts_directory_list = g_list_append - (view->details->scripts_directory_list, directory); + *directory_list = g_list_append (*directory_list, directory); } } static void -remove_directory_from_scripts_directory_list (FMDirectoryView *view, - NautilusDirectory *directory) +remove_directory_from_directory_list (FMDirectoryView *view, + NautilusDirectory *directory, + GList **directory_list, + GCallback changed_callback) { - view->details->scripts_directory_list = g_list_remove - (view->details->scripts_directory_list, directory); + *directory_list = g_list_remove (*directory_list, directory); g_signal_handlers_disconnect_by_func (directory, - G_CALLBACK (scripts_added_or_changed_callback), + G_CALLBACK (changed_callback), view); - nautilus_directory_file_monitor_remove (directory, &view->details->scripts_directory_list); + nautilus_directory_file_monitor_remove (directory, directory_list); nautilus_directory_unref (directory); } + +static void +add_directory_to_scripts_directory_list (FMDirectoryView *view, + NautilusDirectory *directory) +{ + add_directory_to_directory_list (view, directory, + &view->details->scripts_directory_list, + G_CALLBACK (scripts_added_or_changed_callback)); +} + +static void +remove_directory_from_scripts_directory_list (FMDirectoryView *view, + NautilusDirectory *directory) +{ + remove_directory_from_directory_list (view, directory, + &view->details->scripts_directory_list, + G_CALLBACK (scripts_added_or_changed_callback)); +} + +static void +add_directory_to_templates_directory_list (FMDirectoryView *view, + NautilusDirectory *directory) +{ + add_directory_to_directory_list (view, directory, + &view->details->templates_directory_list, + G_CALLBACK (templates_added_or_changed_callback)); +} + +static void +remove_directory_from_templates_directory_list (FMDirectoryView *view, + NautilusDirectory *directory) +{ + remove_directory_from_directory_list (view, directory, + &view->details->templates_directory_list, + G_CALLBACK (templates_added_or_changed_callback)); +} + + + static void fm_directory_view_init (FMDirectoryView *view) { static gboolean setup_autos = FALSE; NautilusDirectory *scripts_directory; + NautilusDirectory *templates_directory; if (!setup_autos) { setup_autos = TRUE; @@ -1387,11 +1529,15 @@ fm_directory_view_init (FMDirectoryView *view) view->details->nautilus_view = nautilus_view_new (GTK_WIDGET (view)); set_up_scripts_directory_global (); - scripts_directory = nautilus_directory_get (scripts_directory_uri); add_directory_to_scripts_directory_list (view, scripts_directory); nautilus_directory_unref (scripts_directory); + set_up_templates_directory_global (); + templates_directory = nautilus_directory_get (templates_directory_uri); + add_directory_to_templates_directory_list (view, templates_directory); + nautilus_directory_unref (templates_directory); + view->details->zoomable = bonobo_zoomable_new (); bonobo_zoomable_set_parameters_full (view->details->zoomable, 0.0, .25, 4.0, TRUE, TRUE, FALSE, @@ -1489,6 +1635,12 @@ fm_directory_view_finalize (GObject *object) remove_directory_from_scripts_directory_list (view, node->data); } + for (node = view->details->templates_directory_list; node != NULL; node = next) { + next = node->next; + remove_directory_from_templates_directory_list (view, node->data); + } + + nautilus_directory_unref (view->details->model); view->details->model = NULL; nautilus_file_unref (view->details->directory_as_file); @@ -1498,6 +1650,7 @@ fm_directory_view_finalize (GObject *object) } remove_update_menus_timeout_callback (view); + remove_update_status_idle_callback (view); fm_directory_view_ignore_hidden_file_preferences (view); @@ -1541,7 +1694,7 @@ fm_directory_view_display_selection_info (FMDirectoryView *view) GnomeVFSFileSize non_folder_size; guint non_folder_count, folder_count, folder_item_count; gboolean folder_item_count_known; - guint item_count; + guint file_item_count; GList *p; char *first_item_name; char *non_folder_str; @@ -1568,8 +1721,8 @@ fm_directory_view_display_selection_info (FMDirectoryView *view) file = p->data; if (nautilus_file_is_directory (file)) { folder_count++; - if (nautilus_file_get_directory_item_count (file, &item_count, NULL)) { - folder_item_count += item_count; + if (nautilus_file_get_directory_item_count (file, &file_item_count, NULL)) { + folder_item_count += file_item_count; } else { folder_item_count_known = FALSE; } @@ -1656,7 +1809,23 @@ fm_directory_view_display_selection_info (FMDirectoryView *view) } if (folder_count == 0 && non_folder_count == 0) { - status_string = g_strdup (""); + char *free_space_str; + char *item_count_str; + guint item_count; + + item_count = fm_directory_view_get_item_count (view); + + item_count_str = g_strdup_printf (ngettext ("%u item", "%u items", item_count), item_count); + + free_space_str = nautilus_file_get_volume_free_space (view->details->directory_as_file); + if (free_space_str != NULL) { + status_string = g_strdup_printf (_("%s, Free space: %s"), item_count_str, free_space_str); + g_free (free_space_str); + g_free (item_count_str); + } else { + status_string = item_count_str; + } + } else if (folder_count == 0) { status_string = g_strdup (non_folder_str); } else if (non_folder_count == 0) { @@ -1801,14 +1970,14 @@ real_file_limit_reached (FMDirectoryView *view) * no more than the constant limit are displayed. */ message = g_strdup_printf (_("The folder \"%s\" contains more files than " - "Nautilus can handle. Some files will not be " - "displayed."), + "Nautilus can handle."), directory_name); g_free (directory_name); dialog = eel_show_warning_dialog (message, - _("Too Many Files"), - fm_directory_view_get_containing_window (view)); + _("Some files will not be displayed."), + _("Too Many Files"), + fm_directory_view_get_containing_window (view)); g_free (message); } @@ -1836,6 +2005,7 @@ done_loading (FMDirectoryView *view) if (view->details->nautilus_view != NULL) { nautilus_view_report_load_complete (view->details->nautilus_view); schedule_update_menus (view); + schedule_update_status (view); check_for_directory_hard_limit (view); uris_selected = view->details->pending_uris_selected; @@ -2455,6 +2625,9 @@ files_added_callback (NautilusDirectory *directory, view = FM_DIRECTORY_VIEW (callback_data); queue_pending_files (view, files, &view->details->new_added_files); + + /* The number of items could have changed */ + schedule_update_status (view); } static void @@ -2467,6 +2640,9 @@ files_changed_callback (NautilusDirectory *directory, view = FM_DIRECTORY_VIEW (callback_data); queue_pending_files (view, files, &view->details->new_changed_files); + /* The free space or the number of items could have changed */ + schedule_update_status (view); + /* A change in MIME type could affect the Open with menu, for * one thing, so we need to update menus when files change. */ @@ -2777,6 +2953,16 @@ fm_directory_view_get_selection (FMDirectoryView *view) get_selection, (view)); } +guint +fm_directory_view_get_item_count (FMDirectoryView *view) +{ + g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), 0); + + return EEL_CALL_METHOD_WITH_RETURN_VALUE + (FM_DIRECTORY_VIEW_CLASS, view, + get_item_count, (view)); +} + /** * fm_directory_view_get_bonobo_ui_container: * @@ -3040,7 +3226,9 @@ fm_directory_view_confirm_deletion (FMDirectoryView *view, GList *uris, gboolean { GtkDialog *dialog; char *prompt; + char *detail; int uri_count; + char *uri; char *file_name; int response; @@ -3050,29 +3238,31 @@ fm_directory_view_confirm_deletion (FMDirectoryView *view, GList *uris, gboolean g_assert (uri_count > 0); if (uri_count == 1) { - file_name = file_name_from_uri ((char *) uris->data); - - prompt = g_strdup_printf (_("\"%s\" cannot be moved to the Trash. Do " - "you want to delete it immediately?"), file_name); + uri = (char *) uris->data; + if (eel_uri_is_desktop (uri)) { + /* Don't ask for desktop icons */ + return TRUE; + } + file_name = file_name_from_uri (uri); + prompt = _("Cannot move file to trash, do you want to delete immediately?"); + detail = g_strdup_printf (_("The file \"%s\" cannot be moved to the trash."), file_name); g_free (file_name); } else { if (all) { - prompt = g_strdup_printf (_("The %d selected items cannot be moved " - "to the Trash. Do you want to delete them " - "immediately?"), uri_count); + prompt = _("Cannot move items to trash, do you want to delete them immediately?"); + detail = g_strdup_printf ("None of the %d selected items can be moved to the Trash", uri_count); } else { - prompt = g_strdup_printf (_("%d of the selected items cannot be moved " - "to the Trash. Do you want to delete those " - "%d items immediately?"), uri_count, uri_count); + prompt = _("Cannot move some items to trash, do you want to delete these immediately?"); + detail = g_strdup_printf ("%d of the selected items cannot be moved to the Trash", uri_count); } } dialog = eel_show_yes_no_dialog (prompt, - _("Delete Immediately?"), GTK_STOCK_DELETE, GTK_STOCK_CANCEL, + detail, _("Delete Immediately?"), GTK_STOCK_DELETE, GTK_STOCK_CANCEL, fm_directory_view_get_containing_window (view)); - g_free (prompt); + g_free (detail); response = gtk_dialog_run (dialog); gtk_object_destroy (GTK_OBJECT (dialog)); @@ -3102,15 +3292,15 @@ confirm_delete_from_trash (FMDirectoryView *view, GList *uris) if (uri_count == 1) { file_name = file_name_from_uri ((char *) uris->data); prompt = g_strdup_printf (_("Are you sure you want to permanently delete \"%s\" " - "from the Trash?"), file_name); + "from the trash?"), file_name); g_free (file_name); } else { prompt = g_strdup_printf (_("Are you sure you want to permanently delete " - "the %d selected items from the Trash?"), uri_count); + "the %d selected items from the trash?"), uri_count); } dialog = eel_show_yes_no_dialog ( - prompt, + prompt, _("If you delete an item, it will be permanently lost."), _("Delete From Trash?"), GTK_STOCK_DELETE, GTK_STOCK_CANCEL, fm_directory_view_get_containing_window (view)); @@ -3219,29 +3409,58 @@ start_renaming_file (FMDirectoryView *view, NautilusFile *file) } static void +rename_file (FMDirectoryView *view, NautilusFile *new_file) +{ + /* no need to select because start_renaming_file selects + * fm_directory_view_select_file (view, new_file); + */ + EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, new_file)); + fm_directory_view_reveal_selection (view); +} + +static void reveal_newly_added_folder (FMDirectoryView *view, NautilusFile *new_file, const char *target_uri) { if (nautilus_file_matches_uri (new_file, target_uri)) { g_signal_handlers_disconnect_by_func (view, G_CALLBACK (reveal_newly_added_folder), (void *) target_uri); - /* no need to select because start_renaming_file selects - * fm_directory_view_select_file (view, new_file); - */ - EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, new_file)); - fm_directory_view_reveal_selection (view); + rename_file (view, new_file); } } +typedef struct { + FMDirectoryView *directory_view; + GHashTable *added_uris; +} NewFolderData; + + +static void +track_newly_added_uris (FMDirectoryView *view, NautilusFile *new_file, gpointer user_data) +{ + NewFolderData *data; + + data = user_data; + + g_hash_table_insert (data->added_uris, nautilus_file_get_uri (new_file), NULL); +} + static void -new_folder_done (const char *new_folder_uri, gpointer data) +new_folder_done (const char *new_folder_uri, gpointer user_data) { FMDirectoryView *directory_view; NautilusFile *file; char *screen_string; GdkScreen *screen; + NewFolderData *data; - directory_view = (FMDirectoryView *) data; + data = (NewFolderData *)user_data; + + if (new_folder_uri == NULL) { + goto fail; + } + + directory_view = data->directory_view; g_assert (FM_IS_DIRECTORY_VIEW (directory_view)); screen = gtk_widget_get_screen (GTK_WIDGET (directory_view)); @@ -3254,31 +3473,92 @@ new_folder_done (const char *new_folder_uri, gpointer data) screen_string); g_free (screen_string); - /* We need to run after the default handler adds the folder we want to - * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we - * must use connect_after. - */ - g_signal_connect_data (directory_view, - "add_file", - G_CALLBACK (reveal_newly_added_folder), - g_strdup (new_folder_uri), - (GClosureNotify)g_free, - G_CONNECT_AFTER); + g_signal_handlers_disconnect_by_func (directory_view, + G_CALLBACK (track_newly_added_uris), + (void *) data); + + if (g_hash_table_lookup_extended (data->added_uris, new_folder_uri, NULL, NULL)) { + /* The file was already added */ + rename_file (directory_view, file); + } else { + /* We need to run after the default handler adds the folder we want to + * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we + * must use connect_after. + */ + g_signal_connect_data (directory_view, + "add_file", + G_CALLBACK (reveal_newly_added_folder), + g_strdup (new_folder_uri), + (GClosureNotify)g_free, + G_CONNECT_AFTER); + } + + fail: + g_hash_table_destroy (data->added_uris); + g_free (data); } void fm_directory_view_new_folder (FMDirectoryView *directory_view) { char *parent_uri; + NewFolderData *data; + + data = g_new (NewFolderData, 1); + data->directory_view = directory_view; + data->added_uris = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + g_signal_connect_data (directory_view, + "add_file", + G_CALLBACK (track_newly_added_uris), + data, + (GClosureNotify)NULL, + G_CONNECT_AFTER); parent_uri = fm_directory_view_get_backing_uri (directory_view); nautilus_file_operations_new_folder (GTK_WIDGET (directory_view), parent_uri, - new_folder_done, directory_view); + new_folder_done, data); g_free (parent_uri); } +void +fm_directory_view_new_file (FMDirectoryView *directory_view, + NautilusFile *source) +{ + char *parent_uri; + char *source_uri; + NewFolderData *data; + + data = g_new (NewFolderData, 1); + data->directory_view = directory_view; + data->added_uris = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); + + g_signal_connect_data (directory_view, + "add_file", + G_CALLBACK (track_newly_added_uris), + data, + (GClosureNotify)NULL, + G_CONNECT_AFTER); + + + source_uri = NULL; + if (source != NULL) { + source_uri = nautilus_file_get_uri (source); + } + parent_uri = fm_directory_view_get_backing_uri (directory_view); + nautilus_file_operations_new_file (GTK_WIDGET (directory_view), + parent_uri, + source_uri, + new_folder_done, data); + g_free (parent_uri); + g_free (source_uri); +} + + /* handle the open command */ static void @@ -3337,6 +3617,10 @@ add_numbered_menu_item (BonoboUIComponent *ui, GDestroyNotify destroy_notify) { char *escaped_parent_path, *escaped_label, *verb_name, *item_path; + + if (parent_path == NULL) { + return; + } escaped_parent_path = eel_str_double_underscores (parent_path); @@ -3372,11 +3656,13 @@ add_submenu (BonoboUIComponent *ui, { char *escaped_parent_path, *escaped_label; - escaped_parent_path = eel_str_double_underscores (parent_path); - escaped_label = eel_str_double_underscores (label); - nautilus_bonobo_add_submenu (ui, escaped_parent_path, escaped_label, pixbuf); - g_free (escaped_label); - g_free (escaped_parent_path); + if (parent_path != NULL) { + escaped_parent_path = eel_str_double_underscores (parent_path); + escaped_label = eel_str_double_underscores (label); + nautilus_bonobo_add_submenu (ui, escaped_parent_path, escaped_label, pixbuf); + g_free (escaped_label); + g_free (escaped_parent_path); + } } static void @@ -3878,12 +4164,12 @@ run_script_callback (BonoboUIComponent *component, gpointer callback_data, const } static void -add_script_to_script_menus (FMDirectoryView *directory_view, - NautilusFile *file, - int index, - const char *menu_path, - const char *popup_path, - const char *popup_bg_path) +add_script_to_scripts_menus (FMDirectoryView *directory_view, + NautilusFile *file, + int index, + const char *menu_path, + const char *popup_path, + const char *popup_bg_path) { ScriptLaunchParameters *launch_parameters; char *tip; @@ -3935,11 +4221,11 @@ add_script_to_script_menus (FMDirectoryView *directory_view, } static void -add_submenu_to_script_menus (FMDirectoryView *directory_view, - NautilusFile *file, - const char *menu_path, - const char *popup_path, - const char *popup_bg_path) +add_submenu_to_directory_menus (FMDirectoryView *directory_view, + NautilusFile *file, + const char *menu_path, + const char *popup_path, + const char *popup_bg_path) { char *name; GdkPixbuf *pixbuf; @@ -3987,19 +4273,22 @@ update_directory_in_scripts_menu (FMDirectoryView *view, NautilusDirectory *dire NautilusFile *file; NautilusDirectory *dir; char *uri; + char *escaped_path; int i; uri = nautilus_directory_get_uri (directory); + escaped_path = gnome_vfs_escape_path_string (uri + scripts_directory_uri_length); + g_free (uri); menu_path = g_strconcat (FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER, - uri + scripts_directory_uri_length, + escaped_path, NULL); popup_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER, - uri + scripts_directory_uri_length, + escaped_path, NULL); popup_bg_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER, - uri + scripts_directory_uri_length, + escaped_path, NULL); - g_free (uri); + g_free (escaped_path); file_list = nautilus_directory_get_file_list (directory); filtered = nautilus_file_list_filter_hidden_and_backup (file_list, FALSE, FALSE); @@ -4013,7 +4302,7 @@ update_directory_in_scripts_menu (FMDirectoryView *view, NautilusDirectory *dire file = node->data; if (file_is_launchable (file)) { - add_script_to_script_menus (view, file, i++, menu_path, popup_path, popup_bg_path); + add_script_to_scripts_menus (view, file, i++, menu_path, popup_path, popup_bg_path); any_scripts = TRUE; } else if (nautilus_file_is_directory (file)) { uri = nautilus_file_get_uri (file); @@ -4022,7 +4311,7 @@ update_directory_in_scripts_menu (FMDirectoryView *view, NautilusDirectory *dire add_directory_to_scripts_directory_list (view, dir); nautilus_directory_unref (dir); - add_submenu_to_script_menus (view, file, menu_path, popup_path, popup_bg_path); + add_submenu_to_directory_menus (view, file, menu_path, popup_path, popup_bg_path); any_scripts = TRUE; } @@ -4097,6 +4386,204 @@ update_scripts_menu (FMDirectoryView *view) } static void +create_template_callback (BonoboUIComponent *component, gpointer callback_data, const char *path) +{ + CreateTemplateParameters *parameters; + + parameters = callback_data; + + fm_directory_view_new_file (parameters->directory_view, parameters->file); +} + + +static void +add_template_to_templates_menus (FMDirectoryView *directory_view, + NautilusFile *file, + int index, + const char *menu_path, + const char *popup_bg_path) +{ + char *tip; + char *name; + char *dot; + GdkPixbuf *pixbuf; + CreateTemplateParameters *parameters; + + name = nautilus_file_get_display_name (file); + + tip = g_strdup_printf (_("Create Document from template \"%s\""), name); + + /* Remove extension */ + dot = strrchr (name, '.'); + if (dot != NULL) { + *dot = 0; + } + + pixbuf = nautilus_icon_factory_get_pixbuf_for_file + (file, NULL, NAUTILUS_ICON_SIZE_FOR_MENUS); + + parameters = create_template_parameters_new (file, directory_view); + add_numbered_menu_item (directory_view->details->ui, + menu_path, + name, + tip, + index, + pixbuf, + create_template_callback, + parameters, + (GDestroyNotify) create_templates_parameters_free); + + /* Use same uri and no DestroyNotify for popup item, which has same + * lifetime as the item in the File menu in the menu bar. + */ + add_numbered_menu_item (directory_view->details->ui, + popup_bg_path, + name, + tip, + index, + pixbuf, + create_template_callback, + parameters, + NULL); + + g_object_unref (pixbuf); + g_free (name); + g_free (tip); +} + + +static gboolean +directory_belongs_in_templates_menu (const char *uri) +{ + int num_levels; + int i; + + if (!eel_str_has_prefix (uri, templates_directory_uri)) { + return FALSE; + } + + num_levels = 0; + for (i = templates_directory_uri_length; uri[i] != '\0'; i++) { + if (uri[i] == '/') { + num_levels++; + } + } + + if (num_levels > MAX_MENU_LEVELS) { + return FALSE; + } + + return TRUE; +} + +static gboolean +update_directory_in_templates_menu (FMDirectoryView *view, NautilusDirectory *directory) +{ + char *menu_path, *popup_bg_path; + GList *file_list, *filtered, *node; + gboolean any_templates; + NautilusFile *file; + NautilusDirectory *dir; + char *escaped_path; + char *uri; + int i; + + uri = nautilus_directory_get_uri (directory); + escaped_path = gnome_vfs_escape_path_string (uri + templates_directory_uri_length); + g_free (uri); + menu_path = g_strconcat (FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER, + escaped_path, + NULL); + popup_bg_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER, + escaped_path, + NULL);; + + g_free (escaped_path); + + file_list = nautilus_directory_get_file_list (directory); + filtered = nautilus_file_list_filter_hidden_and_backup (file_list, FALSE, FALSE); + nautilus_file_list_free (file_list); + + file_list = nautilus_file_list_sort_by_display_name (filtered); + + any_templates = FALSE; + i = 0; + for (node = file_list; node != NULL; node = node->next) { + file = node->data; + + if (nautilus_file_is_directory (file)) { + uri = nautilus_file_get_uri (file); + if (directory_belongs_in_templates_menu (uri)) { + dir = nautilus_directory_get (uri); + add_directory_to_templates_directory_list (view, dir); + nautilus_directory_unref (dir); + + add_submenu_to_directory_menus (view, file, menu_path, NULL, popup_bg_path); + + any_templates = TRUE; + } + g_free (uri); + } else if (nautilus_file_can_read (file)) { + add_template_to_templates_menus (view, file, i++, menu_path, popup_bg_path); + any_templates = TRUE; + } + } + + nautilus_file_list_free (file_list); + + g_free (popup_bg_path); + g_free (menu_path); + + return any_templates; +} + + + +static void +update_templates_menu (FMDirectoryView *view) +{ + gboolean any_templates; + GList *sorted_copy, *node; + NautilusDirectory *directory; + char *uri; + + /* There is a race condition here. If we don't mark the scripts menu as + valid before we begin our task then we can lose template menu updates that + occur before we finish. */ + view->details->templates_invalid = FALSE; + + nautilus_bonobo_remove_menu_items_and_commands + (view->details->ui, FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER); + nautilus_bonobo_remove_menu_items_and_commands + (view->details->ui, FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER); + + /* As we walk through the directories, remove any that no longer belong. */ + any_templates = FALSE; + sorted_copy = nautilus_directory_list_sort_by_uri + (nautilus_directory_list_copy (view->details->templates_directory_list)); + for (node = sorted_copy; node != NULL; node = node->next) { + directory = node->data; + + uri = nautilus_directory_get_uri (directory); + if (!directory_belongs_in_templates_menu (uri)) { + remove_directory_from_templates_directory_list (view, directory); + } else if (update_directory_in_templates_menu (view, directory)) { + any_templates = TRUE; + } + g_free (uri); + } + nautilus_directory_list_free (sorted_copy); + + nautilus_bonobo_set_hidden (view->details->ui, + FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_NO_TEMPLATES, + any_templates); + nautilus_bonobo_set_hidden (view->details->ui, + FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_NO_TEMPLATES, + any_templates); +} + + +static void open_scripts_folder_callback (BonoboUIComponent *component, gpointer callback_data, const char *verb) @@ -4109,7 +4596,8 @@ open_scripts_folder_callback (BonoboUIComponent *component, eel_show_info_dialog_with_details (_("All executable files in this folder will appear in the " - "Scripts menu. Choosing a script from the menu will run " + "Scripts menu."), + _("Choosing a script from the menu will run " "that script with any selected items as input."), _("About Scripts"), _("All executable files in this folder will appear in the " @@ -4437,7 +4925,8 @@ drive_mounted_callback (gboolean succeeded, gpointer data) { if (!succeeded) { - eel_show_error_dialog_with_details (error, _("Mount Error"), detailed_error, NULL); + eel_show_error_dialog_with_details (error, NULL, + _("Mount Error"), detailed_error, NULL); } } @@ -4498,9 +4987,11 @@ volume_or_drive_unmounted_callback (gboolean succeeded, eject = GPOINTER_TO_INT (data); if (!succeeded) { if (eject) { - eel_show_error_dialog_with_details (error, _("Unmount Error"), detailed_error, NULL); + eel_show_error_dialog_with_details (error, NULL, + _("Unmount Error"), detailed_error, NULL); } else { - eel_show_error_dialog_with_details (error, _("Eject Error"), detailed_error, NULL); + eel_show_error_dialog_with_details (error, NULL, + _("Eject Error"), detailed_error, NULL); } } } @@ -4564,9 +5055,11 @@ real_merge_menus (FMDirectoryView *view) BONOBO_UI_VERB ("Duplicate", duplicate_callback), BONOBO_UI_VERB ("Empty Trash", bonobo_menu_empty_trash_callback), BONOBO_UI_VERB ("New Folder", new_folder_callback), + BONOBO_UI_VERB ("New Empty File", new_empty_file_callback), BONOBO_UI_VERB ("New Launcher", new_launcher_callback), BONOBO_UI_VERB ("Open Scripts Folder", open_scripts_folder_callback), BONOBO_UI_VERB ("Open", open_callback), + BONOBO_UI_VERB ("OpenCloseParent", open_close_parent_callback), BONOBO_UI_VERB ("OpenAlternate", open_alternate_callback), BONOBO_UI_VERB ("OtherApplication", other_application_callback), BONOBO_UI_VERB ("OtherViewer", other_viewer_callback), @@ -4603,6 +5096,7 @@ real_merge_menus (FMDirectoryView *view) } view->details->scripts_invalid = TRUE; + view->details->templates_invalid = TRUE; } static void @@ -4977,6 +5471,9 @@ real_update_menus (FMDirectoryView *view) if (view->details->scripts_invalid) { update_scripts_menu (view); } + if (view->details->templates_invalid) { + update_templates_menu (view); + } } /** @@ -5051,6 +5548,47 @@ schedule_update_menus (FMDirectoryView *view) } } +static void +remove_update_status_idle_callback (FMDirectoryView *view) +{ + if (view->details->update_status_idle_id != 0) { + g_source_remove (view->details->update_status_idle_id); + view->details->update_status_idle_id = 0; + } +} + +static gboolean +update_status_idle_callback (gpointer data) +{ + FMDirectoryView *view; + + view = FM_DIRECTORY_VIEW (data); + fm_directory_view_display_selection_info (view); + view->details->update_status_idle_id = 0; + return FALSE; +} + +static void +schedule_update_status (FMDirectoryView *view) +{ + g_assert (FM_IS_DIRECTORY_VIEW (view)); + + /* Make sure we haven't already destroyed it */ + g_assert (view->details->nautilus_view != NULL); + + if (view->details->loading) { + /* Don't update status bar while loading the dir */ + return; + } + + if (view->details->update_status_idle_id == 0) { + view->details->update_status_idle_id = + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE - 20, + update_status_idle_callback, view, NULL); + } +} + + /** * fm_directory_view_notify_selection_changed: * @@ -5110,6 +5648,7 @@ report_broken_symbolic_link (FMDirectoryView *view, NautilusFile *file) { char *target_path; char *prompt; + char *detail; GtkDialog *dialog; GList file_as_list; int response; @@ -5117,17 +5656,16 @@ report_broken_symbolic_link (FMDirectoryView *view, NautilusFile *file) g_assert (nautilus_file_is_broken_symbolic_link (file)); target_path = nautilus_file_get_symbolic_link_target_path (file); + prompt = _("The link is borken, do you want to move it to the Trash?"); if (target_path == NULL) { - prompt = g_strdup_printf (_("This link can't be used, because it has no target. " - "Do you want to move this link to the Trash?")); + detail = g_strdup (_("This link can't be used, because it has no target.")); } else { - prompt = g_strdup_printf (_("This link can't be used, because its target \"%s\" doesn't exist. " - "Do you want to move this link to the Trash?"), - target_path); + detail = g_strdup_printf (_("This link can't be used, because its target " + "\"%s\" doesn't exist."), target_path); } dialog = eel_show_yes_no_dialog (prompt, - _("Broken Link"), _("Mo_ve to Trash"), GTK_STOCK_CANCEL, + detail, _("Broken Link"), _("Mo_ve to Trash"), GTK_STOCK_CANCEL, fm_directory_view_get_containing_window (view)); gtk_dialog_set_default_response (dialog, GTK_RESPONSE_YES); @@ -5152,7 +5690,7 @@ report_broken_symbolic_link (FMDirectoryView *view, NautilusFile *file) } g_free (target_path); - g_free (prompt); + g_free (detail); } static ActivationAction @@ -5161,6 +5699,7 @@ get_executable_text_file_action (FMDirectoryView *view, NautilusFile *file) GtkDialog *dialog; char *file_name; char *prompt; + char *detail; int preferences_value; int response; @@ -5184,21 +5723,25 @@ get_executable_text_file_action (FMDirectoryView *view, NautilusFile *file) file_name = nautilus_file_get_display_name (file); - prompt = g_strdup_printf (_("\"%s\" is an executable text file. " - "Do you want to run it, or display its contents?"), - file_name); + prompt = g_strdup_printf (_("Do you want to run \"%s\", or display its contents?"), + file_name); + detail = g_strdup_printf (_("\"%s\" is an executable text file."), + file_name); g_free (file_name); dialog = eel_create_question_dialog (prompt, + detail, _("Run or Display?"), - _("_Display"), RESPONSE_DISPLAY, _("Run in _Terminal"), RESPONSE_RUN_IN_TERMINAL, + _("_Display"), RESPONSE_DISPLAY, fm_directory_view_get_containing_window (view)); gtk_dialog_add_button (dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); gtk_dialog_add_button (dialog, _("_Run"), RESPONSE_RUN); + gtk_dialog_set_default_response (dialog, GTK_RESPONSE_CANCEL); gtk_widget_show (GTK_WIDGET (dialog)); g_free (prompt); + g_free (detail); response = gtk_dialog_run (dialog); gtk_object_destroy (GTK_OBJECT (dialog)); @@ -5292,9 +5835,13 @@ activate_callback (NautilusFile *file, gpointer callback_data) nautilus_launch_show_file (file, fm_directory_view_get_containing_window (view)); - file_uri = nautilus_file_get_uri (file); - egg_recent_model_add (nautilus_recent_get_model (), file_uri); - g_free (file_uri); + /* We should not add trash and directory uris.*/ + if ((!nautilus_file_is_in_trash (file)) && + (!nautilus_file_is_directory (file))) { + file_uri = nautilus_file_get_uri (file); + egg_recent_model_add (nautilus_recent_get_model (), file_uri); + g_free (file_uri); + } } } @@ -5323,7 +5870,10 @@ activation_drive_mounted_callback (gboolean succeeded, } else { if (!parameters->cancelled) { eel_timed_wait_stop (cancel_activate_callback, parameters); - eel_show_error_dialog_with_details (error, _("Mount Error"), detailed_error, NULL); + eel_show_error_dialog_with_details (error, NULL, + _("Mount Error"), + detailed_error, + NULL); } nautilus_file_unref (parameters->file); @@ -5454,7 +6004,7 @@ fm_directory_view_activate_file (FMDirectoryView *view, parameters->cancelled = FALSE; file_name = nautilus_file_get_display_name (file); - timed_wait_prompt = g_strdup_printf (_("Opening \"%s\""), file_name); + timed_wait_prompt = g_strdup_printf (_("Opening \"%s\"."), file_name); g_free (file_name); eel_timed_wait_start @@ -5523,6 +6073,7 @@ file_changed_callback (NautilusFile *file, gpointer callback_data) FMDirectoryView *view = FM_DIRECTORY_VIEW (callback_data); schedule_update_menus (view); + schedule_update_status (view); /* We might have different capabilities, so we need to update relative icon emblems . (Writeable etc) */ @@ -6429,6 +6980,7 @@ fm_directory_view_class_init (FMDirectoryViewClass *klass) EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, file_changed); EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_background_widget); EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_selection); + EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_item_count); EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, is_empty); EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, reset_to_defaults); EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, restore_default_zoom_level); diff --git a/src/file-manager/fm-directory-view.h b/src/file-manager/fm-directory-view.h index 2fa6afaef..6f138e9bb 100644 --- a/src/file-manager/fm-directory-view.h +++ b/src/file-manager/fm-directory-view.h @@ -152,6 +152,8 @@ struct FMDirectoryViewClass { /* Return an array of locations of selected icons in their view. */ GArray * (* get_selected_icon_locations) (FMDirectoryView *view); + guint (* get_item_count) (FMDirectoryView *view); + /* bump_zoom_level is a function pointer that subclasses must override * to change the zoom level of an object. */ void (* bump_zoom_level) (FMDirectoryView *view, @@ -299,6 +301,7 @@ gboolean fm_directory_view_can_accept_item (Nautilus void fm_directory_view_display_selection_info (FMDirectoryView *view); GList * fm_directory_view_get_selection (FMDirectoryView *view); void fm_directory_view_stop (FMDirectoryView *view); +guint fm_directory_view_get_item_count (FMDirectoryView *view); gboolean fm_directory_view_can_zoom_in (FMDirectoryView *view); gboolean fm_directory_view_can_zoom_out (FMDirectoryView *view); GtkWidget * fm_directory_view_get_background_widget (FMDirectoryView *view); @@ -370,6 +373,8 @@ gboolean fm_directory_view_should_show_file (FMDirect gboolean fm_directory_view_should_sort_directories_first (FMDirectoryView *view); void fm_directory_view_update_menus (FMDirectoryView *view); void fm_directory_view_new_folder (FMDirectoryView *view); +void fm_directory_view_new_file (FMDirectoryView *view, + NautilusFile *source); void fm_directory_view_ignore_hidden_file_preferences (FMDirectoryView *view); #endif /* FM_DIRECTORY_VIEW_H */ diff --git a/src/file-manager/fm-error-reporting.c b/src/file-manager/fm-error-reporting.c index 48167add6..09d22c33e 100644 --- a/src/file-manager/fm-error-reporting.c +++ b/src/file-manager/fm-error-reporting.c @@ -68,7 +68,8 @@ fm_report_error_loading_directory (NautilusFile *file, message = g_strdup_printf (_("Sorry, couldn't display all the contents of \"%s\"."), file_name); } - eel_show_error_dialog (message, _("Error Displaying Folder"), parent_window); + eel_show_error_dialog (_("The folder contents could not be displayed."), message, + _("Error Displaying Folder"), parent_window); g_free (file_name); g_free (message); @@ -140,7 +141,8 @@ fm_report_error_renaming_file (NautilusFile *file, g_free (original_name_truncated); g_free (new_name_truncated); - eel_show_error_dialog (message, _("Renaming Error"), parent_window); + eel_show_error_dialog (_("The item could not be renamed."), message, + _("Renaming Error"), parent_window); g_free (message); } @@ -176,7 +178,7 @@ fm_report_error_setting_group (NautilusFile *file, g_free (file_name); } - eel_show_error_dialog (message, _("Error Setting Group"), parent_window); + eel_show_error_dialog (_("The group could not be changed."), message , _("Error Setting Group"), parent_window); g_free (file_name); g_free (message); @@ -208,7 +210,7 @@ fm_report_error_setting_owner (NautilusFile *file, message = g_strdup_printf (_("Sorry, couldn't change the owner of \"%s\"."), file_name); } - eel_show_error_dialog (message, _("Error Setting Owner"), parent_window); + eel_show_error_dialog (_("The owner could not be changed."), message, _("Error Setting Owner"), parent_window); g_free (file_name); g_free (message); @@ -240,7 +242,8 @@ fm_report_error_setting_permissions (NautilusFile *file, message = g_strdup_printf (_("Sorry, couldn't change the permissions of \"%s\"."), file_name); } - eel_show_error_dialog (message, _("Error Setting Permissions"), parent_window); + eel_show_error_dialog (_("The permissions could not be changed."), message, + _("Error Setting Permissions"), parent_window); g_free (file_name); g_free (message); diff --git a/src/file-manager/fm-icon-container.c b/src/file-manager/fm-icon-container.c index 6cffbaabe..b84509898 100644 --- a/src/file-manager/fm-icon-container.c +++ b/src/file-manager/fm-icon-container.c @@ -24,6 +24,8 @@ #include <config.h> #include <libgnome/gnome-macros.h> +#include <libgnome/gnome-i18n.h> +#include <libgnomevfs/gnome-vfs-mime-handlers.h> #include <libgnomevfs/gnome-vfs-ops.h> #include <libgnomevfs/gnome-vfs-uri.h> #include <libgnomevfs/gnome-vfs-utils.h> @@ -54,7 +56,8 @@ fm_icon_container_get_icon_images (NautilusIconContainer *container, NautilusIconData *data, GList **emblem_icons, char **embedded_text, - gboolean *embedded_text_needs_loading) + gboolean *embedded_text_needs_loading, + gboolean *has_window_open) { FMIconView *icon_view; EelStringList *emblems_to_ignore; @@ -78,9 +81,32 @@ fm_icon_container_get_icon_images (NautilusIconContainer *container, eel_string_list_free (emblems_to_ignore); } + *has_window_open = nautilus_file_has_open_window (file); + return nautilus_icon_factory_get_icon_for_file (file, TRUE); } +static char * +fm_icon_container_get_icon_description (NautilusIconContainer *container, + NautilusIconData *data) +{ + NautilusFile *file; + char *mime_type; + const char *description; + + file = NAUTILUS_FILE (data); + g_assert (NAUTILUS_IS_FILE (file)); + + if (NAUTILUS_IS_DESKTOP_ICON_FILE (file)) { + return NULL; + } + + mime_type = nautilus_file_get_mime_type (file); + description = gnome_vfs_mime_get_description (mime_type); + g_free (mime_type); + return g_strdup (description); +} + static void fm_icon_container_start_monitor_top_left (NautilusIconContainer *container, NautilusIconData *data, @@ -445,6 +471,7 @@ fm_icon_container_class_init (FMIconContainerClass *klass) ic_class->get_icon_text = fm_icon_container_get_icon_text; ic_class->get_icon_images = fm_icon_container_get_icon_images; + ic_class->get_icon_description = fm_icon_container_get_icon_description; ic_class->start_monitor_top_left = fm_icon_container_start_monitor_top_left; ic_class->stop_monitor_top_left = fm_icon_container_stop_monitor_top_left; ic_class->prioritize_thumbnailing = fm_icon_container_prioritize_thumbnailing; @@ -463,9 +490,13 @@ fm_icon_container_instance_init (FMIconContainer *icon_container) NautilusIconContainer * fm_icon_container_construct (FMIconContainer *icon_container, FMIconView *view) { + AtkObject *atk_obj; + g_return_val_if_fail (FM_IS_ICON_VIEW (view), NULL); icon_container->view = view; + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (icon_container)); + atk_object_set_name (atk_obj, _("Icon View")); return NAUTILUS_ICON_CONTAINER (icon_container); } diff --git a/src/file-manager/fm-icon-view.c b/src/file-manager/fm-icon-view.c index f2e66989c..3ee68925c 100644 --- a/src/file-manager/fm-icon-view.c +++ b/src/file-manager/fm-icon-view.c @@ -1358,6 +1358,33 @@ fm_icon_view_get_selection (FMDirectoryView *view) } static void +count_item (NautilusIconData *icon_data, + gpointer callback_data) +{ + guint *count; + + count = callback_data; + (*count)++; +} + +static guint +fm_icon_view_get_item_count (FMDirectoryView *view) +{ + guint count; + + g_return_val_if_fail (FM_IS_ICON_VIEW (view), 0); + + count = 0; + + nautilus_icon_container_for_each + (get_icon_container (FM_ICON_VIEW (view)), + count_item, &count); + + return count; +} + + +static void set_sort_criterion_by_id (FMIconView *icon_view, const char *id) { const SortCriterion *sort; @@ -1771,8 +1798,9 @@ play_file (gpointer callback_data) file_uri = nautilus_file_get_uri (file); mime_type = nautilus_file_get_mime_type (file); is_mp3 = eel_strcasecmp (mime_type, "audio/mpeg") == 0; - is_ogg = eel_strcasecmp (mime_type, "application/x-ogg") == 0; - + is_ogg = eel_strcasecmp (mime_type, "application/x-ogg") == 0 || + eel_strcasecmp (mime_type, "application/ogg") == 0; + mp3_pid = fork (); if (mp3_pid == (pid_t) 0) { /* Set the group (session) id to this process for future killing. */ @@ -2541,8 +2569,9 @@ icon_view_handle_uri_list (NautilusIconContainer *container, const char *item_ur if (eel_vfs_has_capability (container_uri, EEL_VFS_CAPABILITY_IS_REMOTE_AND_SLOW)) { - eel_show_warning_dialog (_("Drag and drop is only supported to local file systems."), - _("Drag and Drop error"), + eel_show_warning_dialog (_("Drag and drop is not supported."), + _("Drag and drop is only supported on local file systems."), + _("Drag and Drop Error"), fm_directory_view_get_containing_window (FM_DIRECTORY_VIEW (view))); g_free (container_uri); return; @@ -2564,8 +2593,9 @@ icon_view_handle_uri_list (NautilusIconContainer *container, const char *item_ur (action != GDK_ACTION_COPY) && (action != GDK_ACTION_MOVE) && (action != GDK_ACTION_LINK)) { - eel_show_warning_dialog (_("An invalid drag type was used."), - _("Drag and Drop error"), + eel_show_warning_dialog (_("Drag and drop is not supported."), + _("An invalid drag type was used."), + _("Drag and Drop Error"), fm_directory_view_get_containing_window (FM_DIRECTORY_VIEW (view))); g_free (container_uri); return; @@ -2747,6 +2777,7 @@ fm_icon_view_class_init (FMIconViewClass *klass) fm_directory_view_class->get_background_widget = fm_icon_view_get_background_widget; fm_directory_view_class->get_selected_icon_locations = fm_icon_view_get_selected_icon_locations; fm_directory_view_class->get_selection = fm_icon_view_get_selection; + fm_directory_view_class->get_item_count = fm_icon_view_get_item_count; fm_directory_view_class->is_empty = fm_icon_view_is_empty; fm_directory_view_class->remove_file = fm_icon_view_remove_file; fm_directory_view_class->reset_to_defaults = fm_icon_view_reset_to_defaults; diff --git a/src/file-manager/fm-list-model.c b/src/file-manager/fm-list-model.c index ad3c746f9..48558537d 100644 --- a/src/file-manager/fm-list-model.c +++ b/src/file-manager/fm-list-model.c @@ -189,6 +189,7 @@ fm_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int column int icon_size; NautilusZoomLevel zoom_level; int width, height; + char *modifier; model = (FMListModel *)tree_model; @@ -220,7 +221,31 @@ fm_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int column zoom_level = fm_list_model_get_zoom_level_from_column_id (column); icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level); - icon = nautilus_icon_factory_get_pixbuf_for_file (file, NULL, icon_size); + + modifier = NULL; + if (model->details->drag_view != NULL) { + GtkTreePath *path_a, *path_b; + + gtk_tree_view_get_drag_dest_row (model->details->drag_view, + &path_a, + NULL); + if (path_a != NULL) { + path_b = gtk_tree_model_get_path (tree_model, iter); + + if (gtk_tree_path_compare (path_a, path_b) == 0) { + modifier = "visiting"; + } + + gtk_tree_path_free (path_a); + gtk_tree_path_free (path_b); + } + } + + if (nautilus_file_has_open_window (file)) { + modifier = "accept"; + } + + icon = nautilus_icon_factory_get_pixbuf_for_file (file, modifier, icon_size); height = gdk_pixbuf_get_height (icon); if (height > icon_size) { @@ -649,6 +674,13 @@ fm_list_model_is_empty (FMListModel *model) return (g_sequence_get_length (model->details->files) == 0); } +guint +fm_list_model_get_length (FMListModel *model) +{ + return g_sequence_get_length (model->details->files); +} + + static void fm_list_model_remove (FMListModel *model, GtkTreeIter *iter) { @@ -691,6 +723,23 @@ fm_list_model_clear (FMListModel *model) } } +NautilusFile * +fm_list_model_file_for_path (FMListModel *model, GtkTreePath *path) +{ + NautilusFile *file; + GtkTreeIter iter; + + file = NULL; + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (model), + &iter, path)) { + gtk_tree_model_get (GTK_TREE_MODEL (model), + &iter, + FM_LIST_MODEL_FILE_COLUMN, &file, + -1); + } + return file; +} + void fm_list_model_set_should_sort_directories_first (FMListModel *model, gboolean sort_directories_first) { diff --git a/src/file-manager/fm-list-model.h b/src/file-manager/fm-list-model.h index 7c6d846ef..dcf3b4c09 100644 --- a/src/file-manager/fm-list-model.h +++ b/src/file-manager/fm-list-model.h @@ -71,6 +71,7 @@ void fm_list_model_add_file (FMListModel * void fm_list_model_file_changed (FMListModel *model, NautilusFile *file); gboolean fm_list_model_is_empty (FMListModel *model); +guint fm_list_model_get_length (FMListModel *model); void fm_list_model_remove_file (FMListModel *model, NautilusFile *file); void fm_list_model_clear (FMListModel *model); diff --git a/src/file-manager/fm-list-view.c b/src/file-manager/fm-list-view.c index fcfd774fb..6d0355646 100644 --- a/src/file-manager/fm-list-view.c +++ b/src/file-manager/fm-list-view.c @@ -80,6 +80,7 @@ struct FMListViewDetails { int drag_y; gboolean drag_started; + gboolean row_selected_on_button_down; }; /* @@ -161,11 +162,18 @@ activate_selected_items (FMListView *view) } static void -activate_selected_items_alternate (FMListView *view) +activate_selected_items_alternate (FMListView *view, + NautilusFile *file) { GList *file_list; - - file_list = fm_list_view_get_selection (FM_DIRECTORY_VIEW (view)); + + + if (file != NULL) { + nautilus_file_ref (file); + file_list = g_list_prepend (NULL, file); + } else { + file_list = fm_list_view_get_selection (FM_DIRECTORY_VIEW (view)); + } fm_directory_view_activate_files (FM_DIRECTORY_VIEW (view), file_list, Nautilus_ViewFrame_OPEN_ACCORDING_TO_MODE, @@ -193,12 +201,17 @@ fm_list_view_did_not_drag (FMListView *view, if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, &path, NULL, NULL, NULL)) { - if((event->button == 1 || event->button == 2) - && (click_policy_auto_value == NAUTILUS_CLICK_POLICY_DOUBLE) - && gtk_tree_selection_path_is_selected (selection, path) - && !button_event_modifies_selection (event)) { - gtk_tree_selection_unselect_all (selection); - gtk_tree_selection_select_path (selection, path); + if ((event->button == 1 || event->button == 2) + && ((event->state & GDK_CONTROL_MASK) != 0 || + (event->state & GDK_SHIFT_MASK) == 0) + && view->details->row_selected_on_button_down + && (click_policy_auto_value == NAUTILUS_CLICK_POLICY_DOUBLE)) { + if (!button_event_modifies_selection (event)) { + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_path (selection, path); + } else { + gtk_tree_selection_unselect_path (selection, path); + } } if ((click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) @@ -206,7 +219,7 @@ fm_list_view_did_not_drag (FMListView *view, if (event->button == 1) { activate_selected_items (view); } else if (event->button == 2) { - activate_selected_items_alternate (view); + activate_selected_items_alternate (view, NULL); } } gtk_tree_path_free (path); @@ -436,6 +449,7 @@ button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callba return TRUE; } + call_parent = TRUE; allow_drag = FALSE; if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, @@ -448,15 +462,26 @@ button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callba view->details->double_click_path[1] = view->details->double_click_path[0]; view->details->double_click_path[0] = gtk_tree_path_copy (path); } - if (event->type == GDK_2BUTTON_PRESS) { + /* Double clicking does not trigger a D&D action. */ + view->details->drag_button = 0; + call_parent = TRUE; if (view->details->double_click_path[1] && - gtk_tree_path_compare (view->details->double_click_path[0], view->details->double_click_path[1]) == 0 - && !button_event_modifies_selection (event)) { - if ((event->button == 1 || event->button == 3)) { - activate_selected_items (view); - } else if (event->button == 2) { - activate_selected_items_alternate (view); + gtk_tree_path_compare (view->details->double_click_path[0], view->details->double_click_path[1]) == 0) { + if (!button_event_modifies_selection (event)) { + if ((event->button == 1 || event->button == 3)) { + activate_selected_items (view); + } else if (event->button == 2) { + activate_selected_items_alternate (view, NULL); + } + } else if (event->button == 1 && + (event->state & GDK_SHIFT_MASK) != 0) { + NautilusFile *file; + file = fm_list_model_file_for_path (view->details->model, path); + if (file != NULL) { + activate_selected_items_alternate (view, file); + nautilus_file_unref (file); + } } } } @@ -471,17 +496,26 @@ button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callba call_parent = FALSE; } - if(!button_event_modifies_selection (event) && - (event->button == 1 || event->button == 2) && - gtk_tree_selection_path_is_selected (selection, path)) { - call_parent = FALSE; + if ((event->button == 1 || event->button == 2) && + ((event->state & GDK_CONTROL_MASK) != 0 || + (event->state & GDK_SHIFT_MASK) == 0)) { + view->details->row_selected_on_button_down = gtk_tree_selection_path_is_selected (selection, path); + if (view->details->row_selected_on_button_down) { + call_parent = FALSE; + } else if ((event->state & GDK_CONTROL_MASK) != 0) { + call_parent = FALSE; + gtk_tree_selection_select_path (selection, path); + } } if (call_parent) { tree_view_class->button_press_event (widget, event); + } else if (gtk_tree_selection_path_is_selected (selection, path)) { + gtk_widget_grab_focus (widget); } - if (event->button == 1 || event->button == 2) { + if ((event->button == 1 || event->button == 2) && + event->type == GDK_BUTTON_PRESS) { view->details->drag_started = FALSE; view->details->drag_button = event->button; view->details->drag_x = event->x; @@ -494,9 +528,21 @@ button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callba gtk_tree_path_free (path); } else { + if ((event->button == 1 || event->button == 2) && + event->type == GDK_BUTTON_PRESS) { + if (view->details->double_click_path[1]) { + gtk_tree_path_free (view->details->double_click_path[1]); + } + view->details->double_click_path[1] = view->details->double_click_path[0]; + view->details->double_click_path[0] = NULL; + } /* Deselect if people click outside any row. It's OK to let default code run; it won't reselect anything. */ gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view)); + + if (event->button == 3) { + do_popup_menu (widget, view, event); + } } /* We chained to the default handler in this method, so never @@ -701,6 +747,7 @@ create_and_set_up_tree_view (FMListView *view) GtkCellRenderer *cell; GtkTreeViewColumn *column; GtkTargetEntry *drag_types; + AtkObject *atk_obj; int num_drag_types; view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); @@ -742,6 +789,9 @@ create_and_set_up_tree_view (FMListView *view) view->details->model = g_object_new (FM_TYPE_LIST_MODEL, NULL); gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model)); + /* Need the model for the dnd drop icon "accept" change */ + fm_list_model_set_drag_view (FM_LIST_MODEL (view->details->model), + view->details->tree_view, 0, 0); g_signal_connect_object (view->details->model, "sort_column_changed", G_CALLBACK (sort_column_changed_callback), view, 0); @@ -815,6 +865,9 @@ create_and_set_up_tree_view (FMListView *view) gtk_widget_show (GTK_WIDGET (view->details->tree_view)); gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (view->details->tree_view)); + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (view->details->tree_view)); + atk_object_set_name (atk_obj, _("List View")); } static void @@ -964,6 +1017,15 @@ fm_list_view_get_selection (FMDirectoryView *view) return list; } + +static guint +fm_list_view_get_item_count (FMDirectoryView *view) +{ + g_return_val_if_fail (FM_IS_LIST_VIEW (view), 0); + + return fm_list_model_get_length (FM_LIST_VIEW (view)->details->model); +} + static gboolean fm_list_view_is_empty (FMDirectoryView *view) { @@ -973,7 +1035,52 @@ fm_list_view_is_empty (FMDirectoryView *view) static void fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file) { - fm_list_model_remove_file (FM_LIST_VIEW (view)->details->model, file); + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeIter temp_iter; + GtkTreeRowReference* row_reference; + FMListView *list_view; + GtkTreeModel* tree_model; + + path = NULL; + row_reference = NULL; + list_view = FM_LIST_VIEW (view); + tree_model = GTK_TREE_MODEL(list_view->details->model); + + if(fm_list_model_get_tree_iter_from_file (list_view->details->model, file, &iter)) { + temp_iter = iter; + + /* get reference for next element in the list view. If the element to be deleted is the + * last one, get reference to previous element. If there is only one element in view + * no need to select anything. */ + + if(gtk_tree_model_iter_next (tree_model, &iter)) { + path = gtk_tree_model_get_path (tree_model, &iter); + row_reference = gtk_tree_row_reference_new (tree_model, path); + } else { + path = gtk_tree_model_get_path (tree_model, &temp_iter); + if(gtk_tree_path_prev (path)) { + row_reference = gtk_tree_row_reference_new (tree_model, path); + } + } + + gtk_tree_path_free (path); + + fm_list_model_remove_file (list_view->details->model, file); + + if(gtk_tree_row_reference_valid(row_reference)) { + path = gtk_tree_row_reference_get_path (row_reference); + gtk_tree_view_set_cursor (list_view->details->tree_view, path, NULL, FALSE); + gtk_tree_path_free (path); + + } + + if(row_reference) { + gtk_tree_row_reference_free (row_reference); + } + } + + } static void @@ -1432,6 +1539,7 @@ fm_list_view_class_init (FMListViewClass *class) fm_directory_view_class->file_changed = fm_list_view_file_changed; fm_directory_view_class->get_background_widget = fm_list_view_get_background_widget; fm_directory_view_class->get_selection = fm_list_view_get_selection; + fm_directory_view_class->get_item_count = fm_list_view_get_item_count; fm_directory_view_class->is_empty = fm_list_view_is_empty; fm_directory_view_class->remove_file = fm_list_view_remove_file; fm_directory_view_class->reset_to_defaults = fm_list_view_reset_to_defaults; diff --git a/src/file-manager/fm-properties-window.c b/src/file-manager/fm-properties-window.c index 55464fcd2..7af9e5b7c 100644 --- a/src/file-manager/fm-properties-window.c +++ b/src/file-manager/fm-properties-window.c @@ -26,6 +26,7 @@ #include "fm-properties-window.h" #include "fm-error-reporting.h" +#include <eel/eel-accessibility.h> #include <eel/eel-ellipsizing-label.h> #include <eel/eel-gdk-pixbuf-extensions.h> #include <eel/eel-glib-extensions.h> @@ -152,7 +153,7 @@ enum { typedef struct { GList *original_files; GList *target_files; - FMDirectoryView *directory_view; + GtkWidget *parent_widget; char *pending_key; GHashTable *pending_files; } StartupData; @@ -186,7 +187,7 @@ static void is_directory_ready_callback (NautilusFile *file, gpointer data); static void cancel_group_change_callback (gpointer callback_data); static void cancel_owner_change_callback (gpointer callback_data); -static void directory_view_destroyed_callback (FMDirectoryView *view, +static void parent_widget_destroyed_callback (GtkWidget *widget, gpointer callback_data); static void select_image_button_callback (GtkWidget *widget, FMPropertiesWindow *properties_window); @@ -473,8 +474,8 @@ fm_properties_window_drag_data_received (GtkWidget *widget, GdkDragContext *cont if (!exactly_one) { eel_show_error_dialog - (_("You can't assign more than one custom icon at a time! " - "Please drag just one image to set a custom icon."), + (_("You can't assign more than one custom icon at a time!"), + _("Please drag just one image to set a custom icon."), _("More Than One Image"), window); } else { @@ -484,15 +485,15 @@ fm_properties_window_drag_data_received (GtkWidget *widget, GdkDragContext *cont } else { if (eel_is_remote_uri (uris[0])) { eel_show_error_dialog - (_("The file that you dropped is not local. " - "You can only use local images as custom icons."), + (_("The file that you dropped is not local."), + _("You can only use local images as custom icons."), _("Local Images Only"), window); } else { eel_show_error_dialog - (_("The file that you dropped is not an image. " - "You can only use local images as custom icons."), + (_("The file that you dropped is not an image."), + _("You can only use local images as custom icons."), _("Images Only"), window); } @@ -1375,7 +1376,7 @@ activate_group_callback (GtkMenuItem *menu_item, FileNamePair *pair) (cancel_group_change_callback, pair->file, _("Cancel Group Change?"), - _("Changing group"), + _("Changing group."), NULL); /* FIXME bugzilla.gnome.org 42397: Parent this? */ nautilus_file_set_group (pair->file, pair->name, @@ -1537,7 +1538,7 @@ activate_owner_callback (GtkMenuItem *menu_item, FileNamePair *pair) (cancel_owner_change_callback, pair->file, _("Cancel Owner Change?"), - _("Changing owner"), + _("Changing owner."), NULL); /* FIXME bugzilla.gnome.org 42397: Parent this? */ nautilus_file_set_owner (pair->file, pair->name, @@ -2667,10 +2668,12 @@ static void add_permissions_checkbox (FMPropertiesWindow *window, GtkTable *table, int row, int column, - GnomeVFSFilePermissions permission_to_check) + GnomeVFSFilePermissions permission_to_check, + GtkLabel *label_for) { GtkWidget *check_button; gchar *label; + gboolean a11y_enabled; if (column == PERMISSIONS_CHECKBOXES_READ_COLUMN) { label = _("_Read"); @@ -2691,6 +2694,12 @@ add_permissions_checkbox (FMPropertiesWindow *window, set_up_permissions_checkbox (window, check_button, permission_to_check); + + a11y_enabled = GTK_IS_ACCESSIBLE (gtk_widget_get_accessible (check_button)); + if (a11y_enabled) { + eel_accessibility_set_up_label_widget_relation (GTK_WIDGET (label_for), + check_button); + } } static GtkWidget * @@ -2845,6 +2854,9 @@ create_permissions_page (FMPropertiesWindow *window) guint checkbox_titles_row; GtkLabel *group_label; GtkLabel *owner_label; + GtkLabel *owner_perm_label; + GtkLabel *group_perm_label; + GtkLabel *other_perm_label; GtkOptionMenu *group_menu; GtkOptionMenu *owner_menu; GList *file_list; @@ -2877,7 +2889,7 @@ create_permissions_page (FMPropertiesWindow *window) owner_label = attach_title_field (page_table, last_row, _("File _owner:")); /* Option menu in this case. */ owner_menu = attach_owner_menu (page_table, last_row, get_target_file (window)); - gtk_label_set_mnemonic_widget (GTK_LABEL (owner_label), + gtk_label_set_mnemonic_widget (owner_label, GTK_WIDGET (owner_menu)); } else { attach_title_field (page_table, last_row, _("File owner:")); @@ -2896,7 +2908,7 @@ create_permissions_page (FMPropertiesWindow *window) /* Option menu in this case. */ group_menu = attach_group_menu (page_table, last_row, get_target_file (window)); - gtk_label_set_mnemonic_widget (GTK_LABEL (group_label), + gtk_label_set_mnemonic_widget (group_label, GTK_WIDGET (group_menu)); } else { last_row = append_title_field (page_table, @@ -2912,9 +2924,9 @@ create_permissions_page (FMPropertiesWindow *window) append_separator (page_table); - checkbox_titles_row = append_title_field (page_table, _("Owner:"), NULL); - append_title_field (page_table, _("Group:"), NULL); - append_title_field (page_table, _("Others:"), NULL); + checkbox_titles_row = append_title_field (page_table, _("Owner:"), &owner_perm_label); + append_title_field (page_table, _("Group:"), &group_perm_label); + append_title_field (page_table, _("Others:"), &other_perm_label); check_button_table = GTK_TABLE (gtk_table_new (PERMISSIONS_CHECKBOXES_ROW_COUNT, @@ -2932,55 +2944,64 @@ create_permissions_page (FMPropertiesWindow *window) check_button_table, PERMISSIONS_CHECKBOXES_OWNER_ROW, PERMISSIONS_CHECKBOXES_READ_COLUMN, - GNOME_VFS_PERM_USER_READ); + GNOME_VFS_PERM_USER_READ, + owner_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_OWNER_ROW, PERMISSIONS_CHECKBOXES_WRITE_COLUMN, - GNOME_VFS_PERM_USER_WRITE); + GNOME_VFS_PERM_USER_WRITE, + owner_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_OWNER_ROW, PERMISSIONS_CHECKBOXES_EXECUTE_COLUMN, - GNOME_VFS_PERM_USER_EXEC); + GNOME_VFS_PERM_USER_EXEC, + owner_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_GROUP_ROW, PERMISSIONS_CHECKBOXES_READ_COLUMN, - GNOME_VFS_PERM_GROUP_READ); + GNOME_VFS_PERM_GROUP_READ, + group_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_GROUP_ROW, PERMISSIONS_CHECKBOXES_WRITE_COLUMN, - GNOME_VFS_PERM_GROUP_WRITE); + GNOME_VFS_PERM_GROUP_WRITE, + group_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_GROUP_ROW, PERMISSIONS_CHECKBOXES_EXECUTE_COLUMN, - GNOME_VFS_PERM_GROUP_EXEC); + GNOME_VFS_PERM_GROUP_EXEC, + group_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_OTHERS_ROW, PERMISSIONS_CHECKBOXES_READ_COLUMN, - GNOME_VFS_PERM_OTHER_READ); + GNOME_VFS_PERM_OTHER_READ, + other_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_OTHERS_ROW, PERMISSIONS_CHECKBOXES_WRITE_COLUMN, - GNOME_VFS_PERM_OTHER_WRITE); + GNOME_VFS_PERM_OTHER_WRITE, + other_perm_label); add_permissions_checkbox (window, check_button_table, PERMISSIONS_CHECKBOXES_OTHERS_ROW, PERMISSIONS_CHECKBOXES_EXECUTE_COLUMN, - GNOME_VFS_PERM_OTHER_EXEC); + GNOME_VFS_PERM_OTHER_EXEC, + other_perm_label); append_separator (page_table); @@ -3115,7 +3136,7 @@ static StartupData * startup_data_new (GList *original_files, GList *target_files, const char *pending_key, - FMDirectoryView *directory_view) + GtkWidget *parent_widget) { StartupData *data; GList *l; @@ -3123,7 +3144,7 @@ startup_data_new (GList *original_files, data = g_new0 (StartupData, 1); data->original_files = nautilus_file_list_copy (original_files); data->target_files = nautilus_file_list_copy (target_files); - data->directory_view = directory_view; + data->parent_widget = parent_widget; data->pending_key = g_strdup (pending_key); data->pending_files = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -3149,19 +3170,15 @@ static void help_button_callback (GtkWidget *widget, GtkWidget *property_window) { GError *error = NULL; - char *message; egg_help_display_desktop_on_screen (NULL, "user-guide", "wgosnautilus.xml", "gosnautilus-51", gtk_window_get_screen (GTK_WINDOW (property_window)), &error); if (error) { - message = g_strdup_printf (_("There was an error displaying help: \n%s"), - error->message); - eel_show_error_dialog (message, _("Couldn't show help"), + eel_show_error_dialog (_("There was an error displaying help."), error->message, _("Couldn't Show Help"), GTK_WINDOW (property_window)); g_error_free (error); - g_free (message); } } @@ -3183,7 +3200,7 @@ create_properties_window (StartupData *startup_data) gtk_window_set_wmclass (GTK_WINDOW (window), "file_properties", "Nautilus"); gtk_window_set_resizable (GTK_WINDOW (window), FALSE); gtk_window_set_screen (GTK_WINDOW (window), - gtk_widget_get_screen (GTK_WIDGET (startup_data->directory_view))); + gtk_widget_get_screen (startup_data->parent_widget)); /* Set initial window title */ update_properties_window_title (window); @@ -3354,9 +3371,9 @@ cancel_create_properties_window_callback (gpointer callback_data) } static void -directory_view_destroyed_callback (FMDirectoryView *view, gpointer callback_data) +parent_widget_destroyed_callback (GtkWidget *widget, gpointer callback_data) { - g_assert (view == ((StartupData *)callback_data)->directory_view); + g_assert (widget == ((StartupData *)callback_data)->parent_widget); remove_pending ((StartupData *)callback_data, TRUE, TRUE, FALSE); } @@ -3389,8 +3406,8 @@ remove_pending (StartupData *startup_data, (cancel_create_properties_window_callback, startup_data); } if (cancel_destroy_handler) { - g_signal_handlers_disconnect_by_func (startup_data->directory_view, - G_CALLBACK (directory_view_destroyed_callback), + g_signal_handlers_disconnect_by_func (startup_data->parent_widget, + G_CALLBACK (parent_widget_destroyed_callback), startup_data); } @@ -3432,7 +3449,7 @@ is_directory_ready_callback (NautilusFile *file, void fm_properties_window_present (GList *original_files, - FMDirectoryView *directory_view) + GtkWidget *parent_widget) { GList *l, *next; GtkWidget *parent_window; @@ -3442,7 +3459,7 @@ fm_properties_window_present (GList *original_files, char *pending_key; g_return_if_fail (original_files != NULL); - g_return_if_fail (FM_IS_DIRECTORY_VIEW (directory_view)); + g_return_if_fail (GTK_IS_WIDGET (parent_widget)); /* Create the hash tables first time through. */ if (windows == NULL) { @@ -3459,7 +3476,7 @@ fm_properties_window_present (GList *original_files, existing_window = get_existing_window (original_files); if (existing_window != NULL) { gtk_window_set_screen (existing_window, - gtk_widget_get_screen (GTK_WIDGET (directory_view))); + gtk_widget_get_screen (parent_widget)); gtk_window_present (existing_window); return; } @@ -3477,7 +3494,7 @@ fm_properties_window_present (GList *original_files, startup_data = startup_data_new (original_files, target_files, pending_key, - directory_view); + parent_widget); nautilus_file_list_free (target_files); g_free(pending_key); @@ -3487,16 +3504,16 @@ fm_properties_window_present (GList *original_files, */ g_hash_table_insert (pending_lists, startup_data->pending_key, startup_data->pending_key); - g_signal_connect (directory_view, "destroy", - G_CALLBACK (directory_view_destroyed_callback), startup_data); + g_signal_connect (parent_widget, "destroy", + G_CALLBACK (parent_widget_destroyed_callback), startup_data); - parent_window = gtk_widget_get_ancestor (GTK_WIDGET (directory_view), GTK_TYPE_WINDOW); + parent_window = gtk_widget_get_ancestor (parent_widget, GTK_TYPE_WINDOW); eel_timed_wait_start (cancel_create_properties_window_callback, startup_data, _("Cancel Showing Properties Window?"), - _("Creating Properties window"), + _("Creating Properties window."), parent_window == NULL ? NULL : GTK_WINDOW (parent_window)); diff --git a/src/file-manager/fm-properties-window.h b/src/file-manager/fm-properties-window.h index 32d59d99a..b05c3b729 100644 --- a/src/file-manager/fm-properties-window.h +++ b/src/file-manager/fm-properties-window.h @@ -26,10 +26,9 @@ #ifndef FM_PROPERTIES_WINDOW_H #define FM_PROPERTIES_WINDOW_H -#include "fm-directory-view.h" - #include <gtk/gtkwindow.h> #include <libnautilus-private/nautilus-file.h> +#include <libnautilus/nautilus-view.h> typedef struct FMPropertiesWindow FMPropertiesWindow; @@ -60,6 +59,6 @@ typedef struct FMPropertiesWindowClass FMPropertiesWindowClass; GType fm_properties_window_get_type (void); void fm_properties_window_present (GList *files, - FMDirectoryView *directory_view); + GtkWidget *parent_widget); #endif /* FM_PROPERTIES_WINDOW_H */ diff --git a/src/file-manager/fm-search-list-view.c b/src/file-manager/fm-search-list-view.c index 682daa757..8b43400fc 100644 --- a/src/file-manager/fm-search-list-view.c +++ b/src/file-manager/fm-search-list-view.c @@ -163,8 +163,9 @@ load_location_callback (NautilusView *nautilus_view, char *location) } } #else - eel_show_error_dialog (_("Sorry, but the Medusa search service is not available because it is not installed."), - _("Search Service Not Available"), + eel_show_error_dialog (_("Sorry, but the Medusa search service is not available."), + _("Medusa is not installed."), + _("Search Service Not Available"), NULL); #endif @@ -179,7 +180,6 @@ real_load_error (FMDirectoryView *nautilus_view, GnomeVFSResult result) { GtkDialog *load_error_dialog; - char *error_string; /* Do not call parent's function; we handle all search errors * here and don't want fm-directory-view's default handling. @@ -190,42 +190,38 @@ real_load_error (FMDirectoryView *nautilus_view, load_error_dialog = eel_show_info_dialog (_("The search you have selected " "is newer than the index on your " "system. The search will return " - "no results right now. You can create a new " - " index by running \"medusa-indexd\" as root " + "no results right now."), + _("You can create a new " + "index by running \"medusa-indexd\" as root " "on the command line."), - _("Search for items that are too new"), + _("Search For Items That Are Too New"), NULL); break; case GNOME_VFS_ERROR_TOO_BIG: eel_show_error_dialog (_("Every indexed file on your computer " - "matches the criteria you selected. " - "You can check the spelling on your selections " + "matches the criteria you selected. "), + _("You can check the spelling on your selections " "or add more criteria to narrow your results."), - _("Error during search"), + _("Error During Search"), NULL); break; case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE: /* FIXME: This dialog does not get shown because a slow search will be performed and will not return an error. */ eel_show_error_dialog (_("Find cannot open your file system index. " - "Your index may be missing or corrupt. You can " - "create a new index by running \"medusa-indexd\" as root " + "Your index may be missing or corrupt."), + _("You can create a new index by running \"medusa-indexd\" as root " "on the command line."), - _("Error reading file index"), + _("Error Reading File Index"), NULL); break; case GNOME_VFS_ERROR_CANCELLED: break; default: - error_string = g_strdup_printf (_("An error occurred while loading " - "this search's contents: " - "%s"), - gnome_vfs_result_to_string (result)); - eel_show_error_dialog (error_string, - _("Error during search"), - NULL); - g_free (error_string); - + eel_show_error_dialog (_("An error occurred while loading this search's contents."), + gnome_vfs_result_to_string (result), + _("Error During Search"), + NULL); } } @@ -234,7 +230,7 @@ real_load_error (FMDirectoryView *nautilus_view, static void display_indexed_search_problems_dialog (gboolean backup_search_is_available) { - const char *error_string, *title_string; + const char *error_string, *detail_string, *title_string; if (medusa_indexed_search_system_index_files_look_available ()) { /* There is an index on the system, but there is no @@ -242,17 +238,19 @@ display_indexed_search_problems_dialog (gboolean backup_search_is_available) confused. Tell the user this. */ error_string = backup_search_is_available ? N_("To do a fast search, Find requires an index " - "of the files on your system. " - "Find can't access your index right now " + "of the files on your system.") + : N_("To do a content search, Find requires an index " + "of the files on your system."); + detail_string = backup_search_is_available + ? N_("Find can't access your index right now " "so a slower search will be performed that " "doesn't use the index.") - : N_("To do a content search, Find requires an index " - "of the files on your system. " - "Find can't access your index right now. "); + : N_("Find can't access your index right now."); title_string = backup_search_is_available - ? N_("Fast searches are not available") - : N_("Content searches are not available"); + ? N_("Fast Searches Are Not Available") + : N_("Content Searches Are Not Available"); eel_show_error_dialog_with_details (error_string, + detail_string, title_string, _("Your index files are available " "but the Medusa search daemon, which handles " @@ -270,19 +268,23 @@ display_indexed_search_problems_dialog (gboolean backup_search_is_available) error_string = backup_search_is_available ? N_("To do a fast search, Find requires an index " "of the files on your system. " - "Your computer is currently creating that " - "index. Because Find cannot use an index, " - "this search may take several " - "minutes.") + "Your computer is currently creating that " + "index.") : N_("To do a content search, Find requires an index " "of the content on your system. " "Your computer is currently creating that " - "index. Content searches will be available " + "index."); + detail_string = backup_search_is_available + ? N_("Because Find cannot use an index, " + "this search may take several " + "minutes.") + : N_("Content searches will be available " "when the index is complete."); title_string = backup_search_is_available - ? N_("Indexed searches are not available") - : N_("Content searches are not available"); + ? N_("Indexed Searches Are Not Available") + : N_("Content Searches Are Not Available"); eel_show_error_dialog (error_string, + detail_string, title_string, NULL); } @@ -290,22 +292,26 @@ display_indexed_search_problems_dialog (gboolean backup_search_is_available) error_string = backup_search_is_available ? N_("To do a fast search, Find requires an index " "of the files on your system. No index " - "is available right now. You can create an " + "is available right now.") + : N_("To do a content search, Find requires an index " + "of the content on your system. No index is " + "available right now."); + detail_string = backup_search_is_available + ? N_("You can create an " "index by running \"medusa-indexd\" as root " "on the command line. Until a complete index " "is available, searches will " "take several minutes.") - : N_("To do a content search, Find requires an index " - "of the content on your system. No index is " - "available right now. You can create an " + : N_("You can create an " "index by running \"medusa-indexd\" as root " "on the command line. Until a complete index " "is available, content searches cannot be " "performed."); title_string = backup_search_is_available - ? N_("Indexed searches are not available") - : N_("Content searches are not available"); + ? N_("Indexed Searches Are Not Available") + : N_("Content Searches Are Not Available"); eel_show_error_dialog (error_string, + detail_string, title_string, NULL); } @@ -320,11 +326,12 @@ display_system_services_are_disabled_dialog (gboolean unindexed_search_is_availa char *details_string; details_string = nautilus_medusa_get_explanation_of_enabling (); - dialog_shown = eel_show_info_dialog_with_details (_("To do a fast search, Find requires an index of " + dialog_shown = eel_show_info_dialog_with_details (_("Fast searches are not enabled on your computer."), + _("To do a fast search, Find requires an index of " "the files on your system. Your system administrator " "has disabled fast search on your computer, so no index " "is available."), - _("Fast searches are not enabled on your computer"), + _("Fast Searches Not Enabled"), details_string, NULL); g_free (details_string); @@ -681,8 +688,8 @@ real_file_limit_reached (FMDirectoryView *view) * to the way files are collected in batches. So you can't assume that * no more than the constant limit are displayed. */ - eel_show_warning_dialog (_("Nautilus found more search results than it can display. " - "Some matching items will not be displayed. "), + eel_show_warning_dialog (_("Nautilus found more search results than it can display."), + _("Some matching items will not be displayed. "), _("Too Many Matches"), fm_directory_view_get_containing_window (view)); } diff --git a/src/file-manager/fm-tree-model.c b/src/file-manager/fm-tree-model.c new file mode 100644 index 000000000..bcee8867a --- /dev/null +++ b/src/file-manager/fm-tree-model.c @@ -0,0 +1,1797 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * Copyright C) 2000, 2001 Eazel, Inc + * Copyright (C) 2002 Anders Carlsson + * Copyright (C) 2002 Bent Spoon Software + * + * 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. + * + * Authors: Anders Carlsson <andersca@gnu.org> + * Darin Adler <darin@bentspoon.com> + */ + +/* fm-tree-model.c - model for the tree view */ + +#include <config.h> +#include "fm-tree-model.h" + +#include <eel/eel-glib-extensions.h> +#include <libgnome/gnome-i18n.h> +#include <libnautilus-private/nautilus-directory.h> +#include <libnautilus-private/nautilus-file-attributes.h> +#include <libnautilus-private/nautilus-file.h> +#include <libnautilus-private/nautilus-icon-factory.h> +#include <string.h> + +enum { + ROW_LOADED, + LAST_SIGNAL +}; + +static guint tree_model_signals[LAST_SIGNAL] = { 0 }; + +typedef gboolean (* FilePredicate) (NautilusFile *); + +/* The user_data of the GtkTreeIter is the TreeNode pointer. + * It's NULL for the dummy node. If it's NULL, then user_data2 + * is the TreeNode pointer to the parent. + */ + +typedef struct TreeNode TreeNode; +typedef struct FMTreeModelRoot FMTreeModelRoot; + +struct TreeNode { + /* part of this node for the file itself */ + int ref_count; + + NautilusFile *file; + char *display_name; + char *icon_name; + GdkPixbuf *closed_pixbuf; + GdkPixbuf *open_pixbuf; + + FMTreeModelRoot *root; + + TreeNode *parent; + TreeNode *next; + TreeNode *prev; + + /* part of the node used only for directories */ + int dummy_child_ref_count; + int all_children_ref_count; + + NautilusDirectory *directory; + guint done_loading_id; + guint files_added_id; + guint files_changed_id; + + TreeNode *first_child; + + /* misc. flags */ + guint done_loading : 1; + guint inserting_first_child : 1; + guint inserted : 1; +}; + +struct FMTreeModelDetails { + int stamp; + + TreeNode *root_node; + + guint monitoring_update_idle_id; + + gboolean show_hidden_files; + gboolean show_backup_files; + gboolean show_only_directories; +}; + +struct FMTreeModelRoot { + FMTreeModel *model; + + /* separate hash table for each root node needed */ + GHashTable *file_to_node_map; + + TreeNode *root_node; + + gulong changed_handler_id; +}; + +typedef struct { + NautilusDirectory *directory; + FMTreeModel *model; +} DoneLoadingParameters; + +static GObjectClass *parent_class; + +static void schedule_monitoring_update (FMTreeModel *model); +static void destroy_node_without_reporting (FMTreeModel *model, + TreeNode *node); +static void report_node_contents_changed (FMTreeModel *model, + TreeNode *node); + +static guint +fm_tree_model_get_flags (GtkTreeModel *tree_model) +{ + return GTK_TREE_MODEL_ITERS_PERSIST; +} + +static void +object_unref_if_not_NULL (gpointer object) +{ + if (object == NULL) { + return; + } + g_object_unref (object); +} + +static FMTreeModelRoot * +tree_model_root_new (FMTreeModel *model) +{ + FMTreeModelRoot *root; + + root = g_new0 (FMTreeModelRoot, 1); + root->model = model; + root->file_to_node_map = g_hash_table_new (NULL, NULL); + + return root; +} + +static TreeNode * +tree_node_new (NautilusFile *file, FMTreeModelRoot *root) +{ + TreeNode *node; + + node = g_new0 (TreeNode, 1); + node->file = nautilus_file_ref (file); + node->root = root; + return node; +} + +static void +tree_node_unparent (FMTreeModel *model, TreeNode *node) +{ + TreeNode *parent, *next, *prev; + + parent = node->parent; + next = node->next; + prev = node->prev; + + if (parent == NULL && + node == model->details->root_node) { + /* it's the first root node -> if there is a next then let it be the first root node */ + model->details->root_node = next; + } + + if (next != NULL) { + next->prev = prev; + } + if (prev == NULL && parent != NULL) { + g_assert (parent->first_child == node); + parent->first_child = next; + } else if (prev != NULL) { + prev->next = next; + } + + node->parent = NULL; + node->next = NULL; + node->prev = NULL; + node->root = NULL; +} + +static void +tree_node_destroy (FMTreeModel *model, TreeNode *node) +{ + g_assert (node->first_child == NULL); + g_assert (node->ref_count == 0); + + tree_node_unparent (model, node); + + g_object_unref (node->file); + g_free (node->display_name); + g_free (node->icon_name); + object_unref_if_not_NULL (node->closed_pixbuf); + object_unref_if_not_NULL (node->open_pixbuf); + + g_assert (node->done_loading_id == 0); + g_assert (node->files_added_id == 0); + g_assert (node->files_changed_id == 0); + object_unref_if_not_NULL (node->directory); + + g_free (node); +} + +static void +tree_node_parent (TreeNode *node, TreeNode *parent) +{ + TreeNode *first_child; + + g_assert (parent != NULL); + g_assert (node->parent == NULL); + g_assert (node->prev == NULL); + g_assert (node->next == NULL); + + first_child = parent->first_child; + + node->parent = parent; + node->root = parent->root; + node->next = first_child; + + if (first_child != NULL) { + g_assert (first_child->prev == NULL); + first_child->prev = node; + } + + parent->first_child = node; +} + +static GdkPixbuf * +tree_node_get_pixbuf_from_factory (TreeNode *node, + const char *modifier) +{ + if (node->parent == NULL) { + return nautilus_icon_factory_get_pixbuf_from_name + (node->icon_name, NULL, + NAUTILUS_ICON_SIZE_FOR_MENUS, NULL); + } + return nautilus_icon_factory_get_pixbuf_for_file + (node->file, modifier, NAUTILUS_ICON_SIZE_FOR_MENUS); +} + +static gboolean +tree_node_update_pixbuf (TreeNode *node, + GdkPixbuf **pixbuf_storage, + const char *modifier) +{ + GdkPixbuf *pixbuf; + + if (*pixbuf_storage == NULL) { + return FALSE; + } + pixbuf = tree_node_get_pixbuf_from_factory (node, modifier); + if (pixbuf == *pixbuf_storage) { + g_object_unref (pixbuf); + return FALSE; + } + g_object_unref (*pixbuf_storage); + *pixbuf_storage = pixbuf; + return TRUE; +} + +static gboolean +tree_node_update_closed_pixbuf (TreeNode *node) +{ + return tree_node_update_pixbuf (node, &node->closed_pixbuf, NULL); +} + +static gboolean +tree_node_update_open_pixbuf (TreeNode *node) +{ + return tree_node_update_pixbuf (node, &node->open_pixbuf, "accept"); +} + +static gboolean +tree_node_update_display_name (TreeNode *node) +{ + char *display_name; + + if (node->display_name == NULL) { + return FALSE; + } + /* don't update root node display names */ + if (node->parent == NULL) { + return FALSE; + } + display_name = nautilus_file_get_display_name (node->file); + if (strcmp (display_name, node->display_name) == 0) { + g_free (display_name); + return FALSE; + } + g_free (node->display_name); + node->display_name = NULL; + return TRUE; +} + +static GdkPixbuf * +tree_node_get_closed_pixbuf (TreeNode *node) +{ + if (node->closed_pixbuf == NULL) { + node->closed_pixbuf = tree_node_get_pixbuf_from_factory (node, NULL); + } + return node->closed_pixbuf; +} + +static GdkPixbuf * +tree_node_get_open_pixbuf (TreeNode *node) +{ + if (node->open_pixbuf == NULL) { + node->open_pixbuf = tree_node_get_pixbuf_from_factory (node, "accept"); + } + return node->open_pixbuf; +} + +static const char * +tree_node_get_display_name (TreeNode *node) +{ + if (node->display_name == NULL) { + node->display_name = nautilus_file_get_display_name (node->file); + } + return node->display_name; +} + +static gboolean +tree_node_has_dummy_child (TreeNode *node) +{ + return node->directory != NULL + && (!node->done_loading + || node->first_child == NULL + || node->inserting_first_child); +} + +static int +tree_node_get_child_index (TreeNode *parent, TreeNode *child) +{ + int i; + TreeNode *node; + + if (child == NULL) { + g_assert (tree_node_has_dummy_child (parent)); + return 0; + } + + i = tree_node_has_dummy_child (parent) ? 1 : 0; + for (node = parent->first_child; node != NULL; node = node->next, i++) { + if (child == node) { + return i; + } + } + + g_assert_not_reached (); + return 0; +} + +static gboolean +make_iter_invalid (GtkTreeIter *iter) +{ + iter->stamp = 0; + iter->user_data = NULL; + iter->user_data2 = NULL; + iter->user_data3 = NULL; + return FALSE; +} + +static gboolean +make_iter_for_node (TreeNode *node, GtkTreeIter *iter, int stamp) +{ + if (node == NULL) { + return make_iter_invalid (iter); + } + iter->stamp = stamp; + iter->user_data = node; + iter->user_data2 = NULL; + iter->user_data3 = NULL; + return TRUE; +} + +static gboolean +make_iter_for_dummy_row (TreeNode *parent, GtkTreeIter *iter, int stamp) +{ + g_assert (tree_node_has_dummy_child (parent)); + g_assert (parent != NULL); + iter->stamp = stamp; + iter->user_data = NULL; + iter->user_data2 = parent; + iter->user_data3 = NULL; + return TRUE; +} + +static TreeNode * +get_node_from_file (FMTreeModelRoot *root, NautilusFile *file) +{ + return g_hash_table_lookup (root->file_to_node_map, file); +} + +static TreeNode * +get_parent_node_from_file (FMTreeModelRoot *root, NautilusFile *file) +{ + NautilusFile *parent_file; + TreeNode *parent_node; + + parent_file = nautilus_file_get_parent (file); + parent_node = get_node_from_file (root, parent_file); + nautilus_file_unref (parent_file); + return parent_node; +} + +static TreeNode * +create_node_for_file (FMTreeModelRoot *root, NautilusFile *file) +{ + TreeNode *node; + + g_assert (get_node_from_file (root, file) == NULL); + node = tree_node_new (file, root); + g_hash_table_insert (root->file_to_node_map, node->file, node); + return node; +} + +#if LOG_REF_COUNTS + +static char * +get_node_uri (GtkTreeIter *iter) +{ + TreeNode *node, *parent; + char *parent_uri, *node_uri; + + node = iter->user_data; + if (node != NULL) { + return nautilus_file_get_uri (node->file); + } + + parent = iter->user_data2; + parent_uri = nautilus_file_get_uri (parent->file); + node_uri = g_strconcat (parent_uri, " -- DUMMY", NULL); + g_free (parent_uri); + return node_uri; +} + +#endif + +static void +decrement_ref_count (FMTreeModel *model, TreeNode *node, int count) +{ + node->all_children_ref_count -= count; + if (node->all_children_ref_count == 0) { + schedule_monitoring_update (model); + } +} + +static void +abandon_node_ref_count (FMTreeModel *model, TreeNode *node) +{ + if (node->parent != NULL) { + decrement_ref_count (model, node->parent, node->ref_count); +#if LOG_REF_COUNTS + if (node->ref_count != 0) { + char *uri; + + uri = nautilus_file_get_uri (node->file); + g_message ("abandoning %d ref of %s, count is now %d", + node->ref_count, uri, node->parent->all_children_ref_count); + g_free (uri); + } +#endif + } + node->ref_count = 0; +} + +static void +abandon_dummy_row_ref_count (FMTreeModel *model, TreeNode *node) +{ + decrement_ref_count (model, node, node->dummy_child_ref_count); + if (node->dummy_child_ref_count != 0) { +#if LOG_REF_COUNTS + char *uri; + + uri = nautilus_file_get_uri (node->file); + g_message ("abandoning %d ref of %s -- DUMMY, count is now %d", + node->dummy_child_ref_count, uri, node->all_children_ref_count); + g_free (uri); +#endif + } + node->dummy_child_ref_count = 0; +} + +static void +report_row_inserted (FMTreeModel *model, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter); + gtk_tree_path_free (path); +} + +static void +report_row_contents_changed (FMTreeModel *model, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, iter); + gtk_tree_path_free (path); +} + +static void +report_row_has_child_toggled (FMTreeModel *model, GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (model), path, iter); + gtk_tree_path_free (path); +} + +static GtkTreePath * +get_node_path (FMTreeModel *model, TreeNode *node) +{ + GtkTreeIter iter; + + make_iter_for_node (node, &iter, model->details->stamp); + return gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); +} + +static void +report_dummy_row_inserted (FMTreeModel *model, TreeNode *parent) +{ + GtkTreeIter iter; + + if (!parent->inserted) { + return; + } + make_iter_for_dummy_row (parent, &iter, model->details->stamp); + report_row_inserted (model, &iter); +} + +static void +report_dummy_row_deleted (FMTreeModel *model, TreeNode *parent) +{ + GtkTreeIter iter; + GtkTreePath *path; + + abandon_dummy_row_ref_count (model, parent); + if (!parent->inserted) { + return; + } + make_iter_for_node (parent, &iter, model->details->stamp); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + gtk_tree_path_append_index (path, 0); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); +} + +static void +report_node_inserted (FMTreeModel *model, TreeNode *node) +{ + GtkTreeIter iter; + + make_iter_for_node (node, &iter, model->details->stamp); + report_row_inserted (model, &iter); + node->inserted = TRUE; + + if (node->directory != NULL) { + report_row_has_child_toggled (model, &iter); + } + if (tree_node_has_dummy_child (node)) { + report_dummy_row_inserted (model, node); + } +} + +static void +report_node_contents_changed (FMTreeModel *model, TreeNode *node) +{ + GtkTreeIter iter; + + if (!node->inserted) { + return; + } + make_iter_for_node (node, &iter, model->details->stamp); + report_row_contents_changed (model, &iter); +} + +static void +report_node_has_child_toggled (FMTreeModel *model, TreeNode *node) +{ + GtkTreeIter iter; + + if (!node->inserted) { + return; + } + make_iter_for_node (node, &iter, model->details->stamp); + report_row_has_child_toggled (model, &iter); +} + +static void +report_dummy_row_contents_changed (FMTreeModel *model, TreeNode *parent) +{ + GtkTreeIter iter; + + if (!parent->inserted) { + return; + } + make_iter_for_dummy_row (parent, &iter, model->details->stamp); + report_row_contents_changed (model, &iter); +} + +static void +stop_monitoring_directory (FMTreeModel *model, TreeNode *node) +{ + NautilusDirectory *directory; + + if (node->done_loading_id == 0) { + g_assert (node->files_added_id == 0); + g_assert (node->files_changed_id == 0); + return; + } + + directory = node->directory; + + g_signal_handler_disconnect (node->directory, node->done_loading_id); + g_signal_handler_disconnect (node->directory, node->files_added_id); + g_signal_handler_disconnect (node->directory, node->files_changed_id); + + node->done_loading_id = 0; + node->files_added_id = 0; + node->files_changed_id = 0; + + nautilus_directory_file_monitor_remove (node->directory, model); +} + +static void +destroy_children_without_reporting (FMTreeModel *model, TreeNode *parent) +{ + while (parent->first_child != NULL) { + destroy_node_without_reporting (model, parent->first_child); + } +} + +static void +destroy_node_without_reporting (FMTreeModel *model, TreeNode *node) +{ + abandon_node_ref_count (model, node); + stop_monitoring_directory (model, node); + node->inserted = FALSE; + destroy_children_without_reporting (model, node); + g_hash_table_remove (node->root->file_to_node_map, node->file); + tree_node_destroy (model, node); +} + +static void +destroy_node (FMTreeModel *model, TreeNode *node) +{ + TreeNode *parent; + gboolean parent_had_dummy_child; + GtkTreePath *path; + + parent = node->parent; + parent_had_dummy_child = tree_node_has_dummy_child (parent); + + path = get_node_path (model, node); + + destroy_node_without_reporting (model, node); + + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); + + if (tree_node_has_dummy_child (parent)) { + if (!parent_had_dummy_child) { + report_dummy_row_inserted (model, parent); + } + } else { + g_assert (!parent_had_dummy_child); + } +} + +static void +destroy_children (FMTreeModel *model, TreeNode *parent) +{ + while (parent->first_child != NULL) { + destroy_node (model, parent->first_child); + } +} + +static void +destroy_children_by_function (FMTreeModel *model, TreeNode *parent, FilePredicate f) +{ + TreeNode *child, *next; + + for (child = parent->first_child; child != NULL; child = next) { + next = child->next; + if (f (child->file)) { + destroy_node (model, child); + } else { + destroy_children_by_function (model, child, f); + } + } +} + +static void +destroy_by_function (FMTreeModel *model, FilePredicate f) +{ + TreeNode *node; + for (node = model->details->root_node; node != NULL; node = node->next) { + destroy_children_by_function (model, node, f); + } +} + +static gboolean +update_node_without_reporting (FMTreeModel *model, TreeNode *node) +{ + gboolean changed; + + changed = FALSE; + + if (node->directory == NULL && nautilus_file_is_directory (node->file)) { + node->directory = nautilus_directory_get_for_file (node->file); + } else if (node->directory != NULL && !nautilus_file_is_directory (node->file)) { + stop_monitoring_directory (model, node); + destroy_children (model, node); + nautilus_directory_unref (node->directory); + node->directory = NULL; + } + + changed |= tree_node_update_display_name (node); + changed |= tree_node_update_closed_pixbuf (node); + changed |= tree_node_update_open_pixbuf (node); + + return changed; +} + +static void +insert_node (FMTreeModel *model, TreeNode *parent, TreeNode *node) +{ + gboolean parent_empty; + + parent_empty = parent->first_child == NULL; + if (parent_empty) { + parent->inserting_first_child = TRUE; + } + + tree_node_parent (node, parent); + + update_node_without_reporting (model, node); + report_node_inserted (model, node); + + if (parent_empty) { + parent->inserting_first_child = FALSE; + if (!tree_node_has_dummy_child (parent)) { + report_dummy_row_deleted (model, parent); + } + } +} + +static void +reparent_node (FMTreeModel *model, TreeNode *node) +{ + GtkTreePath *path; + TreeNode *new_parent; + + new_parent = get_parent_node_from_file (node->root, node->file); + if (new_parent == NULL || new_parent->directory == NULL) { + destroy_node (model, node); + return; + } + + path = get_node_path (model, node); + + abandon_node_ref_count (model, node); + tree_node_unparent (model, node); + + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); + + insert_node (model, new_parent, node); +} + +static gboolean +should_show_file (FMTreeModel *model, NautilusFile *file) +{ + gboolean should; + TreeNode *node; + + should = nautilus_file_should_show (file, + model->details->show_hidden_files, + model->details->show_backup_files); + + if (should + && model->details->show_only_directories + &&! nautilus_file_is_directory (file)) { + should = FALSE; + } + + if (should && nautilus_file_is_gone (file)) { + should = FALSE; + } + + for (node = model->details->root_node; node != NULL; node = node->next) { + if (!should && node != NULL && file == node->file) { + should = TRUE; + } + } + + return should; +} + +static void +update_node (FMTreeModel *model, TreeNode *node) +{ + gboolean had_dummy_child, has_dummy_child; + gboolean had_directory, has_directory; + gboolean changed; + + if (!should_show_file (model, node->file)) { + destroy_node (model, node); + return; + } + + if (node->parent != NULL && node->parent->directory != NULL + && !nautilus_directory_contains_file (node->parent->directory, node->file)) { + reparent_node (model, node); + return; + } + + had_dummy_child = tree_node_has_dummy_child (node); + had_directory = node->directory != NULL; + + changed = update_node_without_reporting (model, node); + + has_dummy_child = tree_node_has_dummy_child (node); + has_directory = node->directory != NULL; + + if (had_dummy_child != has_dummy_child) { + if (has_dummy_child) { + report_dummy_row_inserted (model, node); + } else { + report_dummy_row_deleted (model, node); + } + } + if (had_directory != has_directory) { + report_node_has_child_toggled (model, node); + } + + if (changed) { + report_node_contents_changed (model, node); + } +} + +static void +process_file_change (FMTreeModelRoot *root, + NautilusFile *file) +{ + TreeNode *node, *parent; + + node = get_node_from_file (root, file); + if (node != NULL) { + update_node (root->model, node); + return; + } + + if (!should_show_file (root->model, file)) { + return; + } + + parent = get_parent_node_from_file (root, file); + if (parent == NULL) { + return; + } + + insert_node (root->model, parent, create_node_for_file (root, file)); +} + +static void +files_changed_callback (NautilusDirectory *directory, + GList *changed_files, + gpointer callback_data) +{ + FMTreeModelRoot *root; + GList *node; + + root = (FMTreeModelRoot *) (callback_data); + + for (node = changed_files; node != NULL; node = node->next) { + process_file_change (root, NAUTILUS_FILE (node->data)); + } +} + +static void +set_done_loading (FMTreeModel *model, TreeNode *node, gboolean done_loading) +{ + gboolean had_dummy; + + if (node == NULL || node->done_loading == done_loading) { + return; + } + + had_dummy = tree_node_has_dummy_child (node); + + node->done_loading = done_loading; + + if (tree_node_has_dummy_child (node)) { + if (had_dummy) { + report_dummy_row_contents_changed (model, node); + } else { + report_dummy_row_inserted (model, node); + } + } else { + if (had_dummy) { + report_dummy_row_deleted (model, node); + } else { + g_assert_not_reached (); + } + } +} + +static void +done_loading_callback (NautilusDirectory *directory, + FMTreeModelRoot *root) +{ + NautilusFile *file; + TreeNode *node; + GtkTreeIter iter; + + file = nautilus_directory_get_corresponding_file (directory); + node = get_node_from_file (root, file); + g_assert (node != NULL); + set_done_loading (root->model, node, TRUE); + nautilus_file_unref (file); + + make_iter_for_node (node, &iter, root->model->details->stamp); + g_signal_emit_by_name (root->model, "row_loaded", &iter); +} + +static NautilusFileAttributes +get_tree_monitor_attributes (void) +{ + NautilusFileAttributes attributes; + + attributes = nautilus_icon_factory_get_required_file_attributes (); + attributes |= NAUTILUS_FILE_ATTRIBUTE_IS_DIRECTORY | + NAUTILUS_FILE_ATTRIBUTE_DISPLAY_NAME; + + return attributes; +} + +static void +start_monitoring_directory (FMTreeModel *model, TreeNode *node) +{ + NautilusDirectory *directory; + NautilusFileAttributes attributes; + + if (node->done_loading_id != 0) { + return; + } + + g_assert (node->files_added_id == 0); + g_assert (node->files_changed_id == 0); + + directory = node->directory; + + node->done_loading_id = g_signal_connect + (directory, "done_loading", + G_CALLBACK (done_loading_callback), node->root); + node->files_added_id = g_signal_connect + (directory, "files_added", + G_CALLBACK (files_changed_callback), node->root); + node->files_changed_id = g_signal_connect + (directory, "files_changed", + G_CALLBACK (files_changed_callback), node->root); + + set_done_loading (model, node, nautilus_directory_are_all_files_seen (directory)); + + attributes = get_tree_monitor_attributes (); + nautilus_directory_file_monitor_add (directory, model, + model->details->show_hidden_files, + model->details->show_backup_files, + attributes, files_changed_callback, node->root); +} + +static int +fm_tree_model_get_n_columns (GtkTreeModel *model) +{ + return FM_TREE_MODEL_NUM_COLUMNS; +} + +static GType +fm_tree_model_get_column_type (GtkTreeModel *model, int index) +{ + switch (index) { + case FM_TREE_MODEL_DISPLAY_NAME_COLUMN: + return G_TYPE_STRING; + case FM_TREE_MODEL_CLOSED_PIXBUF_COLUMN: + return GDK_TYPE_PIXBUF; + case FM_TREE_MODEL_OPEN_PIXBUF_COLUMN: + return GDK_TYPE_PIXBUF; + case FM_TREE_MODEL_FONT_STYLE_COLUMN: + return PANGO_TYPE_STYLE; + case FM_TREE_MODEL_FONT_WEIGHT_COLUMN: + return PANGO_TYPE_WEIGHT; + default: + g_assert_not_reached (); + } + + return G_TYPE_INVALID; +} + +static gboolean +iter_is_valid (FMTreeModel *model, const GtkTreeIter *iter) +{ + TreeNode *node, *parent; + + if (iter->stamp != model->details->stamp) { + return FALSE; + } + + node = iter->user_data; + parent = iter->user_data2; + if (node == NULL) { + if (parent != NULL) { + if (!NAUTILUS_IS_FILE (parent->file)) { + return FALSE; + } + if (!tree_node_has_dummy_child (parent)) { + return FALSE; + } + } + } else { + if (!NAUTILUS_IS_FILE (node->file)) { + return FALSE; + } + if (parent != NULL) { + return FALSE; + } + } + if (iter->user_data3 != NULL) { + return FALSE; + } + return TRUE; +} + +static gboolean +fm_tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path) +{ + int *indices; + GtkTreeIter parent; + int depth, i; + + indices = gtk_tree_path_get_indices (path); + depth = gtk_tree_path_get_depth (path); + + if (! gtk_tree_model_iter_nth_child (model, iter, NULL, indices[0])) { + return FALSE; + } + + for (i = 1; i < depth; i++) { + parent = *iter; + + if (! gtk_tree_model_iter_nth_child (model, iter, &parent, indices[i])) { + return FALSE; + } + } + + return TRUE; +} + +static GtkTreePath * +fm_tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) +{ + FMTreeModel *tree_model; + TreeNode *node, *parent, *cnode; + GtkTreePath *path; + GtkTreeIter parent_iter; + int i; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), NULL); + tree_model = FM_TREE_MODEL (model); + g_return_val_if_fail (iter_is_valid (tree_model, iter), NULL); + + node = iter->user_data; + if (node == NULL) { + parent = iter->user_data2; + if (parent == NULL) { + return gtk_tree_path_new (); + } + } else { + parent = node->parent; + if (parent == NULL) { + i = 0; + for (cnode = tree_model->details->root_node; cnode != node; cnode = cnode->next) { + i++; + } + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, i); + return path; + } + } + + parent_iter.stamp = iter->stamp; + parent_iter.user_data = parent; + parent_iter.user_data2 = NULL; + parent_iter.user_data3 = NULL; + + path = fm_tree_model_get_path (model, &parent_iter); + + gtk_tree_path_append_index (path, tree_node_get_child_index (parent, node)); + + return path; +} + +static void +fm_tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, int column, GValue *value) +{ + TreeNode *node, *parent; + + g_return_if_fail (FM_IS_TREE_MODEL (model)); + g_return_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter)); + + node = iter->user_data; + + switch (column) { + case FM_TREE_MODEL_DISPLAY_NAME_COLUMN: + g_value_init (value, G_TYPE_STRING); + if (node == NULL) { + parent = iter->user_data2; + g_value_set_static_string (value, parent->done_loading + ? _("(Empty)") : _("Loading...")); + } else { + g_value_set_string (value, tree_node_get_display_name (node)); + } + break; + case FM_TREE_MODEL_CLOSED_PIXBUF_COLUMN: + g_value_init (value, GDK_TYPE_PIXBUF); + g_value_set_object (value, node == NULL ? NULL : tree_node_get_closed_pixbuf (node)); + break; + case FM_TREE_MODEL_OPEN_PIXBUF_COLUMN: + g_value_init (value, GDK_TYPE_PIXBUF); + g_value_set_object (value, node == NULL ? NULL : tree_node_get_open_pixbuf (node)); + break; + case FM_TREE_MODEL_FONT_STYLE_COLUMN: + g_value_init (value, PANGO_TYPE_STYLE); + if (node == NULL) { + g_value_set_enum (value, PANGO_STYLE_ITALIC); + } else { + g_value_set_enum (value, PANGO_STYLE_NORMAL); + } + break; + case FM_TREE_MODEL_FONT_WEIGHT_COLUMN: + g_value_init (value, PANGO_TYPE_STYLE); + if (node != NULL && node->parent == NULL) { + g_value_set_enum (value, PANGO_WEIGHT_BOLD); + } else { + g_value_set_enum (value, PANGO_WEIGHT_NORMAL); + } + break; + default: + g_assert_not_reached (); + } +} + +static gboolean +fm_tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter) +{ + TreeNode *node, *parent, *next; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); + g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter), FALSE); + + node = iter->user_data; + + if (node == NULL) { + parent = iter->user_data2; + next = parent->first_child; + } else { + next = node->next; + } + + return make_iter_for_node (next, iter, iter->stamp); +} + +static gboolean +fm_tree_model_iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent_iter) +{ + TreeNode *parent; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); + g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), parent_iter), FALSE); + + parent = parent_iter->user_data; + if (parent == NULL) { + return make_iter_invalid (iter); + } + + if (tree_node_has_dummy_child (parent)) { + return make_iter_for_dummy_row (parent, iter, parent_iter->stamp); + } + return make_iter_for_node (parent->first_child, iter, parent_iter->stamp); +} + +static gboolean +fm_tree_model_iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child_iter) +{ TreeNode *child, *parent; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); + g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), child_iter), FALSE); + + child = child_iter->user_data; + + if (child == NULL) { + parent = child_iter->user_data2; + } else { + parent = child->parent; + } + + return make_iter_for_node (parent, iter, child_iter->stamp); +} + +static gboolean +fm_tree_model_iter_has_child (GtkTreeModel *model, GtkTreeIter *iter) +{ + gboolean has_child; + TreeNode *node; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); + g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter), FALSE); + + node = iter->user_data; + + has_child = node != NULL && node->directory != NULL; + +#if 0 + g_warning ("Node '%s' %s", + node && node->file ? nautilus_file_get_uri (node->file) : "no name", + has_child ? "has child" : "no child"); +#endif + + return has_child; +} + +static int +fm_tree_model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter) +{ + FMTreeModel *tree_model; + TreeNode *parent, *node; + int n; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); + g_return_val_if_fail (iter == NULL || iter_is_valid (FM_TREE_MODEL (model), iter), FALSE); + + tree_model = FM_TREE_MODEL (model); + + if (iter == NULL) { + return 1; + } + + parent = iter->user_data; + if (parent == NULL) { + return 0; + } + + n = tree_node_has_dummy_child (parent) ? 1 : 0; + for (node = parent->first_child; node != NULL; node = node->next) { + n++; + } + + return n; +} + +static gboolean +fm_tree_model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, + GtkTreeIter *parent_iter, int n) +{ + FMTreeModel *tree_model; + TreeNode *parent, *node; + int i; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); + g_return_val_if_fail (parent_iter == NULL + || iter_is_valid (FM_TREE_MODEL (model), parent_iter), FALSE); + + tree_model = FM_TREE_MODEL (model); + + if (parent_iter == NULL) { + node = tree_model->details->root_node; + for (i = 0; i < n && node != NULL; i++, node = node->next); + return make_iter_for_node (node, iter, + tree_model->details->stamp); + } + + parent = parent_iter->user_data; + if (parent == NULL) { + return make_iter_invalid (iter); + } + + i = tree_node_has_dummy_child (parent) ? 1 : 0; + if (n == 0 && i == 1) { + return make_iter_for_dummy_row (parent, iter, parent_iter->stamp); + } + for (node = parent->first_child; i != n; i++, node = node->next) { + if (node == NULL) { + return make_iter_invalid (iter); + } + } + + return make_iter_for_node (node, iter, parent_iter->stamp); +} + +static void +update_monitoring (FMTreeModel *model, TreeNode *node) +{ + TreeNode *child; + + if (node->all_children_ref_count == 0) { + stop_monitoring_directory (model, node); + destroy_children (model, node); + } else { + for (child = node->first_child; child != NULL; child = child->next) { + update_monitoring (model, child); + } + start_monitoring_directory (model, node); + } +} + +static gboolean +update_monitoring_idle_callback (gpointer callback_data) +{ + FMTreeModel *model; + TreeNode *node; + + model = FM_TREE_MODEL (callback_data); + model->details->monitoring_update_idle_id = 0; + for (node = model->details->root_node; node != NULL; node = node->next) { + update_monitoring (model, node); + } + return FALSE; +} + +static void +schedule_monitoring_update (FMTreeModel *model) +{ + if (model->details->monitoring_update_idle_id == 0) { + model->details->monitoring_update_idle_id = + g_idle_add (update_monitoring_idle_callback, model); + } +} + +static void +stop_monitoring_directory_and_children (FMTreeModel *model, TreeNode *node) +{ + TreeNode *child; + + stop_monitoring_directory (model, node); + for (child = node->first_child; child != NULL; child = child->next) { + stop_monitoring_directory_and_children (model, child); + } +} + +static void +stop_monitoring (FMTreeModel *model) +{ + TreeNode *node; + + for (node = model->details->root_node; node != NULL; node = node->next) { + stop_monitoring_directory_and_children (model, node); + } +} + +static void +fm_tree_model_ref_node (GtkTreeModel *model, GtkTreeIter *iter) +{ + TreeNode *node, *parent; +#if LOG_REF_COUNTS + char *uri; +#endif + + g_return_if_fail (FM_IS_TREE_MODEL (model)); + g_return_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter)); + + node = iter->user_data; + if (node == NULL) { + parent = iter->user_data2; + g_assert (parent->dummy_child_ref_count >= 0); + ++parent->dummy_child_ref_count; + } else { + parent = node->parent; + g_assert (node->ref_count >= 0); + ++node->ref_count; + } + + if (parent != NULL) { + g_assert (parent->all_children_ref_count >= 0); + if (++parent->all_children_ref_count == 1) { + if (parent->first_child == NULL) { + parent->done_loading = FALSE; + } + schedule_monitoring_update (FM_TREE_MODEL (model)); + } +#if LOG_REF_COUNTS + uri = get_node_uri (iter); + g_message ("ref of %s, count is now %d", + uri, parent->all_children_ref_count); + g_free (uri); +#endif + } +} + +static void +fm_tree_model_unref_node (GtkTreeModel *model, GtkTreeIter *iter) +{ + TreeNode *node, *parent; +#if LOG_REF_COUNTS + char *uri; +#endif + + g_return_if_fail (FM_IS_TREE_MODEL (model)); + g_return_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter)); + + node = iter->user_data; + if (node == NULL) { + parent = iter->user_data2; + g_assert (parent->dummy_child_ref_count > 0); + --parent->dummy_child_ref_count; + } else { + parent = node->parent; + g_assert (node->ref_count > 0); + --node->ref_count; + } + + if (parent != NULL) { + g_assert (parent->all_children_ref_count > 0); +#if LOG_REF_COUNTS + uri = get_node_uri (iter); + g_message ("unref of %s, count is now %d", + uri, parent->all_children_ref_count - 1); + g_free (uri); +#endif + if (--parent->all_children_ref_count == 0) { + schedule_monitoring_update (FM_TREE_MODEL (model)); + } + } +} + +static void +root_node_file_changed_callback (NautilusFile *file, FMTreeModelRoot *root) +{ + if (root->root_node != NULL) { + update_node (root->model, root->root_node); + } +} + +void +fm_tree_model_add_root_uri (FMTreeModel *model, const char *root_uri, const char *display_name, const char *icon_name) +{ + NautilusFile *file; + TreeNode *node, *cnode; + NautilusFileAttributes attributes; + FMTreeModelRoot *newroot; + + file = nautilus_file_get (root_uri); + + newroot = tree_model_root_new (model); + node = create_node_for_file (newroot, file); + node->display_name = g_strdup (display_name); + node->icon_name = g_strdup (icon_name); + newroot->root_node = node; + node->parent = NULL; + if (model->details->root_node == NULL) { + model->details->root_node = node; + } else { + /* append it */ + for (cnode = model->details->root_node; cnode->next != NULL; cnode = cnode->next); + cnode->next = node; + node->prev = cnode; + } + + newroot->changed_handler_id = g_signal_connect (node->file, "changed", + G_CALLBACK (root_node_file_changed_callback), + node->root); + + attributes = get_tree_monitor_attributes (); + nautilus_file_monitor_add (file, model, attributes); + + nautilus_file_unref (file); + + update_node_without_reporting (model, node); + report_node_inserted (model, node); +} + +void +fm_tree_model_remove_root_uri (FMTreeModel *model, const char *uri) +{ + TreeNode *node; + GtkTreePath *path; + FMTreeModelRoot *root; + NautilusFile *file; + + file = nautilus_file_get (uri); + for (node = model->details->root_node; node != NULL; node = node->next) { + if (file == node->file) { + break; + } + } + nautilus_file_unref (file); + + if (node) { + /* remove the node */ + nautilus_file_monitor_remove (node->file, model); + path = get_node_path (model, node); + + if (node->prev) { + node->prev->next = node->next; + } + if (node->next) { + node->next->prev = node->prev; + } + if (node == model->details->root_node) { + model->details->root_node = node->next; + } + + /* destroy the root identifier */ + root = node->root; + destroy_node_without_reporting (model, node); + g_hash_table_destroy (root->file_to_node_map); + g_free (root); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); + } +} + +FMTreeModel * +fm_tree_model_new (void) +{ + FMTreeModel *model; + + model = g_object_new (FM_TYPE_TREE_MODEL, NULL); + + return model; +} + +static void +set_theme (TreeNode *node, FMTreeModel *model) +{ + TreeNode *child; + + tree_node_update_closed_pixbuf (node); + tree_node_update_open_pixbuf (node); + + report_node_contents_changed (model, node); + + for (child = node->first_child; child != NULL; child = child->next) { + set_theme (child, model); + } +} + +void +fm_tree_model_set_theme (FMTreeModel *model) +{ + TreeNode *node; + + g_return_if_fail (FM_IS_TREE_MODEL (model)); + + node = model->details->root_node; + while (node != NULL) { + set_theme (node, model); + node = node->next; + } +} + + +void +fm_tree_model_set_show_hidden_files (FMTreeModel *model, + gboolean show_hidden_files) +{ + g_return_if_fail (FM_IS_TREE_MODEL (model)); + g_return_if_fail (show_hidden_files == FALSE || show_hidden_files == TRUE); + + show_hidden_files = show_hidden_files != FALSE; + if (model->details->show_hidden_files == show_hidden_files) { + return; + } + model->details->show_hidden_files = show_hidden_files; + stop_monitoring (model); + if (!show_hidden_files) { + destroy_by_function (model, nautilus_file_is_hidden_file); + } + schedule_monitoring_update (model); +} + +void +fm_tree_model_set_show_backup_files (FMTreeModel *model, + gboolean show_backup_files) +{ + g_return_if_fail (FM_IS_TREE_MODEL (model)); + g_return_if_fail (show_backup_files == FALSE || show_backup_files == TRUE); + + show_backup_files = show_backup_files != FALSE; + if (model->details->show_backup_files == show_backup_files) { + return; + } + model->details->show_backup_files = show_backup_files; + stop_monitoring (model); + if (!show_backup_files) { + destroy_by_function (model, nautilus_file_is_backup_file); + } + schedule_monitoring_update (model); +} + +static gboolean +file_is_not_directory (NautilusFile *file) +{ + return !nautilus_file_is_directory (file); +} + +void +fm_tree_model_set_show_only_directories (FMTreeModel *model, + gboolean show_only_directories) +{ + g_return_if_fail (FM_IS_TREE_MODEL (model)); + g_return_if_fail (show_only_directories == FALSE || show_only_directories == TRUE); + + show_only_directories = show_only_directories != FALSE; + if (model->details->show_only_directories == show_only_directories) { + return; + } + model->details->show_only_directories = show_only_directories; + stop_monitoring (model); + if (show_only_directories) { + destroy_by_function (model, file_is_not_directory); + } + schedule_monitoring_update (model); +} + +NautilusFile * +fm_tree_model_iter_get_file (FMTreeModel *model, GtkTreeIter *iter) +{ + TreeNode *node; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), 0); + g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter), 0); + + node = iter->user_data; + return node == NULL ? NULL : nautilus_file_ref (node->file); +} + +gboolean +fm_tree_model_iter_is_root (FMTreeModel *model, GtkTreeIter *iter) +{ + TreeNode *node; + + g_return_val_if_fail (FM_IS_TREE_MODEL (model), 0); + g_return_val_if_fail (iter_is_valid (model, iter), 0); + node = iter->user_data; + if (node == NULL) { + return FALSE; + } else { + return (node->parent == NULL); + } +} + +gboolean +fm_tree_model_file_get_iter (FMTreeModel *model, + GtkTreeIter *iter, + NautilusFile *file, + GtkTreeIter *current_iter) +{ + TreeNode *node, *root_node; + + if (current_iter != NULL && current_iter->user_data != NULL) { + node = get_node_from_file (((TreeNode *) current_iter->user_data)->root, file); + return make_iter_for_node (node, iter, model->details->stamp); + } + + for (root_node = model->details->root_node; root_node != NULL; root_node = root_node->next) { + node = get_node_from_file (root_node->root, file); + if (node != NULL) { + return make_iter_for_node (node, iter, model->details->stamp); + } + } + return FALSE; +} + +static void +fm_tree_model_init (FMTreeModel *model) +{ + model->details = g_new0 (FMTreeModelDetails, 1); + + do { + model->details->stamp = g_random_int (); + } while (model->details->stamp == 0); +} + +static void +fm_tree_model_finalize (GObject *object) +{ + FMTreeModel *model; + TreeNode *root_node, *next_root; + FMTreeModelRoot *root; + + model = FM_TREE_MODEL (object); + + for (root_node = model->details->root_node; root_node != NULL; root_node = next_root) { + next_root = root_node->next; + root = root_node->root; + g_signal_handler_disconnect (root_node->file, root->changed_handler_id); + nautilus_file_monitor_remove (root_node->file, model); + destroy_node_without_reporting (model, root_node); + g_hash_table_destroy (root->file_to_node_map); + g_free (root); + } + + if (model->details->monitoring_update_idle_id != 0) { + g_source_remove (model->details->monitoring_update_idle_id); + } + + g_free (model->details); + + parent_class->finalize (object); +} + +static void +fm_tree_model_class_init (FMTreeModelClass *class) +{ + parent_class = g_type_class_peek_parent (class); + + G_OBJECT_CLASS (class)->finalize = fm_tree_model_finalize; + + tree_model_signals[ROW_LOADED] = + g_signal_new ("row_loaded", + FM_TYPE_TREE_MODEL, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FMTreeModelClass, row_loaded), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + GTK_TYPE_TREE_ITER); +} + +static void +fm_tree_model_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = fm_tree_model_get_flags; + iface->get_n_columns = fm_tree_model_get_n_columns; + iface->get_column_type = fm_tree_model_get_column_type; + iface->get_iter = fm_tree_model_get_iter; + iface->get_path = fm_tree_model_get_path; + iface->get_value = fm_tree_model_get_value; + iface->iter_next = fm_tree_model_iter_next; + iface->iter_children = fm_tree_model_iter_children; + iface->iter_has_child = fm_tree_model_iter_has_child; + iface->iter_n_children = fm_tree_model_iter_n_children; + iface->iter_nth_child = fm_tree_model_iter_nth_child; + iface->iter_parent = fm_tree_model_iter_parent; + iface->ref_node = fm_tree_model_ref_node; + iface->unref_node = fm_tree_model_unref_node; +} + +GType +fm_tree_model_get_type (void) +{ + static GType object_type = 0; + + if (object_type == 0) { + static const GTypeInfo object_info = { + sizeof (FMTreeModelClass), + NULL, + NULL, + (GClassInitFunc) fm_tree_model_class_init, + NULL, + NULL, + sizeof (FMTreeModel), + 0, + (GInstanceInitFunc) fm_tree_model_init, + }; + + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) fm_tree_model_tree_model_init, + NULL, + NULL + }; + + object_type = g_type_register_static (G_TYPE_OBJECT, "FMTreeModel", &object_info, 0); + g_type_add_interface_static (object_type, + GTK_TYPE_TREE_MODEL, + &tree_model_info); + } + + return object_type; +} diff --git a/src/file-manager/fm-tree-model.h b/src/file-manager/fm-tree-model.h new file mode 100644 index 000000000..5d7146de5 --- /dev/null +++ b/src/file-manager/fm-tree-model.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * Copyright (C) 2002 Anders Carlsson + * Copyright (C) 2002 Bent Spoon Software + * + * 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: Anders Carlsson <andersca@gnu.org> + */ + +/* fm-tree-model.h - Model for the tree view */ + +#ifndef FM_TREE_MODEL_H +#define FM_TREE_MODEL_H + +#include <glib-object.h> +#include <gtk/gtktreemodel.h> +#include <libnautilus-private/nautilus-file.h> + +#define FM_TYPE_TREE_MODEL (fm_tree_model_get_type ()) +#define FM_TREE_MODEL(obj) (GTK_CHECK_CAST ((obj), FM_TYPE_TREE_MODEL, FMTreeModel)) +#define FM_TREE_MODEL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), FM_TYPE_TREE_MODEL, FMTreeModelClass)) +#define FM_IS_TREE_MODEL(obj) (GTK_CHECK_TYPE ((obj), FM_TYPE_TREE_MODEL)) +#define FM_IS_TREE_MODEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), FM_TYPE_TREE_MODEL)) + +enum { + FM_TREE_MODEL_DISPLAY_NAME_COLUMN, + FM_TREE_MODEL_CLOSED_PIXBUF_COLUMN, + FM_TREE_MODEL_OPEN_PIXBUF_COLUMN, + FM_TREE_MODEL_FONT_STYLE_COLUMN, + FM_TREE_MODEL_FONT_WEIGHT_COLUMN, + FM_TREE_MODEL_NUM_COLUMNS +}; + +typedef struct FMTreeModelDetails FMTreeModelDetails; + +typedef struct { + GObject parent; + FMTreeModelDetails *details; +} FMTreeModel; + +typedef struct { + GObjectClass parent_class; + + void (* row_loaded) (FMTreeModel *tree_model, + GtkTreeIter *iter); +} FMTreeModelClass; + +GType fm_tree_model_get_type (void); +FMTreeModel *fm_tree_model_new (void); +void fm_tree_model_set_show_hidden_files (FMTreeModel *model, + gboolean show_hidden_files); +void fm_tree_model_set_show_backup_files (FMTreeModel *model, + gboolean show_backup_files); +void fm_tree_model_set_show_only_directories (FMTreeModel *model, + gboolean show_only_directories); +NautilusFile * fm_tree_model_iter_get_file (FMTreeModel *model, + GtkTreeIter *iter); +void fm_tree_model_add_root_uri (FMTreeModel *model, + const char *root_uri, + const char *display_name, + const char *icon_name); +void fm_tree_model_remove_root_uri (FMTreeModel *model, + const char *root_uri); +gboolean fm_tree_model_iter_is_root (FMTreeModel *model, + GtkTreeIter *iter); +gboolean fm_tree_model_file_get_iter (FMTreeModel *model, + GtkTreeIter *iter, + NautilusFile *file, + GtkTreeIter *currentIter); + +void fm_tree_model_set_theme (FMTreeModel *model); + +#endif /* FM_TREE_MODEL_H */ diff --git a/src/file-manager/fm-tree-view.c b/src/file-manager/fm-tree-view.c new file mode 100644 index 000000000..9e556a1ab --- /dev/null +++ b/src/file-manager/fm-tree-view.c @@ -0,0 +1,1305 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * Copyright (C) 2000, 2001 Eazel, Inc + * Copyright (C) 2002 Anders Carlsson + * Copyright (C) 2002 Darin Adler + * + * 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. + * + * Authors: + * Maciej Stachowiak <mjs@eazel.com> + * Anders Carlsson <andersca@gnu.org> + * Darin Adler <darin@bentspoon.com> + */ + +/* fm-tree-view.c - tree sidebar panel + */ + +#include <config.h> +#include "fm-tree-view.h" + +#include "fm-tree-model.h" +#include "fm-properties-window.h" +#include <eel/eel-glib-extensions.h> +#include <eel/eel-preferences.h> +#include <eel/eel-string.h> +#include <eel/eel-vfs-extensions.h> +#include <gtk/gtkcellrendererpixbuf.h> +#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtkscrolledwindow.h> +#include <gtk/gtktreemodelsort.h> +#include <gtk/gtktreeselection.h> +#include <gtk/gtktreeview.h> +#include <gtk/gtkstock.h> +#include <gtk/gtkimage.h> +#include <gtk/gtkimagemenuitem.h> +#include <gtk/gtkseparatormenuitem.h> +#include <gtk/gtkmenu.h> +#include <gtk/gtkmenushell.h> +#include <gtk/gtkclipboard.h> +#include <libgnome/gnome-i18n.h> +#include <libgnomeui/gnome-uidefs.h> +#include <libgnomeui/gnome-popup-menu.h> +#include <libgnomevfs/gnome-vfs-utils.h> +#include <libgnomevfs/gnome-vfs-volume-monitor.h> +#include <libnautilus-private/nautilus-file-attributes.h> +#include <libnautilus-private/nautilus-file-operations.h> +#include <libnautilus-private/nautilus-global-preferences.h> +#include <libnautilus-private/nautilus-program-choosing.h> +#include <libnautilus-private/nautilus-tree-view-drag-dest.h> +#include <libnautilus-private/nautilus-icon-factory.h> + +struct FMTreeViewDetails { + GtkWidget *scrolled_window; + GtkTreeView *tree_widget; + GtkTreeModelSort *sort_model; + FMTreeModel *child_model; + + NautilusFile *activation_file; + gboolean activation_in_new_window; + + NautilusTreeViewDragDest *drag_dest; + + char *selection_location; + gboolean selecting; + + guint show_selection_idle_id; + + GtkWidget *popup; + GtkWidget *popup_open; + GtkWidget *popup_open_in_new_window; + GtkWidget *popup_create_folder; + GtkWidget *popup_cut; + GtkWidget *popup_copy; + GtkWidget *popup_paste; + GtkWidget *popup_rename; + GtkWidget *popup_trash; + GtkWidget *popup_properties; + NautilusFile *popup_file; +}; + +typedef struct { + GList *uris; + FMTreeView *view; +} PrependURIParameters; + +static GdkAtom copied_files_atom; + +enum { + GNOME_COPIED_FILES +}; + +static const GtkTargetEntry clipboard_targets[] = { + { "x-special/gnome-copied-files", 0, GNOME_COPIED_FILES }, +}; + +BONOBO_CLASS_BOILERPLATE (FMTreeView, fm_tree_view, + NautilusView, NAUTILUS_TYPE_VIEW) + +static gboolean +show_iter_for_file (FMTreeView *view, NautilusFile *file, GtkTreeIter *iter) +{ + GtkTreeModel *model; + NautilusFile *parent_file; + GtkTreeIter parent_iter; + GtkTreePath *path, *sort_path; + GtkTreeIter cur_iter; + + if (view->details->child_model == NULL) { + return FALSE; + } + model = GTK_TREE_MODEL (view->details->child_model); + + /* check if file is visible in the same root as the currently selected folder is */ + gtk_tree_view_get_cursor (view->details->tree_widget, &path, NULL); + if (path != NULL) { + if (gtk_tree_model_get_iter (model, &cur_iter, path)) { + if (fm_tree_model_file_get_iter (view->details->child_model, + iter, file, &cur_iter)) { + return TRUE; + } + } + } + /* check if file is visible at all */ + if (fm_tree_model_file_get_iter (view->details->child_model, + iter, file, NULL)) { + return TRUE; + } + + parent_file = nautilus_file_get_parent (file); + + if (parent_file == NULL) { + return FALSE; + } + if (!show_iter_for_file (view, parent_file, &parent_iter)) { + nautilus_file_unref (parent_file); + return FALSE; + } + nautilus_file_unref (parent_file); + + if (parent_iter.user_data == NULL || parent_iter.stamp == 0) { + return FALSE; + } + path = gtk_tree_model_get_path (model, &parent_iter); + sort_path = gtk_tree_model_sort_convert_child_path_to_path + (view->details->sort_model, path); + gtk_tree_path_free (path); + gtk_tree_view_expand_row (view->details->tree_widget, sort_path, FALSE); + gtk_tree_path_free (sort_path); + + return FALSE; +} + +static gboolean +show_selection_idle_callback (gpointer callback_data) +{ + FMTreeView *view; + NautilusFile *file, *old_file; + GtkTreeIter iter; + GtkTreePath *path, *sort_path; + + view = FM_TREE_VIEW (callback_data); + + view->details->show_selection_idle_id = 0; + + file = nautilus_file_get (view->details->selection_location); + if (file == NULL) { + return FALSE; + } + + if (!nautilus_file_is_directory (file)) { + old_file = file; + file = nautilus_file_get_parent (file); + nautilus_file_unref (old_file); + if (file == NULL) { + return FALSE; + } + } + + view->details->selecting = TRUE; + if (!show_iter_for_file (view, file, &iter)) { + nautilus_file_unref (file); + return FALSE; + } + view->details->selecting = FALSE; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->child_model), &iter); + sort_path = gtk_tree_model_sort_convert_child_path_to_path + (view->details->sort_model, path); + gtk_tree_path_free (path); + gtk_tree_view_set_cursor (view->details->tree_widget, sort_path, NULL, FALSE); + gtk_tree_view_scroll_to_cell (view->details->tree_widget, sort_path, NULL, FALSE, 0, 0); + gtk_tree_path_free (sort_path); + + nautilus_file_unref (file); + + return FALSE; +} + +static void +schedule_show_selection (FMTreeView *view) +{ + if (view->details->show_selection_idle_id == 0) { + view->details->show_selection_idle_id = g_idle_add (show_selection_idle_callback, view); + } +} + +static void +row_loaded_callback (GtkTreeModel *tree_model, + GtkTreeIter *iter, + FMTreeView *view) +{ + NautilusFile *file, *tmp_file, *selection_file; + + if (view->details->selection_location == NULL + || !view->details->selecting + || iter->user_data == NULL || iter->stamp == 0) { + return; + } + + file = fm_tree_model_iter_get_file (view->details->child_model, iter); + if (file == NULL) { + return; + } + if (!nautilus_file_is_directory (file)) { + nautilus_file_unref(file); + return; + } + + /* if iter is ancestor of wanted selection_location then update selection */ + selection_file = nautilus_file_get (view->details->selection_location); + while (selection_file != NULL) { + if (file == selection_file) { + nautilus_file_unref (file); + nautilus_file_unref (selection_file); + + schedule_show_selection (view); + return; + } + tmp_file = nautilus_file_get_parent (selection_file); + nautilus_file_unref (selection_file); + selection_file = tmp_file; + } + nautilus_file_unref (file); +} + +static NautilusFile * +sort_model_iter_to_file (FMTreeView *view, GtkTreeIter *iter) +{ + GtkTreeIter child_iter; + + gtk_tree_model_sort_convert_iter_to_child_iter (view->details->sort_model, &child_iter, iter); + return fm_tree_model_iter_get_file (view->details->child_model, &child_iter); +} + +static NautilusFile * +sort_model_path_to_file (FMTreeView *view, GtkTreePath *path) +{ + GtkTreeIter iter; + + if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->sort_model), &iter, path)) { + return NULL; + } + return sort_model_iter_to_file (view, &iter); +} + +static void +got_activation_uri_callback (NautilusFile *file, gpointer callback_data) +{ + char *uri, *file_uri; + FMTreeView *view; + GdkScreen *screen; + Nautilus_ViewFrame_OpenMode mode; + + view = FM_TREE_VIEW (callback_data); + + screen = gtk_widget_get_screen (GTK_WIDGET (view->details->tree_widget)); + + g_assert (file == view->details->activation_file); + + mode = view->details->activation_in_new_window ? Nautilus_ViewFrame_OPEN_IN_NAVIGATION : Nautilus_ViewFrame_OPEN_ACCORDING_TO_MODE; + + /* FIXME: reenable && !eel_uris_match_ignore_fragments (view->details->current_main_view_uri, uri) */ + + uri = nautilus_file_get_activation_uri (file); + if (uri != NULL + && eel_str_has_prefix (uri, NAUTILUS_COMMAND_SPECIFIER)) { + + uri += strlen (NAUTILUS_COMMAND_SPECIFIER); + nautilus_launch_application_from_command (screen, NULL, uri, NULL, FALSE); + + } else if (uri != NULL + && eel_str_has_prefix (uri, NAUTILUS_DESKTOP_COMMAND_SPECIFIER)) { + + file_uri = nautilus_file_get_uri (file); + nautilus_launch_desktop_file (screen, file_uri, NULL, NULL); + g_free (file_uri); + + } else if (uri != NULL + && nautilus_file_is_executable (file) + && nautilus_file_can_execute (file) + && !nautilus_file_is_directory (file)) { + + file_uri = gnome_vfs_get_local_path_from_uri (uri); + + /* Non-local executables don't get launched. They act like non-executables. */ + if (file_uri == NULL) { + nautilus_view_open_location + (NAUTILUS_VIEW (view), + uri, + mode, + 0, + NULL); + } else { + nautilus_launch_application_from_command (screen, NULL, file_uri, NULL, FALSE); + g_free (file_uri); + } + + } else if (uri != NULL) { + if (view->details->selection_location == NULL || + strcmp (uri, view->details->selection_location) != 0) { + if (view->details->selection_location != NULL) { + g_free (view->details->selection_location); + } + view->details->selection_location = g_strdup (uri); + nautilus_view_open_location + (NAUTILUS_VIEW (view), + uri, + mode, + 0, + NULL); + } + } + + g_free (uri); + nautilus_file_unref (view->details->activation_file); + view->details->activation_file = NULL; +} + +static void +cancel_activation (FMTreeView *view) +{ + if (view->details->activation_file == NULL) { + return; + } + + nautilus_file_cancel_call_when_ready + (view->details->activation_file, + got_activation_uri_callback, view); + nautilus_file_unref (view->details->activation_file); + view->details->activation_file = NULL; +} + +static void +row_activated_callback (GtkTreeView *treeview, GtkTreePath *path, + GtkTreeViewColumn *column, FMTreeView *view) +{ + if (gtk_tree_view_row_expanded (view->details->tree_widget, path)) { + gtk_tree_view_collapse_row (view->details->tree_widget, path); + } else { + gtk_tree_view_expand_row (view->details->tree_widget, + path, FALSE); + } +} + + +static void +selection_changed_callback (GtkTreeSelection *selection, + FMTreeView *view) +{ + NautilusFileAttributes attributes; + GtkTreeIter iter; + + /* no activation if popup menu is open */ + if (view->details->popup_file != NULL) { + return; + } + + cancel_activation (view); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { + return; + } + + view->details->activation_file = sort_model_iter_to_file (view, &iter); + if (view->details->activation_file == NULL) { + return; + } + view->details->activation_in_new_window = FALSE; + + attributes = NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI; + nautilus_file_call_when_ready (view->details->activation_file, attributes, + got_activation_uri_callback, view); +} + +static int +compare_rows (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer callback_data) +{ + NautilusFile *file_a, *file_b; + int result; + + if (a->user_data == NULL) { + return -1; + } + else if (b->user_data == NULL) { + return -1; + } + + /* don't sort root nodes */ + if (fm_tree_model_iter_is_root (FM_TREE_MODEL (model), a) + || fm_tree_model_iter_is_root (FM_TREE_MODEL (model), b)) { + return 0; + } + + file_a = fm_tree_model_iter_get_file (FM_TREE_MODEL (model), a); + file_b = fm_tree_model_iter_get_file (FM_TREE_MODEL (model), b); + + if (file_a == file_b) { + result = 0; + } else if (file_a == NULL) { + result = -1; + } else if (file_b == NULL) { + result = +1; + } else { + result = nautilus_file_compare_for_sort (file_a, file_b, + NAUTILUS_FILE_SORT_BY_DISPLAY_NAME, + FALSE, FALSE); + } + + nautilus_file_unref (file_a); + nautilus_file_unref (file_b); + + return result; +} + + +static char * +get_root_uri_callback (NautilusTreeViewDragDest *dest, + gpointer user_data) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (user_data); + + /* Don't allow drops on background */ + return NULL; +} + +static NautilusFile * +get_file_for_path_callback (NautilusTreeViewDragDest *dest, + GtkTreePath *path, + gpointer user_data) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (user_data); + + return sort_model_path_to_file (view, path); +} + +static void +move_copy_items_callback (NautilusTreeViewDragDest *dest, + const GList *item_uris, + const char *target_uri, + guint action, + int x, + int y, + gpointer user_data) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (user_data); + + nautilus_file_operations_copy_move + (item_uris, + NULL, + target_uri, + action, + GTK_WIDGET (view->details->tree_widget), + NULL, NULL); +} + +static void +theme_changed_callback (GObject *icon_factory, gpointer callback_data) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (callback_data); + if (view->details->child_model != NULL) { + fm_tree_model_set_theme (FM_TREE_MODEL (view->details->child_model)); + } +} + +static void +add_root_for_volume (FMTreeView *view, + GnomeVFSVolume *volume) +{ + char *icon, *mount_uri, *name; + + if (!gnome_vfs_volume_is_user_visible (volume)) { + return; + } + + icon = gnome_vfs_volume_get_icon (volume); + mount_uri = gnome_vfs_volume_get_activation_uri (volume); + name = gnome_vfs_volume_get_display_name (volume); + + fm_tree_model_add_root_uri (view->details->child_model, + mount_uri, name, icon); + + g_free (icon); + g_free (name); + g_free (mount_uri); + +} + +static void +volume_mounted_callback (GnomeVFSVolumeMonitor *volume_monitor, + GnomeVFSVolume *volume, + FMTreeView *view) +{ + add_root_for_volume (view, volume); +} + +static void +volume_unmounted_callback (GnomeVFSVolumeMonitor *volume_monitor, + GnomeVFSVolume *volume, + FMTreeView *view) +{ + char *mount_uri; + + mount_uri = gnome_vfs_volume_get_activation_uri (volume); + fm_tree_model_remove_root_uri (view->details->child_model, + mount_uri); + g_free (mount_uri); +} + +static void +clipboard_contents_received_callback (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (data); + + if (selection_data->type == copied_files_atom + && selection_data->length > 0) { + gtk_widget_set_sensitive (view->details->popup_paste, TRUE); + } +} + +static GtkClipboard * +get_clipboard (GtkWidget *widget) +{ + return gtk_clipboard_get_for_display (gtk_widget_get_display (widget), + GDK_SELECTION_CLIPBOARD); +} + +static gboolean +can_move_uri_to_trash (const char *file_uri_string) +{ + /* Return TRUE if we can get a trash directory on the same volume as this file. */ + GnomeVFSURI *file_uri; + GnomeVFSURI *directory_uri; + GnomeVFSURI *trash_dir_uri; + gboolean result; + + g_return_val_if_fail (file_uri_string != NULL, FALSE); + + file_uri = gnome_vfs_uri_new (file_uri_string); + + if (file_uri == NULL) { + return FALSE; + } + + /* FIXME: Why can't we just pass file_uri to gnome_vfs_find_directory? */ + directory_uri = gnome_vfs_uri_get_parent (file_uri); + gnome_vfs_uri_unref (file_uri); + + if (directory_uri == NULL) { + return FALSE; + } + + /* + * Create a new trash if needed but don't go looking for an old Trash. + * Passing 0 permissions as gnome-vfs would override the permissions + * passed with 700 while creating .Trash directory + */ + result = gnome_vfs_find_directory (directory_uri, GNOME_VFS_DIRECTORY_KIND_TRASH, + &trash_dir_uri, TRUE, FALSE, 0) == GNOME_VFS_OK; + if (result) { + gnome_vfs_uri_unref (trash_dir_uri); + } + gnome_vfs_uri_unref (directory_uri); + + return result; +} + +static gboolean +button_pressed_callback (GtkTreeView *treeview, GdkEventButton *event, + FMTreeView *view) +{ + GtkTreePath *path, *cursor_path; + char *uri; + + if (event->button == 3) { + if (!gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, + &path, NULL, NULL, NULL)) { + return FALSE; + } + + view->details->popup_file = sort_model_path_to_file (view, path); + if (view->details->popup_file == NULL) { + gtk_tree_path_free (path); + return FALSE; + } + gtk_tree_view_get_cursor (view->details->tree_widget, &cursor_path, NULL); + gtk_tree_view_set_cursor (view->details->tree_widget, path, NULL, FALSE); + gtk_tree_path_free (path); + uri = nautilus_file_get_uri (view->details->popup_file); + + gtk_widget_set_sensitive (view->details->popup_open_in_new_window, + nautilus_file_is_directory (view->details->popup_file)); + gtk_widget_set_sensitive (view->details->popup_create_folder, + nautilus_file_is_directory (view->details->popup_file) && + nautilus_file_can_write (view->details->popup_file)); + gtk_widget_set_sensitive (view->details->popup_paste, FALSE); + if (nautilus_file_is_directory (view->details->popup_file) && + nautilus_file_can_write (view->details->popup_file)) { + gtk_clipboard_request_contents (get_clipboard (GTK_WIDGET (view->details->tree_widget)), + copied_files_atom, + clipboard_contents_received_callback, view); + } + gtk_widget_set_sensitive (view->details->popup_trash, can_move_uri_to_trash (uri)); + g_free (uri); + + gnome_popup_menu_do_popup_modal (view->details->popup, + NULL, NULL, event, NULL, + GTK_WIDGET (treeview)); + + gtk_tree_view_set_cursor (view->details->tree_widget, cursor_path, NULL, FALSE); + gtk_tree_path_free (cursor_path); + + nautilus_file_unref (view->details->popup_file); + view->details->popup_file = NULL; + + return TRUE; + } + + return FALSE; +} + +static void +fm_tree_view_activate_file (FMTreeView *view, + NautilusFile *file, + gboolean open_in_new_window) +{ + NautilusFileAttributes attributes; + + cancel_activation (view); + + view->details->activation_file = nautilus_file_ref (file); + view->details->activation_in_new_window = open_in_new_window; + + attributes = NAUTILUS_FILE_ATTRIBUTE_ACTIVATION_URI; + nautilus_file_call_when_ready (view->details->activation_file, attributes, + got_activation_uri_callback, view); +} + +static void +fm_tree_view_open_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + fm_tree_view_activate_file (view, view->details->popup_file, FALSE); +} + +static void +fm_tree_view_open_in_new_window_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + fm_tree_view_activate_file (view, view->details->popup_file, TRUE); +} + +static void +new_folder_done (const char *new_folder_uri, gpointer data) +{ + GList *list; + + /* show the properties window for the newly created + * folder so the user can change its name + */ + list = g_list_prepend (NULL, nautilus_file_get (new_folder_uri)); + + fm_properties_window_present (list, GTK_WIDGET (data)); + + nautilus_file_list_free (list); +} + +static void +fm_tree_view_create_folder_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + char *parent_uri; + + parent_uri = nautilus_file_get_uri (view->details->popup_file); + nautilus_file_operations_new_folder (GTK_WIDGET (view->details->tree_widget), + parent_uri, + new_folder_done, view->details->tree_widget); + + g_free (parent_uri); +} + +static void +get_clipboard_callback (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer user_data_or_owner) +{ + char *str = user_data_or_owner; + + gtk_selection_data_set (selection_data, + copied_files_atom, + 8, + str, + strlen (str)); +} + +static void +clear_clipboard_callback (GtkClipboard *clipboard, + gpointer user_data_or_owner) +{ + g_free (user_data_or_owner); +} + +static char * +convert_file_to_string (NautilusFile *file, + gboolean cut) +{ + GString *uris; + char *uri, *result; + + uris = g_string_new (cut ? "cut" : "copy"); + + uri = nautilus_file_get_uri (file); + g_string_append_c (uris, '\n'); + g_string_append (uris, uri); + g_free (uri); + + result = uris->str; + g_string_free (uris, FALSE); + + return result; +} + +static void +copy_or_cut_files (FMTreeView *view, + gboolean cut) +{ + char *status_string, *name; + char *clipboard_string; + + clipboard_string = convert_file_to_string (view->details->popup_file, cut); + + gtk_clipboard_set_with_data (get_clipboard (GTK_WIDGET (view->details->tree_widget)), + clipboard_targets, G_N_ELEMENTS (clipboard_targets), + get_clipboard_callback, clear_clipboard_callback, + clipboard_string); + + name = nautilus_file_get_display_name (view->details->popup_file); + if (cut) { + status_string = g_strdup_printf (_("\"%s\" will be moved " + "if you select the Paste Files command"), + name); + } else { + status_string = g_strdup_printf (_("\"%s\" will be copied " + "if you select the Paste Files command"), + name); + } + g_free (name); + + nautilus_view_report_status (NAUTILUS_VIEW (view), + status_string); + g_free (status_string); +} + +static void +fm_tree_view_cut_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + copy_or_cut_files (view, TRUE); +} + +static void +fm_tree_view_copy_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + copy_or_cut_files (view, FALSE); +} + +static GList * +convert_lines_to_str_list (char **lines, gboolean *cut) +{ + int i; + GList *result; + + if (lines[0] == NULL) { + return NULL; + } + + if (strcmp (lines[0], "cut") == 0) { + *cut = TRUE; + } else if (strcmp (lines[0], "copy") == 0) { + *cut = FALSE; + } else { + return NULL; + } + + result = NULL; + for (i = 1; lines[i] != NULL; i++) { + result = g_list_prepend (result, g_strdup (lines[i])); + } + return g_list_reverse (result); +} + +static void +paste_clipboard_data (FMTreeView *view, + GtkSelectionData *selection_data, + char *destination_uri) +{ + char **lines; + gboolean cut; + GList *item_uris; + + if (selection_data->type != copied_files_atom + || selection_data->length <= 0) { + item_uris = NULL; + } else { + /* Not sure why it's legal to assume there's an extra byte + * past the end of the selection data that it's safe to write + * to. But gtk_editable_selection_received does this, so I + * think it is OK. + */ + selection_data->data[selection_data->length] = '\0'; + lines = g_strsplit (selection_data->data, "\n", 0); + item_uris = convert_lines_to_str_list (lines, &cut); + g_strfreev (lines); + } + + if (item_uris == NULL|| destination_uri == NULL) { + nautilus_view_report_status (NAUTILUS_VIEW (view), + _("There is nothing on the clipboard to paste.")); + } else { + nautilus_file_operations_copy_move + (item_uris, NULL, destination_uri, + cut ? GDK_ACTION_MOVE : GDK_ACTION_COPY, + GTK_WIDGET (view->details->tree_widget), + NULL, NULL); + } +} + +static void +paste_into_clipboard_received_callback (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) +{ + FMTreeView *view; + char *directory_uri; + + view = FM_TREE_VIEW (data); + + directory_uri = nautilus_file_get_uri (view->details->popup_file); + + paste_clipboard_data (view, selection_data, directory_uri); + + g_free (directory_uri); +} + +static void +fm_tree_view_paste_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + gtk_clipboard_request_contents (get_clipboard (GTK_WIDGET (view->details->tree_widget)), + copied_files_atom, + paste_into_clipboard_received_callback, view); +} + +static void +fm_tree_view_trash_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + GList *list; + char *directory_uri; + + directory_uri = nautilus_file_get_uri (view->details->popup_file); + + if (can_move_uri_to_trash (directory_uri)) + { + list = g_list_prepend (NULL, g_strdup (directory_uri)); + + nautilus_file_operations_copy_move (list, NULL, + EEL_TRASH_URI, GDK_ACTION_MOVE, GTK_WIDGET (view->details->tree_widget), + NULL, NULL); + } + + g_free (directory_uri); +} + +static void +fm_tree_view_properties_cb (GtkWidget *menu_item, + FMTreeView *view) +{ + GList *list; + + list = g_list_prepend (NULL, nautilus_file_ref (view->details->popup_file)); + + fm_properties_window_present (list, GTK_WIDGET (view->details->tree_widget)); + + nautilus_file_list_free (list); +} + +static void +create_popup_menu (FMTreeView *view) +{ + GtkWidget *popup, *menu_item, *menu_image, *separator_item; + + popup = gtk_menu_new (); + + /* add the "open" menu item */ + menu_image = gtk_image_new_from_stock (GTK_STOCK_OPEN, + GTK_ICON_SIZE_MENU); + gtk_widget_show (menu_image); + menu_item = gtk_image_menu_item_new_with_label (_("Open")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + menu_image); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_open_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_open = menu_item; + + /* add the "open in new window" menu item */ + menu_item = gtk_image_menu_item_new_with_label (_("Open in New Window")); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_open_in_new_window_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_open_in_new_window = menu_item; + + separator_item = gtk_separator_menu_item_new (); + gtk_widget_show (separator_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), separator_item); + + /* add the "create folder" menu item */ + menu_item = gtk_image_menu_item_new_with_label (_("Create Folder")); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_create_folder_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_create_folder = menu_item; + + separator_item = gtk_separator_menu_item_new (); + gtk_widget_show (separator_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), separator_item); + + /* add the "cut folder" menu item */ + menu_image = gtk_image_new_from_stock (GTK_STOCK_CUT, + GTK_ICON_SIZE_MENU); + gtk_widget_show (menu_image); + menu_item = gtk_image_menu_item_new_with_label (_("Cut Folder")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + menu_image); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_cut_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_cut = menu_item; + + /* add the "copy folder" menu item */ + menu_image = gtk_image_new_from_stock (GTK_STOCK_COPY, + GTK_ICON_SIZE_MENU); + gtk_widget_show (menu_image); + menu_item = gtk_image_menu_item_new_with_label (_("Copy Folder")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + menu_image); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_copy_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_copy = menu_item; + + /* add the "paste files into folder" menu item */ + menu_image = gtk_image_new_from_stock (GTK_STOCK_PASTE, + GTK_ICON_SIZE_MENU); + gtk_widget_show (menu_image); + menu_item = gtk_image_menu_item_new_with_label (_("Paste Files into Folder")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + menu_image); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_paste_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_paste = menu_item; + + separator_item = gtk_separator_menu_item_new (); + gtk_widget_show (separator_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), separator_item); + + /* add the "move to trash" menu item */ + menu_image = gtk_image_new_from_stock (GTK_STOCK_DELETE, + GTK_ICON_SIZE_MENU); + gtk_widget_show (menu_image); + menu_item = gtk_image_menu_item_new_with_label (_("Move to Trash")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + menu_image); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_trash_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_trash = menu_item; + + separator_item = gtk_separator_menu_item_new (); + gtk_widget_show (separator_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), separator_item); + + /* add the "properties" menu item */ + menu_image = gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, + GTK_ICON_SIZE_MENU); + gtk_widget_show (menu_image); + menu_item = gtk_image_menu_item_new_with_label (_("Properties")); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + menu_image); + g_signal_connect (menu_item, "activate", + G_CALLBACK (fm_tree_view_properties_cb), + view); + gtk_widget_show (menu_item); + gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); + view->details->popup_properties = menu_item; + + view->details->popup = popup; +} + +static void +create_tree (FMTreeView *view) +{ + GtkCellRenderer *cell; + GtkTreeViewColumn *column; + GnomeVFSVolumeMonitor *volume_monitor; + char *home_uri; + GList *volumes, *l; + + view->details->child_model = fm_tree_model_new (); + view->details->sort_model = GTK_TREE_MODEL_SORT + (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (view->details->child_model))); + view->details->tree_widget = GTK_TREE_VIEW + (gtk_tree_view_new_with_model (GTK_TREE_MODEL (view->details->sort_model))); + g_object_unref (view->details->sort_model); + g_signal_connect_object + (view->details->child_model, "row_loaded", + G_CALLBACK (row_loaded_callback), + view, G_CONNECT_AFTER); + home_uri = gnome_vfs_get_uri_from_local_path (g_get_home_dir ()); + fm_tree_model_add_root_uri (view->details->child_model, home_uri, _("Home Folder"), "gnome-home"); + g_free (home_uri); + fm_tree_model_add_root_uri (view->details->child_model, "file:///", _("Filesystem"), "gnome-folder"); +#ifdef NOT_YET_USABLE + fm_tree_model_add_root_uri (view->details->child_model, "network:///", _("Network Neighbourhood"), "gnome-fs-network"); +#endif + + volume_monitor = gnome_vfs_get_volume_monitor (); + volumes = gnome_vfs_volume_monitor_get_mounted_volumes (volume_monitor); + for (l = volumes; l != NULL; l = l->next) { + add_root_for_volume (view, l->data); + gnome_vfs_volume_unref (l->data); + } + g_list_free (volumes); + + g_signal_connect_object (volume_monitor, "volume_mounted", + G_CALLBACK (volume_mounted_callback), view, 0); + g_signal_connect_object (volume_monitor, "volume_unmounted", + G_CALLBACK (volume_unmounted_callback), view, 0); + + g_object_unref (view->details->child_model); + + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (view->details->sort_model), + compare_rows, view, NULL); + + gtk_tree_view_set_headers_visible (view->details->tree_widget, FALSE); + + view->details->drag_dest = + nautilus_tree_view_drag_dest_new (view->details->tree_widget); + g_signal_connect_object (view->details->drag_dest, + "get_root_uri", + G_CALLBACK (get_root_uri_callback), + view, 0); + g_signal_connect_object (view->details->drag_dest, + "get_file_for_path", + G_CALLBACK (get_file_for_path_callback), + view, 0); + g_signal_connect_object (view->details->drag_dest, + "move_copy_items", + G_CALLBACK (move_copy_items_callback), + view, 0); + + /* Create column */ + column = gtk_tree_view_column_new (); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_set_attributes (column, cell, + "pixbuf", FM_TREE_MODEL_CLOSED_PIXBUF_COLUMN, + "pixbuf_expander_closed", FM_TREE_MODEL_CLOSED_PIXBUF_COLUMN, + "pixbuf_expander_open", FM_TREE_MODEL_OPEN_PIXBUF_COLUMN, + NULL); + + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_set_attributes (column, cell, + "text", FM_TREE_MODEL_DISPLAY_NAME_COLUMN, + "style", FM_TREE_MODEL_FONT_STYLE_COLUMN, + "weight", FM_TREE_MODEL_FONT_WEIGHT_COLUMN, + NULL); + + gtk_tree_view_append_column (view->details->tree_widget, column); + + gtk_widget_show (GTK_WIDGET (view->details->tree_widget)); + + gtk_container_add (GTK_CONTAINER (view->details->scrolled_window), + GTK_WIDGET (view->details->tree_widget)); + + g_signal_connect_object (gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_widget)), "changed", + G_CALLBACK (selection_changed_callback), view, 0); + + g_signal_connect (G_OBJECT (view->details->tree_widget), + "row-activated", G_CALLBACK (row_activated_callback), + view); + + g_signal_connect (G_OBJECT (view->details->tree_widget), + "button_press_event", G_CALLBACK (button_pressed_callback), + view); + + schedule_show_selection (view); +} + +static void +update_filtering_from_preferences (FMTreeView *view) +{ + if (view->details->child_model == NULL) { + return; + } + + fm_tree_model_set_show_hidden_files + (view->details->child_model, + eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES)); + fm_tree_model_set_show_backup_files + (view->details->child_model, + eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_BACKUP_FILES)); + fm_tree_model_set_show_only_directories + (view->details->child_model, + eel_preferences_get_boolean (NAUTILUS_PREFERENCES_TREE_SHOW_ONLY_DIRECTORIES)); +} + +static void +tree_activate_callback (BonoboControl *control, gboolean activating, gpointer user_data) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (user_data); + + if (activating && view->details->tree_widget == NULL) { + create_tree (view); + update_filtering_from_preferences (view); + } +} + +static void +filtering_changed_callback (gpointer callback_data) +{ + update_filtering_from_preferences (FM_TREE_VIEW (callback_data)); +} + +static void +load_location_callback (FMTreeView *view, char *location) +{ + if (view->details->selection_location != NULL) { + g_free (view->details->selection_location); + } + view->details->selection_location = g_strdup (location); + + schedule_show_selection (view); +} + +static void +fm_tree_view_instance_init (FMTreeView *view) +{ + BonoboControl *control; + + view->details = g_new0 (FMTreeViewDetails, 1); + + view->details->scrolled_window = gtk_scrolled_window_new (NULL, NULL); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view->details->scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + + gtk_widget_show (view->details->scrolled_window); + + control = bonobo_control_new (view->details->scrolled_window); + g_signal_connect_object (control, "activate", + G_CALLBACK (tree_activate_callback), view, 0); + + nautilus_view_construct_from_bonobo_control (NAUTILUS_VIEW (view), control); + + view->details->selection_location = NULL; + g_signal_connect_object (view, "load_location", + G_CALLBACK (load_location_callback), view, 0); + view->details->selecting = FALSE; + + eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES, + filtering_changed_callback, view); + eel_preferences_add_callback (NAUTILUS_PREFERENCES_SHOW_BACKUP_FILES, + filtering_changed_callback, view); + eel_preferences_add_callback (NAUTILUS_PREFERENCES_TREE_SHOW_ONLY_DIRECTORIES, + filtering_changed_callback, view); + + g_signal_connect_object (nautilus_icon_factory_get(), "icons_changed", + G_CALLBACK (theme_changed_callback), view, 0); + + view->details->popup_file = NULL; + create_popup_menu (view); +} + +static void +fm_tree_view_dispose (GObject *object) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (object); + + if (view->details->drag_dest) { + g_object_unref (view->details->drag_dest); + view->details->drag_dest = NULL; + } + + if (view->details->show_selection_idle_id) { + g_source_remove (view->details->show_selection_idle_id); + view->details->show_selection_idle_id = 0; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +fm_tree_view_finalize (GObject *object) +{ + FMTreeView *view; + + view = FM_TREE_VIEW (object); + + eel_preferences_remove_callback (NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES, + filtering_changed_callback, view); + eel_preferences_remove_callback (NAUTILUS_PREFERENCES_SHOW_BACKUP_FILES, + filtering_changed_callback, view); + eel_preferences_remove_callback (NAUTILUS_PREFERENCES_TREE_SHOW_ONLY_DIRECTORIES, + filtering_changed_callback, view); + + cancel_activation (view); + + if (view->details->selection_location != NULL) { + g_free (view->details->selection_location); + } + + g_free (view->details); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +fm_tree_view_class_init (FMTreeViewClass *class) +{ + G_OBJECT_CLASS (class)->dispose = fm_tree_view_dispose; + G_OBJECT_CLASS (class)->finalize = fm_tree_view_finalize; + + copied_files_atom = gdk_atom_intern ("x-special/gnome-copied-files", FALSE); +} diff --git a/src/file-manager/fm-tree-view.h b/src/file-manager/fm-tree-view.h new file mode 100644 index 000000000..31ec9da54 --- /dev/null +++ b/src/file-manager/fm-tree-view.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * Copyright (C) 2000, 2001 Eazel, Inc + * Copyright (C) 2002 Anders Carlsson + * + * 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. + * + * Authors: Maciej Stachowiak <mjs@eazel.com> + * Anders Carlsson <andersca@gnu.org> + */ + +/* fm-tree-view.h - tree view. */ + + +#ifndef FM_TREE_VIEW_H +#define FM_TREE_VIEW_H + +#include <libnautilus/nautilus-view.h> + +#define FM_TYPE_TREE_VIEW (fm_tree_view_get_type ()) +#define FM_TREE_VIEW(obj) (GTK_CHECK_CAST ((obj), FM_TYPE_TREE_VIEW, FMTreeView)) +#define FM_TREE_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), FM_TYPE_TREE_VIEW, FMTreeViewClass)) +#define FM_IS_TREE_VIEW(obj) (GTK_CHECK_TYPE ((obj), FM_TYPE_TREE_VIEW)) +#define FM_IS_TREE_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), FM_TYPE_TREE_VIEW)) + +typedef struct FMTreeViewDetails FMTreeViewDetails; + +typedef struct { + NautilusView parent; + FMTreeViewDetails *details; +} FMTreeView; + +typedef struct { + NautilusViewClass parent_class; +} FMTreeViewClass; + +GType fm_tree_view_get_type (void); + +#endif /* FM_TREE_VIEW_H */ diff --git a/src/file-manager/nautilus-directory-view-ui.xml b/src/file-manager/nautilus-directory-view-ui.xml index 58f8c20ad..dbceac809 100644 --- a/src/file-manager/nautilus-directory-view-ui.xml +++ b/src/file-manager/nautilus-directory-view-ui.xml @@ -18,6 +18,9 @@ <cmd name="New Folder" _label="Create _Folder" _tip="Create a new empty folder inside this folder"/> + <cmd name="New Empty File" + _label="_Empty File" + _tip="Create a new empty file inside this folder"/> <cmd name="New Launcher" _label="Create L_auncher" _tip="Create a new launcher"/> @@ -92,6 +95,7 @@ <accel name="Delete" verb="Trash"/> <accel name="*Shift*KP_Delete" verb="Delete"/> <accel name="*Alt*Down" verb="Open"/> + <accel name="*Alt**Shift*Down" verb="OpenCloseParent"/> </keybindings> <menu> <submenu name="File"> @@ -100,6 +104,16 @@ <menuitem name="New Folder" accel="*Shift**Control*n" verb="New Folder"/> + <submenu name="New Documents" + _label="Create _Document"> + <menuitem name="No Templates" + _label="No templates Installed" + sensitive="0"/> + <placeholder name="New Documents Placeholder" delimit="none"/> + <separator name="After New Documents"/> + <menuitem name="New Empty File" + verb="New Empty File"/> + </submenu> <menuitem name="New Launcher" pixtype="stock" pixname="gtk-new" verb="New Launcher"/> @@ -210,6 +224,16 @@ pixtype="stock" pixname="gtk-new" verb="New Launcher"/> </placeholder> + <submenu name="New Documents" + _label="Create _Document"> + <menuitem name="No Templates" + _label="No templates Installed" + sensitive="0"/> + <placeholder name="New Documents Placeholder" delimit="none"/> + <separator name="After New Documents"/> + <menuitem name="New Empty File" + verb="New Empty File"/> + </submenu> <submenu name="Scripts" _label="_Scripts" _tip="Run or manage scripts from ~/Nautilus/scripts" diff --git a/src/file-manager/nautilus-indexing-info.c b/src/file-manager/nautilus-indexing-info.c index f570856a3..85e445e12 100644 --- a/src/file-manager/nautilus-indexing-info.c +++ b/src/file-manager/nautilus-indexing-info.c @@ -161,23 +161,18 @@ last_index_time_dialog_new (void) GtkWidget *label; GtkDialog *dialog; - dialog = eel_create_info_dialog (_("Once a day your files and text content are indexed so " + time_str = nautilus_indexing_info_get_last_index_time (); + label_str = g_strdup_printf (_("Your files were last indexed at %s."), + time_str); + g_free (time_str); + + dialog = eel_create_info_dialog (label_str, + _("Once a day your files and text content are indexed so " "your searches are fast. "), _("Indexing Status"), NULL); set_close_hides_for_dialog (dialog); - time_str = nautilus_indexing_info_get_last_index_time (); - label_str = g_strdup_printf (_("Your files were last indexed at %s"), - time_str); - g_free (time_str); - - label = gtk_label_new (label_str); - gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT); - eel_gtk_label_make_bold (GTK_LABEL (label)); - gtk_box_pack_start (GTK_BOX (dialog->vbox), label, - FALSE, FALSE, 0); - return dialog; } @@ -199,8 +194,9 @@ index_progress_dialog_new (void) ProgressChangeData *progress_data; guint timeout_id; - dialog = eel_create_info_dialog (_("Once a day your files and text content are indexed so " - "your searches are fast. Your files are currently being indexed."), + dialog = eel_create_info_dialog (_("Your files are currently being indexed."), + _("Once a day your files and text content are indexed so " + "your searches are fast."), _("Indexing Status"), NULL); set_close_hides_for_dialog (dialog); percentage_complete = get_index_percentage_complete (); @@ -258,11 +254,12 @@ show_indexing_info_dialog (void) if (!medusa_system_services_are_enabled ()) { details_string = nautilus_medusa_get_explanation_of_enabling (); - dialog_shown = eel_show_info_dialog_with_details (_("When Fast Search is enabled, Find creates an " + dialog_shown = eel_show_info_dialog_with_details (_("There is no index of your files right now."), + _("When Fast Search is enabled, Find creates an " "index to speed up searches. Fast searching " "is not enabled on your computer, so you " "do not have an index right now."), - _("There is no index of your files right now."), + _("No Index of Files"), details_string, NULL); g_free (details_string); @@ -300,6 +297,7 @@ static void show_search_service_not_available_dialog (void) { eel_show_error_dialog (_("Sorry, but the medusa search service is not available."), + _("Please verify medusa has been setup correctly."), _("Search Service Not Available"), NULL); } diff --git a/src/nautilus-application.c b/src/nautilus-application.c index bdae8db1c..a5df8da7e 100644 --- a/src/nautilus-application.c +++ b/src/nautilus-application.c @@ -34,6 +34,7 @@ #include "file-manager/fm-icon-view.h" #include "file-manager/fm-list-view.h" #include "file-manager/fm-search-list-view.h" +#include "file-manager/fm-tree-view.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -86,6 +87,7 @@ #define FACTORY_IID "OAFIID:Nautilus_Factory" #define SEARCH_LIST_VIEW_IID "OAFIID:Nautilus_File_Manager_Search_List_View" #define SHELL_IID "OAFIID:Nautilus_Shell" +#define TREE_VIEW_IID "OAFIID:Nautilus_File_Manager_Tree_View" /* Keeps track of all the desktop windows. */ static GList *nautilus_application_desktop_windows; @@ -138,6 +140,8 @@ create_object (PortableServer_Servant servant, object = BONOBO_OBJECT (nautilus_shell_new (application)); } else if (strcmp (iid, METAFILE_FACTORY_IID) == 0) { object = BONOBO_OBJECT (nautilus_metafile_factory_get_instance ()); + } else if (strcmp (iid, TREE_VIEW_IID) == 0) { + object = BONOBO_OBJECT (g_object_new (fm_tree_view_get_type (), NULL)); } else { object = CORBA_OBJECT_NIL; } @@ -184,6 +188,7 @@ nautilus_application_instance_init (NautilusApplication *application) nautilus_bonobo_register_activation_shortcut (NAUTILUS_DESKTOP_ICON_VIEW_IID, create_object_shortcut, application); nautilus_bonobo_register_activation_shortcut (NAUTILUS_LIST_VIEW_IID, create_object_shortcut, application); nautilus_bonobo_register_activation_shortcut (SEARCH_LIST_VIEW_IID, create_object_shortcut, application); + nautilus_bonobo_register_activation_shortcut (TREE_VIEW_IID, create_object_shortcut, application); } NautilusApplication * @@ -211,6 +216,7 @@ nautilus_application_destroy (BonoboObject *object) nautilus_bonobo_unregister_activation_shortcut (NAUTILUS_DESKTOP_ICON_VIEW_IID); nautilus_bonobo_unregister_activation_shortcut (NAUTILUS_LIST_VIEW_IID); nautilus_bonobo_unregister_activation_shortcut (SEARCH_LIST_VIEW_IID); + nautilus_bonobo_unregister_activation_shortcut (TREE_VIEW_IID); nautilus_bookmarks_exiting (); @@ -227,6 +233,7 @@ check_required_directories (NautilusApplication *application) EelStringList *directories; char *directories_as_string; char *error_string; + char *detail_string; char *dialog_title; GtkDialog *dialog; int failed_count; @@ -251,30 +258,28 @@ check_required_directories (NautilusApplication *application) failed_count = eel_string_list_get_length (directories); if (failed_count != 0) { - directories_as_string = eel_string_list_as_string (directories, "\n", EEL_STRING_LIST_ALL_STRINGS); + directories_as_string = eel_string_list_as_string (directories, ", ", EEL_STRING_LIST_ALL_STRINGS); if (failed_count == 1) { - dialog_title = g_strdup (_("Couldn't Create Required Folder")); - error_string = g_strdup_printf (_("Nautilus could not create the required folder \"%s\". " - "Before running Nautilus, please create this folder, or " - "set permissions such that Nautilus can create it."), + dialog_title = _("Couldn't Create Required Folder"); + error_string = g_strdup_printf (_("Nautilus could not create the required folder \"%s\"."), directories_as_string); + detail_string = _("Before running Nautilus, please create the following folder, or " + "set permissions such that Nautilus can create it."); } else { - dialog_title = g_strdup (_("Couldn't Create Required Folders")); - error_string = g_strdup_printf (_("Nautilus could not create the following required folders:\n\n" - "%s\n\n" - "Before running Nautilus, please create these folders, or " - "set permissions such that Nautilus can create them."), - directories_as_string); + dialog_title = _("Couldn't Create Required Folders"); + error_string = g_strdup_printf (_("Nautilus could not create the following required folders: " + "%s."), directories_as_string); + detail_string = _("Before running Nautilus, please create these folders, or " + "set permissions such that Nautilus can create them."); } - dialog = eel_show_error_dialog (error_string, dialog_title, NULL); + dialog = eel_show_error_dialog (error_string, detail_string, dialog_title, NULL); /* We need the main event loop so the user has a chance to see the dialog. */ nautilus_main_event_loop_register (GTK_OBJECT (dialog)); g_free (directories_as_string); g_free (error_string); - g_free (dialog_title); } eel_string_list_free (directories); @@ -349,10 +354,10 @@ migrate_old_nautilus_files (void) close (fd); } - eel_show_info_dialog (_("The location of the desktop directory has changed in GNOME 2.4. " - "A link called \"Link To Old Desktop\" has been created on the desktop. " - "You can open this to move over the files you want, then delete the link."), - _("Migrated old desktop"), + eel_show_info_dialog (_("A link called \"Link To Old Desktop\" has been created on the desktop."), + _("The location of the desktop directory has changed in GNOME 2.4. " + "You can open the link and move over the files you want, then delete the link."), + _("Migrated Old Desktop"), NULL); } g_free (old_desktop_dir); @@ -597,7 +602,7 @@ nautilus_application_startup (NautilusApplication *application, } if (message != NULL) { - dialog = eel_show_error_dialog_with_details (message, NULL, detailed_message, NULL); + dialog = eel_show_error_dialog_with_details (message, NULL, NULL, detailed_message, NULL); /* We need the main event loop so the user has a chance to see the dialog. */ nautilus_main_event_loop_register (GTK_OBJECT (dialog)); goto out; @@ -886,20 +891,20 @@ find_parent_spatial_window (NautilusSpatialWindow *window) } void -nautilus_application_close_with_parent_windows (NautilusSpatialWindow *window) +nautilus_application_close_parent_windows (NautilusSpatialWindow *window) { NautilusSpatialWindow *parent_window; + NautilusSpatialWindow *new_parent_window; g_return_if_fail (NAUTILUS_IS_SPATIAL_WINDOW (window)); parent_window = find_parent_spatial_window (window); - nautilus_window_close (NAUTILUS_WINDOW (window)); - window = parent_window; while (parent_window) { - parent_window = find_parent_spatial_window (window); - nautilus_window_close (NAUTILUS_WINDOW (window)); - window = parent_window; + + new_parent_window = find_parent_spatial_window (parent_window); + nautilus_window_close (NAUTILUS_WINDOW (parent_window)); + parent_window = new_parent_window; } } diff --git a/src/nautilus-application.h b/src/nautilus-application.h index 6b43d2d0e..84e16333e 100644 --- a/src/nautilus-application.h +++ b/src/nautilus-application.h @@ -78,7 +78,7 @@ NautilusWindow * nautilus_application_present_spatial_window (NautilusAp NautilusWindow * nautilus_application_create_navigation_window (NautilusApplication *application, GdkScreen *screen); void nautilus_application_close_all_navigation_windows (void); -void nautilus_application_close_with_parent_windows (NautilusSpatialWindow *window); +void nautilus_application_close_parent_windows (NautilusSpatialWindow *window); void nautilus_application_open_desktop (NautilusApplication *application); void nautilus_application_close_desktop (void); diff --git a/src/nautilus-connect-server-dialog.c b/src/nautilus-connect-server-dialog.c index 763fdce28..4a8d611fa 100644 --- a/src/nautilus-connect-server-dialog.c +++ b/src/nautilus-connect-server-dialog.c @@ -46,8 +46,7 @@ EEL_CLASS_BOILERPLATE (NautilusConnectServerDialog, nautilus_connect_server_dialog, GTK_TYPE_DIALOG) enum { - RESPONSE_CONNECT, - RESPONSE_CANCEL + RESPONSE_CONNECT }; static void @@ -84,7 +83,9 @@ connect_to_server (NautilusConnectServerDialog *dialog) name = gtk_editable_get_chars (GTK_EDITABLE (dialog->details->name_entry), 0, -1); if (strlen (name) == 0) { - eel_show_error_dialog (_("You must enter a name for the server"), _("Can't connect to server"), GTK_WINDOW (dialog)); + eel_show_error_dialog (_("You must enter a name for the server."), + _("Please enter a name and try again."), + _("Can't Connect to Server"), GTK_WINDOW (dialog)); g_free (name); return; } @@ -97,9 +98,10 @@ connect_to_server (NautilusConnectServerDialog *dialog) if (vfs_uri == NULL) { error_message = g_strdup_printf - (_("\"%s\" is not a valid location. Please check the spelling and try again."), + (_("\"%s\" is not a valid location."), uri); - eel_show_error_dialog (error_message, _("Can't connect to server"), GTK_WINDOW (dialog)); + eel_show_error_dialog (error_message, _("Please check the spelling and try again."), + _("Can't Connect to Server"), GTK_WINDOW (dialog)); g_free (error_message); } else { gnome_vfs_uri_unref (vfs_uri); @@ -132,7 +134,7 @@ response_callback (NautilusConnectServerDialog *dialog, break; case GTK_RESPONSE_NONE: case GTK_RESPONSE_DELETE_EVENT: - case RESPONSE_CANCEL: + case GTK_RESPONSE_CANCEL: gtk_widget_destroy (GTK_WIDGET (dialog)); break; default : @@ -174,15 +176,19 @@ nautilus_connect_server_dialog_init (NautilusConnectServerDialog *dialog) gtk_window_set_title (GTK_WINDOW (dialog), _("Connect to Server")); gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2); table = gtk_table_new (2, 2, FALSE); + gtk_container_set_border_width (GTK_CONTAINER (table), 5); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), - table, TRUE, TRUE, 12); + table, TRUE, TRUE, 0); gtk_table_set_row_spacings (GTK_TABLE (table), 6); - gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacings (GTK_TABLE (table), 12); gtk_widget_show (table); label = gtk_label_new_with_mnemonic (_("_Name:")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_widget_show (label); gtk_table_attach (GTK_TABLE (table), label, 0, 1, @@ -201,6 +207,7 @@ nautilus_connect_server_dialog_init (NautilusConnectServerDialog *dialog) label = gtk_label_new_with_mnemonic (_("_Location:")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); gtk_widget_show (label); gtk_table_attach (GTK_TABLE (table), label, 0, 1, @@ -223,9 +230,9 @@ nautilus_connect_server_dialog_init (NautilusConnectServerDialog *dialog) gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, - RESPONSE_CANCEL); + GTK_RESPONSE_CANCEL); gtk_dialog_add_button (GTK_DIALOG (dialog), - _("Connect"), + _("C_onnect"), RESPONSE_CONNECT); gtk_dialog_set_default_response (GTK_DIALOG (dialog), RESPONSE_CONNECT); diff --git a/src/nautilus-information-panel.c b/src/nautilus-information-panel.c index b599150c8..4826bcf41 100644 --- a/src/nautilus-information-panel.c +++ b/src/nautilus-information-panel.c @@ -503,8 +503,8 @@ receive_dropped_uri_list (NautilusInformationPanel *information_panel, if (!exactly_one) { eel_show_error_dialog ( - _("You can't assign more than one custom icon at a time! " - "Please drag just one image to set a custom icon."), + _("You can't assign more than one custom icon at a time."), + _("Please drag just one image to set a custom icon."), _("More Than One Image"), window); break; @@ -524,15 +524,15 @@ receive_dropped_uri_list (NautilusInformationPanel *information_panel, } else { if (eel_is_remote_uri (uris[0])) { eel_show_error_dialog ( - _("The file that you dropped is not local. " - "You can only use local images as custom icons."), + _("The file that you dropped is not local."), + _("You can only use local images as custom icons."), _("Local Images Only"), window); } else { eel_show_error_dialog ( - _("The file that you dropped is not an image. " - "You can only use local images as custom icons."), + _("The file that you dropped is not an image."), + _("You can only use images as custom icons."), _("Images Only"), window); } @@ -982,7 +982,6 @@ burn_cd_callback (GtkWidget *button, gpointer data) { GError *error; char *argv[] = { "nautilus-cd-burner", NULL}; - char *text; error = NULL; if (!g_spawn_async (NULL, @@ -991,11 +990,9 @@ burn_cd_callback (GtkWidget *button, gpointer data) NULL, NULL, NULL, &error)) { - text = g_strdup_printf (_("Unable to launch the cd burner application:\n%s"), error->message); - eel_show_error_dialog (text, - _("Can't launch cd burner"), + eel_show_error_dialog (_("Unable to launch the cd burner application."), error->message, + _("Can't Launch CD Burner"), GTK_WINDOW (gtk_widget_get_toplevel (button))); - g_free (text); g_error_free (error); } } diff --git a/src/nautilus-location-bar.c b/src/nautilus-location-bar.c index fd4092dfd..12fe82b23 100644 --- a/src/nautilus-location-bar.c +++ b/src/nautilus-location-bar.c @@ -39,7 +39,6 @@ #include <eel/eel-accessibility.h> #include <eel/eel-glib-extensions.h> #include <eel/eel-gtk-macros.h> -#include <eel/eel-input-event-box.h> #include <eel/eel-stock-dialogs.h> #include <eel/eel-string.h> #include <eel/eel-vfs-extensions.h> @@ -130,6 +129,7 @@ drag_data_received_callback (GtkWidget *widget, GdkScreen *screen; gboolean new_windows_for_extras; char *prompt; + char *detail; g_assert (NAUTILUS_IS_LOCATION_BAR (widget)); g_assert (data != NULL); @@ -151,20 +151,24 @@ drag_data_received_callback (GtkWidget *widget, */ name_count = g_list_length (names); if (name_count > 1) { - prompt = g_strdup_printf (_("Do you want to view these %d locations " - "in separate windows?"), - name_count); + prompt = g_strdup_printf (_("Do you want to view %d locations?"), + name_count); + detail = g_strdup_printf (_("This will open %d separate windows."), + name_count); /* eel_run_simple_dialog should really take in pairs * like gtk_dialog_new_with_buttons() does. */ new_windows_for_extras = eel_run_simple_dialog (GTK_WIDGET (window), TRUE, + GTK_MESSAGE_QUESTION, prompt, + detail, _("View in Multiple Windows?"), - GTK_STOCK_OK, GTK_STOCK_CANCEL, - NULL) == 0 /* GNOME_OK */; + GTK_STOCK_CANCEL, GTK_STOCK_OK, + NULL) != 0 /* GNOME_OK */; g_free (prompt); + g_free (detail); if (!new_windows_for_extras) { gtk_drag_finish (context, FALSE, FALSE, time); @@ -365,7 +369,9 @@ nautilus_location_bar_init (NautilusLocationBar *bar) hbox = gtk_hbox_new (0, FALSE); - event_box = eel_input_event_box_new (); + event_box = gtk_event_box_new (); + gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (event_box), GNOME_PAD_SMALL); label = gtk_label_new (LOCATION_LABEL); diff --git a/src/nautilus-location-dialog.c b/src/nautilus-location-dialog.c index ebbe30f6f..c88755781 100644 --- a/src/nautilus-location-dialog.c +++ b/src/nautilus-location-dialog.c @@ -43,8 +43,7 @@ EEL_CLASS_BOILERPLATE (NautilusLocationDialog, nautilus_location_dialog, GTK_TYPE_DIALOG) enum { - RESPONSE_OPEN, - RESPONSE_CANCEL + RESPONSE_OPEN }; static void @@ -96,7 +95,7 @@ response_callback (NautilusLocationDialog *dialog, break; case GTK_RESPONSE_NONE : case GTK_RESPONSE_DELETE_EVENT : - case RESPONSE_CANCEL : + case GTK_RESPONSE_CANCEL : gtk_widget_destroy (GTK_WIDGET (dialog)); break; default : @@ -138,13 +137,16 @@ nautilus_location_dialog_init (NautilusLocationDialog *dialog) gtk_window_set_title (GTK_WINDOW (dialog), _("Open Location")); gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1); gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2); - box = gtk_hbox_new (FALSE, 6); + box = gtk_hbox_new (FALSE, 12); + gtk_container_set_border_width (GTK_CONTAINER (box), 5); gtk_widget_show (box); - label = gtk_label_new (_("Location:")); + label = gtk_label_new_with_mnemonic (_("_Location:")); gtk_widget_show (label); - gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 6); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); dialog->details->entry = nautilus_location_entry_new (); g_signal_connect (dialog->details->entry, @@ -155,14 +157,14 @@ nautilus_location_dialog_init (NautilusLocationDialog *dialog) gtk_widget_show (dialog->details->entry); gtk_box_pack_start (GTK_BOX (box), dialog->details->entry, - TRUE, TRUE, 6); + TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), - box, TRUE, TRUE, 12); + box, TRUE, TRUE, 0); gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, - RESPONSE_CANCEL); + GTK_RESPONSE_CANCEL); gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_OPEN, RESPONSE_OPEN); diff --git a/src/nautilus-location-entry.c b/src/nautilus-location-entry.c index 623f2fa99..f7e4a4463 100644 --- a/src/nautilus-location-entry.c +++ b/src/nautilus-location-entry.c @@ -39,7 +39,6 @@ #include <eel/eel-gtk-macros.h> #include <eel/eel-stock-dialogs.h> #include <eel/eel-string.h> -#include <eel/eel-input-event-box.h> #include <eel/eel-vfs-extensions.h> #include <gtk/gtkdnd.h> #include <gtk/gtksignal.h> diff --git a/src/nautilus-main.c b/src/nautilus-main.c index 69c2d4a79..374a03a02 100644 --- a/src/nautilus-main.c +++ b/src/nautilus-main.c @@ -130,22 +130,23 @@ nautilus_main_event_loop_quit (void) static void register_icons (void) { - GnomeIconTheme *icon_theme; - char *icon; + GtkIconTheme *icon_theme; + GtkIconInfo *info; + const char *icon; GtkIconSource *source; GtkIconSet *set; GtkIconFactory *factory; icon_theme = nautilus_icon_factory_get_icon_theme (); - icon = gnome_icon_theme_lookup_icon (icon_theme, "gnome-fs-client", 48, - NULL, NULL); - if (icon != NULL) { + info = gtk_icon_theme_lookup_icon (icon_theme, "gnome-fs-client", 48, + 0); + if (info != NULL) { + icon = gtk_icon_info_get_filename (info); factory = gtk_icon_factory_new (); gtk_icon_factory_add_default (factory); source = gtk_icon_source_new (); gtk_icon_source_set_filename (source, icon); - g_free (icon); set = gtk_icon_set_new (); gtk_icon_set_add_source (set, source); @@ -155,12 +156,12 @@ register_icons (void) gtk_icon_source_free (source); + gtk_icon_info_free (info); g_object_unref (factory); } g_object_unref (icon_theme); - } int diff --git a/src/nautilus-navigation-bar.c b/src/nautilus-navigation-bar.c index 8307da47e..65709d395 100644 --- a/src/nautilus-navigation-bar.c +++ b/src/nautilus-navigation-bar.c @@ -43,7 +43,7 @@ static guint signals[LAST_SIGNAL]; static void nautilus_navigation_bar_class_init (NautilusNavigationBarClass *class); static void nautilus_navigation_bar_init (NautilusNavigationBar *bar); -EEL_CLASS_BOILERPLATE (NautilusNavigationBar, nautilus_navigation_bar, EEL_TYPE_GENEROUS_BIN) +EEL_CLASS_BOILERPLATE (NautilusNavigationBar, nautilus_navigation_bar, GTK_TYPE_HBOX) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (nautilus_navigation_bar, get_location) EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (nautilus_navigation_bar, set_location) diff --git a/src/nautilus-navigation-bar.h b/src/nautilus-navigation-bar.h index 97e3ccdfa..a5bcd8fc5 100644 --- a/src/nautilus-navigation-bar.h +++ b/src/nautilus-navigation-bar.h @@ -29,7 +29,7 @@ #ifndef NAUTILUS_NAVIGATION_BAR_H #define NAUTILUS_NAVIGATION_BAR_H -#include <eel/eel-generous-bin.h> +#include <gtk/gtkhbox.h> #define NAUTILUS_TYPE_NAVIGATION_BAR (nautilus_navigation_bar_get_type ()) #define NAUTILUS_NAVIGATION_BAR(obj) \ @@ -40,11 +40,11 @@ GTK_CHECK_TYPE (obj, NAUTILUS_TYPE_NAVIGATION_BAR) typedef struct { - EelGenerousBin parent; + GtkHBox parent; } NautilusNavigationBar; typedef struct { - EelGenerousBinClass parent_class; + GtkHBoxClass parent_class; /* signals */ void (* location_changed) (NautilusNavigationBar *bar, diff --git a/src/nautilus-navigation-window-menus.c b/src/nautilus-navigation-window-menus.c index 4d8055887..2c32d0cb5 100644 --- a/src/nautilus-navigation-window-menus.c +++ b/src/nautilus-navigation-window-menus.c @@ -174,6 +174,7 @@ forget_history_if_confirmed (NautilusWindow *window) { GtkDialog *dialog; char *prompt; + char *detail; /* Confirm before forgetting history because it's a rare operation that * is hard to recover from. We don't want people doing it accidentally @@ -183,22 +184,23 @@ forget_history_if_confirmed (NautilusWindow *window) /* This is a little joke, shows up occasionally. I only * implemented this feature so I could use this joke. */ - prompt = g_strdup (_("Are you sure you want to forget history? " - "If you do, you will be doomed to repeat it.")); + prompt = _("Are you sure you want to forget history?"); + detail = _("If you do, you will be doomed to repeat it."); } else { - prompt = g_strdup (_("Are you sure you want to clear the list " - "of locations you have visited?")); + prompt = _("Are you sure you want to clear the list " + "of locations you have visited?"); + detail = _("If you clear the list of locations," + " they will be permanently deleted."); } dialog = eel_create_question_dialog (prompt, + detail, _("Clear History"), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_CLEAR, RESPONSE_FORGET, GTK_WINDOW (window)); gtk_widget_show (GTK_WIDGET (dialog)); - - g_free (prompt); g_signal_connect (dialog, "response", G_CALLBACK (forget_history_if_yes), NULL); @@ -417,14 +419,16 @@ show_bogus_bookmark_window (NautilusWindow *window, char *uri; char *uri_for_display; char *prompt; + char *detail; uri = nautilus_bookmark_get_uri (bookmark); uri_for_display = eel_format_uri_for_display (uri); - prompt = g_strdup_printf (_("The location \"%s\" does not exist. Do you " - "want to remove any bookmarks with this " - "location from your list?"), uri_for_display); - dialog = eel_show_yes_no_dialog (prompt, + prompt = _("Do you want to remove any bookmarks with the " + "non-existing location from your list?"); + detail = g_strdup_printf (_("The location \"%s\" does not exist."), uri_for_display); + + dialog = eel_show_yes_no_dialog (prompt, detail, _("Bookmark for Nonexistent Location"), _("Remove"), GTK_STOCK_CANCEL, GTK_WINDOW (window)); @@ -439,7 +443,7 @@ show_bogus_bookmark_window (NautilusWindow *window, g_free (uri); g_free (uri_for_display); - g_free (prompt); + g_free (detail); } static void diff --git a/src/nautilus-navigation-window-ui.xml b/src/nautilus-navigation-window-ui.xml index 9fe89ebd2..2fba4df0c 100644 --- a/src/nautilus-navigation-window-ui.xml +++ b/src/nautilus-navigation-window-ui.xml @@ -81,6 +81,10 @@ _label="_Computer" pixtype="stock" pixname="gnome-fs-client" verb="Go to Computer"/> + <menuitem name="Go to Templates" + _label="_Templates" + _tip="Go to templates folder" + verb="Go to Templates"/> <menuitem name="Go to Trash" _label="_Trash" _tip="Go to the trash folder" diff --git a/src/nautilus-navigation-window.c b/src/nautilus-navigation-window.c index 12cbbe3bb..d89f1ca72 100644 --- a/src/nautilus-navigation-window.c +++ b/src/nautilus-navigation-window.c @@ -46,7 +46,6 @@ #include <eel/eel-debug.h> #include <eel/eel-gdk-extensions.h> #include <eel/eel-gdk-pixbuf-extensions.h> -#include <eel/eel-generous-bin.h> #include <eel/eel-gtk-extensions.h> #include <eel/eel-gtk-macros.h> #include <eel/eel-stock-dialogs.h> @@ -961,6 +960,11 @@ real_set_content_view_widget (NautilusWindow *nautilus_window, set_content_view_widget, (nautilus_window, new_view)); + + if (new_view == NULL) { + return; + } + connect_view (window, new_view); nautilus_horizontal_splitter_pack2 ( @@ -1103,22 +1107,22 @@ static void report_side_panel_failure_to_user (NautilusWindow *window, NautilusViewFrame *view_frame) { char *message; + char *detail; char *label; label = nautilus_window_get_view_frame_label (view_frame); if (label == NULL) { message = g_strdup - (_("One of the side panels encountered an error and can't continue. " - "Unfortunately I couldn't tell which one.")); + (_("One of the side panels encountered an error and can't continue.")); + detail = _("Unfortunately I couldn't tell which one."); } else { message = g_strdup_printf - (_("The %s side panel encountered an error and can't continue. " - "If this keeps happening, you might want to turn this panel off."), - label); + (_("The %s side panel encountered an error and can't continue."), label); + detail = _("If this keeps happening, you might want to turn this panel off."); } - eel_show_error_dialog (message, _("Side Panel Failed"), GTK_WINDOW (window)); + eel_show_error_dialog (message, detail, _("Side Panel Failed"), GTK_WINDOW (window)); g_free (label); g_free (message); @@ -1466,6 +1470,19 @@ nautilus_navigation_window_show (GtkWidget *widget) GTK_WIDGET_CLASS (parent_class)->show (widget); } +static void +real_get_default_size(NautilusWindow *window, guint *default_width, guint *default_height) +{ + + if(default_width) { + *default_width = NAUTILUS_NAVIGATION_WINDOW_DEFAULT_WIDTH; + } + + if(default_height) { + *default_height = NAUTILUS_NAVIGATION_WINDOW_DEFAULT_HEIGHT; + } +} + static void nautilus_navigation_window_class_init (NautilusNavigationWindowClass *class) { @@ -1482,4 +1499,5 @@ nautilus_navigation_window_class_init (NautilusNavigationWindowClass *class) NAUTILUS_WINDOW_CLASS (class)->set_throbber_active = real_set_throbber_active; NAUTILUS_WINDOW_CLASS (class)->prompt_for_location = real_prompt_for_location; NAUTILUS_WINDOW_CLASS (class)->set_title = real_set_title; + NAUTILUS_WINDOW_CLASS(class)->get_default_size = real_get_default_size; } diff --git a/src/nautilus-property-browser.c b/src/nautilus-property-browser.c index 7bbb8720a..81cf4fcb0 100644 --- a/src/nautilus-property-browser.c +++ b/src/nautilus-property-browser.c @@ -678,7 +678,8 @@ ensure_uri_is_image (const char *uri) GNOME_VFS_FILE_INFO_GET_MIME_TYPE | GNOME_VFS_FILE_INFO_FOLLOW_LINKS); is_image = eel_istr_has_prefix (file_info->mime_type, "image/") - && eel_strcasecmp (file_info->mime_type, "image/svg") != 0; + && eel_strcasecmp (file_info->mime_type, "image/svg") != 0 + && eel_strcasecmp (file_info->mime_type, "image/svg+xml") != 0; gnome_vfs_file_info_unref (file_info); return is_image; } @@ -911,7 +912,8 @@ remove_pattern(NautilusPropertyBrowser *property_browser, const char* pattern_na /* delete the pattern from the pattern directory */ if (gnome_vfs_unlink (pattern_uri) != GNOME_VFS_OK) { char *message = g_strdup_printf (_("Sorry, but pattern %s couldn't be deleted."), pattern_name); - eel_show_error_dialog (message, _("Couldn't delete pattern"), GTK_WINDOW (property_browser)); + char *detail = _("Check that you have permission to delete the pattern."); + eel_show_error_dialog (message, detail, _("Couldn't Delete Pattern"), GTK_WINDOW (property_browser)); g_free (message); } @@ -940,7 +942,8 @@ remove_emblem (NautilusPropertyBrowser *property_browser, const char* emblem_nam /* delete the emblem from the emblem directory */ if (gnome_vfs_unlink (emblem_uri) != GNOME_VFS_OK) { char *message = g_strdup_printf (_("Sorry, but emblem %s couldn't be deleted."), emblem_name); - eel_show_error_dialog (message, _("Couldn't delete pattern"), GTK_WINDOW (property_browser)); + char *detail = _("Check that you have permission to delete the emblem."); + eel_show_error_dialog (message, detail, _("Couldn't Delete Emblem"), GTK_WINDOW (property_browser)); g_free (message); } else { @@ -1097,12 +1100,15 @@ add_pattern_to_browser (const char *path_name, gpointer *data) /* make sure that it's a valid path */ if (path_name == NULL || path_name[0] != '/') { char *message; + char *detail; if (path_name != NULL) { message = g_strdup_printf (_("Sorry, but \"%s\" is not a valid file name."), path_name); + detail = _("Please check the spelling and try again."); } else { message = g_strdup (_("Sorry, but you did not supply a valid file name.")); + detail = _("Please try again."); } - eel_show_error_dialog (message, _("Couldn't install pattern"), GTK_WINDOW (property_browser)); + eel_show_error_dialog (message, detail, _("Couldn't Install Pattern"), GTK_WINDOW (property_browser)); g_free (message); return; } @@ -1113,7 +1119,9 @@ add_pattern_to_browser (const char *path_name, gpointer *data) /* don't allow the user to change the reset image */ basename = eel_uri_get_basename (path_uri); if (basename && eel_strcmp (basename, RESET_IMAGE_NAME) == 0) { - eel_show_error_dialog (_("Sorry, but you can't replace the reset image."), _("Not an Image"), NULL); + eel_show_error_dialog (_("Sorry, but you can't replace the reset image."), + _("Reset is a special image that cannot be deleted."), + _("Not an Image"), NULL); g_free (path_uri); g_free (basename); return; @@ -1145,7 +1153,7 @@ add_pattern_to_browser (const char *path_name, gpointer *data) result = eel_copy_uri_simple (path_name, destination_name); if (result != GNOME_VFS_OK) { char *message = g_strdup_printf (_("Sorry, but the pattern %s couldn't be installed."), path_name); - eel_show_error_dialog (message, _("Couldn't install pattern"), GTK_WINDOW (property_browser)); + eel_show_error_dialog (message, NULL, _("Couldn't Install Pattern"), GTK_WINDOW (property_browser)); g_free (message); } @@ -1225,8 +1233,9 @@ add_color_to_browser (GtkWidget *widget, gint which_button, gpointer *data) color_name = gtk_entry_get_text (GTK_ENTRY (property_browser->details->color_name)); stripped_color_name = g_strstrip (g_strdup (color_name)); if (strlen (stripped_color_name) == 0) { - eel_show_error_dialog (_("Sorry, but you must specify a non-blank name for the new color."), - _("Couldn't install color"), GTK_WINDOW (property_browser)); + eel_show_error_dialog (_("The color cannot be installed."), + _("Sorry, but you must specify a non-blank name for the new color."), + _("Couldn't Install Color"), GTK_WINDOW (property_browser)); } else { add_color_to_file (property_browser, color_spec, stripped_color_name); @@ -1315,8 +1324,8 @@ emblem_dialog_clicked (GtkWidget *dialog, int which_button, NautilusPropertyBrow property_browser->details->image_path = emblem_path; } else { char *message = g_strdup_printf - (_("Sorry, but '%s' is not a usable image file!"), emblem_path); - eel_show_error_dialog (message, _("Not an Image"), GTK_WINDOW (property_browser)); + (_("Sorry, but \"%s\" is not a usable image file."), emblem_path); + eel_show_error_dialog (_("The file is not an image."), message, _("Not an Image"), GTK_WINDOW (property_browser)); g_free (message); g_free (emblem_path); return; @@ -1331,8 +1340,9 @@ emblem_dialog_clicked (GtkWidget *dialog, int which_button, NautilusPropertyBrow if (pixbuf == NULL) { char *message = g_strdup_printf - (_("Sorry, but '%s' is not a usable image file!"), property_browser->details->image_path); - eel_show_error_dialog (message, _("Not an Image"), GTK_WINDOW (property_browser)); + (_("Sorry, but \"%s\" is not a usable image file."), property_browser->details->image_path); + eel_show_error_dialog (_("The file is not an image."), message, _("Not an Image"), GTK_WINDOW (property_browser)); + g_free (message); } new_keyword = gtk_entry_get_text(GTK_ENTRY(property_browser->details->keyword)); diff --git a/src/nautilus-server-connect.c b/src/nautilus-server-connect.c index 858893c3e..7aba928ab 100644 --- a/src/nautilus-server-connect.c +++ b/src/nautilus-server-connect.c @@ -302,11 +302,10 @@ can_connect (const char *uri) static void update_icon (GtkEntry *entry, gpointer user_data) { - GnomeIconTheme *theme; + GtkIconTheme *theme; GtkWidget *button; - char *uri_utf8, *uri, *filename; - const GnomeIconData *icon_data; - int base_size; + char *uri_utf8, *uri; + GdkPixbuf *pixbuf; uri_utf8 = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); if (uri_utf8 == NULL) @@ -316,9 +315,8 @@ update_icon (GtkEntry *entry, gpointer user_data) g_free (uri_utf8); button = glade_xml_get_widget (xml, "button3"); - - if (uri == NULL || strcmp (uri, "") == 0) - { + + if (uri == NULL || strcmp (uri, "") == 0) { gtk_widget_set_sensitive (button, FALSE); naut_icon = "gnome-fs-share"; } else { @@ -337,14 +335,16 @@ update_icon (GtkEntry *entry, gpointer user_data) } } - theme = gnome_icon_theme_new (); - filename = gnome_icon_theme_lookup_icon (theme, naut_icon, - ICON_SIZE_STANDARD, - &icon_data, - &base_size); + theme = gtk_icon_theme_get_default (); + pixbuf = gtk_icon_theme_load_icon (theme, naut_icon, + ICON_SIZE_STANDARD, + 0, NULL); - gtk_image_set_from_file (GTK_IMAGE (image), filename); - g_free (filename); + if (pixbuf != NULL) { + gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf); + g_object_unref (pixbuf); + } + g_free (uri); } diff --git a/src/nautilus-spatial-window-ui.xml b/src/nautilus-spatial-window-ui.xml index aeb8fad4b..686ebd269 100644 --- a/src/nautilus-spatial-window-ui.xml +++ b/src/nautilus-spatial-window-ui.xml @@ -5,6 +5,9 @@ <commands> </commands> +<keybindings> + <accel name="*Alt**Shift*Up" verb="UpCloseCurrent"/> +</keybindings> <menu> <submenu name="File"> <placeholder name="Location Placeholder"> @@ -23,10 +26,10 @@ </placeholder> <placeholder name="Close Items Placeholder"> <menuitem name="Close With Parents" - _label="Close _All Parents" - _tip="Close all Navigation windows" + _label="Close P_arent Folders" + _tip="Close this folder's parents" accel="*Control**Shift*w" - verb="Close With Parents"/> + verb="Close Parent Folders"/> </placeholder> </submenu> @@ -41,6 +44,10 @@ _tip="Go to Computer" pixtype="stock" pixname="gnome-fs-client" verb="Go to Computer"/> + <menuitem name="Go to Templates" + _label="_Templates" + _tip="Go to templates folder" + verb="Go to Templates"/> <menuitem name="Go to Trash" _label="_Trash" _tip="Go to the trash folder" diff --git a/src/nautilus-spatial-window.c b/src/nautilus-spatial-window.c index cf608a71b..a3d84d94d 100644 --- a/src/nautilus-spatial-window.c +++ b/src/nautilus-spatial-window.c @@ -46,7 +46,6 @@ #include <eel/eel-debug.h> #include <eel/eel-gdk-extensions.h> #include <eel/eel-gdk-pixbuf-extensions.h> -#include <eel/eel-generous-bin.h> #include <eel/eel-gtk-extensions.h> #include <eel/eel-gtk-macros.h> #include <eel/eel-string.h> @@ -121,7 +120,7 @@ nautilus_spatial_window_configure_event (GtkWidget *widget, GTK_WIDGET_CLASS (parent_class)->configure_event (widget, event); /* Only save the geometry if the user hasn't resized the window - * for half a second. Otherwise delay the callback another half second. + * for a second. Otherwise delay the callback another second. */ if (window->details->save_geometry_timeout_id != 0) { g_source_remove (window->details->save_geometry_timeout_id); @@ -149,7 +148,7 @@ nautilus_spatial_window_configure_event (GtkWidget *widget, window->details->last_geometry = geometry_string; window->details->save_geometry_timeout_id = - g_timeout_add (500, save_window_geometry_timeout, window); + g_timeout_add (1000, save_window_geometry_timeout, window); } return FALSE; @@ -239,14 +238,23 @@ nautilus_spatial_window_show (GtkWidget *widget) } static void -file_menu_close_with_parent_windows_callback (BonoboUIComponent *component, - gpointer user_data, - const char *verb) +file_menu_close_parent_windows_callback (BonoboUIComponent *component, + gpointer user_data, + const char *verb) { - nautilus_application_close_with_parent_windows (NAUTILUS_SPATIAL_WINDOW (user_data)); + nautilus_application_close_parent_windows (NAUTILUS_SPATIAL_WINDOW (user_data)); } static void +go_up_close_current_window_callback (BonoboUIComponent *component, + gpointer user_data, + const char *verb) +{ + nautilus_window_go_up (NAUTILUS_WINDOW (user_data), TRUE); +} + + +static void real_prompt_for_location (NautilusWindow *window) { GtkWidget *dialog; @@ -275,7 +283,8 @@ real_merge_menus (NautilusWindow *nautilus_window) { NautilusSpatialWindow *window; BonoboUIVerb verbs [] = { - BONOBO_UI_VERB ("Close With Parents", file_menu_close_with_parent_windows_callback), + BONOBO_UI_VERB ("Close Parent Folders", file_menu_close_parent_windows_callback), + BONOBO_UI_VERB ("UpCloseCurrent", go_up_close_current_window_callback), BONOBO_UI_VERB_END }; @@ -304,12 +313,22 @@ real_set_content_view_widget (NautilusWindow *window, GTK_WIDGET (new_view)); } -static gboolean -real_delete_event (GtkWidget *window, GdkEventAny *event) +static void +real_window_close (NautilusWindow *window) { + nautilus_spatial_window_save_geometry (NAUTILUS_SPATIAL_WINDOW (window)); nautilus_spatial_window_save_scroll_position (NAUTILUS_SPATIAL_WINDOW (window)); +} - return FALSE; +static void +real_get_default_size(NautilusWindow *window, guint *default_width, guint *default_height) +{ + if(default_width) { + *default_width = NAUTILUS_SPATIAL_WINDOW_DEFAULT_WIDTH; + } + if(default_height) { + *default_height = NAUTILUS_SPATIAL_WINDOW_DEFAULT_HEIGHT; + } } static void @@ -319,7 +338,7 @@ nautilus_spatial_window_instance_init (NautilusSpatialWindow *window) window->affect_spatial_window_on_next_location_change = TRUE; window->details->content_box = - gtk_widget_new (EEL_TYPE_GENEROUS_BIN, NULL); + gtk_hbox_new (FALSE, 0); gtk_widget_show (window->details->content_box); bonobo_window_set_contents (BONOBO_WINDOW (window), window->details->content_box); @@ -344,6 +363,7 @@ nautilus_spatial_window_class_init (NautilusSpatialWindowClass *class) real_merge_menus; NAUTILUS_WINDOW_CLASS (class)->set_content_view_widget = real_set_content_view_widget; - GTK_WIDGET_CLASS (class)->delete_event = - real_delete_event; + NAUTILUS_WINDOW_CLASS (class)->close = + real_window_close; + NAUTILUS_WINDOW_CLASS(class)->get_default_size = real_get_default_size; } diff --git a/src/nautilus-view-frame.c b/src/nautilus-view-frame.c index a7bb916f6..336350b40 100644 --- a/src/nautilus-view-frame.c +++ b/src/nautilus-view-frame.c @@ -130,7 +130,7 @@ static guint signals[LAST_SIGNAL]; EEL_CLASS_BOILERPLATE (NautilusViewFrame, nautilus_view_frame, - EEL_TYPE_GENEROUS_BIN) + GTK_TYPE_HBOX) void nautilus_view_frame_queue_incoming_call (PortableServer_Servant servant, diff --git a/src/nautilus-view-frame.h b/src/nautilus-view-frame.h index 12906dd31..39b6340aa 100644 --- a/src/nautilus-view-frame.h +++ b/src/nautilus-view-frame.h @@ -35,7 +35,7 @@ #include <bonobo/bonobo-ui-container.h> #include <bonobo/bonobo-zoomable-frame.h> -#include <eel/eel-generous-bin.h> +#include <gtk/gtkhbox.h> #include <libnautilus-private/nautilus-undo-manager.h> #include <libnautilus/nautilus-view-component.h> @@ -48,12 +48,12 @@ typedef struct NautilusViewFrameDetails NautilusViewFrameDetails; typedef struct { - EelGenerousBin parent; + GtkHBox parent; NautilusViewFrameDetails *details; } NautilusViewFrame; typedef struct { - EelGenerousBinClass parent_spot; + GtkHBoxClass parent_spot; /* These roughly correspond to CORBA calls, but in some cases they are higher level. */ diff --git a/src/nautilus-window-manage-views.c b/src/nautilus-window-manage-views.c index 35c59be4a..af9ee03f3 100644 --- a/src/nautilus-window-manage-views.c +++ b/src/nautilus-window-manage-views.c @@ -174,25 +174,17 @@ update_title (NautilusWindow *window) void nautilus_window_update_icon (NautilusWindow *window) { - char *path; GdkPixbuf *pixbuf; - GnomeIconTheme *icon_theme; + GtkIconTheme *icon_theme; pixbuf = NULL; /* Desktop window special icon */ if (NAUTILUS_IS_DESKTOP_WINDOW (window)) { - icon_theme = nautilus_icon_factory_get_icon_theme(); - path = gnome_icon_theme_lookup_icon (icon_theme, - "gnome-fs-desktop", 48, - NULL, NULL); - - if (path != NULL) { - pixbuf = gdk_pixbuf_new_from_file (path, NULL); - - g_free (path); - } - + icon_theme = nautilus_icon_factory_get_icon_theme (); + pixbuf = gtk_icon_theme_load_icon (icon_theme, + "gnome-fs-desktop", 48, + 0, NULL); g_object_unref(icon_theme); } else { @@ -736,11 +728,18 @@ nautilus_window_open_location (NautilusWindow *window, void nautilus_window_open_location_with_selection (NautilusWindow *window, const char *location, - GList *selection) + GList *selection, + gboolean close_behind) { + Nautilus_ViewFrame_OpenFlags flags; + + flags = 0; + if (close_behind) { + flags = Nautilus_ViewFrame_OPEN_FLAG_CLOSE_BEHIND; + } open_location (window, location, Nautilus_ViewFrame_OPEN_ACCORDING_TO_MODE, - 0, selection); + flags, selection); } @@ -800,13 +799,14 @@ view_frame_get_id (NautilusViewFrame *view_frame) static void report_content_view_failure_to_user_internal (NautilusWindow *window, NautilusViewFrame *view_frame, - const char *message) + const char *message, + const char *detail) { char *label; label = nautilus_window_get_view_frame_label (view_frame); message = g_strdup_printf (message, label); - eel_show_error_dialog (message, _("View Failed"), GTK_WINDOW (window)); + eel_show_error_dialog (message, detail, _("View Failed"), GTK_WINDOW (window)); g_free (label); } @@ -817,8 +817,8 @@ report_current_content_view_failure_to_user (NautilusWindow *window, report_content_view_failure_to_user_internal (window, view_frame, - _("The %s view encountered an error and can't continue. " - "You can choose another view or go to a different location.")); + _("The %s view encountered an error and can't continue."), + _("You can choose another view or go to a different location.")); } static void @@ -828,7 +828,8 @@ report_nascent_content_view_failure_to_user (NautilusWindow *window, report_content_view_failure_to_user_internal (window, view_frame, - _("The %s view encountered an error while starting up.")); + _("The %s view encountered an error while starting up."), + _("The location cannot be displayed with this viewer.")); } static void @@ -996,8 +997,9 @@ handle_view_failure (NautilusWindow *window, g_warning ("A view failed. The UI will handle this with a dialog but this should be debugged."); if (view == window->content_view) { + disconnect_view(window, window->content_view); nautilus_window_set_content_view_widget (window, NULL); - + /* FIXME bugzilla.gnome.org 45039: We need a * way to report the specific error that * happens in this case - adapter factory not @@ -1175,6 +1177,7 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, char *full_uri_for_display; char *uri_for_display; char *error_message; + char *detail_message; char *scheme_string; char *type_string; char *dialog_title; @@ -1228,14 +1231,18 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, case NAUTILUS_DETERMINE_VIEW_NOT_FOUND: error_message = g_strdup_printf - (_("Couldn't find \"%s\". Please check the spelling and try again."), + (_("Couldn't find \"%s\"."), uri_for_display); + detail_message = g_strdup + (_("Please check the spelling and try again.")); break; case NAUTILUS_DETERMINE_VIEW_INVALID_URI: error_message = g_strdup_printf - (_("\"%s\" is not a valid location. Please check the spelling and try again."), + (_("\"%s\" is not a valid location."), uri_for_display); + detail_message = g_strdup + (_("Please check the spelling and try again.")); break; case NAUTILUS_DETERMINE_VIEW_NO_HANDLER_FOR_TYPE: @@ -1248,16 +1255,20 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, nautilus_file_unref (file); if (type_string == NULL) { error_message = g_strdup_printf - (_("Couldn't display \"%s\", because Nautilus cannot determine what type of file it is."), + (_("Couldn't display \"%s\"."), uri_for_display); + detail_message = g_strdup + (_("Nautilus cannot determine what type of file it is.")); } else { /* FIXME bugzilla.gnome.org 44932: * Should distinguish URIs with no handlers at all from remote URIs * with local-only handlers. */ - error_message = g_strdup_printf - (_("Nautilus has no installed viewer capable of displaying \"%s\"."), - uri_for_display); + error_message = g_strdup_printf + (_("Couldn't display \"%s\"."), + uri_for_display); + detail_message = g_strdup + (_("Nautilus has no installed viewer capable of displaying the file.")); g_free (type_string); } break; @@ -1268,19 +1279,23 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, */ scheme_string = eel_str_get_prefix (location, ":"); g_assert (scheme_string != NULL); /* Shouldn't have gotten this error unless there's a : separator. */ - error_message = g_strdup_printf (_("Couldn't display \"%s\", because Nautilus cannot handle %s: locations."), - uri_for_display, scheme_string); + error_message = g_strdup_printf (_("Couldn't display \"%s\"."), + uri_for_display); + detail_message = g_strdup_printf (_("Nautilus cannot handle %s: locations."), + scheme_string); g_free (scheme_string); break; case NAUTILUS_DETERMINE_VIEW_LOGIN_FAILED: - error_message = g_strdup_printf (_("Couldn't display \"%s\", because the attempt to log in failed."), - uri_for_display); + error_message = g_strdup_printf (_("Couldn't display \"%s\"."), + uri_for_display); + detail_message = g_strdup (_("The attempt to log in failed.")); break; case NAUTILUS_DETERMINE_VIEW_ACCESS_DENIED: - error_message = g_strdup_printf (_("Couldn't display \"%s\", because access was denied."), + error_message = g_strdup_printf (_("Couldn't display \"%s\"."), uri_for_display); + detail_message = g_strdup (_("Access was denied.")); break; case NAUTILUS_DETERMINE_VIEW_HOST_NOT_FOUND: @@ -1290,24 +1305,25 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, * the proxy is set up wrong. */ vfs_uri = gnome_vfs_uri_new (location); - error_message = g_strdup_printf (_("Couldn't display \"%s\", because no host \"%s\" could be found. " - "Check that the spelling is correct and that your proxy settings are correct."), + error_message = g_strdup_printf (_("Couldn't display \"%s\", because no host \"%s\" could be found."), uri_for_display, gnome_vfs_uri_get_host_name (vfs_uri)); + detail_message = g_strdup (_("Check that the spelling is correct and that your proxy settings are correct.")); gnome_vfs_uri_unref (vfs_uri); break; case NAUTILUS_DETERMINE_VIEW_HOST_HAS_NO_ADDRESS: - error_message = g_strdup_printf (_("Couldn't display \"%s\". " - "Check that your proxy settings are correct."), + error_message = g_strdup_printf (_("Couldn't display \"%s\"."), uri_for_display); + detail_message = g_strdup (_("Check that your proxy settings are correct.")); break; case NAUTILUS_DETERMINE_VIEW_NO_MASTER_BROWSER: error_message = g_strdup_printf - (_("Couldn't display \"%s\", because Nautilus cannot contact the SMB master browser.\n" - "Check that an SMB server is running in the local network."), + (_("Couldn't display \"%s\", because Nautilus cannot contact the SMB master browser."), uri_for_display); + detail_message = g_strdup + (_("Check that an SMB server is running in the local network.")); break; case NAUTILUS_DETERMINE_VIEW_SERVICE_NOT_AVAILABLE: @@ -1318,8 +1334,9 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, */ error_message = g_strdup_printf (_("Searching is unavailable right now, because you either have no index, " - "or the search service isn't running. " - "Be sure that you have started the Medusa search service, and if you " + "or the search service isn't running.")); + detail_message = g_strdup + (_("Be sure that you have started the Medusa search service, and if you " "don't have an index, that the Medusa indexer is running.")); dialog_title = g_strdup (_("Searching Unavailable")); break; @@ -1328,6 +1345,7 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, default: error_message = g_strdup_printf (_("Nautilus cannot display \"%s\"."), uri_for_display); + detail_message = g_strdup (_("Please select another viewer and try again.")); } if (dialog_title == NULL) { @@ -1339,7 +1357,7 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, * happens when a new window cannot display its initial URI. */ - dialog = eel_show_error_dialog (error_message, dialog_title, NULL); + dialog = eel_show_error_dialog (error_message, detail_message, dialog_title, NULL); /* if this is the only window, we don't want to quit, so we redirect it to home */ if (just_one_window ()) { @@ -1368,7 +1386,7 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, } else { /* Clean up state of already-showing window */ nautilus_window_allow_stop (window, FALSE); - eel_show_error_dialog (error_message, dialog_title, GTK_WINDOW (window)); + eel_show_error_dialog (error_message, detail_message, dialog_title, GTK_WINDOW (window)); /* Leave the location bar showing the bad location that the user * typed (or maybe achieved by dragging or something). Many times @@ -1380,6 +1398,7 @@ determined_initial_view_callback (NautilusDetermineViewHandle *handle, g_free (dialog_title); g_free (uri_for_display); g_free (error_message); + g_free (detail_message); } /* diff --git a/src/nautilus-window-manage-views.h b/src/nautilus-window-manage-views.h index b5826beee..8aefc7258 100644 --- a/src/nautilus-window-manage-views.h +++ b/src/nautilus-window-manage-views.h @@ -36,7 +36,8 @@ void nautilus_window_open_location (NautilusWi const char *location); void nautilus_window_open_location_with_selection (NautilusWindow *window, const char *location, - GList *selection); + GList *selection, + gboolean close_behind); void nautilus_window_stop_loading (NautilusWindow *window); void nautilus_window_set_content_view (NautilusWindow *window, NautilusViewIdentifier *id); diff --git a/src/nautilus-window-menus.c b/src/nautilus-window-menus.c index d0b9d6cfb..56956690d 100644 --- a/src/nautilus-window-menus.c +++ b/src/nautilus-window-menus.c @@ -349,7 +349,7 @@ go_menu_up_callback (BonoboUIComponent *component, gpointer user_data, const char *verb) { - nautilus_window_go_up (NAUTILUS_WINDOW (user_data)); + nautilus_window_go_up (NAUTILUS_WINDOW (user_data), FALSE); } static void @@ -379,6 +379,20 @@ go_menu_go_to_computer_callback (BonoboUIComponent *component, } static void +go_menu_go_to_templates_callback (BonoboUIComponent *component, + gpointer user_data, + const char *verb) +{ + char *uri; + + nautilus_create_templates_directory (); + uri = nautilus_get_templates_directory_uri (); + nautilus_window_go_to (NAUTILUS_WINDOW (user_data), + uri); + g_free (uri); +} + +static void go_menu_go_to_trash_callback (BonoboUIComponent *component, gpointer user_data, const char *verb) @@ -658,6 +672,7 @@ nautilus_window_initialize_menus_part_1 (NautilusWindow *window) BONOBO_UI_VERB ("Home", go_menu_home_callback), BONOBO_UI_VERB ("Start Here", go_menu_start_here_callback), BONOBO_UI_VERB ("Go to Computer", go_menu_go_to_computer_callback), + BONOBO_UI_VERB ("Go to Templates", go_menu_go_to_templates_callback), BONOBO_UI_VERB ("Go to Trash", go_menu_go_to_trash_callback), BONOBO_UI_VERB ("Go to Burn CD", go_menu_go_to_burn_cd_callback), BONOBO_UI_VERB ("Go to Location", go_menu_location_callback), diff --git a/src/nautilus-window-private.h b/src/nautilus-window-private.h index ade48b2d0..e451c7686 100644 --- a/src/nautilus-window-private.h +++ b/src/nautilus-window-private.h @@ -139,8 +139,11 @@ struct _NautilusNavigationWindowDetails { #define NAUTILUS_WINDOW_MIN_WIDTH 200 #define NAUTILUS_WINDOW_MIN_HEIGHT 200 -#define NAUTILUS_WINDOW_DEFAULT_WIDTH 500 -#define NAUTILUS_WINDOW_DEFAULT_HEIGHT 300 +#define NAUTILUS_SPATIAL_WINDOW_DEFAULT_WIDTH 500 +#define NAUTILUS_SPATIAL_WINDOW_DEFAULT_HEIGHT 300 + +#define NAUTILUS_NAVIGATION_WINDOW_DEFAULT_WIDTH 800 +#define NAUTILUS_NAVIGATION_WINDOW_DEFAULT_HEIGHT 550 typedef void (*NautilusBookmarkFailedCallback) (NautilusWindow *window, NautilusBookmark *bookmark); diff --git a/src/nautilus-window.c b/src/nautilus-window.c index 187f66a9b..aa0e5bba4 100644 --- a/src/nautilus-window.c +++ b/src/nautilus-window.c @@ -44,11 +44,11 @@ #include <eel/eel-debug.h> #include <eel/eel-gdk-extensions.h> #include <eel/eel-gdk-pixbuf-extensions.h> -#include <eel/eel-generous-bin.h> #include <eel/eel-gtk-extensions.h> #include <eel/eel-gtk-macros.h> #include <eel/eel-stock-dialogs.h> #include <eel/eel-string.h> +#include <eel/eel-vfs-extensions.h> #include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk/gdkx.h> #include <gtk/gtkmain.h> @@ -354,7 +354,7 @@ nautilus_window_go_to (NautilusWindow *window, const char *uri) } void -nautilus_window_go_up (NautilusWindow *window) +nautilus_window_go_up (NautilusWindow *window, gboolean close_behind) { GnomeVFSURI *current_uri; GnomeVFSURI *parent_uri; @@ -379,7 +379,7 @@ nautilus_window_go_up (NautilusWindow *window) selection = g_list_prepend (NULL, g_strdup (window->details->location)); - nautilus_window_open_location_with_selection (window, parent_uri_string, selection); + nautilus_window_open_location_with_selection (window, parent_uri_string, selection, close_behind); g_free (parent_uri_string); eel_g_list_free_deep (selection); @@ -523,7 +523,8 @@ set_initial_window_geometry (NautilusWindow *window) { GdkScreen *screen; guint max_width_for_screen, max_height_for_screen; - + guint default_width, default_height; + screen = gtk_window_get_screen (GTK_WINDOW (window)); /* Don't let GTK determine the minimum size @@ -550,11 +551,14 @@ set_initial_window_geometry (NautilusWindow *window) max_width_for_screen), MIN (NAUTILUS_WINDOW_MIN_HEIGHT, max_height_for_screen)); - + + EEL_CALL_METHOD (NAUTILUS_WINDOW_CLASS, window, + get_default_size, (window, &default_width, &default_height)); + gtk_window_set_default_size (GTK_WINDOW (window), - MIN (NAUTILUS_WINDOW_DEFAULT_WIDTH, + MIN (default_width, max_width_for_screen), - MIN (NAUTILUS_WINDOW_DEFAULT_HEIGHT, + MIN (default_height, max_height_for_screen)); } @@ -747,6 +751,9 @@ nautilus_window_close (NautilusWindow *window) { g_return_if_fail (NAUTILUS_IS_WINDOW (window)); + EEL_CALL_METHOD (NAUTILUS_WINDOW_CLASS, window, + close, (window)); + gtk_widget_destroy (GTK_WIDGET (window)); } @@ -1126,27 +1133,29 @@ compute_default_title (const char *text_uri) { NautilusFile *file; char *title; - - if (text_uri == NULL) { + char *canonical_uri; + + canonical_uri = eel_make_uri_canonical (text_uri); + + if (canonical_uri == NULL) { title = g_strdup (""); - } else if (strcmp (text_uri, "computer://") == 0 || - strcmp (text_uri, "computer:///") == 0) { + } else if (strcmp (canonical_uri, "computer:///") == 0 ) { title = g_strdup (_("Computer")); - } else if (strcmp (text_uri, "network://") == 0 || - strcmp (text_uri, "network:///") == 0) { + } else if (strcmp (canonical_uri, "network:///") == 0 ) { title = g_strdup (_("Network")); - } else if (strcmp (text_uri, "fonts://") == 0 || - strcmp (text_uri, "fonts:///") == 0) { + } else if (strcmp (canonical_uri, "fonts:///") == 0 ) { title = g_strdup (_("Fonts")); - } else if (strcmp (text_uri, "burn://") == 0 || - strcmp (text_uri, "burn:///") == 0) { + } else if (strcmp (canonical_uri, "themes:///") == 0 ) { + title = g_strdup (_("Themes")); + } else if (strcmp (canonical_uri, "burn:///") == 0 ) { title = g_strdup (_("CD Creator")); } else { file = nautilus_file_get (text_uri); title = nautilus_file_get_display_name (file); nautilus_file_unref (file); } - + + g_free (canonical_uri); return title; } @@ -1385,11 +1394,18 @@ nautilus_window_set_viewed_file (NautilusWindow *window, cancel_chose_component_callback (window); if (window->details->viewed_file != NULL) { + if (NAUTILUS_IS_SPATIAL_WINDOW (window)) { + nautilus_file_set_has_open_window (window->details->viewed_file, + FALSE); + } nautilus_file_monitor_remove (window->details->viewed_file, window); } if (file != NULL) { + if (NAUTILUS_IS_SPATIAL_WINDOW (window)) { + nautilus_file_set_has_open_window (file, TRUE); + } attributes = NAUTILUS_FILE_ATTRIBUTE_DISPLAY_NAME; nautilus_file_monitor_add (file, window, attributes); } diff --git a/src/nautilus-window.h b/src/nautilus-window.h index a9be2a192..cd4791600 100644 --- a/src/nautilus-window.h +++ b/src/nautilus-window.h @@ -76,6 +76,8 @@ typedef struct { void (* set_throbber_active) (NautilusWindow *window, gboolean active); void (* prompt_for_location) (NautilusWindow *window); + void (* get_default_size) (NautilusWindow *window, guint *default_width, guint *default_height); + void (* close) (NautilusWindow *window); } NautilusWindowClass; typedef enum { @@ -121,7 +123,8 @@ char * nautilus_window_get_location (NautilusWindow *window void nautilus_window_go_to (NautilusWindow *window, const char *location); void nautilus_window_go_home (NautilusWindow *window); -void nautilus_window_go_up (NautilusWindow *window); +void nautilus_window_go_up (NautilusWindow *window, + gboolean close_behind); void nautilus_window_prompt_for_location (NautilusWindow *window); void nautilus_window_launch_cd_burner (NautilusWindow *window); void nautilus_window_update_title (NautilusWindow *window); |