gth-search-task.c 12.8 KB
Newer Older
Paolo Bacchilega's avatar
Paolo Bacchilega committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  GThumb
 *
 *  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
19
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Paolo Bacchilega's avatar
Paolo Bacchilega committed
20
21
22
23
24
25
26
27
28
29
 */

#include <config.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gthumb.h>
#include <extensions/catalogs/gth-catalog.h>
#include "gth-search-task.h"


30
struct _GthSearchTaskPrivate {
31
32
33
34
	GthBrowser    *browser;
	GthSearch     *search;
	GthTestChain  *test;
	GFile         *search_catalog;
35
	gboolean       show_hidden_files;
36
37
38
39
40
	gboolean       io_operation;
	GError        *error;
	gulong         location_ready_id;
	GtkWidget     *dialog;
	GthFileSource *file_source;
41
	gsize          n_files;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
42
43
44
};


45
46
47
48
49
50
G_DEFINE_TYPE_WITH_CODE (GthSearchTask,
			 gth_search_task,
			 GTH_TYPE_TASK,
			 G_ADD_PRIVATE (GthSearchTask))


Paolo Bacchilega's avatar
Paolo Bacchilega committed
51
52
53
54
55
56
57
58
59
static void
browser_unref_cb (gpointer  data,
		  GObject  *browser)
{
	((GthSearchTask *) data)->priv->browser = NULL;
}


static void
60
gth_search_task_finalize (GObject *object)
Paolo Bacchilega's avatar
Paolo Bacchilega committed
61
62
63
64
65
{
	GthSearchTask *task;

	task = GTH_SEARCH_TASK (object);

66
67
68
69
70
71
	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);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
72

73
	G_OBJECT_CLASS (gth_search_task_parent_class)->finalize (object);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
74
75
76
77
78
79
}


typedef struct {
	GthBrowser    *browser;
	GthSearchTask *task;
80
	gulong         response_id;
81
} InfoBarData;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
82
83
84


static void
85
86
87
info_bar_response_cb (GtkInfoBar *info_bar,
		      int         response_id,
		      gpointer    user_data)
Paolo Bacchilega's avatar
Paolo Bacchilega committed
88
{
89
	InfoBarData *data = user_data;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
90
91
92

	switch (response_id) {
	case GTK_RESPONSE_CANCEL:
93
		gtk_widget_hide (data->task->priv->dialog);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
94
95
		gth_task_cancel (GTH_TASK (data->task));
		break;
96

Paolo Bacchilega's avatar
Paolo Bacchilega committed
97
98
99
	default:
		break;
	}
100

101
	g_signal_handler_disconnect (info_bar, data->response_id);
102
	g_free (data);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
103
104
105
106
}


static void
107
108
109
110
save_search_result_copy_done_cb (void     **buffer,
				 gsize      count,
				 GError    *error,
				 gpointer   user_data)
Paolo Bacchilega's avatar
Paolo Bacchilega committed
111
112
113
114
{
	GthSearchTask *task = user_data;

	task->priv->io_operation = FALSE;
115
116
117
118
119
120

	gth_browser_update_extra_widget (task->priv->browser);
	gth_monitor_folder_changed (gth_main_get_default_monitor (),
				    task->priv->search_catalog,
				    gth_catalog_get_file_list (GTH_CATALOG (task->priv->search)),
				    GTH_MONITOR_EVENT_CREATED);
121
	gtk_widget_hide (task->priv->dialog);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
122
123
124
125
126
	gth_task_completed (GTH_TASK (task), task->priv->error);
}


static void
127
128
done_func (GObject  *object,
	   GError   *error,
Paolo Bacchilega's avatar
Paolo Bacchilega committed
129
130
131
132
133
134
135
136
	   gpointer  user_data)
{
	GthSearchTask *task = user_data;
	DomDocument   *doc;
	char          *data;
	gsize          size;
	GFile         *search_result_real_file;

137
	gth_info_bar_set_secondary_text (GTH_INFO_BAR (task->priv->dialog), NULL);
138

Paolo Bacchilega's avatar
Paolo Bacchilega committed
139
140
141
142
143
	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);
