No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 nautilus-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., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, 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 "nautilus-progress-info.h"
31 #include "nautilus-progress-info-manager.h"
32 #include "nautilus-icon-info.h"
33
34 enum {
35 CHANGED,
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 _NautilusProgressInfo
47 {
48 GObject parent_instance;
49
50 GCancellable *cancellable;
51
52 char *status;
53 char *details;
54 double progress;
55 gboolean activity_mode;
56 gboolean started;
57 gboolean finished;
58 gboolean paused;
59
60 GSource *idle_source;
61 gboolean source_is_now;
62
63 gboolean start_at_idle;
64 gboolean finish_at_idle;
65 gboolean changed_at_idle;
66 gboolean progress_at_idle;
67 };
68
69 struct _NautilusProgressInfoClass
70 {
71 GObjectClass parent_class;
72 };
73
74 G_LOCK_DEFINE_STATIC(progress_info);
75
76 G_DEFINE_TYPE (NautilusProgressInfo, nautilus_progress_info, G_TYPE_OBJECT)
77
78 static void
79 nautilus_progress_info_finalize (GObject *object)
80 {
81 NautilusProgressInfo *info;
82
83 info = NAUTILUS_PROGRESS_INFO (object);
84
85 g_free (info->status);
86 g_free (info->details);
87 g_object_unref (info->cancellable);
88
89 if (G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) {
90 (*G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) (object);
91 }
92 }
93
94 static void
95 nautilus_progress_info_dispose (GObject *object)
96 {
97 NautilusProgressInfo *info;
98
99 info = NAUTILUS_PROGRESS_INFO (object);
100
101 G_LOCK (progress_info);
102
103 /* Destroy source in dispose, because the callback
104 could come here before the destroy, which should
105 ressurect the object for a while */
106 if (info->idle_source) {
107 g_source_destroy (info->idle_source);
108 g_source_unref (info->idle_source);
109 info->idle_source = NULL;
110 }
111 G_UNLOCK (progress_info);
112 }
113
114 static void
115 nautilus_progress_info_class_init (NautilusProgressInfoClass *klass)
116 {
117 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
118
119 gobject_class->finalize = nautilus_progress_info_finalize;
120 gobject_class->dispose = nautilus_progress_info_dispose;
121
122 signals[CHANGED] =
123 g_signal_new ("changed",
124 NAUTILUS_TYPE_PROGRESS_INFO,
125 G_SIGNAL_RUN_LAST,
126 0,
127 NULL, NULL,
128 g_cclosure_marshal_VOID__VOID,
129 G_TYPE_NONE, 0);
130
131 signals[PROGRESS_CHANGED] =
132 g_signal_new ("progress-changed",
133 NAUTILUS_TYPE_PROGRESS_INFO,
134 G_SIGNAL_RUN_LAST,
135 0,
136 NULL, NULL,
137 g_cclosure_marshal_VOID__VOID,
138 G_TYPE_NONE, 0);
139
140 signals[STARTED] =
141 g_signal_new ("started",
142 NAUTILUS_TYPE_PROGRESS_INFO,
143 G_SIGNAL_RUN_LAST,
144 0,
145 NULL, NULL,
146 g_cclosure_marshal_VOID__VOID,
147 G_TYPE_NONE, 0);
148
149 signals[FINISHED] =
150 g_signal_new ("finished",
151 NAUTILUS_TYPE_PROGRESS_INFO,
152 G_SIGNAL_RUN_LAST,
153 0,
154 NULL, NULL,
155 g_cclosure_marshal_VOID__VOID,
156 G_TYPE_NONE, 0);
157
158 }
159
160 static void
161 nautilus_progress_info_init (NautilusProgressInfo *info)
162 {
163 NautilusProgressInfoManager *manager;
164
165 info->cancellable = g_cancellable_new ();
166
167 manager = nautilus_progress_info_manager_new ();
168 nautilus_progress_info_manager_add_new_info (manager, info);
169 g_object_unref (manager);
170 }
171
172 NautilusProgressInfo *
173 nautilus_progress_info_new (void)
174 {
175 NautilusProgressInfo *info;
176
177 info = g_object_new (NAUTILUS_TYPE_PROGRESS_INFO, NULL);
178
179 return info;
180 }
181
182 char *
183 nautilus_progress_info_get_status (NautilusProgressInfo *info)
184 {
185 char *res;
186
187 G_LOCK (progress_info);
188
189 if (info->status) {
190 res = g_strdup (info->status);
191 } else {
192 res = g_strdup (_("Preparing"));
193 }
194
195 G_UNLOCK (progress_info);
196
197 return res;
198 }
199
200 char *
201 nautilus_progress_info_get_details (NautilusProgressInfo *info)
202 {
203 char *res;
204
205 G_LOCK (progress_info);
206
207 if (info->details) {
208 res = g_strdup (info->details);
209 } else {
210 res = g_strdup (_("Preparing"));
211 }
212
213 G_UNLOCK (progress_info);
214
215 return res;
216 }
217
218 double
219 nautilus_progress_info_get_progress (NautilusProgressInfo *info)
220 {
221 double res;
222
223 G_LOCK (progress_info);
224
225 if (info->activity_mode) {
226 res = -1.0;
227 } else {
228 res = info->progress;
229 }
230
231 G_UNLOCK (progress_info);
232
233 return res;
234 }
235
236 void
237 nautilus_progress_info_cancel (NautilusProgressInfo *info)
238 {
239 G_LOCK (progress_info);
240
241 g_cancellable_cancel (info->cancellable);
242
243 G_UNLOCK (progress_info);
244 }
245
246 GCancellable *
247 nautilus_progress_info_get_cancellable (NautilusProgressInfo *info)
248 {
249 GCancellable *c;
250
251 G_LOCK (progress_info);
252
253 c = g_object_ref (info->cancellable);
254
255 G_UNLOCK (progress_info);
256
257 return c;
258 }
259
260 gboolean
261 nautilus_progress_info_get_is_started (NautilusProgressInfo *info)
262 {
263 gboolean res;
264
265 G_LOCK (progress_info);
266
267 res = info->started;
268
269 G_UNLOCK (progress_info);
270
271 return res;
272 }
273
274 gboolean
275 nautilus_progress_info_get_is_finished (NautilusProgressInfo *info)
276 {
277 gboolean res;
278
279 G_LOCK (progress_info);
280
281 res = info->finished;
282
283 G_UNLOCK (progress_info);
284
285 return res;
286 }
287
288 gboolean
289 nautilus_progress_info_get_is_paused (NautilusProgressInfo *info)
290 {
291 gboolean res;
292
293 G_LOCK (progress_info);
294
295 res = info->paused;
296
297 G_UNLOCK (progress_info);
298
299 return res;
300 }
301
302 static gboolean
303 idle_callback (gpointer data)
304 {
305 NautilusProgressInfo *info = data;
306 gboolean start_at_idle;
307 gboolean finish_at_idle;
308 gboolean changed_at_idle;
309 gboolean progress_at_idle;
310 GSource *source;
311
312 source = g_main_current_source ();
313
314 G_LOCK (progress_info);
315
316 /* Protect agains races where the source has
317 been destroyed on another thread while it
318 was being dispatched.
319 Similar to what gdk_threads_add_idle does.
320 */
321 if (g_source_is_destroyed (source)) {
322 G_UNLOCK (progress_info);
323 return FALSE;
324 }
325
326 /* We hadn't destroyed the source, so take a ref.
327 * This might ressurect the object from dispose, but
328 * that should be ok.
329 */
330 g_object_ref (info);
331
332 g_assert (source == info->idle_source);
333
334 g_source_unref (source);
335 info->idle_source = NULL;
336
337 start_at_idle = info->start_at_idle;
338 finish_at_idle = info->finish_at_idle;
339 changed_at_idle = info->changed_at_idle;
340 progress_at_idle = info->progress_at_idle;
341
342 info->start_at_idle = FALSE;
343 info->finish_at_idle = FALSE;
344 info->changed_at_idle = FALSE;
345 info->progress_at_idle = FALSE;
346
347 G_UNLOCK (progress_info);
348
349 if (start_at_idle) {
350 g_signal_emit (info,
351 signals[STARTED],
352 0);
353 }
354
355 if (changed_at_idle) {
356 g_signal_emit (info,
357 signals[CHANGED],
358 0);
359 }
360
361 if (progress_at_idle) {
362 g_signal_emit (info,
363 signals[PROGRESS_CHANGED],
364 0);
365 }
366
367 if (finish_at_idle) {
368 g_signal_emit (info,
369 signals[FINISHED],
370 0);
371 }
372
373 g_object_unref (info);
374
375 return FALSE;
376 }
377
378 /* Called with lock held */
379 static void
380 queue_idle (NautilusProgressInfo *info, gboolean now)
381 {
382 if (info->idle_source == NULL ||
383 (now && !info->source_is_now)) {
384 if (info->idle_source) {
385 g_source_destroy (info->idle_source);
386 g_source_unref (info->idle_source);
387 info->idle_source = NULL;
388 }
389
390 info->source_is_now = now;
391 if (now) {
392 info->idle_source = g_idle_source_new ();
393 } else {
394 info->idle_source = g_timeout_source_new (SIGNAL_DELAY_MSEC);
395 }
396 g_source_set_callback (info->idle_source, idle_callback, info, NULL);
397 g_source_attach (info->idle_source, NULL);
398 }
399 }
400
401 void
402 nautilus_progress_info_pause (NautilusProgressInfo *info)
403 {
404 G_LOCK (progress_info);
405
406 if (!info->paused) {
407 info->paused = TRUE;
408 }
409
410 G_UNLOCK (progress_info);
411 }
412
413 void
414 nautilus_progress_info_resume (NautilusProgressInfo *info)
415 {
416 G_LOCK (progress_info);
417
418 if (info->paused) {
419 info->paused = FALSE;
420 }
421
422 G_UNLOCK (progress_info);
423 }
424
425 void
426 nautilus_progress_info_start (NautilusProgressInfo *info)
427 {
428 G_LOCK (progress_info);
429
430 if (!info->started) {
431 info->started = TRUE;
432
433 info->start_at_idle = TRUE;
434 queue_idle (info, TRUE);
435 }
436
437 G_UNLOCK (progress_info);
438 }
439
440 void
441 nautilus_progress_info_finish (NautilusProgressInfo *info)
442 {
443 G_LOCK (progress_info);
444
445 if (!info->finished) {
446 info->finished = TRUE;
447
448 info->finish_at_idle = TRUE;
449 queue_idle (info, TRUE);
450 }
451
452 G_UNLOCK (progress_info);
453 }
454
455 void
456 nautilus_progress_info_take_status (NautilusProgressInfo *info,
457 char *status)
458 {
459 G_LOCK (progress_info);
460
461 if (g_strcmp0 (info->status, status) != 0) {
462 g_free (info->status);
463 info->status = status;
464
465 info->changed_at_idle = TRUE;
466 queue_idle (info, FALSE);
467 } else {
468 g_free (status);
469 }
470
471 G_UNLOCK (progress_info);
472 }
473
474 void
475 nautilus_progress_info_set_status (NautilusProgressInfo *info,
476 const char *status)
477 {
478 G_LOCK (progress_info);
479
480 if (g_strcmp0 (info->status, status) != 0) {
481 g_free (info->status);
482 info->status = g_strdup (status);
483
484 info->changed_at_idle = TRUE;
485 queue_idle (info, FALSE);
486 }
487
488 G_UNLOCK (progress_info);
489 }
490
491
492 void
493 nautilus_progress_info_take_details (NautilusProgressInfo *info,
494 char *details)
495 {
496 G_LOCK (progress_info);
497
498 if (g_strcmp0 (info->details, details) != 0) {
499 g_free (info->details);
500 info->details = details;
501
502 info->changed_at_idle = TRUE;
503 queue_idle (info, FALSE);
504 } else {
505 g_free (details);
506 }
507
508 G_UNLOCK (progress_info);
509 }
510
511 void
512 nautilus_progress_info_set_details (NautilusProgressInfo *info,
513 const char *details)
514 {
515 G_LOCK (progress_info);
516
517 if (g_strcmp0 (info->details, details) != 0) {
518 g_free (info->details);
519 info->details = g_strdup (details);
520
521 info->changed_at_idle = TRUE;
522 queue_idle (info, FALSE);
523 }
524
525 G_UNLOCK (progress_info);
526 }
527
528 void
529 nautilus_progress_info_pulse_progress (NautilusProgressInfo *info)
530 {
531 G_LOCK (progress_info);
532
533 info->activity_mode = TRUE;
534 info->progress = 0.0;
535 info->progress_at_idle = TRUE;
536 queue_idle (info, FALSE);
537
538 G_UNLOCK (progress_info);
539 }
540
541 void
542 nautilus_progress_info_set_progress (NautilusProgressInfo *info,
543 double current,
544 double total)
545 {
546 double current_percent;
547
548 if (total <= 0) {
549 current_percent = 1.0;
550 } else {
551 current_percent = current / total;
552
553 if (current_percent < 0) {
554 current_percent = 0;
555 }
556
557 if (current_percent > 1.0) {
558 current_percent = 1.0;
559 }
560 }
561
562 G_LOCK (progress_info);
563
564 if (info->activity_mode || /* emit on switch from activity mode */
565 fabs (current_percent - info->progress) > 0.005 /* Emit on change of 0.5 percent */
566 ) {
567 info->activity_mode = FALSE;
568 info->progress = current_percent;
569 info->progress_at_idle = TRUE;
570 queue_idle (info, FALSE);
571 }
572
573 G_UNLOCK (progress_info);
574 }