1 /* Eye Of Gnome - Jobs
2  *
3  * Copyright (C) 2013 The Free Software Foundation
4  *
5  * Author: Javier Sánchez <jsanchez@deskblue.com>
6  *
7  * Based on code (libview/ev-jobs.h) by:
8  *      - Carlos Garcia Campos <carlosgc@gnome.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23  */
24 
25 #include "eog-debug.h"
26 #include "eog-jobs.h"
27 #include "eog-thumbnail.h"
28 #include "eog-pixbuf-util.h"
29 
30 #include <gio/gio.h>
31 
32 G_DEFINE_ABSTRACT_TYPE (EogJob, eog_job, G_TYPE_OBJECT);
33 G_DEFINE_TYPE (EogJobCopy,      eog_job_copy,      EOG_TYPE_JOB);
34 G_DEFINE_TYPE (EogJobLoad,      eog_job_load,      EOG_TYPE_JOB);
35 G_DEFINE_TYPE (EogJobModel,     eog_job_model,     EOG_TYPE_JOB);
36 G_DEFINE_TYPE (EogJobSave,      eog_job_save,      EOG_TYPE_JOB);
37 G_DEFINE_TYPE (EogJobSaveAs,    eog_job_save_as,   EOG_TYPE_JOB_SAVE);
38 G_DEFINE_TYPE (EogJobThumbnail, eog_job_thumbnail, EOG_TYPE_JOB);
39 G_DEFINE_TYPE (EogJobTransform, eog_job_transform, EOG_TYPE_JOB);
40 
41 /* signals */
42 enum {
43 	PROGRESS,
44 	CANCELLED,
45 	FINISHED,
46 	LAST_SIGNAL
47 };
48 
49 static guint job_signals[LAST_SIGNAL];
50 
51 /* notify signal funcs */
52 static gboolean notify_progress              (EogJob               *job);
53 static gboolean notify_cancelled             (EogJob               *job);
54 static gboolean notify_finished              (EogJob               *job);
55 
56 /* gobject vfuncs */
57 static void     eog_job_class_init           (EogJobClass          *class);
58 static void     eog_job_init                 (EogJob               *job);
59 static void     eog_job_dispose              (GObject              *object);
60 
61 static void     eog_job_copy_class_init      (EogJobCopyClass      *class);
62 static void     eog_job_copy_init            (EogJobCopy           *job);
63 static void     eog_job_copy_dispose         (GObject              *object);
64 
65 static void     eog_job_load_class_init      (EogJobLoadClass      *class);
66 static void     eog_job_load_init            (EogJobLoad           *job);
67 static void     eog_job_load_dispose         (GObject              *object);
68 
69 static void     eog_job_model_class_init     (EogJobModelClass     *class);
70 static void     eog_job_model_init           (EogJobModel          *job);
71 static void     eog_job_model_dispose        (GObject              *object);
72 
73 static void     eog_job_save_class_init      (EogJobSaveClass      *class);
74 static void     eog_job_save_init            (EogJobSave           *job);
75 static void     eog_job_save_dispose         (GObject              *object);
76 
77 static void     eog_job_save_as_class_init   (EogJobSaveAsClass    *class);
78 static void     eog_job_save_as_init         (EogJobSaveAs         *job);
79 static void     eog_job_save_as_dispose      (GObject              *object);
80 
81 static void     eog_job_thumbnail_class_init (EogJobThumbnailClass *class);
82 static void     eog_job_thumbnail_init       (EogJobThumbnail      *job);
83 static void     eog_job_thumbnail_dispose    (GObject              *object);
84 
85 static void     eog_job_transform_class_init (EogJobTransformClass *class);
86 static void     eog_job_transform_init       (EogJobTransform      *job);
87 static void     eog_job_transform_dispose    (GObject              *object);
88 
89 /* vfuncs */
90 static void     eog_job_run_unimplemented    (EogJob               *job);
91 static void     eog_job_copy_run             (EogJob               *job);
92 static void     eog_job_load_run             (EogJob               *job);
93 static void     eog_job_model_run            (EogJob               *job);
94 static void     eog_job_save_run             (EogJob               *job);
95 static void     eog_job_save_as_run          (EogJob               *job);
96 static void     eog_job_thumbnail_run        (EogJob               *job);
97 static void     eog_job_transform_run        (EogJob               *job);
98 
99 /* callbacks */
100 static void eog_job_copy_progress_callback (goffset  current_num_bytes,
101 					    goffset  total_num_bytes,
102 					    gpointer user_data);
103 
104 static void eog_job_save_progress_callback (EogImage *image,
105 					    gfloat    progress,
106 					    gpointer  data);
107 
108 /* --------------------------- notify signal funcs --------------------------- */
109 static gboolean
notify_progress(EogJob * job)110 notify_progress (EogJob *job)
111 {
112 	/* check if the current job was previously cancelled */
113 	if (eog_job_is_cancelled (job))
114 		return FALSE;
115 
116 	/* show info for debugging */
117 	eog_debug_message (DEBUG_JOBS,
118 			   "%s (%p) job update its progress to -> %1.2f",
119 			   EOG_GET_TYPE_NAME (job),
120 			   job,
121 			   job->progress);
122 
123 	/* notify progress */
124 	g_signal_emit (job,
125 		       job_signals[PROGRESS],
126 		       0,
127 		       job->progress);
128 	return FALSE;
129 }
130 
131 static gboolean
notify_cancelled(EogJob * job)132 notify_cancelled (EogJob *job)
133 {
134 	/* show info for debugging */
135 	eog_debug_message (DEBUG_JOBS,
136 			   "%s (%p) job was CANCELLED",
137 			   EOG_GET_TYPE_NAME (job),
138 			   job);
139 
140 	/* notify cancelation */
141 	g_signal_emit (job,
142 		       job_signals[CANCELLED],
143 		       0);
144 
145 	return FALSE;
146 }
147 
148 static gboolean
notify_finished(EogJob * job)149 notify_finished (EogJob *job)
150 {
151 	/* show info for debugging */
152 	eog_debug_message (DEBUG_JOBS,
153 			   "%s (%p) job was FINISHED",
154 			   EOG_GET_TYPE_NAME (job),
155 			   job);
156 
157 	/* notify job finalization */
158 	g_signal_emit (job,
159 		       job_signals[FINISHED],
160 		       0);
161 	return FALSE;
162 }
163 
164 /* --------------------------------- EogJob ---------------------------------- */
165 static void
eog_job_class_init(EogJobClass * class)166 eog_job_class_init (EogJobClass *class)
167 {
168 	GObjectClass *g_object_class = (GObjectClass *) class;
169 
170 	g_object_class->dispose = eog_job_dispose;
171 	class->run              = eog_job_run_unimplemented;
172 
173 	/* signals */
174 	job_signals [PROGRESS] =
175 		g_signal_new ("progress",
176 			      EOG_TYPE_JOB,
177 			      G_SIGNAL_RUN_LAST,
178 			      G_STRUCT_OFFSET (EogJobClass, progress),
179 			      NULL,
180 			      NULL,
181 			      g_cclosure_marshal_VOID__FLOAT,
182 			      G_TYPE_NONE,
183 			      1,
184 			      G_TYPE_FLOAT);
185 
186 	job_signals [CANCELLED] =
187 		g_signal_new ("cancelled",
188 			      EOG_TYPE_JOB,
189 			      G_SIGNAL_RUN_LAST,
190 			      G_STRUCT_OFFSET (EogJobClass, cancelled),
191 			      NULL,
192 			      NULL,
193 			      g_cclosure_marshal_VOID__VOID,
194 			      G_TYPE_NONE,
195 			      0);
196 
197 	job_signals [FINISHED] =
198 		g_signal_new ("finished",
199 			      EOG_TYPE_JOB,
200 			      G_SIGNAL_RUN_LAST,
201 			      G_STRUCT_OFFSET (EogJobClass, finished),
202 			      NULL,
203 			      NULL,
204 			      g_cclosure_marshal_VOID__VOID,
205 			      G_TYPE_NONE,
206 			      0);
207 }
208 
209 static
eog_job_init(EogJob * job)210 void eog_job_init (EogJob *job)
211 {
212 	/* initialize all public and private members to reasonable
213 	   default values. */
214 	job->cancellable = g_cancellable_new ();
215 	job->error       = NULL;
216 
217 	job->progress    = 0.0;
218 	job->cancelled   = FALSE;
219 	job->finished    = FALSE;
220 
221 	/* NOTE: we need to allocate the mutex here so the ABI stays
222 	   the same when it used to use g_mutex_new */
223 	job->mutex = g_malloc (sizeof (GMutex));
224 	g_mutex_init (job->mutex);
225 }
226 
227 static
eog_job_dispose(GObject * object)228 void eog_job_dispose (GObject *object)
229 {
230 	EogJob *job;
231 
232 	g_return_if_fail (EOG_IS_JOB (object));
233 
234 	job = EOG_JOB (object);
235 
236 	/* free all public and private members */
237 	if (job->cancellable) {
238 		g_object_unref (job->cancellable);
239 		job->cancellable = NULL;
240 	}
241 
242 	if (job->error) {
243 		g_error_free (job->error);
244 		job->error = NULL;
245 	}
246 
247 	if (job->mutex) {
248 		g_mutex_clear (job->mutex);
249 		g_free        (job->mutex);
250 	}
251 
252 	/* call parent dispose */
253 	G_OBJECT_CLASS (eog_job_parent_class)->dispose (object);
254 }
255 
256 static void
eog_job_run_unimplemented(EogJob * job)257 eog_job_run_unimplemented (EogJob *job)
258 {
259 	g_critical ("Class \"%s\" does not implement the required run action",
260 		    G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (job)));
261 }
262 
263 void
eog_job_run(EogJob * job)264 eog_job_run (EogJob *job)
265 {
266 	EogJobClass *class;
267 
268 	g_return_if_fail (EOG_IS_JOB (job));
269 
270 	class = EOG_JOB_GET_CLASS (job);
271 	class->run (job);
272 }
273 
274 void
eog_job_cancel(EogJob * job)275 eog_job_cancel (EogJob *job)
276 {
277 	g_return_if_fail (EOG_IS_JOB (job));
278 
279 	g_object_ref (job);
280 
281 	/* check if job was cancelled previously */
282 	if (job->cancelled)
283 		return;
284 
285 	/* check if job finished previously */
286         if (job->finished)
287 		return;
288 
289 	/* show info for debugging */
290 	eog_debug_message (DEBUG_JOBS,
291 			   "CANCELLING a %s (%p)",
292 			   EOG_GET_TYPE_NAME (job),
293 			   job);
294 
295 	/* --- enter critical section --- */
296 	g_mutex_lock (job->mutex);
297 
298 	/* cancel job */
299 	job->cancelled = TRUE;
300 	g_cancellable_cancel (job->cancellable);
301 
302 	/* --- leave critical section --- */
303 	g_mutex_unlock (job->mutex);
304 
305 	/* notify job cancellation */
306 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
307 			 (GSourceFunc) notify_cancelled,
308 			 job,
309 			 g_object_unref);
310 }
311 
312 gfloat
eog_job_get_progress(EogJob * job)313 eog_job_get_progress (EogJob *job)
314 {
315 	g_return_val_if_fail (EOG_IS_JOB (job), 0.0);
316 
317 	return job->progress;
318 }
319 
320 void
eog_job_set_progress(EogJob * job,gfloat progress)321 eog_job_set_progress (EogJob *job,
322 		      gfloat  progress)
323 {
324 	g_return_if_fail (EOG_IS_JOB (job));
325 	g_return_if_fail (progress >= 0.0 && progress <= 1.0);
326 
327 	g_object_ref (job);
328 
329 	/* --- enter critical section --- */
330 	g_mutex_lock (job->mutex);
331 
332 	job->progress = progress;
333 
334 	/* --- leave critical section --- */
335 	g_mutex_unlock (job->mutex);
336 
337 	/* notify progress */
338 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
339 			 (GSourceFunc) notify_progress,
340 			 job,
341 			 g_object_unref);
342 }
343 
344 gboolean
eog_job_is_cancelled(EogJob * job)345 eog_job_is_cancelled (EogJob *job)
346 {
347 	g_return_val_if_fail (EOG_IS_JOB (job), TRUE);
348 
349 	return job->cancelled;
350 }
351 
352 gboolean
eog_job_is_finished(EogJob * job)353 eog_job_is_finished (EogJob *job)
354 {
355 	g_return_val_if_fail (EOG_IS_JOB (job), TRUE);
356 
357 	return job->finished;
358 }
359 
360 /* ------------------------------- EogJobCopy -------------------------------- */
361 static void
eog_job_copy_class_init(EogJobCopyClass * class)362 eog_job_copy_class_init (EogJobCopyClass *class)
363 {
364 	GObjectClass *g_object_class = (GObjectClass *) class;
365 	EogJobClass  *eog_job_class  = (EogJobClass *)  class;
366 
367 	g_object_class->dispose = eog_job_copy_dispose;
368 	eog_job_class->run      = eog_job_copy_run;
369 }
370 
371 static
eog_job_copy_init(EogJobCopy * job)372 void eog_job_copy_init (EogJobCopy *job)
373 {
374 	/* initialize all public and private members to reasonable
375 	   default values. */
376 	job->images           = NULL;
377 	job->destination      = NULL;
378 	job->current_position = 0;
379 }
380 
381 static
eog_job_copy_dispose(GObject * object)382 void eog_job_copy_dispose (GObject *object)
383 {
384 	EogJobCopy *job;
385 
386 	g_return_if_fail (EOG_IS_JOB_COPY (object));
387 
388 	job = EOG_JOB_COPY (object);
389 
390 	/* free all public and private members */
391 	if (job->images) {
392 		g_list_foreach (job->images, (GFunc) g_object_unref, NULL);
393 		g_list_free    (job->images);
394 		job->images = NULL;
395 	}
396 
397 	if (job->destination) {
398 		g_free (job->destination);
399 		job->destination = NULL;
400 	}
401 
402 	/* call parent dispose */
403 	G_OBJECT_CLASS (eog_job_copy_parent_class)->dispose (object);
404 }
405 
406 static void
eog_job_copy_progress_callback(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)407 eog_job_copy_progress_callback (goffset  current_num_bytes,
408 				goffset  total_num_bytes,
409 				gpointer user_data)
410 {
411 	gfloat      progress;
412 	guint       n_images;
413 	EogJobCopy *job;
414 
415 	job = EOG_JOB_COPY (user_data);
416 
417 	n_images = g_list_length (job->images);
418 
419 	progress = ((current_num_bytes / (gfloat) total_num_bytes) + job->current_position) / n_images;
420 
421 	eog_job_set_progress (EOG_JOB (job), progress);
422 }
423 
424 static void
eog_job_copy_run(EogJob * job)425 eog_job_copy_run (EogJob *job)
426 {
427 	EogJobCopy *copyjob;
428 	GList *it;
429 
430 	/* initialization */
431 	g_return_if_fail (EOG_IS_JOB_COPY (job));
432 
433 	copyjob = EOG_JOB_COPY (g_object_ref (job));
434 
435 	/* clean previous errors */
436 	if (job->error) {
437 	        g_error_free (job->error);
438 		job->error = NULL;
439 	}
440 
441 	/* check if the current job was previously cancelled */
442 	if (eog_job_is_cancelled (job))
443 	{
444 		g_object_unref (job);
445 		return;
446 	}
447 
448 	copyjob->current_position = 0;
449 
450 	for (it = copyjob->images; it != NULL; it = g_list_next (it), copyjob->current_position++) {
451 		GFile *src, *dest;
452 		gchar *filename, *dest_filename;
453 
454 		src = (GFile *) it->data;
455 		filename = g_file_get_basename (src);
456 		dest_filename = g_build_filename (copyjob->destination, filename, NULL);
457 		dest = g_file_new_for_path (dest_filename);
458 
459 		g_file_copy (src, dest,
460 					 G_FILE_COPY_OVERWRITE, NULL,
461 					 eog_job_copy_progress_callback, job,
462 					 &job->error);
463 		g_object_unref (dest);
464 		g_free (filename);
465 		g_free (dest_filename);
466 	}
467 
468 	/* --- enter critical section --- */
469 	g_mutex_lock (job->mutex);
470 
471 	/* job finished */
472 	job->finished = TRUE;
473 
474 	/* --- leave critical section --- */
475 	g_mutex_unlock (job->mutex);
476 
477 	/* notify job finalization */
478 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
479 			 (GSourceFunc) notify_finished,
480 			 job,
481 			 g_object_unref);
482 }
483 
484 /**
485  * eog_job_copy_new:
486  * @images: (element-type EogImage) (transfer full): a #EogImage list
487  * @destination: destination path for the copy
488  *
489  * Creates a new #EogJob.
490  *
491  * Returns: A #EogJob.
492  */
493 
494 EogJob *
eog_job_copy_new(GList * images,const gchar * destination)495 eog_job_copy_new (GList       *images,
496 		  const gchar *destination)
497 {
498 	EogJobCopy *job;
499 
500 	job = g_object_new (EOG_TYPE_JOB_COPY, NULL);
501 
502 	if (images)
503 		job->images = images;
504 
505 	if (destination)
506 		job->destination = g_strdup (destination);
507 
508 	/* show info for debugging */
509 	eog_debug_message (DEBUG_JOBS,
510 			   "%s (%p) job was CREATED",
511 			   EOG_GET_TYPE_NAME (job),
512 			   job);
513 
514 	return EOG_JOB (job);
515 }
516 
517 /* ------------------------------- EogJobLoad -------------------------------- */
518 static void
eog_job_load_class_init(EogJobLoadClass * class)519 eog_job_load_class_init (EogJobLoadClass *class)
520 {
521 	GObjectClass *g_object_class = (GObjectClass *) class;
522 	EogJobClass  *eog_job_class  = (EogJobClass *)  class;
523 
524 	g_object_class->dispose = eog_job_load_dispose;
525 	eog_job_class->run      = eog_job_load_run;
526 }
527 
528 static
eog_job_load_init(EogJobLoad * job)529 void eog_job_load_init (EogJobLoad *job)
530 {
531 	/* initialize all public and private members to reasonable
532 	   default values. */
533 	job->image = NULL;
534 	job->data  = EOG_IMAGE_DATA_ALL;
535 }
536 
537 static
eog_job_load_dispose(GObject * object)538 void eog_job_load_dispose (GObject *object)
539 {
540 	EogJobLoad *job;
541 
542 	g_return_if_fail (EOG_IS_JOB_LOAD (object));
543 
544 	job = EOG_JOB_LOAD (object);
545 
546 	/* free all public and private members */
547 	if (job->image) {
548 		g_object_unref (job->image);
549 		job->image = NULL;
550 	}
551 
552 	/* call parent dispose */
553 	G_OBJECT_CLASS (eog_job_load_parent_class)->dispose (object);
554 }
555 
556 static void
eog_job_load_run(EogJob * job)557 eog_job_load_run (EogJob *job)
558 {
559 	EogJobLoad *job_load;
560 
561 	/* initialization */
562 	g_return_if_fail (EOG_IS_JOB_LOAD (job));
563 
564 	job_load = EOG_JOB_LOAD (g_object_ref (job));
565 
566 	/* clean previous errors */
567 	if (job->error) {
568 	        g_error_free (job->error);
569 		job->error = NULL;
570 	}
571 
572 	/* load image from file */
573 	eog_image_load (job_load->image,
574 			job_load->data,
575 			job,
576 			&job->error);
577 
578 	/* check if the current job was previously cancelled */
579 	if (eog_job_is_cancelled (job))
580 		return;
581 
582 	/* --- enter critical section --- */
583 	g_mutex_lock (job->mutex);
584 
585 	/* job finished */
586 	job->finished = TRUE;
587 
588 	/* --- leave critical section --- */
589 	g_mutex_unlock (job->mutex);
590 
591 	/* notify job finalization */
592 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
593 			 (GSourceFunc) notify_finished,
594 			 job,
595 			 g_object_unref);
596 }
597 
598 EogJob *
eog_job_load_new(EogImage * image,EogImageData data)599 eog_job_load_new (EogImage     *image,
600 		  EogImageData  data)
601 {
602 	EogJobLoad *job;
603 
604 	job = g_object_new (EOG_TYPE_JOB_LOAD, NULL);
605 
606 	if (image)
607 		job->image = g_object_ref (image);
608 
609 	job->data = data;
610 
611 	/* show info for debugging */
612 	eog_debug_message (DEBUG_JOBS,
613 			   "%s (%p) job was CREATED",
614 			   EOG_GET_TYPE_NAME (job),
615 			   job);
616 
617 	return EOG_JOB (job);
618 }
619 
620 /* ------------------------------- EogJobModel -------------------------------- */
621 static void
eog_job_model_class_init(EogJobModelClass * class)622 eog_job_model_class_init (EogJobModelClass *class)
623 {
624 	GObjectClass *g_object_class = (GObjectClass *) class;
625 	EogJobClass  *eog_job_class  = (EogJobClass *)  class;
626 
627 	g_object_class->dispose = eog_job_model_dispose;
628 	eog_job_class->run      = eog_job_model_run;
629 }
630 
631 static
eog_job_model_init(EogJobModel * job)632 void eog_job_model_init (EogJobModel *job)
633 {
634 	/* initialize all public and private members to reasonable
635 	   default values. */
636 	job->store     = NULL;
637 	job->file_list = NULL;
638 }
639 
640 static
eog_job_model_dispose(GObject * object)641 void eog_job_model_dispose (GObject *object)
642 {
643 	EogJobModel *job;
644 
645 	g_return_if_fail (EOG_IS_JOB_MODEL (object));
646 
647 	job = EOG_JOB_MODEL (object);
648 
649 	/* free all public and private members */
650 	if (job->store) {
651 		g_object_unref (job->store);
652 		job->store = NULL;
653 	}
654 
655 	if (job->file_list) {
656 		// g_slist_foreach (job->file_list, (GFunc) g_object_unref, NULL);
657 		// g_slist_free (job->file_list);
658 		job->file_list = NULL;
659 	}
660 
661 	/* call parent dispose */
662 	G_OBJECT_CLASS (eog_job_model_parent_class)->dispose (object);
663 }
664 
665 typedef struct
666 {
667 	GMutex mutex;
668 	GCond cond;
669 	GAsyncResult *result;
670 } MountData;
671 
672 static void
_g_file_mount_enclosing_volume_sync_cb(GObject * source,GAsyncResult * result,gpointer user_data)673 _g_file_mount_enclosing_volume_sync_cb (GObject *source,
674 					GAsyncResult *result,
675 					gpointer user_data)
676 {
677 	MountData *data = (MountData *) user_data;
678 
679 	data->result = g_object_ref (result);
680 
681 	g_mutex_lock (&data->mutex);
682 	g_cond_signal (&data->cond);
683 	g_mutex_unlock (&data->mutex);
684 }
685 
686 static gboolean
_g_file_mount_enclosing_volume_sync(GFile * location,GMountMountFlags flags,GMountOperation * mount_operation,GError ** error)687 _g_file_mount_enclosing_volume_sync (GFile *location,
688 				     GMountMountFlags flags,
689 				     GMountOperation *mount_operation,
690 				     GError **error)
691 {
692 	MountData *data;
693 	gboolean retval;
694 
695 	data = g_new0 (MountData, 1);
696 
697 	g_mutex_lock (&data->mutex);
698 	g_file_mount_enclosing_volume (location,
699 				       flags,
700 				       mount_operation,
701 				       NULL,
702 				       _g_file_mount_enclosing_volume_sync_cb,
703 				       data);
704 	while (data->result == NULL)
705 		g_cond_wait (&data->cond, &data->mutex);
706 	g_mutex_unlock (&data->mutex);
707 
708 	retval = g_file_mount_enclosing_volume_finish (location, data->result, error);
709 
710 	g_object_unref (data->result);
711 	g_free (data);
712 
713 	return retval;
714 }
715 
716 static void
filter_files(GSList * files,GList ** file_list,GList ** error_list)717 filter_files (GSList *files, GList **file_list, GList **error_list)
718 {
719 	GSList *it;
720 	GFileInfo *file_info;
721 
722 	for (it = files; it != NULL; it = it->next) {
723 		GFile *file;
724 		GFileType type = G_FILE_TYPE_UNKNOWN;
725 
726 		file = (GFile *) it->data;
727 
728 		if (file != NULL) {
729 			GError *error = NULL;
730 
731 			file_info = g_file_query_info (file,
732 						       G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
733 						       0, NULL, &error);
734 			if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) {
735 				GMountOperation *operation;
736 
737 				operation = gtk_mount_operation_new (NULL);
738 				if (_g_file_mount_enclosing_volume_sync (file,
739 									 G_MOUNT_MOUNT_NONE,
740 									 operation,
741 									 NULL))
742 					file_info = g_file_query_info (file,
743 								       G_FILE_ATTRIBUTE_STANDARD_TYPE","G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
744 								       0, NULL, NULL);
745 
746 				g_object_unref (operation);
747 			}
748 			g_clear_error (&error);
749 
750 			if (file_info == NULL) {
751 				type = G_FILE_TYPE_UNKNOWN;
752 			} else {
753 				type = g_file_info_get_file_type (file_info);
754 
755 				/* Workaround for gvfs backends that
756 				   don't set the GFileType. */
757 				if (G_UNLIKELY (type == G_FILE_TYPE_UNKNOWN)) {
758 					const gchar *ctype;
759 
760 					ctype = g_file_info_get_content_type (file_info);
761 
762 					/* If the content type is supported
763 					   adjust the file_type */
764 					if (eog_image_is_supported_mime_type (ctype))
765 						type = G_FILE_TYPE_REGULAR;
766 				}
767 
768 				g_object_unref (file_info);
769 			}
770 		}
771 
772 		switch (type) {
773 		case G_FILE_TYPE_REGULAR:
774 		case G_FILE_TYPE_DIRECTORY:
775 			*file_list = g_list_prepend (*file_list, g_object_ref (file));
776 			break;
777 		default:
778 			*error_list = g_list_prepend (*error_list,
779 						      g_file_get_uri (file));
780 			break;
781 		}
782 	}
783 
784 	*file_list  = g_list_reverse (*file_list);
785 	*error_list = g_list_reverse (*error_list);
786 }
787 
788 static void
eog_job_model_run(EogJob * job)789 eog_job_model_run (EogJob *job)
790 {
791 	EogJobModel *job_model;
792 	GList       *filtered_list;
793 	GList       *error_list;
794 
795 	/* initialization */
796 	g_return_if_fail (EOG_IS_JOB_MODEL (job));
797 
798 	job_model     = EOG_JOB_MODEL (g_object_ref (job));
799 	filtered_list = NULL;
800 	error_list    = NULL;
801 
802 	filter_files (job_model->file_list,
803 		      &filtered_list,
804 		      &error_list);
805 
806 	/* --- enter critical section --- */
807 	g_mutex_lock (job->mutex);
808 
809 	/* create a list store */
810 	job_model->store = EOG_LIST_STORE (eog_list_store_new ());
811 	eog_list_store_add_files (job_model->store, filtered_list);
812 
813 	/* --- leave critical section --- */
814 	g_mutex_unlock (job->mutex);
815 
816 	/* free lists*/
817 	g_list_foreach (filtered_list, (GFunc) g_object_unref, NULL);
818 	g_list_free (filtered_list);
819 
820 	g_list_foreach (error_list, (GFunc) g_free, NULL);
821 	g_list_free (error_list);
822 
823 	/* --- enter critical section --- */
824 	g_mutex_lock (job->mutex);
825 
826 	/* job finished */
827 	job->finished = TRUE;
828 
829 	/* --- leave critical section --- */
830 	g_mutex_unlock (job->mutex);
831 
832 	/* notify job finalization */
833 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
834 			 (GSourceFunc) notify_finished,
835 			 job,
836 			 g_object_unref);
837 }
838 
839 /**
840  * eog_job_model_new:
841  * @file_list: (element-type GFile): a #GFile list
842  *
843  * Creates a new #EogJob model.
844  *
845  * Returns: A #EogJob.
846  */
847 
848 EogJob *
eog_job_model_new(GSList * file_list)849 eog_job_model_new (GSList *file_list)
850 {
851 	EogJobModel *job;
852 
853 	job = g_object_new (EOG_TYPE_JOB_MODEL, NULL);
854 
855 	if (file_list != NULL)
856 		job->file_list = file_list;
857 
858 	/* show info for debugging */
859 	eog_debug_message (DEBUG_JOBS,
860 			   "%s (%p) job was CREATED",
861 			   EOG_GET_TYPE_NAME (job),
862 			   job);
863 
864 	return EOG_JOB (job);
865 }
866 
867 /* ------------------------------- EogJobSave -------------------------------- */
868 static void
eog_job_save_class_init(EogJobSaveClass * class)869 eog_job_save_class_init (EogJobSaveClass *class)
870 {
871 	GObjectClass *g_object_class = (GObjectClass *) class;
872 	EogJobClass  *eog_job_class  = (EogJobClass *)  class;
873 
874 	g_object_class->dispose = eog_job_save_dispose;
875 	eog_job_class->run      = eog_job_save_run;
876 }
877 
878 static
eog_job_save_init(EogJobSave * job)879 void eog_job_save_init (EogJobSave *job)
880 {
881 	/* initialize all public and private members to reasonable
882 	   default values. */
883 	job->images           = NULL;
884 	job->current_position = 0;
885 	job->current_image    = NULL;
886 }
887 
888 static
eog_job_save_dispose(GObject * object)889 void eog_job_save_dispose (GObject *object)
890 {
891 	EogJobSave *job;
892 
893 	g_return_if_fail (EOG_IS_JOB_SAVE (object));
894 
895 	job = EOG_JOB_SAVE (object);
896 
897 	job->current_image = NULL;
898 
899 	/* free all public and private members */
900 	if (job->images) {
901 		g_list_foreach (job->images, (GFunc) g_object_unref, NULL);
902 		g_list_free (job->images);
903 		job->images = NULL;
904 	}
905 
906 	/* call parent dispose */
907 	G_OBJECT_CLASS (eog_job_save_parent_class)->dispose (object);
908 }
909 
910 static void
eog_job_save_progress_callback(EogImage * image,gfloat progress,gpointer data)911 eog_job_save_progress_callback (EogImage *image,
912 				gfloat    progress,
913 				gpointer  data)
914 {
915 	EogJobSave *job;
916 	guint       n_images;
917 	gfloat      job_progress;
918 
919 	job = EOG_JOB_SAVE (data);
920 
921 	n_images     = g_list_length (job->images);
922 	job_progress = (job->current_position / (gfloat) n_images) + (progress / n_images);
923 
924 	eog_job_set_progress (EOG_JOB (job), job_progress);
925 }
926 
927 static void
eog_job_save_run(EogJob * job)928 eog_job_save_run (EogJob *job)
929 {
930 	EogJobSave *save_job;
931 	GList *it;
932 
933 	/* initialization */
934 	g_return_if_fail (EOG_IS_JOB_SAVE (job));
935 
936 	g_object_ref (job);
937 
938 	/* clean previous errors */
939 	if (job->error) {
940 	        g_error_free (job->error);
941 		job->error = NULL;
942 	}
943 
944 	/* check if the current job was previously cancelled */
945 	if (eog_job_is_cancelled (job))
946 		return;
947 
948 	save_job = EOG_JOB_SAVE (job);
949 
950 	save_job->current_position = 0;
951 
952 	for (it = save_job->images; it != NULL; it = it->next, save_job->current_position++) {
953 		EogImage *image = EOG_IMAGE (it->data);
954 		EogImageSaveInfo *save_info = NULL;
955 		gulong handler_id = 0;
956 		gboolean success = FALSE;
957 
958 		save_job->current_image = image;
959 
960 		/* Make sure the image doesn't go away while saving */
961 		eog_image_data_ref (image);
962 
963 		if (!eog_image_has_data (image, EOG_IMAGE_DATA_ALL)) {
964 			EogImageMetadataStatus m_status;
965 			gint data2load = 0;
966 
967 			m_status = eog_image_get_metadata_status (image);
968 			if (!eog_image_has_data (image, EOG_IMAGE_DATA_IMAGE)) {
969 				// Queue full read in this case
970 				data2load = EOG_IMAGE_DATA_ALL;
971 			} else if (m_status == EOG_IMAGE_METADATA_NOT_READ)
972 			{
973 				// Load only if we haven't read it yet
974 				data2load = EOG_IMAGE_DATA_EXIF
975 						| EOG_IMAGE_DATA_XMP;
976 			}
977 
978 			if (data2load != 0) {
979 				eog_image_load (image,
980 						data2load,
981 						NULL,
982 						&job->error);
983 			}
984 		}
985 
986 		handler_id = g_signal_connect (G_OBJECT (image),
987 						   "save-progress",
988 							   G_CALLBACK (eog_job_save_progress_callback),
989 						   job);
990 
991 		save_info = eog_image_save_info_new_from_image (image);
992 
993 		success = eog_image_save_by_info (image,
994 						  save_info,
995 						  &job->error);
996 
997 		if (save_info)
998 			g_object_unref (save_info);
999 
1000 		if (handler_id != 0)
1001 			g_signal_handler_disconnect (G_OBJECT (image), handler_id);
1002 
1003 		eog_image_data_unref (image);
1004 
1005 		if (!success)
1006 			break;
1007 	}
1008 
1009 	/* --- enter critical section --- */
1010 	g_mutex_lock (job->mutex);
1011 
1012 	/* job finished */
1013 	job->finished = TRUE;
1014 
1015 	/* --- leave critical section --- */
1016 	g_mutex_unlock (job->mutex);
1017 
1018 	/* notify job finalization */
1019 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1020 			 (GSourceFunc) notify_finished,
1021 			 job,
1022 			 g_object_unref);
1023 }
1024 
1025 /**
1026  * eog_job_save_new:
1027  * @images: (element-type EogImage) (transfer full): a #EogImage list
1028  *
1029  * Creates a new #EogJob for image saving.
1030  *
1031  * Returns: A #EogJob.
1032  */
1033 
1034 EogJob *
eog_job_save_new(GList * images)1035 eog_job_save_new (GList *images)
1036 {
1037 	EogJobSave *job;
1038 
1039 	job = g_object_new (EOG_TYPE_JOB_SAVE, NULL);
1040 
1041 	if (images)
1042 		job->images = images;
1043 
1044 	/* show info for debugging */
1045 	eog_debug_message (DEBUG_JOBS,
1046 			   "%s (%p) job was CREATED",
1047 			   EOG_GET_TYPE_NAME (job),
1048 			   job);
1049 
1050 	return EOG_JOB (job);
1051 }
1052 
1053 /* ------------------------------- EogJobSaveAs -------------------------------- */
1054 static void
eog_job_save_as_class_init(EogJobSaveAsClass * class)1055 eog_job_save_as_class_init (EogJobSaveAsClass *class)
1056 {
1057 	GObjectClass *g_object_class = (GObjectClass *) class;
1058 	EogJobClass  *eog_job_class  = (EogJobClass *)  class;
1059 
1060 	g_object_class->dispose = eog_job_save_as_dispose;
1061 	eog_job_class->run      = eog_job_save_as_run;
1062 }
1063 
1064 static
eog_job_save_as_init(EogJobSaveAs * job)1065 void eog_job_save_as_init (EogJobSaveAs *job)
1066 {
1067 	/* initialize all public and private members to reasonable
1068 	   default values. */
1069 	job->converter = NULL;
1070 	job->file      = NULL;
1071 }
1072 
1073 static
eog_job_save_as_dispose(GObject * object)1074 void eog_job_save_as_dispose (GObject *object)
1075 {
1076 	EogJobSaveAs *job;
1077 
1078 	g_return_if_fail (EOG_IS_JOB_SAVE_AS (object));
1079 
1080 	job = EOG_JOB_SAVE_AS (object);
1081 
1082 	/* free all public and private members */
1083 	if (job->converter != NULL) {
1084 		g_object_unref (job->converter);
1085 		job->converter = NULL;
1086 	}
1087 
1088 	if (job->file != NULL) {
1089 		g_object_unref (job->file);
1090 		job->file = NULL;
1091 	}
1092 
1093 	/* call parent dispose */
1094 	G_OBJECT_CLASS (eog_job_save_as_parent_class)->dispose (object);
1095 }
1096 
1097 static void
eog_job_save_as_run(EogJob * job)1098 eog_job_save_as_run (EogJob *job)
1099 {
1100 	EogJobSave *save_job;
1101 	EogJobSaveAs *saveas_job;
1102 	GList *it;
1103 	guint n_images;
1104 
1105 	/* initialization */
1106 	g_return_if_fail (EOG_IS_JOB_SAVE_AS (job));
1107 
1108 	/* clean previous errors */
1109 	if (job->error) {
1110 	        g_error_free (job->error);
1111 		job->error = NULL;
1112 	}
1113 
1114 	/* check if the current job was previously cancelled */
1115 	if (eog_job_is_cancelled (job))
1116 		return;
1117 
1118 	save_job = EOG_JOB_SAVE (g_object_ref (job));
1119 	saveas_job = EOG_JOB_SAVE_AS (job);
1120 
1121 	save_job->current_position = 0;
1122 	n_images = g_list_length (save_job->images);
1123 
1124 	for (it = save_job->images; it != NULL; it = it->next, save_job->current_position++) {
1125 		GdkPixbufFormat *format;
1126 		EogImageSaveInfo *src_info, *dest_info;
1127 		EogImage *image = EOG_IMAGE (it->data);
1128 		gboolean success = FALSE;
1129 		gulong handler_id = 0;
1130 
1131 		save_job->current_image = image;
1132 
1133 		eog_image_data_ref (image);
1134 
1135 		if (!eog_image_has_data (image, EOG_IMAGE_DATA_ALL)) {
1136 			EogImageMetadataStatus m_status;
1137 			gint data2load = 0;
1138 
1139 			m_status = eog_image_get_metadata_status (image);
1140 			if (!eog_image_has_data (image, EOG_IMAGE_DATA_IMAGE)) {
1141 				// Queue full read in this case
1142 				data2load = EOG_IMAGE_DATA_ALL;
1143 			} else if (m_status == EOG_IMAGE_METADATA_NOT_READ)
1144 			{
1145 				// Load only if we haven't read it yet
1146 				data2load = EOG_IMAGE_DATA_EXIF
1147 						| EOG_IMAGE_DATA_XMP;
1148 			}
1149 
1150 			if (data2load != 0) {
1151 				eog_image_load (image,
1152 						data2load,
1153 						NULL,
1154 						&job->error);
1155 			}
1156 		}
1157 
1158 
1159 		g_assert (job->error == NULL);
1160 
1161 		handler_id = g_signal_connect (G_OBJECT (image),
1162 						   "save-progress",
1163 							   G_CALLBACK (eog_job_save_progress_callback),
1164 						   job);
1165 
1166 		src_info = eog_image_save_info_new_from_image (image);
1167 
1168 		if (n_images == 1) {
1169 			g_assert (saveas_job->file != NULL);
1170 
1171 			format = eog_pixbuf_get_format (saveas_job->file);
1172 
1173 			dest_info = eog_image_save_info_new_from_file (saveas_job->file,
1174 									   format);
1175 
1176 		/* SaveAsDialog has already secured permission to overwrite */
1177 			if (dest_info->exists) {
1178 				dest_info->overwrite = TRUE;
1179 			}
1180 		} else {
1181 			GFile *dest_file;
1182 			gboolean result;
1183 
1184 			result = eog_uri_converter_do (saveas_job->converter,
1185 							   image,
1186 							   &dest_file,
1187 							   &format,
1188 							   NULL);
1189 
1190 			g_assert (result);
1191 
1192 			dest_info = eog_image_save_info_new_from_file (dest_file,
1193 									   format);
1194 		}
1195 
1196 		success = eog_image_save_as_by_info (image,
1197 							 src_info,
1198 							 dest_info,
1199 							 &job->error);
1200 
1201 		if (src_info)
1202 			g_object_unref (src_info);
1203 
1204 		if (dest_info)
1205 			g_object_unref (dest_info);
1206 
1207 		if (handler_id != 0)
1208 			g_signal_handler_disconnect (G_OBJECT (image), handler_id);
1209 
1210 		eog_image_data_unref (image);
1211 
1212 		if (!success)
1213 			break;
1214 	}
1215 
1216 	/* --- enter critical section --- */
1217 	g_mutex_lock (job->mutex);
1218 
1219 	/* job finished */
1220 	job->finished = TRUE;
1221 
1222 	/* --- leave critical section --- */
1223 	g_mutex_unlock (job->mutex);
1224 
1225 	/* notify job finalization */
1226 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1227 			 (GSourceFunc) notify_finished,
1228 			 job,
1229 			 g_object_unref);
1230 }
1231 
1232 /**
1233  * eog_job_save_as_new:
1234  * @images: (element-type EogImage) (transfer full): a #EogImage list
1235  * @converter: a URI converter
1236  * file: a #GFile
1237  *
1238  * Creates a new #EogJog for save as.
1239  *
1240  * Returns: A #EogJob.
1241  */
1242 
1243 EogJob *
eog_job_save_as_new(GList * images,EogURIConverter * converter,GFile * file)1244 eog_job_save_as_new (GList           *images,
1245 		     EogURIConverter *converter,
1246 		     GFile           *file)
1247 {
1248 	EogJobSaveAs *job;
1249 
1250 	job = g_object_new (EOG_TYPE_JOB_SAVE_AS, NULL);
1251 
1252 	if (images)
1253 		EOG_JOB_SAVE(job)->images = images;
1254 
1255 	if (converter)
1256 		job->converter = g_object_ref (converter);
1257 
1258 	if (file)
1259 		job->file = g_object_ref (file);
1260 
1261 	/* show info for debugging */
1262 	eog_debug_message (DEBUG_JOBS,
1263 			   "%s (%p) job was CREATED",
1264 			   EOG_GET_TYPE_NAME (job),
1265 			   job);
1266 
1267 	return EOG_JOB (job);
1268 }
1269 
1270 /* ------------------------------- EogJobThumbnail -------------------------------- */
1271 static void
eog_job_thumbnail_class_init(EogJobThumbnailClass * class)1272 eog_job_thumbnail_class_init (EogJobThumbnailClass *class)
1273 {
1274 	GObjectClass *g_object_class = (GObjectClass *) class;
1275 	EogJobClass  *eog_job_class  = (EogJobClass *)  class;
1276 
1277 	g_object_class->dispose = eog_job_thumbnail_dispose;
1278 	eog_job_class->run      = eog_job_thumbnail_run;
1279 }
1280 
1281 static
eog_job_thumbnail_init(EogJobThumbnail * job)1282 void eog_job_thumbnail_init (EogJobThumbnail *job)
1283 {
1284 	/* initialize all public and private members to reasonable
1285 	   default values. */
1286 	job->image     = NULL;
1287 	job->thumbnail = NULL;
1288 }
1289 
1290 static
eog_job_thumbnail_dispose(GObject * object)1291 void eog_job_thumbnail_dispose (GObject *object)
1292 {
1293 	EogJobThumbnail *job;
1294 
1295 	g_return_if_fail (EOG_IS_JOB_THUMBNAIL (object));
1296 
1297 	job = EOG_JOB_THUMBNAIL (object);
1298 
1299 	/* free all public and private members */
1300 	if (job->image) {
1301 		g_object_unref (job->image);
1302 		job->image = NULL;
1303 	}
1304 
1305 	if (job->thumbnail) {
1306 		g_object_unref (job->thumbnail);
1307 		job->thumbnail = NULL;
1308 	}
1309 
1310 	/* call parent dispose */
1311 	G_OBJECT_CLASS (eog_job_thumbnail_parent_class)->dispose (object);
1312 }
1313 
1314 static void
eog_job_thumbnail_run(EogJob * job)1315 eog_job_thumbnail_run (EogJob *job)
1316 {
1317 	EogJobThumbnail *job_thumbnail;
1318 
1319 	/* initialization */
1320 	g_return_if_fail (EOG_IS_JOB_THUMBNAIL (job));
1321 
1322 	job_thumbnail = EOG_JOB_THUMBNAIL (g_object_ref (job));
1323 
1324 	/* clean previous errors */
1325 	if (job->error) {
1326 	        g_error_free (job->error);
1327 		job->error = NULL;
1328 	}
1329 
1330 	/* try to load the image thumbnail from cache */
1331 	job_thumbnail->thumbnail = eog_thumbnail_load (job_thumbnail->image,
1332 						       &job->error);
1333 
1334 	if (job_thumbnail->thumbnail) {
1335 		GdkPixbuf *pixbuf;
1336 		gchar     *original_width;
1337 		gchar     *original_height;
1338 		gint       width;
1339 		gint       height;
1340 
1341 		/* create the image thumbnail */
1342 		original_width  = g_strdup (gdk_pixbuf_get_option
1343 					    (job_thumbnail->thumbnail,
1344 					     "tEXt::Thumb::Image::Width"));
1345 		original_height = g_strdup (gdk_pixbuf_get_option
1346 					    (job_thumbnail->thumbnail,
1347 					     "tEXt::Thumb::Image::Height"));
1348 
1349 		pixbuf = eog_thumbnail_fit_to_size (job_thumbnail->thumbnail,
1350 						    EOG_LIST_STORE_THUMB_SIZE);
1351 
1352 		g_object_unref (job_thumbnail->thumbnail);
1353 		job_thumbnail->thumbnail = eog_thumbnail_add_frame (pixbuf);
1354 		g_object_unref (pixbuf);
1355 
1356 		if (original_width) {
1357 			sscanf (original_width, "%i", &width);
1358 			g_object_set_data (G_OBJECT (job_thumbnail->thumbnail),
1359 					   EOG_THUMBNAIL_ORIGINAL_WIDTH,
1360 					   GINT_TO_POINTER (width));
1361 			g_free (original_width);
1362 		}
1363 
1364 		if (original_height) {
1365 			sscanf (original_height, "%i", &height);
1366 			g_object_set_data (G_OBJECT (job_thumbnail->thumbnail),
1367 					   EOG_THUMBNAIL_ORIGINAL_HEIGHT,
1368 					   GINT_TO_POINTER (height));
1369 			g_free (original_height);
1370 		}
1371 	}
1372 
1373 	/* show info for debugging */
1374 	if (job->error)
1375 		g_warning ("%s", job->error->message);
1376 
1377 	/* --- enter critical section --- */
1378 	g_mutex_lock (job->mutex);
1379 
1380 	/* job finished */
1381 	job->finished = TRUE;
1382 
1383 	/* --- leave critical section --- */
1384 	g_mutex_unlock (job->mutex);
1385 
1386 	/* notify job finalization */
1387 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1388 			 (GSourceFunc) notify_finished,
1389 			 job,
1390 			 g_object_unref);
1391 }
1392 
1393 EogJob *
eog_job_thumbnail_new(EogImage * image)1394 eog_job_thumbnail_new (EogImage *image)
1395 {
1396 	EogJobThumbnail *job;
1397 
1398 	job = g_object_new (EOG_TYPE_JOB_THUMBNAIL, NULL);
1399 
1400 	if (image)
1401 		job->image = g_object_ref (image);
1402 
1403 	/* show info for debugging */
1404 	eog_debug_message (DEBUG_JOBS,
1405 			   "%s (%p) job was CREATED",
1406 			   EOG_GET_TYPE_NAME (job),
1407 			   job);
1408 
1409 	return EOG_JOB (job);
1410 }
1411 
1412 /* ------------------------------- EogJobTransform -------------------------------- */
1413 static void
eog_job_transform_class_init(EogJobTransformClass * class)1414 eog_job_transform_class_init (EogJobTransformClass *class)
1415 {
1416 	GObjectClass *g_object_class = (GObjectClass *) class;
1417 	EogJobClass  *eog_job_class  = (EogJobClass *)  class;
1418 
1419 	g_object_class->dispose = eog_job_transform_dispose;
1420 	eog_job_class->run      = eog_job_transform_run;
1421 }
1422 
1423 static
eog_job_transform_init(EogJobTransform * job)1424 void eog_job_transform_init (EogJobTransform *job)
1425 {
1426 	/* initialize all public and private members to reasonable
1427 	   default values. */
1428 	job->images    = NULL;
1429 	job->transform = NULL;
1430 }
1431 
1432 static
eog_job_transform_dispose(GObject * object)1433 void eog_job_transform_dispose (GObject *object)
1434 {
1435 	EogJobTransform *job;
1436 
1437 	g_return_if_fail (EOG_IS_JOB_TRANSFORM (object));
1438 
1439 	job = EOG_JOB_TRANSFORM (object);
1440 
1441 	/* free all public and private members */
1442 	if (job->transform) {
1443 		g_object_unref (job->transform);
1444 		job->transform = NULL;
1445 	}
1446 
1447 	if (job->images) {
1448 		g_list_foreach (job->images, (GFunc) g_object_unref, NULL);
1449 		g_list_free (job->images);
1450 	}
1451 
1452 	/* call parent dispose */
1453 	G_OBJECT_CLASS (eog_job_transform_parent_class)->dispose (object);
1454 }
1455 
1456 static gboolean
eog_job_transform_image_modified(gpointer data)1457 eog_job_transform_image_modified (gpointer data)
1458 {
1459 	g_return_val_if_fail (EOG_IS_IMAGE (data), FALSE);
1460 
1461 	eog_image_modified (EOG_IMAGE (data));
1462 	g_object_unref (G_OBJECT (data));
1463 
1464 	return FALSE;
1465 }
1466 
1467 static void
eog_job_transform_run(EogJob * job)1468 eog_job_transform_run (EogJob *job)
1469 {
1470 	EogJobTransform *transjob;
1471 	GList *it;
1472 
1473 	/* initialization */
1474 	g_return_if_fail (EOG_IS_JOB_TRANSFORM (job));
1475 
1476 	transjob = EOG_JOB_TRANSFORM (g_object_ref (job));
1477 
1478 	/* clean previous errors */
1479 	if (job->error) {
1480 	        g_error_free (job->error);
1481 		job->error = NULL;
1482 	}
1483 
1484 	/* check if the current job was previously cancelled */
1485 	if (eog_job_is_cancelled (job))
1486 	{
1487 		g_object_unref (transjob);
1488 		return;
1489 	}
1490 
1491 	for (it = transjob->images; it != NULL; it = it->next) {
1492 		EogImage *image = EOG_IMAGE (it->data);
1493 
1494 		if (transjob->transform == NULL) {
1495 			eog_image_undo (image);
1496 		} else {
1497 			eog_image_transform (image, transjob->transform, job);
1498 		}
1499 
1500 		if (eog_image_is_modified (image) || transjob->transform == NULL) {
1501 			g_object_ref (image);
1502 			g_idle_add (eog_job_transform_image_modified, image);
1503 		}
1504 
1505 		if (G_UNLIKELY (eog_job_is_cancelled (job)))
1506 		{
1507 			g_object_unref (transjob);
1508 			return;
1509 		}
1510 	}
1511 
1512 	/* --- enter critical section --- */
1513 	g_mutex_lock (job->mutex);
1514 
1515 	/* job finished */
1516 	job->finished = TRUE;
1517 
1518 	/* --- leave critical section --- */
1519 	g_mutex_unlock (job->mutex);
1520 
1521 	/* notify job finalization */
1522 	g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1523 			 (GSourceFunc) notify_finished,
1524 			 job,
1525 			 g_object_unref);
1526 }
1527 
1528 /**
1529  * eog_job_transform_new:
1530  * @images: (element-type EogImage) (transfer full): a #EogImage list
1531  * @transform: a #EogTransform
1532  *
1533  * Create a new #EogJob for image transformation.
1534  *
1535  * Returns: A #EogJob.
1536  */
1537 
1538 EogJob *
eog_job_transform_new(GList * images,EogTransform * transform)1539 eog_job_transform_new (GList        *images,
1540 		       EogTransform *transform)
1541 {
1542 	EogJobTransform *job;
1543 
1544 	job = g_object_new (EOG_TYPE_JOB_TRANSFORM, NULL);
1545 
1546 	if (images)
1547 		job->images = images;
1548 
1549 	if (transform)
1550 		job->transform = g_object_ref (transform);
1551 
1552 	/* show info for debugging */
1553 	eog_debug_message (DEBUG_JOBS,
1554 			   "%s (%p) job was CREATED",
1555 			   EOG_GET_TYPE_NAME (job),
1556 			   job);
1557 
1558 	return EOG_JOB (job);
1559 }
1560