144
145
146
147

			/* reset the cancellable because it's re-used below to
			 * save the partial result. */
			g_cancellable_reset (gth_task_get_cancellable (GTH_TASK (task)));
Paolo Bacchilega's avatar
Paolo Bacchilega committed
148
149
150
151
152
153
154
155
156
157
158
159
		}
		else
			task->priv->error = error;
	}

	/* save the search result */

	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);
160
161
162
163
164
165
166
167
	_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);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
168
169
170
171
172
173

	g_object_unref (search_result_real_file);
	g_object_unref (doc);
}


174
175
176
static void
update_secondary_text (GthSearchTask *task)
{
177
	char *format_str;
178
179
	char *msg;

180
	format_str = g_strdup_printf ("%"G_GSIZE_FORMAT, task->priv->n_files);
181
	msg = g_strdup_printf (_("Files found so far: %s"), format_str);
182
	gth_info_bar_set_secondary_text (GTH_INFO_BAR (task->priv->dialog), msg);
183

184
	g_free (format_str);
185
186
187
188
	g_free (msg);
}


Paolo Bacchilega's avatar
Paolo Bacchilega committed
189
190
191
192
193
194
195
196
197
198
199
200
201
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);

202
	if (gth_test_match (GTH_TEST (task->priv->test), file_data)) {
203
204
		GList *file_list;

205
		gth_catalog_insert_file (GTH_CATALOG (task->priv->search), file_data->file, -1);
206

207
208
		task->priv->n_files++;
		update_secondary_text (task);
209
210
211
212
213
214
215
216

		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);
217
	}
Paolo Bacchilega's avatar
Paolo Bacchilega committed
218
219
220
221
222

	g_object_unref (file_data);
}


223
224
225
226
227
228
229
230
231
232
233
234
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);

}


Paolo Bacchilega's avatar
Paolo Bacchilega committed
235
236
237
238
239
240
241
242
243
244
static DirOp
start_dir_func (GFile      *directory,
		GFileInfo  *info,
		GError    **error,
		gpointer    user_data)
{
	GthSearchTask *task = user_data;
	char          *uri;
	char          *text;

245
246
247
	if (! file_is_visible (task, info))
		return DIR_OP_SKIP;

248
	uri = g_file_get_parse_name (directory);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
249
	text = g_strdup_printf ("Searching in %s", uri);
250
	gth_info_bar_set_primary_text (GTH_INFO_BAR (task->priv->dialog), text);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
251
252
253
254
255
256
257
258
259
260
261
262
263
264

	g_free (text);
	g_free (uri);

	return DIR_OP_CONTINUE;
}


static void
browser_location_ready_cb (GthBrowser    *browser,
			   GFile         *folder,
			   gboolean       error,
			   GthSearchTask *task)
{
265
	GtkWidget          *button;
266
	InfoBarData *dialog_data;
267
	GSettings          *settings;
268
269
	GString            *attributes;
	const char         *test_attributes;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
270
271
272
273

	g_signal_handler_disconnect (task->priv->browser, task->priv->location_ready_id);

	if (error) {
274
		gtk_widget_hide (task->priv->dialog);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
275
276
277
278
		gth_task_completed (GTH_TASK (task), NULL);
		return;
	}

279
280
	task->priv->n_files = 0;

281
282
	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);
283
	gth_info_bar_set_primary_text (GTH_INFO_BAR (task->priv->dialog), _("Searching…"));
284
	update_secondary_text (task);
285
	_gtk_info_bar_clear_action_area (GTK_INFO_BAR (task->priv->dialog));
286
287
	gtk_widget_show (task->priv->dialog);

288
	button = gtk_button_new ();
289
	gtk_container_add (GTK_CONTAINER (button), gtk_image_new_from_icon_name ("process-stop-symbolic", GTK_ICON_SIZE_BUTTON));
290
291
	gtk_widget_set_tooltip_text (button, _("Cancel the operation"));
	gtk_widget_show_all (button);
292
293
294
	gtk_info_bar_add_action_widget (GTK_INFO_BAR (task->priv->dialog),
					button,
					GTK_RESPONSE_CANCEL);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
295

296
	dialog_data = g_new0 (InfoBarData, 1);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
