/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2009 Free Software Foundation, 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, see .
*/
#include
#include
#include
#include
#include
#include "gth-search-source.h"
#include "gth-search-task.h"
struct _GthSearchTaskPrivate {
GthBrowser *browser;
GthSearch *search;
GthTestChain *test;
GFile *search_catalog;
gboolean show_hidden_files;
gboolean io_operation;
GError *error;
gulong location_ready_id;
GtkWidget *dialog;
GthFileSource *file_source;
gsize n_files;
GList *current_location;
gulong info_bar_response_id;
};
G_DEFINE_TYPE_WITH_CODE (GthSearchTask,
gth_search_task,
GTH_TYPE_TASK,
G_ADD_PRIVATE (GthSearchTask))
static void
browser_unref_cb (gpointer data,
GObject *browser)
{
((GthSearchTask *) data)->priv->browser = NULL;
}
static void
gth_search_task_finalize (GObject *object)
{
GthSearchTask *task;
task = GTH_SEARCH_TASK (object);
_g_object_unref (task->priv->file_source);
_g_object_unref (task->priv->search);
_g_object_unref (task->priv->test);
_g_object_unref (task->priv->search_catalog);
if (task->priv->browser != NULL)
g_object_weak_unref (G_OBJECT (task->priv->browser), browser_unref_cb, task);
G_OBJECT_CLASS (gth_search_task_parent_class)->finalize (object);
}
static void
info_bar_response_cb (GtkInfoBar *info_bar,
int response_id,
gpointer user_data)
{
GthSearchTask *task = user_data;
switch (response_id) {
case GTK_RESPONSE_CANCEL:
if (task->priv->info_bar_response_id != 0) {
g_signal_handler_disconnect (task->priv->dialog, task->priv->info_bar_response_id);
task->priv->info_bar_response_id = 0;
}
gth_task_cancel (GTH_TASK (task));
break;
default:
break;
}
}
static void
save_search_result_copy_done_cb (void **buffer,
gsize count,
GError *error,
gpointer user_data)
{
GthSearchTask *task = user_data;
task->priv->io_operation = FALSE;
if (task->priv->n_files == 0)
gth_file_list_clear (GTH_FILE_LIST (gth_browser_get_file_list (task->priv->browser)), _("No file found"));
gth_browser_update_extra_widget (task->priv->browser);
gtk_widget_hide (task->priv->dialog);
gth_task_completed (GTH_TASK (task), task->priv->error);
}
static void
_gth_search_task_save_search_result (GthSearchTask *task)
{
DomDocument *doc;
char *data;
gsize size;
GFile *search_result_real_file;
if (task->priv->info_bar_response_id != 0) {
g_signal_handler_disconnect (task->priv->dialog, task->priv->info_bar_response_id);
task->priv->info_bar_response_id = 0;
}
doc = dom_document_new ();
dom_element_append_child (DOM_ELEMENT (doc), dom_domizable_create_element (DOM_DOMIZABLE (task->priv->search), doc));
data = dom_document_dump (doc, &size);
search_result_real_file = gth_catalog_file_to_gio_file (task->priv->search_catalog);
_g_file_write_async (search_result_real_file,
data,
size,
TRUE,
G_PRIORITY_DEFAULT,
gth_task_get_cancellable (GTH_TASK (task)),
save_search_result_copy_done_cb,
task);
g_object_unref (search_result_real_file);
g_object_unref (doc);
}
static void
_gth_search_task_search_current_location (GthSearchTask *task);
static void
done_func (GObject *object,
GError *error,
gpointer user_data)
{
GthSearchTask *task = user_data;
task->priv->error = NULL;
if (error != NULL) {
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
task->priv->error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_CANCELLED, "");
g_error_free (error);
/* reset the cancellable because it's re-used below to
* save the partial result. */
g_cancellable_reset (gth_task_get_cancellable (GTH_TASK (task)));
}
else
task->priv->error = error;
_gth_search_task_save_search_result (task);
return;
}
task->priv->current_location = g_list_next (task->priv->current_location);
_gth_search_task_search_current_location (task);
}
static void
update_secondary_text (GthSearchTask *task)
{
char *format_str;
char *msg;
format_str = g_strdup_printf ("%"G_GSIZE_FORMAT, task->priv->n_files);
msg = g_strdup_printf (_("Files found so far: %s"), format_str);
gth_info_bar_set_secondary_text (GTH_INFO_BAR (task->priv->dialog), msg);
g_free (format_str);
g_free (msg);
}
static void
for_each_file_func (GFile *file,
GFileInfo *info,
gpointer user_data)
{
GthSearchTask *task = user_data;
GthFileData *file_data;
if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR)
return;
file_data = gth_file_data_new (file, info);
if (gth_test_match (GTH_TEST (task->priv->test), file_data)
&& gth_catalog_insert_file (GTH_CATALOG (task->priv->search), file_data->file, -1))
{
GList *file_list;
task->priv->n_files++;
update_secondary_text (task);
file_list = g_list_prepend (NULL, file_data->file);
gth_monitor_folder_changed (gth_main_get_default_monitor (),
task->priv->search_catalog,
file_list,
GTH_MONITOR_EVENT_CREATED);
g_list_free (file_list);
}
g_object_unref (file_data);
}
static gboolean
file_is_visible (GthSearchTask *task,
GFileInfo *info)
{
if (task->priv->show_hidden_files)
return TRUE;
else
return ! g_file_info_get_is_hidden (info);
}
static DirOp
start_dir_func (GFile *directory,
GFileInfo *info,
GError **error,
gpointer user_data)
{
GthSearchTask *task = user_data;
char *uri;
char *text;
if (! file_is_visible (task, info))
return DIR_OP_SKIP;
uri = g_file_get_parse_name (directory);
text = g_strdup_printf ("Searching in %s", uri);
gth_info_bar_set_primary_text (GTH_INFO_BAR (task->priv->dialog), text);
g_free (text);
g_free (uri);
return DIR_OP_CONTINUE;
}
static void
_gth_search_task_search_current_location (GthSearchTask *task)
{
GthSearchSource *search_location;
GSettings *settings;
GString *attributes;
const char *test_attributes;
if (task->priv->current_location == NULL) {
gth_info_bar_set_secondary_text (GTH_INFO_BAR (task->priv->dialog), NULL);
_gth_search_task_save_search_result (task);
return;
}
settings = g_settings_new (PIX_BROWSER_SCHEMA);
task->priv->show_hidden_files = g_settings_get_boolean (settings, PREF_BROWSER_SHOW_HIDDEN_FILES);
search_location = GTH_SEARCH_SOURCE (task->priv->current_location->data);
task->priv->file_source = gth_main_get_file_source (gth_search_source_get_folder (search_location));
gth_file_source_set_cancellable (task->priv->file_source, gth_task_get_cancellable (GTH_TASK (task)));
attributes = g_string_new (g_settings_get_boolean (settings, PREF_BROWSER_FAST_FILE_TYPE) ? GFILE_STANDARD_ATTRIBUTES_WITH_FAST_CONTENT_TYPE : GFILE_STANDARD_ATTRIBUTES_WITH_CONTENT_TYPE);
test_attributes = gth_test_get_attributes (GTH_TEST (task->priv->test));
if (test_attributes[0] != '\0') {
g_string_append (attributes, ",");
g_string_append (attributes, test_attributes);
}
task->priv->io_operation = TRUE;
gth_file_source_for_each_child (task->priv->file_source,
gth_search_source_get_folder (search_location),
gth_search_source_is_recursive (search_location),
attributes->str,
start_dir_func,
for_each_file_func,
done_func,
task);
g_string_free (attributes, TRUE);
g_object_unref (settings);
}
static void
browser_location_ready_cb (GthBrowser *browser,
GFile *folder,
gboolean error,
GthSearchTask *task)
{
GtkWidget *button;
if (! _g_file_equal (folder, task->priv->search_catalog))
return;
g_signal_handler_disconnect (task->priv->browser, task->priv->location_ready_id);
if (error) {
if (task->priv->dialog != NULL)
gtk_widget_hide (task->priv->dialog);
gth_task_completed (GTH_TASK (task), NULL);
return;
}
task->priv->n_files = 0;
gth_file_list_clear (GTH_FILE_LIST (gth_browser_get_file_list (browser)), _("Searching…"));
task->priv->dialog = gth_browser_get_list_info_bar (browser);
gth_info_bar_set_icon_name (GTH_INFO_BAR (task->priv->dialog), "edit-find-symbolic", GTK_ICON_SIZE_BUTTON);
gth_info_bar_set_primary_text (GTH_INFO_BAR (task->priv->dialog), _("Searching…"));
update_secondary_text (task);
_gtk_info_bar_clear_action_area (GTK_INFO_BAR (task->priv->dialog));
gtk_widget_show (task->priv->dialog);
button = gtk_button_new ();
gtk_container_add (GTK_CONTAINER (button), gtk_image_new_from_icon_name ("process-stop-symbolic", GTK_ICON_SIZE_BUTTON));
gtk_widget_set_tooltip_text (button, _("Cancel the operation"));
gtk_widget_show_all (button);
gtk_info_bar_add_action_widget (GTK_INFO_BAR (task->priv->dialog),
button,
GTK_RESPONSE_CANCEL);
task->priv->info_bar_response_id = g_signal_connect (task->priv->dialog,
"response",
G_CALLBACK (info_bar_response_cb),
task);
/**/
if (gth_search_get_test (task->priv->search) != NULL)
task->priv->test = (GthTestChain*) gth_duplicable_duplicate (GTH_DUPLICABLE (gth_search_get_test (task->priv->search)));
else
task->priv->test = (GthTestChain*) gth_test_chain_new (GTH_MATCH_TYPE_ALL, NULL);
if (! gth_test_chain_has_type_test (task->priv->test)) {
GthTest *general_filter;
GthTest *test_with_general_filter;
general_filter = gth_main_get_general_filter ();
test_with_general_filter = gth_test_chain_new (GTH_MATCH_TYPE_ALL,
general_filter,
task->priv->test,
NULL);
g_object_unref (task->priv->test);
task->priv->test = (GthTestChain *) test_with_general_filter;
g_object_unref (general_filter);
}
task->priv->current_location = gth_search_get_sources (task->priv->search);
_gth_search_task_search_current_location (task);
}
static void
clear_search_result_copy_done_cb (void **buffer,
gsize count,
GError *error,
gpointer user_data)
{
GthSearchTask *task = user_data;
GFile *parent;
GList *files;
task->priv->io_operation = FALSE;
if (error != NULL) {
_gtk_error_dialog_from_gerror_show (GTK_WINDOW (task->priv->browser), _("Could not create the catalog"), error);
return;
}
parent = g_file_get_parent (task->priv->search_catalog);
files = g_list_prepend (NULL, g_object_ref (task->priv->search_catalog));
gth_monitor_folder_changed (gth_main_get_default_monitor (),
parent,
files,
GTH_MONITOR_EVENT_CREATED);
_g_object_list_unref (files);
g_object_unref (parent);
task->priv->location_ready_id = g_signal_connect (task->priv->browser,
"location-ready",
G_CALLBACK (browser_location_ready_cb),
task);
gth_browser_go_to (task->priv->browser, task->priv->search_catalog, NULL);
}
static void
gth_search_task_exec (GthTask *base)
{
GthSearchTask *task = (GthSearchTask *) base;
DomDocument *doc;
char *data;
gsize size;
GFile *search_result_real_file;
gth_catalog_set_file_list (GTH_CATALOG (task->priv->search), NULL);
/* save the search result */
task->priv->io_operation = TRUE;
doc = dom_document_new ();
dom_element_append_child (DOM_ELEMENT (doc), dom_domizable_create_element (DOM_DOMIZABLE (task->priv->search), doc));
data = dom_document_dump (doc, &size);
search_result_real_file = gth_catalog_file_to_gio_file (task->priv->search_catalog);
_g_file_write_async (search_result_real_file,
data,
size,
TRUE,
G_PRIORITY_DEFAULT,
gth_task_get_cancellable (GTH_TASK (task)),
clear_search_result_copy_done_cb,
task);
g_object_unref (search_result_real_file);
g_object_unref (doc);
}
static void
gth_search_task_cancelled (GthTask *base)
{
GthSearchTask *task = (GthSearchTask *) base;
if (! task->priv->io_operation) {
if (task->priv->dialog != NULL)
gtk_widget_hide (task->priv->dialog);
gth_task_completed (GTH_TASK (task), g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_CANCELLED, ""));
}
}
static void
gth_search_task_class_init (GthSearchTaskClass *class)
{
GObjectClass *object_class;
GthTaskClass *task_class;
object_class = (GObjectClass*) class;
object_class->finalize = gth_search_task_finalize;
task_class = (GthTaskClass*) class;
task_class->exec = gth_search_task_exec;
task_class->cancelled = gth_search_task_cancelled;
}
static void
gth_search_task_init (GthSearchTask *task)
{
task->priv = gth_search_task_get_instance_private (task);
task->priv->browser = NULL;
task->priv->search = NULL;
task->priv->test = NULL;
task->priv->search_catalog = NULL;
task->priv->show_hidden_files = FALSE;
task->priv->io_operation = FALSE;
task->priv->error = NULL;
task->priv->location_ready_id = 0;
task->priv->dialog = NULL;
task->priv->file_source = NULL;
task->priv->n_files = 0;
task->priv->current_location = NULL;
task->priv->info_bar_response_id = 0;
}
GthTask *
gth_search_task_new (GthBrowser *browser,
GthSearch *search,
GFile *search_catalog)
{
GthSearchTask *task;
task = (GthSearchTask *) g_object_new (GTH_TYPE_SEARCH_TASK, NULL);
task->priv->browser = browser;
g_object_weak_ref (G_OBJECT (task->priv->browser), browser_unref_cb, task);
task->priv->search = g_object_ref (search);
task->priv->search_catalog = g_object_ref (search_catalog);
return (GthTask*) task;
}
GFile *
gth_search_task_get_catalog (GthSearchTask *task)
{
g_return_val_if_fail (GTH_IS_SEARCH_TASK (task), NULL);
return task->priv->search_catalog;
}