diff options
Diffstat (limited to 'src')
32 files changed, 2826 insertions, 420 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3e19e7b5a..4a2c4c83e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -99,6 +99,10 @@ nautilus_SOURCES = \ nautilus-places-sidebar.h \ nautilus-property-browser.c \ nautilus-property-browser.h \ + nautilus-query-editor.c \ + nautilus-query-editor.h \ + nautilus-search-bar.c \ + nautilus-search-bar.h \ nautilus-self-check-functions.c \ nautilus-self-check-functions.h \ nautilus-shell.c \ diff --git a/src/file-manager/fm-actions.h b/src/file-manager/fm-actions.h index a9128537b..761591ed9 100644 --- a/src/file-manager/fm-actions.h +++ b/src/file-manager/fm-actions.h @@ -36,6 +36,8 @@ #define FM_ACTION_SELF_PROPERTIES "SelfProperties" #define FM_ACTION_NO_TEMPLATES "No Templates" #define FM_ACTION_EMPTY_TRASH "Empty Trash" +#define FM_ACTION_SAVE_SEARCH "Save Search" +#define FM_ACTION_SAVE_SEARCH_AS "Save Search As" #define FM_ACTION_CUT "Cut" #define FM_ACTION_LOCATION_CUT "LocationCut" #define FM_ACTION_COPY "Copy" diff --git a/src/file-manager/fm-directory-view.c b/src/file-manager/fm-directory-view.c index 8b97f5b83..b612491a8 100644 --- a/src/file-manager/fm-directory-view.c +++ b/src/file-manager/fm-directory-view.c @@ -57,7 +57,9 @@ #include <gtk/gtkselection.h> #include <gtk/gtksignal.h> #include <gtk/gtkstock.h> +#include <gtk/gtktable.h> #include <gtk/gtkmessagedialog.h> +#include <gtk/gtkfilechooserbutton.h> #include <gtk/gtkhbox.h> #include <gtk/gtktoggleaction.h> #include <gtk/gtkentry.h> @@ -78,10 +80,12 @@ #include <libnautilus-private/nautilus-clipboard-monitor.h> #include <libnautilus-private/nautilus-desktop-icon-file.h> #include <libnautilus-private/nautilus-desktop-directory.h> +#include <libnautilus-private/nautilus-search-directory.h> #include <libnautilus-private/nautilus-directory-background.h> #include <libnautilus-private/nautilus-directory.h> #include <libnautilus-private/nautilus-dnd.h> #include <libnautilus-private/nautilus-file-attributes.h> +#include <libnautilus-private/nautilus-file-changes-queue.h> #include <libnautilus-private/nautilus-file-dnd.h> #include <libnautilus-private/nautilus-file-operations.h> #include <libnautilus-private/nautilus-file-utilities.h> @@ -89,6 +93,7 @@ #include <libnautilus-private/nautilus-global-preferences.h> #include <libnautilus-private/nautilus-icon-factory.h> #include <libnautilus-private/nautilus-link.h> +#include <libnautilus-private/nautilus-marshal.h> #include <libnautilus-private/nautilus-metadata.h> #include <libnautilus-private/nautilus-mime-actions.h> #include <libnautilus-private/nautilus-module.h> @@ -270,6 +275,8 @@ struct FMDirectoryViewDetails GList *subdirectory_list; + gboolean allow_moves; + GdkPoint context_menu_position; }; @@ -295,6 +302,11 @@ typedef struct { gboolean cancelled; } ActivateParameters; +typedef struct { + NautilusFile *file; + NautilusDirectory *directory; +} FileAndDirectory; + enum { GNOME_COPIED_FILES, UTF8_STRING @@ -450,6 +462,79 @@ typedef struct { } CreateTemplateParameters; + +static GList * +file_and_directory_list_to_files (GList *fad_list) +{ + GList *res, *l; + FileAndDirectory *fad; + + res = NULL; + for (l = fad_list; l != NULL; l = l->next) { + fad = l->data; + res = g_list_prepend (res, nautilus_file_ref (fad->file)); + } + return g_list_reverse (res); +} + + +static GList * +file_and_directory_list_from_files (NautilusDirectory *directory, GList *files) +{ + GList *res, *l; + FileAndDirectory *fad; + + res = NULL; + for (l = files; l != NULL; l = l->next) { + fad = g_new0 (FileAndDirectory, 1); + fad->directory = nautilus_directory_ref (directory); + fad->file = nautilus_file_ref (l->data); + res = g_list_prepend (res, fad); + } + return g_list_reverse (res); +} + +static void +file_and_directory_free (FileAndDirectory *fad) +{ + nautilus_directory_unref (fad->directory); + nautilus_file_unref (fad->file); + g_free (fad); +} + + +static void +file_and_directory_list_free (GList *list) +{ + GList *l; + + for (l = list; l != NULL; l = l->next) { + file_and_directory_free (l->data); + } +} + +static gboolean +file_and_directory_equal (gconstpointer v1, + gconstpointer v2) +{ + const FileAndDirectory *fad1, *fad2; + fad1 = v1; + fad2 = v2; + + return (fad1->file == fad2->file && + fad1->directory == fad2->directory); +} + +static guint +file_and_directory_hash (gconstpointer v) +{ + const FileAndDirectory *fad; + + fad = v; + return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory); +} + + static ApplicationLaunchParameters * application_launch_parameters_new (GnomeVFSMimeApplication *application, NautilusFile *file, @@ -1133,6 +1218,130 @@ action_show_hidden_files_callback (GtkAction *action, } static void +action_save_search_callback (GtkAction *action, + gpointer callback_data) +{ + NautilusSearchDirectory *search; + FMDirectoryView *directory_view; + + directory_view = FM_DIRECTORY_VIEW (callback_data); + + if (directory_view->details->model && + NAUTILUS_IS_SEARCH_DIRECTORY (directory_view->details->model)) { + search = NAUTILUS_SEARCH_DIRECTORY (directory_view->details->model); + nautilus_search_directory_save_search (search); + + /* Save search is disabled */ + schedule_update_menus (directory_view); + } +} + +static void +query_name_entry_changed_cb (GtkWidget *entry, GtkWidget *button) +{ + const char *text; + gboolean sensitive; + + text = gtk_entry_get_text (GTK_ENTRY (entry)); + + sensitive = (text != NULL) && (*text != 0); + + gtk_widget_set_sensitive (button, sensitive); +} + + +static void +action_save_search_as_callback (GtkAction *action, + gpointer callback_data) +{ + FMDirectoryView *directory_view; + NautilusSearchDirectory *search; + NautilusQuery *query; + GtkWidget *dialog, *table, *label, *entry, *chooser, *save_button; + const char *entry_text; + char *filename, *filename_utf8, *dirname, *path, *uri; + + directory_view = FM_DIRECTORY_VIEW (callback_data); + + if (directory_view->details->model && + NAUTILUS_IS_SEARCH_DIRECTORY (directory_view->details->model)) { + search = NAUTILUS_SEARCH_DIRECTORY (directory_view->details->model); + + query = nautilus_search_directory_get_query (search); + + dialog = gtk_dialog_new_with_buttons (_("Save search"), + fm_directory_view_get_containing_window (directory_view), + GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + NULL); + save_button = gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_SAVE, GTK_RESPONSE_OK); + + table = gtk_table_new (2, 2, FALSE); + gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (dialog)->vbox), table); + gtk_widget_show (table); + + label = gtk_label_new (_("Query name:")); + gtk_table_attach_defaults (GTK_TABLE (table), label, + 0, 1, 0, 1); + gtk_widget_show (label); + entry = gtk_entry_new (); + gtk_table_attach_defaults (GTK_TABLE (table), entry, + 1, 2, 0, 1); + + gtk_widget_set_sensitive (save_button, FALSE); + g_signal_connect (entry, "changed", + G_CALLBACK (query_name_entry_changed_cb), save_button); + + gtk_widget_show (entry); + label = gtk_label_new (_("Folder:")); + gtk_table_attach_defaults (GTK_TABLE (table), label, + 0, 1, 1, 2); + gtk_widget_show (label); + + chooser = gtk_file_chooser_button_new (_("Select folder to save search in"), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + gtk_table_attach_defaults (GTK_TABLE (table), chooser, + 1, 2, 1, 2); + gtk_widget_show (chooser); + + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE); + + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), + g_get_home_dir ()); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + entry_text = gtk_entry_get_text (GTK_ENTRY (entry)); + if (g_str_has_suffix (entry_text, NAUTILUS_SAVED_SEARCH_EXTENSION)) { + filename_utf8 = g_strdup (entry_text); + } else { + filename_utf8 = g_strconcat (entry_text, NAUTILUS_SAVED_SEARCH_EXTENSION, NULL); + } + + filename = g_filename_from_utf8 (filename_utf8, -1, NULL, NULL, NULL); + g_free (filename_utf8); + + dirname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); + + path = g_build_filename (dirname, filename, NULL); + g_free (filename); + g_free (dirname); + + uri = gnome_vfs_get_uri_from_local_path (path); + g_free (path); + + nautilus_search_directory_save_to_file (search, uri); + nautilus_file_changes_queue_file_added (uri); + nautilus_file_changes_consume_changes (TRUE); + g_free (uri); + } + + gtk_widget_destroy (dialog); + } +} + + +static void action_empty_trash_callback (GtkAction *action, gpointer callback_data) { @@ -1663,7 +1872,11 @@ fm_directory_view_init (FMDirectoryView *view) view->details = g_new0 (FMDirectoryViewDetails, 1); - view->details->non_ready_files = g_hash_table_new (NULL, NULL); + view->details->non_ready_files = + g_hash_table_new_full (file_and_directory_hash, + file_and_directory_equal, + (GDestroyNotify)file_and_directory_free, + NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view), GTK_POLICY_AUTOMATIC, @@ -2023,6 +2236,12 @@ fm_directory_view_send_selection_change (FMDirectoryView *view) view->details->send_selection_change_to_shell = FALSE; } +gboolean +fm_directory_view_get_allow_moves (FMDirectoryView *view) +{ + return view->details->allow_moves; +} + static void fm_directory_view_load_location (NautilusView *nautilus_view, const char *location) @@ -2032,6 +2251,12 @@ fm_directory_view_load_location (NautilusView *nautilus_view, directory_view = FM_DIRECTORY_VIEW (nautilus_view); + if (eel_uri_is_search (location)) { + directory_view->details->allow_moves = FALSE; + } else { + directory_view->details->allow_moves = TRUE; + } + directory = nautilus_directory_get (location); load_directory (directory_view, directory); nautilus_directory_unref (directory); @@ -2152,6 +2377,7 @@ debuting_uri_data_free (DebutingUriData *data) static void debuting_uri_add_file_callback (FMDirectoryView *view, NautilusFile *new_file, + NautilusDirectory *directory, DebutingUriData *data) { char *uri; @@ -2159,7 +2385,7 @@ debuting_uri_add_file_callback (FMDirectoryView *view, uri = nautilus_file_get_uri (new_file); if (g_hash_table_remove (data->debuting_uris, uri)) { - g_object_ref (new_file); + nautilus_file_ref (new_file); data->added_files = g_list_prepend (data->added_files, new_file); if (g_hash_table_size (data->debuting_uris) == 0) { @@ -2190,7 +2416,7 @@ copy_move_done_data_free (CopyMoveDoneData *data) } static void -pre_copy_move_add_file_callback (FMDirectoryView *view, NautilusFile *new_file, CopyMoveDoneData *data) +pre_copy_move_add_file_callback (FMDirectoryView *view, NautilusFile *new_file, NautilusDirectory *directory, CopyMoveDoneData *data) { g_object_ref (new_file); data->added_files = g_list_prepend (data->added_files, new_file); @@ -2326,29 +2552,21 @@ copy_move_done_callback (GHashTable *debuting_uris, gpointer data) } static gboolean -real_file_still_belongs (FMDirectoryView *view, NautilusFile *file) +real_file_still_belongs (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { - GList *node; - - if (nautilus_directory_contains_file (view->details->model, file)) { - return TRUE; - } - - for (node = view->details->subdirectory_list; node != NULL; node = node->next) { - if (nautilus_directory_contains_file (NAUTILUS_DIRECTORY (node->data), - file)) { - return TRUE; - } + if (view->details->model != directory && + g_list_find (view->details->subdirectory_list, directory) == NULL) { + return FALSE; } - return FALSE; + return nautilus_directory_contains_file (directory, file); } static gboolean -still_should_show_file (FMDirectoryView *view, NautilusFile *file) +still_should_show_file (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { return fm_directory_view_should_show_file (view, file) - && EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, file_still_belongs, (view, file)); + && EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, file_still_belongs, (view, file, directory)); } static gboolean @@ -2357,6 +2575,31 @@ ready_to_load (NautilusFile *file) return nautilus_icon_factory_is_icon_ready_for_file (file); } +static int +compare_files_cover (gconstpointer a, gconstpointer b, gpointer callback_data) +{ + const FileAndDirectory *fad1, *fad2; + FMDirectoryView *view; + + view = callback_data; + fad1 = a; fad2 = b; + + if (fad1->directory < fad2->directory) { + return -1; + } else if (fad1->directory > fad2->directory) { + return 1; + } else { + return EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, compare_files, + (view, fad1->file, fad2->file)); + } +} +static void +sort_files (FMDirectoryView *view, GList **list) +{ + *list = g_list_sort_with_data (*list, compare_files_cover, view); + +} + /* Go through all the new added and changed files. * Put any that are not ready to load in the non_ready_files hash table. * Add all the rest to the old_added_files and old_changed_files lists. @@ -2367,8 +2610,8 @@ process_new_files (FMDirectoryView *view) { GList *new_added_files, *new_changed_files, *old_added_files, *old_changed_files; GHashTable *non_ready_files; - GList *node; - NautilusFile *file; + GList *node, *next; + FileAndDirectory *pending; gboolean in_non_ready; new_added_files = view->details->new_added_files; @@ -2384,56 +2627,54 @@ process_new_files (FMDirectoryView *view) /* Newly added files go into the old_added_files list if they're * ready, and into the hash table if they're not. */ - for (node = new_added_files; node != NULL; node = node->next) { - file = NAUTILUS_FILE (node->data); - in_non_ready = g_hash_table_lookup (non_ready_files, file) != NULL; - if (fm_directory_view_should_show_file (view, file)) { - if (ready_to_load (file)) { + for (node = new_added_files; node != NULL; node = next) { + next = node->next; + pending = (FileAndDirectory *)node->data; + in_non_ready = g_hash_table_lookup (non_ready_files, pending) != NULL; + if (fm_directory_view_should_show_file (view, pending->file)) { + if (ready_to_load (pending->file)) { if (in_non_ready) { - g_hash_table_remove (non_ready_files, file); - nautilus_file_unref (file); + g_hash_table_remove (non_ready_files, pending); } - nautilus_file_ref (file); - old_added_files = g_list_prepend (old_added_files, file); + new_added_files = g_list_delete_link (new_added_files, node); + old_added_files = g_list_prepend (old_added_files, pending); } else { if (!in_non_ready) { - nautilus_file_ref (file); - g_hash_table_insert (non_ready_files, file, file); + new_added_files = g_list_delete_link (new_added_files, node); + g_hash_table_insert (non_ready_files, pending, pending); } } } } - nautilus_file_list_free (new_added_files); + file_and_directory_list_free (new_added_files); /* Newly changed files go into the old_added_files list if they're ready * and were seen non-ready in the past, into the old_changed_files list * if they are read and were not seen non-ready in the past, and into * the hash table if they're not ready. */ - for (node = new_changed_files; node != NULL; node = node->next) { - file = NAUTILUS_FILE (node->data); - if (!still_should_show_file (view, file) || ready_to_load (file)) { - if (g_hash_table_lookup (non_ready_files, file) != NULL) { - g_hash_table_remove (non_ready_files, file); - nautilus_file_unref (file); - if (still_should_show_file (view, file)) { - nautilus_file_ref (file); - old_added_files = g_list_prepend (old_added_files, file); + for (node = new_changed_files; node != NULL; node = next) { + next = node->next; + pending = (FileAndDirectory *)node->data; + if (!still_should_show_file (view, pending->file, pending->directory) || ready_to_load (pending->file)) { + if (g_hash_table_lookup (non_ready_files, pending) != NULL) { + g_hash_table_remove (non_ready_files, pending); + if (still_should_show_file (view, pending->file, pending->directory)) { + new_changed_files = g_list_delete_link (new_changed_files, node); + old_added_files = g_list_prepend (old_added_files, pending); } - } else if (fm_directory_view_should_show_file(view, file)) { - nautilus_file_ref (file); - old_changed_files = g_list_prepend - (old_changed_files, file); + } else if (fm_directory_view_should_show_file (view, pending->file)) { + new_changed_files = g_list_delete_link (new_changed_files, node); + old_changed_files = g_list_prepend (old_changed_files, pending); } } } - nautilus_file_list_free (new_changed_files); + file_and_directory_list_free (new_changed_files); /* If any files were added to old_added_files, then resort it. */ if (old_added_files != view->details->old_added_files) { view->details->old_added_files = old_added_files; - EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, sort_files, - (view, &view->details->old_added_files)); + sort_files (view, &view->details->old_added_files); } /* Resort old_changed_files too, since file attributes @@ -2441,8 +2682,7 @@ process_new_files (FMDirectoryView *view) */ if (old_changed_files != view->details->old_changed_files) { view->details->old_changed_files = old_changed_files; - EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, sort_files, - (view, &view->details->old_changed_files)); + sort_files (view, &view->details->old_changed_files); } } @@ -2451,8 +2691,8 @@ static void process_old_files (FMDirectoryView *view) { GList *files_added, *files_changed, *node; - NautilusFile *file; - GList *selection; + FileAndDirectory *pending; + GList *selection, *files; gboolean send_selection_change; files_added = view->details->old_added_files; @@ -2464,37 +2704,34 @@ process_old_files (FMDirectoryView *view) g_signal_emit (view, signals[BEGIN_FILE_CHANGES], 0); for (node = files_added; node != NULL; node = node->next) { - file = NAUTILUS_FILE (node->data); + pending = node->data; g_signal_emit (view, - signals[ADD_FILE], 0, file); + signals[ADD_FILE], 0, pending->file, pending->directory); } for (node = files_changed; node != NULL; node = node->next) { - file = NAUTILUS_FILE (node->data); - + pending = node->data; g_signal_emit (view, - signals[still_should_show_file (view, file) + signals[still_should_show_file (view, pending->file, pending->directory) ? FILE_CHANGED : REMOVE_FILE], 0, - file); + pending->file, pending->directory); } g_signal_emit (view, signals[END_FILE_CHANGES], 0); if (files_changed != NULL) { selection = fm_directory_view_get_selection (view); + files = file_and_directory_list_to_files (files_changed); send_selection_change = eel_g_lists_sort_and_check_for_intersection - (&files_changed, &selection); + (&files, &selection); + nautilus_file_list_free (files); nautilus_file_list_free (selection); } - - nautilus_file_list_free (view->details->old_added_files); + + file_and_directory_list_free (view->details->old_added_files); view->details->old_added_files = NULL; - /* We free files_changed here instead of view->details->old_changed_files - * because the call to eel_g_lists_sort_and_check_for_intersection might - * change the first element, and so we might lose files to free. - */ - nautilus_file_list_free (files_changed); + file_and_directory_list_free (view->details->old_changed_files); view->details->old_changed_files = NULL; } @@ -2675,6 +2912,7 @@ unschedule_display_of_pending_files (FMDirectoryView *view) static void queue_pending_files (FMDirectoryView *view, + NautilusDirectory *directory, GList *files, GList **pending_list) { @@ -2698,10 +2936,12 @@ queue_pending_files (FMDirectoryView *view, } } - *pending_list = g_list_concat (nautilus_file_list_copy (files), + + + *pending_list = g_list_concat (file_and_directory_list_from_files (directory, files), *pending_list); - if (! view->details->loading || nautilus_directory_are_all_files_seen (view->details->model)) { + if (! view->details->loading || nautilus_directory_are_all_files_seen (directory)) { schedule_timeout_display_of_pending_files (view, view->details->update_interval); } } @@ -2786,7 +3026,7 @@ files_added_callback (NautilusDirectory *directory, schedule_changes (view); - queue_pending_files (view, files, &view->details->new_added_files); + queue_pending_files (view, directory, files, &view->details->new_added_files); /* The number of items could have changed */ schedule_update_status (view); @@ -2798,12 +3038,12 @@ files_changed_callback (NautilusDirectory *directory, gpointer callback_data) { FMDirectoryView *view; - + view = FM_DIRECTORY_VIEW (callback_data); schedule_changes (view); - queue_pending_files (view, files, &view->details->new_changed_files); + queue_pending_files (view, directory, files, &view->details->new_changed_files); /* The free space or the number of items could have changed */ schedule_update_status (view); @@ -2836,6 +3076,7 @@ done_loading_callback (NautilusDirectory *directory, static void load_error_callback (NautilusDirectory *directory, GnomeVFSResult load_error_code, + const char *load_error_message, gpointer callback_data) { FMDirectoryView *view; @@ -2851,11 +3092,11 @@ load_error_callback (NautilusDirectory *directory, * occurred, so they can handle it in the UI. */ g_signal_emit (view, - signals[LOAD_ERROR], 0, load_error_code); + signals[LOAD_ERROR], 0, load_error_code, load_error_message); } static void -real_load_error (FMDirectoryView *view, GnomeVFSResult result) +real_load_error (FMDirectoryView *view, GnomeVFSResult result, const char *error_message) { g_assert (result != GNOME_VFS_OK); @@ -2868,30 +3109,12 @@ real_load_error (FMDirectoryView *view, GnomeVFSResult result) if (!view->details->reported_load_error) { fm_report_error_loading_directory (fm_directory_view_get_directory_as_file (view), - result, + result, error_message, fm_directory_view_get_containing_window (view)); } view->details->reported_load_error = TRUE; } -/** - * fm_directory_queue_notice_file_change - * - * Called by a subclass to put a file into the queue of files to update. - * This is only necessary when the subclass is monitoring files other than - * the ones in the directory for this location. - */ -void -fm_directory_view_queue_file_change (FMDirectoryView *view, NautilusFile *file) -{ - GList singleton_list; - - singleton_list.data = file; - singleton_list.next = NULL; - singleton_list.prev = NULL; - queue_pending_files (view, &singleton_list, &view->details->new_changed_files); -} - void fm_directory_view_add_subdirectory (FMDirectoryView *view, NautilusDirectory*directory) @@ -2908,7 +3131,7 @@ fm_directory_view_add_subdirectory (FMDirectoryView *view, NAUTILUS_FILE_ATTRIBUTE_MIME_TYPE | NAUTILUS_FILE_ATTRIBUTE_DISPLAY_NAME | NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO; - + nautilus_directory_file_monitor_add (directory, &view->details->model, view->details->show_hidden_files, @@ -2929,7 +3152,7 @@ fm_directory_view_add_subdirectory (FMDirectoryView *view, void fm_directory_view_remove_subdirectory (FMDirectoryView *view, - NautilusDirectory*directory) + NautilusDirectory*directory) { g_assert (g_list_find (view->details->subdirectory_list, directory)); @@ -3733,7 +3956,8 @@ typedef struct { static void -track_newly_added_uris (FMDirectoryView *view, NautilusFile *new_file, gpointer user_data) +track_newly_added_uris (FMDirectoryView *view, NautilusFile *new_file, + NautilusDirectory *directory, gpointer user_data) { NewFolderData *data; @@ -3752,7 +3976,7 @@ new_folder_done (const char *new_folder_uri, gpointer user_data) NewFolderData *data; data = (NewFolderData *)user_data; - + directory_view = data->directory_view; if (directory_view == NULL) { @@ -3792,6 +4016,7 @@ new_folder_done (const char *new_folder_uri, gpointer user_data) (GClosureNotify)g_free, G_CONNECT_AFTER); } + nautilus_file_unref (file); fail: g_hash_table_destroy (data->added_uris); @@ -6416,6 +6641,14 @@ static const GtkActionEntry directory_view_entries[] = { N_("Open File and Close window"), "<alt><shift>Down", /* label, accelerator */ NULL, /* tooltip */ G_CALLBACK (action_open_close_parent_callback) }, + { "Save Search", NULL, /* name, stock id */ + N_("Sa_ve Search"), NULL, /* label, accelerator */ + N_("Save the edited search"), /* tooltip */ + G_CALLBACK (action_save_search_callback) }, + { "Save Search As", NULL, /* name, stock id */ + N_("Sa_ve Search As..."), NULL, /* label, accelerator */ + N_("Save the current search as a file"), /* tooltip */ + G_CALLBACK (action_save_search_as_callback) }, /* Location-specific actions */ { FM_ACTION_LOCATION_OPEN_ALTERNATE, NULL, /* name, stock id */ @@ -6947,6 +7180,9 @@ real_update_menus (FMDirectoryView *view) gboolean vfolder_directory; gboolean show_open_alternate; gboolean can_open; + gboolean show_save_search; + gboolean save_search_sensitive; + gboolean show_save_search_as; ActivationAction activation_action; GtkAction *action; @@ -7119,6 +7355,30 @@ real_update_menus (FMDirectoryView *view) gtk_action_set_sensitive (action, !nautilus_trash_monitor_is_empty ()); gtk_action_set_visible (action, should_show_empty_trash (view)); + show_save_search = FALSE; + save_search_sensitive = FALSE; + show_save_search_as = FALSE; + if (view->details->model && + NAUTILUS_IS_SEARCH_DIRECTORY (view->details->model)) { + NautilusSearchDirectory *search; + + search = NAUTILUS_SEARCH_DIRECTORY (view->details->model); + if (nautilus_search_directory_is_saved_search (search)) { + show_save_search = TRUE; + save_search_sensitive = nautilus_search_directory_is_modified (search); + } else { + show_save_search_as = TRUE; + } + } + action = gtk_action_group_get_action (view->details->dir_action_group, + FM_ACTION_SAVE_SEARCH); + gtk_action_set_visible (action, show_save_search); + gtk_action_set_sensitive (action, save_search_sensitive); + action = gtk_action_group_get_action (view->details->dir_action_group, + FM_ACTION_SAVE_SEARCH_AS); + gtk_action_set_visible (action, show_save_search_as); + + action = gtk_action_group_get_action (view->details->dir_action_group, FM_ACTION_SELECT_ALL); gtk_action_set_sensitive (action, !fm_directory_view_is_empty (view)); @@ -8334,9 +8594,8 @@ fm_directory_view_reveal_selection (FMDirectoryView *view) } static gboolean -unref_key_and_remove (gpointer key, gpointer value, gpointer callback_data) +remove_all (gpointer key, gpointer value, gpointer callback_data) { - nautilus_file_unref (key); return TRUE; } @@ -8356,14 +8615,14 @@ fm_directory_view_stop (FMDirectoryView *view) reset_update_interval (view); /* Free extra undisplayed files */ - nautilus_file_list_free (view->details->new_added_files); + file_and_directory_list_free (view->details->new_added_files); view->details->new_added_files = NULL; - nautilus_file_list_free (view->details->new_changed_files); + file_and_directory_list_free (view->details->new_changed_files); view->details->new_changed_files = NULL; - g_hash_table_foreach_remove (view->details->non_ready_files, unref_key_and_remove, NULL); - nautilus_file_list_free (view->details->old_added_files); + g_hash_table_foreach_remove (view->details->non_ready_files, remove_all, NULL); + file_and_directory_list_free (view->details->old_added_files); view->details->old_added_files = NULL; - nautilus_file_list_free (view->details->old_changed_files); + file_and_directory_list_free (view->details->old_changed_files); view->details->old_changed_files = NULL; eel_g_list_free_deep (view->details->pending_uris_selected); view->details->pending_uris_selected = NULL; @@ -8394,11 +8653,29 @@ fm_directory_view_is_empty (FMDirectoryView *view) is_empty, (view)); } +gboolean +fm_directory_view_is_editable (FMDirectoryView *view) +{ + NautilusDirectory *directory; + + directory = fm_directory_view_get_model (view); + + if (directory != NULL) { + return nautilus_directory_is_editable (directory); + } + + return TRUE; +} + static gboolean real_is_read_only (FMDirectoryView *view) { NautilusFile *file; - + + if (!fm_directory_view_is_editable (view)) { + return TRUE; + } + file = fm_directory_view_get_directory_as_file (view); if (file != NULL) { return !nautilus_file_can_write (file); @@ -8453,7 +8730,7 @@ real_accepts_dragged_files (FMDirectoryView *view) { g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE); - return TRUE; + return !fm_directory_view_is_read_only (view); } gboolean @@ -9085,10 +9362,6 @@ fm_directory_view_handle_text_drop (FMDirectoryView *view, } -static void -real_sort_files (FMDirectoryView *view, GList **files) -{ -} static GArray * real_get_selected_icon_locations (FMDirectoryView *view) @@ -9180,8 +9453,8 @@ fm_directory_view_class_init (FMDirectoryViewClass *klass) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (FMDirectoryViewClass, add_file), NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, NAUTILUS_TYPE_FILE); + nautilus_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY); signals[BEGIN_FILE_CHANGES] = g_signal_new ("begin_file_changes", G_TYPE_FROM_CLASS (klass), @@ -9236,24 +9509,24 @@ fm_directory_view_class_init (FMDirectoryViewClass *klass) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (FMDirectoryViewClass, file_changed), NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, NAUTILUS_TYPE_FILE); + nautilus_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY); signals[LOAD_ERROR] = g_signal_new ("load_error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (FMDirectoryViewClass, load_error), NULL, NULL, - g_cclosure_marshal_VOID__INT, - G_TYPE_NONE, 1, G_TYPE_INT); + nautilus_marshal_VOID__INT_STRING, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_STRING); signals[REMOVE_FILE] = g_signal_new ("remove_file", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (FMDirectoryViewClass, remove_file), NULL, NULL, - g_cclosure_marshal_VOID__OBJECT, - G_TYPE_NONE, 1, NAUTILUS_TYPE_FILE); + nautilus_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY); klass->accepts_dragged_files = real_accepts_dragged_files; klass->file_limit_reached = real_file_limit_reached; @@ -9262,7 +9535,6 @@ fm_directory_view_class_init (FMDirectoryViewClass *klass) klass->get_selected_icon_locations = real_get_selected_icon_locations; klass->is_read_only = real_is_read_only; klass->load_error = real_load_error; - klass->sort_files = real_sort_files; klass->can_rename_file = can_rename_file; klass->start_renaming_file = start_renaming_file; klass->supports_creating_files = real_supports_creating_files; diff --git a/src/file-manager/fm-directory-view.h b/src/file-manager/fm-directory-view.h index 1b0d602fa..db08a6d84 100644 --- a/src/file-manager/fm-directory-view.h +++ b/src/file-manager/fm-directory-view.h @@ -76,16 +76,19 @@ struct FMDirectoryViewClass { * It must be replaced by each subclass. */ void (* add_file) (FMDirectoryView *view, - NautilusFile *file); + NautilusFile *file, + NautilusDirectory *directory); void (* remove_file) (FMDirectoryView *view, - NautilusFile *file); + NautilusFile *file, + NautilusDirectory *directory); /* The 'file_changed' signal is emitted to signal a change in a file, * including the file being removed. * It must be replaced by each subclass. */ void (* file_changed) (FMDirectoryView *view, - NautilusFile *file); + NautilusFile *file, + NautilusDirectory *directory); /* The 'end_file_changes' signal is emitted after a set of files * are added to the view. It can be replaced by a subclass to do any @@ -118,7 +121,8 @@ struct FMDirectoryViewClass { * load failures like ACCESS_DENIED. */ void (* load_error) (FMDirectoryView *view, - GnomeVFSResult result); + GnomeVFSResult result, + const char *error_message); /* Function pointers that don't have corresponding signals */ @@ -213,8 +217,9 @@ struct FMDirectoryViewClass { * to provide a sorting order to determine which files should be * presented when only a partial list is provided. */ - void (* sort_files) (FMDirectoryView *view, - GList **files); + int (* compare_files) (FMDirectoryView *view, + NautilusFile *a, + NautilusFile *b); /* get_emblem_names_to_exclude is a function pointer that subclasses * may override to specify a set of emblem names that should not @@ -290,7 +295,8 @@ struct FMDirectoryViewClass { gboolean select_all); gboolean (* file_still_belongs) (FMDirectoryView *view, - NautilusFile *file); + NautilusFile *file, + NautilusDirectory *directory); /* Preference change callbacks, overriden by icon and list views. * Icon and list views respond by synchronizing to the new preference @@ -382,6 +388,7 @@ NautilusDirectory *fm_directory_view_get_model (FMDirect GtkWindow *fm_directory_view_get_containing_window (FMDirectoryView *view); NautilusFile *fm_directory_view_get_directory_as_file (FMDirectoryView *view); EelBackground * fm_directory_view_get_background (FMDirectoryView *view); +gboolean fm_directory_view_get_allow_moves (FMDirectoryView *view); void fm_directory_view_pop_up_background_context_menu (FMDirectoryView *view, GdkEventButton *event); void fm_directory_view_pop_up_selection_context_menu (FMDirectoryView *view, @@ -420,4 +427,6 @@ void fm_directory_view_add_subdirectory (FMDirecto void fm_directory_view_remove_subdirectory (FMDirectoryView *view, NautilusDirectory*directory); +gboolean fm_directory_view_is_editable (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 e2a1c7d16..789d591cb 100644 --- a/src/file-manager/fm-error-reporting.c +++ b/src/file-manager/fm-error-reporting.c @@ -41,31 +41,36 @@ static void cancel_rename (NautilusFile *file); void fm_report_error_loading_directory (NautilusFile *file, GnomeVFSResult error, + const char *error_message, GtkWindow *parent_window) { char *file_name; char *message; - if (error == GNOME_VFS_OK) { + if (error_message == NULL && error == GNOME_VFS_OK) { return; } file_name = nautilus_file_get_display_name (file); - switch (error) { - case GNOME_VFS_ERROR_ACCESS_DENIED: - message = g_strdup_printf (_("You do not have the permissions necessary to view the contents of \"%s\"."), - file_name); - break; - case GNOME_VFS_ERROR_NOT_FOUND: - message = g_strdup_printf (_("\"%s\" couldn't be found. Perhaps it has recently been deleted."), - file_name); - break; - default: - /* We should invent decent error messages for every case we actually experience. */ - g_warning ("Hit unhandled case %d (%s) in fm_report_error_loading_directory", - error, gnome_vfs_result_to_string (error)); - message = g_strdup_printf (_("Sorry, couldn't display all the contents of \"%s\"."), file_name); + if (!error_message) { + switch (error) { + case GNOME_VFS_ERROR_ACCESS_DENIED: + message = g_strdup_printf (_("You do not have the permissions necessary to view the contents of \"%s\"."), + file_name); + break; + case GNOME_VFS_ERROR_NOT_FOUND: + message = g_strdup_printf (_("\"%s\" couldn't be found. Perhaps it has recently been deleted."), + file_name); + break; + default: + /* We should invent decent error messages for every case we actually experience. */ + g_warning ("Hit unhandled case %d (%s) in fm_report_error_loading_directory", + error, gnome_vfs_result_to_string (error)); + message = g_strdup_printf (_("Sorry, couldn't display all the contents of \"%s\"."), file_name); + } + } else { + message = g_strdup (error_message); } eel_show_error_dialog (_("The folder contents could not be displayed."), message, parent_window); diff --git a/src/file-manager/fm-error-reporting.h b/src/file-manager/fm-error-reporting.h index a1b44bf66..a4665a3fb 100644 --- a/src/file-manager/fm-error-reporting.h +++ b/src/file-manager/fm-error-reporting.h @@ -32,6 +32,7 @@ void fm_report_error_loading_directory (NautilusFile *file, GnomeVFSResult error_code, + const char *error_message, GtkWindow *parent_window); void fm_report_error_renaming_file (NautilusFile *file, const char *new_name, diff --git a/src/file-manager/fm-icon-view.c b/src/file-manager/fm-icon-view.c index 5c849c0d2..8ad26b810 100644 --- a/src/file-manager/fm-icon-view.c +++ b/src/file-manager/fm-icon-view.c @@ -508,8 +508,10 @@ should_show_file_on_screen (FMDirectoryView *view, NautilusFile *file) } static void -fm_icon_view_remove_file (FMDirectoryView *view, NautilusFile *file) +fm_icon_view_remove_file (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { + g_assert (directory == fm_directory_view_get_model (view)); + if (nautilus_icon_container_remove (get_icon_container (FM_ICON_VIEW (view)), NAUTILUS_ICON_CONTAINER_ICON_DATA (file))) { nautilus_file_unref (file); @@ -517,10 +519,12 @@ fm_icon_view_remove_file (FMDirectoryView *view, NautilusFile *file) } static void -fm_icon_view_add_file (FMDirectoryView *view, NautilusFile *file) +fm_icon_view_add_file (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { FMIconView *icon_view; NautilusIconContainer *icon_container; + + g_assert (directory == fm_directory_view_get_model (view)); icon_view = FM_ICON_VIEW (view); icon_container = get_icon_container (icon_view); @@ -549,10 +553,12 @@ fm_icon_view_flush_added_files (FMDirectoryView *view) } static void -fm_icon_view_file_changed (FMDirectoryView *view, NautilusFile *file) +fm_icon_view_file_changed (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { FMIconView *icon_view; + g_assert (directory == fm_directory_view_get_model (view)); + g_return_if_fail (view != NULL); icon_view = FM_ICON_VIEW (view); @@ -564,7 +570,7 @@ fm_icon_view_file_changed (FMDirectoryView *view, NautilusFile *file) } if (!should_show_file_on_screen (view, file)) { - fm_icon_view_remove_file (view, file); + fm_icon_view_remove_file (view, file, directory); } else { nautilus_icon_container_request_update @@ -1055,6 +1061,9 @@ fm_icon_view_begin_loading (FMDirectoryView *view) file = fm_directory_view_get_directory_as_file (view); icon_container = GTK_WIDGET (get_icon_container (icon_view)); + nautilus_icon_container_set_allow_moves (NAUTILUS_ICON_CONTAINER (icon_container), + fm_directory_view_get_allow_moves (view)); + /* kill any sound preview process that is ongoing */ preview_audio (icon_view, NULL, FALSE); @@ -1533,6 +1542,7 @@ fm_icon_view_update_menus (FMDirectoryView *view) int selection_count; GtkAction *action; NautilusIconContainer *icon_container; + gboolean editable; icon_view = FM_ICON_VIEW (view); @@ -1559,8 +1569,13 @@ fm_icon_view_update_menus (FMDirectoryView *view) gtk_action_set_sensitive (action, icon_container != NULL && nautilus_icon_container_is_stretched (icon_container)); - + nautilus_file_list_free (selection); + + editable = fm_directory_view_is_editable (view); + action = gtk_action_group_get_action (icon_view->details->icon_action_group, + FM_ACTION_MANUAL_LAYOUT); + gtk_action_set_sensitive (action, editable); } static void @@ -2004,6 +2019,15 @@ fm_icon_view_compare_files (FMIconView *icon_view, icon_view->details->sort_reversed); } +static int +compare_files (FMDirectoryView *icon_view, + NautilusFile *a, + NautilusFile *b) +{ + return fm_icon_view_compare_files ((FMIconView *)icon_view, a, b); +} + + void fm_icon_view_filter_by_screen (FMIconView *icon_view, gboolean filter) @@ -2019,6 +2043,7 @@ fm_icon_view_screen_changed (GtkWidget *widget, FMDirectoryView *view; GList *files, *l; NautilusFile *file; + NautilusDirectory *directory; NautilusIconContainer *icon_container; if (GTK_WIDGET_CLASS (fm_icon_view_parent_class)->screen_changed) { @@ -2028,14 +2053,15 @@ fm_icon_view_screen_changed (GtkWidget *widget, view = FM_DIRECTORY_VIEW (widget); if (FM_ICON_VIEW (view)->details->filter_by_screen) { icon_container = get_icon_container (FM_ICON_VIEW (view)); - - files = nautilus_directory_get_file_list (fm_directory_view_get_model (view)); + + directory = fm_directory_view_get_model (view); + files = nautilus_directory_get_file_list (directory); for (l = files; l != NULL; l = l->next) { file = l->data; if (!should_show_file_on_screen (view, file)) { - fm_icon_view_remove_file (view, file); + fm_icon_view_remove_file (view, file, directory); } else { if (nautilus_icon_container_add (icon_container, NAUTILUS_ICON_CONTAINER_ICON_DATA (file), @@ -2050,27 +2076,6 @@ fm_icon_view_screen_changed (GtkWidget *widget, } } - -static int -compare_files_cover (gconstpointer a, gconstpointer b, gpointer callback_data) -{ - return fm_icon_view_compare_files (callback_data, - NAUTILUS_FILE (a), - NAUTILUS_FILE (b)); -} - -static void -fm_icon_view_sort_files (FMDirectoryView *view, GList **files) -{ - FMIconView *icon_view; - - icon_view = FM_ICON_VIEW (view); - if (!fm_icon_view_using_auto_layout (icon_view)) { - return; - } - *files = g_list_sort_with_data (*files, compare_files_cover, icon_view); -} - static void selection_changed_callback (NautilusIconContainer *container, FMIconView *icon_view) @@ -2568,7 +2573,9 @@ icon_view_scroll_to_file (NautilusView *view, icon_view = FM_ICON_VIEW (view); if (uri != NULL) { - file = nautilus_file_get (uri); + /* Only if existing, since we don't want to add the file to + the directory if it has been removed since then */ + file = nautilus_file_get_existing (uri); if (file != NULL) { nautilus_icon_container_scroll_to_icon (get_icon_container (icon_view), NAUTILUS_ICON_CONTAINER_ICON_DATA (file)); @@ -2613,7 +2620,7 @@ fm_icon_view_class_init (FMIconViewClass *klass) fm_directory_view_class->reveal_selection = fm_icon_view_reveal_selection; fm_directory_view_class->select_all = fm_icon_view_select_all; fm_directory_view_class->set_selection = fm_icon_view_set_selection; - fm_directory_view_class->sort_files = fm_icon_view_sort_files; + fm_directory_view_class->compare_files = compare_files; fm_directory_view_class->zoom_to_level = fm_icon_view_zoom_to_level; fm_directory_view_class->get_zoom_level = fm_icon_view_get_zoom_level; fm_directory_view_class->click_policy_changed = fm_icon_view_click_policy_changed; @@ -2731,9 +2738,15 @@ fm_icon_view_supports_uri (const char *uri, if (file_type == GNOME_VFS_FILE_TYPE_DIRECTORY) { return TRUE; } + if (strcmp (mime_type, NAUTILUS_SAVED_SEARCH_MIMETYPE) == 0){ + return TRUE; + } if (g_str_has_prefix (uri, "trash:")) { return TRUE; } + if (g_str_has_prefix (uri, EEL_SEARCH_URI)) { + return TRUE; + } return FALSE; } diff --git a/src/file-manager/fm-list-model.c b/src/file-manager/fm-list-model.c index 06d162af0..7b6e2f25d 100644 --- a/src/file-manager/fm-list-model.c +++ b/src/file-manager/fm-list-model.c @@ -52,15 +52,12 @@ static int fm_list_model_file_entry_compare_func (gconstpointer a, gconstpointer b, gpointer user_data); -static int fm_list_model_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data); - static GObjectClass *parent_class; struct FMListModelDetails { GSequence *files; - GHashTable *reverse_map; /* map from files to GSequencePtr's */ + GHashTable *directory_reverse_map; /* map from directory to GSequencePtr's */ + GHashTable *top_reverse_map; /* map from files in top dir to GSequencePtr's */ int stamp; @@ -86,6 +83,7 @@ typedef struct FileEntry FileEntry; struct FileEntry { NautilusFile *file; + GHashTable *reverse_map; /* map from files to GSequencePtr's */ NautilusDirectory *subdirectory; FileEntry *parent; GSequence *files; @@ -105,6 +103,10 @@ static void file_entry_free (FileEntry *file_entry) { nautilus_file_unref (file_entry->file); + if (file_entry->reverse_map) { + g_hash_table_destroy (file_entry->reverse_map); + file_entry->reverse_map = NULL; + } if (file_entry->subdirectory != NULL) { nautilus_directory_unref (file_entry->subdirectory); } @@ -160,6 +162,16 @@ fm_list_model_get_column_type (GtkTreeModel *tree_model, int index) } } +static void +fm_list_model_ptr_to_iter (FMListModel *model, GSequencePtr ptr, GtkTreeIter *iter) +{ + g_assert (!g_sequence_ptr_is_end (ptr)); + if (iter != NULL) { + iter->stamp = model->details->stamp; + iter->user_data = ptr; + } +} + static gboolean fm_list_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath *path) { @@ -185,10 +197,7 @@ fm_list_model_get_iter (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreePath files = file_entry->files; } - iter->stamp = model->details->stamp; - iter->user_data = ptr; - - g_assert (!g_sequence_ptr_is_end (iter->user_data)); + fm_list_model_ptr_to_iter (model, ptr, iter); return TRUE; } @@ -494,24 +503,118 @@ fm_list_model_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeI return TRUE; } -gboolean -fm_list_model_get_tree_iter_from_file (FMListModel *model, NautilusFile *file, GtkTreeIter *iter) +static GSequencePtr +lookup_file (FMListModel *model, NautilusFile *file, + NautilusDirectory *directory) +{ + FileEntry *file_entry; + GSequencePtr ptr, parent_ptr; + + parent_ptr = NULL; + if (directory) { + parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map, + directory); + } + + if (parent_ptr) { + file_entry = g_sequence_ptr_get_data (parent_ptr); + ptr = g_hash_table_lookup (file_entry->reverse_map, file); + } else { + ptr = g_hash_table_lookup (model->details->top_reverse_map, file); + } + + if (ptr) { + g_assert (((FileEntry *)g_sequence_ptr_get_data (ptr))->file == file); + } + + return ptr; +} + + +struct GetIters { + FMListModel *model; + NautilusFile *file; + GList *iters; +}; + +static void +dir_to_iters (struct GetIters *data, + GHashTable *reverse_map) { GSequencePtr ptr; + + ptr = g_hash_table_lookup (reverse_map, data->file); + if (ptr) { + GtkTreeIter *iter; + iter = g_new0 (GtkTreeIter, 1); + fm_list_model_ptr_to_iter (data->model, ptr, iter); + data->iters = g_list_prepend (data->iters, iter); + } +} + +static void +file_to_iter_cb (gpointer key, + gpointer value, + gpointer user_data) +{ + struct GetIters *data; + FileEntry *dir_file_entry; + + data = user_data; + dir_file_entry = g_sequence_ptr_get_data ((GSequencePtr)value); + dir_to_iters (data, dir_file_entry->reverse_map); +} + +GList * +fm_list_model_get_all_iters_for_file (FMListModel *model, NautilusFile *file) +{ + struct GetIters data; + + data.file = file; + data.model = model; + data.iters = NULL; + + dir_to_iters (&data, model->details->top_reverse_map); + g_hash_table_foreach (model->details->directory_reverse_map, + file_to_iter_cb, &data); + + return g_list_reverse (data.iters); +} + +gboolean +fm_list_model_get_first_iter_for_file (FMListModel *model, + NautilusFile *file, + GtkTreeIter *iter) +{ + GList *list; + gboolean res; + + res = FALSE; + + list = fm_list_model_get_all_iters_for_file (model, file); + if (list != NULL) { + res = TRUE; + *iter = *(GtkTreeIter *)list->data; + } + eel_g_list_free_deep (list); + + return res; +} + - ptr = g_hash_table_lookup (model->details->reverse_map, file); +gboolean +fm_list_model_get_tree_iter_from_file (FMListModel *model, NautilusFile *file, + NautilusDirectory *directory, + GtkTreeIter *iter) +{ + GSequencePtr ptr; + ptr = lookup_file (model, file, directory); if (!ptr) { return FALSE; } - g_assert (!g_sequence_ptr_is_end (ptr)); - g_assert (((FileEntry *)g_sequence_ptr_get_data (ptr))->file == file); - - if (iter != NULL) { - iter->stamp = model->details->stamp; - iter->user_data = ptr; - } + fm_list_model_ptr_to_iter (model, ptr, iter); return TRUE; } @@ -545,21 +648,13 @@ fm_list_model_file_entry_compare_func (gconstpointer a, return result; } -static int -fm_list_model_compare_func (gconstpointer a, - gconstpointer b, - gpointer user_data) +int +fm_list_model_compare_func (FMListModel *model, + NautilusFile *file1, + NautilusFile *file2) { - NautilusFile *file1; - NautilusFile *file2; - FMListModel *model; int result; - model = (FMListModel *)user_data; - - file1 = (NautilusFile *)a; - file2 = (NautilusFile *)b; - result = nautilus_file_compare_for_sort_by_attribute (file1, file2, model->details->sort_attribute, model->details->sort_directories_first, @@ -641,12 +736,6 @@ fm_list_model_sort (FMListModel *model) gtk_tree_path_free (path); } -void -fm_list_model_sort_files (FMListModel *model, GList **files) -{ - *files = g_list_sort_with_data (*files, fm_list_model_compare_func, model); -} - static gboolean fm_list_model_get_sort_column_id (GtkTreeSortable *sortable, gint *sort_column_id, @@ -716,7 +805,6 @@ each_path_get_data_binder (NautilusDragEachSelectedItemDataGet data_get, char *uri; GdkRectangle cell_area; GtkTreeViewColumn *column; - GtkTreeIter iter; info = context; @@ -728,31 +816,25 @@ each_path_get_data_binder (NautilusDragEachSelectedItemDataGet data_get, row = l->data; path = gtk_tree_row_reference_get_path (row); - - if (gtk_tree_model_get_iter (GTK_TREE_MODEL (info->model), - &iter, path)) { - gtk_tree_model_get (GTK_TREE_MODEL (info->model), - &iter, - FM_LIST_MODEL_FILE_COLUMN, &file, - -1); - - if (file) { - gtk_tree_view_get_cell_area - (info->model->details->drag_view, - path, - column, - &cell_area); + file = fm_list_model_file_for_path (info->model, path); + if (file) { + gtk_tree_view_get_cell_area + (info->model->details->drag_view, + path, + column, + &cell_area); - uri = nautilus_file_get_uri (file); + uri = nautilus_file_get_uri (file); - (*data_get) (uri, - 0, - cell_area.y - info->model->details->drag_begin_y, - cell_area.width, cell_area.height, - data); + (*data_get) (uri, + 0, + cell_area.y - info->model->details->drag_begin_y, + cell_area.width, cell_area.height, + data); - g_free (uri); - } + g_free (uri); + + nautilus_file_unref (file); } gtk_tree_path_free (path); @@ -822,87 +904,65 @@ add_dummy_row (FMListModel *model, FileEntry *parent_entry) } gboolean -fm_list_model_add_file (FMListModel *model, NautilusFile *file) +fm_list_model_add_file (FMListModel *model, NautilusFile *file, + NautilusDirectory *directory) { GtkTreeIter iter; GtkTreePath *path; FileEntry *file_entry; - NautilusFile *parent_file; GSequencePtr ptr, parent_ptr; GSequence *files; gboolean replace_dummy; + GHashTable *parent_hash; - /* We may only add each file once, in one dir. */ - ptr = g_hash_table_lookup (model->details->reverse_map, file); - if (ptr != NULL) { - file_entry = g_sequence_ptr_get_data (ptr); - - parent_file = nautilus_file_get_parent (file); - if (parent_file == NULL) { - return FALSE; - } - parent_ptr = g_hash_table_lookup (model->details->reverse_map, - parent_file); - nautilus_file_unref (parent_file); - if (/* toplevel parent */ - (file_entry->parent == NULL && parent_ptr == NULL) || - /* same in-tree parent */ - (file_entry->parent != NULL && parent_ptr != NULL && - file_entry->parent->ptr == parent_ptr) ) { - /* Don't add twice in same place */ - return FALSE; - } - - /* file has a new parent, due to a move - * this happens because a move results in a - * CHANGE event and an ADD event, and - * if the target directory of the move is - * added to the FMDirectoryView - * then we won't remove the changed file. - */ - fm_list_model_remove_file (model, file); - - /* Now add it back again in the new place. */ + parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map, + directory); + if (parent_ptr) { + file_entry = g_sequence_ptr_get_data (parent_ptr); + ptr = g_hash_table_lookup (file_entry->reverse_map, file); + } else { + file_entry = NULL; + ptr = g_hash_table_lookup (model->details->top_reverse_map, file); } - nautilus_file_ref (file); + if (ptr != NULL) { + g_warning ("file already in tree (parent_ptr: %p)!!!\n", parent_ptr); + return FALSE; + } file_entry = g_new0 (FileEntry, 1); - file_entry->file = file; + file_entry->file = nautilus_file_ref (file); file_entry->parent = NULL; file_entry->subdirectory = NULL; file_entry->files = NULL; files = model->details->files; + parent_hash = model->details->top_reverse_map; replace_dummy = FALSE; - parent_file = nautilus_file_get_parent (file); - if (parent_file != NULL) { - parent_ptr = g_hash_table_lookup (model->details->reverse_map, - parent_file); - nautilus_file_unref (parent_file); - if (parent_ptr != NULL) { - file_entry->parent = g_sequence_ptr_get_data (parent_ptr); - files = file_entry->parent->files; - if (g_sequence_get_length (files) == 1) { - GSequencePtr dummy_ptr = g_sequence_get_ptr_at_pos (files, 0); - FileEntry *dummy_entry = g_sequence_ptr_get_data (dummy_ptr); - if (dummy_entry->file == NULL) { - /* replace the dummy loading entry */ - model->details->stamp++; - g_sequence_remove (dummy_ptr); - - replace_dummy = TRUE; - } + if (parent_ptr != NULL) { + file_entry->parent = g_sequence_ptr_get_data (parent_ptr); + parent_hash = file_entry->parent->reverse_map; + files = file_entry->parent->files; + if (g_sequence_get_length (files) == 1) { + GSequencePtr dummy_ptr = g_sequence_get_ptr_at_pos (files, 0); + FileEntry *dummy_entry = g_sequence_ptr_get_data (dummy_ptr); + if (dummy_entry->file == NULL) { + /* replace the dummy loading entry */ + model->details->stamp++; + g_sequence_remove (dummy_ptr); + + replace_dummy = TRUE; } } } + file_entry->ptr = g_sequence_insert_sorted (files, file_entry, fm_list_model_file_entry_compare_func, model); - g_hash_table_insert (model->details->reverse_map, file, file_entry->ptr); + g_hash_table_insert (parent_hash, file, file_entry->ptr); iter.stamp = model->details->stamp; iter.user_data = file_entry->ptr; @@ -928,23 +988,21 @@ fm_list_model_add_file (FMListModel *model, NautilusFile *file) } void -fm_list_model_file_changed (FMListModel *model, NautilusFile *file) +fm_list_model_file_changed (FMListModel *model, NautilusFile *file, + NautilusDirectory *directory) { GtkTreeIter iter; GtkTreePath *path; GSequencePtr ptr; - ptr = g_hash_table_lookup (model->details->reverse_map, file); + ptr = lookup_file (model, file, directory); if (!ptr) { return; } g_sequence_ptr_sort_changed (ptr, fm_list_model_file_entry_compare_func, model); - if (!fm_list_model_get_tree_iter_from_file (model, file, &iter)) { - return; - } - + fm_list_model_ptr_to_iter (model, ptr, &iter); 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); @@ -979,7 +1037,8 @@ fm_list_model_remove (FMListModel *model, GtkTreeIter *iter) child_file_entry = g_sequence_ptr_get_data (child_ptr); if (child_file_entry->file != NULL) { fm_list_model_remove_file (model, - child_file_entry->file); + child_file_entry->file, + file_entry->subdirectory); } else { path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); gtk_tree_path_append_index (path, 0); @@ -995,7 +1054,11 @@ fm_list_model_remove (FMListModel *model, GtkTreeIter *iter) } - g_hash_table_remove (model->details->reverse_map, file_entry->file); + if (file_entry->parent != NULL) { + g_hash_table_remove (file_entry->parent->reverse_map, file_entry->file); + } else { + g_hash_table_remove (model->details->top_reverse_map, file_entry->file); + } parent_file_entry = file_entry->parent; if (parent_file_entry && g_sequence_get_length (parent_file_entry->files) == 1 && @@ -1011,6 +1074,8 @@ fm_list_model_remove (FMListModel *model, GtkTreeIter *iter) g_signal_emit (model, list_model_signals[SUBDIRECTORY_UNLOADED], 0, file_entry->subdirectory); + g_hash_table_remove (model->details->directory_reverse_map, + file_entry->subdirectory); } path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); @@ -1032,11 +1097,12 @@ fm_list_model_remove (FMListModel *model, GtkTreeIter *iter) } void -fm_list_model_remove_file (FMListModel *model, NautilusFile *file) +fm_list_model_remove_file (FMListModel *model, NautilusFile *file, + NautilusDirectory *directory) { GtkTreeIter iter; - if (fm_list_model_get_tree_iter_from_file (model, file, &iter)) { + if (fm_list_model_get_tree_iter_from_file (model, file, directory, &iter)) { fm_list_model_remove (model, &iter); } } @@ -1090,6 +1156,7 @@ fm_list_model_load_subdirectory (FMListModel *model, GtkTreePath *path, Nautilus { GtkTreeIter iter; FileEntry *file_entry; + NautilusDirectory *subdirectory; if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) { return FALSE; @@ -1101,9 +1168,23 @@ fm_list_model_load_subdirectory (FMListModel *model, GtkTreePath *path, Nautilus return FALSE; } - file_entry->subdirectory = nautilus_directory_get_for_file (file_entry->file); - nautilus_directory_ref (file_entry->subdirectory); - *directory = file_entry->subdirectory; + subdirectory = nautilus_directory_get_for_file (file_entry->file); + + if (g_hash_table_lookup (model->details->directory_reverse_map, + subdirectory) != NULL) { + nautilus_directory_unref (subdirectory); + g_warning ("Already in directory_reverse_map, failing\n"); + return FALSE; + } + + file_entry->subdirectory = subdirectory, + g_hash_table_insert (model->details->directory_reverse_map, + subdirectory, file_entry->ptr); + file_entry->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal); + + /* Return a ref too */ + nautilus_directory_ref (subdirectory); + *directory = subdirectory; return TRUE; } @@ -1114,7 +1195,8 @@ fm_list_model_unload_subdirectory (FMListModel *model, GtkTreeIter *iter) { GSequencePtr child_ptr; FileEntry *file_entry, *child_file_entry; - + GtkTreeIter child_iter; + file_entry = g_sequence_ptr_get_data (iter->user_data); if (file_entry->file == NULL || file_entry->subdirectory == NULL) { @@ -1131,8 +1213,8 @@ fm_list_model_unload_subdirectory (FMListModel *model, GtkTreeIter *iter) /* Don't delete the dummy node */ break; } else { - fm_list_model_remove_file (model, - child_file_entry->file); + fm_list_model_ptr_to_iter (model, child_ptr, &child_iter); + fm_list_model_remove (model, &child_iter); } } @@ -1142,8 +1224,14 @@ fm_list_model_unload_subdirectory (FMListModel *model, GtkTreeIter *iter) file_entry->subdirectory); /* actually unload */ + g_hash_table_remove (model->details->directory_reverse_map, + file_entry->subdirectory); nautilus_directory_unref (file_entry->subdirectory); file_entry->subdirectory = NULL; + + g_assert (g_hash_table_size (file_entry->reverse_map) == 0); + g_hash_table_destroy (file_entry->reverse_map); + file_entry->reverse_map = NULL; } @@ -1386,9 +1474,13 @@ fm_list_model_dispose (GObject *object) model->details->files = NULL; } - if (model->details->reverse_map) { - g_hash_table_destroy (model->details->reverse_map); - model->details->reverse_map = NULL; + if (model->details->top_reverse_map) { + g_hash_table_destroy (model->details->top_reverse_map); + model->details->top_reverse_map = NULL; + } + if (model->details->directory_reverse_map) { + g_hash_table_destroy (model->details->directory_reverse_map); + model->details->directory_reverse_map = NULL; } EEL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); @@ -1412,7 +1504,8 @@ fm_list_model_init (FMListModel *model) { model->details = g_new0 (FMListModelDetails, 1); model->details->files = g_sequence_new ((GDestroyNotify)file_entry_free); - model->details->reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal); + model->details->top_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal); + model->details->directory_reverse_map = g_hash_table_new (g_direct_hash, g_direct_equal); model->details->stamp = g_random_int (); model->details->sort_attribute = NULL; model->details->columns = g_ptr_array_new (); @@ -1526,7 +1619,7 @@ fm_list_model_get_type (void) struct ChangeDummyData { FMListModel *model; - NautilusFile *file; + NautilusDirectory *directory; }; static gboolean @@ -1545,8 +1638,8 @@ change_dummy_row_callback (gpointer callback_data) if (data->model != NULL) { model = data->model; - parent_ptr = g_hash_table_lookup (model->details->reverse_map, - data->file); + parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map, + data->directory); file_entry = g_sequence_ptr_get_data (parent_ptr); file_entry->loaded = 1; @@ -1569,7 +1662,7 @@ change_dummy_row_callback (gpointer callback_data) } eel_remove_weak_pointer (&data->model); - nautilus_file_unref (data->file); + nautilus_directory_unref (data->directory); g_free (data); return FALSE; @@ -1578,15 +1671,12 @@ change_dummy_row_callback (gpointer callback_data) void fm_list_model_subdirectory_done_loading (FMListModel *model, NautilusDirectory *directory) { - NautilusFile *parent_file; GSequencePtr parent_ptr; struct ChangeDummyData *data; - parent_file = nautilus_directory_get_corresponding_file (directory); - parent_ptr = g_hash_table_lookup (model->details->reverse_map, - parent_file); + parent_ptr = g_hash_table_lookup (model->details->directory_reverse_map, + directory); if (parent_ptr == NULL) { - nautilus_file_unref (parent_file); return; } @@ -1596,10 +1686,9 @@ fm_list_model_subdirectory_done_loading (FMListModel *model, NautilusDirectory * */ data = g_new (struct ChangeDummyData, 1); data->model = model; - data->file = nautilus_file_ref (parent_file); + data->directory = directory; + nautilus_directory_ref (directory); eel_add_weak_pointer (&data->model); g_timeout_add (LOADING_TO_EMPTY_DELAY, change_dummy_row_callback, data); - - nautilus_file_unref (parent_file); } diff --git a/src/file-manager/fm-list-model.h b/src/file-manager/fm-list-model.h index 6dcb9becf..809f24a8a 100644 --- a/src/file-manager/fm-list-model.h +++ b/src/file-manager/fm-list-model.h @@ -76,16 +76,25 @@ typedef struct { GType fm_list_model_get_type (void); gboolean fm_list_model_add_file (FMListModel *model, - NautilusFile *file); + NautilusFile *file, + NautilusDirectory *directory); void fm_list_model_file_changed (FMListModel *model, - NautilusFile *file); + NautilusFile *file, + NautilusDirectory *directory); 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); + NautilusFile *file, + NautilusDirectory *directory); void fm_list_model_clear (FMListModel *model); gboolean fm_list_model_get_tree_iter_from_file (FMListModel *model, NautilusFile *file, + NautilusDirectory *directory, + GtkTreeIter *iter); +GList * fm_list_model_get_all_iters_for_file (FMListModel *model, + NautilusFile *file); +gboolean fm_list_model_get_first_iter_for_file (FMListModel *model, + NautilusFile *file, GtkTreeIter *iter); void fm_list_model_set_should_sort_directories_first (FMListModel *model, gboolean sort_directories_first); @@ -95,7 +104,7 @@ int fm_list_model_get_sort_column_id_from_attribute (FMListModel *model, const char *attribute); char *fm_list_model_get_attribute_from_sort_column_id (FMListModel *model, int sort_column_id); -void fm_list_model_sort_files (FMListModel *model, +void fm_list_model_sort_files (FMListModel *model, GList **files); NautilusZoomLevel fm_list_model_get_zoom_level_from_column_id (int column); @@ -114,6 +123,9 @@ void fm_list_model_set_drag_view (FMListModel *model, void fm_list_model_get_drag_types (const GtkTargetEntry **entries, int *num_entries); +int fm_list_model_compare_func (FMListModel *model, + NautilusFile *file1, + NautilusFile *file2); int fm_list_model_add_column (FMListModel *model, @@ -122,6 +134,6 @@ int fm_list_model_get_column_number (FMListModel *model, const char *column_name); void fm_list_model_subdirectory_done_loading (FMListModel *model, - NautilusDirectory *directory); + NautilusDirectory *directory); #endif /* FM_LIST_MODEL_H */ diff --git a/src/file-manager/fm-list-view.c b/src/file-manager/fm-list-view.c index de1244570..badff87e7 100644 --- a/src/file-manager/fm-list-view.c +++ b/src/file-manager/fm-list-view.c @@ -767,6 +767,7 @@ row_expanded_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *pa struct UnloadDelayData { NautilusFile *file; + NautilusDirectory *directory; FMListView *view; }; @@ -782,6 +783,7 @@ unload_file_timeout (gpointer data) model = unload_data->view->details->model; if (fm_list_model_get_tree_iter_from_file (model, unload_data->file, + unload_data->directory, &iter)) { path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); if (!gtk_tree_view_row_expanded (unload_data->view->details->tree_view, @@ -794,8 +796,10 @@ unload_file_timeout (gpointer data) eel_remove_weak_pointer (&unload_data->view); - - g_object_unref (unload_data->file); + if (unload_data->directory) { + nautilus_directory_unref (unload_data->directory); + } + nautilus_file_unref (unload_data->file); g_free (unload_data); return FALSE; } @@ -805,18 +809,29 @@ row_collapsed_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *p { FMListView *view; NautilusFile *file; + NautilusDirectory *directory; + GtkTreeIter parent; struct UnloadDelayData *unload_data; + GtkTreeModel *model; view = FM_LIST_VIEW (callback_data); + model = GTK_TREE_MODEL (view->details->model); - gtk_tree_model_get (GTK_TREE_MODEL (view->details->model), - iter, + gtk_tree_model_get (model, iter, FM_LIST_MODEL_FILE_COLUMN, &file, -1); + + directory = NULL; + if (gtk_tree_model_iter_parent (model, &parent, iter)) { + gtk_tree_model_get (model, &parent, + FM_LIST_MODEL_SUBDIRECTORY_COLUMN, &directory, + -1); + } unload_data = g_new (struct UnloadDelayData, 1); unload_data->view = view; unload_data->file = file; + unload_data->directory = directory; eel_add_weak_pointer (&unload_data->view); @@ -994,23 +1009,10 @@ get_file_for_path_callback (NautilusTreeViewDragDest *dest, gpointer user_data) { FMListView *view; - GtkTreeIter iter; - NautilusFile *file; view = FM_LIST_VIEW (user_data); - file = NULL; - - if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model), - &iter, path)) { - gtk_tree_model_get (GTK_TREE_MODEL (view->details->model), - &iter, - FM_LIST_MODEL_FILE_COLUMN, - &file, - -1); - } - - return file; + return fm_list_model_file_for_path (view->details->model, path); } /* Handles an URL received from Mozilla */ @@ -1332,12 +1334,12 @@ create_and_set_up_tree_view (FMListView *view) } static void -fm_list_view_add_file (FMDirectoryView *view, NautilusFile *file) +fm_list_view_add_file (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { FMListModel *model; - + model = FM_LIST_VIEW (view)->details->model; - fm_list_model_add_file (model, file); + fm_list_model_add_file (model, file, directory); } static GList * @@ -1508,9 +1510,9 @@ fm_list_view_clear (FMDirectoryView *view) } static void -fm_list_view_file_changed (FMDirectoryView *view, NautilusFile *file) +fm_list_view_file_changed (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { - fm_list_model_file_changed (FM_LIST_VIEW (view)->details->model, file); + fm_list_model_file_changed (FM_LIST_VIEW (view)->details->model, file, directory); } static GtkWidget * @@ -1532,7 +1534,6 @@ fm_list_view_get_selection_foreach_func (GtkTreeModel *model, GtkTreePath *path, -1); if (file != NULL) { - nautilus_file_ref (file); (* list) = g_list_prepend ((* list), file); } } @@ -1631,7 +1632,7 @@ fm_list_view_end_file_changes (FMDirectoryView *view) } static void -fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file) +fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory) { GtkTreePath *path; GtkTreePath *file_path; @@ -1641,13 +1642,13 @@ fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file) FMListView *list_view; GtkTreeModel* tree_model; GtkTreeSelection *selection; - + 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)) { + if (fm_list_model_get_tree_iter_from_file (list_view->details->model, file, directory, &iter)) { selection = gtk_tree_view_get_selection (list_view->details->tree_view); file_path = gtk_tree_model_get_path (tree_model, &iter); @@ -1672,7 +1673,7 @@ fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file) gtk_tree_path_free (file_path); - fm_list_model_remove_file (list_view->details->model, file); + fm_list_model_remove_file (list_view->details->model, file, directory); if (gtk_tree_row_reference_valid (row_reference)) { if (list_view->details->new_selection_path) { @@ -1681,7 +1682,7 @@ fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file) list_view->details->new_selection_path = gtk_tree_row_reference_get_path (row_reference); } - if(row_reference) { + if (row_reference) { gtk_tree_row_reference_free (row_reference); } } @@ -1695,7 +1696,7 @@ fm_list_view_set_selection (FMDirectoryView *view, GList *selection) FMListView *list_view; GtkTreeSelection *tree_selection; GList *node; - GtkTreeIter iter; + GList *iters, *l; NautilusFile *file; list_view = FM_LIST_VIEW (view); @@ -1706,9 +1707,13 @@ fm_list_view_set_selection (FMDirectoryView *view, GList *selection) gtk_tree_selection_unselect_all (tree_selection); for (node = selection; node != NULL; node = node->next) { file = node->data; - if (fm_list_model_get_tree_iter_from_file (list_view->details->model, file, &iter)) { - gtk_tree_selection_select_iter (tree_selection, &iter); + iters = fm_list_model_get_all_iters_for_file (list_view->details->model, file); + + for (l = iters; l != NULL; l = l->next) { + gtk_tree_selection_select_iter (tree_selection, + (GtkTreeIter *)l->data); } + eel_g_list_free_deep (iters); } g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view); @@ -1739,15 +1744,13 @@ fm_list_view_reveal_selection (FMDirectoryView *view) list_view = FM_LIST_VIEW (view); file = selection->data; - if (!fm_list_model_get_tree_iter_from_file (list_view->details->model, file, &iter)) { - return; - } - path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter); + if (fm_list_model_get_first_iter_for_file (list_view->details->model, file, &iter)) { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter); - gtk_tree_view_scroll_to_cell (list_view->details->tree_view, path, NULL, FALSE, 0.0, 0.0); - - gtk_tree_path_free (path); - path = NULL; + gtk_tree_view_scroll_to_cell (list_view->details->tree_view, path, NULL, FALSE, 0.0, 0.0); + + gtk_tree_path_free (path); + } } nautilus_file_list_free (selection); @@ -2146,7 +2149,7 @@ fm_list_view_start_renaming_file (FMDirectoryView *view, return; } - if (!fm_list_model_get_tree_iter_from_file (list_view->details->model, file, &iter)) { + if (!fm_list_model_get_first_iter_for_file (list_view->details->model, file, &iter)) { return; } @@ -2295,14 +2298,13 @@ fm_list_view_sort_directories_first_changed (FMDirectoryView *view) fm_directory_view_should_sort_directories_first (view)); } -static void -fm_list_view_sort_files (FMDirectoryView *view, GList **files) +static int +fm_list_view_compare_files (FMDirectoryView *view, NautilusFile *file1, NautilusFile *file2) { FMListView *list_view; list_view = FM_LIST_VIEW (view); - - fm_list_model_sort_files (list_view->details->model, files); + return fm_list_model_compare_func (list_view->details->model, file1, file2); } static gboolean @@ -2426,7 +2428,7 @@ fm_list_view_scroll_to_file (FMListView *view, GtkTreePath *path; GtkTreeIter iter; - if (!fm_list_model_get_tree_iter_from_file (view->details->model, file, &iter)) { + if (!fm_list_model_get_first_iter_for_file (view->details->model, file, &iter)) { return; } @@ -2446,9 +2448,13 @@ list_view_scroll_to_file (NautilusView *view, NautilusFile *file; if (uri != NULL) { - file = nautilus_file_get (uri); - fm_list_view_scroll_to_file (FM_LIST_VIEW (view), file); - nautilus_file_unref (file); + /* Only if existing, since we don't want to add the file to + the directory if it has been removed since then */ + file = nautilus_file_get_existing (uri); + if (file != NULL) { + fm_list_view_scroll_to_file (FM_LIST_VIEW (view), file); + nautilus_file_unref (file); + } } } @@ -2484,7 +2490,7 @@ fm_list_view_class_init (FMListViewClass *class) fm_directory_view_class->reveal_selection = fm_list_view_reveal_selection; fm_directory_view_class->select_all = fm_list_view_select_all; fm_directory_view_class->set_selection = fm_list_view_set_selection; - fm_directory_view_class->sort_files = fm_list_view_sort_files; + fm_directory_view_class->compare_files = fm_list_view_compare_files; fm_directory_view_class->sort_directories_first_changed = fm_list_view_sort_directories_first_changed; fm_directory_view_class->start_renaming_file = fm_list_view_start_renaming_file; fm_directory_view_class->get_zoom_level = fm_list_view_get_zoom_level; @@ -2582,9 +2588,15 @@ fm_list_view_supports_uri (const char *uri, if (file_type == GNOME_VFS_FILE_TYPE_DIRECTORY) { return TRUE; } + if (strcmp (mime_type, NAUTILUS_SAVED_SEARCH_MIMETYPE) == 0){ + return TRUE; + } if (g_str_has_prefix (uri, "trash:")) { return TRUE; } + if (g_str_has_prefix (uri, EEL_SEARCH_URI)) { + return TRUE; + } return FALSE; } diff --git a/src/file-manager/fm-properties-window.c b/src/file-manager/fm-properties-window.c index be93d0bf1..c53fe6f86 100644 --- a/src/file-manager/fm-properties-window.c +++ b/src/file-manager/fm-properties-window.c @@ -2908,9 +2908,11 @@ create_permissions_page (FMPropertiesWindow *window) file_list = window->details->original_files; - window->details->initial_permissions = get_initial_permissions (window->details->target_files); + window->details->initial_permissions = NULL; if (all_can_get_permissions (file_list)) { + window->details->initial_permissions = get_initial_permissions (window->details->target_files); + if (!all_can_set_permissions (file_list)) { add_prompt_and_separator ( GTK_VBOX (vbox), @@ -3273,6 +3275,11 @@ create_open_with_page (FMPropertiesWindow *window) char *mime_type; uri = nautilus_file_get_uri (get_target_file (window)); + + if (uri == NULL) { + return; + } + mime_type = nautilus_file_get_mime_type (get_target_file (window)); vbox = eel_mime_application_chooser_new (uri, mime_type); diff --git a/src/file-manager/nautilus-directory-view-ui.xml b/src/file-manager/nautilus-directory-view-ui.xml index fcbe5cda3..3a1cdbdda 100644 --- a/src/file-manager/nautilus-directory-view-ui.xml +++ b/src/file-manager/nautilus-directory-view-ui.xml @@ -39,6 +39,8 @@ </placeholder> <placeholder name="Global File Items Placeholder"> <menuitem name="Empty Trash" action="Empty Trash"/> + <menuitem name="Save Search" action="Save Search"/> + <menuitem name="Save Search As" action="Save Search As"/> </placeholder> </menu> <menu action="Edit"> diff --git a/src/nautilus-actions.h b/src/nautilus-actions.h index 77c92f265..e7296262e 100644 --- a/src/nautilus-actions.h +++ b/src/nautilus-actions.h @@ -42,5 +42,6 @@ #define NAUTILUS_ACTION_ZOOM_OUT "Zoom Out" #define NAUTILUS_ACTION_ZOOM_NORMAL "Zoom Normal" #define NAUTILUS_ACTION_CLOSE "Close" +#define NAUTILUS_ACTION_SEARCH "Search" #endif /* NAUTILUS_ACTIONS_H */ diff --git a/src/nautilus-history-sidebar.c b/src/nautilus-history-sidebar.c index 3a23df8c0..7b34fe4f5 100644 --- a/src/nautilus-history-sidebar.c +++ b/src/nautilus-history-sidebar.c @@ -110,6 +110,7 @@ update_history (NautilusHistorySidebar *sidebar) HISTORY_SIDEBAR_COLUMN_NAME, name, HISTORY_SIDEBAR_COLUMN_BOOKMARK, bookmark, -1); + g_object_unref (bookmark); if (pixbuf != NULL) { g_object_unref (pixbuf); @@ -219,6 +220,7 @@ nautilus_history_sidebar_init (NautilusHistorySidebar *sidebar) NAUTILUS_TYPE_BOOKMARK); gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store)); + g_object_unref (store); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar), GTK_POLICY_AUTOMATIC, diff --git a/src/nautilus-location-bar.c b/src/nautilus-location-bar.c index 1887ad474..b400c2fff 100644 --- a/src/nautilus-location-bar.c +++ b/src/nautilus-location-bar.c @@ -497,12 +497,17 @@ nautilus_location_bar_set_location (NautilusNavigationBar *navigation_bar, /* Note: This is called in reaction to external changes, and * thus should not emit the LOCATION_CHANGED signal. */ - - formatted_location = eel_format_uri_for_display (location); - nautilus_entry_set_text (NAUTILUS_ENTRY (bar->details->entry), - formatted_location); - set_position_and_selection_to_end (GTK_EDITABLE (bar->details->entry)); - g_free (formatted_location); + + if (eel_uri_is_search (location)) { + nautilus_location_entry_set_special_text (NAUTILUS_LOCATION_ENTRY (bar->details->entry), + ""); + } else { + formatted_location = eel_format_uri_for_display (location); + nautilus_entry_set_text (NAUTILUS_ENTRY (bar->details->entry), + formatted_location); + set_position_and_selection_to_end (GTK_EDITABLE (bar->details->entry)); + g_free (formatted_location); + } /* free up the cached file info from the previous location */ g_free (bar->details->current_directory); diff --git a/src/nautilus-location-entry.c b/src/nautilus-location-entry.c index f294124f3..6ede24c10 100644 --- a/src/nautilus-location-entry.c +++ b/src/nautilus-location-entry.c @@ -60,6 +60,10 @@ struct NautilusLocationEntryDetails { GList *file_info_list; guint idle_id; + + gboolean has_special_text; + gboolean setting_special_text; + gchar *special_text; }; static void nautilus_location_entry_class_init (NautilusLocationEntryClass *class); @@ -431,6 +435,7 @@ finalize (GObject *object) entry = NAUTILUS_LOCATION_ENTRY (object); + g_free (entry->details->special_text); g_free (entry->details); EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); @@ -461,11 +466,42 @@ destroy (GtkObject *object) } static void +nautilus_location_entry_text_changed (NautilusLocationEntry *entry, + GParamSpec *pspec) +{ + if (entry->details->setting_special_text) { + return; + } + + entry->details->has_special_text = FALSE; +} + +static gboolean +nautilus_location_entry_focus_in (GtkWidget *widget, + GdkEventFocus *event) +{ + NautilusLocationEntry *entry = NAUTILUS_LOCATION_ENTRY (widget); + + if (entry->details->has_special_text) { + entry->details->setting_special_text = TRUE; + gtk_entry_set_text (GTK_ENTRY (entry), ""); + entry->details->setting_special_text = FALSE; + } + + return EEL_CALL_PARENT_WITH_RETURN_VALUE (GTK_WIDGET_CLASS, focus_in_event, (widget, event)); +} + +static void nautilus_location_entry_class_init (NautilusLocationEntryClass *class) { + GtkWidgetClass *widget_class; GObjectClass *gobject_class; GtkObjectClass *object_class; + widget_class = GTK_WIDGET_CLASS (class); + + widget_class->focus_in_event = nautilus_location_entry_focus_in; + gobject_class = G_OBJECT_CLASS (class); gobject_class->finalize = finalize; @@ -483,6 +519,9 @@ nautilus_location_entry_init (NautilusLocationEntry *entry) g_signal_connect (entry, "event_after", G_CALLBACK (editable_event_after_callback), entry); + g_signal_connect (entry, "notify::text", + G_CALLBACK (nautilus_location_entry_text_changed), NULL); + } GtkWidget * @@ -494,3 +533,18 @@ nautilus_location_entry_new (void) return entry; } + +void +nautilus_location_entry_set_special_text (NautilusLocationEntry *entry, + const char *special_text) +{ + entry->details->has_special_text = TRUE; + + g_free (entry->details->special_text); + entry->details->special_text = g_strdup (special_text); + + entry->details->setting_special_text = TRUE; + gtk_entry_set_text (GTK_ENTRY (entry), special_text); + entry->details->setting_special_text = FALSE; +} + diff --git a/src/nautilus-location-entry.h b/src/nautilus-location-entry.h index 596f18a33..2869e112a 100644 --- a/src/nautilus-location-entry.h +++ b/src/nautilus-location-entry.h @@ -50,5 +50,7 @@ typedef struct { GType nautilus_location_entry_get_type (void); GtkWidget* nautilus_location_entry_new (void); +void nautilus_location_entry_set_special_text (NautilusLocationEntry *entry, + const char *special_text); #endif /* NAUTILUS_LOCATION_ENTRY_H */ diff --git a/src/nautilus-navigation-window-menus.c b/src/nautilus-navigation-window-menus.c index 6b9a1cfe2..63d14f9c4 100644 --- a/src/nautilus-navigation-window-menus.c +++ b/src/nautilus-navigation-window-menus.c @@ -60,6 +60,7 @@ #include <libnautilus-private/nautilus-ui-utilities.h> #include <libnautilus-private/nautilus-icon-factory.h> #include <libnautilus-private/nautilus-undo-manager.h> +#include <libnautilus-private/nautilus-search-engine.h> #define MENU_PATH_HISTORY_PLACEHOLDER "/MenuBar/Other Menus/Go/History Placeholder" @@ -410,6 +411,17 @@ action_go_to_location_callback (GtkAction *action, nautilus_window_prompt_for_location (window, NULL); } +static void +action_search_callback (GtkAction *action, + gpointer user_data) +{ + NautilusNavigationWindow *window; + + window = NAUTILUS_NAVIGATION_WINDOW (user_data); + + nautilus_navigation_window_show_search (window); +} + static const GtkActionEntry navigation_entries[] = { { "Go", NULL, N_("_Go") }, /* name, stock id, label */ { "Bookmarks", NULL, N_("_Bookmarks") }, /* name, stock id, label */ @@ -431,6 +443,10 @@ static const GtkActionEntry navigation_entries[] = { { "Edit Bookmarks", NULL, N_("_Edit Bookmarks"), /* name, stock id, label */ "<control>b", N_("Display a window that allows editing the bookmarks in this menu"), G_CALLBACK (action_edit_bookmarks_callback) }, + { "Search", "gtk-find", N_("_Search"), /* name, stock id, label */ + "<control>F", N_("Search for files"), + G_CALLBACK (action_search_callback) }, + }; static const GtkToggleActionEntry navigation_toggle_entries[] = { diff --git a/src/nautilus-navigation-window-ui.xml b/src/nautilus-navigation-window-ui.xml index b15da7441..1da8913bc 100644 --- a/src/nautilus-navigation-window-ui.xml +++ b/src/nautilus-navigation-window-ui.xml @@ -31,6 +31,7 @@ <menuitem name="Go to Trash" action="Go to Trash"/> <menuitem name="Go to Burn CD" action="Go to Burn CD"/> <menuitem name="Go to Location" action="Go to Location"/> + <menuitem name="Search" action="Search"/> <separator/> <menuitem name="Clear History" action="Clear History"/> <separator/> @@ -54,7 +55,8 @@ <separator/> <toolitem name="Home" action="Home"/> <toolitem name="Computer" action="Go to Computer"/> - + <separator/> + <toolitem name="Search" action="Search"/> <placeholder name="Extra Buttons Placeholder"> <placeholder name="Extension Actions"/> </placeholder> diff --git a/src/nautilus-navigation-window.c b/src/nautilus-navigation-window.c index b18886968..d5dc20881 100644 --- a/src/nautilus-navigation-window.c +++ b/src/nautilus-navigation-window.c @@ -38,6 +38,8 @@ #include "nautilus-signaller.h" #include "nautilus-location-bar.h" #include "nautilus-pathbar.h" +#include "nautilus-query-editor.h" +#include "nautilus-search-bar.h" #include "nautilus-window-manage-views.h" #include "nautilus-zoom-control.h" #include <eel/eel-accessibility.h> @@ -48,6 +50,7 @@ #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> @@ -81,6 +84,7 @@ #include <libnautilus-private/nautilus-undo.h> #include <libnautilus-private/nautilus-module.h> #include <libnautilus-private/nautilus-sidebar-provider.h> +#include <libnautilus-private/nautilus-search-directory.h> #include <math.h> #include <sys/time.h> @@ -99,6 +103,12 @@ #define MENU_PATH_BOOKMARKS_PLACEHOLDER "/MenuBar/Other Menus/Bookmarks/Bookmarks Placeholder" +typedef enum { + NAUTILUS_BAR_PATH, + NAUTILUS_BAR_NAVIGATION, + NAUTILUS_BAR_SEARCH +} NautilusBarMode; + enum { ARG_0, ARG_APP_ID, @@ -121,6 +131,14 @@ static void path_bar_location_changed_callback (GtkWidget * NautilusNavigationWindow *window); static void always_use_location_entry_changed (gpointer callback_data); +static void nautilus_navigation_window_set_bar_mode (NautilusNavigationWindow *window, + NautilusBarMode mode); +static void search_bar_activate_callback (NautilusSearchBar *bar, + NautilusWindow *window); +static void search_bar_cancel_callback (GtkWidget *widget, + NautilusNavigationWindow *window); + +static void nautilus_navigation_window_show_location_bar_temporarily (NautilusNavigationWindow *window); GNOME_CLASS_BOILERPLATE (NautilusNavigationWindow, nautilus_navigation_window, NautilusWindow, NAUTILUS_TYPE_WINDOW) @@ -133,7 +151,7 @@ nautilus_navigation_window_instance_init (NautilusNavigationWindow *window) GtkWidget *location_bar; GtkWidget *view_as_menu_vbox; GtkToolItem *item; - GtkWidget *hbox; + GtkWidget *hbox, *vbox, *eventbox, *extras_vbox; window->details = g_new0 (NautilusNavigationWindowDetails, 1); @@ -150,6 +168,25 @@ nautilus_navigation_window_instance_init (NautilusNavigationWindow *window) 0, 0); gtk_widget_show (window->details->content_paned); + vbox = gtk_vbox_new (FALSE, 0); + nautilus_horizontal_splitter_pack2 ( + NAUTILUS_HORIZONTAL_SPLITTER (window->details->content_paned), + vbox); + gtk_widget_show (vbox); + + eventbox = gtk_event_box_new (); + gtk_widget_set_name (eventbox, "nautilus-extra-view-widget"); + gtk_box_pack_start (GTK_BOX (vbox), eventbox, FALSE, FALSE, 0); + gtk_widget_show (eventbox); + + extras_vbox = gtk_vbox_new (FALSE, 0); + NAUTILUS_WINDOW (window)->details->extra_location_widgets = extras_vbox; + gtk_container_add (GTK_CONTAINER (eventbox), extras_vbox); + + window->details->content_box = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), window->details->content_box, TRUE, TRUE, 0); + gtk_widget_show (window->details->content_box); + nautilus_navigation_window_initialize_actions (window); nautilus_navigation_window_initialize_menus (window); @@ -207,6 +244,15 @@ nautilus_navigation_window_instance_init (NautilusNavigationWindow *window) window->navigation_bar, TRUE, TRUE, 0); + window->search_bar = nautilus_search_bar_new (); + g_signal_connect_object (window->search_bar, "activate", + G_CALLBACK (search_bar_activate_callback), window, 0); + g_signal_connect_object (window->search_bar, "cancel", + G_CALLBACK (search_bar_cancel_callback), window, 0); + gtk_box_pack_start (GTK_BOX (hbox), + window->search_bar, + TRUE, TRUE, 0); + /* Option menu for content view types; it's empty here, filled in when a uri is set. * Pack it into vbox so it doesn't grow vertically when location bar does. */ @@ -270,9 +316,9 @@ always_use_location_entry_changed (gpointer callback_data) window = NAUTILUS_NAVIGATION_WINDOW (callback_data); if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY)) { - nautilus_navigation_window_hide_path_bar (window); + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_NAVIGATION); } else { - nautilus_navigation_window_show_path_bar (window); + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_PATH); } } @@ -331,11 +377,23 @@ hide_temporary_bars (NautilusNavigationWindow *window) window->details->temporary_location_bar = FALSE; } if (window->details->temporary_navigation_bar) { - if (!eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY)) { - nautilus_navigation_window_show_path_bar (window); + if (NAUTILUS_WINDOW (window)->details->search_mode) { + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_SEARCH); + } else { + if (!eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY)) { + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_PATH); + } } window->details->temporary_navigation_bar = FALSE; } + if (window->details->temporary_search_bar) { + if (!eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY)) { + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_PATH); + } else { + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_NAVIGATION); + } + window->details->temporary_search_bar = FALSE; + } } static void @@ -843,9 +901,8 @@ real_set_content_view_widget (NautilusWindow *nautilus_window, connect_view (window, new_view); - nautilus_horizontal_splitter_pack2 ( - NAUTILUS_HORIZONTAL_SPLITTER (window->details->content_paned), - GTK_WIDGET (new_view)); + gtk_container_add (GTK_CONTAINER (window->details->content_box), + GTK_WIDGET (new_view)); if (new_view != NULL && nautilus_view_supports_zooming (new_view)) { gtk_widget_show (window->zoom_control); @@ -871,15 +928,20 @@ real_set_throbber_active (NautilusWindow *window, gboolean active) } static void -nautilus_navigation_window_show_location_bar_temporarily (NautilusNavigationWindow *window, - gboolean in_search_mode) +nautilus_navigation_window_show_location_bar_temporarily (NautilusNavigationWindow *window) { if (!nautilus_navigation_window_location_bar_showing (window)) { nautilus_navigation_window_show_location_bar (window, FALSE); window->details->temporary_location_bar = TRUE; } - if (nautilus_navigation_window_path_bar_showing (window)) { - nautilus_navigation_window_hide_path_bar (window); +} + +static void +nautilus_navigation_window_show_navigation_bar_temporarily (NautilusNavigationWindow *window) +{ + if (nautilus_navigation_window_path_bar_showing (window) + || nautilus_navigation_window_search_bar_showing (window)) { + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_NAVIGATION); window->details->temporary_navigation_bar = TRUE; } nautilus_navigation_bar_activate @@ -889,13 +951,132 @@ nautilus_navigation_window_show_location_bar_temporarily (NautilusNavigationWind static void real_prompt_for_location (NautilusWindow *window, const char *initial) { - nautilus_navigation_window_show_location_bar_temporarily (NAUTILUS_NAVIGATION_WINDOW (window), FALSE); - + nautilus_navigation_window_show_location_bar_temporarily (NAUTILUS_NAVIGATION_WINDOW (window)); + nautilus_navigation_window_show_navigation_bar_temporarily (NAUTILUS_NAVIGATION_WINDOW (window)); + if (initial) { nautilus_navigation_bar_set_location (NAUTILUS_NAVIGATION_BAR (NAUTILUS_NAVIGATION_WINDOW (window)->navigation_bar), initial); } - +} + +static void +search_bar_activate_callback (NautilusSearchBar *bar, + NautilusWindow *window) +{ + char *uri, *home_uri; + NautilusDirectory *directory; + NautilusSearchDirectory *search_directory; + NautilusQuery *query; + + uri = nautilus_search_directory_generate_new_uri (); + directory = nautilus_directory_get (uri); + + g_assert (NAUTILUS_IS_SEARCH_DIRECTORY (directory)); + + search_directory = NAUTILUS_SEARCH_DIRECTORY (directory); + + query = nautilus_search_bar_get_query (NAUTILUS_SEARCH_BAR (NAUTILUS_NAVIGATION_WINDOW (window)->search_bar)); + if (query != NULL) { + if (!nautilus_search_directory_is_indexed (search_directory)) { + home_uri = nautilus_get_home_directory_uri (); + nautilus_query_set_location (query, home_uri); + g_free (home_uri); + } + nautilus_search_directory_set_query (search_directory, query); + g_object_unref (query); + } + + nautilus_window_go_to (window, uri); + + nautilus_directory_unref (directory); + g_free (uri); +} + +static void +search_bar_cancel_callback (GtkWidget *widget, + NautilusNavigationWindow *window) +{ + hide_temporary_bars (window); +} + +void +nautilus_navigation_window_show_search (NautilusNavigationWindow *window) +{ + if (!nautilus_navigation_window_search_bar_showing (window)) { + nautilus_navigation_window_show_location_bar_temporarily (window); + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_SEARCH); + window->details->temporary_search_bar = TRUE; + nautilus_search_bar_clear (NAUTILUS_SEARCH_BAR (window->search_bar)); + } + + nautilus_search_bar_grab_focus (NAUTILUS_SEARCH_BAR (window->search_bar)); +} + +static void +query_editor_changed_callback (NautilusSearchBar *bar, + NautilusQuery *query, + gboolean reload, + NautilusWindow *window) +{ + NautilusDirectory *directory; + + directory = nautilus_directory_get_for_file (window->details->viewed_file); + g_assert (NAUTILUS_IS_SEARCH_DIRECTORY (directory)); + + nautilus_search_directory_set_query (NAUTILUS_SEARCH_DIRECTORY (directory), + query); + if (reload) { + nautilus_window_reload (window); + } + + nautilus_directory_unref (directory); +} + +static void +real_set_search_mode (NautilusWindow *window, gboolean search_mode, + NautilusSearchDirectory *search_directory) +{ + NautilusNavigationWindow *nav_window; + GtkWidget *query_editor; + NautilusQuery *query; + + nav_window = NAUTILUS_NAVIGATION_WINDOW (window); + + if (!search_mode) { + nav_window->details->temporary_search_bar = TRUE; + hide_temporary_bars (nav_window); + return; + } + + if (nautilus_search_directory_is_saved_search (search_directory)) { + query_editor = nautilus_query_editor_new (TRUE, + nautilus_search_directory_is_indexed (search_directory)); + } else { + nautilus_navigation_window_show_location_bar_temporarily (nav_window); + nautilus_navigation_window_set_bar_mode (nav_window, NAUTILUS_BAR_SEARCH); + nav_window->details->temporary_search_bar = FALSE; + + query_editor = nautilus_query_editor_new_with_bar (FALSE, + nautilus_search_directory_is_indexed (search_directory), + NAUTILUS_SEARCH_BAR (nav_window->search_bar)); + } + + g_signal_connect_object (query_editor, "changed", + G_CALLBACK (query_editor_changed_callback), window, 0); + + query = nautilus_search_directory_get_query (search_directory); + if (query != NULL) { + nautilus_query_editor_set_query (NAUTILUS_QUERY_EDITOR (query_editor), + query); + g_object_unref (query); + }else { + nautilus_query_editor_set_default_query (NAUTILUS_QUERY_EDITOR (query_editor)); + } + + nautilus_window_add_extra_location_widget (window, query_editor); + gtk_widget_show (query_editor); + nautilus_query_editor_grab_focus (NAUTILUS_QUERY_EDITOR (query_editor)); } void @@ -1004,19 +1185,40 @@ nautilus_navigation_window_location_bar_showing (NautilusNavigationWindow *windo return TRUE; } -void -nautilus_navigation_window_hide_path_bar (NautilusNavigationWindow *window) +gboolean +nautilus_navigation_window_search_bar_showing (NautilusNavigationWindow *window) { - window->details->temporary_navigation_bar = FALSE; - gtk_widget_hide (window->path_bar); - gtk_widget_show (window->navigation_bar); + if (window->search_bar != NULL) { + return GTK_WIDGET_VISIBLE (window->search_bar); + } + /* If we're not visible yet we haven't changed visibility, so its TRUE */ + return TRUE; } -void -nautilus_navigation_window_show_path_bar (NautilusNavigationWindow *window) +static void +nautilus_navigation_window_set_bar_mode (NautilusNavigationWindow *window, + NautilusBarMode mode) { - gtk_widget_show (window->path_bar); - gtk_widget_hide (window->navigation_bar); + switch (mode) { + + case NAUTILUS_BAR_PATH: + gtk_widget_show (window->path_bar); + gtk_widget_hide (window->navigation_bar); + gtk_widget_hide (window->search_bar); + break; + + case NAUTILUS_BAR_NAVIGATION: + gtk_widget_show (window->navigation_bar); + gtk_widget_hide (window->path_bar); + gtk_widget_hide (window->search_bar); + break; + + case NAUTILUS_BAR_SEARCH: + gtk_widget_show (window->search_bar); + gtk_widget_hide (window->path_bar); + gtk_widget_hide (window->navigation_bar); + break; + } } gboolean @@ -1196,9 +1398,9 @@ nautilus_navigation_window_show (GtkWidget *widget) } if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY)) { - nautilus_navigation_window_hide_path_bar (window); + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_NAVIGATION); } else { - nautilus_navigation_window_show_path_bar (window); + nautilus_navigation_window_set_bar_mode (window, NAUTILUS_BAR_PATH); } if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_START_WITH_SIDEBAR)) { @@ -1271,8 +1473,9 @@ nautilus_navigation_window_class_init (NautilusNavigationWindowClass *class) NAUTILUS_WINDOW_CLASS (class)->set_content_view_widget = real_set_content_view_widget; 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_search_mode = real_set_search_mode; NAUTILUS_WINDOW_CLASS (class)->set_title = real_set_title; NAUTILUS_WINDOW_CLASS (class)->get_icon_name = real_get_icon_name; - NAUTILUS_WINDOW_CLASS(class)->get_default_size = real_get_default_size; + NAUTILUS_WINDOW_CLASS (class)->get_default_size = real_get_default_size; NAUTILUS_WINDOW_CLASS (class)->close = real_window_close; } diff --git a/src/nautilus-navigation-window.h b/src/nautilus-navigation-window.h index afefcca77..aa4988d8a 100644 --- a/src/nautilus-navigation-window.h +++ b/src/nautilus-navigation-window.h @@ -60,6 +60,7 @@ struct _NautilusNavigationWindow { GtkWidget *view_as_combo_box; GtkWidget *navigation_bar; GtkWidget *path_bar; + GtkWidget *search_bar; /* Back/Forward chain, and history list. * The data in these lists are NautilusBookmark pointers. @@ -97,6 +98,8 @@ void nautilus_navigation_window_hide_path_bar (NautilusNavigationWind void nautilus_navigation_window_show_path_bar (NautilusNavigationWindow *window); gboolean nautilus_navigation_window_path_bar_showing (NautilusNavigationWindow *window); +gboolean nautilus_navigation_window_search_bar_showing (NautilusNavigationWindow *window); + gboolean nautilus_navigation_window_location_bar_showing (NautilusNavigationWindow *window); void nautilus_navigation_window_hide_toolbar (NautilusNavigationWindow *window); void nautilus_navigation_window_show_toolbar (NautilusNavigationWindow *window); @@ -114,5 +117,6 @@ gboolean nautilus_navigation_window_status_bar_showing (NautilusNavigationWind void nautilus_navigation_window_back_or_forward (NautilusNavigationWindow *window, gboolean back, guint distance); +void nautilus_navigation_window_show_search (NautilusNavigationWindow *window); #endif diff --git a/src/nautilus-query-editor.c b/src/nautilus-query-editor.c new file mode 100644 index 000000000..cbc8dd728 --- /dev/null +++ b/src/nautilus-query-editor.c @@ -0,0 +1,1141 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Copyright (C) 2005 Red Hat, Inc. + * + * Nautilus 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. + * + * Nautilus 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; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + * + */ + +#include <config.h> +#include "nautilus-query-editor.h" + +#include <string.h> +#include <libnautilus-private/nautilus-marshal.h> +#include <glib/gi18n.h> +#include <eel/eel-gtk-macros.h> +#include <eel/eel-glib-extensions.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtkbindings.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkentry.h> +#include <gtk/gtkframe.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkstock.h> +#include <gtk/gtkcombobox.h> +#include "gtk/gtkliststore.h" +#include <gtk/gtkfilechooserbutton.h> +#include "gtk/gtkcelllayout.h" +#include "gtk/gtkcellrenderertext.h" +#include <libgnomevfs/gnome-vfs-utils.h> +#include <libgnomevfs/gnome-vfs-mime-info.h> + +typedef enum { + NAUTILUS_QUERY_EDITOR_ROW_LOCATION, + NAUTILUS_QUERY_EDITOR_ROW_TYPE, + + NAUTILUS_QUERY_EDITOR_ROW_LAST +} NautilusQueryEditorRowType; + +typedef struct { + NautilusQueryEditorRowType type; + NautilusQueryEditor *editor; + GtkWidget *hbox; + GtkWidget *combo; + + GtkWidget *type_widget; + + void *data; +} NautilusQueryEditorRow; + + +typedef struct { + const char *name; + GtkWidget * (*create_widgets) (NautilusQueryEditorRow *row); + void (*add_to_query) (NautilusQueryEditorRow *row, + NautilusQuery *query); + void (*free_data) (NautilusQueryEditorRow *row); + void (*add_rows_from_query) (NautilusQueryEditor *editor, + NautilusQuery *query); +} NautilusQueryEditorRowOps; + +struct NautilusQueryEditorDetails { + gboolean is_indexed; + GtkWidget *entry; + gboolean change_frozen; + guint typing_timeout_id; + gboolean is_visible; + GtkWidget *invisible_vbox; + GtkWidget *visible_vbox; + + GList *rows; + + NautilusSearchBar *bar; +}; + +enum { + CHANGED, + CANCEL, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +static void nautilus_query_editor_class_init (NautilusQueryEditorClass *class); +static void nautilus_query_editor_init (NautilusQueryEditor *editor); + +static void entry_activate_cb (GtkWidget *entry, NautilusQueryEditor *editor); +static void entry_changed_cb (GtkWidget *entry, NautilusQueryEditor *editor); +static void nautilus_query_editor_changed_force (NautilusQueryEditor *editor, + gboolean force); +static void nautilus_query_editor_changed (NautilusQueryEditor *editor); +static NautilusQueryEditorRow * nautilus_query_editor_add_row (NautilusQueryEditor *editor, + NautilusQueryEditorRowType type); + +static GtkWidget *location_row_create_widgets (NautilusQueryEditorRow *row); +static void location_row_add_to_query (NautilusQueryEditorRow *row, + NautilusQuery *query); +static void location_row_free_data (NautilusQueryEditorRow *row); +static void location_add_rows_from_query (NautilusQueryEditor *editor, + NautilusQuery *query); +static GtkWidget *type_row_create_widgets (NautilusQueryEditorRow *row); +static void type_row_add_to_query (NautilusQueryEditorRow *row, + NautilusQuery *query); +static void type_row_free_data (NautilusQueryEditorRow *row); +static void type_add_rows_from_query (NautilusQueryEditor *editor, + NautilusQuery *query); + + + +static NautilusQueryEditorRowOps row_type[] = { + { N_("Location"), + location_row_create_widgets, + location_row_add_to_query, + location_row_free_data, + location_add_rows_from_query + }, + { N_("File Type"), + type_row_create_widgets, + type_row_add_to_query, + type_row_free_data, + type_add_rows_from_query + }, +}; + +EEL_CLASS_BOILERPLATE (NautilusQueryEditor, + nautilus_query_editor, + GTK_TYPE_VBOX) + +static void +nautilus_query_editor_finalize (GObject *object) +{ + NautilusQueryEditor *editor; + + editor = NAUTILUS_QUERY_EDITOR (object); + + g_free (editor->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +nautilus_query_editor_dispose (GObject *object) +{ + NautilusQueryEditor *editor; + + editor = NAUTILUS_QUERY_EDITOR (object); + + + if (editor->details->bar != NULL) { + g_signal_handlers_disconnect_by_func (editor->details->entry, + entry_activate_cb, + editor); + g_signal_handlers_disconnect_by_func (editor->details->entry, + entry_changed_cb, + editor); + + nautilus_search_bar_return_entry (editor->details->bar); + eel_remove_weak_pointer (&editor->details->bar); + } + + EEL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object)); +} + +static void +nautilus_query_editor_class_init (NautilusQueryEditorClass *class) +{ + GObjectClass *gobject_class; + GtkBindingSet *binding_set; + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = nautilus_query_editor_finalize; + gobject_class->dispose = nautilus_query_editor_dispose; + + signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusQueryEditorClass, changed), + NULL, NULL, + nautilus_marshal_VOID__OBJECT_BOOLEAN, + G_TYPE_NONE, 2, NAUTILUS_TYPE_QUERY, G_TYPE_BOOLEAN); + + signals[CANCEL] = + g_signal_new ("cancel", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, + G_STRUCT_OFFSET (NautilusQueryEditorClass, cancel), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + binding_set = gtk_binding_set_by_class (class); + gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "cancel", 0); +} + +static void +entry_activate_cb (GtkWidget *entry, NautilusQueryEditor *editor) +{ + if (editor->details->typing_timeout_id) { + g_source_remove (editor->details->typing_timeout_id); + editor->details->typing_timeout_id = 0; + } + + nautilus_query_editor_changed_force (editor, TRUE); +} + +static gboolean +typing_timeout_cb (gpointer user_data) +{ + NautilusQueryEditor *editor; + + editor = NAUTILUS_QUERY_EDITOR (user_data); + + nautilus_query_editor_changed (editor); + + editor->details->typing_timeout_id = 0; + + return FALSE; +} + +#define TYPING_TIMEOUT 750 + +static void +entry_changed_cb (GtkWidget *entry, NautilusQueryEditor *editor) +{ + if (editor->details->change_frozen) { + return; + } + + if (editor->details->typing_timeout_id) { + g_source_remove (editor->details->typing_timeout_id); + } + + editor->details->typing_timeout_id = + g_timeout_add (TYPING_TIMEOUT, + typing_timeout_cb, + editor); +} + +static void +edit_clicked (GtkButton *button, NautilusQueryEditor *editor) +{ + nautilus_query_editor_set_visible (editor, TRUE); +} + +/* Location */ + +static GtkWidget * +location_row_create_widgets (NautilusQueryEditorRow *row) +{ + GtkWidget *chooser; + + chooser = gtk_file_chooser_button_new (_("Select folder search in"), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), + g_get_home_dir ()); + gtk_widget_show (chooser); + + g_signal_connect_swapped (chooser, "current-folder-changed", + G_CALLBACK (nautilus_query_editor_changed), + row->editor); + + gtk_box_pack_start (GTK_BOX (row->hbox), chooser, FALSE, FALSE, 0); + + return chooser; +} + +static void +location_row_add_to_query (NautilusQueryEditorRow *row, + NautilusQuery *query) +{ + char *folder, *uri; + + folder = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (row->type_widget)); + uri = gnome_vfs_get_uri_from_local_path (folder); + g_free (folder); + + nautilus_query_set_location (query, uri); + g_free (uri); +} + +static void +location_row_free_data (NautilusQueryEditorRow *row) +{ +} + +static void +location_add_rows_from_query (NautilusQueryEditor *editor, + NautilusQuery *query) +{ + NautilusQueryEditorRow *row; + char *uri, *folder; + + uri = nautilus_query_get_location (query); + + if (uri == NULL) { + return; + } + folder = gnome_vfs_get_local_path_from_uri (uri); + g_free (uri); + if (folder == NULL) { + return; + } + + row = nautilus_query_editor_add_row (editor, + NAUTILUS_QUERY_EDITOR_ROW_LOCATION); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (row->type_widget), + folder); + + g_free (folder); +} + + +/* Type */ + +static gboolean +type_separator_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + char *text; + gboolean res; + + gtk_tree_model_get (model, iter, 0, &text, -1); + + res = strcmp (text, "---") == 0; + + g_free (text); + return res; +} + +struct { + char *name; + char *mimetypes[20]; +} mime_type_groups[] = { + { N_("Documents"), + { "application/rtf", + "application/msword", + "application/vnd.sun.xml.writer", + "application/vnd.sun.xml.writer.global", + "application/vnd.sun.xml.writer.template", + "application/vnd.oasis.opendocument.text", + "application/vnd.oasis.opendocument.text-template", + "application/x-abiword", + "application/x-applix-word", + "application/x-mswrite", + "application/docbook+xml", + "application/x-kword", + "application/x-kword-crypt", + "application/x-lyx", + NULL + } + }, + { N_("Music"), + { "application/ogg", + "audio/ac3", + "audio/basic", + "audio/midi", + "audio/x-flac", + "audio/mp4", + "audio/mpeg", + "audio/x-mpeg", + "audio/x-ms-asx", + "audio/x-pn-realaudio", + NULL + } + }, + { N_("Video"), + { "video/mp4", + "video/3gpp", + "video/mpeg", + "video/quicktime", + "video/vivo", + "video/x-avi", + "video/x-mng", + "video/x-ms-asf", + "video/x-ms-wmv", + "video/x-msvideo", + "video/x-nsv", + "video/x-real-video", + NULL + } + }, + { N_("Picture"), + { "application/vnd.oasis.opendocument.image", + "application/x-krita", + "image/bmp", + "image/cgm", + "image/gif", + "image/jpeg", + "image/jpeg2000", + "image/png", + "image/svg+xml", + "image/tiff", + "image/x-compressed-xcf", + "image/x-pcx", + "image/x-photo-cd", + "image/x-psd", + "image/x-tga", + "image/x-xcf", + NULL + } + }, + { N_("Illustration"), + { "application/illustrator", + "application/vnd.corel-draw", + "application/vnd.stardivision.draw", + "application/vnd.oasis.opendocument.graphics", + "application/x-dia-diagram", + "application/x-karbon", + "application/x-killustrator", + "application/x-kivio", + "application/x-kontour", + "application/x-wpg", + NULL + } + }, + { N_("Spreadsheet"), + { "application/vnd.lotus-1-2-3", + "application/vnd.ms-excel", + "application/vnd.stardivision.calc", + "application/vnd.sun.xml.calc", + "application/vnd.oasis.opendocument.spreadsheet", + "application/x-applix-spreadsheet", + "application/x-gnumeric", + "application/x-kspread", + "application/x-kspread-crypt", + "application/x-quattropro", + "application/x-sc", + "application/x-siag", + NULL + } + }, + { N_("Presentation"), + { "application/vnd.ms-powerpoint", + "application/vnd.sun.xml.impress", + "application/vnd.oasis.opendocument.presentation", + "application/x-magicpoint", + "application/x-kpresenter", + NULL + } + }, + { N_("Pdf / Postscript"), + { "application/pdf", + "application/postscript", + "application/x-dvi", + "image/x-eps", + NULL + } + }, + { N_("Text File"), + { "text/plain", + NULL + } + } +}; + +static void +type_combo_changed (GtkComboBox *combo_box, NautilusQueryEditorRow *row) +{ + GtkTreeIter iter; + gboolean other; + GtkTreeModel *model; + + if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (row->type_widget), + &iter)) { + return; + } + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget)); + gtk_tree_model_get (model, &iter, 3, &other, -1); + + if (other) { + /* TODO: Ask for other mimetype and add it to list + select it */ + /* But can't read list of mimetypes atm */ + } + + nautilus_query_editor_changed (row->editor); +} + +static void +type_add_custom_type (NautilusQueryEditorRow *row, + const char *mime_type, + const char *description, + GtkTreeIter *iter) +{ + GtkTreeModel *model; + GtkListStore *store; + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget)); + store = GTK_LIST_STORE (model); + + gtk_list_store_append (store, iter); + gtk_list_store_set (store, iter, + 0, description, + 2, mime_type, + -1); +} + + +static GtkWidget * +type_row_create_widgets (NautilusQueryEditorRow *row) +{ + GtkWidget *combo; + GtkCellRenderer *cell; + GtkListStore *store; + GtkTreeIter iter; + int i; + + store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN); + combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + g_object_unref (store); + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell, + "text", 0, + NULL); + gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo), + type_separator_func, + NULL, NULL); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, _("Any"), -1); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "---", -1); + + for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++) { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, gettext (mime_type_groups[i].name), + 1, mime_type_groups[i].mimetypes, + -1); + } + +#if 0 /* Disable this for now, as there is no way to read list of mimetypes */ + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "---", -1); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, _("Other Type..."), 3, TRUE, -1); +#endif + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); + + g_signal_connect (combo, "changed", + G_CALLBACK (type_combo_changed), + row); + + gtk_widget_show (combo); + + gtk_box_pack_start (GTK_BOX (row->hbox), combo, FALSE, FALSE, 0); + + return combo; +} + +static void +type_row_add_to_query (NautilusQueryEditorRow *row, + NautilusQuery *query) +{ + GtkTreeIter iter; + char **mimetypes; + char *mimetype; + GtkTreeModel *model; + + if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (row->type_widget), + &iter)) { + return; + } + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget)); + gtk_tree_model_get (model, &iter, 1, &mimetypes, 2, &mimetype, -1); + + if (mimetypes != NULL) { + while (*mimetypes != NULL) { + nautilus_query_add_mime_type (query, *mimetypes); + mimetypes++; + } + } + if (mimetype) { + nautilus_query_add_mime_type (query, mimetype); + g_free (mimetype); + } +} + +static void +type_row_free_data (NautilusQueryEditorRow *row) +{ +} + +static gboolean +all_group_types_in_list (char **group_types, GList *mime_types) +{ + GList *l; + char **group_type; + char *mime_type; + gboolean found; + + group_type = group_types; + while (*group_type != NULL) { + found = FALSE; + + for (l = mime_types; l != NULL; l = l->next) { + mime_type = l->data; + + if (strcmp (mime_type, *group_type) == 0) { + found = TRUE; + break; + } + } + + if (!found) { + return FALSE; + } + group_type++; + } + return TRUE; +} + +static GList * +remove_group_types_from_list (char **group_types, GList *mime_types) +{ + GList *l, *next; + char **group_type; + char *mime_type; + gboolean found; + + group_type = group_types; + while (*group_type != NULL) { + found = FALSE; + + for (l = mime_types; l != NULL; l = next) { + mime_type = l->data; + next = l->next; + + if (strcmp (mime_type, *group_type) == 0) { + mime_types = g_list_remove_link (mime_types, l); + g_free (mime_type); + break; + } + } + + group_type++; + } + return mime_types; +} + + +static void +type_add_rows_from_query (NautilusQueryEditor *editor, + NautilusQuery *query) +{ + GList *mime_types; + char *mime_type; + const char *desc; + NautilusQueryEditorRow *row; + GtkTreeIter iter; + int i; + GtkTreeModel *model; + GList *l; + + mime_types = nautilus_query_get_mime_types (query); + + if (mime_types == NULL) { + return; + } + + for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++) { + if (all_group_types_in_list (mime_type_groups[i].mimetypes, + mime_types)) { + mime_types = remove_group_types_from_list (mime_type_groups[i].mimetypes, + mime_types); + + row = nautilus_query_editor_add_row (editor, + NAUTILUS_QUERY_EDITOR_ROW_TYPE); + + model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget)); + + gtk_tree_model_iter_nth_child (model, &iter, NULL, i + 2); + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (row->type_widget), + &iter); + } + } + + for (l = mime_types; l != NULL; l = l->next) { + mime_type = l->data; + + desc = gnome_vfs_mime_get_value (mime_type, "description"); + if (desc == NULL) { + desc = mime_type; + } + + row = nautilus_query_editor_add_row (editor, + NAUTILUS_QUERY_EDITOR_ROW_TYPE); + model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget)); + + type_add_custom_type (row, mime_type, desc, &iter); + gtk_combo_box_set_active_iter (GTK_COMBO_BOX (row->type_widget), + &iter); + } + + eel_g_list_free_deep (mime_types); + +} + +/* End of row types */ + +static NautilusQueryEditorRowType +get_next_free_type (NautilusQueryEditor *editor) +{ + NautilusQueryEditorRow *row; + NautilusQueryEditorRowType type; + gboolean found; + GList *l; + + + for (type = 0; type < NAUTILUS_QUERY_EDITOR_ROW_LAST; type++) { + found = FALSE; + for (l = editor->details->rows; l != NULL; l = l->next) { + row = l->data; + if (row->type == type) { + found = TRUE; + break; + } + } + if (!found) { + return type; + } + } + return NAUTILUS_QUERY_EDITOR_ROW_TYPE; +} + +static void +remove_row_cb (GtkButton *clicked_button, NautilusQueryEditorRow *row) +{ + NautilusQueryEditor *editor; + + editor = row->editor; + gtk_container_remove (GTK_CONTAINER (editor->details->visible_vbox), + row->hbox); + + editor->details->rows = g_list_remove (editor->details->rows, row); + + row_type[row->type].free_data (row); + g_free (row); + + nautilus_query_editor_changed (editor); +} + +static void +create_type_widgets (NautilusQueryEditorRow *row) +{ + row->type_widget = row_type[row->type].create_widgets (row); +} + +static void +row_type_combo_changed_cb (GtkComboBox *combo_box, NautilusQueryEditorRow *row) +{ + NautilusQueryEditorRowType type; + + type = gtk_combo_box_get_active (combo_box); + + if (type == row->type) { + return; + } + + if (row->type_widget != NULL) { + gtk_widget_destroy (row->type_widget); + row->type_widget = NULL; + } + + row_type[row->type].free_data (row); + row->data = NULL; + + row->type = type; + + create_type_widgets (row); + + nautilus_query_editor_changed (row->editor); +} + +static NautilusQueryEditorRow * +nautilus_query_editor_add_row (NautilusQueryEditor *editor, + NautilusQueryEditorRowType type) +{ + GtkWidget *hbox, *button, *image, *combo; + NautilusQueryEditorRow *row; + int i; + + row = g_new0 (NautilusQueryEditorRow, 1); + row->editor = editor; + row->type = type; + + hbox = gtk_hbox_new (FALSE, 6); + row->hbox = hbox; + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (editor->details->visible_vbox), hbox, FALSE, FALSE, 0); + + combo = gtk_combo_box_new_text (); + row->combo = combo; + for (i = 0; i < NAUTILUS_QUERY_EDITOR_ROW_LAST; i++) { + gtk_combo_box_append_text (GTK_COMBO_BOX (combo), gettext (row_type[i].name)); + } + gtk_widget_show (combo); + gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), row->type); + + editor->details->rows = g_list_append (editor->details->rows, row); + + g_signal_connect (combo, "changed", + G_CALLBACK (row_type_combo_changed_cb), row); + + create_type_widgets (row); + + button = gtk_button_new (); + image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, + GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_widget_show (image); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (remove_row_cb), row); + + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + return row; +} + +static void +go_search_cb (GtkButton *clicked_button, NautilusQueryEditor *editor) +{ + nautilus_query_editor_changed_force (editor, TRUE); +} + +static void +add_new_row_cb (GtkButton *clicked_button, NautilusQueryEditor *editor) +{ + nautilus_query_editor_add_row (editor, get_next_free_type (editor)); + nautilus_query_editor_changed (editor); +} + +static void +nautilus_query_editor_init (NautilusQueryEditor *editor) +{ + GtkWidget *hbox, *label, *button; + + editor->details = g_new0 (NautilusQueryEditorDetails, 1); + editor->details->is_visible = TRUE; + + editor->details->invisible_vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (editor), editor->details->invisible_vbox, + FALSE, FALSE, 0); + editor->details->visible_vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (editor), editor->details->visible_vbox, + FALSE, FALSE, 0); + /* Only show visible vbox */ + gtk_widget_show (editor->details->visible_vbox); + + /* Create invisible part: */ + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (editor->details->invisible_vbox), + hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL (label), _("<b>Search Folder</b>")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + button = gtk_button_new_with_label (_("Edit")); + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (edit_clicked), editor); +} + +void +nautilus_query_editor_set_default_query (NautilusQueryEditor *editor) +{ + if (!editor->details->is_indexed) { + nautilus_query_editor_add_row (editor, NAUTILUS_QUERY_EDITOR_ROW_LOCATION); + nautilus_query_editor_changed (editor); + } +} + +static void +finish_first_line (NautilusQueryEditor *editor, GtkWidget *hbox, gboolean use_go) +{ + GtkWidget *button, *image; + + button = gtk_button_new (); + image = gtk_image_new_from_stock (GTK_STOCK_ADD, + GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_container_add (GTK_CONTAINER (button), image); + gtk_widget_show (image); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (add_new_row_cb), editor); + + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + if (!editor->details->is_indexed) { + if (use_go) { + button = gtk_button_new_with_label (_("Go")); + } else { + button = gtk_button_new_with_label (_("Reload")); + } + gtk_widget_show (button); + + g_signal_connect (button, "clicked", + G_CALLBACK (go_search_cb), editor); + + gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); + } +} + +static void +setup_internal_entry (NautilusQueryEditor *editor) +{ + GtkWidget *hbox, *label; + + /* Create visible part: */ + hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (editor->details->visible_vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (""); + gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>_Search for:</b>")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + editor->details->entry = gtk_entry_new (); + gtk_label_set_mnemonic_widget (GTK_LABEL (label), editor->details->entry); + gtk_box_pack_start (GTK_BOX (hbox), editor->details->entry, TRUE, TRUE, 0); + + g_signal_connect (editor->details->entry, "activate", + G_CALLBACK (entry_activate_cb), editor); + g_signal_connect (editor->details->entry, "changed", + G_CALLBACK (entry_changed_cb), editor); + gtk_widget_show (editor->details->entry); + + finish_first_line (editor, hbox, TRUE); +} + +static void +setup_external_entry (NautilusQueryEditor *editor, GtkWidget *entry) +{ + GtkWidget *hbox, *label; + + /* Create visible part: */ + hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (hbox); + gtk_box_pack_start (GTK_BOX (editor->details->visible_vbox), hbox, FALSE, FALSE, 0); + + label = gtk_label_new (_("Search results")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + editor->details->entry = entry; + g_signal_connect (editor->details->entry, "activate", + G_CALLBACK (entry_activate_cb), editor); + g_signal_connect (editor->details->entry, "changed", + G_CALLBACK (entry_changed_cb), editor); + + finish_first_line (editor, hbox, FALSE); + +} + +void +nautilus_query_editor_set_visible (NautilusQueryEditor *editor, + gboolean visible) +{ + editor->details->is_visible = visible; + if (visible) { + gtk_widget_show (editor->details->visible_vbox); + gtk_widget_hide (editor->details->invisible_vbox); + } else { + gtk_widget_hide (editor->details->visible_vbox); + gtk_widget_show (editor->details->invisible_vbox); + } +} + +static gboolean +query_is_valid (NautilusQueryEditor *editor) +{ + const char *text; + + text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry)); + + return text != NULL && text[0] != '\0'; +} + +static void +nautilus_query_editor_changed_force (NautilusQueryEditor *editor, gboolean force_reload) +{ + NautilusQuery *query; + + if (editor->details->change_frozen) { + return; + } + + if (query_is_valid (editor)) { + query = nautilus_query_editor_get_query (editor); + g_signal_emit (editor, signals[CHANGED], 0, + query, editor->details->is_indexed || force_reload); + g_object_unref (query); + } +} + +static void +nautilus_query_editor_changed (NautilusQueryEditor *editor) +{ + nautilus_query_editor_changed_force (editor, FALSE); +} + +void +nautilus_query_editor_grab_focus (NautilusQueryEditor *editor) +{ + gtk_widget_grab_focus (editor->details->entry); +} + +NautilusQuery * +nautilus_query_editor_get_query (NautilusQueryEditor *editor) +{ + const char *query_text; + NautilusQuery *query; + GList *l; + NautilusQueryEditorRow *row; + + query_text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry)); + + /* Empty string is a NULL query */ + if (query_text && query_text[0] == '\0') { + return NULL; + } + + query = nautilus_query_new (); + nautilus_query_set_text (query, query_text); + + for (l = editor->details->rows; l != NULL; l = l->next) { + row = l->data; + + row_type[row->type].add_to_query (row, query); + } + + return query; +} + +void +nautilus_query_editor_clear_query (NautilusQueryEditor *editor) +{ + editor->details->change_frozen = TRUE; + gtk_entry_set_text (GTK_ENTRY (editor->details->entry), ""); + editor->details->change_frozen = FALSE; +} + +GtkWidget * +nautilus_query_editor_new (gboolean start_hidden, gboolean is_indexed) +{ + GtkWidget *editor; + + editor = g_object_new (NAUTILUS_TYPE_QUERY_EDITOR, NULL); + + NAUTILUS_QUERY_EDITOR (editor)->details->is_indexed = is_indexed; + + nautilus_query_editor_set_visible (NAUTILUS_QUERY_EDITOR (editor), + !start_hidden); + + setup_internal_entry (NAUTILUS_QUERY_EDITOR (editor)); + + return editor; +} + +GtkWidget* +nautilus_query_editor_new_with_bar (gboolean start_hidden, + gboolean is_indexed, + NautilusSearchBar *bar) +{ + GtkWidget *entry; + NautilusQueryEditor *editor; + + editor = NAUTILUS_QUERY_EDITOR (g_object_new (NAUTILUS_TYPE_QUERY_EDITOR, NULL)); + editor->details->is_indexed = is_indexed; + + nautilus_query_editor_set_visible (editor, !start_hidden); + + editor->details->bar = bar; + eel_add_weak_pointer (&editor->details->bar); + + entry = nautilus_search_bar_borrow_entry (bar); + setup_external_entry (editor, entry); + + return GTK_WIDGET (editor); +} + +void +nautilus_query_editor_set_query (NautilusQueryEditor *editor, NautilusQuery *query) +{ + NautilusQueryEditorRowType type; + const char *text; + + if (!query) { + nautilus_query_editor_clear_query (editor); + return; + } + + text = nautilus_query_get_text (query); + if (!text) { + text = ""; + } + + editor->details->change_frozen = TRUE; + gtk_entry_set_text (GTK_ENTRY (editor->details->entry), text); + + for (type = 0; type < NAUTILUS_QUERY_EDITOR_ROW_LAST; type++) { + row_type[type].add_rows_from_query (editor, query); + } + + editor->details->change_frozen = FALSE; +} diff --git a/src/nautilus-query-editor.h b/src/nautilus-query-editor.h new file mode 100644 index 000000000..38bb88e6d --- /dev/null +++ b/src/nautilus-query-editor.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Copyright (C) 2005 Red Hat, Inc. + * + * Nautilus 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. + * + * Nautilus 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; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + * + */ + +#ifndef NAUTILUS_QUERY_EDITOR_H +#define NAUTILUS_QUERY_EDITOR_H + +#include <gtk/gtkvbox.h> +#include <gtk/gtkentry.h> +#include <libnautilus-private/nautilus-query.h> +#include <nautilus-search-bar.h> + +#define NAUTILUS_TYPE_QUERY_EDITOR (nautilus_query_editor_get_type ()) +#define NAUTILUS_QUERY_EDITOR(obj) GTK_CHECK_CAST (obj, NAUTILUS_TYPE_QUERY_EDITOR, NautilusQueryEditor) +#define NAUTILUS_QUERY_EDITOR_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, NAUTILUS_TYPE_QUERY_EDITOR, NautilusQueryEditorClass) +#define NAUTILUS_IS_QUERY_EDITOR(obj) GTK_CHECK_TYPE (obj, NAUTILUS_TYPE_QUERY_EDITOR) + +typedef struct NautilusQueryEditorDetails NautilusQueryEditorDetails; + +typedef struct NautilusQueryEditor { + GtkVBox parent; + NautilusQueryEditorDetails *details; +} NautilusQueryEditor; + +typedef struct { + GtkVBoxClass parent_class; + + void (* changed) (NautilusQueryEditor *editor, + NautilusQuery *query, + gboolean reload); + void (* cancel) (NautilusQueryEditor *editor); +} NautilusQueryEditorClass; + +GType nautilus_query_editor_get_type (void); +GtkWidget* nautilus_query_editor_new (gboolean start_hidden, + gboolean is_indexed); +GtkWidget* nautilus_query_editor_new_with_bar (gboolean start_hidden, + gboolean is_indexed, + NautilusSearchBar *bar); +void nautilus_query_editor_set_default_query (NautilusQueryEditor *editor); + +void nautilus_query_editor_grab_focus (NautilusQueryEditor *editor); +void nautilus_query_editor_clear_query (NautilusQueryEditor *editor); + +NautilusQuery *nautilus_query_editor_get_query (NautilusQueryEditor *editor); +void nautilus_query_editor_set_query (NautilusQueryEditor *editor, + NautilusQuery *query); +void nautilus_query_editor_set_visible (NautilusQueryEditor *editor, + gboolean visible); + +#endif /* NAUTILUS_QUERY_EDITOR_H */ diff --git a/src/nautilus-search-bar.c b/src/nautilus-search-bar.c new file mode 100644 index 000000000..00c74e146 --- /dev/null +++ b/src/nautilus-search-bar.c @@ -0,0 +1,218 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Copyright (C) 2005 Novell, Inc. + * + * Nautilus 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. + * + * Nautilus 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; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Anders Carlsson <andersca@imendio.com> + * + */ + +#include <config.h> +#include "nautilus-search-bar.h" + +#include <glib/gi18n.h> +#include <eel/eel-gtk-macros.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtkalignment.h> +#include <gtk/gtkbindings.h> +#include <gtk/gtkbutton.h> +#include <gtk/gtkentry.h> +#include <gtk/gtkframe.h> +#include <gtk/gtkhbox.h> +#include <gtk/gtklabel.h> + +struct NautilusSearchBarDetails { + GtkWidget *entry; + gboolean entry_borrowed; +}; + +enum { + ACTIVATE, + CANCEL, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +static void nautilus_search_bar_class_init (NautilusSearchBarClass *class); +static void nautilus_search_bar_init (NautilusSearchBar *bar); + +EEL_CLASS_BOILERPLATE (NautilusSearchBar, + nautilus_search_bar, + GTK_TYPE_EVENT_BOX) + + +static void +finalize (GObject *object) +{ + NautilusSearchBar *bar; + + bar = NAUTILUS_SEARCH_BAR (object); + + g_free (bar->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +nautilus_search_bar_class_init (NautilusSearchBarClass *class) +{ + GObjectClass *gobject_class; + GtkBindingSet *binding_set; + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = finalize; + + signals[ACTIVATE] = + g_signal_new ("activate", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusSearchBarClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[CANCEL] = + g_signal_new ("cancel", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | GTK_RUN_ACTION, + G_STRUCT_OFFSET (NautilusSearchBarClass, cancel), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + binding_set = gtk_binding_set_by_class (class); + gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "cancel", 0); +} + +static gboolean +entry_has_text (NautilusSearchBar *bar) +{ + const char *text; + + text = gtk_entry_get_text (GTK_ENTRY (bar->details->entry)); + + return text != NULL && text[0] != '\0'; +} + +static void +entry_activate_cb (GtkWidget *entry, NautilusSearchBar *bar) +{ + if (entry_has_text (bar) && !bar->details->entry_borrowed) { + g_signal_emit (bar, signals[ACTIVATE], 0); + } +} + + +static void +nautilus_search_bar_init (NautilusSearchBar *bar) +{ + GtkWidget *alignment; + GtkWidget *hbox; + GtkWidget *label; + + bar->details = g_new0 (NautilusSearchBarDetails, 1); + + alignment = gtk_alignment_new (0.5, 0.5, + 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), + 0, 0, 6, 6); + gtk_widget_show (alignment); + gtk_container_add (GTK_CONTAINER (bar), alignment); + + hbox = gtk_hbox_new (FALSE, 6); + gtk_widget_show (hbox); + gtk_container_add (GTK_CONTAINER (alignment), hbox); + + label = gtk_label_new (""); + gtk_label_set_markup (GTK_LABEL (label), _("<b>Search:</b>")); + gtk_widget_show (label); + + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + bar->details->entry = gtk_entry_new (); + gtk_box_pack_start (GTK_BOX (hbox), bar->details->entry, TRUE, TRUE, 0); + + g_signal_connect (bar->details->entry, "activate", + G_CALLBACK (entry_activate_cb), bar); + + gtk_widget_show (bar->details->entry); +} + +GtkWidget * +nautilus_search_bar_borrow_entry (NautilusSearchBar *bar) +{ + GtkBindingSet *binding_set; + + bar->details->entry_borrowed = TRUE; + + binding_set = gtk_binding_set_by_class (G_OBJECT_GET_CLASS (bar)); + gtk_binding_entry_clear (binding_set, GDK_Escape, 0); + return bar->details->entry; +} + +void +nautilus_search_bar_return_entry (NautilusSearchBar *bar) +{ + GtkBindingSet *binding_set; + + bar->details->entry_borrowed = FALSE; + + binding_set = gtk_binding_set_by_class (G_OBJECT_GET_CLASS (bar)); + gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "cancel", 0); +} + +GtkWidget * +nautilus_search_bar_new (void) +{ + GtkWidget *bar; + + bar = g_object_new (NAUTILUS_TYPE_SEARCH_BAR, NULL); + + return bar; +} + +NautilusQuery * +nautilus_search_bar_get_query (NautilusSearchBar *bar) +{ + const char *query_text; + NautilusQuery *query; + + query_text = gtk_entry_get_text (GTK_ENTRY (bar->details->entry)); + + /* Empty string is a NULL query */ + if (query_text && query_text[0] == '\0') { + return NULL; + } + + query = nautilus_query_new (); + nautilus_query_set_text (query, query_text); + + return query; +} + +void +nautilus_search_bar_grab_focus (NautilusSearchBar *bar) +{ + gtk_widget_grab_focus (bar->details->entry); +} + +void +nautilus_search_bar_clear (NautilusSearchBar *bar) +{ + gtk_entry_set_text (GTK_ENTRY (bar->details->entry), ""); +} diff --git a/src/nautilus-search-bar.h b/src/nautilus-search-bar.h new file mode 100644 index 000000000..c278caff4 --- /dev/null +++ b/src/nautilus-search-bar.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Copyright (C) 2005 Novell, Inc. + * + * Nautilus 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. + * + * Nautilus 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; see the file COPYING. If not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Anders Carlsson <andersca@imendio.com> + * + */ + +#ifndef NAUTILUS_SEARCH_BAR_H +#define NAUTILUS_SEARCH_BAR_H + +#include <gtk/gtkeventbox.h> +#include <libnautilus-private/nautilus-query.h> + +#define NAUTILUS_TYPE_SEARCH_BAR (nautilus_search_bar_get_type ()) +#define NAUTILUS_SEARCH_BAR(obj) \ + GTK_CHECK_CAST (obj, NAUTILUS_TYPE_SEARCH_BAR, NautilusSearchBar) +#define NAUTILUS_SEARCH_BAR_CLASS(klass) \ + GTK_CHECK_CLASS_CAST (klass, NAUTILUS_TYPE_SEARCH_BAR, NautilusSearchBarClass) +#define NAUTILUS_IS_SEARCH_BAR(obj) \ + GTK_CHECK_TYPE (obj, NAUTILUS_TYPE_SEARCH_BAR) + +typedef struct NautilusSearchBarDetails NautilusSearchBarDetails; + +typedef struct NautilusSearchBar { + GtkEventBox parent; + NautilusSearchBarDetails *details; +} NautilusSearchBar; + +typedef struct { + GtkEventBoxClass parent_class; + + void (* activate) (NautilusSearchBar *bar); + void (* cancel) (NautilusSearchBar *bar); +} NautilusSearchBarClass; + +GType nautilus_search_bar_get_type (void); +GtkWidget* nautilus_search_bar_new (void); + +GtkWidget * nautilus_search_bar_borrow_entry (NautilusSearchBar *bar); +void nautilus_search_bar_return_entry (NautilusSearchBar *bar); +void nautilus_search_bar_grab_focus (NautilusSearchBar *bar); +NautilusQuery *nautilus_search_bar_get_query (NautilusSearchBar *bar); +void nautilus_search_bar_clear (NautilusSearchBar *bar); + +#endif /* NAUTILUS_SEARCH_BAR_H */ diff --git a/src/nautilus-spatial-window-ui.xml b/src/nautilus-spatial-window-ui.xml index 941450df7..eefb0d590 100644 --- a/src/nautilus-spatial-window-ui.xml +++ b/src/nautilus-spatial-window-ui.xml @@ -17,6 +17,7 @@ <menuitem name="Go to Templates" action="Go to Templates"/> <menuitem name="Go to Trash" action="Go to Trash"/> <menuitem name="Go to Burn CD" action="Go to Burn CD"/> + <menuitem name="Search" action="Search"/> <separator/> <placeholder name="Bookmarks Placeholder"/> <separator/> diff --git a/src/nautilus-spatial-window.c b/src/nautilus-spatial-window.c index 67610d79b..075b37fc7 100644 --- a/src/nautilus-spatial-window.c +++ b/src/nautilus-spatial-window.c @@ -38,6 +38,8 @@ #include "nautilus-bookmarks-window.h" #include "nautilus-location-dialog.h" #include "nautilus-main.h" +#include "nautilus-query-editor.h" +#include "nautilus-search-bar.h" #include "nautilus-signaller.h" #include "nautilus-window-manage-views.h" #include "nautilus-zoom-control.h" @@ -77,6 +79,8 @@ #include <libnautilus-private/nautilus-program-choosing.h> #include <libnautilus-private/nautilus-clipboard.h> #include <libnautilus-private/nautilus-undo.h> +#include <libnautilus-private/nautilus-search-directory.h> +#include <libnautilus-private/nautilus-search-engine.h> #include <math.h> #include <sys/time.h> @@ -98,6 +102,8 @@ struct _NautilusSpatialWindowDetails { GtkWidget *location_label; GtkWidget *location_icon; + GtkWidget *query_editor; + GnomeVFSURI *location; }; @@ -288,6 +294,11 @@ nautilus_spatial_window_show (GtkWidget *widget) window = NAUTILUS_SPATIAL_WINDOW (widget); GTK_WIDGET_CLASS (parent_class)->show (widget); + + if (NAUTILUS_WINDOW (window)->details->search_mode && + window->details->query_editor != NULL) { + nautilus_query_editor_grab_focus (NAUTILUS_QUERY_EDITOR (window->details->query_editor)); + } } static void @@ -319,6 +330,60 @@ real_prompt_for_location (NautilusWindow *window, gtk_widget_show (dialog); } +static void +query_editor_changed_callback (NautilusSearchBar *bar, + NautilusQuery *query, + gboolean reload, + NautilusWindow *window) +{ + NautilusDirectory *directory; + + directory = nautilus_directory_get_for_file (window->details->viewed_file); + g_assert (NAUTILUS_IS_SEARCH_DIRECTORY (directory)); + + nautilus_search_directory_set_query (NAUTILUS_SEARCH_DIRECTORY (directory), + query); + if (reload) { + nautilus_window_reload (window); + } + + nautilus_directory_unref (directory); +} + +static void +real_set_search_mode (NautilusWindow *window, gboolean search_mode, + NautilusSearchDirectory *search_directory) +{ + NautilusSpatialWindow *spatial_window; + GtkWidget *query_editor; + NautilusQuery *query; + + spatial_window = NAUTILUS_SPATIAL_WINDOW (window); + + spatial_window->details->query_editor = NULL; + + if (search_mode) { + query_editor = nautilus_query_editor_new (nautilus_search_directory_is_saved_search (search_directory), + nautilus_search_directory_is_indexed (search_directory)); + spatial_window->details->query_editor = query_editor; + + nautilus_window_add_extra_location_widget (window, query_editor); + gtk_widget_show (query_editor); + nautilus_query_editor_grab_focus (NAUTILUS_QUERY_EDITOR (query_editor)); + g_signal_connect_object (query_editor, "changed", + G_CALLBACK (query_editor_changed_callback), window, 0); + + query = nautilus_search_directory_get_query (search_directory); + if (query != NULL) { + nautilus_query_editor_set_query (NAUTILUS_QUERY_EDITOR (query_editor), + query); + g_object_unref (query); + } else { + nautilus_query_editor_set_default_query (NAUTILUS_QUERY_EDITOR (query_editor)); + } + } +} + static char * real_get_icon_name (NautilusWindow *window) { @@ -746,6 +811,20 @@ action_edit_bookmarks_callback (GtkAction *action, nautilus_window_edit_bookmarks (NAUTILUS_WINDOW (user_data)); } +static void +action_search_callback (GtkAction *action, + gpointer user_data) +{ + NautilusWindow *window; + char *uri; + + window = NAUTILUS_WINDOW (user_data); + + uri = nautilus_search_directory_generate_new_uri (); + nautilus_window_go_to (window, uri); + g_free (uri); +} + static const GtkActionEntry spatial_entries[] = { { SPATIAL_ACTION_PLACES, NULL, N_("_Places") }, /* name, stock id, label */ { SPATIAL_ACTION_GO_TO_LOCATION, NULL, N_("Open _Location..."), /* name, stock id, label */ @@ -763,6 +842,9 @@ static const GtkActionEntry spatial_entries[] = { { "Edit Bookmarks", NULL, N_("_Edit Bookmarks"), /* name, stock id, label */ "<control>b", N_("Display a window that allows editing the bookmarks in this menu"), G_CALLBACK (action_edit_bookmarks_callback) }, + { "Search", "gtk-find", N_("_Search"), /* name, stock id, label */ + "<control>F", N_("Search for files"), + G_CALLBACK (action_search_callback) }, }; static void @@ -770,7 +852,7 @@ nautilus_spatial_window_instance_init (NautilusSpatialWindow *window) { GtkRcStyle *rc_style; GtkWidget *arrow; - GtkWidget *hbox; + GtkWidget *hbox, *vbox, *eventbox, *extras_vbox; GtkActionGroup *action_group; GtkUIManager *ui_manager; GtkTargetList *targets; @@ -779,16 +861,28 @@ nautilus_spatial_window_instance_init (NautilusSpatialWindow *window) window->details = g_new0 (NautilusSpatialWindowDetails, 1); window->affect_spatial_window_on_next_location_change = TRUE; - window->details->content_box = - gtk_hbox_new (FALSE, 0); + vbox = gtk_vbox_new (FALSE, 0); gtk_table_attach (GTK_TABLE (NAUTILUS_WINDOW (window)->details->table), - window->details->content_box, + vbox, /* X direction */ /* Y direction */ 0, 1, 1, 4, GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0); - gtk_widget_show (window->details->content_box); + gtk_widget_show (vbox); + eventbox = gtk_event_box_new (); + gtk_widget_set_name (eventbox, "nautilus-extra-view-widget"); + gtk_box_pack_start (GTK_BOX (vbox), eventbox, FALSE, FALSE, 0); + gtk_widget_show (eventbox); + + extras_vbox = gtk_vbox_new (FALSE, 0); + NAUTILUS_WINDOW (window)->details->extra_location_widgets = extras_vbox; + gtk_container_add (GTK_CONTAINER (eventbox), extras_vbox); + + window->details->content_box = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), window->details->content_box, TRUE, TRUE, 0); + gtk_widget_show (window->details->content_box); + window->details->location_button = gtk_button_new (); g_signal_connect (window->details->location_button, "button-press-event", @@ -861,8 +955,6 @@ nautilus_spatial_window_instance_init (NautilusSpatialWindow *window) ui = nautilus_ui_string_get ("nautilus-spatial-window-ui.xml"); gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL); - - return; } static void @@ -881,6 +973,8 @@ nautilus_spatial_window_class_init (NautilusSpatialWindowClass *class) NAUTILUS_WINDOW_CLASS (class)->prompt_for_location = real_prompt_for_location; + NAUTILUS_WINDOW_CLASS (class)->set_search_mode = + real_set_search_mode; NAUTILUS_WINDOW_CLASS (class)->get_icon_name = real_get_icon_name; NAUTILUS_WINDOW_CLASS (class)->set_title = diff --git a/src/nautilus-window-manage-views.c b/src/nautilus-window-manage-views.c index 305eeb824..1384cbc61 100644 --- a/src/nautilus-window-manage-views.c +++ b/src/nautilus-window-manage-views.c @@ -31,6 +31,7 @@ #include "nautilus-actions.h" #include "nautilus-application.h" #include "nautilus-location-bar.h" +#include "nautilus-search-bar.h" #include "nautilus-pathbar.h" #include "nautilus-main.h" #include "nautilus-window-private.h" @@ -59,6 +60,7 @@ #include <libnautilus-private/nautilus-metadata.h> #include <libnautilus-private/nautilus-mime-actions.h> #include <libnautilus-private/nautilus-monitor.h> +#include <libnautilus-private/nautilus-search-directory.h> #include <libnautilus-private/nautilus-view-factory.h> #include <libnautilus-private/nautilus-window-info.h> @@ -104,6 +106,8 @@ static void location_has_really_changed (NautilusWindow static void update_for_new_location (NautilusWindow *window); static void zoom_parameters_changed_callback (NautilusView *view, NautilusWindow *window); +static void update_extra_location_widgets_visibility (NautilusWindow *window); +static void remove_extra_location_widgets (NautilusWindow *window); void nautilus_window_report_selection_changed (NautilusWindowInfo *window) @@ -1139,6 +1143,8 @@ update_for_new_location (NautilusWindow *window) { char *new_location; NautilusFile *file; + NautilusDirectory *directory; + gboolean location_really_changed; new_location = window->details->pending_location; window->details->pending_location = NULL; @@ -1147,6 +1153,8 @@ update_for_new_location (NautilusWindow *window) update_history (window, window->details->location_change_type, new_location); + location_really_changed = eel_strcmp (window->details->location, new_location) != 0; + /* Set the new location. */ g_free (window->details->location); window->details->location = new_location; @@ -1176,6 +1184,20 @@ update_for_new_location (NautilusWindow *window) /* Load menus from nautilus extensions for this location */ nautilus_window_load_extension_menus (window); + if (location_really_changed) { + remove_extra_location_widgets (window); + + directory = nautilus_directory_get (window->details->location); + if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { + nautilus_window_set_search_mode (window, TRUE, NAUTILUS_SEARCH_DIRECTORY (directory)); + } else { + nautilus_window_set_search_mode (window, FALSE, NULL); + } + nautilus_directory_unref (directory); + + update_extra_location_widgets_visibility (window); + } + #if !NEW_UI_COMPLETE if (NAUTILUS_IS_NAVIGATION_WINDOW (window)) { /* Check if the back and forward buttons need enabling or disabling. */ @@ -1701,3 +1723,44 @@ nautilus_window_reload (NautilusWindow *window) g_free (location); eel_g_list_free_deep (selection); } + +static void +remove_all (GtkWidget *widget, + gpointer data) +{ + GtkContainer *container; + container = GTK_CONTAINER (data); + + gtk_container_remove (container, widget); +} + +static void +remove_extra_location_widgets (NautilusWindow *window) +{ + gtk_container_foreach (GTK_CONTAINER (window->details->extra_location_widgets), + remove_all, + window->details->extra_location_widgets); +} + +void +nautilus_window_add_extra_location_widget (NautilusWindow *window, + GtkWidget *widget) +{ + gtk_box_pack_start (GTK_BOX (window->details->extra_location_widgets), + widget, TRUE, TRUE, 0); +} + +static void +update_extra_location_widgets_visibility (NautilusWindow *window) +{ + GList *children; + + children = gtk_container_get_children (GTK_CONTAINER (window->details->extra_location_widgets)); + + if (children != NULL) { + gtk_widget_show (window->details->extra_location_widgets); + } else { + gtk_widget_show (window->details->extra_location_widgets); + } + g_list_free (children); +} diff --git a/src/nautilus-window-menus.c b/src/nautilus-window-menus.c index aa2c96301..2adbcf8df 100644 --- a/src/nautilus-window-menus.c +++ b/src/nautilus-window-menus.c @@ -39,6 +39,7 @@ #include "nautilus-window-bookmarks.h" #include "nautilus-window-private.h" #include "nautilus-desktop-window.h" +#include "nautilus-search-bar.h" #include <eel/eel-debug.h> #include <eel/eel-glib-extensions.h> #include <eel/eel-gnome-extensions.h> @@ -65,6 +66,8 @@ #include <libnautilus-private/nautilus-icon-factory.h> #include <libnautilus-private/nautilus-module.h> #include <libnautilus-private/nautilus-undo-manager.h> +#include <libnautilus-private/nautilus-search-directory.h> +#include <libnautilus-private/nautilus-search-engine.h> #define MENU_PATH_EXTENSION_ACTIONS "/MenuBar/File/Extension Actions" #define POPUP_PATH_EXTENSION_ACTIONS "/background/Before Zoom Items/Extension Actions" diff --git a/src/nautilus-window-private.h b/src/nautilus-window-private.h index c2bf5f641..45e2242d0 100644 --- a/src/nautilus-window-private.h +++ b/src/nautilus-window-private.h @@ -52,6 +52,8 @@ struct NautilusWindowDetails GtkWidget *table; GtkWidget *statusbar; GtkWidget *menubar; + + GtkWidget *extra_location_widgets; GtkUIManager *ui_manager; GtkActionGroup *main_action_group; /* owned by ui_manager */ @@ -94,15 +96,18 @@ struct NautilusWindowDetails guint location_change_at_idle_id; NautilusWindowShowHiddenFilesMode show_hidden_files_mode; + gboolean search_mode; }; struct _NautilusNavigationWindowDetails { GtkWidget *content_paned; + GtkWidget *content_box; GtkActionGroup *navigation_action_group; /* owned by ui_manager */ /* Location bar */ gboolean temporary_navigation_bar; gboolean temporary_location_bar; + gboolean temporary_search_bar; /* Side Pane */ int side_pane_width; diff --git a/src/nautilus-window.c b/src/nautilus-window.c index 6bd000133..365250a69 100644 --- a/src/nautilus-window.c +++ b/src/nautilus-window.c @@ -40,6 +40,7 @@ #include "nautilus-window-manage-views.h" #include "nautilus-window-bookmarks.h" #include "nautilus-zoom-control.h" +#include "nautilus-search-bar.h" #include <eel/eel-debug.h> #include <eel/eel-marshal.h> #include <eel/eel-gdk-extensions.h> @@ -77,6 +78,7 @@ #include <libnautilus-private/nautilus-view-factory.h> #include <libnautilus-private/nautilus-clipboard.h> #include <libnautilus-private/nautilus-undo.h> +#include <libnautilus-private/nautilus-search-directory.h> #include <math.h> #include <sys/time.h> @@ -88,6 +90,8 @@ #define MAX_HISTORY_ITEMS 50 +#define EXTRA_VIEW_WIDGETS_BACKGROUND "#a7c6e1" + /* FIXME bugzilla.gnome.org 41245: hardwired sizes */ #define SIDE_PANE_MINIMUM_WIDTH 1 #define SIDE_PANE_MINIMUM_HEIGHT 400 @@ -142,7 +146,7 @@ nautilus_window_init (NautilusWindow *window) GtkWidget *table; GtkWidget *menu; GtkWidget *statusbar; - + window->details = g_new0 (NautilusWindowDetails, 1); window->details->show_hidden_files_mode = NAUTILUS_WINDOW_SHOW_HIDDEN_FILES_DEFAULT; @@ -150,7 +154,7 @@ nautilus_window_init (NautilusWindow *window) /* Set initial window title */ gtk_window_set_title (GTK_WINDOW (window), _("Nautilus")); - table = gtk_table_new (1, 5, FALSE); + table = gtk_table_new (1, 6, FALSE); window->details->table = table; gtk_widget_show (table); gtk_container_add (GTK_CONTAINER (window), table); @@ -161,7 +165,7 @@ nautilus_window_init (NautilusWindow *window) gtk_table_attach (GTK_TABLE (table), statusbar, /* X direction */ /* Y direction */ - 0, 1, 4, 5, + 0, 1, 5, 6, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0, 0); window->details->help_message_cid = gtk_statusbar_get_context_id @@ -180,7 +184,6 @@ nautilus_window_init (NautilusWindow *window) GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0, 0); - /* Register IconFactory callback to update the window border icon * when the icon-theme is changed. */ @@ -396,6 +399,20 @@ nautilus_window_get_location (NautilusWindow *window) } void +nautilus_window_set_search_mode (NautilusWindow *window, + gboolean search_mode, + NautilusSearchDirectory *search_directory) +{ + g_assert (NAUTILUS_IS_WINDOW (window)); + + window->details->search_mode = search_mode; + + EEL_CALL_METHOD (NAUTILUS_WINDOW_CLASS, window, + set_search_mode, (window, search_mode, search_directory)); +} + + +void nautilus_window_zoom_in (NautilusWindow *window) { g_return_if_fail (NAUTILUS_IS_WINDOW (window)); @@ -1037,7 +1054,6 @@ nautilus_window_display_error (NautilusWindow *window, const char *error_msg) gtk_widget_show (dialog); } - static char * real_get_title (NautilusWindow *window) { @@ -1558,4 +1574,14 @@ nautilus_window_class_init (NautilusWindowClass *class) class->reload = nautilus_window_reload; class->go_up = nautilus_window_go_up_signal; + + /* Allow to set the colors of the extra view widgets */ + gtk_rc_parse_string ("\n" + " style \"nautilus-extra-view-widgets-style-internal\"\n" + " {\n" + " bg[NORMAL] = \"" EXTRA_VIEW_WIDGETS_BACKGROUND "\"\n" + " }\n" + "\n" + " widget \"*.nautilus-extra-view-widget\" style:rc \"nautilus-extra-view-widgets-style-internal\" \n" + "\n"); } diff --git a/src/nautilus-window.h b/src/nautilus-window.h index 553b71fc9..8c656f7b6 100644 --- a/src/nautilus-window.h +++ b/src/nautilus-window.h @@ -29,11 +29,12 @@ #ifndef NAUTILUS_WINDOW_H #define NAUTILUS_WINDOW_H -#include <bonobo/bonobo-window.h> #include <gtk/gtkuimanager.h> +#include <gtk/gtkwindow.h> #include <eel/eel-glib-extensions.h> #include <libnautilus-private/nautilus-bookmark.h> #include <libnautilus-private/nautilus-window-info.h> +#include <libnautilus-private/nautilus-search-directory.h> #include "nautilus-application.h" #include "nautilus-information-panel.h" #include "nautilus-side-pane.h" @@ -77,6 +78,7 @@ typedef struct { void (* set_allow_up) (NautilusWindow *window, gboolean allow); void (* reload) (NautilusWindow *window); void (* prompt_for_location) (NautilusWindow *window, const char *initial); + void (* set_search_mode) (NautilusWindow *window, gboolean search_enabled, NautilusSearchDirectory *search_directory); void (* get_default_size) (NautilusWindow *window, guint *default_width, guint *default_height); void (* show_window) (NautilusWindow *window); void (* close) (NautilusWindow *window); @@ -128,6 +130,9 @@ void nautilus_window_go_up (NautilusWindow *window gboolean close_behind); void nautilus_window_prompt_for_location (NautilusWindow *window, const char *initial); +void nautilus_window_set_search_mode (NautilusWindow *window, + gboolean search_mode, + NautilusSearchDirectory *search_directory); void nautilus_window_launch_cd_burner (NautilusWindow *window); void nautilus_window_update_title (NautilusWindow *window); void nautilus_window_display_error (NautilusWindow *window, @@ -143,5 +148,7 @@ void nautilus_window_allow_stop (NautilusWindow *window void nautilus_window_allow_burn_cd (NautilusWindow *window, gboolean allow); GtkUIManager * nautilus_window_get_ui_manager (NautilusWindow *window); +void nautilus_window_add_extra_location_widget (NautilusWindow *window, + GtkWidget *widget); #endif |