297
298
	dialog_data->browser = task->priv->browser;
	dialog_data->task = task;
299
300
	dialog_data->response_id = g_signal_connect (task->priv->dialog,
						     "response",
301
						     G_CALLBACK (info_bar_response_cb),
302
						     dialog_data);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
303
304
305
306
307
308
309
310
311
312

	/**/

	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;
313
		GthTest *test_with_general_filter;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
314
315

		general_filter = gth_main_get_general_filter ();
316
317
318
319
320
321
322
		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;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
323
324
325
326

		g_object_unref (general_filter);
	}

327
328
329
	settings = g_settings_new (GTHUMB_BROWSER_SCHEMA);

	task->priv->show_hidden_files = g_settings_get_boolean (settings, PREF_BROWSER_SHOW_HIDDEN_FILES);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
330
	task->priv->io_operation = TRUE;
331
332
333

	task->priv->file_source = gth_main_get_file_source (gth_search_get_folder (task->priv->search));
	gth_file_source_set_cancellable (task->priv->file_source, gth_task_get_cancellable (GTH_TASK (task)));
334

335
	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);
336
337
338
339
340
341
	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);
	}

342
343
344
	gth_file_source_for_each_child (task->priv->file_source,
					gth_search_get_folder (task->priv->search),
					gth_search_is_recursive (task->priv->search),
345
					attributes->str,
346
347
348
349
					start_dir_func,
					for_each_file_func,
					done_func,
					task);
350

351
	g_object_unref (settings);
352
	g_string_free (attributes, TRUE);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
353
354
355
356
}


static void
357
358
359
360
clear_search_result_copy_done_cb (void     **buffer,
				  gsize      count,
				  GError    *error,
				  gpointer   user_data)
Paolo Bacchilega's avatar
Paolo Bacchilega committed
361
362
{
	GthSearchTask *task = user_data;
363
364
	GFile         *parent;
	GList         *files;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
365

366
367
	task->priv->io_operation = FALSE;

368
	if (error != NULL) {
369
		_gtk_error_dialog_from_gerror_show (GTK_WINDOW (task->priv->browser), _("Could not create the catalog"), error);
370
371
372
		return;
	}

373
374
375
376
377
378
379
380
381
382
	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);

Paolo Bacchilega's avatar
Paolo Bacchilega committed
383
384
385
386
	task->priv->location_ready_id = g_signal_connect (task->priv->browser,
							  "location-ready",
							  G_CALLBACK (browser_location_ready_cb),
							  task);
387
	gth_browser_go_to (task->priv->browser, task->priv->search_catalog, NULL);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
}


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 */

404
405
	task->priv->io_operation = TRUE;

Paolo Bacchilega's avatar
Paolo Bacchilega committed
406
407
408
409
410
	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);
411
412
413
414
415
416
417
418
	_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);
Paolo Bacchilega's avatar
Paolo Bacchilega committed
419
420
421
422
423
424
425

	g_object_unref (search_result_real_file);
	g_object_unref (doc);
}


static void
426
gth_search_task_cancelled (GthTask *task)
Paolo Bacchilega's avatar
Paolo Bacchilega committed
427
{
428
429
	if (! GTH_SEARCH_TASK (task)->priv->io_operation) {
		gtk_widget_hide (GTH_SEARCH_TASK (task)->priv->dialog);
430
		gth_task_completed (task, g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_CANCELLED, ""));
431
	}
Paolo Bacchilega's avatar
Paolo Bacchilega committed
432
433
434
435
436
437
438
439
440
441
}


static void
gth_search_task_class_init (GthSearchTaskClass *class)
{
	GObjectClass *object_class;
	GthTaskClass *task_class;

	object_class = (GObjectClass*) class;
442
	object_class->finalize = gth_search_task_finalize;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
443
444
445

	task_class = (GthTaskClass*) class;
	task_class->exec = gth_search_task_exec;
446
	task_class->cancelled = gth_search_task_cancelled;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
447
448
449
450
451
452
}


static void
gth_search_task_init (GthSearchTask *task)
{
453
454
455
456
457
458
459
460
461
462
463
464
	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;
Paolo Bacchilega's avatar
Paolo Bacchilega committed
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
}


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;
}