1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 nemo-progress-info.h: file operation progress info.
4
5 Copyright (C) 2007 Red Hat, Inc.
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public
18 License along with this program; if not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
20 Boston, MA 02110-1335, USA.
21
22 Author: Alexander Larsson <alexl@redhat.com>
23 */
24
25 #include <config.h>
26 #include <math.h>
27 #include <glib/gi18n.h>
28 #include <eel/eel-string.h>
29 #include <eel/eel-glib-extensions.h>
30 #include "nemo-progress-info.h"
31 #include "nemo-progress-info-manager.h"
32
33 enum {
34 CHANGED,
35 QUEUED,
36 PROGRESS_CHANGED,
37 STARTED,
38 FINISHED,
39 LAST_SIGNAL
40 };
41
42 #define SIGNAL_DELAY_MSEC 100
43
44 static guint signals[LAST_SIGNAL] = { 0 };
45
46 struct _NemoProgressInfo
47 {
48 GObject parent_instance;
49
50 GCancellable *cancellable;
51
52 char *status;
53 char *details;
54 char *initial_details;
55 double progress;
56 gboolean activity_mode;
57 gboolean started;
58 gboolean finished;
59 gboolean paused;
60 gboolean queued;
61
62 GSource *idle_source;
63 gboolean source_is_now;
64
65 gboolean start_at_idle;
66 gboolean finish_at_idle;
67 gboolean changed_at_idle;
68 gboolean progress_at_idle;
69 gboolean queue_at_idle;
70 };
71
72 struct _NemoProgressInfoClass
73 {
74 GObjectClass parent_class;
75 };
76
77 G_LOCK_DEFINE_STATIC(progress_info);
78
G_DEFINE_TYPE(NemoProgressInfo,nemo_progress_info,G_TYPE_OBJECT)79 G_DEFINE_TYPE (NemoProgressInfo, nemo_progress_info, G_TYPE_OBJECT)
80
81 static void
82 nemo_progress_info_finalize (GObject *object)
83 {
84 NemoProgressInfo *info;
85
86 info = NEMO_PROGRESS_INFO (object);
87
88 g_free (info->status);
89 g_free (info->details);
90 g_free (info->initial_details);
91 g_object_unref (info->cancellable);
92
93 if (G_OBJECT_CLASS (nemo_progress_info_parent_class)->finalize) {
94 (*G_OBJECT_CLASS (nemo_progress_info_parent_class)->finalize) (object);
95 }
96 }
97
98 static void
nemo_progress_info_dispose(GObject * object)99 nemo_progress_info_dispose (GObject *object)
100 {
101 NemoProgressInfo *info;
102
103 info = NEMO_PROGRESS_INFO (object);
104
105 G_LOCK (progress_info);
106
107 /* Destroy source in dispose, because the callback
108 could come here before the destroy, which should
109 ressurect the object for a while */
110 if (info->idle_source) {
111 g_source_destroy (info->idle_source);
112 g_source_unref (info->idle_source);
113 info->idle_source = NULL;
114 }
115 G_UNLOCK (progress_info);
116 }
117
118 static void
nemo_progress_info_class_init(NemoProgressInfoClass * klass)119 nemo_progress_info_class_init (NemoProgressInfoClass *klass)
120 {
121 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
122
123 gobject_class->finalize = nemo_progress_info_finalize;
124 gobject_class->dispose = nemo_progress_info_dispose;
125
126 signals[CHANGED] =
127 g_signal_new ("changed",
128 NEMO_TYPE_PROGRESS_INFO,
129 G_SIGNAL_RUN_LAST,
130 0,
131 NULL, NULL,
132 g_cclosure_marshal_VOID__VOID,
133 G_TYPE_NONE, 0);
134
135 signals[QUEUED] =
136 g_signal_new ("queued",
137 NEMO_TYPE_PROGRESS_INFO,
138 G_SIGNAL_RUN_LAST,
139 0,
140 NULL, NULL,
141 g_cclosure_marshal_VOID__VOID,
142 G_TYPE_NONE, 0);
143
144 signals[PROGRESS_CHANGED] =
145 g_signal_new ("progress-changed",
146 NEMO_TYPE_PROGRESS_INFO,
147 G_SIGNAL_RUN_LAST,
148 0,
149 NULL, NULL,
150 g_cclosure_marshal_VOID__VOID,
151 G_TYPE_NONE, 0);
152
153 signals[STARTED] =
154 g_signal_new ("started",
155 NEMO_TYPE_PROGRESS_INFO,
156 G_SIGNAL_RUN_LAST,
157 0,
158 NULL, NULL,
159 g_cclosure_marshal_VOID__VOID,
160 G_TYPE_NONE, 0);
161
162 signals[FINISHED] =
163 g_signal_new ("finished",
164 NEMO_TYPE_PROGRESS_INFO,
165 G_SIGNAL_RUN_LAST,
166 0,
167 NULL, NULL,
168 g_cclosure_marshal_VOID__VOID,
169 G_TYPE_NONE, 0);
170
171 }
172
173 static void
nemo_progress_info_init(NemoProgressInfo * info)174 nemo_progress_info_init (NemoProgressInfo *info)
175 {
176 NemoProgressInfoManager *manager;
177
178 info->cancellable = g_cancellable_new ();
179
180 manager = nemo_progress_info_manager_new ();
181 nemo_progress_info_manager_add_new_info (manager, info);
182 g_object_unref (manager);
183 }
184
185 NemoProgressInfo *
nemo_progress_info_new(void)186 nemo_progress_info_new (void)
187 {
188 NemoProgressInfo *info;
189
190 info = g_object_new (NEMO_TYPE_PROGRESS_INFO, NULL);
191
192 return info;
193 }
194
195 char *
nemo_progress_info_get_status(NemoProgressInfo * info)196 nemo_progress_info_get_status (NemoProgressInfo *info)
197 {
198 char *res;
199
200 G_LOCK (progress_info);
201
202 if (info->status) {
203 res = g_strdup (info->status);
204 } else {
205 res = g_strdup (_("Preparing"));
206 }
207
208 G_UNLOCK (progress_info);
209
210 return res;
211 }
212
213 char *
nemo_progress_info_get_details(NemoProgressInfo * info)214 nemo_progress_info_get_details (NemoProgressInfo *info)
215 {
216 char *res;
217
218 G_LOCK (progress_info);
219
220 if (info->details) {
221 res = g_strdup (info->details);
222 } else {
223 res = g_strdup (_("Preparing"));
224 }
225
226 G_UNLOCK (progress_info);
227
228 return res;
229 }
230
231 char *
nemo_progress_info_get_initial_details(NemoProgressInfo * info)232 nemo_progress_info_get_initial_details (NemoProgressInfo *info)
233 {
234 char *res;
235
236 G_LOCK (progress_info);
237
238 if (info->initial_details) {
239 res = g_strdup (info->initial_details);
240 } else {
241 res = g_strdup (_("Preparing"));
242 }
243
244 G_UNLOCK (progress_info);
245
246 return res;
247 }
248
249 double
nemo_progress_info_get_progress(NemoProgressInfo * info)250 nemo_progress_info_get_progress (NemoProgressInfo *info)
251 {
252 double res;
253
254 G_LOCK (progress_info);
255
256 if (info->activity_mode) {
257 res = -1.0;
258 } else {
259 res = info->progress;
260 }
261
262 G_UNLOCK (progress_info);
263
264 return res;
265 }
266
267 void
nemo_progress_info_cancel(NemoProgressInfo * info)268 nemo_progress_info_cancel (NemoProgressInfo *info)
269 {
270 G_LOCK (progress_info);
271
272 g_cancellable_cancel (info->cancellable);
273
274 G_UNLOCK (progress_info);
275 }
276
277 GCancellable *
nemo_progress_info_get_cancellable(NemoProgressInfo * info)278 nemo_progress_info_get_cancellable (NemoProgressInfo *info)
279 {
280 GCancellable *c;
281
282 G_LOCK (progress_info);
283
284 c = g_object_ref (info->cancellable);
285
286 G_UNLOCK (progress_info);
287
288 return c;
289 }
290
291 gboolean
nemo_progress_info_get_is_started(NemoProgressInfo * info)292 nemo_progress_info_get_is_started (NemoProgressInfo *info)
293 {
294 gboolean res;
295
296 G_LOCK (progress_info);
297
298 res = info->started;
299
300 G_UNLOCK (progress_info);
301
302 return res;
303 }
304
305 gboolean
nemo_progress_info_get_is_finished(NemoProgressInfo * info)306 nemo_progress_info_get_is_finished (NemoProgressInfo *info)
307 {
308 gboolean res;
309
310 G_LOCK (progress_info);
311
312 res = info->finished;
313
314 G_UNLOCK (progress_info);
315
316 return res;
317 }
318
319 gboolean
nemo_progress_info_get_is_paused(NemoProgressInfo * info)320 nemo_progress_info_get_is_paused (NemoProgressInfo *info)
321 {
322 gboolean res;
323
324 G_LOCK (progress_info);
325
326 res = info->paused;
327
328 G_UNLOCK (progress_info);
329
330 return res;
331 }
332
333 static gboolean
idle_callback(gpointer data)334 idle_callback (gpointer data)
335 {
336 NemoProgressInfo *info = data;
337 gboolean start_at_idle;
338 gboolean finish_at_idle;
339 gboolean changed_at_idle;
340 gboolean progress_at_idle;
341 gboolean queue_at_idle;
342 GSource *source;
343
344 source = g_main_current_source ();
345
346 G_LOCK (progress_info);
347
348 /* Protect agains races where the source has
349 been destroyed on another thread while it
350 was being dispatched.
351 Similar to what gdk_threads_add_idle does.
352 */
353 if (g_source_is_destroyed (source)) {
354 G_UNLOCK (progress_info);
355 return FALSE;
356 }
357
358 /* We hadn't destroyed the source, so take a ref.
359 * This might ressurect the object from dispose, but
360 * that should be ok.
361 */
362 g_object_ref (info);
363
364 g_assert (source == info->idle_source);
365
366 g_source_unref (source);
367 info->idle_source = NULL;
368
369 start_at_idle = info->start_at_idle;
370 finish_at_idle = info->finish_at_idle;
371 changed_at_idle = info->changed_at_idle;
372 progress_at_idle = info->progress_at_idle;
373 queue_at_idle = info->queue_at_idle;
374
375 info->start_at_idle = FALSE;
376 info->finish_at_idle = FALSE;
377 info->changed_at_idle = FALSE;
378 info->progress_at_idle = FALSE;
379 info->queue_at_idle = FALSE;
380
381 G_UNLOCK (progress_info);
382
383 if (queue_at_idle) {
384 g_signal_emit (info,
385 signals[QUEUED],
386 0);
387 }
388
389 if (start_at_idle) {
390 g_signal_emit (info,
391 signals[STARTED],
392 0);
393 }
394
395 if (changed_at_idle) {
396 g_signal_emit (info,
397 signals[CHANGED],
398 0);
399 }
400
401 if (progress_at_idle) {
402 g_signal_emit (info,
403 signals[PROGRESS_CHANGED],
404 0);
405 }
406
407 if (finish_at_idle) {
408 g_signal_emit (info,
409 signals[FINISHED],
410 0);
411 }
412
413 g_object_unref (info);
414
415 return FALSE;
416 }
417
418 /* Called with lock held */
419 static void
queue_idle(NemoProgressInfo * info,gboolean now)420 queue_idle (NemoProgressInfo *info, gboolean now)
421 {
422 if (info->idle_source == NULL ||
423 (now && !info->source_is_now)) {
424 if (info->idle_source) {
425 g_source_destroy (info->idle_source);
426 g_source_unref (info->idle_source);
427 info->idle_source = NULL;
428 }
429
430 info->source_is_now = now;
431 if (now) {
432 info->idle_source = g_idle_source_new ();
433 } else {
434 info->idle_source = g_timeout_source_new (SIGNAL_DELAY_MSEC);
435 }
436 g_source_set_callback (info->idle_source, idle_callback, info, NULL);
437 g_source_attach (info->idle_source, NULL);
438 }
439 }
440
441 void
nemo_progress_info_queue(NemoProgressInfo * info)442 nemo_progress_info_queue (NemoProgressInfo *info)
443 {
444 G_LOCK (progress_info);
445
446 if (!info->queued) {
447 info->queued = TRUE;
448
449 info->queue_at_idle = TRUE;
450 queue_idle (info, TRUE);
451 }
452
453 G_UNLOCK (progress_info);
454 }
455
456 void
nemo_progress_info_pause(NemoProgressInfo * info)457 nemo_progress_info_pause (NemoProgressInfo *info)
458 {
459 G_LOCK (progress_info);
460
461 if (!info->paused) {
462 info->paused = TRUE;
463 }
464
465 G_UNLOCK (progress_info);
466 }
467
468 void
nemo_progress_info_resume(NemoProgressInfo * info)469 nemo_progress_info_resume (NemoProgressInfo *info)
470 {
471 G_LOCK (progress_info);
472
473 if (info->paused) {
474 info->paused = FALSE;
475 }
476
477 G_UNLOCK (progress_info);
478 }
479
480 void
nemo_progress_info_start(NemoProgressInfo * info)481 nemo_progress_info_start (NemoProgressInfo *info)
482 {
483 G_LOCK (progress_info);
484
485 if (!info->started) {
486 info->started = TRUE;
487
488 info->start_at_idle = TRUE;
489 queue_idle (info, TRUE);
490 }
491
492 G_UNLOCK (progress_info);
493 }
494
495 void
nemo_progress_info_finish(NemoProgressInfo * info)496 nemo_progress_info_finish (NemoProgressInfo *info)
497 {
498 G_LOCK (progress_info);
499
500 if (!info->finished) {
501 info->finished = TRUE;
502
503 info->finish_at_idle = TRUE;
504 queue_idle (info, TRUE);
505 }
506
507 G_UNLOCK (progress_info);
508 }
509
510 void
nemo_progress_info_take_status(NemoProgressInfo * info,char * status)511 nemo_progress_info_take_status (NemoProgressInfo *info,
512 char *status)
513 {
514 G_LOCK (progress_info);
515
516 if (g_strcmp0 (info->status, status) != 0) {
517 g_free (info->status);
518 info->status = status;
519
520 info->changed_at_idle = TRUE;
521 queue_idle (info, FALSE);
522 } else {
523 g_free (status);
524 }
525
526 G_UNLOCK (progress_info);
527 }
528
529 void
nemo_progress_info_set_status(NemoProgressInfo * info,const char * status)530 nemo_progress_info_set_status (NemoProgressInfo *info,
531 const char *status)
532 {
533 G_LOCK (progress_info);
534
535 if (g_strcmp0 (info->status, status) != 0) {
536 g_free (info->status);
537 info->status = g_strdup (status);
538
539 info->changed_at_idle = TRUE;
540 queue_idle (info, FALSE);
541 }
542
543 G_UNLOCK (progress_info);
544 }
545
546
547 void
nemo_progress_info_take_details(NemoProgressInfo * info,char * details)548 nemo_progress_info_take_details (NemoProgressInfo *info,
549 char *details)
550 {
551 G_LOCK (progress_info);
552
553 if (g_strcmp0 (info->details, details) != 0) {
554 g_free (info->details);
555 info->details = details;
556
557 info->changed_at_idle = TRUE;
558 queue_idle (info, FALSE);
559 } else {
560 g_free (details);
561 }
562
563 G_UNLOCK (progress_info);
564 }
565
566 void
nemo_progress_info_set_details(NemoProgressInfo * info,const char * details)567 nemo_progress_info_set_details (NemoProgressInfo *info,
568 const char *details)
569 {
570 G_LOCK (progress_info);
571
572 if (g_strcmp0 (info->details, details) != 0) {
573 g_free (info->details);
574 info->details = g_strdup (details);
575
576 info->changed_at_idle = TRUE;
577 queue_idle (info, FALSE);
578 }
579
580 G_UNLOCK (progress_info);
581 }
582
583 void
nemo_progress_info_take_initial_details(NemoProgressInfo * info,char * initial_details)584 nemo_progress_info_take_initial_details (NemoProgressInfo *info,
585 char *initial_details)
586 {
587 G_LOCK (progress_info);
588
589 if (g_strcmp0 (info->initial_details, initial_details) != 0) {
590 g_free (info->initial_details);
591 info->initial_details = initial_details;
592
593 info->changed_at_idle = TRUE;
594 queue_idle (info, FALSE);
595 } else {
596 g_free (initial_details);
597 }
598
599 G_UNLOCK (progress_info);
600 }
601
602 void
nemo_progress_info_pulse_progress(NemoProgressInfo * info)603 nemo_progress_info_pulse_progress (NemoProgressInfo *info)
604 {
605 G_LOCK (progress_info);
606
607 info->activity_mode = TRUE;
608 info->progress = 0.0;
609 info->progress_at_idle = TRUE;
610 queue_idle (info, FALSE);
611
612 G_UNLOCK (progress_info);
613 }
614
615 void
nemo_progress_info_set_progress(NemoProgressInfo * info,double current,double total)616 nemo_progress_info_set_progress (NemoProgressInfo *info,
617 double current,
618 double total)
619 {
620 double current_percent;
621
622 if (total <= 0) {
623 current_percent = 1.0;
624 } else {
625 current_percent = current / total;
626
627 if (current_percent < 0) {
628 current_percent = 0;
629 }
630
631 if (current_percent > 1.0) {
632 current_percent = 1.0;
633 }
634 }
635
636 G_LOCK (progress_info);
637
638 if (info->activity_mode || /* emit on switch from activity mode */
639 fabs (current_percent - info->progress) > 0.005 /* Emit on change of 0.5 percent */
640 ) {
641 info->activity_mode = FALSE;
642 info->progress = current_percent;
643 info->progress_at_idle = TRUE;
644 queue_idle (info, FALSE);
645 }
646
647 G_UNLOCK (progress_info);
648 }
649