1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /*-
3  * Copyright (c) 2009-2011 Jannis Pohlmann <jannis@xfce.org>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #ifdef HAVE_MEMORY_H
26 #include <memory.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #endif
31 
32 #include <exo/exo.h>
33 
34 #include <thunar/thunar-enum-types.h>
35 #include <thunar/thunar-job.h>
36 #include <thunar/thunar-marshal.h>
37 #include <thunar/thunar-private.h>
38 
39 
40 
41 /* Signal identifiers */
42 enum
43 {
44   ASK,
45   ASK_REPLACE,
46   ASK_JOBS,
47   FILES_READY,
48   NEW_FILES,
49   FROZEN,
50   UNFROZEN,
51   LAST_SIGNAL,
52 };
53 
54 
55 
56 static void              thunar_job_finalize            (GObject            *object);
57 static ThunarJobResponse thunar_job_real_ask            (ThunarJob          *job,
58                                                          const gchar        *message,
59                                                          ThunarJobResponse   choices);
60 static ThunarJobResponse thunar_job_real_ask_replace    (ThunarJob          *job,
61                                                          ThunarFile         *source_file,
62                                                          ThunarFile         *target_file);
63 
64 
65 
66 struct _ThunarJobPrivate
67 {
68   ThunarJobResponse earlier_ask_create_response;
69   ThunarJobResponse earlier_ask_overwrite_response;
70   ThunarJobResponse earlier_ask_delete_response;
71   ThunarJobResponse earlier_ask_skip_response;
72   GList            *total_files;
73   guint             n_total_files;
74   gboolean          pausable;
75   gboolean          paused; /* the job has been manually paused using the UI */
76   gboolean          frozen; /* the job has been automaticaly paused regarding some parallel copy behavior */
77 };
78 
79 
80 
81 static guint job_signals[LAST_SIGNAL];
82 
83 
84 
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(ThunarJob,thunar_job,EXO_TYPE_JOB)85 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ThunarJob, thunar_job, EXO_TYPE_JOB)
86 
87 
88 
89 static gboolean
90 _thunar_job_ask_accumulator (GSignalInvocationHint *ihint,
91                              GValue                *return_accu,
92                              const GValue          *handler_return,
93                              gpointer               data)
94 {
95   g_value_copy (handler_return, return_accu);
96   return FALSE;
97 }
98 
99 
100 
101 static void
thunar_job_class_init(ThunarJobClass * klass)102 thunar_job_class_init (ThunarJobClass *klass)
103 {
104   GObjectClass *gobject_class;
105 
106   gobject_class = G_OBJECT_CLASS (klass);
107   gobject_class->finalize = thunar_job_finalize;
108 
109   klass->ask = thunar_job_real_ask;
110   klass->ask_replace = thunar_job_real_ask_replace;
111 
112   /**
113    * ThunarJob::ask:
114    * @job     : a #ThunarJob.
115    * @message : question to display to the user.
116    * @choices : a combination of #ThunarJobResponse<!---->s.
117    *
118    * The @message is garantied to contain valid UTF-8.
119    *
120    * Return value: the selected choice.
121    **/
122   job_signals[ASK] =
123     g_signal_new (I_("ask"),
124                   G_TYPE_FROM_CLASS (klass),
125                   G_SIGNAL_NO_HOOKS | G_SIGNAL_RUN_LAST,
126                   G_STRUCT_OFFSET (ThunarJobClass, ask),
127                   _thunar_job_ask_accumulator, NULL,
128                   _thunar_marshal_FLAGS__STRING_FLAGS,
129                   THUNAR_TYPE_JOB_RESPONSE,
130                   2, G_TYPE_STRING,
131                   THUNAR_TYPE_JOB_RESPONSE);
132 
133   /**
134    * ThunarJob::ask-replace:
135    * @job      : a #ThunarJob.
136    * @src_file : the #ThunarFile of the source file.
137    * @dst_file : the #ThunarFile of the destination file, that
138    *             may be replaced with the source file.
139    *
140    * Emitted to ask the user whether the destination file should
141    * be replaced by the source file.
142    *
143    * Return value: the selected choice.
144    **/
145   job_signals[ASK_REPLACE] =
146     g_signal_new (I_("ask-replace"),
147                   G_TYPE_FROM_CLASS (klass),
148                   G_SIGNAL_NO_HOOKS | G_SIGNAL_RUN_LAST,
149                   G_STRUCT_OFFSET (ThunarJobClass, ask_replace),
150                   _thunar_job_ask_accumulator, NULL,
151                   _thunar_marshal_FLAGS__OBJECT_OBJECT,
152                   THUNAR_TYPE_JOB_RESPONSE,
153                   2, THUNAR_TYPE_FILE, THUNAR_TYPE_FILE);
154 
155   /**
156    * ThunarJob::files-ready:
157    * @job       : a #ThunarJob.
158    * @file_list : a list of #ThunarFile<!---->s.
159    *
160    * This signal is used by #ThunarJob<!---->s returned by
161    * the thunar_io_jobs_list_directory() function whenever
162    * there's a bunch of #ThunarFile<!---->s ready. This signal
163    * is garantied to be never emitted with an @file_list
164    * parameter of %NULL.
165    *
166    * To allow some further optimizations on the handler-side,
167    * the handler is allowed to take over ownership of the
168    * @file_list, i.e. it can reuse the @infos list and just replace
169    * the data elements with it's own objects based on the
170    * #ThunarFile<!---->s contained within the @file_list (and
171    * of course properly unreffing the previously contained infos).
172    * If a handler takes over ownership of @file_list it must return
173    * %TRUE here, and no further handlers will be run. Else, if
174    * the handler doesn't want to take over ownership of @infos,
175    * it must return %FALSE, and other handlers will be run. Use
176    * this feature with care, and only if you can be sure that
177    * you are the only handler connected to this signal for a
178    * given job!
179    *
180    * Return value: %TRUE if the handler took over ownership of
181    *               @file_list, else %FALSE.
182    **/
183   job_signals[FILES_READY] =
184     g_signal_new (I_("files-ready"),
185                   G_TYPE_FROM_CLASS (klass), G_SIGNAL_NO_HOOKS,
186                   0, g_signal_accumulator_true_handled, NULL,
187                   _thunar_marshal_BOOLEAN__POINTER,
188                   G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
189 
190   /**
191    * ThunarJob::new-files:
192    * @job       : a #ThunarJob.
193    * @file_list : a list of #GFile<!---->s that were created by @job.
194    *
195    * This signal is emitted by the @job right before the @job is terminated
196    * and informs the application about the list of created files in @file_list.
197    * @file_list contains only the toplevel file items, that were specified by
198    * the application on creation of the @job.
199    **/
200   job_signals[NEW_FILES] =
201     g_signal_new (I_("new-files"),
202                   G_TYPE_FROM_CLASS (klass),
203                   G_SIGNAL_NO_HOOKS, 0, NULL, NULL,
204                   g_cclosure_marshal_VOID__POINTER,
205                   G_TYPE_NONE, 1, G_TYPE_POINTER);
206 
207   /**
208    * ThunarJob::ask-jobs:
209    * @job      : a #ThunarJob.
210    *
211    * Emitted to ask the running job list.
212    *
213    * Return value: GList* of running jobs.
214    **/
215   job_signals[ASK_JOBS] =
216     g_signal_new (I_("ask-jobs"),
217                   G_TYPE_FROM_CLASS (klass),
218                   G_SIGNAL_NO_HOOKS, 0,
219                   NULL, NULL,
220                   g_cclosure_marshal_generic,
221                   G_TYPE_POINTER, 0);
222 
223   /**
224    * ThunarJob::frozen:
225    * @job       : a #ThunarJob.
226    *
227    * This signal is emitted by the @job right after the @job is being frozen.
228    **/
229   job_signals[FROZEN] =
230     g_signal_new (I_("frozen"),
231                   G_TYPE_FROM_CLASS (klass),
232                   G_SIGNAL_NO_HOOKS, 0,
233                   NULL, NULL,
234                   g_cclosure_marshal_generic,
235                   G_TYPE_NONE, 0);
236 
237   /**
238    * ThunarJob::unfrozen:
239    * @job       : a #ThunarJob.
240    *
241    * This signal is emitted by the @job right after the @job is being unfrozen.
242    **/
243   job_signals[UNFROZEN] =
244     g_signal_new (I_("unfrozen"),
245                   G_TYPE_FROM_CLASS (klass),
246                   G_SIGNAL_NO_HOOKS, 0,
247                   NULL, NULL,
248                   g_cclosure_marshal_generic,
249                   G_TYPE_NONE, 0);
250 }
251 
252 
253 
254 static void
thunar_job_init(ThunarJob * job)255 thunar_job_init (ThunarJob *job)
256 {
257   job->priv = thunar_job_get_instance_private (job);
258   job->priv->earlier_ask_create_response = 0;
259   job->priv->earlier_ask_overwrite_response = 0;
260   job->priv->earlier_ask_delete_response = 0;
261   job->priv->earlier_ask_skip_response = 0;
262   job->priv->n_total_files = 0;
263   job->priv->pausable = FALSE;
264   job->priv->paused = FALSE;
265   job->priv->frozen = FALSE;
266 }
267 
268 
269 
270 static void
thunar_job_finalize(GObject * object)271 thunar_job_finalize (GObject *object)
272 {
273   (*G_OBJECT_CLASS (thunar_job_parent_class)->finalize) (object);
274 }
275 
276 
277 
278 static ThunarJobResponse
thunar_job_real_ask(ThunarJob * job,const gchar * message,ThunarJobResponse choices)279 thunar_job_real_ask (ThunarJob        *job,
280                      const gchar      *message,
281                      ThunarJobResponse choices)
282 {
283   ThunarJobResponse response;
284 
285   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
286   g_signal_emit (job, job_signals[ASK], 0, message, choices, &response);
287   return response;
288 }
289 
290 
291 
292 static ThunarJobResponse
thunar_job_real_ask_replace(ThunarJob * job,ThunarFile * source_file,ThunarFile * target_file)293 thunar_job_real_ask_replace (ThunarJob  *job,
294                              ThunarFile *source_file,
295                              ThunarFile *target_file)
296 {
297   ThunarJobResponse response;
298   gchar            *message;
299 
300   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
301   _thunar_return_val_if_fail (THUNAR_IS_FILE (source_file), THUNAR_JOB_RESPONSE_CANCEL);
302   _thunar_return_val_if_fail (THUNAR_IS_FILE (target_file), THUNAR_JOB_RESPONSE_CANCEL);
303 
304   message = g_strdup_printf (_("The file \"%s\" already exists. Would you like to replace it?\n\n"
305                                "If you replace an existing file, its contents will be overwritten."),
306                              thunar_file_get_display_name (source_file));
307 
308   g_signal_emit (job, job_signals[ASK], 0, message,
309                  THUNAR_JOB_RESPONSE_REPLACE
310                  | THUNAR_JOB_RESPONSE_REPLACE_ALL
311                  | THUNAR_JOB_RESPONSE_RENAME
312                  | THUNAR_JOB_RESPONSE_RENAME_ALL
313                  | THUNAR_JOB_RESPONSE_SKIP
314                  | THUNAR_JOB_RESPONSE_SKIP_ALL
315                  | THUNAR_JOB_RESPONSE_CANCEL,
316                  &response);
317 
318   /* clean up */
319   g_free (message);
320 
321   return response;
322 }
323 
324 
325 
326 static ThunarJobResponse
_thunar_job_ask_valist(ThunarJob * job,const gchar * format,va_list var_args,const gchar * question,ThunarJobResponse choices)327 _thunar_job_ask_valist (ThunarJob        *job,
328                         const gchar      *format,
329                         va_list           var_args,
330                         const gchar      *question,
331                         ThunarJobResponse choices)
332 {
333   ThunarJobResponse response;
334   gchar            *text;
335   gchar            *message;
336 
337   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
338   _thunar_return_val_if_fail (g_utf8_validate (format, -1, NULL), THUNAR_JOB_RESPONSE_CANCEL);
339 
340   /* generate the dialog message */
341   text = g_strdup_vprintf (format, var_args);
342   message = (question != NULL)
343             ? g_strconcat (text, ".\n\n", question, NULL)
344             : g_strconcat (text, ".", NULL);
345   g_free (text);
346 
347   /* send the question and wait for the answer */
348   exo_job_emit (EXO_JOB (job), job_signals[ASK], 0, message, choices, &response);
349   g_free (message);
350 
351   /* cancel the job as per users request */
352   if (G_UNLIKELY (response == THUNAR_JOB_RESPONSE_CANCEL))
353     exo_job_cancel (EXO_JOB (job));
354 
355   return response;
356 }
357 
358 
359 
360 ThunarJobResponse
thunar_job_ask_overwrite(ThunarJob * job,const gchar * format,...)361 thunar_job_ask_overwrite (ThunarJob   *job,
362                           const gchar *format,
363                           ...)
364 {
365   ThunarJobResponse response;
366   va_list           var_args;
367 
368   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
369   _thunar_return_val_if_fail (format != NULL, THUNAR_JOB_RESPONSE_CANCEL);
370 
371   /* check if the user already cancelled the job */
372   if (G_UNLIKELY (exo_job_is_cancelled (EXO_JOB (job))))
373     return THUNAR_JOB_RESPONSE_CANCEL;
374 
375   /* check if the user said "Overwrite All" earlier */
376   if (G_UNLIKELY (job->priv->earlier_ask_overwrite_response == THUNAR_JOB_RESPONSE_REPLACE_ALL))
377     return THUNAR_JOB_RESPONSE_REPLACE;
378 
379   /* check if the user said "Overwrite None" earlier */
380   if (G_UNLIKELY (job->priv->earlier_ask_overwrite_response == THUNAR_JOB_RESPONSE_SKIP_ALL))
381     return THUNAR_JOB_RESPONSE_SKIP;
382 
383   /* ask the user what he wants to do */
384   va_start (var_args, format);
385   response = _thunar_job_ask_valist (job, format, var_args,
386                                      _("Do you want to overwrite it?"),
387                                      THUNAR_JOB_RESPONSE_REPLACE
388                                      | THUNAR_JOB_RESPONSE_REPLACE_ALL
389                                      | THUNAR_JOB_RESPONSE_SKIP
390                                      | THUNAR_JOB_RESPONSE_SKIP_ALL
391                                      | THUNAR_JOB_RESPONSE_CANCEL);
392   va_end (var_args);
393 
394   /* remember response for later */
395   job->priv->earlier_ask_overwrite_response = response;
396 
397   /* translate response */
398   switch (response)
399     {
400     case THUNAR_JOB_RESPONSE_REPLACE_ALL:
401       response = THUNAR_JOB_RESPONSE_REPLACE;
402       break;
403 
404     case THUNAR_JOB_RESPONSE_SKIP_ALL:
405       response = THUNAR_JOB_RESPONSE_SKIP;
406       break;
407 
408     default:
409       break;
410     }
411 
412   return response;
413 }
414 
415 
416 
417 ThunarJobResponse
thunar_job_ask_delete(ThunarJob * job,const gchar * format,...)418 thunar_job_ask_delete (ThunarJob   *job,
419                        const gchar *format,
420                        ...)
421 {
422   ThunarJobResponse response;
423   va_list           var_args;
424 
425   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
426   _thunar_return_val_if_fail (format != NULL, THUNAR_JOB_RESPONSE_CANCEL);
427 
428   /* check if the user already cancelled the job */
429   if (G_UNLIKELY (exo_job_is_cancelled (EXO_JOB (job))))
430     return THUNAR_JOB_RESPONSE_CANCEL;
431 
432   /* check if the user said "Delete All" earlier */
433   if (G_UNLIKELY (job->priv->earlier_ask_delete_response == THUNAR_JOB_RESPONSE_YES_ALL))
434     return THUNAR_JOB_RESPONSE_YES;
435 
436   /* check if the user said "Delete None" earlier */
437   if (G_UNLIKELY (job->priv->earlier_ask_delete_response == THUNAR_JOB_RESPONSE_NO_ALL))
438     return THUNAR_JOB_RESPONSE_NO;
439 
440   /* ask the user what he wants to do */
441   va_start (var_args, format);
442   response = _thunar_job_ask_valist (job, format, var_args,
443                                      _("Do you want to permanently delete it?"),
444                                      THUNAR_JOB_RESPONSE_YES
445                                      | THUNAR_JOB_RESPONSE_YES_ALL
446                                      | THUNAR_JOB_RESPONSE_NO
447                                      | THUNAR_JOB_RESPONSE_NO_ALL
448                                      | THUNAR_JOB_RESPONSE_CANCEL);
449   va_end (var_args);
450 
451   /* remember response for later */
452   job->priv->earlier_ask_delete_response = response;
453 
454   /* translate response */
455   switch (response)
456     {
457     case THUNAR_JOB_RESPONSE_YES_ALL:
458       response = THUNAR_JOB_RESPONSE_YES;
459       break;
460 
461     case THUNAR_JOB_RESPONSE_NO_ALL:
462       response = THUNAR_JOB_RESPONSE_NO;
463       break;
464 
465     default:
466       break;
467     }
468 
469   return response;
470 }
471 
472 
473 
474 ThunarJobResponse
thunar_job_ask_create(ThunarJob * job,const gchar * format,...)475 thunar_job_ask_create (ThunarJob   *job,
476                        const gchar *format,
477                        ...)
478 {
479   ThunarJobResponse response;
480   va_list           var_args;
481 
482   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
483 
484   if (G_UNLIKELY (exo_job_is_cancelled (EXO_JOB (job))))
485     return THUNAR_JOB_RESPONSE_CANCEL;
486 
487   /* check if the user said "Create All" earlier */
488   if (G_UNLIKELY (job->priv->earlier_ask_create_response == THUNAR_JOB_RESPONSE_YES_ALL))
489     return THUNAR_JOB_RESPONSE_YES;
490 
491   /* check if the user said "Create None" earlier */
492   if (G_UNLIKELY (job->priv->earlier_ask_create_response == THUNAR_JOB_RESPONSE_NO_ALL))
493     return THUNAR_JOB_RESPONSE_NO;
494 
495   va_start (var_args, format);
496   response = _thunar_job_ask_valist (job, format, var_args,
497                                      _("Do you want to create it?"),
498                                      THUNAR_JOB_RESPONSE_YES
499                                      | THUNAR_JOB_RESPONSE_CANCEL);
500   va_end (var_args);
501 
502   job->priv->earlier_ask_create_response = response;
503 
504   /* translate the response */
505   if (response == THUNAR_JOB_RESPONSE_YES_ALL)
506     response = THUNAR_JOB_RESPONSE_YES;
507   else if (response == THUNAR_JOB_RESPONSE_NO_ALL)
508     response = THUNAR_JOB_RESPONSE_NO;
509   else if (response == THUNAR_JOB_RESPONSE_CANCEL)
510     exo_job_cancel (EXO_JOB (job));
511 
512   return response;
513 }
514 
515 
516 
517 ThunarJobResponse
thunar_job_ask_replace(ThunarJob * job,GFile * source_path,GFile * target_path,GError ** error)518 thunar_job_ask_replace (ThunarJob *job,
519                         GFile     *source_path,
520                         GFile     *target_path,
521                         GError   **error)
522 {
523   ThunarJobResponse response;
524   ThunarFile       *source_file;
525   ThunarFile       *target_file;
526 
527   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
528   _thunar_return_val_if_fail (G_IS_FILE (source_path), THUNAR_JOB_RESPONSE_CANCEL);
529   _thunar_return_val_if_fail (G_IS_FILE (target_path), THUNAR_JOB_RESPONSE_CANCEL);
530 
531   if (G_UNLIKELY (exo_job_set_error_if_cancelled (EXO_JOB (job), error)))
532     return THUNAR_JOB_RESPONSE_CANCEL;
533 
534   /* check if the user said "Overwrite All" earlier */
535   if (G_UNLIKELY (job->priv->earlier_ask_overwrite_response == THUNAR_JOB_RESPONSE_REPLACE_ALL))
536     return THUNAR_JOB_RESPONSE_REPLACE;
537 
538   /* check if the user said "Rename All" earlier */
539   if (G_UNLIKELY (job->priv->earlier_ask_overwrite_response == THUNAR_JOB_RESPONSE_RENAME_ALL))
540     return THUNAR_JOB_RESPONSE_RENAME;
541 
542   /* check if the user said "Overwrite None" earlier */
543   if (G_UNLIKELY (job->priv->earlier_ask_overwrite_response == THUNAR_JOB_RESPONSE_SKIP_ALL))
544     return THUNAR_JOB_RESPONSE_SKIP;
545 
546   source_file = thunar_file_get (source_path, error);
547 
548   if (G_UNLIKELY (source_file == NULL))
549     return THUNAR_JOB_RESPONSE_SKIP;
550 
551   target_file = thunar_file_get (target_path, error);
552 
553   if (G_UNLIKELY (target_file == NULL))
554     {
555       g_object_unref (source_file);
556       return THUNAR_JOB_RESPONSE_SKIP;
557     }
558 
559   exo_job_emit (EXO_JOB (job), job_signals[ASK_REPLACE], 0,
560                 source_file, target_file, &response);
561 
562   g_object_unref (source_file);
563   g_object_unref (target_file);
564 
565   /* remember the response for later */
566   job->priv->earlier_ask_overwrite_response = response;
567 
568   /* translate the response */
569   if (response == THUNAR_JOB_RESPONSE_REPLACE_ALL)
570     response = THUNAR_JOB_RESPONSE_REPLACE;
571   else if (response == THUNAR_JOB_RESPONSE_RENAME_ALL)
572     response = THUNAR_JOB_RESPONSE_RENAME;
573   else if (response == THUNAR_JOB_RESPONSE_SKIP_ALL)
574     response = THUNAR_JOB_RESPONSE_SKIP;
575   else if (response == THUNAR_JOB_RESPONSE_CANCEL)
576     exo_job_cancel (EXO_JOB (job));
577 
578   return response;
579 }
580 
581 
582 
583 ThunarJobResponse
thunar_job_ask_skip(ThunarJob * job,const gchar * format,...)584 thunar_job_ask_skip (ThunarJob   *job,
585                      const gchar *format,
586                      ...)
587 {
588   ThunarJobResponse response;
589   va_list           var_args;
590 
591   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
592   _thunar_return_val_if_fail (format != NULL, THUNAR_JOB_RESPONSE_CANCEL);
593 
594   /* check if the user already cancelled the job */
595   if (G_UNLIKELY (exo_job_is_cancelled (EXO_JOB (job))))
596     return THUNAR_JOB_RESPONSE_CANCEL;
597 
598   /* check if the user said "Skip All" earlier */
599   if (G_UNLIKELY (job->priv->earlier_ask_skip_response == THUNAR_JOB_RESPONSE_SKIP_ALL))
600     return THUNAR_JOB_RESPONSE_SKIP;
601 
602   /* ask the user what he wants to do */
603   va_start (var_args, format);
604   response = _thunar_job_ask_valist (job, format, var_args,
605                                      _("Do you want to skip it?"),
606                                      THUNAR_JOB_RESPONSE_SKIP
607                                      | THUNAR_JOB_RESPONSE_SKIP_ALL
608                                      | THUNAR_JOB_RESPONSE_CANCEL
609                                      | THUNAR_JOB_RESPONSE_RETRY);
610   va_end (var_args);
611 
612   /* remember the response */
613   job->priv->earlier_ask_skip_response = response;
614 
615   /* translate the response */
616   switch (response)
617     {
618     case THUNAR_JOB_RESPONSE_SKIP_ALL:
619       response = THUNAR_JOB_RESPONSE_SKIP;
620       break;
621 
622     case THUNAR_JOB_RESPONSE_SKIP:
623     case THUNAR_JOB_RESPONSE_CANCEL:
624     case THUNAR_JOB_RESPONSE_RETRY:
625       break;
626 
627     default:
628       _thunar_assert_not_reached ();
629     }
630 
631   return response;
632 }
633 
634 
635 
636 gboolean
thunar_job_ask_no_size(ThunarJob * job,const gchar * format,...)637 thunar_job_ask_no_size (ThunarJob   *job,
638                         const gchar *format,
639                         ...)
640 {
641   ThunarJobResponse response;
642   va_list           var_args;
643 
644   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), THUNAR_JOB_RESPONSE_CANCEL);
645   _thunar_return_val_if_fail (format != NULL, THUNAR_JOB_RESPONSE_CANCEL);
646 
647   /* check if the user already cancelled the job */
648   if (G_UNLIKELY (exo_job_is_cancelled (EXO_JOB (job))))
649     return THUNAR_JOB_RESPONSE_CANCEL;
650 
651   /* ask the user what he wants to do */
652   va_start (var_args, format);
653   response = _thunar_job_ask_valist (job, format, var_args,
654                                      _("There is not enough space on the destination. Try to remove files to make space."),
655                                      THUNAR_JOB_RESPONSE_FORCE
656                                      | THUNAR_JOB_RESPONSE_CANCEL);
657   va_end (var_args);
658 
659   return (response == THUNAR_JOB_RESPONSE_FORCE);
660 }
661 
662 
663 
664 gboolean
thunar_job_files_ready(ThunarJob * job,GList * file_list)665 thunar_job_files_ready (ThunarJob *job,
666                         GList     *file_list)
667 {
668   gboolean handled = FALSE;
669 
670   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE);
671 
672   exo_job_emit (EXO_JOB (job), job_signals[FILES_READY], 0, file_list, &handled);
673   return handled;
674 }
675 
676 
677 
678 void
thunar_job_new_files(ThunarJob * job,const GList * file_list)679 thunar_job_new_files (ThunarJob   *job,
680                       const GList *file_list)
681 {
682   ThunarFile  *file;
683   const GList *lp;
684 
685   _thunar_return_if_fail (THUNAR_IS_JOB (job));
686 
687   /* check if we have any files */
688   if (G_LIKELY (file_list != NULL))
689     {
690       /* schedule a reload of cached files when idle */
691       for (lp = file_list; lp != NULL; lp = lp->next)
692         {
693           file = thunar_file_cache_lookup (lp->data);
694           if (file != NULL)
695             {
696               thunar_file_reload_idle_unref (file);
697             }
698         }
699 
700       /* emit the "new-files" signal */
701       exo_job_emit (EXO_JOB (job), job_signals[NEW_FILES], 0, file_list);
702     }
703 }
704 
705 
706 
707 GList *
thunar_job_ask_jobs(ThunarJob * job)708 thunar_job_ask_jobs (ThunarJob *job)
709 {
710   GList* jobs = NULL;
711 
712   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), NULL);
713 
714   g_signal_emit (EXO_JOB (job), job_signals[ASK_JOBS], 0, &jobs);
715   return jobs;
716 }
717 
718 
719 
720 void
thunar_job_set_total_files(ThunarJob * job,GList * total_files)721 thunar_job_set_total_files (ThunarJob *job,
722                             GList     *total_files)
723 {
724   _thunar_return_if_fail (THUNAR_IS_JOB (job));
725   _thunar_return_if_fail (job->priv->total_files == NULL);
726   _thunar_return_if_fail (total_files != NULL);
727 
728   job->priv->total_files = total_files;
729   job->priv->n_total_files = g_list_length (total_files);
730 }
731 
732 
733 
734 void
thunar_job_set_pausable(ThunarJob * job,gboolean pausable)735 thunar_job_set_pausable (ThunarJob *job,
736                          gboolean   pausable)
737 {
738   _thunar_return_if_fail (THUNAR_IS_JOB (job));
739   job->priv->pausable = pausable;
740 }
741 
742 
743 
744 gboolean
thunar_job_is_pausable(ThunarJob * job)745 thunar_job_is_pausable (ThunarJob *job)
746 {
747   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE);
748   return job->priv->pausable;
749 }
750 
751 
752 
753 void
thunar_job_pause(ThunarJob * job)754 thunar_job_pause (ThunarJob *job)
755 {
756   _thunar_return_if_fail (THUNAR_IS_JOB (job));
757   job->priv->paused = TRUE;
758 }
759 
760 
761 
762 void
thunar_job_resume(ThunarJob * job)763 thunar_job_resume (ThunarJob *job)
764 {
765   _thunar_return_if_fail (THUNAR_IS_JOB (job));
766   job->priv->paused = FALSE;
767 }
768 
769 
770 
771 void
thunar_job_freeze(ThunarJob * job)772 thunar_job_freeze (ThunarJob *job)
773 {
774   _thunar_return_if_fail (THUNAR_IS_JOB (job));
775   job->priv->frozen = TRUE;
776   g_signal_emit (EXO_JOB (job), job_signals[FROZEN], 0);
777 }
778 
779 
780 
781 void
thunar_job_unfreeze(ThunarJob * job)782 thunar_job_unfreeze (ThunarJob *job)
783 {
784   _thunar_return_if_fail (THUNAR_IS_JOB (job));
785   job->priv->frozen = FALSE;
786   g_signal_emit (EXO_JOB (job), job_signals[UNFROZEN], 0);
787 }
788 
789 
790 
791 gboolean
thunar_job_is_paused(ThunarJob * job)792 thunar_job_is_paused (ThunarJob *job)
793 {
794   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE);
795   return job->priv->paused;
796 }
797 
798 
799 
800 gboolean
thunar_job_is_frozen(ThunarJob * job)801 thunar_job_is_frozen (ThunarJob *job)
802 {
803   _thunar_return_val_if_fail (THUNAR_IS_JOB (job), FALSE);
804   return job->priv->frozen;
805 }
806 
807 
808 
809 void
thunar_job_processing_file(ThunarJob * job,GList * current_file,guint n_processed)810 thunar_job_processing_file (ThunarJob *job,
811                             GList     *current_file,
812                             guint      n_processed)
813 {
814   gchar *base_name;
815   gchar *display_name;
816 
817   _thunar_return_if_fail (THUNAR_IS_JOB (job));
818   _thunar_return_if_fail (current_file != NULL);
819 
820   /* emit only if n_processed is a multiple of 8 */
821   if ((n_processed % 8) != 0)
822     return;
823 
824   base_name = g_file_get_basename (current_file->data);
825   display_name = g_filename_display_name (base_name);
826   g_free (base_name);
827 
828   exo_job_info_message (EXO_JOB (job), "%s", display_name);
829   g_free (display_name);
830 
831   /* verify that we have total files set */
832   if (G_LIKELY (job->priv->n_total_files > 0))
833     exo_job_percent (EXO_JOB (job), (n_processed * 100.0) / job->priv->n_total_files);
834 }
835