diff options
82 files changed, 6464 insertions, 506 deletions
diff --git a/.cvsignore b/.cvsignore index d5e3cbdf3..b6db3bc11 100644 --- a/.cvsignore +++ b/.cvsignore @@ -25,6 +25,7 @@ nautilus.desktop nautilus.spec nautilus-computer.desktop nautilus-file-management-properties.desktop +nautilus-folder-handler.desktop nautilus-home.desktop obj stamp-h @@ -1,3 +1,242 @@ +2005-12-06 Alexander Larsson <alexl@redhat.com> + + Merge in changes from NAUTILUS_SEARCH2_MERGE_ANCHOR1 to + NAUTILUS_SEARCH2_MERGE_ANCHOR2 (on HEAD). + (This is NAUTILUS_SEARCH2_MERGE_POINT2) + +2005-12-12 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-file.c (nautilus_file_new_from_relative_uri): + Another case where we need to create a saved search file. + +2005-12-12 Alexander Larsson <alexl@redhat.com> + + * configure.in: + Make it possible to build without beagle. + +2005-12-12 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/Makefile.am: + * libnautilus-private/nautilus-search-engine-simple.[ch]: + Add new NautilusSearchEngineSimple backend. + + * libnautilus-private/nautilus-file-utilities.[ch]: + Add nautilus_get_home_directory_uri. + + * libnautilus-private/nautilus-marshal.list: + Add VOID:OBJECT,BOOLEAN, sort + + * libnautilus-private/nautilus-search-directory.[ch]: + Add nautilus_search_directory_is_indexed. + Make sure saved searches are never returned as search hits. + + * libnautilus-private/nautilus-search-engine-beagle.[ch]: + Make _new fail if we can't contact beagle daemon. + Set text on query as one string (it can have keywords in it) + Move freeing of query data to after query is done. + Add _is_indexed + + * libnautilus-private/nautilus-search-engine.[ch]: + Add nautilus_search_engine_is_indexed + Remove nautilus_search_engine_enabled + Use beagle if availible, otherwise simple backend. + + * src/nautilus-navigation-window-menus.c: + Remove search disabled code + + * src/nautilus-query-editor.[ch]: + Rename activate to changed and add reload argument + Add go/reload button for non-indexed searches + Add mnemonic for entry + Change saved search string to "Search Folder" + + * src/nautilus-navigation-window.c: + Update for query editor changes + For indexed search engines, default to search in homedir + + * src/nautilus-spatial-window.c: + Update for query editor changes + For indexed search engines, default to search in homedir + Remove search disabled code + +2005-12-07 Alexander Larsson <alexl@redhat.com> + + * src/nautilus-query-editor.c (setup_external_entry): + Make ugly string slightly better. + +2005-12-07 Alexander Larsson <alexl@redhat.com> + + * configure.in: + Mime API needs gnome-vfs-module. Unfortunate... + + * libnautilus-private/nautilus-query.c: + Parse queries fully + + * libnautilus-private/nautilus-search-engine-beagle.c: + Look at mime types and location. + + * src/nautilus-query-editor.c: + Add mime type and location to query editor + +2005-12-06 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-query.[ch]: + Add new fields to NautilusQuery + +2005-12-06 Alexander Larsson <alexl@redhat.com> + + Merge in changes from NAUTILUS_2_12_1 to + NAUTILUS_SEARCH2_MERGE_ANCHOR1 (on HEAD). + (This is NAUTILUS_SEARCH2_MERGE_POINT1) + +2005-12-06 Alexander Larsson <alexl@redhat.com> + + * src/nautilus-history-sidebar.c: (update_history), + (nautilus_history_sidebar_init): + Fix leaks of bookmarks and model. + +2005-12-06 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-search-engine-beagle.c: + (nautilus_search_engine_beagle_set_query): + Unref the right query. + +2005-12-05 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-file-utilities.c: + (nautilus_compute_title_for_uri): + Handle NULL query in search. + + * libnautilus-private/nautilus-file-utilities.h: + * libnautilus-private/nautilus-search-directory.h: + Move saved search uri/mimetype define to + nautilus-file-utilities.h. + + * libnautilus-private/nautilus-search-directory.c: + Ref query in get + + * libnautilus-private/nautilus-search-engine.c: + Remove unused stuff. + + * src/Makefile.am: + * src/nautilus-query-editor.[ch]: + Add new files implementing NautilusQueryEditor + + * src/nautilus-places-sidebar.c: + Remove search from places sidebar. + + * src/nautilus-search-bar.[ch]: + Only handle text queries. + + * src/nautilus-window.[ch]: + * src/nautilus-window-private.h: + * src/nautilus-navigation-window-menus.c: + * src/nautilus-window-manage-views.c: + extra location widgets support and some search mode changes + + * src/nautilus-navigation-window.[ch]: + * src/nautilus-spatial-window.c: + Implement new search ui using extra location widgets + + * src/file-manager/fm-directory-view.c: + Implement save search and save search as. + Fix free bugs in fm_directory_view_stop. + + * src/file-manager/fm-icon-view.c: (fm_icon_view_supports_uri): + * src/file-manager/fm-list-view.c: (fm_list_view_supports_uri): + Also handle saved search mimetypes. + + +2005-12-02 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-search-directory.[ch]: + More functions to save queres. + + * src/file-manager/fm-actions.h: + * src/file-manager/fm-directory-view.c: + * src/file-manager/nautilus-directory-view-ui.xml: + Initial stubs for save search menu items. + +2005-12-01 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-directory.c: + * libnautilus-private/nautilus-search-directory.[ch]: + * src/nautilus-bookmark-list.c: + Further refactor the query loading, and remove all traces + of the old load/save mechanism. + +2005-12-01 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-directory.c: + * libnautilus-private/nautilus-query.[ch]: + * libnautilus-private/nautilus-search-directory.[ch]: + Refactor query loading into NautilusQuery object. + +2005-12-01 Alexander Larsson <alexl@redhat.com> + + * configure.in: + Check for update-mime-database + + * data/.cvsignore: + * data/Makefile.am: + * data/nautilus.xml.in: + Add mime database entries for saved searches. + + * libnautilus-private/Makefile.am: + * libnautilus-private/nautilus-saved-search-file.[ch]: + Special file type for saved search files to pretend that + they are directories. + + * libnautilus-private/nautilus-directory.[ch]: + Make nautilus_directory_ref return its arg. + Create NautilusSearchDirectory for saved searches. + + * libnautilus-private/nautilus-file-dnd.c: + Non-editable directories do not accept drops. + + * libnautilus-private/nautilus-file-private.h: + * libnautilus-private/nautilus-file.h: + Move nautilus_file_get_existing to public header. + + * libnautilus-private/nautilus-file.c: + Create saved search files at right time + + * libnautilus-private/nautilus-marshal.list: + Add new marshallers + + * libnautilus-private/nautilus-monitor.c: + Remove some spew + + * libnautilus-private/nautilus-search-directory.[ch]: + Split out search file loading to a separate function. + + * libnautilus-private/nautilus-tree-view-drag-dest.c: + Handle several layers of cannot accept drop. + + * src/file-manager/fm-directory-view.[ch]: + Make files be related to the directory they are in, since + a file can be in a tree multiple times, with searches. + Sorting us now done in base class, and inheriting classes just + add a compare function. + Fix file leak in new_folder_done. + + * src/file-manager/fm-icon-view.c: + Update to new base class API. + In scroll_to_file, don't create a NautilusFile if it doesn't exist. + + * src/file-manager/fm-list-model.[ch]: + Rearrange data so that we can have one file in multiple dirs. + Still problems with the same directory in multiple dirs. + In scroll_to_file, don't create a NautilusFile if it doesn't exist. + Fix leak in each_path_get_data_binder + + * src/file-manager/fm-list-view.c: + Update to new APIs (base class and model). + Fix leak in fm_list_view_get_selection_foreach_func. + + +--- nautilus-search2 branch --- + 2005-12-12 Alexander Larsson <alexl@redhat.com> * src/file-manager/fm-directory-view.c: diff --git a/configure.in b/configure.in index b61142e04..efcc72073 100644 --- a/configure.in +++ b/configure.in @@ -19,6 +19,7 @@ m4_define(rsvg_minver, 2.0.1) m4_define(xml_minver, 2.4.7) m4_define(startup_notification_minver, 0.8) m4_define(exif_minver, 0.5.12) +m4_define(beagle_minver, 0.0.12) AC_INIT(nautilus, 2.13.3, [http://bugzilla.gnome.org/enter_bug.cgi?product=nautilus]) @@ -85,6 +86,7 @@ PKG_CHECK_MODULES(ALL, [ glib-2.0 >= glib_minver gnome-desktop-2.0 >= gnome_desktop_minver gnome-vfs-2.0 >= gnome_vfs_minver + gnome-vfs-module-2.0 >= gnome_vfs_minver ORBit-2.0 >= orbit_minver pango >= pango_minver gtk+-2.0 >= gtk_minver @@ -224,6 +226,27 @@ PKG_CHECK_MODULES(EXIF, libexif > exif_minver, [ AC_SUBST(EXIF_LIBS) ])]) + +dnl ========================================================================== +dnl search implementations +dnl **************************** +AM_CONDITIONAL(HAVE_BEAGLE, false) + +dnl libbeagle checking + +AC_ARG_ENABLE(beagle, [ --disable-beagle build without beagle support]) +msg_beagle=no +if test "x$enable_beagle" != "xno"; then + PKG_CHECK_MODULES(BEAGLE, libbeagle-0.0 >= beagle_minver, [ + AM_CONDITIONAL(HAVE_BEAGLE, true) + AC_DEFINE(HAVE_BEAGLE, 1, [Define to enable beagle support]) + ] + msg_beagle=yes, + [AM_CONDITIONAL(HAVE_BEAGLE, false)]) + AC_SUBST(BEAGLE_CFLAGS) + AC_SUBST(BEAGLE_LIBS) +fi + dnl ========================================================================== dnl Turn on the additional warnings last, so -Werror doesn't affect other tests. @@ -293,7 +316,7 @@ LIBNAUTILUS_EXTENSION_LIBS="`$PKG_CONFIG --libs $LIBNAUTILUS_EXTENSION_MODULES`" AC_SUBST(LIBNAUTILUS_EXTENSION_LIBS) dnl core nautilus (must list bonobo-activation and libbonobo because idldir does not respect "requires") -CORE_MODULES="eel-2.0 librsvg-2.0 bonobo-activation-2.0 libbonobo-2.0 esound gnome-desktop-2.0 $EXTRA_CORE_MODULES" +CORE_MODULES="eel-2.0 librsvg-2.0 bonobo-activation-2.0 libbonobo-2.0 esound gnome-desktop-2.0 gnome-vfs-module-2.0 $EXTRA_CORE_MODULES" CORE_CFLAGS="`$PKG_CONFIG --cflags $CORE_MODULES` $x_cflags" AC_SUBST(CORE_CFLAGS) CORE_LIBS="`$PKG_CONFIG --libs $CORE_MODULES` $CDDA_LIBS $LIBJPEG $x_libs" @@ -331,6 +354,17 @@ fi AM_GCONF_SOURCE_2 +dnl ========================================================================== + +AC_PATH_PROG(UPDATE_MIME_DATABASE, update-mime-database, no) + +AC_ARG_ENABLE(update-mimedb, + AC_HELP_STRING([--disable-update-mimedb], + [disable the update-mime-database after install [default=no]]),, + enable_update_mimedb=yes) +AM_CONDITIONAL(ENABLE_UPDATE_MIMEDB, test x$enable_update_mimedb = xyes) + + AC_CONFIG_FILES([ Makefile cut-n-paste-code/Makefile @@ -362,6 +396,7 @@ nautilus-$VERSION: prefix: ${prefix} source code location: ${srcdir} compiler: ${CC} + beagle support: $msg_beagle profiling support: ${profiling_support} " diff --git a/data/.cvsignore b/data/.cvsignore index 07c9a1a1b..60fc27a7c 100644 --- a/data/.cvsignore +++ b/data/.cvsignore @@ -2,3 +2,4 @@ Makefile Makefile.in *.desktop *.directory +nautilus.xml diff --git a/data/Makefile.am b/data/Makefile.am index 200744891..6fea76049 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,5 +1,12 @@ NULL= +xml_in_files = nautilus.xml.in +xml_files = $(xml_in_files:.xml.in=.xml) +@INTLTOOL_XML_RULE@ + +mimedir = $(datadir)/mime/packages +mime_DATA = $(xml_files) + nautilusdatadir = $(datadir)/nautilus nautilusdata_DATA = \ @@ -9,8 +16,21 @@ nautilusdata_DATA = \ $(NULL) EXTRA_DIST = $(nautilusdata_DATA) \ + $(xml_in_files) \ $(NULL) +CLEANFILES = $(xml_files) + SUBDIRS = \ patterns \ $(NULL) + +install-data-hook: +if ENABLE_UPDATE_MIMEDB + $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" +endif + +uninstall-hook: +if ENABLE_UPDATE_MIMEDB + $(UPDATE_MIME_DATABASE) "$(DESTDIR)$(datadir)/mime" +endif diff --git a/data/nautilus.xml.in b/data/nautilus.xml.in new file mode 100644 index 000000000..744d0d481 --- /dev/null +++ b/data/nautilus.xml.in @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> + <mime-type type="application/x-gnome-saved-search"> + <sub-class-of type="text/xml"/> + <_comment>Saved search</_comment> + <glob pattern="*.savedSearch"/> + </mime-type> +</mime-info> diff --git a/icons/Search.png b/icons/Search.png Binary files differnew file mode 100644 index 000000000..b324cbc2b --- /dev/null +++ b/icons/Search.png diff --git a/icons/nautilus-desktop.png b/icons/nautilus-desktop.png Binary files differnew file mode 100644 index 000000000..e3107a6e0 --- /dev/null +++ b/icons/nautilus-desktop.png diff --git a/icons/nautilus-file-management-properties.png b/icons/nautilus-file-management-properties.png Binary files differnew file mode 100644 index 000000000..05921a668 --- /dev/null +++ b/icons/nautilus-file-management-properties.png diff --git a/icons/nautilus-mini-logo.png b/icons/nautilus-mini-logo.png Binary files differnew file mode 100644 index 000000000..fe1273142 --- /dev/null +++ b/icons/nautilus-mini-logo.png diff --git a/icons/nautilus-server-connect.png b/icons/nautilus-server-connect.png Binary files differnew file mode 100644 index 000000000..cb504eb82 --- /dev/null +++ b/icons/nautilus-server-connect.png diff --git a/icons/side_bar_image.png b/icons/side_bar_image.png Binary files differnew file mode 100644 index 000000000..ee65b95af --- /dev/null +++ b/icons/side_bar_image.png diff --git a/libnautilus-private/Makefile.am b/libnautilus-private/Makefile.am index d3abe35ca..d01102961 100644 --- a/libnautilus-private/Makefile.am +++ b/libnautilus-private/Makefile.am @@ -8,6 +8,7 @@ INCLUDES = \ -I$(top_srcdir)/cut-n-paste-code \ $(CORE_CFLAGS) \ $(DISABLE_DEPRECATED_CFLAGS) \ + $(BEAGLE_CFLAGS) \ -DDATADIR=\""$(datadir)"\" \ -DSYSCONFDIR=\""$(sysconfdir)"\" \ -DNAUTILUS_DATADIR=\""$(datadir)/nautilus"\" \ @@ -27,8 +28,10 @@ libnautilus_private_la_LDFLAGS = \ libnautilus_private_la_LIBADD = \ $(dependency_static_libs) \ + $(BEAGLE_LIBS) \ $(top_builddir)/libnautilus-extension/libnautilus-extension.la \ - $(CORE_LIBS) + $(CORE_LIBS) \ + $(NULL) nautilus_metafile_server_idl_sources = \ nautilus-metafile-server-stubs.c \ @@ -151,10 +154,22 @@ libnautilus_private_la_SOURCES = \ nautilus-program-choosing.h \ nautilus-recent.c \ nautilus-recent.h \ + nautilus-saved-search-file.c \ + nautilus-saved-search-file.h \ + nautilus-search-directory.c \ + nautilus-search-directory.h \ + nautilus-search-directory-file.c \ + nautilus-search-directory-file.h \ + nautilus-search-engine.c \ + nautilus-search-engine.h \ + nautilus-search-engine-simple.c \ + nautilus-search-engine-simple.h \ nautilus-sidebar-provider.c \ nautilus-sidebar-provider.h \ nautilus-sidebar.c \ nautilus-sidebar.h \ + nautilus-query.c \ + nautilus-query.h \ nautilus-thumbnails.c \ nautilus-thumbnails.h \ nautilus-trash-directory.c \ @@ -188,6 +203,12 @@ libnautilus_private_la_SOURCES = \ nautilus-window-info.h \ $(NULL) +if HAVE_BEAGLE +libnautilus_private_la_SOURCES += \ + nautilus-search-engine-beagle.c + nautilus-search-engine-beagle.h +endif + $(lib_LTLIBRARIES): $(dependency_static_libs) $(nautilus_metafile_server_idl_sources): nautilus_metafile_server_idl_stamp diff --git a/libnautilus-private/nautilus-directory-async.c b/libnautilus-private/nautilus-directory-async.c index 20e46c75b..87dc9cfee 100644 --- a/libnautilus-private/nautilus-directory-async.c +++ b/libnautilus-private/nautilus-directory-async.c @@ -1002,7 +1002,7 @@ directory_load_done (NautilusDirectory *directory, } nautilus_directory_emit_load_error (directory, - result); + result, NULL); } /* Call the idle function right away. */ diff --git a/libnautilus-private/nautilus-directory-metafile.c b/libnautilus-private/nautilus-directory-metafile.c index 840d96d66..d6b8ee230 100644 --- a/libnautilus-private/nautilus-directory-metafile.c +++ b/libnautilus-private/nautilus-directory-metafile.c @@ -158,8 +158,6 @@ get_metafile (NautilusDirectory *directory) g_free (uri); } - g_assert (directory->details->metafile_corba_object != CORBA_OBJECT_NIL); - return directory->details->metafile_corba_object; } @@ -168,12 +166,19 @@ nautilus_directory_is_metadata_read (NautilusDirectory *directory) { CORBA_Environment ev; gboolean result; + Nautilus_Metafile metafile; g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), FALSE); CORBA_exception_init (&ev); - result = Nautilus_Metafile_is_read (get_metafile (directory), &ev); + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return TRUE; + } + + result = Nautilus_Metafile_is_read (metafile, &ev); /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ CORBA_exception_free (&ev); @@ -191,6 +196,7 @@ nautilus_directory_get_file_metadata (NautilusDirectory *directory, char *result; const char *non_null_default; CORBA_char *corba_value; + Nautilus_Metafile metafile; g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), g_strdup (default_metadata)); g_return_val_if_fail (!eel_str_is_empty (file_name), g_strdup (default_metadata)); @@ -201,7 +207,13 @@ nautilus_directory_get_file_metadata (NautilusDirectory *directory, CORBA_exception_init (&ev); - corba_value = Nautilus_Metafile_get (get_metafile (directory), file_name, key, non_null_default, &ev); + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return g_strdup (default_metadata); + } + + corba_value = Nautilus_Metafile_get (metafile, file_name, key, non_null_default, &ev); if (ev._major != CORBA_NO_EXCEPTION) { g_warning ("Failed to get file metadata."); @@ -235,16 +247,23 @@ nautilus_directory_get_file_metadata_list (NautilusDirectory *directory, GList *result; Nautilus_MetadataList *corba_value; CORBA_unsigned_long buf_pos; + Nautilus_Metafile metafile; g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL); g_return_val_if_fail (!eel_str_is_empty (file_name), NULL); g_return_val_if_fail (!eel_str_is_empty (list_key), NULL); g_return_val_if_fail (!eel_str_is_empty (list_subkey), NULL); - CORBA_exception_init (&ev); + metafile = get_metafile (directory); - corba_value = Nautilus_Metafile_get_list (get_metafile (directory), file_name, list_key, list_subkey, &ev); + if (metafile == CORBA_OBJECT_NIL) { + return NULL; + } + CORBA_exception_init (&ev); + + corba_value = Nautilus_Metafile_get_list (metafile, file_name, list_key, list_subkey, &ev); + /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ CORBA_exception_free (&ev); @@ -266,11 +285,18 @@ nautilus_directory_set_file_metadata (NautilusDirectory *directory, const char *metadata) { CORBA_Environment ev; + Nautilus_Metafile metafile; g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); g_return_if_fail (!eel_str_is_empty (file_name)); g_return_if_fail (!eel_str_is_empty (key)); + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + /* We can't pass NULL as a CORBA_string - pass "" instead. */ if (default_metadata == NULL) { @@ -282,7 +308,7 @@ nautilus_directory_set_file_metadata (NautilusDirectory *directory, CORBA_exception_init (&ev); - Nautilus_Metafile_set (get_metafile (directory), file_name, key, default_metadata, metadata, &ev); + Nautilus_Metafile_set (metafile, file_name, key, default_metadata, metadata, &ev); /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ CORBA_exception_free (&ev); @@ -296,7 +322,7 @@ nautilus_directory_set_file_metadata_list (NautilusDirectory *directory, GList *list) { CORBA_Environment ev; - + Nautilus_Metafile metafile; Nautilus_MetadataList *corba_list; int len; int buf_pos; @@ -306,7 +332,13 @@ nautilus_directory_set_file_metadata_list (NautilusDirectory *directory, g_return_if_fail (!eel_str_is_empty (file_name)); g_return_if_fail (!eel_str_is_empty (list_key)); g_return_if_fail (!eel_str_is_empty (list_subkey)); - + + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + len = g_list_length (list); corba_list = Nautilus_MetadataList__alloc (); @@ -329,7 +361,7 @@ nautilus_directory_set_file_metadata_list (NautilusDirectory *directory, CORBA_exception_init (&ev); - Nautilus_Metafile_set_list (get_metafile (directory), file_name, list_key, list_subkey, corba_list, &ev); + Nautilus_Metafile_set_list (metafile, file_name, list_key, list_subkey, corba_list, &ev); /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ CORBA_exception_free (&ev); @@ -438,17 +470,24 @@ nautilus_directory_copy_file_metadata (NautilusDirectory *source_directory, { CORBA_Environment ev; char *destination_uri; + Nautilus_Metafile metafile; g_return_if_fail (NAUTILUS_IS_DIRECTORY (source_directory)); g_return_if_fail (source_file_name != NULL); g_return_if_fail (NAUTILUS_IS_DIRECTORY (destination_directory)); g_return_if_fail (destination_file_name != NULL); - + + metafile = get_metafile (source_directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + destination_uri = nautilus_directory_get_uri (destination_directory); CORBA_exception_init (&ev); - Nautilus_Metafile_copy (get_metafile (source_directory), source_file_name, + Nautilus_Metafile_copy (metafile, source_file_name, destination_uri, destination_file_name, &ev); /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ @@ -462,13 +501,20 @@ nautilus_directory_remove_file_metadata (NautilusDirectory *directory, const char *file_name) { CORBA_Environment ev; + Nautilus_Metafile metafile; g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); g_return_if_fail (file_name != NULL); - + + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + CORBA_exception_init (&ev); - Nautilus_Metafile_remove (get_metafile (directory), file_name, &ev); + Nautilus_Metafile_remove (metafile, file_name, &ev); /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ CORBA_exception_free (&ev); @@ -480,14 +526,21 @@ nautilus_directory_rename_file_metadata (NautilusDirectory *directory, const char *new_file_name) { CORBA_Environment ev; + Nautilus_Metafile metafile; g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); g_return_if_fail (old_file_name != NULL); g_return_if_fail (new_file_name != NULL); - + + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + CORBA_exception_init (&ev); - Nautilus_Metafile_rename (get_metafile (directory), old_file_name, new_file_name, &ev); + Nautilus_Metafile_rename (metafile, old_file_name, new_file_name, &ev); /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ CORBA_exception_free (&ev); @@ -498,13 +551,20 @@ nautilus_directory_rename_directory_metadata (NautilusDirectory *directory, const char *new_directory_uri) { CORBA_Environment ev; + Nautilus_Metafile metafile; g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); g_return_if_fail (new_directory_uri != NULL); - + + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + CORBA_exception_init (&ev); - Nautilus_Metafile_rename_directory (get_metafile (directory), new_directory_uri, &ev); + Nautilus_Metafile_rename_directory (metafile, new_directory_uri, &ev); /* FIXME bugzilla.gnome.org 46664: examine ev for errors */ CORBA_exception_free (&ev); @@ -514,6 +574,7 @@ void nautilus_directory_register_metadata_monitor (NautilusDirectory *directory) { CORBA_Environment ev; + Nautilus_Metafile metafile; g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); @@ -522,12 +583,18 @@ nautilus_directory_register_metadata_monitor (NautilusDirectory *directory) return; } + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + directory->details->metafile_monitor = nautilus_metafile_monitor_new (directory); CORBA_exception_init (&ev); Nautilus_Metafile_register_monitor - (get_metafile (directory), + (metafile, BONOBO_OBJREF (directory->details->metafile_monitor), &ev); @@ -539,14 +606,21 @@ void nautilus_directory_unregister_metadata_monitor (NautilusDirectory *directory) { CORBA_Environment ev; + Nautilus_Metafile metafile; g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); g_return_if_fail (NAUTILUS_IS_METAFILE_MONITOR (directory->details->metafile_monitor)); + metafile = get_metafile (directory); + + if (metafile == CORBA_OBJECT_NIL) { + return; + } + CORBA_exception_init (&ev); Nautilus_Metafile_unregister_monitor - (get_metafile (directory), + (metafile, BONOBO_OBJREF (directory->details->metafile_monitor), &ev); diff --git a/libnautilus-private/nautilus-directory-private.h b/libnautilus-private/nautilus-directory-private.h index 4c0cf4e0c..930084f93 100644 --- a/libnautilus-private/nautilus-directory-private.h +++ b/libnautilus-private/nautilus-directory-private.h @@ -192,7 +192,8 @@ void nautilus_directory_emit_change_signals (NautilusD void emit_change_signals_for_all_files (NautilusDirectory *directory); void nautilus_directory_emit_done_loading (NautilusDirectory *directory); void nautilus_directory_emit_load_error (NautilusDirectory *directory, - GnomeVFSResult error_result); + GnomeVFSResult error_result, + const char *error_message); NautilusDirectory *nautilus_directory_get_internal (const char *uri, gboolean create); char * nautilus_directory_get_name_for_self_as_new_file (NautilusDirectory *directory); diff --git a/libnautilus-private/nautilus-directory.c b/libnautilus-private/nautilus-directory.c index 4831480f5..c31b345ca 100644 --- a/libnautilus-private/nautilus-directory.c +++ b/libnautilus-private/nautilus-directory.c @@ -30,8 +30,10 @@ #include "nautilus-file-attributes.h" #include "nautilus-file-private.h" #include "nautilus-file-utilities.h" +#include "nautilus-search-directory.h" #include "nautilus-global-preferences.h" #include "nautilus-lib-self-check-functions.h" +#include "nautilus-marshal.h" #include "nautilus-metadata.h" #include "nautilus-metafile.h" #include "nautilus-desktop-directory.h" @@ -71,6 +73,7 @@ static void nautilus_directory_class_init (NautilusDirectoryClass static NautilusDirectory *nautilus_directory_new (const char *uri); static char * real_get_name_for_self_as_new_file (NautilusDirectory *directory); static GList * real_get_file_list (NautilusDirectory *directory); +static gboolean real_is_editable (NautilusDirectory *directory); static void set_directory_uri (NautilusDirectory *directory, const char *new_uri); @@ -117,11 +120,12 @@ nautilus_directory_class_init (NautilusDirectoryClass *klass) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (NautilusDirectoryClass, 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); klass->get_name_for_self_as_new_file = real_get_name_for_self_as_new_file; klass->get_file_list = real_get_file_list; + klass->is_editable = real_is_editable; g_type_class_add_private (klass, sizeof (NautilusDirectoryDetails)); } @@ -143,16 +147,17 @@ nautilus_directory_init (gpointer object, gpointer klass) directory->details->hidden_file_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); } -void +NautilusDirectory * nautilus_directory_ref (NautilusDirectory *directory) { if (directory == NULL) { - return; + return directory; } - g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory)); + g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL); g_object_ref (directory); + return directory; } void @@ -515,6 +520,10 @@ nautilus_directory_new (const char *uri) directory = NAUTILUS_DIRECTORY (g_object_new (NAUTILUS_TYPE_TRASH_DIRECTORY, NULL)); } else if (eel_uri_is_desktop (uri)) { directory = NAUTILUS_DIRECTORY (g_object_new (NAUTILUS_TYPE_DESKTOP_DIRECTORY, NULL)); + } else if (eel_uri_is_search (uri)) { + directory = NAUTILUS_DIRECTORY (g_object_new (NAUTILUS_TYPE_SEARCH_DIRECTORY, NULL)); + } else if (g_str_has_suffix (uri, NAUTILUS_SAVED_SEARCH_EXTENSION)) { + directory = NAUTILUS_DIRECTORY (nautilus_search_directory_new_from_saved_search (uri)); } else { directory = NAUTILUS_DIRECTORY (g_object_new (NAUTILUS_TYPE_VFS_DIRECTORY, NULL)); } @@ -764,11 +773,12 @@ nautilus_directory_emit_done_loading (NautilusDirectory *directory) void nautilus_directory_emit_load_error (NautilusDirectory *directory, - GnomeVFSResult error_result) + GnomeVFSResult error_result, + const char *error_message) { g_signal_emit (directory, signals[LOAD_ERROR], 0, - error_result); + error_result, error_message); } @@ -994,7 +1004,6 @@ nautilus_directory_notify_files_added (GList *uris) nautilus_directory_unref (directory); } - /* Now get file info for the new files. This creates NautilusFile * objects for the new files, and sends out a files_added signal. */ @@ -1652,6 +1661,20 @@ real_get_file_list (NautilusDirectory *directory) return non_tentative_files; } +static gboolean +real_is_editable (NautilusDirectory *directory) +{ + return TRUE; +} + +gboolean +nautilus_directory_is_editable (NautilusDirectory *directory) +{ + return EEL_CALL_METHOD_WITH_RETURN_VALUE + (NAUTILUS_DIRECTORY_CLASS, directory, + is_editable, (directory)); +} + GList * nautilus_directory_match_pattern (NautilusDirectory *directory, const char *pattern) { diff --git a/libnautilus-private/nautilus-directory.h b/libnautilus-private/nautilus-directory.h index c708daaaa..10571bb4a 100644 --- a/libnautilus-private/nautilus-directory.h +++ b/libnautilus-private/nautilus-directory.h @@ -100,7 +100,9 @@ typedef struct */ void (* done_loading) (NautilusDirectory *directory); - void (* load_error) (NautilusDirectory *directory); + void (* load_error) (NautilusDirectory *directory, + GnomeVFSResult error_result, + const char *error_message); /*** Virtual functions for subclasses to override. ***/ gboolean (* contains_file) (NautilusDirectory *directory, @@ -134,6 +136,12 @@ typedef struct * the list of standard icons (Computer, Home, Trash) on the desktop. */ GList * (* get_file_list) (NautilusDirectory *directory); + + /* Should return FALSE if the directory is read-only and doesn't + * allow setting of metadata. + * An example of this is the search directory. + */ + gboolean (* is_editable) (NautilusDirectory *directory); } NautilusDirectoryClass; /* Basic GObject requirements. */ @@ -151,7 +159,7 @@ NautilusDirectory *nautilus_directory_get_for_file (NautilusFile * 1) You don't have to cast to GtkObject *, so using these is type safe. * 2) You are allowed to call these with NULL, */ -void nautilus_directory_ref (NautilusDirectory *directory); +NautilusDirectory *nautilus_directory_ref (NautilusDirectory *directory); void nautilus_directory_unref (NautilusDirectory *directory); /* Access to a URI. */ @@ -227,5 +235,7 @@ GList * nautilus_directory_list_sort_by_uri (GList /* Fast way to check if a directory is the desktop directory */ gboolean nautilus_directory_is_desktop_directory (NautilusDirectory *directory); +gboolean nautilus_directory_is_editable (NautilusDirectory *directory); + #endif /* NAUTILUS_DIRECTORY_H */ diff --git a/libnautilus-private/nautilus-file-dnd.c b/libnautilus-private/nautilus-file-dnd.c index fd55a3d5c..4729831d6 100644 --- a/libnautilus-private/nautilus-file-dnd.c +++ b/libnautilus-private/nautilus-file-dnd.c @@ -28,6 +28,7 @@ #include "nautilus-desktop-icon-file.h" #include "nautilus-dnd.h" +#include "nautilus-directory.h" #include <eel/eel-glib-extensions.h> #include <eel/eel-string.h> @@ -35,14 +36,20 @@ gboolean nautilus_drag_can_accept_item (NautilusFile *drop_target_item, const char *item_uri) { + NautilusDirectory *directory; + gboolean res; + if (nautilus_file_matches_uri (drop_target_item, item_uri)) { /* can't accept itself */ return FALSE; } if (nautilus_file_is_directory (drop_target_item)) { - /* target is a directory, accept anything */ - return TRUE; + /* target is a directory, accept if editable */ + directory = nautilus_directory_get_for_file (drop_target_item); + res = nautilus_directory_is_editable (directory); + nautilus_directory_unref (directory); + return res; } if (NAUTILUS_IS_DESKTOP_ICON_FILE (drop_target_item)) { diff --git a/libnautilus-private/nautilus-file-private.h b/libnautilus-private/nautilus-file-private.h index 24466f8ab..7faae5a85 100644 --- a/libnautilus-private/nautilus-file-private.h +++ b/libnautilus-private/nautilus-file-private.h @@ -151,7 +151,6 @@ struct NautilusFileDetails NautilusFile *nautilus_file_new_from_info (NautilusDirectory *directory, GnomeVFSFileInfo *info); -NautilusFile *nautilus_file_get_existing (const char *uri); void nautilus_file_emit_changed (NautilusFile *file); void nautilus_file_mark_gone (NautilusFile *file); gboolean nautilus_file_info_missing (NautilusFile *file, diff --git a/libnautilus-private/nautilus-file-utilities.c b/libnautilus-private/nautilus-file-utilities.c index d5fc80833..0f4fbc74f 100644 --- a/libnautilus-private/nautilus-file-utilities.c +++ b/libnautilus-private/nautilus-file-utilities.c @@ -30,6 +30,7 @@ #include "nautilus-metadata.h" #include "nautilus-metafile.h" #include "nautilus-file.h" +#include "nautilus-search-directory.h" #include <eel/eel-glib-extensions.h> #include <eel/eel-string.h> #include <eel/eel-vfs-extensions.h> @@ -56,10 +57,26 @@ nautilus_compute_title_for_uri (const char *text_uri) GnomeVFSURI *uri; char *title, *displayname; const char *hostname; - + NautilusDirectory *directory; + NautilusQuery *query; hostname = NULL; if (text_uri) { + if (eel_uri_is_search (text_uri)) { + directory = nautilus_directory_get (text_uri); + + query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory)); + nautilus_directory_unref (directory); + + if (query != NULL) { + title = nautilus_query_to_readable_string (query); + g_object_unref (query); + } else { + title = g_strdup (_("Search")); + } + + return title; + } file = nautilus_file_get (text_uri); uri = gnome_vfs_uri_new (text_uri); if (uri && !gnome_vfs_uri_is_local (uri)) { @@ -208,6 +225,13 @@ nautilus_get_desktop_directory_uri_no_create (void) } char * +nautilus_get_home_directory_uri (void) +{ + return gnome_vfs_get_uri_from_local_path (g_get_home_dir ()); +} + + +char * nautilus_get_templates_directory (void) { return g_build_filename (g_get_home_dir(), @@ -237,6 +261,22 @@ nautilus_get_templates_directory_uri (void) return uri; } +char * +nautilus_get_searches_directory (void) +{ + char *user_dir; + char *searches_dir; + + user_dir = nautilus_get_user_directory (); + searches_dir = g_build_filename (user_dir, "searches", NULL); + g_free (user_dir); + + if (!g_file_test (searches_dir, G_FILE_TEST_EXISTS)) + mkdir (searches_dir, DEFAULT_NAUTILUS_DIRECTORY_MODE); + + return searches_dir; +} + /* These need to be reset to NULL when desktop_is_home_dir changes */ static char *escaped_desktop_dir = NULL; static char *escaped_desktop_dir_dirname = NULL; diff --git a/libnautilus-private/nautilus-file-utilities.h b/libnautilus-private/nautilus-file-utilities.h index 15b04c68c..ac8a520ea 100644 --- a/libnautilus-private/nautilus-file-utilities.h +++ b/libnautilus-private/nautilus-file-utilities.h @@ -27,6 +27,9 @@ #include <libgnomevfs/gnome-vfs-types.h> +#define NAUTILUS_SAVED_SEARCH_EXTENSION ".savedSearch" +#define NAUTILUS_SAVED_SEARCH_MIMETYPE "application/x-gnome-saved-search" + /* Recognizing special file names. */ gboolean nautilus_file_name_matches_hidden_pattern (const char *name_or_relative_uri); gboolean nautilus_file_name_matches_backup_pattern (const char *name_or_relative_uri); @@ -37,6 +40,7 @@ gboolean nautilus_file_name_matches_backup_pattern (const char *name_or_relati char * nautilus_get_user_directory (void); char * nautilus_get_desktop_directory (void); char * nautilus_get_desktop_directory_uri (void); +char * nautilus_get_home_directory_uri (void); gboolean nautilus_is_desktop_directory_file_escaped (char *escaped_dirname, char *escaped_filename); gboolean nautilus_is_desktop_directory_escaped (char *escaped_dir); @@ -49,6 +53,8 @@ char * nautilus_get_templates_directory (void); char * nautilus_get_templates_directory_uri (void); void nautilus_create_templates_directory (void); +char * nautilus_get_searches_directory (void); + char * nautilus_compute_title_for_uri (const char *text_uri); /* This function returns something that needs to be freed with g_free, diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c index bd0daa4c1..aa55dd441 100644 --- a/libnautilus-private/nautilus-file.c +++ b/libnautilus-private/nautilus-file.c @@ -40,10 +40,13 @@ #include "nautilus-link-desktop-file.h" #include "nautilus-metadata.h" #include "nautilus-module.h" +#include "nautilus-search-directory.h" +#include "nautilus-search-directory-file.h" #include "nautilus-thumbnails.h" #include "nautilus-trash-directory.h" #include "nautilus-trash-file.h" #include "nautilus-vfs-file.h" +#include "nautilus-saved-search-file.h" #include <eel/eel-debug.h> #include <eel/eel-glib-extensions.h> #include <eel/eel-gtk-extensions.h> @@ -72,12 +75,15 @@ /* Time in seconds to cache getpwuid results */ #define GETPWUID_CACHE_TIME (5*60) + #undef NAUTILUS_FILE_DEBUG_REF +#undef NAUTILUS_FILE_DEBUG_REF_VALGRIND -#ifdef NAUTILUS_FILE_DEBUG_REF -extern void eazel_dump_stack_trace (const char *print_prefix, - int num_levels); -/* from libleakcheck.so */ +#ifdef NAUTILUS_FILE_DEBUG_REF_VALGRIND +#include <valgrind/valgrind.h> +#define DEBUG_REF_PRINTF VALGRIND_PRINTF_BACKTRACE +#else +#define DEBUG_REF_PRINTF printf #endif /* Files that start with these characters sort after files that don't. */ @@ -195,20 +201,28 @@ nautilus_file_new_from_relative_uri (NautilusDirectory *directory, file = NULL; g_assert_not_reached (); } + } else if (NAUTILUS_IS_SEARCH_DIRECTORY (directory)) { + if (self_owned) { + file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_SEARCH_DIRECTORY_FILE, NULL)); + } else { + file = NULL; + g_assert_not_reached (); + } + } else if (g_str_has_suffix (relative_uri, NAUTILUS_SAVED_SEARCH_EXTENSION)) { + file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_SAVED_SEARCH_FILE, NULL)); } else { file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_VFS_FILE, NULL)); } -#ifdef NAUTILUS_FILE_DEBUG_REF - printf("%10p ref'd\n", file); - eazel_dump_stack_trace ("\t", 10); -#endif - nautilus_directory_ref (directory); file->details->directory = directory; file->details->relative_uri = g_strdup (relative_uri); +#ifdef NAUTILUS_FILE_DEBUG_REF + DEBUG_REF_PRINTF("%10p ref'd", file); +#endif + return file; } @@ -305,17 +319,23 @@ nautilus_file_new_from_info (NautilusDirectory *directory, g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL); g_return_val_if_fail (info != NULL, NULL); - file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_VFS_FILE, NULL)); - -#ifdef NAUTILUS_FILE_DEBUG_REF - printf("%10p ref'd\n", file); - eazel_dump_stack_trace ("\t", 10); -#endif + if (info->mime_type && + strcmp (info->mime_type, NAUTILUS_SAVED_SEARCH_MIMETYPE) == 0) { + file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_SAVED_SEARCH_FILE, NULL)); + } else { + file = NAUTILUS_FILE (g_object_new (NAUTILUS_TYPE_VFS_FILE, NULL)); + } nautilus_directory_ref (directory); file->details->directory = directory; update_info_and_name (file, info, FALSE); + + +#ifdef NAUTILUS_FILE_DEBUG_REF + DEBUG_REF_PRINTF("%10p ref'd", file); +#endif + return file; } @@ -397,14 +417,22 @@ nautilus_file_get_internal (const char *uri, gboolean create) file_name = nautilus_directory_get_name_for_self_as_new_file (directory); relative_uri = gnome_vfs_escape_string (file_name); g_free (file_name); - } else if (eel_uri_is_desktop (uri)) { - /* Special case desktop files here. They have no vfs_uri */ - relative_uri_tmp = uri + strlen (EEL_DESKTOP_URI); + } else if (eel_uri_is_desktop (uri) || + eel_uri_is_search (uri)) { + /* Special case virtual methods like desktop and search + files here. They have no vfs_uri. */ + relative_uri_tmp = uri; + /* Skip "method:" */ + while (*relative_uri_tmp != 0 && *relative_uri_tmp != ':') { + relative_uri_tmp++; + } + relative_uri_tmp++; + /* Skip initial slashes */ while (*relative_uri_tmp == '/') { relative_uri_tmp++; } relative_uri = strdup (relative_uri_tmp); - } + } } /* Check to see if it's a file that's already known. */ @@ -524,12 +552,10 @@ nautilus_file_ref (NautilusFile *file) g_return_val_if_fail (NAUTILUS_IS_FILE (file), NULL); #ifdef NAUTILUS_FILE_DEBUG_REF - printf("%10p ref'd\n", file); - eazel_dump_stack_trace ("\t", 10); + DEBUG_REF_PRINTF("%10p ref'd", file); #endif - - g_object_ref (file); - return file; + + return g_object_ref (file); } void @@ -542,10 +568,9 @@ nautilus_file_unref (NautilusFile *file) g_return_if_fail (NAUTILUS_IS_FILE (file)); #ifdef NAUTILUS_FILE_DEBUG_REF - printf("%10p unref'd\n", file); - eazel_dump_stack_trace ("\t", 10); + DEBUG_REF_PRINTF("%10p unref'd", file); #endif - + g_object_unref (file); } diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h index bb2562a18..d14d0e07a 100644 --- a/libnautilus-private/nautilus-file.h +++ b/libnautilus-private/nautilus-file.h @@ -97,6 +97,9 @@ GType nautilus_file_get_type (void); /* Getting at a single file. */ NautilusFile * nautilus_file_get (const char *uri); +/* Get a file only if the nautilus version already exists */ +NautilusFile * nautilus_file_get_existing (const char *uri); + /* Covers for gtk_object_ref and gtk_object_unref that provide two conveniences: * 1) You don't have to cast to GtkObject *, so using these is type safe. * 2) You are allowed to call these with NULL, diff --git a/libnautilus-private/nautilus-icon-container.c b/libnautilus-private/nautilus-icon-container.c index db92bb4a5..85207694f 100644 --- a/libnautilus-private/nautilus-icon-container.c +++ b/libnautilus-private/nautilus-icon-container.c @@ -3558,6 +3558,7 @@ motion_notify_event (GtkWidget *widget, NautilusIconContainerDetails *details; double world_x, world_y; int canvas_x, canvas_y; + GdkDragAction actions; container = NAUTILUS_ICON_CONTAINER (widget); details = container->details; @@ -3588,13 +3589,16 @@ motion_notify_event (GtkWidget *widget, &canvas_x, &canvas_y); + actions = GDK_ACTION_COPY + | GDK_ACTION_LINK + | GDK_ACTION_ASK; + + if (container->details->drag_allow_moves) { + actions |= GDK_ACTION_MOVE; + } + nautilus_icon_dnd_begin_drag (container, - details->drag_state == DRAG_STATE_MOVE_OR_COPY - ? (GDK_ACTION_MOVE - | GDK_ACTION_COPY - | GDK_ACTION_LINK - | GDK_ACTION_ASK) - : GDK_ACTION_ASK, + actions, details->drag_button, event, canvas_x, @@ -7216,6 +7220,23 @@ nautilus_icon_container_get_icon_description (NautilusIconContainer *container, } } +gboolean +nautilus_icon_container_get_allow_moves (NautilusIconContainer *container) +{ + g_return_val_if_fail (NAUTILUS_IS_ICON_CONTAINER (container), FALSE); + + return container->details->drag_allow_moves; +} + +void +nautilus_icon_container_set_allow_moves (NautilusIconContainer *container, + gboolean allow_moves) +{ + g_return_if_fail (NAUTILUS_IS_ICON_CONTAINER (container)); + + container->details->drag_allow_moves = allow_moves; +} + /* NautilusIconContainerAccessible */ static NautilusIconContainerAccessiblePrivate * diff --git a/libnautilus-private/nautilus-icon-container.h b/libnautilus-private/nautilus-icon-container.h index c3fe4c5ae..83b899ed8 100644 --- a/libnautilus-private/nautilus-icon-container.h +++ b/libnautilus-private/nautilus-icon-container.h @@ -287,5 +287,8 @@ void nautilus_icon_container_set_use_drop_shadows (Nautilu gboolean use_drop_shadows); char* nautilus_icon_container_get_icon_description (NautilusIconContainer *container, NautilusIconData *data); +gboolean nautilus_icon_container_get_allow_moves (NautilusIconContainer *container); +void nautilus_icon_container_set_allow_moves (NautilusIconContainer *container, + gboolean allow_moves); #endif /* NAUTILUS_ICON_CONTAINER_H */ diff --git a/libnautilus-private/nautilus-icon-factory.c b/libnautilus-private/nautilus-icon-factory.c index 10ddfb888..e47f97dbe 100644 --- a/libnautilus-private/nautilus-icon-factory.c +++ b/libnautilus-private/nautilus-icon-factory.c @@ -830,6 +830,9 @@ get_special_icon_for_file (NautilusFile *file) } else { ret = ICON_NAME_TRASH_FULL; } + } else if (eel_uri_is_search (uri)) { + /* FIXME: We really need a better icon than this */ + ret = "gnome-searchtool"; } g_free (uri); diff --git a/libnautilus-private/nautilus-icon-private.h b/libnautilus-private/nautilus-icon-private.h index ae8a993a4..442b2dfa5 100644 --- a/libnautilus-private/nautilus-icon-private.h +++ b/libnautilus-private/nautilus-icon-private.h @@ -155,6 +155,7 @@ struct NautilusIconContainerDetails { DragState drag_state; gboolean drag_started; StretchState stretch_start; + gboolean drag_allow_moves; gboolean icon_selected_on_button_down; NautilusIcon *double_click_icon[2]; /* Both clicks in a double click need to be on the same icon */ diff --git a/libnautilus-private/nautilus-marshal.list b/libnautilus-private/nautilus-marshal.list index 5b7826c1b..beeff9dc5 100644 --- a/libnautilus-private/nautilus-marshal.list +++ b/libnautilus-private/nautilus-marshal.list @@ -1,17 +1,20 @@ -STRING:VOID -OBJECT:BOXED BOOLEAN:POINTER BOOLEAN:VOID -INT:POINTER,INT INT:POINTER,BOOLEAN +INT:POINTER,INT INT:POINTER,POINTER +OBJECT:BOXED POINTER:VOID +STRING:VOID VOID:DOUBLE -VOID:STRING,ENUM,INT,INT +VOID:INT,STRING +VOID:OBJECT,BOOLEAN +VOID:OBJECT,OBJECT +VOID:POINTER,ENUM VOID:POINTER,POINTER VOID:POINTER,POINTER +VOID:POINTER,POINTER,POINTER,ENUM,INT,INT VOID:POINTER,STRING VOID:POINTER,STRING,ENUM,INT,INT +VOID:STRING,ENUM,INT,INT VOID:STRING,STRING -VOID:POINTER,POINTER,POINTER,ENUM,INT,INT -VOID:POINTER,ENUM diff --git a/libnautilus-private/nautilus-metafile-factory.c b/libnautilus-private/nautilus-metafile-factory.c index eadd69336..c939b440b 100644 --- a/libnautilus-private/nautilus-metafile-factory.c +++ b/libnautilus-private/nautilus-metafile-factory.c @@ -54,6 +54,10 @@ corba_open (PortableServer_Servant servant, metafile = nautilus_metafile_get (directory); + if (!metafile) { + return CORBA_OBJECT_NIL; + } + return CORBA_Object_duplicate (BONOBO_OBJREF (metafile), ev); } diff --git a/libnautilus-private/nautilus-metafile.c b/libnautilus-private/nautilus-metafile.c index 7ddefd490..f0541c163 100644 --- a/libnautilus-private/nautilus-metafile.c +++ b/libnautilus-private/nautilus-metafile.c @@ -249,7 +249,12 @@ nautilus_metafile_get (const char *directory_uri) char *canonical_uri; g_return_val_if_fail (directory_uri != NULL, NULL); - + + /* We don't have metafiles for search uris */ + if (eel_uri_is_search (directory_uri)) { + return NULL; + } + if (metafiles == NULL) { metafiles = eel_g_hash_table_new_free_at_exit (g_str_hash, g_str_equal, __FILE__ ": metafiles"); diff --git a/libnautilus-private/nautilus-query.c b/libnautilus-private/nautilus-query.c new file mode 100644 index 000000000..b8e761a63 --- /dev/null +++ b/libnautilus-private/nautilus-query.c @@ -0,0 +1,372 @@ +/* -*- 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 <string.h> + +#include "nautilus-query.h" +#include <libgnomevfs/gnome-vfs-utils.h> +#include <eel/eel-gtk-macros.h> +#include <eel/eel-glib-extensions.h> + +struct NautilusQueryDetails { + char *text; + char *location_uri; + GList *mime_types; +}; + +static void nautilus_query_class_init (NautilusQueryClass *class); +static void nautilus_query_init (NautilusQuery *query); + +G_DEFINE_TYPE (NautilusQuery, + nautilus_query, + G_TYPE_OBJECT); + +static GObjectClass *parent_class = NULL; + +static void +finalize (GObject *object) +{ + NautilusQuery *query; + + query = NAUTILUS_QUERY (object); + + g_free (query->details->text); + g_free (query->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +nautilus_query_class_init (NautilusQueryClass *class) +{ + GObjectClass *gobject_class; + + parent_class = g_type_class_peek_parent (class); + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = finalize; +} + +static void +nautilus_query_init (NautilusQuery *query) +{ + query->details = g_new0 (NautilusQueryDetails, 1); +} + +NautilusQuery * +nautilus_query_new (void) +{ + return g_object_new (NAUTILUS_TYPE_QUERY, NULL); +} + + +char * +nautilus_query_get_text (NautilusQuery *query) +{ + return g_strdup (query->details->text); +} + +void +nautilus_query_set_text (NautilusQuery *query, const char *text) +{ + g_free (query->details->text); + query->details->text = g_strdup (text); +} + +char * +nautilus_query_get_location (NautilusQuery *query) +{ + return g_strdup (query->details->location_uri); +} + +void +nautilus_query_set_location (NautilusQuery *query, const char *uri) +{ + g_free (query->details->location_uri); + query->details->location_uri = g_strdup (uri); +} + +GList * +nautilus_query_get_mime_types (NautilusQuery *query) +{ + return eel_g_str_list_copy (query->details->mime_types); +} + +void +nautilus_query_set_mime_types (NautilusQuery *query, GList *mime_types) +{ + eel_g_list_free_deep (query->details->mime_types); + query->details->mime_types = eel_g_str_list_copy (mime_types); +} + +void +nautilus_query_add_mime_type (NautilusQuery *query, const char *mime_type) +{ + query->details->mime_types = g_list_append (query->details->mime_types, + g_strdup (mime_type)); +} + +char * +nautilus_query_to_readable_string (NautilusQuery *query) +{ + if (!query || !query->details->text) { + return g_strdup ("Search"); + } + + return g_strdup_printf ("Search for \"%s\"", query->details->text); +} + +static char * +encode_home_uri (const char *uri) +{ + char *home_uri; + const char *encoded_uri; + + home_uri = gnome_vfs_get_uri_from_local_path (g_get_home_dir ()); + + if (g_str_has_prefix (uri, home_uri)) { + encoded_uri = uri + strlen (home_uri); + if (*encoded_uri == '/') { + encoded_uri++; + } + } else { + encoded_uri = uri; + } + + g_free (home_uri); + + return g_markup_escape_text (encoded_uri, -1); +} + +static char * +decode_home_uri (const char *uri) +{ + char *home_uri; + char *decoded_uri; + + if (g_str_has_prefix (uri, "file:")) { + decoded_uri = g_strdup (uri); + } else { + home_uri = gnome_vfs_get_uri_from_local_path (g_get_home_dir ()); + + decoded_uri = g_strconcat (home_uri, "/", uri, NULL); + + g_free (home_uri); + } + + return decoded_uri; +} + + +typedef struct { + NautilusQuery *query; + gboolean in_text; + gboolean in_location; + gboolean in_mimetypes; + gboolean in_mimetype; +} ParserInfo; + +static void +start_element_cb (GMarkupParseContext *ctx, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + gpointer user_data, + GError **err) +{ + ParserInfo *info; + + info = (ParserInfo *) user_data; + + if (strcmp (element_name, "text") == 0) + info->in_text = TRUE; + else if (strcmp (element_name, "location") == 0) + info->in_location = TRUE; + else if (strcmp (element_name, "mimetypes") == 0) + info->in_mimetypes = TRUE; + else if (strcmp (element_name, "mimetype") == 0) + info->in_mimetype = TRUE; +} + +static void +end_element_cb (GMarkupParseContext *ctx, + const char *element_name, + gpointer user_data, + GError **err) +{ + ParserInfo *info; + + info = (ParserInfo *) user_data; + + if (strcmp (element_name, "text") == 0) + info->in_text = FALSE; + else if (strcmp (element_name, "location") == 0) + info->in_location = FALSE; + else if (strcmp (element_name, "mimetypes") == 0) + info->in_mimetypes = FALSE; + else if (strcmp (element_name, "mimetype") == 0) + info->in_mimetype = FALSE; +} + +static void +text_cb (GMarkupParseContext *ctx, + const char *text, + gsize text_len, + gpointer user_data, + GError **err) +{ + ParserInfo *info; + char *t, *uri; + + info = (ParserInfo *) user_data; + + t = g_strndup (text, text_len); + + if (info->in_text) { + nautilus_query_set_text (info->query, t); + } else if (info->in_location) { + uri = decode_home_uri (t); + nautilus_query_set_location (info->query, uri); + g_free (uri); + } else if (info->in_mimetypes && info->in_mimetype) { + nautilus_query_add_mime_type (info->query, t); + } + + g_free (t); + +} + +static void +error_cb (GMarkupParseContext *ctx, + GError *err, + gpointer user_data) +{ +} + +static GMarkupParser parser = { + start_element_cb, + end_element_cb, + text_cb, + NULL, + error_cb +}; + + +static NautilusQuery * +nautilus_query_parse_xml (char *xml, gsize xml_len) +{ + ParserInfo info = { NULL }; + GMarkupParseContext *ctx; + + if (xml_len == -1) { + xml_len = strlen (xml); + } + + info.query = nautilus_query_new (); + info.in_text = FALSE; + + ctx = g_markup_parse_context_new (&parser, 0, &info, NULL); + g_markup_parse_context_parse (ctx, xml, xml_len, NULL); + + return info.query; +} + + +NautilusQuery * +nautilus_query_load (char *file) +{ + NautilusQuery *query; + char *xml; + gsize xml_len; + + if (!g_file_test (file, G_FILE_TEST_EXISTS)) { + return NULL; + } + + + g_file_get_contents (file, &xml, &xml_len, NULL); + query = nautilus_query_parse_xml (xml, xml_len); + g_free (xml); + + return query; +} + +static char * +nautilus_query_to_xml (NautilusQuery *query) +{ + GString *xml; + char *text; + char *uri; + char *mimetype; + GList *l; + + xml = g_string_new (""); + g_string_append (xml, + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<query version=\"1.0\">\n"); + + text = g_markup_escape_text (query->details->text, -1); + g_string_append_printf (xml, " <text>%s</text>\n", text); + g_free (text); + + if (query->details->location_uri) { + uri = encode_home_uri (query->details->location_uri); + g_string_append_printf (xml, " <location>%s</location>\n", uri); + g_free (uri); + } + + if (query->details->mime_types) { + g_string_append (xml, " <mimetypes>\n"); + for (l = query->details->mime_types; l != NULL; l = l->next) { + mimetype = g_markup_escape_text (l->data, -1); + g_string_append_printf (xml, " <mimetype>%s</mimetype>\n", mimetype); + g_free (mimetype); + } + g_string_append (xml, " </mimetypes>\n"); + } + + g_string_append (xml, "</query>\n"); + + return g_string_free (xml, FALSE); +} + +gboolean +nautilus_query_save (NautilusQuery *query, char *file) +{ + char *xml; + GError *err = NULL; + gboolean res; + + + res = TRUE; + xml = nautilus_query_to_xml (query); + g_file_set_contents (file, xml, strlen (xml), &err); + g_free (xml); + + if (err != NULL) { + res = FALSE; + g_error_free (err); + } + return res; +} diff --git a/libnautilus-private/nautilus-query.h b/libnautilus-private/nautilus-query.h new file mode 100644 index 000000000..fae278042 --- /dev/null +++ b/libnautilus-private/nautilus-query.h @@ -0,0 +1,67 @@ +/* -*- 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_QUERY_H +#define NAUTILUS_QUERY_H + +#include <glib-object.h> +#include <libgnomevfs/gnome-vfs-result.h> + +#define NAUTILUS_TYPE_QUERY (nautilus_query_get_type ()) +#define NAUTILUS_QUERY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_QUERY, NautilusQuery)) +#define NAUTILUS_QUERY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_QUERY, NautilusQueryClass)) +#define NAUTILUS_IS_QUERY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_QUERY)) +#define NAUTILUS_IS_QUERY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_QUERY)) +#define NAUTILUS_QUERY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_QUERY, NautilusQueryClass)) + +typedef struct NautilusQueryDetails NautilusQueryDetails; + +typedef struct NautilusQuery { + GObject parent; + NautilusQueryDetails *details; +} NautilusQuery; + +typedef struct { + GObjectClass parent_class; +} NautilusQueryClass; + +GType nautilus_query_get_type (void); +gboolean nautilus_query_enabled (void); + +NautilusQuery* nautilus_query_new (void); + +char * nautilus_query_get_text (NautilusQuery *query); +void nautilus_query_set_text (NautilusQuery *query, const char *text); + +char * nautilus_query_get_location (NautilusQuery *query); +void nautilus_query_set_location (NautilusQuery *query, const char *uri); + +GList * nautilus_query_get_mime_types (NautilusQuery *query); +void nautilus_query_set_mime_types (NautilusQuery *query, GList *mime_types); +void nautilus_query_add_mime_type (NautilusQuery *query, const char *mime_type); + +char * nautilus_query_to_readable_string (NautilusQuery *query); +NautilusQuery *nautilus_query_load (char *file); +gboolean nautilus_query_save (NautilusQuery *query, char *file); + +#endif /* NAUTILUS_QUERY_H */ diff --git a/libnautilus-private/nautilus-saved-search-file.c b/libnautilus-private/nautilus-saved-search-file.c new file mode 100644 index 000000000..35ce6d6a3 --- /dev/null +++ b/libnautilus-private/nautilus-saved-search-file.c @@ -0,0 +1,62 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-saved-search-file.h: Subclass of NautilusVFSFile to implement the + the case of a Saved Search file. + + Copyright (C) 2005 Red Hat, Inc + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Alexander Larsson +*/ +#include <config.h> +#include "nautilus-saved-search-file.h" +#include <eel/eel-gtk-macros.h> + +static void nautilus_saved_search_file_init (gpointer object, gpointer klass); +static void nautilus_saved_search_file_class_init (gpointer klass); + +EEL_CLASS_BOILERPLATE (NautilusSavedSearchFile, + nautilus_saved_search_file, + NAUTILUS_TYPE_VFS_FILE) + + + +static void +nautilus_saved_search_file_init (gpointer object, gpointer klass) +{ + NautilusVFSFile *file; + + file = NAUTILUS_VFS_FILE (object); + +} + +static GnomeVFSFileType +saved_search_get_file_type (NautilusFile *file) +{ + return GNOME_VFS_FILE_TYPE_DIRECTORY; +} + +static void +nautilus_saved_search_file_class_init (gpointer klass) +{ + NautilusFileClass *file_class; + + file_class = NAUTILUS_FILE_CLASS (klass); + + file_class->get_file_type = saved_search_get_file_type; +} + diff --git a/libnautilus-private/nautilus-saved-search-file.h b/libnautilus-private/nautilus-saved-search-file.h new file mode 100644 index 000000000..16cf35943 --- /dev/null +++ b/libnautilus-private/nautilus-saved-search-file.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-saved-search-file.h: Subclass of NautilusVFSFile to implement the + the case of a Saved Search file. + + Copyright (C) 2005 Red Hat, Inc + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Alexander Larsson +*/ + +#ifndef NAUTILUS_SAVED_SEARCH_FILE_H +#define NAUTILUS_SAVED_SEARCH_FILE_H + +#include <libnautilus-private/nautilus-vfs-file.h> + +#define NAUTILUS_TYPE_SAVED_SEARCH_FILE \ + (nautilus_saved_search_file_get_type ()) +#define NAUTILUS_SAVED_SEARCH_FILE(obj) \ + (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_SAVED_SEARCH_FILE, NautilusSavedSearchFile)) +#define NAUTILUS_SAVED_SEARCH_FILE_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SAVED_SEARCH_FILE, NautilusSavedSearchFileClass)) +#define NAUTILUS_IS_SAVED_SEARCH_FILE(obj) \ + (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_SAVED_SEARCH_FILE)) +#define NAUTILUS_IS_SAVED_SEARCH_FILE_CLASS(klass) \ + (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SAVED_SEARCH_FILE)) + +typedef struct NautilusSavedSearchFileDetails NautilusSavedSearchFileDetails; + +typedef struct { + NautilusFile parent_slot; +} NautilusSavedSearchFile; + +typedef struct { + NautilusFileClass parent_slot; +} NautilusSavedSearchFileClass; + +GType nautilus_saved_search_file_get_type (void); + +#endif /* NAUTILUS_SAVED_SEARCH_FILE_H */ diff --git a/libnautilus-private/nautilus-search-directory-file.c b/libnautilus-private/nautilus-search-directory-file.c new file mode 100644 index 000000000..86c077a94 --- /dev/null +++ b/libnautilus-private/nautilus-search-directory-file.c @@ -0,0 +1,239 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-search-directory-file.c: Subclass of NautilusFile to help implement the + searches + + Copyright (C) 2005 Novell, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Anders Carlsson <andersca@imendio.com> +*/ + +#include <config.h> +#include "nautilus-search-directory-file.h" + +#include "nautilus-directory-notify.h" +#include "nautilus-directory-private.h" +#include "nautilus-file-attributes.h" +#include "nautilus-file-private.h" +#include "nautilus-file-utilities.h" +#include <eel/eel-glib-extensions.h> +#include <eel/eel-gtk-macros.h> +#include "nautilus-search-directory.h" +#include <gtk/gtksignal.h> +#include <libgnome/gnome-i18n.h> +#include <string.h> + +static void nautilus_search_directory_file_init (gpointer object, + gpointer klass); +static void nautilus_search_directory_file_class_init (gpointer klass); + +EEL_CLASS_BOILERPLATE (NautilusSearchDirectoryFile, + nautilus_search_directory_file, + NAUTILUS_TYPE_FILE); + +struct NautilusSearchDirectoryFileDetails { + NautilusSearchDirectory *search_directory; +}; + +static void +search_directory_file_monitor_add (NautilusFile *file, + gconstpointer client, + NautilusFileAttributes attributes) +{ + /* No need for monitoring, we always emit changed when files + are added/removed, and no other metadata changes */ +} + +static void +search_directory_file_monitor_remove (NautilusFile *file, + gconstpointer client) +{ + /* Do nothing here, we don't have any monitors */ +} + +static void +search_directory_file_call_when_ready (NautilusFile *file, + NautilusFileAttributes file_attributes, + NautilusFileCallback callback, + gpointer callback_data) + +{ + /* All data for directory-as-file is always uptodate */ + (* callback) (file, callback_data); +} + +static void +search_directory_file_cancel_call_when_ready (NautilusFile *file, + NautilusFileCallback callback, + gpointer callback_data) +{ + /* Do nothing here, we don't have any pending calls */ +} + + +static gboolean +search_directory_file_check_if_ready (NautilusFile *file, + NautilusFileAttributes attributes) +{ + return TRUE; +} + +static GnomeVFSFileType +search_directory_file_get_file_type (NautilusFile *file) +{ + return GNOME_VFS_FILE_TYPE_DIRECTORY; +} + +static gboolean +search_directory_file_get_item_count (NautilusFile *file, + guint *count, + gboolean *count_unreadable) +{ + NautilusSearchDirectory *search_dir; + GList *file_list; + + if (count) { + search_dir = NAUTILUS_SEARCH_DIRECTORY (file->details->directory); + + file_list = nautilus_directory_get_file_list (file->details->directory); + + *count = g_list_length (file_list); + + nautilus_file_list_free (file_list); + } + + return TRUE; +} + +static NautilusRequestStatus +search_directory_file_get_deep_counts (NautilusFile *file, + guint *directory_count, + guint *file_count, + guint *unreadable_directory_count, + GnomeVFSFileSize *total_size) +{ + NautilusSearchDirectory *search_dir; + NautilusFile *dir_file; + GList *file_list, *l; + guint dirs, files; + GnomeVFSFileType type; + + search_dir = NAUTILUS_SEARCH_DIRECTORY (file->details->directory); + + file_list = nautilus_directory_get_file_list (file->details->directory); + + dirs = files = 0; + for (l = file_list; l != NULL; l = l->next) { + dir_file = NAUTILUS_FILE (l->data); + type = nautilus_file_get_file_type (dir_file); + if (type == GNOME_VFS_FILE_TYPE_DIRECTORY) { + dirs++; + } else { + files++; + } + } + + if (directory_count != NULL) { + *directory_count = dirs; + } + if (file_count != NULL) { + *file_count = files; + } + if (unreadable_directory_count != NULL) { + *unreadable_directory_count = 0; + } + if (total_size != NULL) { + /* FIXME: Maybe we want to calculate this? */ + *total_size = 0; + } + + nautilus_file_list_free (file_list); + + return NAUTILUS_REQUEST_DONE; +} + +static char * +search_directory_file_get_where_string (NautilusFile *file) +{ + return g_strdup (_("Search")); +} + +static void +nautilus_search_directory_file_init (gpointer object, gpointer klass) +{ + NautilusSearchDirectoryFile *search_file; + NautilusFile *file; + GnomeVFSFileInfo *file_info; + + search_file = NAUTILUS_SEARCH_DIRECTORY_FILE (object); + file = NAUTILUS_FILE(object); + + file_info = file->details->info = gnome_vfs_file_info_new (); + + file_info->name = g_strdup (_("Search")); + file_info->mime_type = g_strdup ("x-directory/normal"); + file_info->type = GNOME_VFS_FILE_TYPE_DIRECTORY; + file_info->flags = GNOME_VFS_FILE_FLAGS_NONE; + file_info->link_count = 1; + file_info->size = 0; + file_info->permissions = + GNOME_VFS_PERM_OTHER_WRITE | + GNOME_VFS_PERM_GROUP_WRITE | + GNOME_VFS_PERM_USER_READ | + GNOME_VFS_PERM_OTHER_READ | + GNOME_VFS_PERM_GROUP_READ; + + file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE | + GNOME_VFS_FILE_INFO_FIELDS_FLAGS | + GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE | + GNOME_VFS_FILE_INFO_FIELDS_SIZE | + GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS | + GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT; + + file->details->file_info_is_up_to_date = TRUE; + + file->details->display_name = g_strdup (_("Search")); + file->details->custom_icon = NULL; + file->details->activation_uri = NULL; + file->details->got_link_info = TRUE; + file->details->link_info_is_up_to_date = TRUE; + + file->details->directory_count = 0; + file->details->got_directory_count = TRUE; + file->details->directory_count_is_up_to_date = TRUE; +} + +static void +nautilus_search_directory_file_class_init (gpointer klass) +{ + GObjectClass *object_class; + NautilusFileClass *file_class; + + object_class = G_OBJECT_CLASS (klass); + file_class = NAUTILUS_FILE_CLASS (klass); + + file_class->monitor_add = search_directory_file_monitor_add; + file_class->monitor_remove = search_directory_file_monitor_remove; + file_class->call_when_ready = search_directory_file_call_when_ready; + file_class->cancel_call_when_ready = search_directory_file_cancel_call_when_ready; + file_class->get_file_type = search_directory_file_get_file_type; + file_class->check_if_ready = search_directory_file_check_if_ready; + file_class->get_item_count = search_directory_file_get_item_count; + file_class->get_deep_counts = search_directory_file_get_deep_counts; + file_class->get_where_string = search_directory_file_get_where_string; +} diff --git a/libnautilus-private/nautilus-search-directory-file.h b/libnautilus-private/nautilus-search-directory-file.h new file mode 100644 index 000000000..57432f887 --- /dev/null +++ b/libnautilus-private/nautilus-search-directory-file.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-search-directory-file.h: Subclass of NautilusFile to implement the + the case of the search directory + + Copyright (C) 2003 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Alexander Larsson <alexl@redhat.com> +*/ + +#ifndef NAUTILUS_SEARCH_DIRECTORY_FILE_H +#define NAUTILUS_SEARCH_DIRECTORY_FILE_H + +#include <libnautilus-private/nautilus-file.h> + +#define NAUTILUS_TYPE_SEARCH_DIRECTORY_FILE \ + (nautilus_search_directory_file_get_type ()) +#define NAUTILUS_SEARCH_DIRECTORY_FILE(obj) \ + (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_SEARCH_DIRECTORY_FILE, NautilusSearchDirectoryFile)) +#define NAUTILUS_SEARCH_DIRECTORY_FILE_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SEARCH_DIRECTORY_FILE, NautilusSearchDirectoryFileClass)) +#define NAUTILUS_IS_SEARCH_DIRECTORY_FILE(obj) \ + (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_SEARCH_DIRECTORY_FILE)) +#define NAUTILUS_IS_SEARCH_DIRECTORY_FILE_CLASS(klass) \ + (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SEARCH_DIRECTORY_FILE)) + +typedef struct NautilusSearchDirectoryFileDetails NautilusSearchDirectoryFileDetails; + +typedef struct { + NautilusFile parent_slot; + NautilusSearchDirectoryFileDetails *details; +} NautilusSearchDirectoryFile; + +typedef struct { + NautilusFileClass parent_slot; +} NautilusSearchDirectoryFileClass; + +GType nautilus_search_directory_file_get_type (void); + +#endif /* NAUTILUS_SEARCH_DIRECTORY_FILE_H */ diff --git a/libnautilus-private/nautilus-search-directory.c b/libnautilus-private/nautilus-search-directory.c new file mode 100644 index 000000000..2172bc245 --- /dev/null +++ b/libnautilus-private/nautilus-search-directory.c @@ -0,0 +1,864 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + Copyright (C) 2005 Novell, Inc + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Anders Carlsson <andersca@imendio.com> +*/ + +#include <config.h> +#include "nautilus-search-directory.h" + +#include "nautilus-directory-private.h" +#include "nautilus-file.h" +#include "nautilus-file-private.h" +#include "nautilus-file-utilities.h" +#include "nautilus-search-engine.h" +#include <eel/eel-glib-extensions.h> +#include <libgnomevfs/gnome-vfs-utils.h> +#include <gtk/gtksignal.h> +#include <libgnome/gnome-macros.h> +#include <string.h> +#include <sys/time.h> + +struct NautilusSearchDirectoryDetails { + NautilusQuery *query; + char *saved_search_uri; + gboolean modified; + + NautilusSearchEngine *engine; + + gboolean search_running; + gboolean search_finished; + + GList *files; + GHashTable *file_hash; + + GList *monitor_list; + GList *callback_list; + GList *pending_callback_list; +}; + +typedef struct { + gboolean monitor_hidden_files; + gboolean monitor_backup_files; + NautilusFileAttributes monitor_attributes; + + gconstpointer client; +} SearchMonitor; + +typedef struct { + NautilusSearchDirectory *search_directory; + + NautilusDirectoryCallback callback; + gpointer callback_data; + + NautilusFileAttributes wait_for_attributes; + gboolean wait_for_file_list; + GList *file_list; + GHashTable *non_ready_hash; +} SearchCallback; + +GNOME_CLASS_BOILERPLATE (NautilusSearchDirectory, nautilus_search_directory, + NautilusDirectory, NAUTILUS_TYPE_DIRECTORY) + +static void search_engine_hits_added (NautilusSearchEngine *engine, GList *hits, NautilusSearchDirectory *search); +static void search_engine_hits_subtracted (NautilusSearchEngine *engine, GList *hits, NautilusSearchDirectory *search); +static void search_engine_finished (NautilusSearchEngine *engine, NautilusSearchDirectory *search); +static void search_engine_error (NautilusSearchEngine *engine, const char *error, NautilusSearchDirectory *search); +static void search_callback_file_ready_callback (NautilusFile *file, gpointer data); +static void file_changed (NautilusFile *file, NautilusSearchDirectory *search); + +static void +ensure_search_engine (NautilusSearchDirectory *search) +{ + if (!search->details->engine) { + search->details->engine = nautilus_search_engine_new (); + g_signal_connect (search->details->engine, "hits-added", + G_CALLBACK (search_engine_hits_added), + search); + g_signal_connect (search->details->engine, "hits-subtracted", + G_CALLBACK (search_engine_hits_subtracted), + search); + g_signal_connect (search->details->engine, "finished", + G_CALLBACK (search_engine_finished), + search); + g_signal_connect (search->details->engine, "error", + G_CALLBACK (search_engine_error), + search); + } +} + +static void +reset_file_list (NautilusSearchDirectory *search) +{ + GList *list, *monitor_list; + NautilusFile *file; + SearchMonitor *monitor; + + /* Remove file connections */ + for (list = search->details->files; list != NULL; list = list->next) { + file = list->data; + + /* Disconnect change handler */ + g_signal_handlers_disconnect_by_func (file, file_changed, search); + + /* Remove monitors */ + for (monitor_list = search->details->monitor_list; monitor_list; + monitor_list = monitor_list->next) { + monitor = monitor_list->data; + nautilus_file_monitor_remove (file, monitor); + } + } + + nautilus_file_list_free (search->details->files); + search->details->files = NULL; +} + +static void +start_or_stop_search_engine (NautilusSearchDirectory *search, gboolean adding) +{ + if (adding && (search->details->monitor_list || + search->details->pending_callback_list) && + search->details->query && + !search->details->search_running) { + /* We need to start the search engine */ + search->details->search_running = TRUE; + search->details->search_finished = FALSE; + ensure_search_engine (search); + nautilus_search_engine_set_query (search->details->engine, search->details->query); + + reset_file_list (search); + + nautilus_search_engine_start (search->details->engine); + } else if (!adding && !search->details->monitor_list && + !search->details->pending_callback_list && + search->details->engine && + search->details->search_running) { + search->details->search_running = FALSE; + nautilus_search_engine_stop (search->details->engine); + + reset_file_list (search); + } + +} + +static void +file_changed (NautilusFile *file, NautilusSearchDirectory *search) +{ + GList list; + + list.data = file; + list.next = NULL; + + nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (search), &list); +} + +static void +search_monitor_add (NautilusDirectory *directory, + gconstpointer client, + gboolean monitor_hidden_files, + gboolean monitor_backup_files, + NautilusFileAttributes file_attributes, + NautilusDirectoryCallback callback, + gpointer callback_data) +{ + GList *list; + SearchMonitor *monitor; + NautilusSearchDirectory *search; + NautilusFile *file; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + + monitor = g_new0 (SearchMonitor, 1); + monitor->monitor_hidden_files = monitor_hidden_files; + monitor->monitor_backup_files = monitor_backup_files; + monitor->monitor_attributes = file_attributes; + monitor->client = client; + + search->details->monitor_list = g_list_prepend (search->details->monitor_list, monitor); + + if (callback != NULL) { + (* callback) (directory, search->details->files, callback_data); + } + + for (list = search->details->files; list != NULL; list = list->next) { + file = list->data; + + /* Add monitors */ + nautilus_file_monitor_add (file, monitor, file_attributes); + } + + start_or_stop_search_engine (search, TRUE); +} + +static void +search_monitor_remove_file_monitors (SearchMonitor *monitor, NautilusSearchDirectory *search) +{ + GList *list; + NautilusFile *file; + + for (list = search->details->files; list != NULL; list = list->next) { + file = list->data; + + nautilus_file_monitor_remove (file, monitor); + } +} + +static void +search_monitor_destroy (SearchMonitor *monitor, NautilusSearchDirectory *search) +{ + search_monitor_remove_file_monitors (monitor, search); + + g_free (monitor); +} + +static void +search_monitor_remove (NautilusDirectory *directory, + gconstpointer client) +{ + NautilusSearchDirectory *search; + SearchMonitor *monitor; + GList *list; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + + for (list = search->details->monitor_list; list != NULL; list = list->next) { + monitor = list->data; + + if (monitor->client == client) { + search->details->monitor_list = g_list_delete_link (search->details->monitor_list, list); + + search_monitor_destroy (monitor, search); + + break; + } + } + + start_or_stop_search_engine (search, FALSE); +} + +static void +cancel_call_when_ready (gpointer key, gpointer value, gpointer user_data) +{ + SearchCallback *search_callback; + NautilusFile *file; + + file = key; + search_callback = user_data; + + nautilus_file_cancel_call_when_ready (file, search_callback_file_ready_callback, + search_callback); +} + +static void +search_callback_destroy (SearchCallback *search_callback) +{ + if (search_callback->non_ready_hash) { + g_hash_table_foreach (search_callback->non_ready_hash, cancel_call_when_ready, search_callback); + g_hash_table_destroy (search_callback->non_ready_hash); + } + + nautilus_file_list_free (search_callback->file_list); + + g_free (search_callback); +} + +static void +search_callback_invoke_and_destroy (SearchCallback *search_callback) +{ + search_callback->callback (NAUTILUS_DIRECTORY (search_callback->search_directory), + search_callback->file_list, + search_callback->callback_data); + + search_callback->search_directory->details->callback_list = + g_list_remove (search_callback->search_directory->details->callback_list, search_callback); + + search_callback_destroy (search_callback); +} + +static void +search_callback_file_ready_callback (NautilusFile *file, gpointer data) +{ + SearchCallback *search_callback = data; + + g_hash_table_remove (search_callback->non_ready_hash, file); + + if (g_hash_table_size (search_callback->non_ready_hash) == 0) { + search_callback_invoke_and_destroy (search_callback); + } +} + +static void +search_callback_add_file_callbacks (SearchCallback *callback) +{ + GList *file_list_copy, *list; + NautilusFile *file; + + file_list_copy = g_list_copy (callback->file_list); + + for (list = file_list_copy; list != NULL; list = list->next) { + file = list->data; + + nautilus_file_call_when_ready (file, + callback->wait_for_attributes, + search_callback_file_ready_callback, + callback); + } + g_list_free (file_list_copy); +} + +static SearchCallback * +search_callback_find (NautilusSearchDirectory *search, NautilusDirectoryCallback callback, gpointer callback_data) +{ + SearchCallback *search_callback; + GList *list; + + for (list = search->details->callback_list; list != NULL; list = list->next) { + search_callback = list->data; + + if (search_callback->callback == callback && + search_callback->callback_data == callback_data) { + return search_callback; + } + } + + return NULL; +} + +static SearchCallback * +search_callback_find_pending (NautilusSearchDirectory *search, NautilusDirectoryCallback callback, gpointer callback_data) +{ + SearchCallback *search_callback; + GList *list; + + for (list = search->details->pending_callback_list; list != NULL; list = list->next) { + search_callback = list->data; + + if (search_callback->callback == callback && + search_callback->callback_data == callback_data) { + return search_callback; + } + } + + return NULL; +} + +static GHashTable * +file_list_to_hash_table (GList *file_list) +{ + GList *list; + GHashTable *table; + + if (!file_list) + return NULL; + + table = g_hash_table_new (NULL, NULL); + + for (list = file_list; list != NULL; list = list->next) { + g_hash_table_insert (table, list->data, list->data); + } + + return table; +} + +static void +search_call_when_ready (NautilusDirectory *directory, + NautilusFileAttributes file_attributes, + gboolean wait_for_file_list, + NautilusDirectoryCallback callback, + gpointer callback_data) +{ + NautilusSearchDirectory *search; + SearchCallback *search_callback; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + + search_callback = search_callback_find (search, callback, callback_data); + if (search_callback == NULL) { + search_callback = search_callback_find_pending (search, callback, callback_data); + } + + if (search_callback) { + g_warning ("tried to add a new callback while an old one was pending"); + return; + } + + search_callback = g_new0 (SearchCallback, 1); + search_callback->search_directory = search; + search_callback->callback = callback; + search_callback->callback_data = callback_data; + search_callback->wait_for_attributes = file_attributes; + search_callback->wait_for_file_list = wait_for_file_list; + + if (wait_for_file_list && !search->details->search_finished) { + /* Add it to the pending callback list, which will be + * processed when the directory has finished loading + */ + search->details->pending_callback_list = + g_list_prepend (search->details->pending_callback_list, search_callback); + + /* We might need to start the search engine */ + start_or_stop_search_engine (search, TRUE); + } else { + search_callback->file_list = nautilus_file_list_copy (search->details->files); + search_callback->non_ready_hash = file_list_to_hash_table (search->details->files); + + if (!search_callback->non_ready_hash) { + /* If there are no ready files, we invoke the callback + with an empty list. + */ + search_callback_invoke_and_destroy (search_callback); + } else { + search->details->callback_list = g_list_prepend (search->details->callback_list, search_callback); + search_callback_add_file_callbacks (search_callback); + } + } +} + +static void +search_cancel_callback (NautilusDirectory *directory, + NautilusDirectoryCallback callback, + gpointer callback_data) +{ + NautilusSearchDirectory *search; + SearchCallback *search_callback; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + search_callback = search_callback_find (search, callback, callback_data); + + if (search_callback) { + search->details->callback_list = g_list_remove (search->details->callback_list, search_callback); + + search_callback_destroy (search_callback); + + return; + } + + /* Check for a pending callback */ + search_callback = search_callback_find_pending (search, callback, callback_data); + + if (search_callback) { + search->details->pending_callback_list = g_list_remove (search->details->pending_callback_list, search_callback); + + search_callback_destroy (search_callback); + + /* We might need to stop the search engine now */ + start_or_stop_search_engine (search, FALSE); + } +} + + +static void +search_engine_hits_added (NautilusSearchEngine *engine, GList *hits, + NautilusSearchDirectory *search) +{ + GList *hit_list; + GList *file_list; + NautilusFile *file; + char *uri; + SearchMonitor *monitor; + GList *monitor_list; + + file_list = NULL; + + for (hit_list = hits; hit_list != NULL; hit_list = hit_list->next) { + uri = hit_list->data; + + if (g_str_has_suffix (uri, NAUTILUS_SAVED_SEARCH_EXTENSION)) { + /* Never return saved searches themselves as hits */ + continue; + } + + file = nautilus_file_get (uri); + + for (monitor_list = search->details->monitor_list; monitor_list; monitor_list = monitor_list->next) { + monitor = monitor_list->data; + + /* Add monitors */ + nautilus_file_monitor_add (file, monitor, monitor->monitor_attributes); + } + + g_signal_connect (file, "changed", G_CALLBACK (file_changed), search), + + file_list = g_list_prepend (file_list, file); + } + + search->details->files = g_list_concat (search->details->files, file_list); + + nautilus_directory_emit_files_added (NAUTILUS_DIRECTORY (search), file_list); + + file = nautilus_directory_get_corresponding_file (NAUTILUS_DIRECTORY (search)); + nautilus_file_emit_changed (file); + nautilus_file_unref (file); +} + +static void +search_engine_hits_subtracted (NautilusSearchEngine *engine, GList *hits, + NautilusSearchDirectory *search) +{ + GList *hit_list; + GList *monitor_list; + SearchMonitor *monitor; + GList *file_list; + char *uri; + NautilusFile *file; + + file_list = NULL; + + for (hit_list = hits; hit_list != NULL; hit_list = hit_list->next) { + uri = hit_list->data; + file = nautilus_file_get (uri); + + for (monitor_list = search->details->monitor_list; monitor_list; + monitor_list = monitor_list->next) { + monitor = monitor_list->data; + /* Remove monitors */ + nautilus_file_monitor_remove (file, monitor); + } + + g_signal_handlers_disconnect_by_func (file, file_changed, search); + + search->details->files = g_list_remove (search->details->files, file); + + file_list = g_list_prepend (file_list, file); + } + + nautilus_directory_emit_files_changed (NAUTILUS_DIRECTORY (search), file_list); + + nautilus_file_list_free (file_list); + + file = nautilus_directory_get_corresponding_file (NAUTILUS_DIRECTORY (search)); + nautilus_file_emit_changed (file); + nautilus_file_unref (file); +} + +static void +search_callback_add_pending_file_callbacks (SearchCallback *callback) +{ + callback->file_list = nautilus_file_list_copy (callback->search_directory->details->files); + callback->non_ready_hash = file_list_to_hash_table (callback->search_directory->details->files); + + search_callback_add_file_callbacks (callback); +} + +static void +search_engine_error (NautilusSearchEngine *engine, const char *error_message, NautilusSearchDirectory *search) +{ + nautilus_directory_emit_load_error (NAUTILUS_DIRECTORY (search), + -1, error_message); +} + +static void +search_engine_finished (NautilusSearchEngine *engine, NautilusSearchDirectory *search) +{ + search->details->search_finished = TRUE; + + nautilus_directory_emit_done_loading (NAUTILUS_DIRECTORY (search)); + + /* Add all file callbacks */ + g_list_foreach (search->details->pending_callback_list, + (GFunc)search_callback_add_pending_file_callbacks, NULL); + search->details->callback_list = g_list_concat (search->details->callback_list, + search->details->pending_callback_list); + + g_list_free (search->details->pending_callback_list); + search->details->pending_callback_list = NULL; +} + +static void +search_force_reload (NautilusDirectory *directory) +{ + NautilusSearchDirectory *search; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + + if (!search->details->query) { + return; + } + + search->details->search_finished = FALSE; + + if (!search->details->engine) { + return; + } + + /* Remove file monitors */ + reset_file_list (search); + + if (search->details->search_running) { + nautilus_search_engine_stop (search->details->engine); + nautilus_search_engine_set_query (search->details->engine, search->details->query); + nautilus_search_engine_start (search->details->engine); + } +} + +static gboolean +search_are_all_files_seen (NautilusDirectory *directory) +{ + NautilusSearchDirectory *search; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + + return (!search->details->query || + search->details->search_finished); +} + +static gboolean +search_contains_file (NautilusDirectory *directory, + NautilusFile *file) +{ + NautilusSearchDirectory *search; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + + /* FIXME: Maybe put the files in a hash */ + return (g_list_find (search->details->files, file) != NULL); +} + +static GList * +search_get_file_list (NautilusDirectory *directory) +{ + NautilusSearchDirectory *search; + + search = NAUTILUS_SEARCH_DIRECTORY (directory); + + return nautilus_file_list_copy (search->details->files); +} + + +static gboolean +search_is_editable (NautilusDirectory *directory) +{ + return FALSE; +} + +static void +search_dispose (GObject *object) +{ + NautilusSearchDirectory *search; + GList *list; + + search = NAUTILUS_SEARCH_DIRECTORY (object); + + /* Remove search monitors */ + if (search->details->monitor_list) { + for (list = search->details->monitor_list; list != NULL; list = list->next) { + search_monitor_destroy ((SearchMonitor *)list->data, search); + } + + g_list_free (search->details->monitor_list); + search->details->monitor_list = NULL; + } + + reset_file_list (search); + + if (search->details->callback_list) { + /* Remove callbacks */ + g_list_foreach (search->details->callback_list, + (GFunc)search_callback_destroy, NULL); + g_list_free (search->details->callback_list); + search->details->callback_list = NULL; + } + + if (search->details->pending_callback_list) { + g_list_foreach (search->details->pending_callback_list, + (GFunc)search_callback_destroy, NULL); + g_list_free (search->details->pending_callback_list); + search->details->pending_callback_list = NULL; + } + + if (search->details->query) { + g_object_unref (search->details->query); + search->details->query = NULL; + } + + if (search->details->engine) { + if (search->details->search_running) { + nautilus_search_engine_stop (search->details->engine); + } + + g_object_unref (search->details->engine); + search->details->engine = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +search_finalize (GObject *object) +{ + NautilusSearchDirectory *search; + + search = NAUTILUS_SEARCH_DIRECTORY (object); + + g_free (search->details->saved_search_uri); + + g_free (search->details); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +nautilus_search_directory_instance_init (NautilusSearchDirectory *search) +{ + search->details = g_new0 (NautilusSearchDirectoryDetails, 1); +} + +static void +nautilus_search_directory_class_init (NautilusSearchDirectoryClass *class) +{ + NautilusDirectoryClass *directory_class; + + G_OBJECT_CLASS (class)->dispose = search_dispose; + G_OBJECT_CLASS (class)->finalize = search_finalize; + + directory_class = NAUTILUS_DIRECTORY_CLASS (class); + + directory_class->are_all_files_seen = search_are_all_files_seen; + directory_class->contains_file = search_contains_file; + directory_class->force_reload = search_force_reload; + directory_class->call_when_ready = search_call_when_ready; + directory_class->cancel_callback = search_cancel_callback; + + directory_class->file_monitor_add = search_monitor_add; + directory_class->file_monitor_remove = search_monitor_remove; + + directory_class->get_file_list = search_get_file_list; + directory_class->is_editable = search_is_editable; +} + +char * +nautilus_search_directory_generate_new_uri (void) +{ + static int counter = 0; + struct timeval tv; + char *uri; + + gettimeofday (&tv, NULL); + + uri = g_strdup_printf (EEL_SEARCH_URI"///%ld-%ld-%d/", tv.tv_sec, tv.tv_usec, counter++); + + return uri; +} + + +void +nautilus_search_directory_set_query (NautilusSearchDirectory *search, + NautilusQuery *query) +{ + if (search->details->query != query) { + search->details->modified = TRUE; + } + + if (query) { + g_object_ref (query); + } + + if (search->details->query) { + g_object_unref (search->details->query); + } + + search->details->query = query; +} + +NautilusQuery * +nautilus_search_directory_get_query (NautilusSearchDirectory *search) +{ + if (search->details->query != NULL) { + return g_object_ref (search->details->query); + } + + return NULL; +} + +NautilusSearchDirectory * +nautilus_search_directory_new_from_saved_search (const char *uri) +{ + NautilusSearchDirectory *search; + NautilusQuery *query; + char *file; + + search = NAUTILUS_SEARCH_DIRECTORY (g_object_new (NAUTILUS_TYPE_SEARCH_DIRECTORY, NULL)); + + search->details->saved_search_uri = g_strdup (uri); + + file = gnome_vfs_get_local_path_from_uri (uri); + if (file != NULL) { + query = nautilus_query_load (file); + if (query != NULL) { + nautilus_search_directory_set_query (search, query); + g_object_unref (query); + } + g_free (file); + } else { + g_warning ("Non-local saved searches not supported"); + } + + search->details->modified = FALSE; + return search; +} + +gboolean +nautilus_search_directory_is_saved_search (NautilusSearchDirectory *search) +{ + return search->details->saved_search_uri != NULL; +} + +gboolean +nautilus_search_directory_is_modified (NautilusSearchDirectory *search) +{ + return search->details->modified; +} + +gboolean +nautilus_search_directory_is_indexed (NautilusSearchDirectory *search) +{ + ensure_search_engine (search); + return nautilus_search_engine_is_indexed (search->details->engine); +} + + +void +nautilus_search_directory_save_to_file (NautilusSearchDirectory *search, + const char *save_file_uri) +{ + char *file; + + file = gnome_vfs_get_local_path_from_uri (save_file_uri); + if (file == NULL) { + return; + } + + if (search->details->query != NULL) { + nautilus_query_save (search->details->query, file); + } + + g_free (file); +} + +void +nautilus_search_directory_save_search (NautilusSearchDirectory *search) +{ + if (search->details->saved_search_uri == NULL) { + return; + } + + nautilus_search_directory_save_to_file (search, + search->details->saved_search_uri); + search->details->modified = FALSE; +} diff --git a/libnautilus-private/nautilus-search-directory.h b/libnautilus-private/nautilus-search-directory.h new file mode 100644 index 000000000..c00869206 --- /dev/null +++ b/libnautilus-private/nautilus-search-directory.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- + + nautilus-search-directory.h: Subclass of NautilusDirectory to implement + a virtual directory consisting of the search directory and the search + icons + + Copyright (C) 2005 Novell, Inc + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef NAUTILUS_SEARCH_DIRECTORY_H +#define NAUTILUS_SEARCH_DIRECTORY_H + +#include <libnautilus-private/nautilus-directory.h> +#include <libnautilus-private/nautilus-query.h> + +#define NAUTILUS_TYPE_SEARCH_DIRECTORY \ + (nautilus_search_directory_get_type ()) +#define NAUTILUS_SEARCH_DIRECTORY(obj) \ + (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_SEARCH_DIRECTORY, NautilusSearchDirectory)) +#define NAUTILUS_SEARCH_DIRECTORY_CLASS(klass) \ + (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SEARCH_DIRECTORY, NautilusSearchDirectoryClass)) +#define NAUTILUS_IS_SEARCH_DIRECTORY(obj) \ + (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_SEARCH_DIRECTORY)) +#define NAUTILUS_IS_SEARCH_DIRECTORY_CLASS(klass) \ + (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SEARCH_DIRECTORY)) + +typedef struct NautilusSearchDirectoryDetails NautilusSearchDirectoryDetails; + +typedef struct { + NautilusDirectory parent_slot; + NautilusSearchDirectoryDetails *details; +} NautilusSearchDirectory; + +typedef struct { + NautilusDirectoryClass parent_slot; +} NautilusSearchDirectoryClass; + +GType nautilus_search_directory_get_type (void); + +char *nautilus_search_directory_generate_new_uri (void); + +NautilusSearchDirectory *nautilus_search_directory_new_from_saved_search (const char *uri); + +gboolean nautilus_search_directory_is_saved_search (NautilusSearchDirectory *search); +gboolean nautilus_search_directory_is_modified (NautilusSearchDirectory *search); +gboolean nautilus_search_directory_is_indexed (NautilusSearchDirectory *search); +void nautilus_search_directory_save_search (NautilusSearchDirectory *search); +void nautilus_search_directory_save_to_file (NautilusSearchDirectory *search, + const char *save_file_uri); + +NautilusQuery *nautilus_search_directory_get_query (NautilusSearchDirectory *search); +void nautilus_search_directory_set_query (NautilusSearchDirectory *search, + NautilusQuery *query); + +#endif /* NAUTILUS_SEARCH_DIRECTORY_H */ diff --git a/libnautilus-private/nautilus-search-engine-beagle.c b/libnautilus-private/nautilus-search-engine-beagle.c new file mode 100644 index 000000000..e86f10d01 --- /dev/null +++ b/libnautilus-private/nautilus-search-engine-beagle.c @@ -0,0 +1,289 @@ +/* -*- 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-engine-beagle.h" +#include <beagle/beagle.h> + +#include <eel/eel-gtk-macros.h> +#include <eel/eel-glib-extensions.h> + +struct NautilusSearchEngineBeagleDetails { + BeagleClient *client; + NautilusQuery *query; + + BeagleQuery *current_query; + char *current_query_uri_prefix; + gboolean query_finished; +}; + + +static void nautilus_search_engine_beagle_class_init (NautilusSearchEngineBeagleClass *class); +static void nautilus_search_engine_beagle_init (NautilusSearchEngineBeagle *engine); + +G_DEFINE_TYPE (NautilusSearchEngineBeagle, + nautilus_search_engine_beagle, + NAUTILUS_TYPE_SEARCH_ENGINE); + +static NautilusSearchEngineClass *parent_class = NULL; + +static void +finalize (GObject *object) +{ + NautilusSearchEngineBeagle *beagle; + + beagle = NAUTILUS_SEARCH_ENGINE_BEAGLE (object); + + if (beagle->details->current_query) { + g_object_unref (beagle->details->current_query); + beagle->details->current_query = NULL; + g_free (beagle->details->current_query_uri_prefix); + beagle->details->current_query_uri_prefix = NULL; + } + + if (beagle->details->query) { + g_object_unref (beagle->details->query); + beagle->details->query = NULL; + } + + if (beagle->details->client) { + g_object_unref (beagle->details->client); + beagle->details->client = NULL; + } + + g_free (beagle->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +beagle_hits_added (BeagleQuery *query, + BeagleHitsAddedResponse *response, + NautilusSearchEngineBeagle *engine) +{ + GSList *hits, *list; + GList *hit_uris; + const char *uri; + + hit_uris = NULL; + + hits = beagle_hits_added_response_get_hits (response); + + for (list = hits; list != NULL; list = list->next) { + BeagleHit *hit = BEAGLE_HIT (list->data); + + uri = beagle_hit_get_uri (hit); + + if (engine->details->current_query_uri_prefix && + !g_str_has_prefix (uri, engine->details->current_query_uri_prefix)) { + continue; + } + + hit_uris = g_list_prepend (hit_uris, (char *)uri); + } + + nautilus_search_engine_hits_added (NAUTILUS_SEARCH_ENGINE (engine), hit_uris); + g_list_free (hit_uris); +} + +static void +beagle_hits_subtracted (BeagleQuery *query, + BeagleHitsSubtractedResponse *response, + NautilusSearchEngineBeagle *engine) +{ + GSList *uris, *list; + GList *hit_uris; + + hit_uris = NULL; + + uris = beagle_hits_subtracted_response_get_uris (response); + + for (list = uris; list != NULL; list = list->next) { + hit_uris = g_list_prepend (hit_uris, (char *)list->data); + } + + nautilus_search_engine_hits_subtracted (NAUTILUS_SEARCH_ENGINE (engine), hit_uris); + g_list_free (hit_uris); +} + +static void +beagle_finished (BeagleQuery *query, + BeagleFinishedResponse *response, + NautilusSearchEngineBeagle *engine) +{ + /* For some reason we keep getting finished events, + * only emit finished once */ + if (engine->details->query_finished) { + return; + } + + engine->details->query_finished = TRUE; + nautilus_search_engine_finished (NAUTILUS_SEARCH_ENGINE (engine)); +} + +static void +beagle_error (BeagleQuery *query, + GError *error, + NautilusSearchEngineBeagle *engine) +{ + nautilus_search_engine_error (NAUTILUS_SEARCH_ENGINE (engine), error->message); +} + +static void +nautilus_search_engine_beagle_start (NautilusSearchEngine *engine) +{ + NautilusSearchEngineBeagle *beagle; + GError *error; + GList *mimetypes, *l; + char *text, *mimetype; + + error = NULL; + beagle = NAUTILUS_SEARCH_ENGINE_BEAGLE (engine); + + if (beagle->details->current_query) { + return; + } + + beagle->details->query_finished = FALSE; + beagle->details->current_query = beagle_query_new (); + g_signal_connect (beagle->details->current_query, + "hits-added", G_CALLBACK (beagle_hits_added), engine); + g_signal_connect (beagle->details->current_query, + "hits-subtracted", G_CALLBACK (beagle_hits_subtracted), engine); + g_signal_connect (beagle->details->current_query, + "finished", G_CALLBACK (beagle_finished), engine); + g_signal_connect (beagle->details->current_query, + "error", G_CALLBACK (beagle_error), engine); + + /* We only want files */ + beagle_query_add_hit_type (beagle->details->current_query, + "File"); + beagle_query_set_max_hits (beagle->details->current_query, + 1000); + + text = nautilus_query_get_text (beagle->details->query); + beagle_query_add_text (beagle->details->current_query, + text); + + mimetypes = nautilus_query_get_mime_types (beagle->details->query); + for (l = mimetypes; l != NULL; l = l->next) { + mimetype = l->data; + beagle_query_add_mime_type (beagle->details->current_query, + mimetype); + } + + beagle->details->current_query_uri_prefix = nautilus_query_get_location (beagle->details->query); + + if (!beagle_client_send_request_async (beagle->details->client, + BEAGLE_REQUEST (beagle->details->current_query), &error)) { + nautilus_search_engine_error (engine, error->message); + g_error_free (error); + } + + /* These must live during the lifetime of the query */ + g_free (text); + eel_g_list_free_deep (mimetypes); +} + +static void +nautilus_search_engine_beagle_stop (NautilusSearchEngine *engine) +{ + NautilusSearchEngineBeagle *beagle; + + beagle = NAUTILUS_SEARCH_ENGINE_BEAGLE (engine); + + if (beagle->details->current_query) { + g_object_unref (beagle->details->current_query); + beagle->details->current_query = NULL; + g_free (beagle->details->current_query_uri_prefix); + beagle->details->current_query_uri_prefix = NULL; + } +} + +static gboolean +nautilus_search_engine_beagle_is_indexed (NautilusSearchEngine *engine) +{ + return TRUE; +} + +static void +nautilus_search_engine_beagle_set_query (NautilusSearchEngine *engine, NautilusQuery *query) +{ + NautilusSearchEngineBeagle *beagle; + + beagle = NAUTILUS_SEARCH_ENGINE_BEAGLE (engine); + + if (query) { + g_object_ref (query); + } + + if (beagle->details->query) { + g_object_unref (beagle->details->query); + } + + beagle->details->query = query; +} + +static void +nautilus_search_engine_beagle_class_init (NautilusSearchEngineBeagleClass *class) +{ + GObjectClass *gobject_class; + NautilusSearchEngineClass *engine_class; + + parent_class = g_type_class_peek_parent (class); + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = finalize; + + engine_class = NAUTILUS_SEARCH_ENGINE_CLASS (class); + engine_class->set_query = nautilus_search_engine_beagle_set_query; + engine_class->start = nautilus_search_engine_beagle_start; + engine_class->stop = nautilus_search_engine_beagle_stop; + engine_class->is_indexed = nautilus_search_engine_beagle_is_indexed; +} + +static void +nautilus_search_engine_beagle_init (NautilusSearchEngineBeagle *engine) +{ + engine->details = g_new0 (NautilusSearchEngineBeagleDetails, 1); +} + + +NautilusSearchEngine * +nautilus_search_engine_beagle_new (void) +{ + NautilusSearchEngineBeagle *engine; + BeagleClient *client; + + client = beagle_client_new (NULL); + + if (client == NULL) { + return NULL; + } + + engine = g_object_new (NAUTILUS_TYPE_SEARCH_ENGINE_BEAGLE, NULL); + + engine->details->client = client; + + return NAUTILUS_SEARCH_ENGINE (engine); +} diff --git a/libnautilus-private/nautilus-search-engine-beagle.h b/libnautilus-private/nautilus-search-engine-beagle.h new file mode 100644 index 000000000..7ee801b37 --- /dev/null +++ b/libnautilus-private/nautilus-search-engine-beagle.h @@ -0,0 +1,51 @@ +/* -*- 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_ENGINE_BEAGLE_H +#define NAUTILUS_SEARCH_ENGINE_BEAGLE_H + +#include <libnautilus-private/nautilus-search-engine.h> + +#define NAUTILUS_TYPE_SEARCH_ENGINE_BEAGLE (nautilus_search_engine_beagle_get_type ()) +#define NAUTILUS_SEARCH_ENGINE_BEAGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_SEARCH_ENGINE_BEAGLE, NautilusSearchEngineBeagle)) +#define NAUTILUS_SEARCH_ENGINE_BEAGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SEARCH_ENGINE_BEAGLE, NautilusSearchEngineBeagleClass)) +#define NAUTILUS_IS_SEARCH_ENGINE_BEAGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_SEARCH_ENGINE_BEAGLE)) +#define NAUTILUS_IS_SEARCH_ENGINE_BEAGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SEARCH_ENGINE_BEAGLE)) +#define NAUTILUS_SEARCH_ENGINE_BEAGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_SEARCH_ENGINE_BEAGLE, NautilusSearchEngineBeagleClass)) + +typedef struct NautilusSearchEngineBeagleDetails NautilusSearchEngineBeagleDetails; + +typedef struct NautilusSearchEngineBeagle { + NautilusSearchEngine parent; + NautilusSearchEngineBeagleDetails *details; +} NautilusSearchEngineBeagle; + +typedef struct { + NautilusSearchEngineClass parent_class; +} NautilusSearchEngineBeagleClass; + +GType nautilus_search_engine_beagle_get_type (void); + +NautilusSearchEngine* nautilus_search_engine_beagle_new (void); + +#endif /* NAUTILUS_SEARCH_ENGINE_BEAGLE_H */ diff --git a/libnautilus-private/nautilus-search-engine-simple.c b/libnautilus-private/nautilus-search-engine-simple.c new file mode 100644 index 000000000..7e5d5f314 --- /dev/null +++ b/libnautilus-private/nautilus-search-engine-simple.c @@ -0,0 +1,370 @@ +/* -*- 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-search-engine-simple.h" + +#include <string.h> +#include <glib/gstrfuncs.h> +#include <eel/eel-gtk-macros.h> +#include <eel/eel-glib-extensions.h> +#include <libgnomevfs/gnome-vfs-directory.h> + +#define BATCH_SIZE 500 + +typedef struct { + NautilusSearchEngineSimple *engine; + + GnomeVFSURI *uri; + GList *mime_types; + char **words; + GList *found_list; + + gint n_processed_files; + GList *uri_hits; + + /* accessed on both threads: */ + volatile gboolean cancelled; +} SearchThreadData; + + +struct NautilusSearchEngineSimpleDetails { + NautilusQuery *query; + + SearchThreadData *active_search; + + gboolean query_finished; +}; + + +static void nautilus_search_engine_simple_class_init (NautilusSearchEngineSimpleClass *class); +static void nautilus_search_engine_simple_init (NautilusSearchEngineSimple *engine); + +G_DEFINE_TYPE (NautilusSearchEngineSimple, + nautilus_search_engine_simple, + NAUTILUS_TYPE_SEARCH_ENGINE); + +static NautilusSearchEngineClass *parent_class = NULL; + +static void +finalize (GObject *object) +{ + NautilusSearchEngineSimple *simple; + + simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (object); + + if (simple->details->query) { + g_object_unref (simple->details->query); + simple->details->query = NULL; + } + + g_free (simple->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static SearchThreadData * +search_thread_data_new (NautilusSearchEngineSimple *engine, + NautilusQuery *query) +{ + SearchThreadData *data; + char *text, *lower, *uri; + + data = g_new0 (SearchThreadData, 1); + + data->engine = engine; + uri = nautilus_query_get_location (query); + if (uri != NULL) { + data->uri = gnome_vfs_uri_new (uri); + g_free (uri); + } + if (data->uri == NULL) { + data->uri = gnome_vfs_uri_new ("file:///"); + } + + text = nautilus_query_get_text (query); + lower = g_ascii_strdown (text, -1); + data->words = g_strsplit (lower, " ", -1); + g_free (text); + g_free (lower); + + data->mime_types = nautilus_query_get_mime_types (query); + + return data; +} + +static void +search_thread_data_free (SearchThreadData *data) +{ + gnome_vfs_uri_unref (data->uri); + g_strfreev (data->words); + eel_g_list_free_deep (data->mime_types); + g_free (data); +} + +static gboolean +search_thread_done_idle (gpointer user_data) +{ + SearchThreadData *data; + + data = user_data; + + if (!data->cancelled) { + nautilus_search_engine_finished (NAUTILUS_SEARCH_ENGINE (data->engine)); + data->engine->details->active_search = NULL; + } + + search_thread_data_free (data); + + return FALSE; +} + +typedef struct { + GList *uris; + SearchThreadData *thread_data; +} SearchHits; + + +static gboolean +search_thread_add_hits_idle (gpointer user_data) +{ + SearchHits *hits; + + hits = user_data; + + if (!hits->thread_data->cancelled) { + nautilus_search_engine_hits_added (NAUTILUS_SEARCH_ENGINE (hits->thread_data->engine), + hits->uris); + } + + eel_g_list_free_deep (hits->uris); + g_free (hits); + + return FALSE; +} + +static void +send_batch (SearchThreadData *data) +{ + SearchHits *hits; + + data->n_processed_files = 0; + + if (data->uri_hits) { + hits = g_new (SearchHits, 1); + hits->uris = data->uri_hits; + hits->thread_data = data; + g_idle_add (search_thread_add_hits_idle, hits); + } + data->uri_hits = NULL; +} + +static gboolean +search_visit_func (const gchar *rel_path, + GnomeVFSFileInfo *info, + gboolean recursing_will_loop, + gpointer user_data, + gboolean *recurse) +{ + SearchThreadData *data; + int i; + char *lower_name; + GnomeVFSURI *uri; + gboolean hit; + GList *l; + gboolean is_hidden; + + data = user_data; + + if (data->cancelled) { + return FALSE; + } + + is_hidden = *info->name == '.'; + + if (recursing_will_loop || is_hidden) { + *recurse = FALSE; + } else { + *recurse = TRUE; + } + + hit = FALSE; + + if (!is_hidden) { + lower_name = g_ascii_strdown (info->name, -1); + + hit = TRUE; + for (i = 0; data->words[i] != NULL; i++) { + if (strstr (lower_name, data->words[i]) == NULL) { + hit = FALSE; + break; + } + } + g_free (lower_name); + } + + if (hit && data->mime_types != NULL) { + hit = FALSE; + + if (info->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE) { + for (l = data->mime_types; l != NULL; l = l->next) { + if (strcmp (info->mime_type, l->data) == 0) { + hit = TRUE; + break; + } + } + } + } + + if (hit) { + uri = gnome_vfs_uri_append_string (data->uri, rel_path); + data->uri_hits = g_list_prepend (data->uri_hits, gnome_vfs_uri_to_string (uri, 0)); + gnome_vfs_uri_unref (uri); + } + + data->n_processed_files++; + + if (data->n_processed_files > BATCH_SIZE) { + send_batch (data); + } + + + return TRUE; +} + + + +static gpointer +search_thread_func (gpointer user_data) +{ + SearchThreadData *data; + GnomeVFSResult res; + + data = user_data; + + res = gnome_vfs_directory_visit_uri (data->uri, + GNOME_VFS_FILE_INFO_GET_MIME_TYPE | GNOME_VFS_FILE_INFO_FOLLOW_LINKS, + GNOME_VFS_DIRECTORY_VISIT_LOOPCHECK, + search_visit_func, + data); + send_batch (data); + + g_idle_add (search_thread_done_idle, data); + + return NULL; +} + +static void +nautilus_search_engine_simple_start (NautilusSearchEngine *engine) +{ + NautilusSearchEngineSimple *simple; + SearchThreadData *data; + + simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (engine); + + if (simple->details->active_search != NULL) { + return; + } + + if (simple->details->query == NULL) { + return; + } + + data = search_thread_data_new (simple, simple->details->query); + + g_thread_create (search_thread_func, data, FALSE, NULL); + + simple->details->active_search = data; +} + +static void +nautilus_search_engine_simple_stop (NautilusSearchEngine *engine) +{ + NautilusSearchEngineSimple *simple; + + simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (engine); + + if (simple->details->active_search != NULL) { + simple->details->active_search->cancelled = TRUE; + simple->details->active_search = NULL; + } +} + +static gboolean +nautilus_search_engine_simple_is_indexed (NautilusSearchEngine *engine) +{ + return FALSE; +} + +static void +nautilus_search_engine_simple_set_query (NautilusSearchEngine *engine, NautilusQuery *query) +{ + NautilusSearchEngineSimple *simple; + + simple = NAUTILUS_SEARCH_ENGINE_SIMPLE (engine); + + if (query) { + g_object_ref (query); + } + + if (simple->details->query) { + g_object_unref (simple->details->query); + } + + simple->details->query = query; +} + +static void +nautilus_search_engine_simple_class_init (NautilusSearchEngineSimpleClass *class) +{ + GObjectClass *gobject_class; + NautilusSearchEngineClass *engine_class; + + parent_class = g_type_class_peek_parent (class); + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = finalize; + + engine_class = NAUTILUS_SEARCH_ENGINE_CLASS (class); + engine_class->set_query = nautilus_search_engine_simple_set_query; + engine_class->start = nautilus_search_engine_simple_start; + engine_class->stop = nautilus_search_engine_simple_stop; + engine_class->is_indexed = nautilus_search_engine_simple_is_indexed; +} + +static void +nautilus_search_engine_simple_init (NautilusSearchEngineSimple *engine) +{ + engine->details = g_new0 (NautilusSearchEngineSimpleDetails, 1); +} + + +NautilusSearchEngine * +nautilus_search_engine_simple_new (void) +{ + NautilusSearchEngine *engine; + + engine = g_object_new (NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE, NULL); + + return engine; +} diff --git a/libnautilus-private/nautilus-search-engine-simple.h b/libnautilus-private/nautilus-search-engine-simple.h new file mode 100644 index 000000000..be7bfb1b0 --- /dev/null +++ b/libnautilus-private/nautilus-search-engine-simple.h @@ -0,0 +1,51 @@ +/* -*- 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_SEARCH_ENGINE_SIMPLE_H +#define NAUTILUS_SEARCH_ENGINE_SIMPLE_H + +#include <libnautilus-private/nautilus-search-engine.h> + +#define NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE (nautilus_search_engine_simple_get_type ()) +#define NAUTILUS_SEARCH_ENGINE_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE, NautilusSearchEngineSimple)) +#define NAUTILUS_SEARCH_ENGINE_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE, NautilusSearchEngineSimpleClass)) +#define NAUTILUS_IS_SEARCH_ENGINE_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE)) +#define NAUTILUS_IS_SEARCH_ENGINE_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE)) +#define NAUTILUS_SEARCH_ENGINE_SIMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_SEARCH_ENGINE_SIMPLE, NautilusSearchEngineSimpleClass)) + +typedef struct NautilusSearchEngineSimpleDetails NautilusSearchEngineSimpleDetails; + +typedef struct NautilusSearchEngineSimple { + NautilusSearchEngine parent; + NautilusSearchEngineSimpleDetails *details; +} NautilusSearchEngineSimple; + +typedef struct { + NautilusSearchEngineClass parent_class; +} NautilusSearchEngineSimpleClass; + +GType nautilus_search_engine_simple_get_type (void); + +NautilusSearchEngine* nautilus_search_engine_simple_new (void); + +#endif /* NAUTILUS_SEARCH_ENGINE_SIMPLE_H */ diff --git a/libnautilus-private/nautilus-search-engine.c b/libnautilus-private/nautilus-search-engine.c new file mode 100644 index 000000000..d98b1f40e --- /dev/null +++ b/libnautilus-private/nautilus-search-engine.c @@ -0,0 +1,207 @@ +/* -*- 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-engine.h" +#include "nautilus-search-engine-beagle.h" +#include "nautilus-search-engine-simple.h" + +#include <eel/eel-gtk-macros.h> + +struct NautilusSearchEngineDetails { + int none; +}; + +enum { + HITS_ADDED, + HITS_SUBTRACTED, + FINISHED, + ERROR, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +static void nautilus_search_engine_class_init (NautilusSearchEngineClass *class); +static void nautilus_search_engine_init (NautilusSearchEngine *engine); + +G_DEFINE_ABSTRACT_TYPE (NautilusSearchEngine, + nautilus_search_engine, + G_TYPE_OBJECT); + +static GObjectClass *parent_class = NULL; + +static void +finalize (GObject *object) +{ + NautilusSearchEngine *engine; + + engine = NAUTILUS_SEARCH_ENGINE (object); + + g_free (engine->details); + + EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +nautilus_search_engine_class_init (NautilusSearchEngineClass *class) +{ + GObjectClass *gobject_class; + + parent_class = g_type_class_peek_parent (class); + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = finalize; + + signals[HITS_ADDED] = + g_signal_new ("hits-added", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusSearchEngineClass, hits_added), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + signals[HITS_SUBTRACTED] = + g_signal_new ("hits-subtracted", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusSearchEngineClass, hits_subtracted), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + signals[FINISHED] = + g_signal_new ("finished", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusSearchEngineClass, finished), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[ERROR] = + g_signal_new ("error", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NautilusSearchEngineClass, error), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + +} + +static void +nautilus_search_engine_init (NautilusSearchEngine *engine) +{ + engine->details = g_new0 (NautilusSearchEngineDetails, 1); +} + +NautilusSearchEngine * +nautilus_search_engine_new (void) +{ + NautilusSearchEngine *engine; + +#ifdef HAVE_BEAGLE + engine = nautilus_search_engine_beagle_new (); + if (engine) { + return engine; + } +#endif + engine = nautilus_search_engine_simple_new (); + return engine; +} + +void +nautilus_search_engine_set_query (NautilusSearchEngine *engine, NautilusQuery *query) +{ + g_return_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine)); + g_return_if_fail (NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->set_query != NULL); + + NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->set_query (engine, query); +} + +void +nautilus_search_engine_start (NautilusSearchEngine *engine) +{ + g_return_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine)); + g_return_if_fail (NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->start != NULL); + + NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->start (engine); +} + + +void +nautilus_search_engine_stop (NautilusSearchEngine *engine) +{ + g_return_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine)); + g_return_if_fail (NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->stop != NULL); + + NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->stop (engine); +} + +gboolean +nautilus_search_engine_is_indexed (NautilusSearchEngine *engine) +{ + g_return_val_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine), FALSE); + g_return_val_if_fail (NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->is_indexed != NULL, FALSE); + + return NAUTILUS_SEARCH_ENGINE_GET_CLASS (engine)->is_indexed (engine); +} + +void +nautilus_search_engine_hits_added (NautilusSearchEngine *engine, GList *hits) +{ + g_return_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine)); + + g_signal_emit (engine, signals[HITS_ADDED], 0, hits); +} + + +void +nautilus_search_engine_hits_subtracted (NautilusSearchEngine *engine, GList *hits) +{ + g_return_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine)); + + g_signal_emit (engine, signals[HITS_SUBTRACTED], 0, hits); +} + + +void +nautilus_search_engine_finished (NautilusSearchEngine *engine) +{ + g_return_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine)); + + g_signal_emit (engine, signals[FINISHED], 0); +} + +void +nautilus_search_engine_error (NautilusSearchEngine *engine, const char *error_message) +{ + g_return_if_fail (NAUTILUS_IS_SEARCH_ENGINE (engine)); + + g_signal_emit (engine, signals[ERROR], 0, error_message); +} diff --git a/libnautilus-private/nautilus-search-engine.h b/libnautilus-private/nautilus-search-engine.h new file mode 100644 index 000000000..79e68aeb3 --- /dev/null +++ b/libnautilus-private/nautilus-search-engine.h @@ -0,0 +1,76 @@ +/* -*- 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_ENGINE_H +#define NAUTILUS_SEARCH_ENGINE_H + +#include <glib-object.h> +#include <libgnomevfs/gnome-vfs-result.h> +#include <libnautilus-private/nautilus-query.h> + +#define NAUTILUS_TYPE_SEARCH_ENGINE (nautilus_search_engine_get_type ()) +#define NAUTILUS_SEARCH_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NAUTILUS_TYPE_SEARCH_ENGINE, NautilusSearchEngine)) +#define NAUTILUS_SEARCH_ENGINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_SEARCH_ENGINE, NautilusSearchEngineClass)) +#define NAUTILUS_IS_SEARCH_ENGINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NAUTILUS_TYPE_SEARCH_ENGINE)) +#define NAUTILUS_IS_SEARCH_ENGINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_SEARCH_ENGINE)) +#define NAUTILUS_SEARCH_ENGINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NAUTILUS_TYPE_SEARCH_ENGINE, NautilusSearchEngineClass)) + +typedef struct NautilusSearchEngineDetails NautilusSearchEngineDetails; + +typedef struct NautilusSearchEngine { + GObject parent; + NautilusSearchEngineDetails *details; +} NautilusSearchEngine; + +typedef struct { + GObjectClass parent_class; + + /* VTable */ + void (*set_query) (NautilusSearchEngine *engine, NautilusQuery *query); + void (*start) (NautilusSearchEngine *engine); + void (*stop) (NautilusSearchEngine *engine); + gboolean (*is_indexed) (NautilusSearchEngine *engine); + + /* Signals */ + void (*hits_added) (NautilusSearchEngine *engine, GList *hits); + void (*hits_subtracted) (NautilusSearchEngine *engine, GList *hits); + void (*finished) (NautilusSearchEngine *engine); + void (*error) (NautilusSearchEngine *engine, const char *error_message); +} NautilusSearchEngineClass; + +GType nautilus_search_engine_get_type (void); +gboolean nautilus_search_engine_enabled (void); + +NautilusSearchEngine* nautilus_search_engine_new (void); + +void nautilus_search_engine_set_query (NautilusSearchEngine *engine, NautilusQuery *query); +void nautilus_search_engine_start (NautilusSearchEngine *engine); +void nautilus_search_engine_stop (NautilusSearchEngine *engine); +gboolean nautilus_search_engine_is_indexed (NautilusSearchEngine *engine); + +void nautilus_search_engine_hits_added (NautilusSearchEngine *engine, GList *hits); +void nautilus_search_engine_hits_subtracted (NautilusSearchEngine *engine, GList *hits); +void nautilus_search_engine_finished (NautilusSearchEngine *engine); +void nautilus_search_engine_error (NautilusSearchEngine *engine, const char *error_message); + +#endif /* NAUTILUS_SEARCH_ENGINE_H */ diff --git a/libnautilus-private/nautilus-tree-view-drag-dest.c b/libnautilus-private/nautilus-tree-view-drag-dest.c index 4d6b3aedb..1d0c94df8 100644 --- a/libnautilus-private/nautilus-tree-view-drag-dest.c +++ b/libnautilus-private/nautilus-tree-view-drag-dest.c @@ -310,21 +310,23 @@ get_drop_path (NautilusTreeViewDragDest *dest, return NULL; } - file = file_for_path (dest, path); - - ret = NULL; + ret = gtk_tree_path_copy (path); + file = file_for_path (dest, ret); - if (!file || !nautilus_drag_can_accept_items (file, dest->details->drag_list)){ + /* Go up the tree until we find a file that can accept a drop */ + while (file == NULL /* dummy row */ || + !nautilus_drag_can_accept_items (file, dest->details->drag_list)) { if (gtk_tree_path_get_depth (path) == 1) { + gtk_tree_path_free (ret); ret = NULL; + break; } else { - ret = gtk_tree_path_copy (path); gtk_tree_path_up (ret); + + nautilus_file_unref (file); + file = file_for_path (dest, ret); } - } else { - ret = gtk_tree_path_copy (path); } - nautilus_file_unref (file); return ret; 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 diff --git a/test/.cvsignore b/test/.cvsignore index 32b1fd7b8..57492f72f 100644 --- a/test/.cvsignore +++ b/test/.cvsignore @@ -8,3 +8,5 @@ test-nautilus-mime-actions-set test-nautilus-preferences-change test-nautilus-preferences-display test-nautilus-wrap-table +test-nautilus-directory-async +test-nautilus-search-engine diff --git a/test/Makefile.am b/test/Makefile.am index b6c59464e..b6fd4cd70 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -14,10 +14,16 @@ LDADD =\ noinst_PROGRAMS =\ test-nautilus-wrap-table \ + test-nautilus-search-engine \ + test-nautilus-directory-async \ $(NULL) test_nautilus_wrap_table_SOURCES = test-nautilus-wrap-table.c test.c +test_nautilus_search_engine_SOURCES = test-nautilus-search-engine.c + +test_nautilus_directory_async_SOURCES = test-nautilus-directory-async.c + EXTRA_DIST = \ test.h \ $(NULL) diff --git a/test/test-nautilus-directory-async.c b/test/test-nautilus-directory-async.c new file mode 100644 index 000000000..ed6eb35ae --- /dev/null +++ b/test/test-nautilus-directory-async.c @@ -0,0 +1,106 @@ +#include <gtk/gtk.h> +#include <libgnomevfs/gnome-vfs.h> +#include <libnautilus-private/nautilus-directory.h> +#include <libnautilus-private/nautilus-search-directory.h> +#include <libnautilus-private/nautilus-file.h> +#include <unistd.h> + +void *client1, *client2; + +#if 0 +static gboolean +quit_cb (gpointer data) +{ + gtk_main_quit (); + + return FALSE; +} +#endif + +static void +files_added (NautilusDirectory *directory, + GList *added_files) +{ +#if 0 + GList *list; + + for (list = added_files; list != NULL; list = list->next) { + NautilusFile *file = list->data; + + g_print (" - %s\n", nautilus_file_get_uri (file)); + } +#endif + + g_print ("files added: %d files\n", + g_list_length (added_files)); +} + +static void +files_changed (NautilusDirectory *directory, + GList *changed_files) +{ +#if 0 + GList *list; + + for (list = changed_files; list != NULL; list = list->next) { + NautilusFile *file = list->data; + + g_print (" - %s\n", nautilus_file_get_uri (file)); + } +#endif + g_print ("files changed: %d\n", + g_list_length (changed_files)); +} + +static gboolean +force_reload (NautilusDirectory *directory) +{ + g_print ("forcing reload!\n"); + + nautilus_directory_force_reload (directory); + + return FALSE; +} + +static void +done_loading (NautilusDirectory *directory) +{ + static int i = 0; + + g_print ("done loading\n"); + + if (i == 0) { + gtk_timeout_add (5000, (GSourceFunc)force_reload, directory); + i++; + } else { + } +} + +int +main (int argc, char **argv) +{ + NautilusDirectory *directory; + client1 = g_new0 (int, 1); + client2 = g_new0 (int, 1); + NautilusQuery *query; + + gnome_vfs_init (); + gtk_init (&argc, &argv); + + query = nautilus_query_new (); + nautilus_query_set_text (query, "richard hult"); + directory = nautilus_directory_get ("x-nautilus-search://0/"); + nautilus_search_directory_set_query (NAUTILUS_SEARCH_DIRECTORY (directory), query); + g_object_unref (query); + + g_signal_connect (directory, "files-added", G_CALLBACK (files_added), NULL); + g_signal_connect (directory, "files-changed", G_CALLBACK (files_changed), NULL); + g_signal_connect (directory, "done-loading", G_CALLBACK (done_loading), NULL); + nautilus_directory_file_monitor_add (directory, client1, TRUE, TRUE, + NAUTILUS_FILE_ATTRIBUTE_METADATA, + NULL, NULL); + + + gtk_main (); + return 0; +} diff --git a/test/test-nautilus-search-engine.c b/test/test-nautilus-search-engine.c new file mode 100644 index 000000000..f6224d004 --- /dev/null +++ b/test/test-nautilus-search-engine.c @@ -0,0 +1,56 @@ +#include <libnautilus-private/nautilus-search-engine.h> +#include <gtk/gtk.h> + +static void +hits_added_cb (NautilusSearchEngine *engine, GSList *hits) +{ + g_print ("hits added\n"); + while (hits) { + g_print (" - %s\n", (char *)hits->data); + hits = hits->next; + } +} + +static void +hits_subtracted_cb (NautilusSearchEngine *engine, GSList *hits) +{ + g_print ("hits subtracted\n"); + while (hits) { + g_print (" - %s\n", (char *)hits->data); + hits = hits->next; + } +} + +static void +finished_cb (NautilusSearchEngine *engine) +{ + g_print ("finished!\n"); +// gtk_main_quit (); +} + +int +main (int argc, char* argv[]) +{ + NautilusSearchEngine *engine; + NautilusQuery *query; + + gtk_init (&argc, &argv); + + engine = nautilus_search_engine_new (); + g_signal_connect (engine, "hits-added", + G_CALLBACK (hits_added_cb), NULL); + g_signal_connect (engine, "hits-subtracted", + G_CALLBACK (hits_subtracted_cb), NULL); + g_signal_connect (engine, "finished", + G_CALLBACK (finished_cb), NULL); + + query = nautilus_query_new (); + nautilus_query_set_text (query, "richard hult"); + nautilus_search_engine_set_query (engine, query); + g_object_unref (query); + + nautilus_search_engine_start (engine); + + gtk_main (); + return 0; +} |