1 /*
2 * nautilus-progress-info.h: file operation progress info.
3 *
4 * Copyright (C) 2007 Red Hat, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Alexander Larsson <alexl@redhat.com>
20 */
21
22 #include <config.h>
23 #include <math.h>
24 #include <glib/gi18n.h>
25 #include <eel/eel-string.h>
26 #include <eel/eel-glib-extensions.h>
27 #include "nautilus-progress-info.h"
28 #include "nautilus-progress-info-manager.h"
29 #include "nautilus-icon-info.h"
30
31 enum
32 {
33 CHANGED,
34 PROGRESS_CHANGED,
35 STARTED,
36 FINISHED,
37 CANCELLED,
38 LAST_SIGNAL
39 };
40
41 #define SIGNAL_DELAY_MSEC 100
42
43 static guint signals[LAST_SIGNAL] = { 0 };
44
45 struct _NautilusProgressInfo
46 {
47 GObject parent_instance;
48
49 GCancellable *cancellable;
50 guint cancellable_id;
51
52 GTimer *progress_timer;
53
54 char *status;
55 char *details;
56 double progress;
57 gdouble remaining_time;
58 gdouble elapsed_time;
59 gboolean activity_mode;
60 gboolean started;
61 gboolean finished;
62 gboolean paused;
63
64 GSource *idle_source;
65 gboolean source_is_now;
66
67 gboolean start_at_idle;
68 gboolean finish_at_idle;
69 gboolean cancel_at_idle;
70 gboolean changed_at_idle;
71 gboolean progress_at_idle;
72
73 GFile *destination;
74 };
75
76 G_LOCK_DEFINE_STATIC (progress_info);
77
78 G_DEFINE_TYPE (NautilusProgressInfo, nautilus_progress_info, G_TYPE_OBJECT)
79
80 static void set_details (NautilusProgressInfo *info,
81 const char *details);
82 static void set_status (NautilusProgressInfo *info,
83 const char *status);
84
85 static void
nautilus_progress_info_finalize(GObject * object)86 nautilus_progress_info_finalize (GObject *object)
87 {
88 NautilusProgressInfo *info;
89
90 info = NAUTILUS_PROGRESS_INFO (object);
91
92 g_free (info->status);
93 g_free (info->details);
94 g_clear_pointer (&info->progress_timer, g_timer_destroy);
95 g_cancellable_disconnect (info->cancellable, info->cancellable_id);
96 g_object_unref (info->cancellable);
97 g_clear_object (&info->destination);
98
99 if (G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize)
100 {
101 (*G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize)(object);
102 }
103 }
104
105 static void
nautilus_progress_info_dispose(GObject * object)106 nautilus_progress_info_dispose (GObject *object)
107 {
108 NautilusProgressInfo *info;
109
110 info = NAUTILUS_PROGRESS_INFO (object);
111
112 G_LOCK (progress_info);
113
114 /* Destroy source in dispose, because the callback
115 * could come here before the destroy, which should
116 * ressurect the object for a while */
117 if (info->idle_source)
118 {
119 g_source_destroy (info->idle_source);
120 g_source_unref (info->idle_source);
121 info->idle_source = NULL;
122 }
123 G_UNLOCK (progress_info);
124 }
125
126 static void
nautilus_progress_info_class_init(NautilusProgressInfoClass * klass)127 nautilus_progress_info_class_init (NautilusProgressInfoClass *klass)
128 {
129 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
130
131 gobject_class->finalize = nautilus_progress_info_finalize;
132 gobject_class->dispose = nautilus_progress_info_dispose;
133
134 signals[CHANGED] =
135 g_signal_new ("changed",
136 NAUTILUS_TYPE_PROGRESS_INFO,
137 G_SIGNAL_RUN_LAST,
138 0,
139 NULL, NULL,
140 g_cclosure_marshal_VOID__VOID,
141 G_TYPE_NONE, 0);
142
143 signals[PROGRESS_CHANGED] =
144 g_signal_new ("progress-changed",
145 NAUTILUS_TYPE_PROGRESS_INFO,
146 G_SIGNAL_RUN_LAST,
147 0,
148 NULL, NULL,
149 g_cclosure_marshal_VOID__VOID,
150 G_TYPE_NONE, 0);
151
152 signals[STARTED] =
153 g_signal_new ("started",
154 NAUTILUS_TYPE_PROGRESS_INFO,
155 G_SIGNAL_RUN_LAST,
156 0,
157 NULL, NULL,
158 g_cclosure_marshal_VOID__VOID,
159 G_TYPE_NONE, 0);
160
161 signals[FINISHED] =
162 g_signal_new ("finished",
163 NAUTILUS_TYPE_PROGRESS_INFO,
164 G_SIGNAL_RUN_LAST,
165 0,
166 NULL, NULL,
167 g_cclosure_marshal_VOID__VOID,
168 G_TYPE_NONE, 0);
169
170 signals[CANCELLED] =
171 g_signal_new ("cancelled",
172 NAUTILUS_TYPE_PROGRESS_INFO,
173 G_SIGNAL_RUN_LAST,
174 0,
175 NULL, NULL,
176 g_cclosure_marshal_VOID__VOID,
177 G_TYPE_NONE, 0);
178 }
179
180 static gboolean
idle_callback(gpointer data)181 idle_callback (gpointer data)
182 {
183 NautilusProgressInfo *info = data;
184 gboolean start_at_idle;
185 gboolean finish_at_idle;
186 gboolean changed_at_idle;
187 gboolean progress_at_idle;
188 gboolean cancelled_at_idle;
189 GSource *source;
190
191 G_LOCK (progress_info);
192
193 source = g_main_current_source ();
194
195 /* Protect agains races where the source has
196 * been destroyed on another thread while it
197 * was being dispatched.
198 * Similar to what gdk_threads_add_idle does.
199 */
200 if (g_source_is_destroyed (source))
201 {
202 G_UNLOCK (progress_info);
203 return FALSE;
204 }
205
206 /* We hadn't destroyed the source, so take a ref.
207 * This might ressurect the object from dispose, but
208 * that should be ok.
209 */
210 g_object_ref (info);
211
212 g_assert (source == info->idle_source);
213
214 g_source_unref (source);
215 info->idle_source = NULL;
216
217 start_at_idle = info->start_at_idle;
218 finish_at_idle = info->finish_at_idle;
219 changed_at_idle = info->changed_at_idle;
220 progress_at_idle = info->progress_at_idle;
221 cancelled_at_idle = info->cancel_at_idle;
222
223 info->start_at_idle = FALSE;
224 info->finish_at_idle = FALSE;
225 info->changed_at_idle = FALSE;
226 info->progress_at_idle = FALSE;
227 info->cancel_at_idle = FALSE;
228
229 G_UNLOCK (progress_info);
230
231 if (start_at_idle)
232 {
233 g_signal_emit (info,
234 signals[STARTED],
235 0);
236 }
237
238 if (changed_at_idle)
239 {
240 g_signal_emit (info,
241 signals[CHANGED],
242 0);
243 }
244
245 if (progress_at_idle)
246 {
247 g_signal_emit (info,
248 signals[PROGRESS_CHANGED],
249 0);
250 }
251
252 if (finish_at_idle)
253 {
254 g_signal_emit (info,
255 signals[FINISHED],
256 0);
257 }
258
259 if (cancelled_at_idle)
260 {
261 g_signal_emit (info,
262 signals[CANCELLED],
263 0);
264 }
265
266 g_object_unref (info);
267
268 return FALSE;
269 }
270
271
272 /* Called with lock held */
273 static void
queue_idle(NautilusProgressInfo * info,gboolean now)274 queue_idle (NautilusProgressInfo *info,
275 gboolean now)
276 {
277 if (info->idle_source == NULL ||
278 (now && !info->source_is_now))
279 {
280 if (info->idle_source)
281 {
282 g_source_destroy (info->idle_source);
283 g_source_unref (info->idle_source);
284 info->idle_source = NULL;
285 }
286
287 info->source_is_now = now;
288 if (now)
289 {
290 info->idle_source = g_idle_source_new ();
291 }
292 else
293 {
294 info->idle_source = g_timeout_source_new (SIGNAL_DELAY_MSEC);
295 }
296 g_source_set_callback (info->idle_source, idle_callback, info, NULL);
297 g_source_attach (info->idle_source, NULL);
298 }
299 }
300
301 static void
on_canceled(GCancellable * cancellable,NautilusProgressInfo * info)302 on_canceled (GCancellable *cancellable,
303 NautilusProgressInfo *info)
304 {
305 G_LOCK (progress_info);
306 set_details (info, _("Canceled"));
307 info->cancel_at_idle = TRUE;
308 g_timer_stop (info->progress_timer);
309 queue_idle (info, TRUE);
310 G_UNLOCK (progress_info);
311 }
312
313 static void
nautilus_progress_info_init(NautilusProgressInfo * info)314 nautilus_progress_info_init (NautilusProgressInfo *info)
315 {
316 NautilusProgressInfoManager *manager;
317
318 info->cancellable = g_cancellable_new ();
319 info->cancellable_id = g_cancellable_connect (info->cancellable,
320 G_CALLBACK (on_canceled),
321 info,
322 NULL);
323
324 manager = nautilus_progress_info_manager_dup_singleton ();
325 nautilus_progress_info_manager_add_new_info (manager, info);
326 g_object_unref (manager);
327 info->progress_timer = g_timer_new ();
328 }
329
330 NautilusProgressInfo *
nautilus_progress_info_new(void)331 nautilus_progress_info_new (void)
332 {
333 NautilusProgressInfo *info;
334
335 info = g_object_new (NAUTILUS_TYPE_PROGRESS_INFO, NULL);
336
337 return info;
338 }
339
340 char *
nautilus_progress_info_get_status(NautilusProgressInfo * info)341 nautilus_progress_info_get_status (NautilusProgressInfo *info)
342 {
343 char *res;
344
345 G_LOCK (progress_info);
346
347 if (info->status)
348 {
349 res = g_strdup (info->status);
350 }
351 else
352 {
353 res = g_strdup (_("Preparing"));
354 }
355
356 G_UNLOCK (progress_info);
357
358 return res;
359 }
360
361 char *
nautilus_progress_info_get_details(NautilusProgressInfo * info)362 nautilus_progress_info_get_details (NautilusProgressInfo *info)
363 {
364 char *res;
365
366 G_LOCK (progress_info);
367
368 if (info->details)
369 {
370 res = g_strdup (info->details);
371 }
372 else
373 {
374 res = g_strdup (_("Preparing"));
375 }
376
377 G_UNLOCK (progress_info);
378
379 return res;
380 }
381
382 double
nautilus_progress_info_get_progress(NautilusProgressInfo * info)383 nautilus_progress_info_get_progress (NautilusProgressInfo *info)
384 {
385 double res;
386
387 G_LOCK (progress_info);
388
389 if (info->activity_mode)
390 {
391 res = -1.0;
392 }
393 else
394 {
395 res = info->progress;
396 }
397
398 G_UNLOCK (progress_info);
399
400 return res;
401 }
402
403 void
nautilus_progress_info_cancel(NautilusProgressInfo * info)404 nautilus_progress_info_cancel (NautilusProgressInfo *info)
405 {
406 GCancellable *cancellable;
407
408 cancellable = nautilus_progress_info_get_cancellable (info);
409 g_cancellable_cancel (cancellable);
410 g_object_unref (cancellable);
411 }
412
413 GCancellable *
nautilus_progress_info_get_cancellable(NautilusProgressInfo * info)414 nautilus_progress_info_get_cancellable (NautilusProgressInfo *info)
415 {
416 GCancellable *c;
417
418 G_LOCK (progress_info);
419
420 c = g_object_ref (info->cancellable);
421
422 G_UNLOCK (progress_info);
423
424 return c;
425 }
426
427 gboolean
nautilus_progress_info_get_is_cancelled(NautilusProgressInfo * info)428 nautilus_progress_info_get_is_cancelled (NautilusProgressInfo *info)
429 {
430 gboolean cancelled;
431
432 G_LOCK (progress_info);
433 cancelled = g_cancellable_is_cancelled (info->cancellable);
434 G_UNLOCK (progress_info);
435
436 return cancelled;
437 }
438
439 gboolean
nautilus_progress_info_get_is_started(NautilusProgressInfo * info)440 nautilus_progress_info_get_is_started (NautilusProgressInfo *info)
441 {
442 gboolean res;
443
444 G_LOCK (progress_info);
445
446 res = info->started;
447
448 G_UNLOCK (progress_info);
449
450 return res;
451 }
452
453 gboolean
nautilus_progress_info_get_is_finished(NautilusProgressInfo * info)454 nautilus_progress_info_get_is_finished (NautilusProgressInfo *info)
455 {
456 gboolean res;
457
458 G_LOCK (progress_info);
459
460 res = info->finished;
461
462 G_UNLOCK (progress_info);
463
464 return res;
465 }
466
467 gboolean
nautilus_progress_info_get_is_paused(NautilusProgressInfo * info)468 nautilus_progress_info_get_is_paused (NautilusProgressInfo *info)
469 {
470 gboolean res;
471
472 G_LOCK (progress_info);
473
474 res = info->paused;
475
476 G_UNLOCK (progress_info);
477
478 return res;
479 }
480
481 void
nautilus_progress_info_pause(NautilusProgressInfo * info)482 nautilus_progress_info_pause (NautilusProgressInfo *info)
483 {
484 G_LOCK (progress_info);
485
486 if (!info->paused)
487 {
488 info->paused = TRUE;
489 g_timer_stop (info->progress_timer);
490 }
491
492 G_UNLOCK (progress_info);
493 }
494
495 void
nautilus_progress_info_resume(NautilusProgressInfo * info)496 nautilus_progress_info_resume (NautilusProgressInfo *info)
497 {
498 G_LOCK (progress_info);
499
500 if (info->paused)
501 {
502 info->paused = FALSE;
503 g_timer_continue (info->progress_timer);
504 }
505
506 G_UNLOCK (progress_info);
507 }
508
509 void
nautilus_progress_info_start(NautilusProgressInfo * info)510 nautilus_progress_info_start (NautilusProgressInfo *info)
511 {
512 G_LOCK (progress_info);
513
514 if (!info->started)
515 {
516 info->started = TRUE;
517 g_timer_start (info->progress_timer);
518
519 info->start_at_idle = TRUE;
520 queue_idle (info, TRUE);
521 }
522
523 G_UNLOCK (progress_info);
524 }
525
526 void
nautilus_progress_info_finish(NautilusProgressInfo * info)527 nautilus_progress_info_finish (NautilusProgressInfo *info)
528 {
529 G_LOCK (progress_info);
530
531 if (!info->finished)
532 {
533 info->finished = TRUE;
534 g_timer_stop (info->progress_timer);
535
536 info->finish_at_idle = TRUE;
537 queue_idle (info, TRUE);
538 }
539
540 G_UNLOCK (progress_info);
541 }
542
543 static void
set_status(NautilusProgressInfo * info,const char * status)544 set_status (NautilusProgressInfo *info,
545 const char *status)
546 {
547 g_free (info->status);
548 info->status = g_strdup (status);
549
550 info->changed_at_idle = TRUE;
551 queue_idle (info, FALSE);
552 }
553
554 void
nautilus_progress_info_take_status(NautilusProgressInfo * info,char * status)555 nautilus_progress_info_take_status (NautilusProgressInfo *info,
556 char *status)
557 {
558 G_LOCK (progress_info);
559
560 if (g_strcmp0 (info->status, status) != 0 &&
561 !g_cancellable_is_cancelled (info->cancellable))
562 {
563 set_status (info, status);
564 }
565
566 G_UNLOCK (progress_info);
567
568 g_free (status);
569 }
570
571 void
nautilus_progress_info_set_status(NautilusProgressInfo * info,const char * status)572 nautilus_progress_info_set_status (NautilusProgressInfo *info,
573 const char *status)
574 {
575 G_LOCK (progress_info);
576
577 if (g_strcmp0 (info->status, status) != 0 &&
578 !g_cancellable_is_cancelled (info->cancellable))
579 {
580 set_status (info, status);
581 }
582
583 G_UNLOCK (progress_info);
584 }
585
586 static void
set_details(NautilusProgressInfo * info,const char * details)587 set_details (NautilusProgressInfo *info,
588 const char *details)
589 {
590 g_free (info->details);
591 info->details = g_strdup (details);
592
593 info->changed_at_idle = TRUE;
594 queue_idle (info, FALSE);
595 }
596
597 void
nautilus_progress_info_take_details(NautilusProgressInfo * info,char * details)598 nautilus_progress_info_take_details (NautilusProgressInfo *info,
599 char *details)
600 {
601 G_LOCK (progress_info);
602
603 if (g_strcmp0 (info->details, details) != 0 &&
604 !g_cancellable_is_cancelled (info->cancellable))
605 {
606 set_details (info, details);
607 }
608
609 G_UNLOCK (progress_info);
610
611 g_free (details);
612 }
613
614 void
nautilus_progress_info_set_details(NautilusProgressInfo * info,const char * details)615 nautilus_progress_info_set_details (NautilusProgressInfo *info,
616 const char *details)
617 {
618 G_LOCK (progress_info);
619
620 if (g_strcmp0 (info->details, details) != 0 &&
621 !g_cancellable_is_cancelled (info->cancellable))
622 {
623 set_details (info, details);
624 }
625
626 G_UNLOCK (progress_info);
627 }
628
629 void
nautilus_progress_info_pulse_progress(NautilusProgressInfo * info)630 nautilus_progress_info_pulse_progress (NautilusProgressInfo *info)
631 {
632 G_LOCK (progress_info);
633
634 info->activity_mode = TRUE;
635 info->progress = 0.0;
636 info->progress_at_idle = TRUE;
637 queue_idle (info, FALSE);
638
639 G_UNLOCK (progress_info);
640 }
641
642 void
nautilus_progress_info_set_progress(NautilusProgressInfo * info,double current,double total)643 nautilus_progress_info_set_progress (NautilusProgressInfo *info,
644 double current,
645 double total)
646 {
647 double current_percent;
648
649 if (total <= 0)
650 {
651 current_percent = 1.0;
652 }
653 else
654 {
655 current_percent = current / total;
656
657 if (current_percent < 0)
658 {
659 current_percent = 0;
660 }
661
662 if (current_percent > 1.0)
663 {
664 current_percent = 1.0;
665 }
666 }
667
668 G_LOCK (progress_info);
669
670 if ((info->activity_mode || /* emit on switch from activity mode */
671 fabs (current_percent - info->progress) > 0.005) && /* Emit on change of 0.5 percent */
672 !g_cancellable_is_cancelled (info->cancellable))
673 {
674 info->activity_mode = FALSE;
675 info->progress = current_percent;
676 info->progress_at_idle = TRUE;
677 queue_idle (info, FALSE);
678 }
679
680 G_UNLOCK (progress_info);
681 }
682
683 void
nautilus_progress_info_set_remaining_time(NautilusProgressInfo * info,gdouble time)684 nautilus_progress_info_set_remaining_time (NautilusProgressInfo *info,
685 gdouble time)
686 {
687 G_LOCK (progress_info);
688 info->remaining_time = time;
689 G_UNLOCK (progress_info);
690 }
691
692 gdouble
nautilus_progress_info_get_remaining_time(NautilusProgressInfo * info)693 nautilus_progress_info_get_remaining_time (NautilusProgressInfo *info)
694 {
695 gint remaining_time;
696
697 G_LOCK (progress_info);
698 remaining_time = info->remaining_time;
699 G_UNLOCK (progress_info);
700
701 return remaining_time;
702 }
703
704 void
nautilus_progress_info_set_elapsed_time(NautilusProgressInfo * info,gdouble time)705 nautilus_progress_info_set_elapsed_time (NautilusProgressInfo *info,
706 gdouble time)
707 {
708 G_LOCK (progress_info);
709 info->elapsed_time = time;
710 G_UNLOCK (progress_info);
711 }
712
713 gdouble
nautilus_progress_info_get_elapsed_time(NautilusProgressInfo * info)714 nautilus_progress_info_get_elapsed_time (NautilusProgressInfo *info)
715 {
716 gint elapsed_time;
717
718 G_LOCK (progress_info);
719 elapsed_time = info->elapsed_time;
720 G_UNLOCK (progress_info);
721
722 return elapsed_time;
723 }
724
725 gdouble
nautilus_progress_info_get_total_elapsed_time(NautilusProgressInfo * info)726 nautilus_progress_info_get_total_elapsed_time (NautilusProgressInfo *info)
727 {
728 gdouble elapsed_time;
729
730 G_LOCK (progress_info);
731 elapsed_time = g_timer_elapsed (info->progress_timer, NULL);
732 G_UNLOCK (progress_info);
733
734 return elapsed_time;
735 }
736
737 void
nautilus_progress_info_set_destination(NautilusProgressInfo * info,GFile * file)738 nautilus_progress_info_set_destination (NautilusProgressInfo *info,
739 GFile *file)
740 {
741 G_LOCK (progress_info);
742 g_clear_object (&info->destination);
743 info->destination = g_object_ref (file);
744 G_UNLOCK (progress_info);
745 }
746
747 GFile *
nautilus_progress_info_get_destination(NautilusProgressInfo * info)748 nautilus_progress_info_get_destination (NautilusProgressInfo *info)
749 {
750 GFile *destination = NULL;
751
752 G_LOCK (progress_info);
753 if (info->destination)
754 {
755 destination = g_object_ref (info->destination);
756 }
757 G_UNLOCK (progress_info);
758
759 return destination;
760 }
761