/*
* Copyright (C) 2005 Novell, Inc.
*
* Nautilus is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* Nautilus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; see the file COPYING. If not,
* see .
*
* Author: Anders Carlsson
*
*/
#include "nautilus-query.h"
#include
#include
#include "nautilus-enum-types.h"
#include "nautilus-file-utilities.h"
#include "nautilus-global-preferences.h"
#define RANK_SCALE_FACTOR 100
#define MIN_RANK 10.0
#define MAX_RANK 50.0
struct _NautilusQuery
{
GObject parent;
char *text;
GFile *location;
GPtrArray *mime_types;
gboolean show_hidden;
GPtrArray *date_range;
NautilusQueryRecursive recursive;
NautilusQuerySearchType search_type;
NautilusQuerySearchContent search_content;
gboolean searching;
char **prepared_words;
GMutex prepared_words_mutex;
};
static void nautilus_query_class_init (NautilusQueryClass *class);
static void nautilus_query_init (NautilusQuery *query);
G_DEFINE_TYPE (NautilusQuery, nautilus_query, G_TYPE_OBJECT);
enum
{
PROP_0,
PROP_DATE_RANGE,
PROP_LOCATION,
PROP_MIMETYPES,
PROP_RECURSIVE,
PROP_SEARCH_TYPE,
PROP_SEARCHING,
PROP_SHOW_HIDDEN,
PROP_TEXT,
LAST_PROP
};
static void
finalize (GObject *object)
{
NautilusQuery *query;
query = NAUTILUS_QUERY (object);
g_free (query->text);
g_strfreev (query->prepared_words);
g_clear_object (&query->location);
g_clear_pointer (&query->date_range, g_ptr_array_unref);
g_mutex_clear (&query->prepared_words_mutex);
G_OBJECT_CLASS (nautilus_query_parent_class)->finalize (object);
}
static void
nautilus_query_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
NautilusQuery *self = NAUTILUS_QUERY (object);
switch (prop_id)
{
case PROP_DATE_RANGE:
{
g_value_set_pointer (value, self->date_range);
}
break;
case PROP_LOCATION:
{
g_value_set_object (value, self->location);
}
break;
case PROP_MIMETYPES:
{
g_value_set_pointer (value, self->mime_types);
}
break;
case PROP_RECURSIVE:
{
g_value_set_enum (value, self->recursive);
}
break;
case PROP_SEARCH_TYPE:
{
g_value_set_enum (value, self->search_type);
}
break;
case PROP_SEARCHING:
{
g_value_set_boolean (value, self->searching);
}
break;
case PROP_SHOW_HIDDEN:
{
g_value_set_boolean (value, self->show_hidden);
}
break;
case PROP_TEXT:
{
g_value_set_string (value, self->text);
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
nautilus_query_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
NautilusQuery *self = NAUTILUS_QUERY (object);
switch (prop_id)
{
case PROP_DATE_RANGE:
{
nautilus_query_set_date_range (self, g_value_get_pointer (value));
}
break;
case PROP_LOCATION:
{
nautilus_query_set_location (self, g_value_get_object (value));
}
break;
case PROP_MIMETYPES:
{
nautilus_query_set_mime_types (self, g_value_get_pointer (value));
}
break;
case PROP_RECURSIVE:
{
nautilus_query_set_recursive (self, g_value_get_enum (value));
}
break;
case PROP_SEARCH_TYPE:
{
nautilus_query_set_search_type (self, g_value_get_enum (value));
}
break;
case PROP_SEARCHING:
{
nautilus_query_set_searching (self, g_value_get_boolean (value));
}
break;
case PROP_SHOW_HIDDEN:
{
nautilus_query_set_show_hidden_files (self, g_value_get_boolean (value));
}
break;
case PROP_TEXT:
{
nautilus_query_set_text (self, g_value_get_string (value));
}
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
nautilus_query_class_init (NautilusQueryClass *class)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (class);
gobject_class->finalize = finalize;
gobject_class->get_property = nautilus_query_get_property;
gobject_class->set_property = nautilus_query_set_property;
/**
* NautilusQuery::date-range:
*
* The date range of the query.
*
*/
g_object_class_install_property (gobject_class,
PROP_DATE_RANGE,
g_param_spec_pointer ("date-range",
"Date range of the query",
"The range date of the query",
G_PARAM_READWRITE));
/**
* NautilusQuery::location:
*
* The location of the query.
*
*/
g_object_class_install_property (gobject_class,
PROP_LOCATION,
g_param_spec_object ("location",
"Location of the query",
"The location of the query",
G_TYPE_FILE,
G_PARAM_READWRITE));
/**
* NautilusQuery::mimetypes: (type GPtrArray) (element-type gchar*)
*
* MIME types the query holds. An empty array means "Any type".
*
*/
g_object_class_install_property (gobject_class,
PROP_MIMETYPES,
g_param_spec_pointer ("mimetypes",
"MIME types of the query",
"The MIME types of the query",
G_PARAM_READWRITE));
/**
* NautilusQuery::recursive:
*
* Whether the query is being performed on subdirectories or not.
*
*/
g_object_class_install_property (gobject_class,
PROP_RECURSIVE,
g_param_spec_enum ("recursive",
"Whether the query is being performed on subdirectories",
"Whether the query is being performed on subdirectories or not",
NAUTILUS_TYPE_QUERY_RECURSIVE,
NAUTILUS_QUERY_RECURSIVE_ALWAYS,
G_PARAM_READWRITE));
/**
* NautilusQuery::search-type:
*
* The search type of the query.
*
*/
g_object_class_install_property (gobject_class,
PROP_SEARCH_TYPE,
g_param_spec_enum ("search-type",
"Type of the query",
"The type of the query",
NAUTILUS_TYPE_QUERY_SEARCH_TYPE,
NAUTILUS_QUERY_SEARCH_TYPE_LAST_MODIFIED,
G_PARAM_READWRITE));
/**
* NautilusQuery::searching:
*
* Whether the query is being performed or not.
*
*/
g_object_class_install_property (gobject_class,
PROP_SEARCHING,
g_param_spec_boolean ("searching",
"Whether the query is being performed",
"Whether the query is being performed or not",
FALSE,
G_PARAM_READWRITE));
/**
* NautilusQuery::show-hidden:
*
* Whether the search should include hidden files.
*
*/
g_object_class_install_property (gobject_class,
PROP_SHOW_HIDDEN,
g_param_spec_boolean ("show-hidden",
"Show hidden files",
"Whether the search should show hidden files",
FALSE,
G_PARAM_READWRITE));
/**
* NautilusQuery::text:
*
* The search string.
*
*/
g_object_class_install_property (gobject_class,
PROP_TEXT,
g_param_spec_string ("text",
"Text of the search",
"The text string of the search",
NULL,
G_PARAM_READWRITE));
}
static void
nautilus_query_init (NautilusQuery *query)
{
query->location = g_file_new_for_path (g_get_home_dir ());
query->mime_types = g_ptr_array_new ();
query->show_hidden = TRUE;
query->search_type = g_settings_get_enum (nautilus_preferences, "search-filter-time-type");
query->search_content = NAUTILUS_QUERY_SEARCH_CONTENT_SIMPLE;
g_mutex_init (&query->prepared_words_mutex);
}
static gchar *
prepare_string_for_compare (const gchar *string)
{
gchar *normalized, *res;
normalized = g_utf8_normalize (string, -1, G_NORMALIZE_NFD);
res = g_utf8_strdown (normalized, -1);
g_free (normalized);
return res;
}
gdouble
nautilus_query_matches_string (NautilusQuery *query,
const gchar *string)
{
gchar *prepared_string, *ptr;
gboolean found;
gdouble retval;
gint idx, nonexact_malus;
if (!query->text)
{
return -1;
}
g_mutex_lock (&query->prepared_words_mutex);
if (!query->prepared_words)
{
prepared_string = prepare_string_for_compare (query->text);
query->prepared_words = g_strsplit (prepared_string, " ", -1);
g_free (prepared_string);
}
prepared_string = prepare_string_for_compare (string);
found = TRUE;
ptr = NULL;
nonexact_malus = 0;
for (idx = 0; query->prepared_words[idx] != NULL; idx++)
{
if ((ptr = strstr (prepared_string, query->prepared_words[idx])) == NULL)
{
found = FALSE;
break;
}
nonexact_malus += strlen (ptr) - strlen (query->prepared_words[idx]);
}
g_mutex_unlock (&query->prepared_words_mutex);
if (!found)
{
g_free (prepared_string);
return -1;
}
/* The rank value depends on the numbers of letters before and after the match.
* To make the prefix matches prefered over sufix ones, the number of letters
* after the match is divided by a factor, so that it decreases the rank by a
* smaller amount.
*/
retval = MAX (MIN_RANK, MAX_RANK - (gdouble) (ptr - prepared_string) - (gdouble) nonexact_malus / RANK_SCALE_FACTOR);
g_free (prepared_string);
return retval;
}
NautilusQuery *
nautilus_query_new (void)
{
return g_object_new (NAUTILUS_TYPE_QUERY, NULL);
}
char *
nautilus_query_get_text (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), NULL);
return g_strdup (query->text);
}
void
nautilus_query_set_text (NautilusQuery *query,
const char *text)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
g_free (query->text);
query->text = g_strstrip (g_strdup (text));
g_mutex_lock (&query->prepared_words_mutex);
g_strfreev (query->prepared_words);
query->prepared_words = NULL;
g_mutex_unlock (&query->prepared_words_mutex);
g_object_notify (G_OBJECT (query), "text");
}
GFile *
nautilus_query_get_location (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), NULL);
return g_object_ref (query->location);
}
void
nautilus_query_set_location (NautilusQuery *query,
GFile *location)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
if (g_set_object (&query->location, location))
{
g_object_notify (G_OBJECT (query), "location");
}
}
/**
* nautilus_query_get_mime_type:
* @query: A #NautilusQuery
*
* Retrieves the current MIME Types filter from @query. Its content must not be
* modified. It can be read by multiple threads.
*
* Returns: (transfer container) A #GPtrArray reference with MIME type name strings.
*/
GPtrArray *
nautilus_query_get_mime_types (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), NULL);
return g_ptr_array_ref (query->mime_types);
}
/**
* nautilus_query_set_mime_types:
* @query: A #NautilusQuery
* @mime_types: (transfer none): A #GPtrArray of MIME type strings
*
* Set a new MIME types filter for @query. Once set, the filter must not be
* modified, and it can only be replaced by setting another filter.
*
* Search engines that are already running for a previous filter will ignore the
* new filter. So, the caller must ensure that the search will be reloaded
* afterwards.
*/
void
nautilus_query_set_mime_types (NautilusQuery *query,
GPtrArray *mime_types)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
g_return_if_fail (mime_types != NULL);
g_clear_pointer (&query->mime_types, g_ptr_array_unref);
query->mime_types = g_ptr_array_ref (mime_types);
g_object_notify (G_OBJECT (query), "mimetypes");
}
gboolean
nautilus_query_get_show_hidden_files (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), FALSE);
return query->show_hidden;
}
void
nautilus_query_set_show_hidden_files (NautilusQuery *query,
gboolean show_hidden)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
if (query->show_hidden != show_hidden)
{
query->show_hidden = show_hidden;
g_object_notify (G_OBJECT (query), "show-hidden");
}
}
char *
nautilus_query_to_readable_string (NautilusQuery *query)
{
if (!query || !query->text || query->text[0] == '\0')
{
return g_strdup (_("Search"));
}
return g_strdup_printf (_("Search for ā%sā"), query->text);
}
NautilusQuerySearchContent
nautilus_query_get_search_content (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), -1);
return query->search_content;
}
void
nautilus_query_set_search_content (NautilusQuery *query,
NautilusQuerySearchContent content)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
if (query->search_content != content)
{
query->search_content = content;
g_object_notify (G_OBJECT (query), "search-type");
}
}
NautilusQuerySearchType
nautilus_query_get_search_type (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), -1);
return query->search_type;
}
void
nautilus_query_set_search_type (NautilusQuery *query,
NautilusQuerySearchType type)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
if (query->search_type != type)
{
query->search_type = type;
g_object_notify (G_OBJECT (query), "search-type");
}
}
/**
* nautilus_query_get_date_range:
* @query: a #NautilusQuery
*
* Retrieves the #GptrArray composed of #GDateTime representing the date range.
* This function is thread safe.
*
* Returns: (transfer full): the #GptrArray composed of #GDateTime representing the date range.
*/
GPtrArray *
nautilus_query_get_date_range (NautilusQuery *query)
{
static GMutex mutex;
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), NULL);
g_mutex_lock (&mutex);
if (query->date_range)
{
g_ptr_array_ref (query->date_range);
}
g_mutex_unlock (&mutex);
return query->date_range;
}
void
nautilus_query_set_date_range (NautilusQuery *query,
GPtrArray *date_range)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
g_clear_pointer (&query->date_range, g_ptr_array_unref);
if (date_range)
{
query->date_range = g_ptr_array_ref (date_range);
}
g_object_notify (G_OBJECT (query), "date-range");
}
gboolean
nautilus_query_get_searching (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query), FALSE);
return query->searching;
}
void
nautilus_query_set_searching (NautilusQuery *query,
gboolean searching)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
searching = !!searching;
if (query->searching != searching)
{
query->searching = searching;
g_object_notify (G_OBJECT (query), "searching");
}
}
NautilusQueryRecursive
nautilus_query_get_recursive (NautilusQuery *query)
{
g_return_val_if_fail (NAUTILUS_IS_QUERY (query),
NAUTILUS_QUERY_RECURSIVE_ALWAYS);
return query->recursive;
}
void
nautilus_query_set_recursive (NautilusQuery *query,
NautilusQueryRecursive recursive)
{
g_return_if_fail (NAUTILUS_IS_QUERY (query));
if (query->recursive != recursive)
{
query->recursive = recursive;
g_object_notify (G_OBJECT (query), "recursive");
}
}
gboolean
nautilus_query_is_empty (NautilusQuery *query)
{
if (!query)
{
return TRUE;
}
if (!query->date_range &&
(!query->text || (query->text && query->text[0] == '\0')) &&
query->mime_types->len == 0)
{
return TRUE;
}
return FALSE;
}