From 3541baa5502e896ebea0d7892569cf4691a7093e Mon Sep 17 00:00:00 2001 From: Michael Webster Date: Tue, 18 May 2021 11:49:39 -0400 Subject: [PATCH] Implement Content search. Modify existing search ui and non-tracker engine to: - Allow toggling folder recursion, case sensitivity (for both file and content search). - Content search can be plain text or a regular expression, if toggle is enabled - If visible, the 'Hits' list view column will display of how many matches are in each file. - Initial Support for text/plain files, pdf, ps, OpenDocument, exif (photo metadata) and id3 (mpeg metadata). - text/plain support is native, the others are provided via 'search helpers, and additional formats are easily added. A search helper is defined by a desktop file (in a similar manner to thumbnailers) - it only needs to be able to parse a given file type and print the text to stdout. - Multiple helpers can be defined for the same mimetype, and given priorities. If a helper is missing its executable, it is skipped, or a lower priority helper can be chosen. - The tracker engine is unchanged, but can be made to take advantage of these changes to provide content search as well. - Previous saved search functionality has been removed. --- config.h.meson.in | 2 + .../nemo-recursive-search-symbolic.svg | 203 +++ data/icons/meson.build | 1 + debian/control | 5 + .../search-helpers/exif.nemo_search_helper | 5 + .../search-helpers/id3.nemo_search_helper | 5 + .../libreoffice.nemo_search_helper | 5 + .../search-helpers/pdf2txt.nemo_search_helper | 5 + .../pdftotext.nemo_search_helper | 5 + .../search-helpers/ps2txt.nemo_search_helper | 5 + gresources/nemo-directory-view-ui.xml | 2 - gresources/nemo-search-bar.glade | 247 ++++ gresources/nemo.gresource.xml | 1 + libnemo-extension/nemo-column.c | 80 +- libnemo-extension/nemo-column.h | 10 + libnemo-private/meson.build | 3 +- libnemo-private/nemo-column-utilities.c | 41 +- libnemo-private/nemo-debug.c | 1 + libnemo-private/nemo-debug.h | 3 +- libnemo-private/nemo-directory.c | 14 +- libnemo-private/nemo-directory.h | 1 + libnemo-private/nemo-file-private.h | 2 + libnemo-private/nemo-file-utilities.h | 3 - libnemo-private/nemo-file.c | 209 ++- libnemo-private/nemo-file.h | 22 +- libnemo-private/nemo-global-preferences.c | 3 + libnemo-private/nemo-global-preferences.h | 21 +- libnemo-private/nemo-query.c | 305 ++--- libnemo-private/nemo-query.h | 19 +- libnemo-private/nemo-saved-search-file.c | 46 - libnemo-private/nemo-saved-search-file.h | 56 - libnemo-private/nemo-search-directory.c | 83 +- libnemo-private/nemo-search-engine-advanced.c | 1150 +++++++++++++++++ libnemo-private/nemo-search-engine-advanced.h | 48 + libnemo-private/nemo-search-engine-simple.c | 537 -------- libnemo-private/nemo-search-engine-simple.h | 51 - libnemo-private/nemo-search-engine-tracker.c | 11 +- libnemo-private/nemo-search-engine.c | 43 +- libnemo-private/nemo-search-engine.h | 14 +- libnemo-private/org.nemo.gschema.xml | 58 +- src/nemo-actions.h | 2 - src/nemo-icon-view-container.c | 5 +- src/nemo-icon-view-grid-container.c | 2 +- src/nemo-icon-view.c | 6 +- src/nemo-list-model.c | 26 +- src/nemo-list-model.h | 1 + src/nemo-list-view.c | 113 +- src/nemo-main-application.c | 3 + src/nemo-query-editor.c | 982 ++++++-------- src/nemo-tree-sidebar.c | 3 +- src/nemo-view.c | 176 --- src/nemo-window-slot.c | 52 +- src/nemo-window-slot.h | 1 + test/test-nemo-directory-async.c | 2 +- test/test-nemo-search-engine.c | 2 +- 55 files changed, 2832 insertions(+), 1869 deletions(-) create mode 100644 data/icons/hicolor/actions/scalable/nemo-recursive-search-symbolic.svg create mode 100644 files/usr/share/nemo/search-helpers/exif.nemo_search_helper create mode 100644 files/usr/share/nemo/search-helpers/id3.nemo_search_helper create mode 100644 files/usr/share/nemo/search-helpers/libreoffice.nemo_search_helper create mode 100644 files/usr/share/nemo/search-helpers/pdf2txt.nemo_search_helper create mode 100644 files/usr/share/nemo/search-helpers/pdftotext.nemo_search_helper create mode 100644 files/usr/share/nemo/search-helpers/ps2txt.nemo_search_helper create mode 100644 gresources/nemo-search-bar.glade delete mode 100644 libnemo-private/nemo-saved-search-file.c delete mode 100644 libnemo-private/nemo-saved-search-file.h create mode 100644 libnemo-private/nemo-search-engine-advanced.c create mode 100644 libnemo-private/nemo-search-engine-advanced.h delete mode 100644 libnemo-private/nemo-search-engine-simple.c delete mode 100644 libnemo-private/nemo-search-engine-simple.h diff --git a/config.h.meson.in b/config.h.meson.in index 46ba4c8bf..f72453955 100644 --- a/config.h.meson.in +++ b/config.h.meson.in @@ -63,3 +63,5 @@ // we just construct the definitons ourselves instead. Runtime // support is still checked at runtime (safely.) #mesondefine NATIVE_STATX + +#mesondefine ENABLE_TRACKER diff --git a/data/icons/hicolor/actions/scalable/nemo-recursive-search-symbolic.svg b/data/icons/hicolor/actions/scalable/nemo-recursive-search-symbolic.svg new file mode 100644 index 000000000..73104950b --- /dev/null +++ b/data/icons/hicolor/actions/scalable/nemo-recursive-search-symbolic.svg @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/data/icons/meson.build b/data/icons/meson.build index 4c7283a9b..393a527d4 100644 --- a/data/icons/meson.build +++ b/data/icons/meson.build @@ -29,6 +29,7 @@ publicIcons = [ 'hicolor/actions/scalable/nemo-horizontal-layout-wide-symbolic.svg', 'hicolor/actions/scalable/nemo-vertical-layout-symbolic.svg', 'hicolor/actions/scalable/nemo-vertical-layout-wide-symbolic.svg', + 'hicolor/actions/scalable/nemo-recursive-search-symbolic.svg', 'hicolor/devices/scalable/drive-removable-media-usb-symbolic.svg', 'hicolor/status/48x48/progress-0.png', 'hicolor/status/48x48/progress-10.png', diff --git a/debian/control b/debian/control index fc16364bb..58179dcc1 100644 --- a/debian/control +++ b/debian/control @@ -18,6 +18,7 @@ Build-Depends: libgirepository1.0-dev (>= 0.9.12), libglib2.0-dev (>= 2.45.7), libglib2.0-doc, + libgsf-1-dev, libgtk-3-dev (>= 3.10), libgtk-3-doc, libnotify-dev (>= 0.7.0), @@ -98,6 +99,10 @@ Recommends: gvfs-fuse, librsvg2-common, nemo-fileroller, + poppler-utils, + exif, + id3, + odt2txt Suggests: eog, evince | pdf-viewer, totem | mp3-decoder, xdg-user-dirs Description: file manager and graphical shell for Cinnamon Nemo is the official file manager for the Cinnamon desktop. It allows diff --git a/files/usr/share/nemo/search-helpers/exif.nemo_search_helper b/files/usr/share/nemo/search-helpers/exif.nemo_search_helper new file mode 100644 index 000000000..7e63298f1 --- /dev/null +++ b/files/usr/share/nemo/search-helpers/exif.nemo_search_helper @@ -0,0 +1,5 @@ +[Nemo Search Cat Helper] +TryExec=exif +Exec=exif -m %s +MimeType=image/jpeg;image/png;image/gif;image/bmp;image/tiff +Priority=100 diff --git a/files/usr/share/nemo/search-helpers/id3.nemo_search_helper b/files/usr/share/nemo/search-helpers/id3.nemo_search_helper new file mode 100644 index 000000000..0ef0692ab --- /dev/null +++ b/files/usr/share/nemo/search-helpers/id3.nemo_search_helper @@ -0,0 +1,5 @@ +[Nemo Search Cat Helper] +TryExec=id3 +Exec=id3 -l %s +MimeType=audio/mpeg +Priority=100 diff --git a/files/usr/share/nemo/search-helpers/libreoffice.nemo_search_helper b/files/usr/share/nemo/search-helpers/libreoffice.nemo_search_helper new file mode 100644 index 000000000..bd22ff9e4 --- /dev/null +++ b/files/usr/share/nemo/search-helpers/libreoffice.nemo_search_helper @@ -0,0 +1,5 @@ +[Nemo Search Cat Helper] +TryExec=odt2txt +Exec=odt2txt %s +MimeType=application/vnd.oasis.opendocument.text;application/vnd.oasis.opendocument.spreadsheet;application/vnd.oasis.opendocument.presentation;application/vnd.oasis.opendocument.graphics; +priority=100 diff --git a/files/usr/share/nemo/search-helpers/pdf2txt.nemo_search_helper b/files/usr/share/nemo/search-helpers/pdf2txt.nemo_search_helper new file mode 100644 index 000000000..6684783e0 --- /dev/null +++ b/files/usr/share/nemo/search-helpers/pdf2txt.nemo_search_helper @@ -0,0 +1,5 @@ +[Nemo Search Cat Helper] +TryExec=pdf2txt +Exec=pdf2txt %s +MimeType=application/pdf; +Priority=100 diff --git a/files/usr/share/nemo/search-helpers/pdftotext.nemo_search_helper b/files/usr/share/nemo/search-helpers/pdftotext.nemo_search_helper new file mode 100644 index 000000000..9dc6f4c47 --- /dev/null +++ b/files/usr/share/nemo/search-helpers/pdftotext.nemo_search_helper @@ -0,0 +1,5 @@ +[Nemo Search Cat Helper] +TryExec=pdftotext +Exec=pdftotext %s - +MimeType=application/pdf; +Priority=200 diff --git a/files/usr/share/nemo/search-helpers/ps2txt.nemo_search_helper b/files/usr/share/nemo/search-helpers/ps2txt.nemo_search_helper new file mode 100644 index 000000000..27f7d1b87 --- /dev/null +++ b/files/usr/share/nemo/search-helpers/ps2txt.nemo_search_helper @@ -0,0 +1,5 @@ +[Nemo Search Cat Helper] +TryExec=ps2txt +Exec=ps2txt %s +MimeType=application/ps; +Priority=100; diff --git a/gresources/nemo-directory-view-ui.xml b/gresources/nemo-directory-view-ui.xml index d69e3fe3e..d4d542b34 100644 --- a/gresources/nemo-directory-view-ui.xml +++ b/gresources/nemo-directory-view-ui.xml @@ -47,8 +47,6 @@ - - diff --git a/gresources/nemo-search-bar.glade b/gresources/nemo-search-bar.glade new file mode 100644 index 000000000..c44a1dc3e --- /dev/null +++ b/gresources/nemo-search-bar.glade @@ -0,0 +1,247 @@ + + + + + + + True + False + True + other + + + False + True + 6 + end + + + + + + False + False + 0 + + + + + False + + + True + False + True + vertical + 6 + + + True + False + 6 + + + True + False + Search for files: + 1 + + + False + True + 0 + + + + + True + False + + + True + True + False + system-search-symbolic + + + True + True + 1 + + + + + True + True + False + True + Case sensitive + + + True + False + xapp-text-case-symbolic + + + + + False + True + 2 + + + + + True + True + False + True + Search folders recursively + + + True + False + nemo-recursive-search-symbolic + + + + + False + True + 3 + + + + + + True + True + 1 + + + + + False + True + 0 + + + + + True + False + 6 + + + True + False + Search content: + 1 + + + False + True + 0 + + + + + True + False + + + True + True + False + system-search-symbolic + Enter text to search for + + + True + True + 1 + + + + + True + True + False + True + Case sensitive + + + True + False + xapp-text-case-symbolic + + + + + False + True + 2 + + + + + True + True + False + True + Regular expression + + + True + False + xapp-use-regex-symbolic + + + + + False + True + 3 + + + + + + True + True + 1 + + + + + False + True + 1 + + + + + False + True + 0 + + + + + False + False + 0 + + + + + + + + + + diff --git a/gresources/nemo.gresource.xml b/gresources/nemo.gresource.xml index 572019f92..91d50e2c7 100644 --- a/gresources/nemo.gresource.xml +++ b/gresources/nemo.gresource.xml @@ -5,6 +5,7 @@ nemo-file-management-properties.glade nemo-desktop-overlay.glade nemo-desktop-preferences.glade + nemo-search-bar.glade nemo-shortcuts.ui nemo-icon-view-ui.xml nemo-directory-view-ui.xml diff --git a/libnemo-extension/nemo-column.c b/libnemo-extension/nemo-column.c index f4bc0c26f..bb4433b66 100644 --- a/libnemo-extension/nemo-column.c +++ b/libnemo-extension/nemo-column.c @@ -34,6 +34,8 @@ enum { PROP_LABEL, PROP_DESCRIPTION, PROP_XALIGN, + PROP_WIDTH_CHARS, + PROP_ELLIPSIZE, LAST_PROP }; @@ -44,6 +46,8 @@ typedef struct char *label; char *description; float xalign; + gint width_chars; + gboolean ellipsize; } NemoColumnPrivate; struct _NemoColumn @@ -74,11 +78,11 @@ G_DEFINE_TYPE_WITH_PRIVATE (NemoColumn, nemo_column, G_TYPE_OBJECT) * * Creates a new column * - * Returns: a newly created #NemoColumn + * Returns: (transfer full): a newly created #NemoColumn */ NemoColumn * nemo_column_new (const char *name, - const char *attribute, + const char *attribute, const char *label, const char *description) { @@ -99,6 +103,47 @@ nemo_column_new (const char *name, return column; } + +/** + * nemo_column_new2: + * @name: identifier of the column + * @attribute: the file attribute to be displayed in the column + * @label: the user-visible label for the column + * @description: a user-visible description of the column + * @width_chars: the default width of the column (-1 for auto-calc) + * @ellipsize: PangoEllipsizeMode to set when truncating column + * + * Creates a new column + * + * Returns: (transfer full): a newly created #NemoColumn + */ +NemoColumn * +nemo_column_new2 (const char *name, + const char *attribute, + const char *label, + const char *description, + gint width_chars, + PangoEllipsizeMode ellipsize) +{ + NemoColumn *column; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (attribute != NULL, NULL); + g_return_val_if_fail (label != NULL, NULL); + g_return_val_if_fail (description != NULL, NULL); + + column = g_object_new (NEMO_TYPE_COLUMN, + "name", name, + "attribute", attribute, + "label", label, + "description", description, + "width-chars", width_chars, + "ellipsize", ellipsize, + NULL); + + return column; +} + static void nemo_column_get_property (GObject *object, guint param_id, @@ -128,6 +173,13 @@ nemo_column_get_property (GObject *object, case PROP_XALIGN : g_value_set_float (value, column->details->xalign); break; + case PROP_WIDTH_CHARS : + g_value_set_int (value, column->details->width_chars); + break; + case PROP_ELLIPSIZE : + g_value_set_enum (value, column->details->ellipsize); + break; + default : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -169,6 +221,15 @@ nemo_column_set_property (GObject *object, column->details->xalign = g_value_get_float (value); g_object_notify (object, "xalign"); break; + case PROP_WIDTH_CHARS : + column->details->width_chars = g_value_get_int (value); + g_object_notify (object, "width-chars"); + break; + case PROP_ELLIPSIZE : + column->details->ellipsize = g_value_get_enum (value); + g_object_notify (object, "ellipsize"); + break; + default : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; @@ -251,5 +312,20 @@ nemo_column_class_init (NemoColumnClass *class) 1.0, 0.0, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (class), + PROP_WIDTH_CHARS, + g_param_spec_int ("width-chars", + "Default column width", + "Default column width", + -1, G_MAXINT, -1, + G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (class), + PROP_ELLIPSIZE, + g_param_spec_enum ("ellipsize", + "Pango ellipsize mode when text won't fit", + "Pango ellipsize mode when text won't fit", + PANGO_TYPE_ELLIPSIZE_MODE, + PANGO_ELLIPSIZE_NONE, + G_PARAM_READWRITE)); } diff --git a/libnemo-extension/nemo-column.h b/libnemo-extension/nemo-column.h index 76f0d3f77..4d05d2194 100644 --- a/libnemo-extension/nemo-column.h +++ b/libnemo-extension/nemo-column.h @@ -26,6 +26,8 @@ #define NEMO_COLUMN_H #include +#include + #include "nemo-extension-types.h" G_BEGIN_DECLS @@ -38,6 +40,12 @@ NemoColumn * nemo_column_new (const char *name, const char *attribute, const char *label, const char *description); +NemoColumn * nemo_column_new2 (const char *name, + const char *attribute, + const char *label, + const char *description, + gint width_chars, + PangoEllipsizeMode ellipsize); /* NemoColumn has the following properties: * name (string) - the identifier for the column @@ -46,6 +54,8 @@ NemoColumn * nemo_column_new (const char *name, * label (string) - the user-visible label for the column * description (string) - a user-visible description of the column * xalign (float) - x-alignment of the column + * width-chars (int) default width + * ellipsize (PangoEllipsizeMode whether to ellipsize when truncating text */ G_END_DECLS diff --git a/libnemo-private/meson.build b/libnemo-private/meson.build index dcf03ae02..fffd8544d 100644 --- a/libnemo-private/meson.build +++ b/libnemo-private/meson.build @@ -60,10 +60,9 @@ nemo_private_sources = [ 'nemo-progress-info.c', 'nemo-query.c', 'nemo-recent.c', - 'nemo-saved-search-file.c', 'nemo-search-directory-file.c', 'nemo-search-directory.c', - 'nemo-search-engine-simple.c', + 'nemo-search-engine-advanced.c', 'nemo-search-engine.c', 'nemo-selection-canvas-item.c', 'nemo-separator-action.c', diff --git a/libnemo-private/nemo-column-utilities.c b/libnemo-private/nemo-column-utilities.c index 283b5b6d8..e7e97d8f3 100644 --- a/libnemo-private/nemo-column-utilities.c +++ b/libnemo-private/nemo-column-utilities.c @@ -158,6 +158,8 @@ get_builtin_columns (void) "attribute", "where", "label", _("Location"), "description", _("The location of the file."), + "width-chars", 60, + "ellipsize", PANGO_ELLIPSIZE_END, NULL)); return columns; @@ -213,6 +215,32 @@ get_trash_columns (void) return nemo_column_list_copy (columns); } +static GList * +get_search_columns (void) +{ + static GList *columns = NULL; + + if (columns == NULL) { + // columns = g_list_append (columns, + // g_object_new (NEMO_TYPE_COLUMN, + // "name", "search-result-snippet", + // "attribute", "search_result_snippet", + // "label", _("Result"), + // "description", _("A portion of the contents where the string was found"), + // NULL)); + columns = g_list_append (columns, + g_object_new (NEMO_TYPE_COLUMN, + "name", "search_result_count", + "attribute", "search_result_count", + "label", _("Hits"), + "description", _("How many times the search string appeared in the file"), + NULL)); + } + + return nemo_column_list_copy (columns); +} + + GList * nemo_get_common_columns (void) { @@ -229,12 +257,15 @@ nemo_get_common_columns (void) GList * nemo_get_all_columns (void) { - GList *columns = NULL; + GList *columns = NULL; + GList *with_search_columns = NULL; columns = g_list_concat (nemo_get_common_columns (), get_trash_columns ()); - return columns; + with_search_columns = g_list_concat (columns, get_search_columns ()); + + return with_search_columns; } GList * @@ -247,7 +278,11 @@ nemo_get_columns_for_file (NemoFile *file) if (file != NULL && nemo_file_is_in_trash (file)) { columns = g_list_concat (columns, get_trash_columns ()); - } + } else + if (file != NULL && nemo_file_is_in_search (file)) { + columns = g_list_concat (columns, + get_search_columns ()); + } return columns; } diff --git a/libnemo-private/nemo-debug.c b/libnemo-private/nemo-debug.c index 480c7fd97..ea448de53 100644 --- a/libnemo-private/nemo-debug.c +++ b/libnemo-private/nemo-debug.c @@ -53,6 +53,7 @@ static GDebugKey keys[] = { { "Actions", NEMO_DEBUG_ACTIONS }, { "Desktop", NEMO_DEBUG_DESKTOP }, { "Thumbnails", NEMO_DEBUG_THUMBNAILS }, + { "Search", NEMO_DEBUG_SEARCH }, { 0, } }; diff --git a/libnemo-private/nemo-debug.h b/libnemo-private/nemo-debug.h index a7458cd7c..59baa2534 100644 --- a/libnemo-private/nemo-debug.h +++ b/libnemo-private/nemo-debug.h @@ -49,7 +49,8 @@ typedef enum { NEMO_DEBUG_UNDO = 1 << 14, NEMO_DEBUG_ACTIONS = 1 << 15, NEMO_DEBUG_DESKTOP = 1 << 16, - NEMO_DEBUG_THUMBNAILS = 1 << 17 + NEMO_DEBUG_THUMBNAILS = 1 << 17, + NEMO_DEBUG_SEARCH = 1 << 18 } DebugFlags; void nemo_debug_set_flags (DebugFlags flags); diff --git a/libnemo-private/nemo-directory.c b/libnemo-private/nemo-directory.c index ab4beeb8d..96bd8502f 100644 --- a/libnemo-private/nemo-directory.c +++ b/libnemo-private/nemo-directory.c @@ -497,8 +497,6 @@ nemo_directory_new (GFile *location) directory = NEMO_DIRECTORY (g_object_new (NEMO_TYPE_DESKTOP_DIRECTORY, NULL)); } else if (eel_uri_is_search (uri)) { directory = NEMO_DIRECTORY (g_object_new (NEMO_TYPE_SEARCH_DIRECTORY, NULL)); - } else if (g_str_has_suffix (uri, NEMO_SAVED_SEARCH_EXTENSION)) { - directory = NEMO_DIRECTORY (nemo_search_directory_new_from_saved_search (uri)); } else { directory = NEMO_DIRECTORY (g_object_new (NEMO_TYPE_VFS_DIRECTORY, NULL)); } @@ -547,6 +545,18 @@ nemo_directory_is_in_recent (NemoDirectory *directory) return g_file_has_uri_scheme (directory->details->location, "recent"); } +gboolean +nemo_directory_is_in_search (NemoDirectory *directory) +{ + g_assert (NEMO_IS_DIRECTORY (directory)); + + if (directory->details->location == NULL) { + return FALSE; + } + + return g_file_has_uri_scheme (directory->details->location, "x-nemo-search"); +} + gboolean nemo_directory_is_in_favorites (NemoDirectory *directory) { diff --git a/libnemo-private/nemo-directory.h b/libnemo-private/nemo-directory.h index 669bfffe0..37c6b2ce4 100644 --- a/libnemo-private/nemo-directory.h +++ b/libnemo-private/nemo-directory.h @@ -219,6 +219,7 @@ gboolean nemo_directory_is_in_trash (NemoDirectory gboolean nemo_directory_is_in_recent (NemoDirectory *directory); gboolean nemo_directory_is_in_favorites (NemoDirectory *directory); gboolean nemo_directory_is_in_admin (NemoDirectory *directory); +gboolean nemo_directory_is_in_search (NemoDirectory *directory); /* Return false if directory contains anything besides a Nemo metafile. * Only valid if directory is monitored. Used by the Trash monitor. */ diff --git a/libnemo-private/nemo-file-private.h b/libnemo-private/nemo-file-private.h index 8bbca5b19..da774ac38 100644 --- a/libnemo-private/nemo-file-private.h +++ b/libnemo-private/nemo-file-private.h @@ -108,6 +108,8 @@ struct NemoFileDetails GList *mime_list; /* If this is a directory, the list of MIME types in it. */ + GHashTable *search_results; + /* Info you might get from a link (.desktop, .directory or nemo link) */ GIcon *custom_icon; char *activation_uri; diff --git a/libnemo-private/nemo-file-utilities.h b/libnemo-private/nemo-file-utilities.h index 6bee2321e..875c6785b 100644 --- a/libnemo-private/nemo-file-utilities.h +++ b/libnemo-private/nemo-file-utilities.h @@ -33,9 +33,6 @@ #define NORMAL_TEXT_WEIGHT PANGO_WEIGHT_NORMAL #define PINNED_TEXT_WEIGHT PANGO_WEIGHT_BOLD -#define NEMO_SAVED_SEARCH_EXTENSION ".savedSearch" -#define NEMO_SAVED_SEARCH_MIMETYPE "application/x-gnome-saved-search" - #define DEFAULT_NEMO_DIRECTORY_MODE (0755) #define DEFAULT_DESKTOP_DIRECTORY_MODE (0755) diff --git a/libnemo-private/nemo-file.c b/libnemo-private/nemo-file.c index 691485229..2716396e0 100644 --- a/libnemo-private/nemo-file.c +++ b/libnemo-private/nemo-file.c @@ -42,13 +42,13 @@ #include "nemo-metadata.h" #include "nemo-module.h" #include "nemo-search-directory.h" +#include "nemo-search-engine.h" #include "nemo-search-directory-file.h" #include "nemo-thumbnails.h" #include "nemo-trash-monitor.h" #include "nemo-vfs-file.h" #include "nemo-file-undo-operations.h" #include "nemo-file-undo-manager.h" -#include "nemo-saved-search-file.h" #include #include #include @@ -164,7 +164,9 @@ static GQuark attribute_name_q, attribute_where_q, attribute_link_target_q, attribute_volume_q, - attribute_free_space_q; + attribute_free_space_q, + attribute_search_result_snippet_q, + attribute_search_result_count_q; static void nemo_file_info_iface_init (NemoFileInfoInterface *iface); @@ -558,8 +560,6 @@ nemo_file_new_from_filename (NemoDirectory *directory, * that references a file like this. (See #349840) */ file = NEMO_FILE (g_object_new (NEMO_TYPE_VFS_FILE, NULL)); } - } else if (g_str_has_suffix (filename, NEMO_SAVED_SEARCH_EXTENSION)) { - file = NEMO_FILE (g_object_new (NEMO_TYPE_SAVED_SEARCH_FILE, NULL)); } else { file = NEMO_FILE (g_object_new (NEMO_TYPE_VFS_FILE, NULL)); } @@ -663,19 +663,11 @@ nemo_file_new_from_info (NemoDirectory *directory, GFileInfo *info) { NemoFile *file; - const char *mime_type; g_return_val_if_fail (NEMO_IS_DIRECTORY (directory), NULL); g_return_val_if_fail (info != NULL, NULL); - mime_type = g_file_info_get_content_type (info); - if (mime_type && - strcmp (mime_type, NEMO_SAVED_SEARCH_MIMETYPE) == 0) { - g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); - file = NEMO_FILE (g_object_new (NEMO_TYPE_SAVED_SEARCH_FILE, NULL)); - } else { - file = NEMO_FILE (g_object_new (NEMO_TYPE_VFS_FILE, NULL)); - } + file = NEMO_FILE (g_object_new (NEMO_TYPE_VFS_FILE, NULL)); file->details->directory = nemo_directory_ref (directory); @@ -3240,6 +3232,17 @@ compare_by_full_path (NemoFile *file_1, NemoFile *file_2) return compare_by_display_name (file_1, file_2); } +static gint +compare_by_search_result_count (NemoFile *file_1, + NemoFile *file_2, + gpointer search_dir) +{ + gint count_1 = nemo_file_get_search_result_count (file_1, search_dir); + gint count_2 = nemo_file_get_search_result_count (file_2, search_dir); + + return (count_1 == count_2) ? 0 : (count_1 - count_2); +} + static int nemo_file_compare_for_sort_internal (NemoFile *file_1, NemoFile *file_2, @@ -3319,7 +3322,8 @@ nemo_file_compare_for_sort (NemoFile *file_1, NemoFileSortType sort_type, gboolean directories_first, gboolean favorites_first, - gboolean reversed) + gboolean reversed, + gpointer search_dir) { int result; @@ -3382,6 +3386,12 @@ nemo_file_compare_for_sort (NemoFile *file_1, result = compare_by_full_path (file_1, file_2); } break; + case NEMO_FILE_SORT_BY_SEARCH_RESULT_COUNT: + result = compare_by_search_result_count (file_1, file_2, search_dir); + if (result == 0) { + result = compare_by_full_path (file_1, file_2); + } + break; case NEMO_FILE_SORT_NONE: default: g_return_val_if_reached (0); @@ -3401,7 +3411,8 @@ nemo_file_compare_for_sort_by_attribute_q (NemoFile *file_1, GQuark attribute, gboolean directories_first, gboolean favorites_first, - gboolean reversed) + gboolean reversed, + gpointer search_dir) { int result; @@ -3417,25 +3428,29 @@ nemo_file_compare_for_sort_by_attribute_q (NemoFile *file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, directories_first, favorites_first, - reversed); + reversed, + search_dir); } else if (attribute == attribute_size_q) { return nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_SIZE, directories_first, favorites_first, - reversed); + reversed, + search_dir); } else if (attribute == attribute_type_q) { return nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_TYPE, directories_first, favorites_first, - reversed); + reversed, + search_dir); } else if (attribute == attribute_detailed_type_q) { return nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_DETAILED_TYPE, directories_first, favorites_first, - reversed); + reversed, + search_dir); } else if (attribute == attribute_modification_date_q || attribute == attribute_date_modified_q || attribute == attribute_date_modified_with_time_q || @@ -3444,7 +3459,8 @@ nemo_file_compare_for_sort_by_attribute_q (NemoFile *file_1, NEMO_FILE_SORT_BY_MTIME, directories_first, favorites_first, - reversed); + reversed, + search_dir); } else if (attribute == attribute_accessed_date_q || attribute == attribute_date_accessed_q || attribute == attribute_date_accessed_full_q) { @@ -3452,7 +3468,8 @@ nemo_file_compare_for_sort_by_attribute_q (NemoFile *file_1, NEMO_FILE_SORT_BY_ATIME, directories_first, favorites_first, - reversed); + reversed, + search_dir); } else if (attribute == attribute_creation_date_q || attribute == attribute_date_created_q || attribute == attribute_date_created_with_time_q || @@ -3461,15 +3478,24 @@ nemo_file_compare_for_sort_by_attribute_q (NemoFile *file_1, NEMO_FILE_SORT_BY_BTIME, directories_first, favorites_first, - reversed); + reversed, + search_dir); } else if (attribute == attribute_trashed_on_q || attribute == attribute_trashed_on_full_q) { return nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_TRASHED_TIME, directories_first, favorites_first, - reversed); - } + reversed, + search_dir); + } else if (attribute == attribute_search_result_count_q) { + return nemo_file_compare_for_sort (file_1, file_2, + NEMO_FILE_SORT_BY_SEARCH_RESULT_COUNT, + directories_first, + favorites_first, + reversed, + search_dir); + } /* it is a normal attribute, compare by strings */ @@ -3505,13 +3531,15 @@ nemo_file_compare_for_sort_by_attribute (NemoFile *file_1, const char *attribute, gboolean directories_first, gboolean favorites_first, - gboolean reversed) + gboolean reversed, + gpointer search_dir) { return nemo_file_compare_for_sort_by_attribute_q (file_1, file_2, g_quark_from_string (attribute), directories_first, favorites_first, - reversed); + reversed, + search_dir); } @@ -7608,6 +7636,23 @@ nemo_file_is_in_favorites (NemoFile *file) return nemo_directory_is_in_favorites (file->details->directory); } +/** + * nemo_file_is_in_search + * + * Check if this file is a search result. + * @file: NemoFile representing the file in question. + * + * Returns: TRUE if @file is a search result. + * + **/ +gboolean +nemo_file_is_in_search (NemoFile *file) +{ + g_assert (NEMO_IS_FILE (file)); + + return nemo_directory_is_in_search (file->details->directory); +} + /** * nemo_file_is_unavailable_favorite * @@ -7941,7 +7986,7 @@ add_line (GString *string, const gchar *add, gboolean prefix_newline) } gchar * -nemo_file_construct_tooltip (NemoFile *file, NemoFileTooltipFlags flags) +nemo_file_construct_tooltip (NemoFile *file, NemoFileTooltipFlags flags, gpointer search_dir) { gchar *scheme = nemo_file_get_uri_scheme (file); gchar *nice = NULL; @@ -8036,6 +8081,17 @@ nemo_file_construct_tooltip (NemoFile *file, NemoFileTooltipFlags flags) } } + if (search_dir != NULL) { + gchar *snippet = nemo_file_get_search_result_snippet (file, search_dir); + if (snippet != NULL) { + gchar *ellipsized = g_strdup_printf ("%s", snippet); + string = add_line (string, "\n----------------------", FALSE); + string = add_line (string, ellipsized, TRUE); + g_free (snippet); + g_free (ellipsized); + } + } + ret = string->str; g_string_free (string, FALSE); @@ -8822,6 +8878,8 @@ nemo_file_class_init (NemoFileClass *class) attribute_link_target_q = g_quark_from_static_string ("link_target"); attribute_volume_q = g_quark_from_static_string ("volume"); attribute_free_space_q = g_quark_from_static_string ("free_space"); + attribute_search_result_snippet_q = g_quark_from_string ("search_result_snippet"); + attribute_search_result_count_q = g_quark_from_string ("search_result_count"); G_OBJECT_CLASS (class)->finalize = finalize; G_OBJECT_CLASS (class)->constructor = nemo_file_constructor; @@ -8929,6 +8987,91 @@ nemo_file_add_string_attribute (NemoFile *file, nemo_file_changed (file); } +void +nemo_file_add_search_result_data (NemoFile *file, + gpointer search_dir, + GPtrArray *search_hits) +{ + if (file->details->search_results == NULL) { + file->details->search_results = g_hash_table_new_full (NULL, NULL, + NULL, (GDestroyNotify) g_ptr_array_unref); + } + + if (!g_hash_table_replace (file->details->search_results, + search_dir, + g_ptr_array_ref (search_hits))) { + +#ifndef ENABLE_TRACKER // this is abnormal only in -advanced search. + g_warning ("Search hits directory already existed - %s", nemo_file_peek_name (file)); +#endif + } +} + +void +nemo_file_clear_search_result_data (NemoFile *file, + gpointer search_dir) +{ + g_return_if_fail (file->details->search_results != NULL); + + if (!g_hash_table_remove (file->details->search_results, + search_dir)) { + + g_warning ("Attempting to remove search hits that don't exist - %s", nemo_file_peek_name (file)); + } + + if (g_hash_table_size (file->details->search_results) == 0) { + g_hash_table_destroy (file->details->search_results); + file->details->search_results = NULL; + } +} + +static GPtrArray * +get_file_hit_list (NemoFile *file, gpointer search_dir) +{ + if (file->details->search_results == NULL) { + return NULL; + } + + return g_hash_table_lookup (file->details->search_results, search_dir); +} + +gint +nemo_file_get_search_result_count (NemoFile *file, gpointer search_dir) +{ + GPtrArray *hit_list = get_file_hit_list (file, search_dir); + + if (hit_list != NULL) { + return hit_list->len; + } + + return 0; +} + +gchar * +nemo_file_get_search_result_count_as_string (NemoFile *file, gpointer search_dir) +{ + gint count = nemo_file_get_search_result_count (file, search_dir); + + if (count > 0) { + return g_strdup_printf ("%d", count); + } + + return NULL; +} + +gchar * +nemo_file_get_search_result_snippet (NemoFile *file, gpointer search_dir) +{ + GPtrArray *hit_list = get_file_hit_list (file, search_dir); + + if (hit_list != NULL && hit_list->len > 0) { + SearchHit *hit = (SearchHit *) g_ptr_array_index (hit_list, 0); + return g_strdup (hit->snippet); + } + + return NULL; +} + static void nemo_file_invalidate_extension_info (NemoFile *file) { @@ -9059,12 +9202,12 @@ nemo_self_check_file (void) EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_1)->ref_count, 1); EEL_CHECK_INTEGER_RESULT (G_OBJECT (file_2)->ref_count, 1); - EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, FALSE) < 0, TRUE); - EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, TRUE) > 0, TRUE); - EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, FALSE) == 0, TRUE); - EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, TRUE, FALSE, FALSE) == 0, TRUE); - EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, TRUE) == 0, TRUE); - EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, TRUE, FALSE, TRUE) == 0, TRUE); + EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, FALSE, NULL) < 0, TRUE); + EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_2, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, TRUE, NULL) > 0, TRUE); + EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, FALSE, NULL) == 0, TRUE); + EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, TRUE, FALSE, FALSE, NULL) == 0, TRUE); + EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, FALSE, FALSE, TRUE, NULL) == 0, TRUE); + EEL_CHECK_BOOLEAN_RESULT (nemo_file_compare_for_sort (file_1, file_1, NEMO_FILE_SORT_BY_DISPLAY_NAME, TRUE, FALSE, TRUE, NULL) == 0, TRUE); diff --git a/libnemo-private/nemo-file.h b/libnemo-private/nemo-file.h index 04f90bc69..a7cb1043b 100644 --- a/libnemo-private/nemo-file.h +++ b/libnemo-private/nemo-file.h @@ -63,7 +63,8 @@ typedef enum { NEMO_FILE_SORT_BY_MTIME, NEMO_FILE_SORT_BY_ATIME, NEMO_FILE_SORT_BY_TRASHED_TIME, - NEMO_FILE_SORT_BY_BTIME + NEMO_FILE_SORT_BY_BTIME, + NEMO_FILE_SORT_BY_SEARCH_RESULT_COUNT } NemoFileSortType; typedef enum { @@ -227,6 +228,7 @@ gboolean nemo_file_is_archive (NemoFile *file); gboolean nemo_file_is_in_trash (NemoFile *file); gboolean nemo_file_is_in_recent (NemoFile *file); gboolean nemo_file_is_in_favorites (NemoFile *file); +gboolean nemo_file_is_in_search (NemoFile *file); gboolean nemo_file_is_unavailable_favorite (NemoFile *file); gboolean nemo_file_is_in_admin (NemoFile *file); gboolean nemo_file_is_in_desktop (NemoFile *file); @@ -434,19 +436,22 @@ int nemo_file_compare_for_sort (NemoFile NemoFileSortType sort_type, gboolean directories_first, gboolean favorites_first, - gboolean reversed); + gboolean reversed, + gpointer search_dir); int nemo_file_compare_for_sort_by_attribute (NemoFile *file_1, NemoFile *file_2, const char *attribute, gboolean directories_first, gboolean favorites_first, - gboolean reversed); + gboolean reversed, + gpointer search_dir); int nemo_file_compare_for_sort_by_attribute_q (NemoFile *file_1, NemoFile *file_2, GQuark attribute, gboolean directories_first, gboolean favorites_first, - gboolean reversed); + gboolean reversed, + gpointer search_dir); gboolean nemo_file_is_date_sort_attribute_q (GQuark attribute); int nemo_file_compare_display_name (NemoFile *file_1, @@ -518,7 +523,7 @@ char * nemo_file_get_owner_as_string (NemoFile *file, char * nemo_file_get_type_as_string (NemoFile *file); char * nemo_file_get_detailed_type_as_string (NemoFile *file); -gchar * nemo_file_construct_tooltip (NemoFile *file, NemoFileTooltipFlags flags); +gchar * nemo_file_construct_tooltip (NemoFile *file, NemoFileTooltipFlags flags, gpointer search_dir); gboolean nemo_file_has_thumbnail_access_problem (NemoFile *file); @@ -536,6 +541,13 @@ void nemo_file_set_is_favorite (NemoFile *file, gboolean favo void nemo_file_set_load_deferred_attrs (NemoFile *file, NemoFileLoadDeferredAttrs load_deferred_attrs); NemoFileLoadDeferredAttrs nemo_file_get_load_deferred_attrs (NemoFile *file); + +void nemo_file_add_search_result_data (NemoFile *file, gpointer search_dir, GPtrArray *search_hits); +void nemo_file_clear_search_result_data (NemoFile *file, gpointer search_dir); +gint nemo_file_get_search_result_count (NemoFile *file, gpointer search_dir); +gchar *nemo_file_get_search_result_count_as_string (NemoFile *file, gpointer search_dir); +gchar *nemo_file_get_search_result_snippet (NemoFile *file, gpointer search_dir); + /* Debugging */ void nemo_file_dump (NemoFile *file); diff --git a/libnemo-private/nemo-global-preferences.c b/libnemo-private/nemo-global-preferences.c index 4a3426485..e01893be0 100644 --- a/libnemo-private/nemo-global-preferences.c +++ b/libnemo-private/nemo-global-preferences.c @@ -45,6 +45,7 @@ GSettings *nemo_tree_sidebar_preferences; GSettings *nemo_window_state; GSettings *nemo_plugin_preferences; GSettings *nemo_menu_config_preferences; +GSettings *nemo_search_preferences; GSettings *gnome_lockdown_preferences; GSettings *gnome_background_preferences; GSettings *gnome_media_handling_preferences; @@ -332,6 +333,7 @@ nemo_global_preferences_init (void) nemo_tree_sidebar_preferences = g_settings_new("org.nemo.sidebar-panels.tree"); nemo_plugin_preferences = g_settings_new("org.nemo.plugins"); nemo_menu_config_preferences = g_settings_new("org.nemo.preferences.menu-config"); + nemo_search_preferences = g_settings_new("org.nemo.search"); gnome_lockdown_preferences = g_settings_new("org.cinnamon.desktop.lockdown"); gnome_background_preferences = g_settings_new("org.cinnamon.desktop.background"); gnome_media_handling_preferences = g_settings_new("org.cinnamon.desktop.media-handling"); @@ -360,6 +362,7 @@ nemo_global_preferences_finalize (void) g_object_unref (nemo_tree_sidebar_preferences); g_object_unref (nemo_plugin_preferences); g_object_unref (nemo_menu_config_preferences); + g_object_unref (nemo_search_preferences); g_object_unref (gnome_lockdown_preferences); g_object_unref (gnome_background_preferences); g_object_unref (gnome_media_handling_preferences); diff --git a/libnemo-private/nemo-global-preferences.h b/libnemo-private/nemo-global-preferences.h index f34ce3cbe..8461f912d 100644 --- a/libnemo-private/nemo-global-preferences.h +++ b/libnemo-private/nemo-global-preferences.h @@ -172,7 +172,6 @@ enum #define NEMO_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL "default-zoom-level" #define NEMO_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS "default-visible-columns" #define NEMO_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER "default-column-order" -#define NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS "search-visible-columns" enum { @@ -199,12 +198,6 @@ typedef enum #define NEMO_PREFERENCES_IMAGE_FILE_THUMBNAIL_LIMIT "thumbnail-limit" #define NEMO_PREFERENCES_INHERIT_SHOW_THUMBNAILS "inherit-show-thumbnails" -typedef enum -{ - NEMO_COMPLEX_SEARCH_BAR, - NEMO_SIMPLE_SEARCH_BAR -} NemoSearchBarMode; - #define NEMO_PREFERENCES_DESKTOP_FONT "font" #define NEMO_PREFERENCES_DESKTOP_HOME_VISIBLE "home-icon-visible" #define NEMO_PREFERENCES_DESKTOP_COMPUTER_VISIBLE "computer-icon-visible" @@ -265,10 +258,21 @@ typedef enum #define NEMO_PREFERENCES_CLICK_DOUBLE_PARENT_FOLDER "click-double-parent-folder" -#define NEMO_PREFERENCES_SAVED_SEARCHES "saved-searches" #define NEMO_PREFERENCES_SHOW_MIME_MAKE_EXECUTABLE "enable-mime-actions-make-executable" #define NEMO_PREFERENCES_DEFERRED_ATTR_PRELOAD_LIMIT "deferred-attribute-preload-limit" +#define NEMO_PREFERENCES_SEARCH_CONTENT_REGEX "search-content-use-regex" +#define NEMO_PREFERENCES_SEARCH_REGEX_FORMAT "search-regex-format" +#define NEMO_PREFERENCES_SEARCH_USE_RAW "search-content-use-raw" +#define NEMO_PREFERENCES_SEARCH_FILE_CASE "search-file-case-sensitive" +#define NEMO_PREFERENCES_SEARCH_CONTENT_CASE "search-content-case-sensitive" +#define NEMO_PREFERENCES_SEARCH_SKIP_FOLDERS "search-skip-folders" +#define NEMO_PREFERENCES_SEARCH_FILES_RECURSIVELY "search-files-recursively" +#define NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS "search-visible-columns" +#define NEMO_PREFERENCES_SEARCH_FILE_HISTORY "search-file-history" +#define NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY "search-content-history" +#define NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY_LENGTH "search-content-history-length" + void nemo_global_preferences_init (void); void nemo_global_preferences_finalize (void); char *nemo_global_preferences_get_default_folder_viewer_preference_as_iid (void); @@ -290,6 +294,7 @@ extern GSettings *nemo_tree_sidebar_preferences; extern GSettings *nemo_window_state; extern GSettings *nemo_plugin_preferences; extern GSettings *nemo_menu_config_preferences; +extern GSettings *nemo_search_preferences; extern GSettings *gnome_lockdown_preferences; extern GSettings *gnome_background_preferences; extern GSettings *gnome_media_handling_preferences; diff --git a/libnemo-private/nemo-query.c b/libnemo-private/nemo-query.c index 6758f8033..c7adb1375 100644 --- a/libnemo-private/nemo-query.c +++ b/libnemo-private/nemo-query.c @@ -30,10 +30,16 @@ #include struct NemoQueryDetails { - char *text; - char *location_uri; - GList *mime_types; + gchar *file_pattern; + gchar *content_pattern; + char *location_uri; + GList *mime_types; gboolean show_hidden; + gboolean file_case_sensitive; + gboolean content_case_sensitive; + gboolean use_regex; + gboolean count_hits; + gboolean recurse; }; G_DEFINE_TYPE (NemoQuery, nemo_query, G_TYPE_OBJECT); @@ -44,7 +50,8 @@ finalize (GObject *object) NemoQuery *query; query = NEMO_QUERY (object); - g_free (query->details->text); + g_free (query->details->file_pattern); + g_free (query->details->content_pattern); g_free (query->details->location_uri); G_OBJECT_CLASS (nemo_query_parent_class)->finalize (object); @@ -75,20 +82,40 @@ nemo_query_new (void) } char * -nemo_query_get_text (NemoQuery *query) +nemo_query_get_file_pattern (NemoQuery *query) { g_return_val_if_fail (NEMO_IS_QUERY (query), NULL); - return g_strdup (query->details->text); + return g_strdup (query->details->file_pattern); } void -nemo_query_set_text (NemoQuery *query, const char *text) +nemo_query_set_file_pattern (NemoQuery *query, const char *text) { g_return_if_fail (NEMO_IS_QUERY (query)); - g_free (query->details->text); - query->details->text = g_strstrip (g_strdup (text)); + g_free (query->details->file_pattern); + query->details->file_pattern = g_strstrip (g_strdup (text)); +} + +char * +nemo_query_get_content_pattern (NemoQuery *query) +{ + g_return_val_if_fail (NEMO_IS_QUERY (query), NULL); + + return g_strdup (query->details->content_pattern); +} + +void +nemo_query_set_content_pattern (NemoQuery *query, const char *text) +{ + g_return_if_fail (NEMO_IS_QUERY (query)); + + g_clear_pointer (&query->details->content_pattern, g_free); + + if (text && text[0] != '\0') { + query->details->content_pattern = g_strstrip (g_strdup (text)); + } } char * @@ -158,7 +185,7 @@ nemo_query_to_readable_string (NemoQuery *query) GFile *file; gchar *location_title, *readable; - if (!query || !query->details->text || query->details->text[0] == '\0') { + if (!query || !query->details->file_pattern || query->details->file_pattern[0] == '\0') { return g_strdup (_("Search")); } @@ -167,257 +194,85 @@ nemo_query_to_readable_string (NemoQuery *query) g_object_unref (file); - readable = g_strdup_printf (_("Search for \"%s\" in \"%s\""), query->details->text, location_title); + readable = g_strdup_printf (_("Search in \"%s\""), location_title); g_free (location_title); return readable; } -static char * -encode_home_uri (const char *uri) +gboolean +nemo_query_get_file_case_sensitive (NemoQuery *query) { - char *home_uri; - const char *encoded_uri; - - home_uri = nemo_get_home_directory_uri (); - - 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); + g_return_val_if_fail (NEMO_IS_QUERY (query), FALSE); + return query->details->file_case_sensitive; } -static char * -decode_home_uri (const char *uri) +void +nemo_query_set_file_case_sensitive (NemoQuery *query, gboolean case_sensitive) { - char *home_uri; - char *decoded_uri; - - if (g_str_has_prefix (uri, "file:")) { - decoded_uri = g_strdup (uri); - } else { - home_uri = nemo_get_home_directory_uri (); - - decoded_uri = g_strconcat (home_uri, "/", uri, NULL); - - g_free (home_uri); - } - - return decoded_uri; -} - - -typedef struct { - NemoQuery *query; - gboolean in_text; - gboolean in_location; - gboolean in_mimetypes; - gboolean in_mimetype; - gboolean error; -} ParserInfo; + g_return_if_fail (NEMO_IS_QUERY (query)); -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; + query->details->file_case_sensitive = case_sensitive; } -static void -end_element_cb (GMarkupParseContext *ctx, - const char *element_name, - gpointer user_data, - GError **err) +gboolean +nemo_query_get_content_case_sensitive (NemoQuery *query) { - 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; + g_return_val_if_fail (NEMO_IS_QUERY (query), FALSE); + return query->details->content_case_sensitive; } -static void -text_cb (GMarkupParseContext *ctx, - const char *text, - gsize text_len, - gpointer user_data, - GError **err) +void +nemo_query_set_content_case_sensitive (NemoQuery *query, gboolean case_sensitive) { - ParserInfo *info; - char *t, *uri; - - info = (ParserInfo *) user_data; - - t = g_strndup (text, text_len); - - if (info->in_text) { - nemo_query_set_text (info->query, t); - } else if (info->in_location) { - uri = decode_home_uri (t); - nemo_query_set_location (info->query, uri); - g_free (uri); - } else if (info->in_mimetypes && info->in_mimetype) { - nemo_query_add_mime_type (info->query, t); - } - - g_free (t); + g_return_if_fail (NEMO_IS_QUERY (query)); + query->details->content_case_sensitive = case_sensitive; } -static void -error_cb (GMarkupParseContext *ctx, - GError *err, - gpointer user_data) +gboolean +nemo_query_get_use_regex (NemoQuery *query) { - ParserInfo *info; - - info = (ParserInfo *) user_data; - - info->error = TRUE; + g_return_val_if_fail (NEMO_IS_QUERY (query), FALSE); + return query->details->use_regex; } -static GMarkupParser parser = { - start_element_cb, - end_element_cb, - text_cb, - NULL, - error_cb -}; - - -static NemoQuery * -nemo_query_parse_xml (char *xml, gsize xml_len) +void +nemo_query_set_use_regex (NemoQuery *query, gboolean use_regex) { - ParserInfo info = { NULL }; - GMarkupParseContext *ctx; - - info.query = nemo_query_new (); - info.in_text = FALSE; - info.error = FALSE; - - ctx = g_markup_parse_context_new (&parser, 0, &info, NULL); - g_markup_parse_context_parse (ctx, xml, xml_len, NULL); - g_markup_parse_context_free (ctx); - - if (info.error) { - g_object_unref (info.query); - return NULL; - } + g_return_if_fail (NEMO_IS_QUERY (query)); - return info.query; + query->details->use_regex = use_regex; } - -NemoQuery * -nemo_query_load (char *file) +gboolean +nemo_query_get_count_hits (NemoQuery *query) { - NemoQuery *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); - - if (xml_len == 0) { - return NULL; - } - - query = nemo_query_parse_xml (xml, xml_len); - g_free (xml); - - return query; + g_return_val_if_fail (NEMO_IS_QUERY (query), FALSE); + return query->details->count_hits; } -static char * -nemo_query_to_xml (NemoQuery *query) +void +nemo_query_set_count_hits (NemoQuery *query, gboolean count_hits) { - GString *xml; - char *text; - char *uri; - char *mimetype; - GList *l; - - xml = g_string_new (""); - g_string_append (xml, - "\n" - "\n"); - - text = g_markup_escape_text (query->details->text, -1); - g_string_append_printf (xml, " %s\n", text); - g_free (text); - - if (query->details->location_uri) { - uri = encode_home_uri (query->details->location_uri); - g_string_append_printf (xml, " %s\n", uri); - g_free (uri); - } - - if (query->details->mime_types) { - g_string_append (xml, " \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, " %s\n", mimetype); - g_free (mimetype); - } - g_string_append (xml, " \n"); - } - - g_string_append (xml, "\n"); + g_return_if_fail (NEMO_IS_QUERY (query)); - return g_string_free (xml, FALSE); + query->details->count_hits = count_hits; } gboolean -nemo_query_save (NemoQuery *query, char *file) +nemo_query_get_recurse (NemoQuery *query) { - char *xml; - GError *err = NULL; - gboolean res; - + g_return_val_if_fail (NEMO_IS_QUERY (query), FALSE); + return query->details->recurse; +} - res = TRUE; - xml = nemo_query_to_xml (query); - g_file_set_contents (file, xml, strlen (xml), &err); - g_free (xml); +void +nemo_query_set_recurse (NemoQuery *query, gboolean recurse) +{ + g_return_if_fail (NEMO_IS_QUERY (query)); - if (err != NULL) { - res = FALSE; - g_error_free (err); - } - return res; + query->details->recurse = recurse; } + diff --git a/libnemo-private/nemo-query.h b/libnemo-private/nemo-query.h index df074b148..c994d5e31 100644 --- a/libnemo-private/nemo-query.h +++ b/libnemo-private/nemo-query.h @@ -49,8 +49,11 @@ gboolean nemo_query_enabled (void); NemoQuery* nemo_query_new (void); -char * nemo_query_get_text (NemoQuery *query); -void nemo_query_set_text (NemoQuery *query, const char *text); +char * nemo_query_get_file_pattern (NemoQuery *query); +void nemo_query_set_file_pattern (NemoQuery *query, const char *text); + +char * nemo_query_get_content_pattern (NemoQuery *query); +void nemo_query_set_content_pattern (NemoQuery *query, const char *text); char * nemo_query_get_location (NemoQuery *query); void nemo_query_set_location (NemoQuery *query, const char *uri); @@ -62,6 +65,18 @@ void nemo_query_add_mime_type (NemoQuery *query, const char *mime void nemo_query_set_show_hidden (NemoQuery *query, gboolean hidden); gboolean nemo_query_get_show_hidden (NemoQuery *query); +gboolean nemo_query_get_file_case_sensitive (NemoQuery *query); +void nemo_query_set_file_case_sensitive (NemoQuery *query, gboolean case_sensitive); + +gboolean nemo_query_get_content_case_sensitive (NemoQuery *query); +void nemo_query_set_content_case_sensitive (NemoQuery *query, gboolean case_sensitive); + +gboolean nemo_query_get_use_regex (NemoQuery *query); +void nemo_query_set_use_regex (NemoQuery *query, gboolean use_regex); + +gboolean nemo_query_get_recurse (NemoQuery *query); +void nemo_query_set_recurse (NemoQuery *query, gboolean recurse); + char * nemo_query_to_readable_string (NemoQuery *query); NemoQuery *nemo_query_load (char *file); gboolean nemo_query_save (NemoQuery *query, char *file); diff --git a/libnemo-private/nemo-saved-search-file.c b/libnemo-private/nemo-saved-search-file.c deleted file mode 100644 index e0d9b8dfc..000000000 --- a/libnemo-private/nemo-saved-search-file.c +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- - - nemo-saved-search-file.h: Subclass of NemoVFSFile 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., 51 Franklin Street - Suite 500, - Boston, MA 02110-1335, USA. - - Author: Alexander Larsson -*/ -#include -#include "nemo-saved-search-file.h" -#include "nemo-file-private.h" - -G_DEFINE_TYPE(NemoSavedSearchFile, nemo_saved_search_file, NEMO_TYPE_VFS_FILE) - - -static void -nemo_saved_search_file_init (NemoSavedSearchFile *search_file) -{ -} - -static void -nemo_saved_search_file_class_init (NemoSavedSearchFileClass * klass) -{ - NemoFileClass *file_class; - - file_class = NEMO_FILE_CLASS (klass); - - file_class->default_file_type = G_FILE_TYPE_DIRECTORY; -} - diff --git a/libnemo-private/nemo-saved-search-file.h b/libnemo-private/nemo-saved-search-file.h deleted file mode 100644 index 50c455061..000000000 --- a/libnemo-private/nemo-saved-search-file.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- - - nemo-saved-search-file.h: Subclass of NemoVFSFile 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., 51 Franklin Street - Suite 500, - Boston, MA 02110-1335, USA. - - Author: Alexander Larsson -*/ - -#ifndef NEMO_SAVED_SEARCH_FILE_H -#define NEMO_SAVED_SEARCH_FILE_H - -#include - -#define NEMO_TYPE_SAVED_SEARCH_FILE nemo_saved_search_file_get_type() -#define NEMO_SAVED_SEARCH_FILE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_TYPE_SAVED_SEARCH_FILE, NemoSavedSearchFile)) -#define NEMO_SAVED_SEARCH_FILE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), NEMO_TYPE_SAVED_SEARCH_FILE, NemoSavedSearchFileClass)) -#define NEMO_IS_SAVED_SEARCH_FILE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NEMO_TYPE_SAVED_SEARCH_FILE)) -#define NEMO_IS_SAVED_SEARCH_FILE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), NEMO_TYPE_SAVED_SEARCH_FILE)) -#define NEMO_SAVED_SEARCH_FILE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), NEMO_TYPE_SAVED_SEARCH_FILE, NemoSavedSearchFileClass)) - - -typedef struct NemoSavedSearchFileDetails NemoSavedSearchFileDetails; - -typedef struct { - NemoFile parent_slot; -} NemoSavedSearchFile; - -typedef struct { - NemoFileClass parent_slot; -} NemoSavedSearchFileClass; - -GType nemo_saved_search_file_get_type (void); - -#endif /* NEMO_SAVED_SEARCH_FILE_H */ diff --git a/libnemo-private/nemo-search-directory.c b/libnemo-private/nemo-search-directory.c index 601cd39aa..f6f3544cc 100644 --- a/libnemo-private/nemo-search-directory.c +++ b/libnemo-private/nemo-search-directory.c @@ -38,7 +38,6 @@ struct NemoSearchDirectoryDetails { NemoQuery *query; - char *saved_search_uri; gboolean modified; NemoSearchEngine *engine; @@ -123,6 +122,9 @@ reset_file_list (NemoSearchDirectory *search) monitor = monitor_list->data; nemo_file_monitor_remove (file, monitor); } + + nemo_file_clear_search_result_data (file, (gpointer) search); + } nemo_file_list_free (search->details->files); @@ -472,22 +474,18 @@ search_engine_hits_added (NemoSearchEngine *engine, GList *hits, GList *hit_list; GList *file_list; NemoFile *file; - char *uri; + FileSearchResult *fsr; SearchMonitor *monitor; GList *monitor_list; file_list = NULL; for (hit_list = hits; hit_list != NULL; hit_list = hit_list->next) { - uri = hit_list->data; + fsr = (FileSearchResult *) hit_list->data; + + file = nemo_file_get_by_uri (fsr->uri); + nemo_file_add_search_result_data (file, (gpointer) search, fsr->hits); - if (g_str_has_suffix (uri, NEMO_SAVED_SEARCH_EXTENSION)) { - /* Never return saved searches themselves as hits */ - continue; - } - - file = nemo_file_get_by_uri (uri); - for (monitor_list = search->details->monitor_list; monitor_list; monitor_list = monitor_list->next) { monitor = monitor_list->data; @@ -727,8 +725,6 @@ search_finalize (GObject *object) search = NEMO_SEARCH_DIRECTORY (object); - g_free (search->details->saved_search_uri); - g_free (search->details); G_OBJECT_CLASS (nemo_search_directory_parent_class)->finalize (object); @@ -814,71 +810,8 @@ nemo_search_directory_get_query (NemoSearchDirectory *search) return NULL; } -NemoSearchDirectory * -nemo_search_directory_new_from_saved_search (const char *uri) -{ - NemoSearchDirectory *search; - NemoQuery *query; - char *file; - - search = NEMO_SEARCH_DIRECTORY (g_object_new (NEMO_TYPE_SEARCH_DIRECTORY, NULL)); - - search->details->saved_search_uri = g_strdup (uri); - - file = g_filename_from_uri (uri, NULL, NULL); - if (file != NULL) { - query = nemo_query_load (file); - if (query != NULL) { - nemo_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 -nemo_search_directory_is_saved_search (NemoSearchDirectory *search) -{ - return search->details->saved_search_uri != NULL; -} - gboolean nemo_search_directory_is_modified (NemoSearchDirectory *search) { return search->details->modified; } - -void -nemo_search_directory_save_to_file (NemoSearchDirectory *search, - const char *save_file_uri) -{ - char *file; - - file = g_filename_from_uri (save_file_uri, NULL, NULL); - if (file == NULL) { - return; - } - - if (search->details->query != NULL) { - nemo_query_save (search->details->query, file); - } - - g_free (file); -} - -void -nemo_search_directory_save_search (NemoSearchDirectory *search) -{ - if (search->details->saved_search_uri == NULL) { - return; - } - - nemo_search_directory_save_to_file (search, - search->details->saved_search_uri); - search->details->modified = FALSE; -} diff --git a/libnemo-private/nemo-search-engine-advanced.c b/libnemo-private/nemo-search-engine-advanced.c new file mode 100644 index 000000000..64a19d40a --- /dev/null +++ b/libnemo-private/nemo-search-engine-advanced.c @@ -0,0 +1,1150 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Nemo 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. + * + * Nemo 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., 51 Franklin Street - Suite 500, + * Boston, MA 02110-1335, USA. + * + */ + +#include +#include "nemo-file.h" +#include "nemo-directory.h" +#include "nemo-file-utilities.h" +#include "nemo-search-engine-advanced.h" +#include "nemo-global-preferences.h" + +#include +#include +#include +#include + +#define DEBUG_FLAG NEMO_DEBUG_SEARCH +#include "nemo-debug.h" + +#define SEARCH_HELPER_GROUP "Nemo Search Cat Helper" + +#define FILE_SEARCH_ONLY_BATCH_SIZE 500 +#define CONTENT_SEARCH_BATCH_SIZE 1 +#define SNIPPET_EXTEND_SIZE 100 + +typedef struct { + gchar *exec_format; + gint priority; + /* future? */ +} SearchHelper; + +typedef struct { + NemoSearchEngineAdvanced *engine; + GCancellable *cancellable; + + GList *mime_types; + gchar **words; + gboolean *word_strstr; + gboolean words_and; + + GQueue *directories; /* GFiles */ + + GHashTable *visited; + GHashTable *skip_folders; + + gint n_processed_files; + GRegex *match_re; + GRegex *newline_re; + + GMutex hit_list_lock; + GList *hit_list; // holds SearchHitData + + gboolean show_hidden; + gboolean count_hits; + gboolean recurse; + gboolean file_case_sensitive; + gboolean location_supports_content_search; + + GTimer *timer; +} SearchThreadData; + +struct NemoSearchEngineAdvancedDetails { + NemoQuery *query; + + SearchThreadData *active_search; + + gboolean query_finished; +}; + +G_DEFINE_TYPE (NemoSearchEngineAdvanced, nemo_search_engine_advanced, + NEMO_TYPE_SEARCH_ENGINE); + +static GHashTable *search_helpers = NULL; + +static void +search_helper_free (SearchHelper *helper) +{ + g_free (helper->exec_format); + g_slice_free (SearchHelper, helper); +} + +static GList * +get_cat_helper_directories (void) +{ + gchar **data_dirs; + gchar *path; + guint i; + GList *helper_dirs = NULL; + + data_dirs = (gchar **) g_get_system_data_dirs (); + + for (i = 0; i < g_strv_length (data_dirs); i++) { + path = g_build_filename (data_dirs[i], "nemo", "search-helpers", NULL); + + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_free (path); + continue; + } + + helper_dirs = g_list_prepend (helper_dirs, path); + } + + path = g_build_filename (g_get_user_data_dir (), "nemo", "search-helpers", NULL); + + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_mkdir_with_parents (path, DEFAULT_NEMO_DIRECTORY_MODE); + } + + helper_dirs = g_list_prepend (helper_dirs, path); + + return helper_dirs; +} + +static void +process_search_helper_file (const gchar *path) +{ + GKeyFile *key_file; + gchar *exec_format = NULL; + gchar *try_exec = NULL; + gchar *abs_try_path = NULL; + gchar **mime_types = NULL; + gint priority = 100; + gsize n_types; + gint i; + + DEBUG ("Loading search helper: %s", path); + + key_file = g_key_file_new(); + + g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL); + + if (!g_key_file_has_group (key_file, SEARCH_HELPER_GROUP)) { + g_warning ("Nemo search_helper file is missing group '%s' - %s", SEARCH_HELPER_GROUP, path); + goto done; + } + + if (!g_key_file_has_key (key_file, SEARCH_HELPER_GROUP, "TryExec", NULL) || + !g_key_file_has_key (key_file, SEARCH_HELPER_GROUP, "Exec", NULL) || + !g_key_file_has_key (key_file, SEARCH_HELPER_GROUP, "MimeType", NULL)) { + g_warning ("Nemo search_helper file is missing mandatory fields - you must have TryExec, Exec and MimeType, " + "and MimeType list must terminate with a ; - %s", path); + goto done; + } + + try_exec = g_key_file_get_string (key_file, SEARCH_HELPER_GROUP, "TryExec", NULL); + abs_try_path = g_find_program_in_path (try_exec); + if (!abs_try_path) { + g_message ("Skipping search helper '%s' - program is not available (%s)", path, try_exec); + goto done; + } + + n_types = 0; + mime_types = g_key_file_get_string_list (key_file, SEARCH_HELPER_GROUP, "MimeType", &n_types, NULL); + + if (n_types == 0) { + g_warning ("Nemo search_helper no mimetypes defined - %s", path); + goto done; + } + + exec_format = g_key_file_get_string (key_file, SEARCH_HELPER_GROUP, "Exec", NULL); + + if (exec_format == NULL) { + g_warning ("Nemo search_helper could not retrieve Exec field - %s", path); + goto done; + } + + if (g_key_file_has_key (key_file, SEARCH_HELPER_GROUP, "Priority", NULL)) { + priority = g_key_file_get_integer (key_file, SEARCH_HELPER_GROUP, "Priority", NULL); + + // Failure sets the return to 0, make it 100 for the default when there's no key. + if (priority == 0) { + priority = 100; + } + } + + /* The helper table is keyed to mimetype strings, which will point to the same value */ + + for (i = 0; i < n_types; i++) { + SearchHelper *helper, *existing; + const gchar *mime_type; + + mime_type = mime_types[i]; + + existing = g_hash_table_lookup (search_helpers, mime_type); + if (existing && existing->priority > priority) { + g_message ("Existing nemo search_helper for '%s' has higher priority than a new one (%s), ignoring the new one.", mime_type, path); + continue; + } else if (existing) { + g_message ("Replacing existing nemo search_helper for '%s' with %s based on priority.", mime_type, path); + } + + helper = g_slice_new0 (SearchHelper); + helper->exec_format = g_strdup (exec_format); + helper->priority = priority; + + g_hash_table_replace (search_helpers, g_strdup (mime_type), helper); + } + +done: + g_key_file_free (key_file); + g_free (exec_format); + g_free (try_exec); + g_free (abs_try_path); + + if (mime_types != NULL) { + g_strfreev (mime_types); + } +} + +static void +initialize_search_helpers (NemoSearchEngineAdvanced *engine) +{ + GList *dir_list, *d_iter; + + search_helpers = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) search_helper_free); + + dir_list = get_cat_helper_directories (); + + for (d_iter = dir_list; d_iter != NULL; d_iter = d_iter->next) { + GError *error; + GDir *dir; + const gchar *filename; + const gchar *path; + + path = (const gchar *) d_iter->data; + error = NULL; + + dir = g_dir_open (path, 0, &error); + if (error != NULL) { + g_warning ("Could not load helper dir (%s): %s", path, error->message); + g_clear_error (&error); + continue; + } + + while ((filename = g_dir_read_name (dir)) != NULL) { + gchar *file_path; + + if (!g_str_has_suffix (filename, ".nemo_search_helper")) { + continue; + } + + file_path = g_build_filename (path, filename, NULL); + process_search_helper_file (file_path); + g_free (file_path); + } + + g_dir_close (dir); + } + + g_list_free_full (dir_list, g_free); +} + +void free_search_helpers (void) +{ + if (search_helpers != NULL) { + g_hash_table_destroy (search_helpers); + search_helpers = NULL; + } +} + +static void +finalize (GObject *object) +{ + NemoSearchEngineAdvanced *simple; + + simple = NEMO_SEARCH_ENGINE_ADVANCED (object); + + if (simple->details->query) { + g_object_unref (simple->details->query); + simple->details->query = NULL; + } + + G_OBJECT_CLASS (nemo_search_engine_advanced_parent_class)->finalize (object); +} + +/** + * function modified taken from glib2 / gstrfuncs.c + */ +static gchar** +strsplit_esc_n (const gchar *string, + const gchar delimiter, + const gchar escape, + gint max_tokens, + gint *n_tokens) +{ + GSList *string_list = NULL, *slist; + gchar **str_array; + guint n = 0; + const gchar *remainder, *s; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (delimiter != '\0', NULL); + + if (max_tokens < 1) + max_tokens = G_MAXINT; + + remainder = string; + s = remainder; + while (s && *s) { + if (*s == delimiter) break; + else if (*s == escape) { + s++; + if (*s == 0) break; + } + s++; + } + if (*s == 0) s = NULL; + if (s) { + while (--max_tokens && s) { + gsize len; + + len = s - remainder; + string_list = g_slist_prepend (string_list, + g_strndup (remainder, len)); + n++; + remainder = s + 1; + + s = remainder; + while (s && *s) { + if (*s == delimiter) break; + else if (*s == escape) { + s++; + if (*s == 0) break; + } + s++; + } + if (*s == 0) s = NULL; + } + } + if (*string) { + n++; + string_list = g_slist_prepend (string_list, g_strdup (remainder)); + } + *n_tokens = n; + str_array = g_new (gchar*, n + 1); + + str_array[n--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[n--] = slist->data; + + g_slist_free (string_list); + + return str_array; +} + +static SearchThreadData * +search_thread_data_new (NemoSearchEngineAdvanced *engine, + NemoQuery *query) +{ + GRegexCompileFlags flags; + GError *error; + SearchThreadData *data; + char *text, *cased, *normalized, *uri; + GFile *location; + gint n = 1, i; + gchar *format; + + data = g_new0 (SearchThreadData, 1); + + data->show_hidden = nemo_query_get_show_hidden (query); + data->engine = engine; + data->directories = g_queue_new (); + data->visited = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + uri = nemo_query_get_location (query); + location = NULL; + if (uri != NULL) { + location = g_file_new_for_uri (uri); + data->location_supports_content_search = g_file_is_native (location); + g_free (uri); + } + if (location == NULL) { + location = g_file_new_for_path ("/"); + } + g_queue_push_tail (data->directories, location); + + text = nemo_query_get_file_pattern (query); + normalized = g_utf8_normalize (text, -1, G_NORMALIZE_NFD); + + data->file_case_sensitive = nemo_query_get_file_case_sensitive (query); + + if (!data->file_case_sensitive) { + cased = g_utf8_strdown (normalized, -1); + } else { + cased = g_strdup (normalized); + } + + data->words = strsplit_esc_n (cased, ' ', '\\', -1, &n); + g_free (text); + g_free (cased); + g_free (normalized); + + data->word_strstr = g_malloc(sizeof(gboolean)*n); + data->words_and = TRUE; + for (i = 0; data->words[i] != NULL; i++) { + data->word_strstr[i]=TRUE; + text = data->words[i]; + while(*text!=0) { + if(*text=='\\' || *text=='?' || *text=='*') { + data->word_strstr[i]=FALSE; + break; + } + text++; + } + if (!data->word_strstr[i]) data->words_and = FALSE; + } + + data->skip_folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + gchar **folders_array = g_settings_get_strv (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_SKIP_FOLDERS); + for (i = 0; i < g_strv_length (folders_array); i++) { + DEBUG ("Ignoring folder in search: '%s'", folders_array[i]); + g_hash_table_add (data->skip_folders, g_strdup (folders_array[i])); + } + g_strfreev (folders_array); + + data->count_hits = FALSE; + + gchar **saved_search_columns = g_settings_get_strv (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS); + if (g_strv_contains ((const gchar * const *) saved_search_columns, "search_result_count")) { + data->count_hits = TRUE; + DEBUG ("Counting search hits"); + } else { + DEBUG ("Not counting search hits"); + } + g_strfreev (saved_search_columns); + + data->mime_types = nemo_query_get_mime_types (query); + data->recurse = nemo_query_get_recurse (query); + data->file_case_sensitive = nemo_query_get_file_case_sensitive (query); + + data->cancellable = g_cancellable_new (); + data->timer = g_timer_new (); + + g_mutex_init (&data->hit_list_lock); + + gchar *content_pattern = nemo_query_get_content_pattern (query); + + if (content_pattern != NULL) { + gchar *escaped; + + if (nemo_query_get_use_regex (query)) { + escaped = g_strdup (content_pattern); + } else { + escaped = g_regex_escape_string (content_pattern, -1); + } + + flags = G_REGEX_MULTILINE | + G_REGEX_OPTIMIZE; + + format = g_settings_get_string (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_REGEX_FORMAT); + + if (g_strcmp0 (format, "javascript") == 0) { + flags |= G_REGEX_JAVASCRIPT_COMPAT; + } + + if (g_settings_get_boolean (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_USE_RAW)) { + flags |= G_REGEX_RAW; + } + + if (!nemo_query_get_content_case_sensitive (query)) { + flags |= G_REGEX_CASELESS; + } + + g_free (format); + + error = NULL; + + data->match_re= g_regex_new (escaped, + flags, + 0, + &error); + + if (data->match_re == NULL) { + if (error != NULL) { + // TODO: Maybe do something in the ui, make the info bar red? + g_warning ("Pattern /%s/ is invalid: code %d - %s", escaped, error->code, error->message); + } + g_clear_error (&error); + } else { + DEBUG ("regex is '%s'", g_regex_get_pattern (data->match_re)); + } + + g_free (escaped); + + data->newline_re = g_regex_new ("\\n{2,}", + G_REGEX_OPTIMIZE, + 0, + &error); + + if (data->newline_re == NULL) { + if (error != NULL) { + // TODO: Maybe do something in the ui, make the info bar red? + g_warning ("Whitespace match regex is invalid: code %d - %s", error->code, error->message); + } + g_clear_error (&error); + } + } + + g_free (content_pattern); + + return data; +} + +static void +search_thread_data_free (SearchThreadData *data) +{ + g_queue_foreach (data->directories, + (GFunc)g_object_unref, NULL); + g_queue_free (data->directories); + g_hash_table_destroy (data->visited); + g_hash_table_destroy (data->skip_folders); + g_object_unref (data->cancellable); + g_strfreev (data->words); + g_free (data->word_strstr); + g_list_free_full (data->mime_types, g_free); + g_list_free_full (data->hit_list, (GDestroyNotify) file_search_result_free); + g_clear_pointer (&data->match_re, g_regex_unref); + g_clear_pointer (&data->newline_re, g_regex_unref); + g_timer_destroy (data->timer); + g_mutex_clear (&data->hit_list_lock); + + g_free (data); +} + +static gboolean +search_thread_done_idle (gpointer user_data) +{ + SearchThreadData *data; + + data = user_data; + + if (!g_cancellable_is_cancelled (data->cancellable)) { + nemo_search_engine_finished (NEMO_SEARCH_ENGINE (data->engine)); + data->engine->details->active_search = NULL; + } + + DEBUG ("Search took: %f seconds", g_timer_elapsed (data->timer, NULL)); + search_thread_data_free (data); + + return FALSE; +} + +typedef struct { + GList *hit_list; + SearchThreadData *thread_data; +} SearchHits; + + +static gboolean +search_thread_add_hits_idle (gpointer user_data) +{ + SearchHits *hits; + + hits = user_data; + + if (!g_cancellable_is_cancelled (hits->thread_data->cancellable)) { + nemo_search_engine_hits_added (NEMO_SEARCH_ENGINE (hits->thread_data->engine), + hits->hit_list); + } + + g_list_free_full (hits->hit_list, (GDestroyNotify) file_search_result_free); + g_free (hits); + + return FALSE; +} + +static void +send_batch (SearchThreadData *data) +{ + SearchHits *hits; + + g_mutex_lock (&data->hit_list_lock); + data->n_processed_files = 0; + + if (data->hit_list) { + hits = g_new0 (SearchHits, 1); + hits->hit_list = data->hit_list; + hits->thread_data = data; + g_idle_add (search_thread_add_hits_idle, hits); + } + data->hit_list = NULL; + g_mutex_unlock (&data->hit_list_lock); +} + +static gboolean +strwildcardcmp(char *a, char *b) +{ + if (*a == 0 && *b == 0) return TRUE; + while(*a!=0 && *b!=0) { + if(*a=='\\') { // escaped character + a++; + if (*a != *b) return FALSE; + } + else { + if (*a=='*') { + if(*(a+1)==0) return TRUE; + if(*b==0) return FALSE; + if (strwildcardcmp(a+1, b) || strwildcardcmp(a, b+1)) return TRUE; + else return FALSE; + } + else if (*a!='?' && (*a != *b)) return FALSE; + } + a++; + b++; + } + if ((*a == 0 && *b == 0) || (*a=='*' && *(a+1)==0)) return TRUE; + return FALSE; +} + +#define STD_ATTRIBUTES \ + G_FILE_ATTRIBUTE_STANDARD_NAME "," \ + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \ + G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," \ + G_FILE_ATTRIBUTE_STANDARD_TYPE "," \ + G_FILE_ATTRIBUTE_ID_FILE + +static GInputStream * +get_stream_from_helper (SearchHelper *helper, + GFile *file, + GSubprocess **proc, + GError **error) +{ + GSubprocess *helper_proc; + GSubprocessFlags flags; + GInputStream *stream; + GString *command_line; + gchar **argv; + gchar *ptr, *path, *quoted; + + path = g_file_get_path (file); + quoted = g_strdup_printf ("\"%s\"", path); + g_free (path); + + command_line = g_string_new (helper->exec_format); + + ptr = g_strstr_len (command_line->str, -1, "%s"); + if (ptr != NULL) { + g_string_erase (command_line, ptr - command_line->str, 2); + g_string_insert (command_line, ptr - command_line->str, quoted); + } else { + g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED, + "Search helper exec field missing %%s need to insert file path - '%s'", command_line->str); + g_string_free (command_line, TRUE); + g_free (quoted); + return NULL; + } + + if (!g_shell_parse_argv (command_line->str, + NULL, + &argv, + error)) { + g_string_free (command_line, TRUE); + g_free (quoted); + } + + flags = G_SUBPROCESS_FLAGS_STDOUT_PIPE; + + if (!DEBUGGING) { + flags |= G_SUBPROCESS_FLAGS_STDERR_SILENCE; + } + + helper_proc = g_subprocess_newv ((const gchar * const *) argv, flags, error); + + stream = NULL; + + if (helper_proc != NULL) { + stream = g_subprocess_get_stdout_pipe (helper_proc); + *proc = helper_proc; + } + + g_strfreev (argv); + g_free (quoted); + g_string_free (command_line, TRUE); + + return stream; +} + +static gchar * +create_snippet (GMatchInfo *match_info, + const gchar *contents, + gssize total_length) +{ + gint start, end, new_start, new_end; + gchar *snippet = NULL; + + start = end = -1; + + if (g_match_info_fetch_pos (match_info, 0, &start, &end) && start >= 0) { + // Extend the snipped forwards and back a bit to give context. + new_start = MAX (0, start - SNIPPET_EXTEND_SIZE); + new_end = MIN (end + SNIPPET_EXTEND_SIZE, total_length - 1); + + gchar *matched_str = g_match_info_fetch (match_info, 0); + GString *marked_up = g_string_new (NULL); + gchar *escaped_str = NULL; + + escaped_str = g_markup_escape_text ((const gchar *) contents + new_start, start - new_start); + g_string_append (marked_up, escaped_str); + g_string_append (marked_up, ""); + + g_free (escaped_str); + escaped_str = g_markup_escape_text (matched_str, -1); + + g_string_append (marked_up, escaped_str); + g_string_append (marked_up, ""); + + g_free (escaped_str); + escaped_str = g_markup_escape_text ((const gchar *) contents + end, new_end - end); + + g_string_append (marked_up, escaped_str); + + g_free (escaped_str); + g_free (matched_str); + + snippet = g_string_free (marked_up, FALSE); + } + + return snippet; +} + +static gchar * +load_contents (SearchThreadData *data, + GFile *file, + SearchHelper *helper, + gsize *buffer_size, + gboolean *finish_early, + GError **error) +{ + // TODO: Use flock/mmap for local files? + GSubprocess *helper_proc; + GInputStream *stream = NULL; + GString *str; + + helper_proc = NULL; + + if (helper != NULL) { + stream = get_stream_from_helper (helper, file, &helper_proc, error); + } else { + // text/plain + stream = G_INPUT_STREAM (g_file_read (file, data->cancellable, error)); + } + + if (stream == NULL) { + return NULL; + } + + str = g_string_new (NULL); + gssize len = 0; + + do { + gchar chunk[4097]; + len = g_input_stream_read (stream, chunk, 4096, data->cancellable, error); + + if (len <= 0) { + break; + } + + if (chunk != NULL) { + gchar *stripped; + chunk[len] = '\0'; + + if (data->newline_re != NULL) { + stripped = g_regex_replace_literal (data->newline_re, (const gchar *) chunk, -1, 0, "\n", 0, NULL); + } else { + stripped = g_strdup ((const gchar *) chunk); + } + + if (!data->count_hits) { + GMatchInfo *match_info = NULL; + FileSearchResult *fsr = NULL; + + g_regex_match (data->match_re, stripped, 0, &match_info); + + if (g_match_info_matches (match_info) && !g_cancellable_is_cancelled (data->cancellable)) { + if (fsr == NULL) { + fsr = file_search_result_new (g_file_get_uri (file)); + } + + file_search_result_add_hit (fsr, create_snippet (match_info, stripped, strlen (stripped))); + + g_mutex_lock (&data->hit_list_lock); + data->hit_list = g_list_prepend (data->hit_list, fsr); + g_mutex_unlock (&data->hit_list_lock); + + *finish_early = TRUE; + } + + g_match_info_unref (match_info); + } + + g_string_append (str, stripped); + g_free (stripped); + } + } while (!(*finish_early) && !g_cancellable_is_cancelled (data->cancellable)); + + g_input_stream_close (stream, + data->cancellable, + *error == NULL ? error : NULL); + + // GSubprocess owns the input stream for its STDOUT, but we own it for the text/plain stream. + if (helper_proc != NULL) { + g_subprocess_wait (helper_proc, + data->cancellable, + *error == NULL ? error : NULL); + g_object_unref (helper_proc); + } else { + g_object_unref (stream); + } + + *buffer_size = str->len; + return g_string_free (str, FALSE); +} + +static void +search_for_content_hits (SearchThreadData *data, + GFile *file, + SearchHelper *helper) +{ + GMatchInfo *match_info; + GError *error; + gchar *buffer = NULL; + gsize buffer_size = 0; + gboolean finish_early; + + error = NULL; + finish_early = FALSE; + + buffer = load_contents (data, file, helper, &buffer_size, &finish_early, &error); + + if (finish_early || g_cancellable_is_cancelled (data->cancellable)) { + g_clear_error (&error); + g_free (buffer); + return; + } + + if (error != NULL) { + gchar *uri = g_file_get_uri (file); + g_warning ("Could not load contents of '%s' during content search: %s", uri, error->message); + g_free (uri); + g_error_free (error); + g_free (buffer); + return; + } + + FileSearchResult *fsr = NULL; + + g_regex_match (data->match_re, buffer, 0, &match_info); + + while (g_match_info_matches (match_info) && !g_cancellable_is_cancelled (data->cancellable)) { + if (fsr == NULL) { + fsr = file_search_result_new (g_file_get_uri (file)); + } + + file_search_result_add_hit (fsr, create_snippet (match_info, buffer, buffer_size)); + + if (!g_match_info_next (match_info, &error) && error) { + g_warning ("Error iterating thru pattern matches (/%s/): code %d - %s", + g_regex_get_pattern (data->match_re), error->code, error->message); + g_error_free (error); + break; + } + } + + g_match_info_unref (match_info); + g_free (buffer); + + if (fsr != NULL) { + g_mutex_lock (&data->hit_list_lock); + data->hit_list = g_list_prepend (data->hit_list, fsr); + g_mutex_unlock (&data->hit_list_lock); + } +} + +static void +visit_directory (GFile *dir, SearchThreadData *data) +{ + GFileEnumerator *enumerator; + GFileInfo *info; + GFile *child; + const char *mime_type, *display_name; + char *cased, *normalized; + gboolean hit; + int i; + const char *id; + gboolean visited; + + enumerator = g_file_enumerate_children (dir, + STD_ATTRIBUTES "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, data->cancellable, NULL); + + if (enumerator == NULL) { + return; + } + + while ((info = g_file_enumerator_next_file (enumerator, data->cancellable, NULL)) != NULL) { + if (g_file_info_get_is_hidden (info) && !data->show_hidden) { + goto next; + } + + display_name = g_file_info_get_display_name (info); + if (display_name == NULL) { + goto next; + } + + normalized = g_utf8_normalize (display_name, -1, G_NORMALIZE_NFD); + + if (!data->file_case_sensitive) { + cased = g_utf8_strdown (normalized, -1); + } else { + cased = g_strdup (normalized); + } + + g_free (normalized); + + hit = data->words_and; + for (i = 0; data->words[i] != NULL; i++) { + if (data->word_strstr[i]) { + if ((strstr (cased, data->words[i]) != NULL)^data->words_and) { + hit = !data->words_and; + break; + } + } + else if (strwildcardcmp (data->words[i], cased)^data->words_and) { + hit = !data->words_and; + break; + } + } + g_free (cased); + + child = g_file_get_child (dir, g_file_info_get_name (info)); + if (hit) { + mime_type = g_file_info_get_content_type (info); + + // Our helpers don't currently support uris, so we shouldn't at all - + // probably best, as search would transfer the contents of every file + // to our machines. + if (data->match_re && data->location_supports_content_search) { + SearchHelper *helper = NULL; + + helper = g_hash_table_lookup (search_helpers, mime_type); + + if (helper != NULL || g_content_type_is_a (mime_type, "text/plain")) { + if (DEBUGGING) { + g_message ("Evaluating '%s'", g_file_peek_path (child)); + } + search_for_content_hits (data, child, helper); + } + } else { + FileSearchResult *fsr = NULL; + + fsr = file_search_result_new (g_file_get_uri (child)); + g_mutex_lock (&data->hit_list_lock); + data->hit_list = g_list_prepend (data->hit_list, fsr); + g_mutex_unlock (&data->hit_list_lock); + } + } + + data->n_processed_files++; + + if (data->n_processed_files > (data->match_re ? CONTENT_SEARCH_BATCH_SIZE : + FILE_SEARCH_ONLY_BATCH_SIZE)) { + send_batch (data); + } + + if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY && data->recurse) { + id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE); + visited = FALSE; + if (id) { + if (g_hash_table_lookup_extended (data->visited, + id, NULL, NULL)) { + visited = TRUE; + } else { + g_hash_table_insert (data->visited, g_strdup (id), NULL); + } + } + + if (!visited) { + g_queue_push_tail (data->directories, g_object_ref (child)); + } + } + + g_object_unref (child); + next: + g_object_unref (info); + } + + g_object_unref (enumerator); +} + + +static gpointer +search_thread_func (gpointer user_data) +{ + SearchThreadData *data; + GFile *dir; + GFileInfo *info; + const char *id; + gboolean toplevel; + data = user_data; + + /* Insert id for toplevel directory into visited */ + dir = g_queue_peek_head (data->directories); + info = g_file_query_info (dir, G_FILE_ATTRIBUTE_ID_FILE, 0, data->cancellable, NULL); + if (info) { + id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE); + if (id) { + g_hash_table_insert (data->visited, g_strdup (id), NULL); + } + g_object_unref (info); + } + + toplevel = TRUE; + + while (!g_cancellable_is_cancelled (data->cancellable) && + (dir = g_queue_pop_head (data->directories)) != NULL) { + + if (!toplevel) { + if (g_hash_table_contains (data->skip_folders, g_file_peek_path (dir))) { + g_object_unref (data); + continue; + } + + g_autofree gchar *filename = NULL; + filename = g_file_get_basename (dir); + if (g_hash_table_contains (data->skip_folders, filename)) { + g_object_unref (dir); + continue; + } + } + + toplevel = FALSE; + + visit_directory (dir, data); + g_object_unref (dir); + } + send_batch (data); + + g_idle_add (search_thread_done_idle, data); + + return NULL; +} + +static void +nemo_search_engine_advanced_start (NemoSearchEngine *engine) +{ + NemoSearchEngineAdvanced *simple; + SearchThreadData *data; + GThread *thread; + + simple = NEMO_SEARCH_ENGINE_ADVANCED (engine); + + if (simple->details->active_search != NULL) { + return; + } + + if (simple->details->query == NULL) { + return; + } + + data = search_thread_data_new (simple, simple->details->query); + + thread = g_thread_new ("nemo-search-simple", search_thread_func, data); + simple->details->active_search = data; + + g_thread_unref (thread); +} + +static void +nemo_search_engine_advanced_stop (NemoSearchEngine *engine) +{ + NemoSearchEngineAdvanced *simple; + + simple = NEMO_SEARCH_ENGINE_ADVANCED (engine); + + if (simple->details->active_search != NULL) { + g_cancellable_cancel (simple->details->active_search->cancellable); + simple->details->active_search = NULL; + } +} + +static void +nemo_search_engine_advanced_set_query (NemoSearchEngine *engine, NemoQuery *query) +{ + NemoSearchEngineAdvanced *simple; + + simple = NEMO_SEARCH_ENGINE_ADVANCED (engine); + + if (query) { + g_object_ref (query); + } + + if (simple->details->query) { + g_object_unref (simple->details->query); + } + + simple->details->query = query; +} + +static void +nemo_search_engine_advanced_class_init (NemoSearchEngineAdvancedClass *class) +{ + GObjectClass *gobject_class; + NemoSearchEngineClass *engine_class; + + gobject_class = G_OBJECT_CLASS (class); + gobject_class->finalize = finalize; + + engine_class = NEMO_SEARCH_ENGINE_CLASS (class); + engine_class->set_query = nemo_search_engine_advanced_set_query; + engine_class->start = nemo_search_engine_advanced_start; + engine_class->stop = nemo_search_engine_advanced_stop; + + g_type_class_add_private (class, sizeof (NemoSearchEngineAdvancedDetails)); +} + +static void +nemo_search_engine_advanced_init (NemoSearchEngineAdvanced *engine) +{ + engine->details = G_TYPE_INSTANCE_GET_PRIVATE (engine, NEMO_TYPE_SEARCH_ENGINE_ADVANCED, + NemoSearchEngineAdvancedDetails); + + if (search_helpers == NULL) { + initialize_search_helpers (engine); + } +} + +NemoSearchEngine * +nemo_search_engine_advanced_new (void) +{ + NemoSearchEngine *engine; + + engine = g_object_new (NEMO_TYPE_SEARCH_ENGINE_ADVANCED, NULL); + + return engine; +} diff --git a/libnemo-private/nemo-search-engine-advanced.h b/libnemo-private/nemo-search-engine-advanced.h new file mode 100644 index 000000000..54e82135b --- /dev/null +++ b/libnemo-private/nemo-search-engine-advanced.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* + * Nemo 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. + * + * Nemo 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., 51 Franklin Street - Suite 500, + * Boston, MA 02110-1335, USA. + * + */ + +#ifndef NEMO_SEARCH_ENGINE_ADVANCED_H +#define NEMO_SEARCH_ENGINE_ADVANCED_H + +#include + +#define NEMO_TYPE_SEARCH_ENGINE_ADVANCED (nemo_search_engine_advanced_get_type ()) +#define NEMO_SEARCH_ENGINE_ADVANCED(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_TYPE_SEARCH_ENGINE_ADVANCED, NemoSearchEngineAdvanced)) +#define NEMO_SEARCH_ENGINE_ADVANCED_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NEMO_TYPE_SEARCH_ENGINE_ADVANCED, NemoSearchEngineAdvancedClass)) +#define NEMO_IS_SEARCH_ENGINE_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NEMO_TYPE_SEARCH_ENGINE_ADVANCED)) +#define NEMO_IS_SEARCH_ENGINE_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NEMO_TYPE_SEARCH_ENGINE_ADVANCED)) +#define NEMO_SEARCH_ENGINE_ADVANCED_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NEMO_TYPE_SEARCH_ENGINE_ADVANCED, NemoSearchEngineAdvancedClass)) + +typedef struct NemoSearchEngineAdvancedDetails NemoSearchEngineAdvancedDetails; + +typedef struct NemoSearchEngineAdvanced { + NemoSearchEngine parent; + NemoSearchEngineAdvancedDetails *details; +} NemoSearchEngineAdvanced; + +typedef struct { + NemoSearchEngineClass parent_class; +} NemoSearchEngineAdvancedClass; + +GType nemo_search_engine_advanced_get_type (void); + +NemoSearchEngine* nemo_search_engine_advanced_new (void); +void free_search_helpers (void); + +#endif /* NEMO_SEARCH_ENGINE_ADVANCED_H */ diff --git a/libnemo-private/nemo-search-engine-simple.c b/libnemo-private/nemo-search-engine-simple.c deleted file mode 100644 index 53d9d07f4..000000000 --- a/libnemo-private/nemo-search-engine-simple.c +++ /dev/null @@ -1,537 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* - * Copyright (C) 2005 Red Hat, Inc - * - * Nemo 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. - * - * Nemo 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., 51 Franklin Street - Suite 500, - * Boston, MA 02110-1335, USA. - * - * Author: Alexander Larsson - * - */ - -#include -#include "nemo-search-engine-simple.h" - -#include -#include -#include - -#define BATCH_SIZE 500 - -typedef struct { - NemoSearchEngineSimple *engine; - GCancellable *cancellable; - - GList *mime_types; - char **words; - gboolean *word_strstr; - gboolean words_and; - - GList *found_list; - - GQueue *directories; /* GFiles */ - - GHashTable *visited; - - gint n_processed_files; - GList *uri_hits; - - gboolean show_hidden; -} SearchThreadData; - - -struct NemoSearchEngineSimpleDetails { - NemoQuery *query; - - SearchThreadData *active_search; - - gboolean query_finished; -}; - -G_DEFINE_TYPE (NemoSearchEngineSimple, nemo_search_engine_simple, - NEMO_TYPE_SEARCH_ENGINE); - -static void -finalize (GObject *object) -{ - NemoSearchEngineSimple *simple; - - simple = NEMO_SEARCH_ENGINE_SIMPLE (object); - - if (simple->details->query) { - g_object_unref (simple->details->query); - simple->details->query = NULL; - } - - G_OBJECT_CLASS (nemo_search_engine_simple_parent_class)->finalize (object); -} - -/** - * function modified taken from glib2 / gstrfuncs.c - */ -static gchar** -strsplit_esc_n (const gchar *string, - const gchar delimiter, - const gchar escape, - gint max_tokens, - gint *n_tokens) -{ - GSList *string_list = NULL, *slist; - gchar **str_array; - guint n = 0; - const gchar *remainder, *s; - - g_return_val_if_fail (string != NULL, NULL); - g_return_val_if_fail (delimiter != '\0', NULL); - - if (max_tokens < 1) - max_tokens = G_MAXINT; - - remainder = string; - s = remainder; - while (s && *s) { - if (*s == delimiter) break; - else if (*s == escape) { - s++; - if (*s == 0) break; - } - s++; - } - if (*s == 0) s = NULL; - if (s) { - while (--max_tokens && s) { - gsize len; - - len = s - remainder; - string_list = g_slist_prepend (string_list, - g_strndup (remainder, len)); - n++; - remainder = s + 1; - - s = remainder; - while (s && *s) { - if (*s == delimiter) break; - else if (*s == escape) { - s++; - if (*s == 0) break; - } - s++; - } - if (*s == 0) s = NULL; - } - } - if (*string) { - n++; - string_list = g_slist_prepend (string_list, g_strdup (remainder)); - } - *n_tokens = n; - str_array = g_new (gchar*, n + 1); - - str_array[n--] = NULL; - for (slist = string_list; slist; slist = slist->next) - str_array[n--] = slist->data; - - g_slist_free (string_list); - - return str_array; -} - -static SearchThreadData * -search_thread_data_new (NemoSearchEngineSimple *engine, - NemoQuery *query) -{ - SearchThreadData *data; - char *text, *lower, *normalized, *uri; - GFile *location; - gint n=1, i; - - data = g_new0 (SearchThreadData, 1); - - data->show_hidden = nemo_query_get_show_hidden (query); - data->engine = engine; - data->directories = g_queue_new (); - data->visited = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - uri = nemo_query_get_location (query); - location = NULL; - if (uri != NULL) { - location = g_file_new_for_uri (uri); - g_free (uri); - } - if (location == NULL) { - location = g_file_new_for_path ("/"); - } - g_queue_push_tail (data->directories, location); - - text = nemo_query_get_text (query); - normalized = g_utf8_normalize (text, -1, G_NORMALIZE_NFD); - lower = g_utf8_strdown (normalized, -1); - data->words = strsplit_esc_n (lower, ' ', '\\', -1, &n); - g_free (text); - g_free (lower); - g_free (normalized); - - data->word_strstr = g_malloc(sizeof(gboolean)*n); - data->words_and = TRUE; - for (i = 0; data->words[i] != NULL; i++) { - data->word_strstr[i]=TRUE; - text = data->words[i]; - while(*text!=0) { - if(*text=='\\' || *text=='?' || *text=='*') { - data->word_strstr[i]=FALSE; - break; - } - text++; - } - if (!data->word_strstr[i]) data->words_and = FALSE; - } - - data->mime_types = nemo_query_get_mime_types (query); - - data->cancellable = g_cancellable_new (); - - return data; -} - -static void -search_thread_data_free (SearchThreadData *data) -{ - g_queue_foreach (data->directories, - (GFunc)g_object_unref, NULL); - g_queue_free (data->directories); - g_hash_table_destroy (data->visited); - g_object_unref (data->cancellable); - g_strfreev (data->words); - g_free (data->word_strstr); - g_list_free_full (data->mime_types, g_free); - g_list_free_full (data->uri_hits, g_free); - g_free (data); -} - -static gboolean -search_thread_done_idle (gpointer user_data) -{ - SearchThreadData *data; - - data = user_data; - - if (!g_cancellable_is_cancelled (data->cancellable)) { - nemo_search_engine_finished (NEMO_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 (!g_cancellable_is_cancelled (hits->thread_data->cancellable)) { - nemo_search_engine_hits_added (NEMO_SEARCH_ENGINE (hits->thread_data->engine), - hits->uris); - } - - g_list_free_full (hits->uris, g_free); - 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 -strwildcardcmp(char *a, char *b) -{ - if (*a == 0 && *b == 0) return TRUE; - while(*a!=0 && *b!=0) { - if(*a=='\\') { // escaped character - a++; - if (*a != *b) return FALSE; - } - else { - if (*a=='*') { - if(*(a+1)==0) return TRUE; - if(*b==0) return FALSE; - if (strwildcardcmp(a+1, b) || strwildcardcmp(a, b+1)) return TRUE; - else return FALSE; - } - else if (*a!='?' && (*a != *b)) return FALSE; - } - a++; - b++; - } - if ((*a == 0 && *b == 0) || (*a=='*' && *(a+1)==0)) return TRUE; - return FALSE; -} - -#define STD_ATTRIBUTES \ - G_FILE_ATTRIBUTE_STANDARD_NAME "," \ - G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \ - G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN "," \ - G_FILE_ATTRIBUTE_STANDARD_TYPE "," \ - G_FILE_ATTRIBUTE_ID_FILE - -static void -visit_directory (GFile *dir, SearchThreadData *data) -{ - GFileEnumerator *enumerator; - GFileInfo *info; - GFile *child; - const char *mime_type, *display_name; - char *lower_name, *normalized; - gboolean hit; - int i; - GList *l; - const char *id; - gboolean visited; - - enumerator = g_file_enumerate_children (dir, - data->mime_types != NULL ? - STD_ATTRIBUTES "," - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE - : - STD_ATTRIBUTES - , - 0, data->cancellable, NULL); - - if (enumerator == NULL) { - return; - } - - while ((info = g_file_enumerator_next_file (enumerator, data->cancellable, NULL)) != NULL) { - if (g_file_info_get_is_hidden (info) && !data->show_hidden) { - goto next; - } - - display_name = g_file_info_get_display_name (info); - if (display_name == NULL) { - goto next; - } - - normalized = g_utf8_normalize (display_name, -1, G_NORMALIZE_NFD); - lower_name = g_utf8_strdown (normalized, -1); - g_free (normalized); - - hit = data->words_and; - for (i = 0; data->words[i] != NULL; i++) { - if (data->word_strstr[i]) { - if ((strstr (lower_name, data->words[i]) != NULL)^data->words_and) { - hit = !data->words_and; - break; - } - } - else if (strwildcardcmp (data->words[i], lower_name)^data->words_and) { - hit = !data->words_and; - break; - } - } - g_free (lower_name); - - if (hit && data->mime_types) { - mime_type = g_file_info_get_content_type (info); - hit = FALSE; - - for (l = data->mime_types; mime_type != NULL && l != NULL; l = l->next) { - if (g_content_type_equals (mime_type, l->data)) { - hit = TRUE; - break; - } - } - } - - child = g_file_get_child (dir, g_file_info_get_name (info)); - - if (hit) { - data->uri_hits = g_list_prepend (data->uri_hits, g_file_get_uri (child)); - } - - data->n_processed_files++; - if (data->n_processed_files > BATCH_SIZE) { - send_batch (data); - } - - if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY) { - id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE); - visited = FALSE; - if (id) { - if (g_hash_table_lookup_extended (data->visited, - id, NULL, NULL)) { - visited = TRUE; - } else { - g_hash_table_insert (data->visited, g_strdup (id), NULL); - } - } - - if (!visited) { - g_queue_push_tail (data->directories, g_object_ref (child)); - } - } - - g_object_unref (child); - next: - g_object_unref (info); - } - - g_object_unref (enumerator); -} - - -static gpointer -search_thread_func (gpointer user_data) -{ - SearchThreadData *data; - GFile *dir; - GFileInfo *info; - const char *id; - - data = user_data; - - /* Insert id for toplevel directory into visited */ - dir = g_queue_peek_head (data->directories); - info = g_file_query_info (dir, G_FILE_ATTRIBUTE_ID_FILE, 0, data->cancellable, NULL); - if (info) { - id = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE); - if (id) { - g_hash_table_insert (data->visited, g_strdup (id), NULL); - } - g_object_unref (info); - } - - while (!g_cancellable_is_cancelled (data->cancellable) && - (dir = g_queue_pop_head (data->directories)) != NULL) { - visit_directory (dir, data); - g_object_unref (dir); - } - send_batch (data); - - g_idle_add (search_thread_done_idle, data); - - return NULL; -} - -static void -nemo_search_engine_simple_start (NemoSearchEngine *engine) -{ - NemoSearchEngineSimple *simple; - SearchThreadData *data; - GThread *thread; - - simple = NEMO_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); - - thread = g_thread_new ("nemo-search-simple", search_thread_func, data); - simple->details->active_search = data; - - g_thread_unref (thread); -} - -static void -nemo_search_engine_simple_stop (NemoSearchEngine *engine) -{ - NemoSearchEngineSimple *simple; - - simple = NEMO_SEARCH_ENGINE_SIMPLE (engine); - - if (simple->details->active_search != NULL) { - g_cancellable_cancel (simple->details->active_search->cancellable); - simple->details->active_search = NULL; - } -} - -static void -nemo_search_engine_simple_set_query (NemoSearchEngine *engine, NemoQuery *query) -{ - NemoSearchEngineSimple *simple; - - simple = NEMO_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 -nemo_search_engine_simple_class_init (NemoSearchEngineSimpleClass *class) -{ - GObjectClass *gobject_class; - NemoSearchEngineClass *engine_class; - - gobject_class = G_OBJECT_CLASS (class); - gobject_class->finalize = finalize; - - engine_class = NEMO_SEARCH_ENGINE_CLASS (class); - engine_class->set_query = nemo_search_engine_simple_set_query; - engine_class->start = nemo_search_engine_simple_start; - engine_class->stop = nemo_search_engine_simple_stop; - - g_type_class_add_private (class, sizeof (NemoSearchEngineSimpleDetails)); -} - -static void -nemo_search_engine_simple_init (NemoSearchEngineSimple *engine) -{ - engine->details = G_TYPE_INSTANCE_GET_PRIVATE (engine, NEMO_TYPE_SEARCH_ENGINE_SIMPLE, - NemoSearchEngineSimpleDetails); -} - -NemoSearchEngine * -nemo_search_engine_simple_new (void) -{ - NemoSearchEngine *engine; - - engine = g_object_new (NEMO_TYPE_SEARCH_ENGINE_SIMPLE, NULL); - - return engine; -} diff --git a/libnemo-private/nemo-search-engine-simple.h b/libnemo-private/nemo-search-engine-simple.h deleted file mode 100644 index 02bbe75f4..000000000 --- a/libnemo-private/nemo-search-engine-simple.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* - * Copyright (C) 2005 Red Hat, Inc - * - * Nemo 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. - * - * Nemo 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., 51 Franklin Street - Suite 500, - * Boston, MA 02110-1335, USA. - * - * Author: Alexander Larsson - * - */ - -#ifndef NEMO_SEARCH_ENGINE_SIMPLE_H -#define NEMO_SEARCH_ENGINE_SIMPLE_H - -#include - -#define NEMO_TYPE_SEARCH_ENGINE_SIMPLE (nemo_search_engine_simple_get_type ()) -#define NEMO_SEARCH_ENGINE_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_TYPE_SEARCH_ENGINE_SIMPLE, NemoSearchEngineSimple)) -#define NEMO_SEARCH_ENGINE_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NEMO_TYPE_SEARCH_ENGINE_SIMPLE, NemoSearchEngineSimpleClass)) -#define NEMO_IS_SEARCH_ENGINE_SIMPLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NEMO_TYPE_SEARCH_ENGINE_SIMPLE)) -#define NEMO_IS_SEARCH_ENGINE_SIMPLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NEMO_TYPE_SEARCH_ENGINE_SIMPLE)) -#define NEMO_SEARCH_ENGINE_SIMPLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NEMO_TYPE_SEARCH_ENGINE_SIMPLE, NemoSearchEngineSimpleClass)) - -typedef struct NemoSearchEngineSimpleDetails NemoSearchEngineSimpleDetails; - -typedef struct NemoSearchEngineSimple { - NemoSearchEngine parent; - NemoSearchEngineSimpleDetails *details; -} NemoSearchEngineSimple; - -typedef struct { - NemoSearchEngineClass parent_class; -} NemoSearchEngineSimpleClass; - -GType nemo_search_engine_simple_get_type (void); - -NemoSearchEngine* nemo_search_engine_simple_new (void); - -#endif /* NEMO_SEARCH_ENGINE_SIMPLE_H */ diff --git a/libnemo-private/nemo-search-engine-tracker.c b/libnemo-private/nemo-search-engine-tracker.c index 5093d5701..a7a951875 100644 --- a/libnemo-private/nemo-search-engine-tracker.c +++ b/libnemo-private/nemo-search-engine-tracker.c @@ -99,6 +99,7 @@ cursor_callback (GObject *object, gpointer user_data) { NemoSearchEngineTracker *tracker; + FileSearchResult *fsr = NULL; GError *error = NULL; TrackerSparqlCursor *cursor; GList *hits; @@ -127,9 +128,11 @@ cursor_callback (GObject *object, } /* We iterate result by result, not n at a time. */ - hits = g_list_append (NULL, (gchar*) tracker_sparql_cursor_get_string (cursor, 0, NULL)); - nemo_search_engine_hits_added (NEMO_SEARCH_ENGINE (tracker), hits); - g_list_free (hits); + + fsr = file_search_result_new (g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL))); + hits = g_list_append (NULL, fsr); + nemo_search_engine_hits_added (NEMO_SEARCH_ENGINE (tracker), hits); + g_list_free_full (hits, (GDestroyNotify) file_search_result_free); /* Get next */ cursor_next (tracker, cursor); @@ -189,7 +192,7 @@ nemo_search_engine_tracker_start (NemoSearchEngine *engine) g_cancellable_reset (tracker->details->cancellable); - search_text = nemo_query_get_text (tracker->details->query); + search_text = nemo_query_get_file_pattern (tracker->details->query); location_uri = nemo_query_get_location (tracker->details->query); mimetypes = nemo_query_get_mime_types (tracker->details->query); diff --git a/libnemo-private/nemo-search-engine.c b/libnemo-private/nemo-search-engine.c index 60238d6ab..2b8c924f4 100644 --- a/libnemo-private/nemo-search-engine.c +++ b/libnemo-private/nemo-search-engine.c @@ -23,7 +23,7 @@ #include #include "nemo-search-engine.h" -#include "nemo-search-engine-simple.h" +#include "nemo-search-engine-advanced.h" #ifdef ENABLE_TRACKER #include "nemo-search-engine-tracker.h" @@ -102,8 +102,8 @@ nemo_search_engine_new (void) return engine; } #endif - - engine = nemo_search_engine_simple_new (); + + engine = nemo_search_engine_advanced_new (); return engine; } @@ -168,3 +168,40 @@ nemo_search_engine_error (NemoSearchEngine *engine, const char *error_message) g_signal_emit (engine, signals[ERROR], 0, error_message); } + +static void +search_hit_free (SearchHit *hit) +{ + g_free (hit->snippet); + g_slice_free (SearchHit, hit); +} + +FileSearchResult * +file_search_result_new (gchar *uri) +{ + FileSearchResult *ret = g_slice_new0 (FileSearchResult); + + ret->uri = uri; + ret->hits = g_ptr_array_new_full (1, (GDestroyNotify) search_hit_free); + + return ret; +} + +void +file_search_result_free (FileSearchResult *res) +{ + g_free (res->uri); + g_ptr_array_unref (res->hits); + + g_slice_free (FileSearchResult, res); +} + +void +file_search_result_add_hit (FileSearchResult *result, + gchar *snippet) +{ + SearchHit *hit = g_slice_new0 (SearchHit); + hit->snippet = snippet; + + g_ptr_array_add (result->hits, hit); +} diff --git a/libnemo-private/nemo-search-engine.h b/libnemo-private/nemo-search-engine.h index 7b4daa780..b9577b7fd 100644 --- a/libnemo-private/nemo-search-engine.h +++ b/libnemo-private/nemo-search-engine.h @@ -50,7 +50,7 @@ typedef struct { void (*stop) (NemoSearchEngine *engine); /* Signals */ - void (*hits_added) (NemoSearchEngine *engine, GList *hits); + void (*hits_added) (NemoSearchEngine *engine, GList *hit_infos); void (*hits_subtracted) (NemoSearchEngine *engine, GList *hits); void (*finished) (NemoSearchEngine *engine); void (*error) (NemoSearchEngine *engine, const char *error_message); @@ -70,4 +70,16 @@ void nemo_search_engine_hits_subtracted (NemoSearchEngine *engine, GList void nemo_search_engine_finished (NemoSearchEngine *engine); void nemo_search_engine_error (NemoSearchEngine *engine, const char *error_message); +typedef struct { + gchar *snippet; +} SearchHit; + +typedef struct { + gchar *uri; // The file uri; + GPtrArray *hits; // List of hits. +} FileSearchResult; + +FileSearchResult *file_search_result_new (gchar *uri); +void file_search_result_free (FileSearchResult *result); +void file_search_result_add_hit (FileSearchResult *result, gchar *snippet); #endif /* NEMO_SEARCH_ENGINE_H */ diff --git a/libnemo-private/org.nemo.gschema.xml b/libnemo-private/org.nemo.gschema.xml index 1d6ac56f0..972a44158 100644 --- a/libnemo-private/org.nemo.gschema.xml +++ b/libnemo-private/org.nemo.gschema.xml @@ -69,6 +69,7 @@ + @@ -401,10 +402,6 @@ false If true, double click left on blank area will go to parent folder - - [] - List of saved search strings - false Display the 'Make executable and run' button in the mime-action dialog (open an unknown filetype) @@ -503,11 +500,6 @@ Default column order in the list view Default column order in the list view. - - [ 'name', 'size', 'type', 'where' ] - Default list of columns visible in the search view. - Default list of columns visible in the search view. - @@ -819,4 +811,52 @@ Show the background context menu's Customize item (new-style desktop only). + + + + false + Stores the most recent state of the search regex toggle + + + 'pcre' + valid formats: pcre, javascript + + + false + Treat patterns as raw bytes, not utf-8 + + + true + Stores the most recent state of the file search case toggle + + + true + Stores the most recent state of the content search case toggle + + + [] + Paths or folder names to never recurse into when searching + List of locations that the search engine will never enter when looking for matches. These can be absolute or simply folder names (like .git). You can still enter those folders and search inside of them, however. + + + false + Recurse into subfolders when performing a search + + + [] + Saved list of columns visible in the search view. + + + [] + List of previous file searches + + + [] + Not currently used + + + 10 + Not currently used + + diff --git a/src/nemo-actions.h b/src/nemo-actions.h index 1b9afc7e1..907474560 100644 --- a/src/nemo-actions.h +++ b/src/nemo-actions.h @@ -74,8 +74,6 @@ #define NEMO_ACTION_LOCATION_PROPERTIES "LocationProperties" #define NEMO_ACTION_NO_TEMPLATES "No Templates" #define NEMO_ACTION_EMPTY_TRASH "Empty Trash" -#define NEMO_ACTION_SAVE_SEARCH "Save Search" -#define NEMO_ACTION_SAVE_SEARCH_AS "Save Search As" #define NEMO_ACTION_CUT "Cut" #define NEMO_ACTION_LOCATION_CUT "LocationCut" #define NEMO_ACTION_COPY "Copy" diff --git a/src/nemo-icon-view-container.c b/src/nemo-icon-view-container.c index 11eb7fd6a..8cf54b033 100644 --- a/src/nemo-icon-view-container.c +++ b/src/nemo-icon-view-container.c @@ -496,7 +496,8 @@ fm_desktop_icon_container_icons_compare (NemoIconContainer *container, (file_a, file_b, NEMO_FILE_SORT_BY_DISPLAY_NAME, nemo_view_should_sort_directories_first (directory_view), nemo_view_should_sort_favorites_first (directory_view), - FALSE); + FALSE, + NULL); } if (category_a < category_b) { @@ -2007,7 +2008,7 @@ on_get_tooltip_text (NemoIconContainer *container, (container->details->show_icon_view_tooltips && !is_desktop); if (show_tooltip) { - tooltip_text = nemo_file_construct_tooltip (file, container->details->tooltip_flags); + tooltip_text = nemo_file_construct_tooltip (file, container->details->tooltip_flags, NULL); } return tooltip_text; diff --git a/src/nemo-icon-view-grid-container.c b/src/nemo-icon-view-grid-container.c index 48907177d..1a6c3f923 100644 --- a/src/nemo-icon-view-grid-container.c +++ b/src/nemo-icon-view-grid-container.c @@ -1574,7 +1574,7 @@ on_get_tooltip_text (NemoIconContainer *container, gchar *tooltip_text = NULL; if (container->details->show_desktop_tooltips) { - tooltip_text = nemo_file_construct_tooltip (file, container->details->tooltip_flags); + tooltip_text = nemo_file_construct_tooltip (file, container->details->tooltip_flags, NULL); } return tooltip_text; diff --git a/src/nemo-icon-view.c b/src/nemo-icon-view.c index ec31564b6..e449753f6 100644 --- a/src/nemo-icon-view.c +++ b/src/nemo-icon-view.c @@ -1764,7 +1764,8 @@ nemo_icon_view_compare_files (NemoIconView *icon_view, /* Use type-unsafe cast for performance */ nemo_view_should_sort_directories_first ((NemoView *)icon_view), nemo_view_should_sort_favorites_first ((NemoView *)icon_view), - icon_view->details->sort_reversed); + icon_view->details->sort_reversed, + NULL); } static int @@ -2825,9 +2826,6 @@ nemo_icon_view_supports_uri (const char *uri, if (file_type == G_FILE_TYPE_DIRECTORY) { return TRUE; } - if (strcmp (mime_type, NEMO_SAVED_SEARCH_MIMETYPE) == 0){ - return TRUE; - } if (g_str_has_prefix (uri, "trash:")) { return TRUE; } diff --git a/src/nemo-list-model.c b/src/nemo-list-model.c index 0d1e295fa..ec21d14f9 100644 --- a/src/nemo-list-model.c +++ b/src/nemo-list-model.c @@ -47,7 +47,8 @@ enum { static GQuark attribute_name_q, attribute_modification_date_q, - attribute_date_modified_q; + attribute_date_modified_q, + attribute_search_result_count_q; /* msec delay after Loading... dummy row turns into (empty) */ #define LOADING_TO_EMPTY_DELAY 100 @@ -66,6 +67,8 @@ struct NemoListModelDetails { GHashTable *directory_reverse_map; /* map from directory to GSequenceIter's */ GHashTable *top_reverse_map; /* map from files in top dir to GSequenceIter's */ + NemoDirectory *view_dir; + int stamp; GQuark sort_attribute; @@ -445,8 +448,11 @@ nemo_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int colu "attribute_q", &attribute, NULL); if (file != NULL) { - str = nemo_file_get_string_attribute_with_default_q (file, - attribute); + if (attribute == attribute_search_result_count_q) { + str = nemo_file_get_search_result_count_as_string (file, (gpointer) model->details->view_dir); + } else { + str = nemo_file_get_string_attribute_with_default_q (file, attribute); + } g_value_take_string (value, str); } else if (attribute == attribute_name_q) { if (file_entry->parent->loaded) { @@ -719,7 +725,8 @@ nemo_list_model_file_entry_compare_func (gconstpointer a, model->details->sort_attribute, model->details->sort_directories_first, model->details->sort_favorites_first, - (model->details->order == GTK_SORT_DESCENDING)); + (model->details->order == GTK_SORT_DESCENDING), + model->details->view_dir); } else if (file_entry1->file == NULL) { return -1; } else { @@ -740,7 +747,8 @@ nemo_list_model_compare_func (NemoListModel *model, model->details->sort_attribute, model->details->sort_directories_first, model->details->sort_favorites_first, - (model->details->order == GTK_SORT_DESCENDING)); + (model->details->order == GTK_SORT_DESCENDING), + model->details->view_dir); return result; } @@ -1686,6 +1694,7 @@ nemo_list_model_class_init (NemoListModelClass *klass) attribute_name_q = g_quark_from_static_string ("name"); attribute_modification_date_q = g_quark_from_static_string ("modification_date"); attribute_date_modified_q = g_quark_from_static_string ("date_modified"); + attribute_search_result_count_q = g_quark_from_string ("search_result_count"); object_class = (GObjectClass *)klass; object_class->finalize = nemo_list_model_finalize; @@ -1865,3 +1874,10 @@ nemo_list_model_set_expanding (NemoListModel *model, NemoDirectory *directory) entry = g_sequence_get (ptr); entry->expanding = TRUE; } + +void +nemo_list_model_set_view_directory (NemoListModel *model, NemoDirectory *dir) +{ + model->details->view_dir = dir; +} + diff --git a/src/nemo-list-model.h b/src/nemo-list-model.h index 23ad3fee4..f06576702 100644 --- a/src/nemo-list-model.h +++ b/src/nemo-list-model.h @@ -139,4 +139,5 @@ void nemo_list_model_set_highlight_for_files (NemoListModel *model, void nemo_list_model_set_temporarily_disable_sort (NemoListModel *model, gboolean disable); gboolean nemo_list_model_get_temporarily_disable_sort (NemoListModel *model); void nemo_list_model_set_expanding (NemoListModel *model, NemoDirectory *directory); +void nemo_list_model_set_view_directory (NemoListModel *model, NemoDirectory *dir); #endif /* NEMO_LIST_MODEL_H */ diff --git a/src/nemo-list-view.c b/src/nemo-list-view.c index 1e078dbd8..4e09ca32d 100644 --- a/src/nemo-list-view.c +++ b/src/nemo-list-view.c @@ -211,6 +211,10 @@ static const char * default_favorites_columns_order[] = { "name", "size", "date_modified", NULL }; +static const char * default_search_columns[] = { + "name", "where", "date_modified", NULL +}; + static gchar ** string_array_from_string_glist (GList *list) { @@ -725,8 +729,10 @@ query_tooltip_callback (GtkWidget *widget, if (file) { gchar *tooltip_text; - tooltip_text = nemo_file_construct_tooltip (file, list_view->details->tooltip_flags); - gtk_tooltip_set_text (tooltip, tooltip_text); + tooltip_text = nemo_file_construct_tooltip (file, + list_view->details->tooltip_flags, + nemo_view_get_model (NEMO_VIEW (list_view))); + gtk_tooltip_set_markup (tooltip, tooltip_text); gtk_tree_view_set_tooltip_cell (GTK_TREE_VIEW (widget), tooltip, path, NULL, NULL); g_free (tooltip_text); @@ -809,7 +815,6 @@ columns_reordered_callback (AtkObject *atk, gpointer user_data) { NemoListView *view = NEMO_LIST_VIEW (user_data); - NemoDirectory *directory; gchar **columns; GList *vis_columns = NULL; @@ -838,17 +843,14 @@ columns_reordered_callback (AtkObject *atk, list = g_list_reverse (list); - directory = nemo_view_get_model (NEMO_VIEW (view)); - if (nemo_global_preferences_get_ignore_view_metadata ()) { nemo_window_set_ignore_meta_column_order (nemo_view_get_nemo_window (NEMO_VIEW (view)), list); - } else if (NEMO_IS_SEARCH_DIRECTORY (directory)) { + } else if (nemo_file_is_in_search (file)) { gchar **column_array = string_array_from_string_glist (list); - g_settings_set_strv (nemo_list_view_preferences, - NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS, - (const gchar **) column_array); - g_strfreev (column_array); + g_settings_set_strv (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS, + (const gchar **) column_array); } else { nemo_file_set_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, @@ -1826,7 +1828,6 @@ column_header_menu_toggled (GtkCheckMenuItem *menu_item, NemoListView *list_view) { NemoFile *file; - NemoDirectory *directory; char **visible_columns; const char *menu_item_column_id; GList *list = NULL; @@ -1853,16 +1854,15 @@ column_header_menu_toggled (GtkCheckMenuItem *menu_item, } } - directory = nemo_view_get_model (NEMO_VIEW (list_view)); - list = g_list_reverse (list); - if (nemo_global_preferences_get_ignore_view_metadata ()) + if (nemo_global_preferences_get_ignore_view_metadata ()) { nemo_window_set_ignore_meta_visible_columns (nemo_view_get_nemo_window (NEMO_VIEW (list_view)), list); - else if (NEMO_IS_SEARCH_DIRECTORY (directory)) { + } else if (nemo_file_is_in_search (file)) { gchar **column_array = string_array_from_string_glist (list); - g_settings_set_strv (nemo_list_view_preferences, - NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS, + + g_settings_set_strv (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS, (const gchar **) column_array); g_strfreev (column_array); @@ -1891,7 +1891,6 @@ column_header_menu_use_default (GtkMenuItem *menu_item, NemoListView *list_view) { NemoFile *file; - NemoDirectory *directory; char **default_columns; char **default_order; @@ -1910,10 +1909,9 @@ column_header_menu_use_default (GtkMenuItem *menu_item, nemo_file_set_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL); } - directory = nemo_view_get_model (NEMO_VIEW (list_view)); - - if (NEMO_IS_SEARCH_DIRECTORY (directory)) - g_settings_reset (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS); + if (nemo_file_is_in_search (file)) { + g_settings_reset (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS); + } default_columns = get_default_visible_columns (list_view); @@ -1977,6 +1975,12 @@ column_header_clicked (GtkWidget *column_button, continue; } + if (!nemo_file_is_in_search (file)) { + if (g_strcmp0 (name, "search_result_count") == 0 || + g_strcmp0 (name, "search_result_snippet") == 0) + continue; + } + g_object_get (G_OBJECT (c), "title", &label, "visible", &visible, @@ -2442,13 +2446,18 @@ create_and_set_up_tree_view (NemoListView *view) char *name; char *label; float xalign; + gint width_chars; + gboolean ellipsize; + nemo_column = NEMO_COLUMN (l->data); g_object_get (nemo_column, "name", &name, "label", &label, - "xalign", &xalign, NULL); + "xalign", &xalign, + "width-chars", &width_chars, + "ellipsize", &ellipsize, NULL); column_num = nemo_list_model_add_column (view->details->model, nemo_column); @@ -2515,6 +2524,8 @@ create_and_set_up_tree_view (NemoListView *view) g_object_set (cell, "xalign", xalign, "xpad", 5, + "width-chars", width_chars, + "ellipsize", ellipsize, NULL); view->details->cells = g_list_append (view->details->cells, cell); @@ -2595,7 +2606,6 @@ static char ** get_default_visible_columns (NemoListView *list_view) { NemoFile *file; - NemoDirectory *directory; file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view)); @@ -2611,9 +2621,8 @@ get_default_visible_columns (NemoListView *list_view) return g_strdupv ((gchar **) default_favorites_visible_columns); } - directory = nemo_view_get_model (NEMO_VIEW (list_view)); - if (NEMO_IS_SEARCH_DIRECTORY (directory)) { - return g_settings_get_strv (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS); + if (nemo_file_is_in_search (file)) { + return g_strdupv ((gchar **) default_search_columns); } return g_settings_get_strv (nemo_list_view_preferences, @@ -2628,14 +2637,26 @@ get_visible_columns (NemoListView *list_view) char **ret; ret = NULL; + visible_columns = NULL; file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view)); if (nemo_global_preferences_get_ignore_view_metadata ()) { visible_columns = nemo_window_get_ignore_meta_visible_columns (nemo_view_get_nemo_window (NEMO_VIEW (list_view))); } else { - visible_columns = nemo_file_get_metadata_list (file, - NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS); + if (nemo_file_is_in_search (file)) { + gchar **modified_cols; + + modified_cols = g_settings_get_strv (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS); + + if (g_strv_length (modified_cols) > 0) { + return modified_cols; + } else { + g_strfreev (modified_cols); + } + } else { + visible_columns = nemo_file_get_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS); + } } if (visible_columns) { @@ -2654,7 +2675,6 @@ static char ** get_default_column_order (NemoListView *list_view) { NemoFile *file; - NemoDirectory *directory; file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view)); @@ -2670,9 +2690,8 @@ get_default_column_order (NemoListView *list_view) return g_strdupv ((gchar **) default_favorites_columns_order); } - directory = nemo_view_get_model (NEMO_VIEW (list_view)); - if (NEMO_IS_SEARCH_DIRECTORY (directory)) { - return g_settings_get_strv (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS); + if (nemo_file_is_in_search (file)) { + return g_strdupv ((gchar **) default_search_columns); } return g_settings_get_strv (nemo_list_view_preferences, @@ -2686,6 +2705,7 @@ get_column_order (NemoListView *list_view) GList *column_order; char **ret; + column_order = NULL; ret = NULL; file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view)); @@ -2693,8 +2713,18 @@ get_column_order (NemoListView *list_view) if (nemo_global_preferences_get_ignore_view_metadata ()) { column_order = nemo_window_get_ignore_meta_column_order (nemo_view_get_nemo_window (NEMO_VIEW (list_view))); } else { - column_order = nemo_file_get_metadata_list (file, - NEMO_METADATA_KEY_LIST_VIEW_COLUMN_ORDER); + if (nemo_file_is_in_search (file)) { + gchar **modified_cols; + modified_cols = g_settings_get_strv (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS); + + if (g_strv_length (modified_cols) > 0) { + return modified_cols; + } else { + g_strfreev (modified_cols); + } + } else { + column_order = nemo_file_get_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_COLUMN_ORDER); + } } if (column_order) { @@ -2840,6 +2870,8 @@ nemo_list_view_begin_loading (NemoView *view) set_ok_to_load_deferred_attrs (list_view, FALSE); + nemo_list_model_set_view_directory (list_view->details->model, nemo_view_get_model (view)); + AtkObject *atk = gtk_widget_get_accessible (GTK_WIDGET (NEMO_LIST_VIEW (view)->details->tree_view)); g_signal_connect_object (atk, "column-reordered", @@ -3449,7 +3481,6 @@ static void nemo_list_view_reset_to_defaults (NemoView *view) { NemoFile *file; - NemoDirectory *directory; file = nemo_view_get_directory_as_file (view); @@ -3472,10 +3503,9 @@ nemo_list_view_reset_to_defaults (NemoView *view) nemo_file_set_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL); } - directory = nemo_view_get_model (view); - - if (NEMO_IS_SEARCH_DIRECTORY (directory)) - g_settings_reset (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS); + if (nemo_file_is_in_search (file)) { + g_settings_reset (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_VISIBLE_COLUMNS); + } char **default_columns, **default_order; @@ -4211,9 +4241,6 @@ nemo_list_view_supports_uri (const char *uri, if (file_type == G_FILE_TYPE_DIRECTORY) { return TRUE; } - if (strcmp (mime_type, NEMO_SAVED_SEARCH_MIMETYPE) == 0){ - return TRUE; - } if (g_str_has_prefix (uri, "trash:")) { return TRUE; } diff --git a/src/nemo-main-application.c b/src/nemo-main-application.c index cf14f8d50..b9834ec8b 100644 --- a/src/nemo-main-application.c +++ b/src/nemo-main-application.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #define DEBUG_FLAG NEMO_DEBUG_APPLICATION @@ -569,6 +570,8 @@ nemo_main_application_finalize (GObject *object) g_clear_object (&application->priv->dbus_manager); g_clear_object (&application->priv->fdb_manager); + free_search_helpers (); + G_OBJECT_CLASS (nemo_main_application_parent_class)->finalize (object); } diff --git a/src/nemo-query-editor.c b/src/nemo-query-editor.c index 394d723c3..b0f9b11c5 100644 --- a/src/nemo-query-editor.c +++ b/src/nemo-query-editor.c @@ -36,21 +36,35 @@ typedef struct { + GtkBuilder *builder; GtkWidget *infobar; - GtkWidget *entry; - GtkWidget *menu; + GtkWidget *file_entry; + GtkWidget *file_entry_combo; + GtkWidget *content_entry; + GtkWidget *content_entry_combo; + GtkWidget *content_case_toggle; + GtkWidget *file_case_toggle; + GtkWidget *regex_toggle; + GtkWidget *file_recurse_toggle; + GtkWidget *content_main_box; + + GtkWidget *content_view; + GtkWidget *last_focus_widget; + GList *focus_chain; gboolean change_frozen; guint typing_timeout_id; gboolean is_visible; GtkWidget *vbox; - gchar **faves; + gboolean history_enabled; + gboolean focus_frozen; - char *current_uri; - char *base_uri; + gchar *current_uri; + gchar *base_uri; - char *last_set_query_text; + gchar *last_set_query_file_pattern; + gchar *last_set_query_content_pattern; } NemoQueryEditorPrivate; struct _NemoQueryEditor @@ -70,23 +84,17 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; -static void entry_activate_cb (GtkWidget *entry, NemoQueryEditor *editor); -static void entry_changed_cb (GtkWidget *entry, NemoQueryEditor *editor); static void nemo_query_editor_changed_force (NemoQueryEditor *editor, gboolean force); static void nemo_query_editor_changed (NemoQueryEditor *editor); -static void on_saved_searches_setting_changed (GSettings *settings, - gchar *key, - gpointer user_data); - static gchar * -get_sanitized_query_string (NemoQueryEditor *editor) +get_sanitized_file_search_string (NemoQueryEditor *editor) { const gchar *entry_text; gchar *ret; - entry_text = gtk_entry_get_text (GTK_ENTRY (editor->priv->entry)); + entry_text = gtk_entry_get_text (GTK_ENTRY (editor->priv->file_entry)); ret = g_strdup (entry_text); ret = g_strstrip (ret); @@ -102,19 +110,23 @@ nemo_query_editor_dispose (GObject *object) editor = NEMO_QUERY_EDITOR (object); g_clear_pointer (&editor->priv->base_uri, g_free); - g_clear_pointer (&editor->priv->last_set_query_text, g_free); + g_clear_pointer (&editor->priv->current_uri, g_free); + g_clear_pointer (&editor->priv->last_set_query_file_pattern, g_free); + g_clear_pointer (&editor->priv->last_set_query_content_pattern, g_free); if (editor->priv->typing_timeout_id > 0) { g_source_remove (editor->priv->typing_timeout_id); editor->priv->typing_timeout_id = 0; } - g_clear_object (&editor->priv->menu); - g_clear_pointer (&editor->priv->faves, g_strfreev); + g_clear_object (&editor->priv->builder); - g_signal_handlers_disconnect_by_func (nemo_preferences, - on_saved_searches_setting_changed, - editor); + if (editor->priv->focus_chain != NULL) { + editor->priv->focus_chain->prev->next = NULL; + editor->priv->focus_chain->prev = NULL; + g_list_free (editor->priv->focus_chain); + editor->priv->focus_chain = NULL; + } G_OBJECT_CLASS (nemo_query_editor_parent_class)->dispose (object); } @@ -125,7 +137,11 @@ nemo_query_editor_grab_focus (GtkWidget *widget) NemoQueryEditor *editor = NEMO_QUERY_EDITOR (widget); if (gtk_widget_get_visible (widget)) { - gtk_entry_grab_focus_without_selecting (GTK_ENTRY (editor->priv->entry)); + if (editor->priv->last_focus_widget != NULL) { + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (editor->priv->last_focus_widget)); + } else { + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (editor->priv->file_entry)); + } } } @@ -160,626 +176,411 @@ nemo_query_editor_class_init (NemoQueryEditorClass *class) gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "cancel", 0); } -GFile * -nemo_query_editor_get_location (NemoQueryEditor *editor) -{ - GFile *file = NULL; - if (editor->priv->current_uri != NULL) - file = g_file_new_for_uri (editor->priv->current_uri); - return file; -} - static void entry_activate_cb (GtkWidget *entry, NemoQueryEditor *editor) { - g_autofree gchar *text = NULL; - if (editor->priv->typing_timeout_id > 0) { g_source_remove (editor->priv->typing_timeout_id); editor->priv->typing_timeout_id = 0; } - text = get_sanitized_query_string (editor); - - if (strlen (text) > 2) { - nemo_query_editor_changed_force (editor, TRUE); - } -} - -static gboolean -typing_timeout_cb (gpointer user_data) -{ - NemoQueryEditor *editor; - - editor = NEMO_QUERY_EDITOR (user_data); - editor->priv->typing_timeout_id = 0; - - nemo_query_editor_changed (editor); - - return FALSE; -} - -#define TYPING_TIMEOUT 250 - -static gchar * -construct_favorite_entry (const gchar *uri, - const gchar *key) -{ - return g_strdup_printf ("%s::%s", uri, key); -} - -static gboolean -parse_favorite_entry (const gchar *favorite_entry, - gchar **uri, - gchar **key) -{ - gchar **split; - - split = g_strsplit (favorite_entry, "::", 2); - - if (split == NULL || g_strv_length (split) < 2) { - *key = NULL; - *uri = NULL; - return FALSE; - } - - *uri = g_strdup (split[0]); - *key = g_strdup (split[1]); - - g_strfreev (split); - - return TRUE; -} - -static gboolean -is_search_criteria_in_faves (NemoQueryEditor *editor, - const gchar *key) -{ - gint i, length; - gboolean ret; - - length = g_strv_length (editor->priv->faves); - - if (length == 0) { - return FALSE; - } - - ret = FALSE; - - for (i = 0; i < length; i++) { - gchar *favorite_uri, *favorite_key; - - if (parse_favorite_entry (editor->priv->faves[i], - &favorite_uri, - &favorite_key)) { - if (g_strcmp0 (editor->priv->current_uri, favorite_uri) == 0) { - if (g_strcmp0 (key, favorite_key) == 0) { - ret = TRUE; - } - } - - g_clear_pointer (&favorite_uri, g_free); - g_clear_pointer (&favorite_key, g_free); - } - - if (ret) { - break; - } - } - - return ret; + nemo_query_editor_changed_force (editor, TRUE); } static void -add_key_to_faves (NemoQueryEditor *editor, - const gchar *entry) +content_case_button_toggled_cb (GtkWidget *toggle, NemoQueryEditor *editor) { - gint i; - GPtrArray *array; - - array = g_ptr_array_new (); - - g_ptr_array_add (array, g_strdup (entry)); - - if (editor->priv->faves != NULL) { - for (i = 0; i < g_strv_length (editor->priv->faves); i++) { - g_ptr_array_add (array, g_strdup (editor->priv->faves[i])); - } - } - - g_ptr_array_add (array, NULL); - - g_signal_handlers_block_by_func (nemo_preferences, - on_saved_searches_setting_changed, - editor); - - g_settings_set_strv (nemo_preferences, - NEMO_PREFERENCES_SAVED_SEARCHES, - (const gchar * const *) array->pdata); - - g_signal_handlers_unblock_by_func (nemo_preferences, - on_saved_searches_setting_changed, - editor); - - g_clear_pointer (&editor->priv->faves, g_strfreev); - editor->priv->faves = (gchar **) g_ptr_array_free (array, FALSE); + g_settings_set_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_CONTENT_CASE, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->content_case_toggle))); } static void -remove_key_from_faves (NemoQueryEditor *editor, - const gchar *entry) +file_case_button_toggled_cb (GtkWidget *toggle, NemoQueryEditor *editor) { - gint i; - gchar *key, *uri; - GPtrArray *array; - - if (!parse_favorite_entry (entry, &uri, &key)) { - return; - } - - array = g_ptr_array_new (); - - if (editor->priv->faves != NULL) { - for (i = 0; i < g_strv_length (editor->priv->faves); i++) { - gchar *favorite_key, *favorite_uri; - - if (parse_favorite_entry (editor->priv->faves[i], - &favorite_uri, - &favorite_key)) { - if (g_strcmp0 (key, favorite_key) != 0 || - g_strcmp0 (uri, favorite_uri) != 0) { - g_ptr_array_add (array, g_strdup (editor->priv->faves[i])); - } - - g_free (favorite_key); - g_free (favorite_uri); - } - } - } - - g_ptr_array_add (array, NULL); - - g_signal_handlers_block_by_func (nemo_preferences, - on_saved_searches_setting_changed, - editor); - - g_settings_set_strv (nemo_preferences, - NEMO_PREFERENCES_SAVED_SEARCHES, - (const gchar * const *) array->pdata); - - g_signal_handlers_unblock_by_func (nemo_preferences, - on_saved_searches_setting_changed, - editor); - - g_free (key); - g_free (uri); - - g_clear_pointer (&editor->priv->faves, g_strfreev); - editor->priv->faves = (gchar **) g_ptr_array_free (array, FALSE); + g_settings_set_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_FILE_CASE, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->file_case_toggle))); } static void -update_fav_icon (NemoQueryEditor *editor) +regex_button_toggled_cb (GtkWidget *toggle, NemoQueryEditor *editor) { - g_autofree gchar *current_key = NULL; - - current_key = get_sanitized_query_string (editor); - - if (is_search_criteria_in_faves (editor, current_key)) { - gtk_entry_set_icon_from_icon_name (GTK_ENTRY (editor->priv->entry), - GTK_ENTRY_ICON_SECONDARY, - "starred-symbolic"); - return; - } - - gtk_entry_set_icon_from_icon_name (GTK_ENTRY (editor->priv->entry), - GTK_ENTRY_ICON_SECONDARY, - "non-starred-symbolic"); + g_settings_set_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_CONTENT_REGEX, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->regex_toggle))); } static void -entry_changed_cb (GtkWidget *entry, NemoQueryEditor *editor) +file_recurse_button_toggled_cb (GtkWidget *toggle, NemoQueryEditor *editor) { - g_autofree gchar *text = NULL; - - if (editor->priv->change_frozen) { - return; - } - - if (editor->priv->typing_timeout_id > 0) { - g_source_remove (editor->priv->typing_timeout_id); - editor->priv->typing_timeout_id = 0; - } - - update_fav_icon (editor); - - text = get_sanitized_query_string (editor); - - if (strlen (text) > 2) { - editor->priv->typing_timeout_id = g_timeout_add (TYPING_TIMEOUT, - typing_timeout_cb, - editor); - } -} - -static void -get_markup_for_fave (NemoQueryEditor *editor, - const gchar *favorite, - gchar **loc_markup, - gchar **key_markup) -{ - GFile *location; - gchar *favorite_key, *favorite_location; - gchar *location_string, *mnemonic_key; - - if (!parse_favorite_entry (favorite, &favorite_location, &favorite_key)) { - *loc_markup = NULL; - *key_markup = NULL; - return; - } - - location = g_file_new_for_uri (favorite_location); - location_string = nemo_compute_search_title_for_location (location); - - mnemonic_key = g_strdup_printf ("_%s", favorite_key); - - *loc_markup = g_strdup_printf (_("in %s"), location_string); - *key_markup = g_strdup_printf (_("Search for %s"), mnemonic_key); - - g_free (favorite_location); - g_free (favorite_key); - g_free (location_string); - g_free (mnemonic_key); - g_object_unref (location); -} - -static void -on_menu_item_activated (GtkMenuItem *item, - gpointer user_data) -{ - NemoQueryEditor *editor; - NemoQuery *query; - const gchar *fave_entry; - gchar *favorite_key, *favorite_location; - - editor = NEMO_QUERY_EDITOR (user_data); - - fave_entry = g_object_get_data (G_OBJECT (item), - "fave-entry"); - - if (parse_favorite_entry (fave_entry, &favorite_location, &favorite_key)) { - query = nemo_query_new (); - - nemo_query_set_location (query, favorite_location); - nemo_query_set_text (query, favorite_key); - - nemo_query_editor_set_query (editor, query); - nemo_query_editor_changed (editor); - update_fav_icon (editor); - - g_free (favorite_location); - g_free (favorite_key); - g_object_unref (query); - } + g_settings_set_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_FILES_RECURSIVELY, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->file_recurse_toggle))); } static gboolean -on_menu_item_key_press (GtkWidget *widget, - GdkEvent *event, - gpointer user_data) +on_key_press_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) { - if (event->key.state == 0 && event->key.keyval == GDK_KEY_Delete) { - NemoQueryEditor *editor; - GtkWidget *item; - const gchar *fave_entry; - - editor = NEMO_QUERY_EDITOR (user_data); + NemoQueryEditor *editor = NEMO_QUERY_EDITOR (user_data); - item = gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (widget)); + if ((event->key.state & gtk_accelerator_get_default_mod_mask ()) == 0) { + // if (event->key.keyval == GDK_KEY_Up) { + // popup_favorites (NEMO_QUERY_EDITOR (user_data), event, FALSE); + // } else + if (event->key.keyval == GDK_KEY_Tab) { + GList *focus_iter = editor->priv->focus_chain; - if (item == NULL) { - return GDK_EVENT_PROPAGATE; - } + while (focus_iter->data != widget) { + focus_iter = focus_iter->next; + } - fave_entry = g_object_get_data (G_OBJECT (item), - "fave-entry"); + gtk_widget_grab_focus (GTK_WIDGET (focus_iter->next->data)); - remove_key_from_faves (editor, fave_entry); + return GDK_EVENT_STOP; + } + if (event->key.keyval == GDK_KEY_Escape) { - gtk_widget_set_sensitive (item, FALSE); - update_fav_icon (editor); + g_signal_emit (editor, signals[CANCEL], 0); - return GDK_EVENT_STOP; + return GDK_EVENT_STOP; + } } return GDK_EVENT_PROPAGATE; } -#if !GTK_CHECK_VERSION (3, 22, 0) static void -menu_position_function (GtkMenu *menu, - gint *x, - gint *y, - gboolean *push_in, - gpointer user_data) +search_icon_clicked_cb (GtkWidget *widget, + GtkEntryIconPosition position, + GdkEvent *event, + gpointer user_data) { NemoQueryEditor *editor; - GtkWidget *parent; - GtkAllocation menu_allocation; - gint window_x, window_y, translated_x, translated_y; - - g_return_if_fail (NEMO_IS_QUERY_EDITOR (user_data)); - editor = NEMO_QUERY_EDITOR (user_data); - - parent = gtk_widget_get_toplevel (GTK_WIDGET (editor)); - - gtk_widget_translate_coordinates (editor->priv->entry, - parent, - 0, 0, - &translated_x, - &translated_y); - - gdk_window_get_position (gtk_widget_get_window (parent), &window_x, &window_y); - - gtk_widget_get_allocation (GTK_WIDGET (menu), &menu_allocation); - - *x = translated_x + window_x; - *y = translated_y + window_y - menu_allocation.height; - *push_in = TRUE; -} -#endif - -static void -popup_favorites (NemoQueryEditor *editor, - GdkEvent *event, - gboolean use_pointer_location) -{ - GtkWidget *menu, *item, *item_child; - GtkSizeGroup *group; - gchar **faves; - gint i; - - if (g_strv_length (editor->priv->faves) == 0) { + if (position == GTK_ENTRY_ICON_PRIMARY) { return; } - g_clear_object (&editor->priv->menu); - editor->priv->menu = menu = g_object_ref_sink (gtk_menu_new ()); - - group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); - - faves = editor->priv->faves; - - for (i = 0; i < g_strv_length (faves); i++) { - GtkWidget *label; - gchar *loc_markup, *key_markup; - - get_markup_for_fave (editor, - faves[i], - &loc_markup, - &key_markup); - - if (loc_markup == NULL || key_markup == NULL) { - continue; - } - - item = gtk_menu_item_new(); - - item_child = gtk_bin_get_child (GTK_BIN (item)); - - if (item_child != NULL) { - gtk_widget_destroy (item_child); - } - - item_child = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10); - gtk_container_add (GTK_CONTAINER (item), item_child); - - label = gtk_label_new (NULL); - - gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), key_markup); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_box_pack_start (GTK_BOX (item_child), label, FALSE, FALSE, 0); - gtk_size_group_add_widget (group, label); - - label = gtk_label_new (NULL); - - gtk_label_set_markup (GTK_LABEL (label), loc_markup); - gtk_label_set_xalign (GTK_LABEL (label), 0.0); - gtk_box_pack_start (GTK_BOX (item_child), label, FALSE, FALSE, 0); - - g_object_set_data_full (G_OBJECT (item), - "fave-entry", - g_strdup (faves[i]), - (GDestroyNotify) g_free); - - g_free (loc_markup); - g_free (key_markup); + editor = NEMO_QUERY_EDITOR (user_data); - gtk_widget_show_all (GTK_WIDGET (item)); + if ((event->button.state & gtk_accelerator_get_default_mod_mask ()) == 0 && event->button.button == 1) { + nemo_query_editor_changed (editor); + } +} - gtk_menu_attach (GTK_MENU (menu), item, 0, 1, i, i + 1); +// static void +// file_combo_iter_changed (GtkWidget *widget, +// GParamSpec *spec, +// gpointer data) +// { +// NemoQueryEditor *editor = NEMO_QUERY_EDITOR (data); - g_signal_connect (item, - "activate", - G_CALLBACK (on_menu_item_activated), - editor); - } +// if (editor->priv->focus_frozen) { +// return; +// } - g_object_unref (group); +// gtk_widget_grab_focus (editor->priv->file_entry); - g_signal_connect (menu, - "key-press-event", - G_CALLBACK (on_menu_item_key_press), - editor); +// editor->priv->last_focus_widget = editor->priv->file_entry; +// gtk_editable_set_position (GTK_EDITABLE (editor->priv->file_entry), -1); +// } -#if GTK_CHECK_VERSION (3, 22, 0) - if (use_pointer_location) { - gtk_menu_popup_at_pointer (GTK_MENU (menu), event); - } else { - gtk_menu_popup_at_widget (GTK_MENU (menu), - editor->priv->entry, - GDK_GRAVITY_NORTH_WEST, - GDK_GRAVITY_SOUTH_WEST, - event); - } -#else - if (use_pointer_location) { - gtk_menu_popup (GTK_MENU (menu), - NULL, NULL, NULL, NULL, - 3, - gtk_get_current_event_time ()); - } else { - gtk_menu_popup (GTK_MENU (menu), - NULL, NULL, - (GtkMenuPositionFunc) menu_position_function, - editor, - 0, - gtk_get_current_event_time ()); - } -#endif -} +// static void +// content_combo_iter_changed (GtkWidget *widget, +// GParamSpec *spec, +// gpointer data) +// { +// NemoQueryEditor *editor = NEMO_QUERY_EDITOR (data); -static gboolean -on_key_press_event (GtkWidget *widget, - GdkEvent *event, - gpointer user_data) -{ - if ((event->key.state & gtk_accelerator_get_default_mod_mask ()) == 0 && event->key.keyval == GDK_KEY_Up) { - popup_favorites (NEMO_QUERY_EDITOR (user_data), event, FALSE); - return GDK_EVENT_STOP; - } +// if (editor->priv->focus_frozen) { +// return; +// } - return GDK_EVENT_PROPAGATE; -} +// editor->priv->last_focus_widget = editor->priv->content_entry; +// gtk_editable_set_position (GTK_EDITABLE (editor->priv->content_entry), -1); +// } static void -fave_icon_clicked_cb (GtkWidget *widget, - GtkEntryIconPosition position, - GdkEvent *event, - gpointer user_data) +entry_focus_changed (GtkWidget *widget, + GParamSpec *spec, + gpointer data) { - NemoQueryEditor *editor; - g_autofree gchar *current_key = NULL; + NemoQueryEditor *editor = NEMO_QUERY_EDITOR (data); - if (position == GTK_ENTRY_ICON_PRIMARY) { - return; + if (gtk_widget_is_focus (widget)) { + editor->priv->last_focus_widget = widget; } +} - editor = NEMO_QUERY_EDITOR (user_data); - - current_key = get_sanitized_query_string (editor); - - if ((event->button.state & gtk_accelerator_get_default_mod_mask ()) == 0 && event->button.button == 1) { - gchar *entry; - - if (strlen (current_key) < 3) { - return; - } - - entry = construct_favorite_entry (editor->priv->current_uri, current_key); - - if (is_search_criteria_in_faves (editor, current_key)) { - remove_key_from_faves (editor, entry); - } else { - add_key_to_faves (editor, entry); - } - - g_free (entry); +// static void +// setup_entry_history (NemoQueryEditor *editor, +// GtkComboBoxText *combo, +// const gchar *settings_key) +// { +// gchar **history_entries; +// gchar *active_text; +// gint i, n_entries; - update_fav_icon (editor); - } else { - popup_favorites (editor, event, TRUE); - } -} +// gtk_combo_box_text_remove_all (combo); -static void -on_saved_searches_setting_changed (GSettings *settings, - gchar *key, - gpointer user_data) -{ - NemoQueryEditor *editor; +// history_entries = g_settings_get_strv (nemo_search_preferences, settings_key); +// n_entries = g_strv_length (history_entries); + +// for (i = 0; i < n_entries; i++) { +// const gchar *entry = history_entries[i]; - g_return_if_fail (NEMO_IS_QUERY_EDITOR (user_data)); +// gtk_combo_box_text_prepend_text (combo, entry); +// } - editor = NEMO_QUERY_EDITOR (user_data); +// g_strfreev (history_entries); + +// editor->priv->focus_frozen = TRUE; - g_clear_pointer (&editor->priv->faves, g_strfreev); - editor->priv->faves = g_settings_get_strv (settings, key); -} +// active_text = gtk_combo_box_text_get_active_text (combo); + +// if (active_text != NULL && active_text[0] != '\0') { +// gtk_combo_box_set_active (GTK_COMBO_BOX (combo), n_entries - 1); +// } + +// g_free (active_text); + +// editor->priv->focus_frozen = FALSE; +// } + +// static void +// update_history_from_entry (NemoQueryEditor *editor, +// const gchar *key, +// gchar **history, +// GtkWidget *widget) +// { +// GPtrArray *array; +// gchar *current_search; +// gint i; + +// current_search = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget))); +// g_strstrip (current_search); // sanitize; + +// if (g_strcmp0 (current_search, "") == 0) { +// return; +// } + +// array = g_ptr_array_new_full (0, g_free); + +// g_ptr_array_add (array, (gpointer) g_strdup (current_search)); + +// if (history != NULL) { +// for (i = 0; i < g_strv_length ((gchar **) history); i++) { +// if (g_strcmp0 (history[i], current_search) == 0) { +// continue; +// } + +// g_ptr_array_add (array, (gpointer) g_strdup (history[i])); + +// if (array->len == g_settings_get_int (nemo_search_preferences, +// NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY_LENGTH)) { +// break; +// } +// } +// } + +// g_ptr_array_add (array, NULL); +// g_settings_set_strv (nemo_search_preferences, key, (const gchar * const *) array->pdata); +// g_ptr_array_free (array, TRUE); +// g_free (current_search); +// } + +// static void +// update_histories (NemoQueryEditor *editor) +// { +// NemoQueryEditorPrivate *priv; +// gchar **file_history, **content_history; + +// priv = editor->priv; + +// file_history = g_settings_get_strv (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_FILE_HISTORY); +// content_history = g_settings_get_strv (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY); + +// update_history_from_entry (editor, +// NEMO_PREFERENCES_SEARCH_FILE_HISTORY, +// file_history, +// editor->priv->file_entry); + +// update_history_from_entry (editor, +// NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY, +// content_history, +// editor->priv->content_entry); + +// g_strfreev (file_history); +// g_strfreev (content_history); + +// setup_entry_history (editor, +// GTK_COMBO_BOX_TEXT (priv->file_entry_combo), +// NEMO_PREFERENCES_SEARCH_FILE_HISTORY); + +// setup_entry_history (editor, +// GTK_COMBO_BOX_TEXT (priv->content_entry_combo), +// NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY); +// } + +// static void +// apply_privacy_pref (NemoQueryEditor *editor) +// { +// NemoQueryEditorPrivate *priv = editor->priv; + +// priv->history_enabled = g_settings_get_boolean (cinnamon_privacy_preferences, +// NEMO_PREFERENCES_RECENT_ENABLED); + +// if (!priv->history_enabled) { +// g_settings_reset (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_FILE_HISTORY); +// g_settings_reset (nemo_search_preferences, NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY); +// } + +// setup_entry_history (editor, +// GTK_COMBO_BOX_TEXT (priv->file_entry_combo), +// NEMO_PREFERENCES_SEARCH_FILE_HISTORY); +// setup_entry_history (editor, +// GTK_COMBO_BOX_TEXT (priv->content_entry_combo), +// NEMO_PREFERENCES_SEARCH_CONTENT_HISTORY); +// } static void nemo_query_editor_init (NemoQueryEditor *editor) { NemoQueryEditorPrivate *priv; + GtkBuilder *builder; GtkWidget *separator; editor->priv = G_TYPE_INSTANCE_GET_PRIVATE (editor, NEMO_TYPE_QUERY_EDITOR, NemoQueryEditorPrivate); priv = editor->priv; - priv->base_uri = NULL; - priv->menu = NULL; - priv->faves = g_settings_get_strv (nemo_preferences, - NEMO_PREFERENCES_SAVED_SEARCHES); + builder = gtk_builder_new (); + gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE); + if (!gtk_builder_add_from_resource (builder, "/org/nemo/nemo-search-bar.glade", NULL)) { + g_object_unref (builder); + return; + } + priv->builder = builder; gtk_orientable_set_orientation (GTK_ORIENTABLE (editor), GTK_ORIENTATION_VERTICAL); - priv->infobar = gtk_info_bar_new (); + priv->infobar = GTK_WIDGET (gtk_builder_get_object (builder, "search_bar")); gtk_box_pack_start (GTK_BOX (editor), priv->infobar, TRUE, TRUE, 0); - gtk_widget_set_no_show_all (priv->infobar, TRUE); - gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->infobar), GTK_MESSAGE_OTHER); - priv->entry = gtk_entry_new (); - gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->infobar))), - priv->entry, - TRUE, TRUE, 0); - gtk_widget_show (priv->entry); + priv->content_main_box = GTK_WIDGET (gtk_builder_get_object (builder, "content_main_box")); + + // TODO: Need to make a custom combo so entries can be deleted using 'Del' key + // priv->file_entry_combo = GTK_WIDGET (gtk_builder_get_object (builder, "file_entry_combo")); - gtk_entry_set_placeholder_text (GTK_ENTRY (priv->entry), _("Type to search or arrow-up to select a favorite")); + // g_signal_connect (priv->file_entry_combo, + // "notify::active-id", + // G_CALLBACK (file_combo_iter_changed), + // editor); - g_signal_connect (priv->entry, + // priv->content_entry_combo = GTK_WIDGET (gtk_builder_get_object (builder, "content_entry_combo")); + + // g_signal_connect (priv->content_entry_combo, + // "notify::active-id", + // G_CALLBACK (content_combo_iter_changed), + // editor); + + priv->file_entry = GTK_WIDGET (gtk_builder_get_object (builder, "file_search_entry")); + g_signal_connect (priv->file_entry, "activate", G_CALLBACK (entry_activate_cb), editor); + g_signal_connect (priv->file_entry, + "icon-press", + G_CALLBACK (search_icon_clicked_cb), + editor); + g_signal_connect (priv->file_entry, + "notify::is-focus", + G_CALLBACK (entry_focus_changed), + editor); - g_signal_connect (priv->entry, - "changed", - G_CALLBACK (entry_changed_cb), + priv->content_entry = GTK_WIDGET (gtk_builder_get_object (builder, "content_search_entry")); + g_signal_connect (priv->content_entry, + "activate", + G_CALLBACK (entry_activate_cb), + editor); + g_signal_connect (priv->content_entry, + "icon-press", + G_CALLBACK (search_icon_clicked_cb), + editor); + g_signal_connect (priv->content_entry, + "notify::is-focus", + G_CALLBACK (entry_focus_changed), editor); - gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->entry), - GTK_ENTRY_ICON_PRIMARY, - "edit-find-symbolic"); + priv->content_case_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "content_search_case_toggle")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->content_case_toggle), + g_settings_get_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_CONTENT_CASE)); - gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->entry), - GTK_ENTRY_ICON_SECONDARY, - "non-starred-symbolic"); + g_signal_connect (priv->content_case_toggle, + "toggled", + G_CALLBACK (content_case_button_toggled_cb), + editor); - gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->entry), - GTK_ENTRY_ICON_SECONDARY, - _("Click to save or forget a favorite search. " - "Right-click to display favorites.")); + priv->file_case_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "file_search_case_toggle")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->file_case_toggle), + g_settings_get_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_FILE_CASE)); - g_signal_connect (priv->entry, - "icon-press", - G_CALLBACK (fave_icon_clicked_cb), + g_signal_connect (priv->file_case_toggle, + "toggled", + G_CALLBACK (file_case_button_toggled_cb), editor); - separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL); - gtk_box_pack_start (GTK_BOX (editor), separator, TRUE, TRUE, 0); + priv->regex_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "content_search_regex_toggle")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->regex_toggle), + g_settings_get_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_CONTENT_REGEX)); - g_object_bind_property (priv->infobar, "visible", - separator, "visible", - G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_signal_connect (priv->regex_toggle, + "toggled", + G_CALLBACK (regex_button_toggled_cb), + editor); + + priv->file_recurse_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "file_search_recurse_toggle")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->file_recurse_toggle), + g_settings_get_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_FILES_RECURSIVELY)); - g_signal_connect (nemo_preferences, - "changed::" NEMO_PREFERENCES_SAVED_SEARCHES, - G_CALLBACK (on_saved_searches_setting_changed), + g_signal_connect (priv->file_recurse_toggle, + "toggled", + G_CALLBACK (file_recurse_button_toggled_cb), editor); + gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (builder, "button_box"))); + + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_start (GTK_BOX (editor), separator, FALSE, FALSE, 0); + gtk_widget_show (separator); + + priv->focus_chain = g_list_append (NULL, priv->file_entry); + priv->focus_chain = g_list_append (priv->focus_chain, priv->content_entry); + priv->focus_chain->prev = g_list_last (priv->focus_chain); + priv->focus_chain->prev->next = priv->focus_chain; + + // g_signal_connect_swapped (cinnamon_privacy_preferences, + // "changed::" NEMO_PREFERENCES_RECENT_ENABLED, + // G_CALLBACK (apply_privacy_pref), + // editor); + + // apply_privacy_pref (editor); + +#ifdef ENABLE_TRACKER // No options currently supported with tracker. + gtk_widget_hide (priv->content_main_box); + gtk_widget_hide (priv->file_recurse_toggle); + gtk_widget_hide (priv->file_case_toggle); +#endif + gtk_widget_show (GTK_WIDGET (editor)); } @@ -796,6 +597,10 @@ nemo_query_editor_changed_force (NemoQueryEditor *editor, gboolean force_reload) g_signal_emit (editor, signals[CHANGED], 0, query, force_reload); g_clear_object (&query); + + // if (editor->priv->history_enabled) { + // update_histories (editor); + // } } static void @@ -821,23 +626,33 @@ NemoQuery * nemo_query_editor_get_query (NemoQueryEditor *editor) { NemoQuery *query; - g_autofree gchar *query_text = NULL; + gchar *file_search_text = NULL; + const gchar *content_search_text = NULL; - if (editor == NULL || editor->priv == NULL || editor->priv->entry == NULL) { + if (editor == NULL || editor->priv == NULL || editor->priv->file_entry == NULL) { return NULL; } - query_text = get_sanitized_query_string (editor); + file_search_text = get_sanitized_file_search_string (editor); + content_search_text = gtk_entry_get_text (GTK_ENTRY (editor->priv->content_entry)); - if (g_strcmp0 (query_text, "") == 0) { - return NULL; + if (g_strcmp0 (file_search_text, "") == 0) { + g_free (file_search_text); + file_search_text = g_strdup ("*"); } query = nemo_query_new (); - nemo_query_set_text (query, query_text); + nemo_query_set_file_pattern (query, file_search_text); + nemo_query_set_content_pattern (query, content_search_text); + nemo_query_set_content_case_sensitive (query, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->content_case_toggle))); + nemo_query_set_file_case_sensitive (query, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->file_case_toggle))); + nemo_query_set_use_regex (query, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->regex_toggle))); + nemo_query_set_recurse (query, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->priv->file_recurse_toggle))); add_location_to_query (editor, query); + g_free (file_search_text); + return query; } @@ -851,27 +666,44 @@ void nemo_query_editor_set_location (NemoQueryEditor *editor, GFile *location) { + gchar *basename, *placeholder_text; + g_free (editor->priv->current_uri); editor->priv->current_uri = g_file_get_uri (location); + + basename = g_file_get_basename (location); + placeholder_text = g_strdup_printf (_("Search in %s"), basename); + + gtk_entry_set_placeholder_text (GTK_ENTRY (editor->priv->file_entry), placeholder_text); + + g_free (basename); + g_free (placeholder_text); } void nemo_query_editor_set_query (NemoQueryEditor *editor, NemoQuery *query) { - char *text = NULL; + gchar *file_pattern = NULL; + gchar *content_pattern = NULL; - if (query != NULL) { - text = nemo_query_get_text (query); - } + if (query != NULL) { + file_pattern = nemo_query_get_file_pattern (query); + content_pattern = nemo_query_get_file_pattern (query); + } - if (!text) { - text = g_strdup (""); - } + if (!file_pattern) { + file_pattern = g_strdup (""); + } + + if (!content_pattern) { + content_pattern = g_strdup (""); + } editor->priv->change_frozen = TRUE; - gtk_entry_set_text (GTK_ENTRY (editor->priv->entry), text); - gtk_widget_grab_focus (editor->priv->entry); + gtk_entry_set_text (GTK_ENTRY (editor->priv->file_entry), file_pattern); + gtk_entry_set_text (GTK_ENTRY (editor->priv->content_entry), content_pattern); + gtk_widget_grab_focus (editor->priv->file_entry); g_free (editor->priv->current_uri); editor->priv->current_uri = NULL; @@ -880,8 +712,10 @@ nemo_query_editor_set_query (NemoQueryEditor *editor, editor->priv->current_uri = nemo_query_get_location (query); } - g_free (editor->priv->last_set_query_text); - editor->priv->last_set_query_text = text; + g_free (editor->priv->last_set_query_file_pattern); + g_free (editor->priv->last_set_query_content_pattern); + editor->priv->last_set_query_file_pattern = file_pattern; + editor->priv->last_set_query_content_pattern = content_pattern; editor->priv->change_frozen = FALSE; } @@ -894,24 +728,48 @@ nemo_query_editor_set_active (NemoQueryEditor *editor, g_return_if_fail (NEMO_IS_QUERY_EDITOR (editor)); if (active) { + GFile *base; + gtk_widget_show (editor->priv->infobar); gtk_widget_queue_resize (GTK_WIDGET (editor->priv->infobar)); g_clear_pointer (&editor->priv->base_uri, g_free); editor->priv->base_uri = base_uri; - g_signal_connect (editor->priv->entry, + base = g_file_new_for_uri (base_uri); + + if (g_file_is_native (base)) { + gtk_widget_set_sensitive (editor->priv->content_main_box, TRUE); + gtk_entry_set_placeholder_text (GTK_ENTRY (editor->priv->content_entry), + _("Enter text to search for")); + } else { + gtk_widget_set_sensitive (editor->priv->content_main_box, FALSE); + gtk_entry_set_placeholder_text (GTK_ENTRY (editor->priv->content_entry), + _("Not supported in this location")); + } + + g_object_unref (base); + + g_signal_connect (editor->priv->file_entry, "key-press-event", G_CALLBACK (on_key_press_event), editor); - - update_fav_icon (editor); + g_signal_connect (editor->priv->content_entry, + "key-press-event", + G_CALLBACK (on_key_press_event), + editor); + editor->priv->last_focus_widget = editor->priv->file_entry; } else { - g_signal_handlers_disconnect_by_func (editor->priv->entry, + g_signal_handlers_disconnect_by_func (editor->priv->file_entry, + on_key_press_event, + editor); + g_signal_handlers_disconnect_by_func (editor->priv->content_entry, on_key_press_event, editor); gtk_widget_hide (editor->priv->infobar); + + editor->priv->last_focus_widget = NULL; } } diff --git a/src/nemo-tree-sidebar.c b/src/nemo-tree-sidebar.c index e1af69024..a07c540ee 100644 --- a/src/nemo-tree-sidebar.c +++ b/src/nemo-tree-sidebar.c @@ -545,7 +545,8 @@ compare_rows (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer call NEMO_FILE_SORT_BY_DISPLAY_NAME, FM_TREE_VIEW (callback_data)->details->sort_directories_first, FM_TREE_VIEW (callback_data)->details->sort_favorites_first, - FALSE); + FALSE, + NULL); } nemo_file_unref (file_a); diff --git a/src/nemo-view.c b/src/nemo-view.c index 30c91539c..b1b03aba4 100644 --- a/src/nemo-view.c +++ b/src/nemo-view.c @@ -1806,147 +1806,6 @@ hidden_files_mode_changed (NemoWindow *window, nemo_view_init_show_hidden_files (directory_view); } -static void -action_save_search_callback (GtkAction *action, - gpointer callback_data) -{ - NemoSearchDirectory *search; - NemoView *directory_view; - - directory_view = NEMO_VIEW (callback_data); - - if (directory_view->details->model && - NEMO_IS_SEARCH_DIRECTORY (directory_view->details->model)) { - search = NEMO_SEARCH_DIRECTORY (directory_view->details->model); - nemo_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) -{ - NemoView *directory_view; - NemoSearchDirectory *search; - GtkWidget *dialog, *grid, *label, *entry, *chooser, *save_button; - const char *entry_text; - char *filename, *filename_utf8, *dirname, *path, *uri; - GFile *location; - - directory_view = NEMO_VIEW (callback_data); - - if (directory_view->details->model && - NEMO_IS_SEARCH_DIRECTORY (directory_view->details->model)) { - search = NEMO_SEARCH_DIRECTORY (directory_view->details->model); - - dialog = gtk_dialog_new_with_buttons (_("Save Search as"), - nemo_view_get_containing_window (directory_view), - 0, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - NULL); - save_button = gtk_dialog_add_button (GTK_DIALOG (dialog), - GTK_STOCK_SAVE, GTK_RESPONSE_OK); - gtk_dialog_set_default_response (GTK_DIALOG (dialog), - GTK_RESPONSE_OK); - gtk_container_set_border_width (GTK_CONTAINER (dialog), 5); - gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2); - gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); - - grid = gtk_grid_new (); - g_object_set (grid, - "orientation", GTK_ORIENTATION_VERTICAL, - "border-width", 5, - "row-spacing", 6, - "column-spacing", 12, - NULL); - gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), grid, TRUE, TRUE, 0); - gtk_widget_show (grid); - - label = gtk_label_new_with_mnemonic (_("Search _name:")); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_container_add (GTK_CONTAINER (grid), label); - gtk_widget_show (label); - - entry = gtk_entry_new (); - gtk_widget_set_hexpand (entry, TRUE); - gtk_grid_attach_next_to (GTK_GRID (grid), entry, label, - GTK_POS_RIGHT, 1, 1); - gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry); - - 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_with_mnemonic (_("_Folder:")); - gtk_misc_set_alignment (GTK_MISC(label), 0.0, 0.5); - gtk_container_add (GTK_CONTAINER (grid), label); - gtk_widget_show (label); - - chooser = gtk_file_chooser_button_new (_("Select Folder to Save Search In"), - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); - gtk_widget_set_hexpand (chooser, TRUE); - gtk_grid_attach_next_to (GTK_GRID (grid), chooser, label, - GTK_POS_RIGHT, 1, 1); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser); - 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, NEMO_SAVED_SEARCH_EXTENSION)) { - filename_utf8 = g_strdup (entry_text); - } else { - filename_utf8 = g_strconcat (entry_text, NEMO_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 = g_filename_to_uri (path, NULL, NULL); - g_free (path); - - nemo_search_directory_save_to_file (search, uri); - location = g_file_new_for_uri (uri); - nemo_file_changes_queue_file_added (location); - g_object_unref (location); - nemo_file_changes_consume_changes (TRUE); - g_free (uri); - } - - gtk_widget_destroy (dialog); - } -} - - static void action_empty_trash_callback (GtkAction *action, gpointer callback_data) @@ -8543,15 +8402,6 @@ static const GtkActionEntry directory_view_entries[] = { /* label, accelerator */ N_("Open File and Close window"), "Down", /* tooltip */ NULL, G_CALLBACK (action_open_close_parent_callback) }, - /* name, stock id */ { "Save Search", NULL, - /* label, accelerator */ N_("Sa_ve Search"), NULL, - /* tooltip */ N_("Save the edited search"), - G_CALLBACK (action_save_search_callback) }, - /* name, stock id */ { "Save Search As", NULL, - /* label, accelerator */ N_("Sa_ve Search As..."), NULL, - /* tooltip */ N_("Save the current search as a file"), - G_CALLBACK (action_save_search_as_callback) }, - /* Location-specific actions */ /* name, stock id */ { NEMO_ACTION_LOCATION_OPEN_ALTERNATE, NULL, /* label, accelerator */ N_("Open in Navigation Window"), "", @@ -9775,9 +9625,6 @@ real_update_menus (NemoView *view) gboolean can_open; gboolean show_app; gboolean showing_search; - gboolean show_save_search; - gboolean save_search_sensitive; - gboolean show_save_search_as; gboolean show_desktop_target; gboolean is_desktop_view; GtkAction *action; @@ -10046,29 +9893,6 @@ real_update_menus (NemoView *view) showing_search = view->details->model && NEMO_IS_SEARCH_DIRECTORY (view->details->model); - show_save_search = FALSE; - save_search_sensitive = FALSE; - show_save_search_as = FALSE; - if (showing_search) { - NemoSearchDirectory *search; - - search = NEMO_SEARCH_DIRECTORY (view->details->model); - if (nemo_search_directory_is_saved_search (search)) { - show_save_search = TRUE; - save_search_sensitive = nemo_search_directory_is_modified (search); - } else { - show_save_search_as = TRUE; - } - } - action = gtk_action_group_get_action (view->details->dir_action_group, - NEMO_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, - NEMO_ACTION_SAVE_SEARCH_AS); - gtk_action_set_visible (action, show_save_search_as); - - action = gtk_action_group_get_action (view->details->dir_action_group, NEMO_ACTION_SELECT_ALL); gtk_action_set_sensitive (action, !nemo_view_is_empty (view)); diff --git a/src/nemo-window-slot.c b/src/nemo-window-slot.c index 6a518802b..4f521d5a4 100644 --- a/src/nemo-window-slot.c +++ b/src/nemo-window-slot.c @@ -137,6 +137,7 @@ query_editor_changed_callback (NemoQueryEditor *editor, g_assert (NEMO_IS_FILE (slot->viewed_file)); + gtk_widget_hide (slot->no_search_results_box); directory = nemo_directory_get_for_file (slot->viewed_file); if (!NEMO_IS_SEARCH_DIRECTORY (directory)) { /* this is the first change from the query editor. we @@ -195,6 +196,8 @@ void nemo_window_slot_set_query_editor_visible (NemoWindowSlot *slot, gboolean visible) { + gtk_widget_hide (slot->no_search_results_box); + if (visible) { ensure_query_editor (slot); @@ -272,6 +275,34 @@ floating_bar_action_cb (NemoFloatingBar *floating_bar, } } +static GtkWidget * +create_nsr_box (void) +{ + GtkWidget *box; + GtkWidget *widget; + PangoAttrList *attrs; + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); + + widget = gtk_image_new_from_icon_name ("system-search-symbolic", GTK_ICON_SIZE_DIALOG); + gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0); + + widget = gtk_label_new (_("No files found")); + attrs = pango_attr_list_new (); + pango_attr_list_insert (attrs, pango_attr_size_new (20 * PANGO_SCALE)); + gtk_label_set_attributes (GTK_LABEL (widget), attrs); + pango_attr_list_unref (attrs); + gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 0); + + gtk_widget_set_halign (box, GTK_ALIGN_CENTER); + gtk_widget_set_valign (box, GTK_ALIGN_CENTER); + + gtk_widget_show_all (box); + gtk_widget_set_no_show_all (box, TRUE); + gtk_widget_hide (box); + return box; +} + static void nemo_window_slot_init (NemoWindowSlot *slot) { @@ -303,6 +334,10 @@ nemo_window_slot_init (NemoWindowSlot *slot) gtk_overlay_add_overlay (GTK_OVERLAY (slot->view_overlay), slot->floating_bar); + slot->no_search_results_box = create_nsr_box (); + gtk_overlay_add_overlay (GTK_OVERLAY (slot->view_overlay), + slot->no_search_results_box); + g_signal_connect (slot->floating_bar, "action", G_CALLBACK (floating_bar_action_cb), slot); @@ -319,7 +354,22 @@ view_end_loading_cb (NemoView *view, if (slot->needs_reload) { nemo_window_slot_queue_reload (slot, FALSE); slot->needs_reload = FALSE; - } + } else if (all_files_seen) { + NemoDirectory *directory; + + directory = nemo_directory_get_for_file (slot->viewed_file); + + if (NEMO_IS_SEARCH_DIRECTORY (directory)) { + if (!nemo_directory_is_not_empty (directory)) { + gtk_widget_show (slot->no_search_results_box); + } else { + gtk_widget_hide (slot->no_search_results_box); + + } + } + + nemo_directory_unref (directory); + } } static void diff --git a/src/nemo-window-slot.h b/src/nemo-window-slot.h index 4d87b4440..1e5de44e8 100644 --- a/src/nemo-window-slot.h +++ b/src/nemo-window-slot.h @@ -69,6 +69,7 @@ struct NemoWindowSlot { GtkWidget *view_overlay; GtkWidget *floating_bar; GtkWidget *cache_bar; + GtkWidget *no_search_results_box; guint set_status_timeout_id; guint loading_timeout_id; diff --git a/test/test-nemo-directory-async.c b/test/test-nemo-directory-async.c index a8c0c3df2..0e06d5e39 100644 --- a/test/test-nemo-directory-async.c +++ b/test/test-nemo-directory-async.c @@ -86,7 +86,7 @@ main (int argc, char **argv) gtk_init (&argc, &argv); query = nemo_query_new (); - nemo_query_set_text (query, "richard hult"); + nemo_query_set_file_pattern (query, "richard hult"); directory = nemo_directory_get_by_uri ("x-nemo-search://0/"); nemo_search_directory_set_query (NEMO_SEARCH_DIRECTORY (directory), query); g_object_unref (query); diff --git a/test/test-nemo-search-engine.c b/test/test-nemo-search-engine.c index 154f26ead..8facab10c 100644 --- a/test/test-nemo-search-engine.c +++ b/test/test-nemo-search-engine.c @@ -45,7 +45,7 @@ main (int argc, char* argv[]) G_CALLBACK (finished_cb), NULL); query = nemo_query_new (); - nemo_query_set_text (query, "richard hult"); + nemo_query_set_file_pattern (query, "richard hult"); nemo_search_engine_set_query (engine, query); g_object_unref (query);