1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2009 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * 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/>.
20 */
21
22 #include <config.h>
23 #include <glib/gi18n.h>
24 #include "glib-utils.h"
25 #include "gth-main.h"
26 #include "gth-overwrite-dialog.h"
27 #include "gth-image.h"
28 #include "gth-image-list-task.h"
29 #include "gth-image-loader.h"
30 #include "gth-image-saver.h"
31 #include "gtk-utils.h"
32
33
34 struct _GthImageListTaskPrivate {
35 GthBrowser *browser;
36 GList *file_list;
37 GthTask *task;
38 gulong task_completed;
39 gulong task_progress;
40 gulong task_dialog;
41 GList *current;
42 int n_current;
43 int n_files;
44 GthImage *original_image;
45 GthImage *new_image;
46 GFile *destination_folder;
47 GthFileData *destination_file_data;
48 GthOverwriteMode overwrite_mode;
49 GthOverwriteResponse overwrite_response;
50 char *mime_type;
51 };
52
53
G_DEFINE_TYPE_WITH_CODE(GthImageListTask,gth_image_list_task,GTH_TYPE_TASK,G_ADD_PRIVATE (GthImageListTask))54 G_DEFINE_TYPE_WITH_CODE (GthImageListTask,
55 gth_image_list_task,
56 GTH_TYPE_TASK,
57 G_ADD_PRIVATE (GthImageListTask))
58
59
60 static void
61 gth_image_list_task_finalize (GObject *object)
62 {
63 GthImageListTask *self;
64
65 self = GTH_IMAGE_LIST_TASK (object);
66
67 g_free (self->priv->mime_type);
68 _g_object_unref (self->priv->destination_folder);
69 _g_object_unref (self->priv->original_image);
70 _g_object_unref (self->priv->new_image);
71 g_signal_handler_disconnect (self->priv->task, self->priv->task_completed);
72 g_signal_handler_disconnect (self->priv->task, self->priv->task_progress);
73 g_signal_handler_disconnect (self->priv->task, self->priv->task_dialog);
74 g_object_unref (self->priv->task);
75 _g_object_list_unref (self->priv->file_list);
76 _g_object_unref (self->priv->destination_file_data);
77
78 G_OBJECT_CLASS (gth_image_list_task_parent_class)->finalize (object);
79 }
80
81
82 static void process_current_file (GthImageListTask *self);
83
84
85 static void
process_next_file(GthImageListTask * self)86 process_next_file (GthImageListTask *self)
87 {
88 self->priv->n_current++;
89 self->priv->current = self->priv->current->next;
90 process_current_file (self);
91 }
92
93
94 static void image_task_save_current_image (GthImageListTask *self,
95 GFile *file,
96 gboolean replace);
97
98
99 static void
overwrite_dialog_response_cb(GtkDialog * dialog,gint response_id,gpointer user_data)100 overwrite_dialog_response_cb (GtkDialog *dialog,
101 gint response_id,
102 gpointer user_data)
103 {
104 GthImageListTask *self = user_data;
105 gboolean close_overwrite_dialog = TRUE;
106
107 if (response_id != GTK_RESPONSE_OK)
108 self->priv->overwrite_response = GTH_OVERWRITE_RESPONSE_CANCEL;
109 else
110 self->priv->overwrite_response = gth_overwrite_dialog_get_response (GTH_OVERWRITE_DIALOG (dialog));
111
112 gtk_widget_hide (GTK_WIDGET (dialog));
113 gth_task_dialog (GTH_TASK (self), FALSE, NULL);
114
115 switch (self->priv->overwrite_response) {
116 case GTH_OVERWRITE_RESPONSE_NO:
117 case GTH_OVERWRITE_RESPONSE_ALWAYS_NO:
118 case GTH_OVERWRITE_RESPONSE_UNSPECIFIED:
119 if (self->priv->overwrite_response == GTH_OVERWRITE_RESPONSE_ALWAYS_NO)
120 self->priv->overwrite_mode = GTH_OVERWRITE_SKIP;
121 process_next_file (self);
122 break;
123
124 case GTH_OVERWRITE_RESPONSE_YES:
125 case GTH_OVERWRITE_RESPONSE_ALWAYS_YES:
126 if (self->priv->overwrite_response == GTH_OVERWRITE_RESPONSE_ALWAYS_YES)
127 self->priv->overwrite_mode = GTH_OVERWRITE_OVERWRITE;
128 image_task_save_current_image (self, NULL, TRUE);
129 break;
130
131 case GTH_OVERWRITE_RESPONSE_RENAME:
132 {
133 GFile *parent;
134 GFile *new_destination;
135 GError *error = NULL;
136
137 if (self->priv->destination_folder != NULL)
138 parent = g_object_ref (self->priv->destination_folder);
139 else
140 parent = g_file_get_parent (self->priv->destination_file_data->file);
141
142 new_destination = g_file_get_child_for_display_name (parent, gth_overwrite_dialog_get_filename (GTH_OVERWRITE_DIALOG (dialog)), &error);
143 if (new_destination == NULL) {
144 _gtk_error_dialog_from_gerror_run (GTK_WINDOW (dialog), _("Could not rename the file"), error);
145 g_clear_error (&error);
146 gtk_widget_show (GTK_WIDGET (dialog));
147 gth_task_dialog (GTH_TASK (self), TRUE, GTK_WIDGET (dialog));
148 close_overwrite_dialog = FALSE;
149 }
150 else
151 image_task_save_current_image (self, new_destination, FALSE);
152
153 g_object_unref (new_destination);
154 g_object_unref (parent);
155 }
156 break;
157
158 case GTH_OVERWRITE_RESPONSE_CANCEL:
159 {
160 GError *error;
161
162 error = g_error_new_literal (GTH_TASK_ERROR, GTH_TASK_ERROR_CANCELLED, "");
163 gth_task_completed (GTH_TASK (self), error);
164 }
165 break;
166 }
167
168 if (close_overwrite_dialog)
169 gtk_widget_destroy (GTK_WIDGET (dialog));
170 }
171
172
173 static void
image_saved_cb(GthFileData * file_data,GError * error,gpointer user_data)174 image_saved_cb (GthFileData *file_data,
175 GError *error,
176 gpointer user_data)
177 {
178 GthImageListTask *self = user_data;
179 GFile *parent;
180 GList *file_list;
181
182 if (error != NULL) {
183 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
184 if (self->priv->overwrite_mode == GTH_OVERWRITE_SKIP) {
185 process_next_file (self);
186 }
187 else {
188 GtkWidget *dialog;
189
190 dialog = gth_overwrite_dialog_new (NULL,
191 self->priv->new_image,
192 self->priv->destination_file_data->file,
193 GTH_OVERWRITE_RESPONSE_YES,
194 (self->priv->n_files == 1));
195 gth_task_dialog (GTH_TASK (self), TRUE, dialog);
196
197 g_signal_connect (dialog,
198 "response",
199 G_CALLBACK (overwrite_dialog_response_cb),
200 self);
201 gtk_widget_show (dialog);
202 }
203 }
204 else
205 gth_task_completed (GTH_TASK (self), error);
206 return;
207 }
208
209 parent = g_file_get_parent (file_data->file);
210 file_list = g_list_append (NULL, file_data->file);
211 gth_monitor_folder_changed (gth_main_get_default_monitor (),
212 parent,
213 file_list,
214 GTH_MONITOR_EVENT_CHANGED);
215
216 g_list_free (file_list);
217 g_object_unref (parent);
218
219 process_next_file (self);
220 }
221
222
223 static void
image_task_dialog_cb(GthTask * task,gboolean opened,GtkWidget * dialog,gpointer user_data)224 image_task_dialog_cb (GthTask *task,
225 gboolean opened,
226 GtkWidget *dialog,
227 gpointer user_data)
228 {
229 gth_task_dialog (GTH_TASK (user_data), opened, dialog);
230 }
231
232
233 static void
image_task_progress_cb(GthTask * task,const char * description,const char * details,gboolean pulse,double fraction,gpointer user_data)234 image_task_progress_cb (GthTask *task,
235 const char *description,
236 const char *details,
237 gboolean pulse,
238 double fraction,
239 gpointer user_data)
240 {
241 GthImageListTask *self = user_data;
242 double total_fraction;
243 double file_fraction;
244
245 total_fraction = ((double) self->priv->n_current + 1) / (self->priv->n_files + 1);
246 if (pulse)
247 file_fraction = 0.5;
248 else
249 file_fraction = fraction;
250
251 if (details == NULL) {
252 GthFileData *source_file_data = self->priv->current->data;
253 details = g_file_info_get_display_name (source_file_data->info);
254 }
255
256 gth_task_progress (GTH_TASK (self),
257 description,
258 details,
259 FALSE,
260 total_fraction + (file_fraction / (self->priv->n_files + 1)));
261 }
262
263
264 static void
image_task_save_current_image(GthImageListTask * self,GFile * file,gboolean replace)265 image_task_save_current_image (GthImageListTask *self,
266 GFile *file,
267 gboolean replace)
268 {
269 GthImage *destination;
270
271 if (file != NULL)
272 gth_file_data_set_file (self->priv->destination_file_data, file);
273
274 destination = gth_image_task_get_destination (GTH_IMAGE_TASK (self->priv->task));
275 if (destination == NULL) {
276 process_next_file (self);
277 return;
278 }
279
280 /* add a reference before unref-ing new_image because dest and
281 * new_image can be the same object. */
282
283 g_object_ref (destination);
284 _g_object_unref (self->priv->new_image);
285 self->priv->new_image = destination;
286
287 gth_image_save_to_file (self->priv->new_image,
288 gth_file_data_get_mime_type (self->priv->destination_file_data),
289 self->priv->destination_file_data,
290 replace,
291 gth_task_get_cancellable (GTH_TASK (self)),
292 image_saved_cb,
293 self);
294 }
295
296
297 static void
set_current_destination_file(GthImageListTask * self)298 set_current_destination_file (GthImageListTask *self)
299 {
300 char *display_name;
301 GFile *parent;
302 GFile *destination;
303
304 _g_object_unref (self->priv->destination_file_data);
305 self->priv->destination_file_data = g_object_ref (self->priv->current->data);
306
307 if (self->priv->mime_type != NULL) {
308 char *no_ext;
309 GthImageSaver *saver;
310
311 no_ext = _g_path_remove_extension (g_file_info_get_display_name (self->priv->destination_file_data->info));
312 saver = gth_main_get_image_saver (self->priv->mime_type);
313 g_return_if_fail (saver != NULL);
314
315 display_name = g_strconcat (no_ext, ".", gth_image_saver_get_default_ext (saver), NULL);
316 gth_file_data_set_mime_type (self->priv->destination_file_data, self->priv->mime_type);
317
318 g_object_unref (saver);
319 g_free (no_ext);
320 }
321 else
322 display_name = g_strdup (g_file_info_get_display_name (self->priv->destination_file_data->info));
323
324 if (self->priv->destination_folder != NULL)
325 parent = g_object_ref (self->priv->destination_folder);
326 else
327 parent = g_file_get_parent (self->priv->destination_file_data->file);
328 destination = g_file_get_child_for_display_name (parent, display_name, NULL);
329
330 gth_file_data_set_file (self->priv->destination_file_data, destination);
331
332 g_object_unref (destination);
333 g_object_unref (parent);
334 g_free (display_name);
335 }
336
337
338 static void
image_task_completed_cb(GthTask * task,GError * error,gpointer user_data)339 image_task_completed_cb (GthTask *task,
340 GError *error,
341 gpointer user_data)
342 {
343 GthImageListTask *self = user_data;
344
345 if (g_error_matches (error, GTH_TASK_ERROR, GTH_TASK_ERROR_SKIP_TO_NEXT_FILE)) {
346 process_next_file (self);
347 return;
348 }
349
350 if (error != NULL) {
351 gth_task_completed (GTH_TASK (self), error);
352 return;
353 }
354
355 set_current_destination_file (self);
356 image_task_save_current_image (self, NULL, (self->priv->overwrite_mode == GTH_OVERWRITE_OVERWRITE));
357 }
358
359
360 static void
file_buffer_ready_cb(void ** buffer,gsize count,GError * error,gpointer user_data)361 file_buffer_ready_cb (void **buffer,
362 gsize count,
363 GError *error,
364 gpointer user_data)
365 {
366 GthImageListTask *self = user_data;
367 GInputStream *istream;
368
369 if (error != NULL) {
370 gth_task_completed (GTH_TASK (self), error);
371 return;
372 }
373
374 istream = g_memory_input_stream_new_from_data (*buffer, count, NULL);
375 self->priv->original_image = gth_image_new_from_stream (istream, -1, NULL, NULL, gth_task_get_cancellable (GTH_TASK (self)), &error);
376
377 g_object_unref (istream);
378
379 if (self->priv->original_image == NULL) {
380 gth_task_completed (GTH_TASK (self), error);
381 return;
382 }
383
384 gth_image_task_set_source (GTH_IMAGE_TASK (self->priv->task), self->priv->original_image);
385 gth_task_exec (self->priv->task, gth_task_get_cancellable (GTH_TASK (self)));
386 }
387
388
389 static void
file_info_ready_cb(GList * files,GError * error,gpointer user_data)390 file_info_ready_cb (GList *files,
391 GError *error,
392 gpointer user_data)
393 {
394 GthImageListTask *self = user_data;
395 GthFileData *updated_file_data;
396 GthFileData *source_file_data;
397
398 if (error != NULL) {
399 gth_task_completed (GTH_TASK (self), error);
400 return;
401 }
402
403 source_file_data = self->priv->current->data;
404 updated_file_data = (GthFileData*) files->data;
405 g_file_info_copy_into (updated_file_data->info, source_file_data->info);
406
407 _g_file_load_async (source_file_data->file,
408 G_PRIORITY_DEFAULT,
409 gth_task_get_cancellable (GTH_TASK (self)),
410 file_buffer_ready_cb,
411 self);
412 }
413
414
415 static void
process_current_file(GthImageListTask * self)416 process_current_file (GthImageListTask *self)
417 {
418 GthFileData *source_file_data;
419 GList *source_singleton;
420
421 if (self->priv->current == NULL) {
422 if (self->priv->destination_folder != NULL)
423 gth_browser_go_to (self->priv->browser, self->priv->destination_folder, NULL);
424 gth_task_completed (GTH_TASK (self), NULL);
425 return;
426 }
427
428 _g_object_unref (self->priv->original_image);
429 self->priv->original_image = NULL;
430
431 _g_object_unref (self->priv->new_image);
432 self->priv->new_image = NULL;
433
434 gth_task_progress (GTH_TASK (self),
435 NULL,
436 NULL,
437 FALSE,
438 ((double) self->priv->n_current + 1) / (self->priv->n_files + 1));
439
440 source_file_data = self->priv->current->data;
441 source_singleton = g_list_append (NULL, g_object_ref (source_file_data->file));
442 _g_query_all_metadata_async (source_singleton,
443 GTH_LIST_DEFAULT,
444 "*",
445 gth_task_get_cancellable (GTH_TASK (self)),
446 file_info_ready_cb,
447 self);
448
449 _g_object_list_unref (source_singleton);
450 }
451
452
453 static void
gth_image_list_task_exec(GthTask * task)454 gth_image_list_task_exec (GthTask *task)
455 {
456 GthImageListTask *self;
457
458 g_return_if_fail (GTH_IS_IMAGE_LIST_TASK (task));
459
460 self = GTH_IMAGE_LIST_TASK (task);
461
462 self->priv->current = self->priv->file_list;
463 self->priv->n_current = 0;
464 self->priv->n_files = g_list_length (self->priv->file_list);
465 process_current_file (self);
466 }
467
468
469 static void
gth_image_list_task_class_init(GthImageListTaskClass * klass)470 gth_image_list_task_class_init (GthImageListTaskClass *klass)
471 {
472 GObjectClass *object_class;
473 GthTaskClass *task_class;
474
475 object_class = G_OBJECT_CLASS (klass);
476 object_class->finalize = gth_image_list_task_finalize;
477
478 task_class = GTH_TASK_CLASS (klass);
479 task_class->exec = gth_image_list_task_exec;
480 }
481
482
483 static void
gth_image_list_task_init(GthImageListTask * self)484 gth_image_list_task_init (GthImageListTask *self)
485 {
486 self->priv = gth_image_list_task_get_instance_private (self);
487 self->priv->original_image = NULL;
488 self->priv->new_image = NULL;
489 self->priv->destination_folder = NULL;
490 self->priv->overwrite_response = GTH_OVERWRITE_RESPONSE_UNSPECIFIED;
491 self->priv->mime_type = NULL;
492 }
493
494
495 GthTask *
gth_image_list_task_new(GthBrowser * browser,GList * file_list,GthImageTask * task)496 gth_image_list_task_new (GthBrowser *browser,
497 GList *file_list,
498 GthImageTask *task)
499 {
500 GthImageListTask *self;
501
502 g_return_val_if_fail (task != NULL, NULL);
503 g_return_val_if_fail (GTH_IS_IMAGE_TASK (task), NULL);
504
505 self = GTH_IMAGE_LIST_TASK (g_object_new (GTH_TYPE_IMAGE_LIST_TASK, NULL));
506 self->priv->browser = browser;
507 self->priv->file_list = _g_object_list_ref (file_list);
508 self->priv->task = GTH_TASK (g_object_ref (task));
509 self->priv->task_completed = g_signal_connect (self->priv->task,
510 "completed",
511 G_CALLBACK (image_task_completed_cb),
512 self);
513 self->priv->task_progress = g_signal_connect (self->priv->task,
514 "progress",
515 G_CALLBACK (image_task_progress_cb),
516 self);
517 self->priv->task_dialog = g_signal_connect (self->priv->task,
518 "dialog",
519 G_CALLBACK (image_task_dialog_cb),
520 self);
521
522 return (GthTask *) self;
523 }
524
525
526 void
gth_image_list_task_set_destination(GthImageListTask * self,GFile * folder)527 gth_image_list_task_set_destination (GthImageListTask *self,
528 GFile *folder)
529 {
530 _g_object_unref (self->priv->destination_folder);
531 self->priv->destination_folder = _g_object_ref (folder);
532 }
533
534
535 void
gth_image_list_task_set_overwrite_mode(GthImageListTask * self,GthOverwriteMode overwrite_mode)536 gth_image_list_task_set_overwrite_mode (GthImageListTask *self,
537 GthOverwriteMode overwrite_mode)
538 {
539 self->priv->overwrite_mode = overwrite_mode;
540 }
541
542
543 void
gth_image_list_task_set_output_mime_type(GthImageListTask * self,const char * mime_type)544 gth_image_list_task_set_output_mime_type (GthImageListTask *self,
545 const char *mime_type)
546 {
547 g_free (self->priv->mime_type);
548 self->priv->mime_type = NULL;
549 if (mime_type != NULL)
550 self->priv->mime_type = g_strdup (mime_type);
551 }
552