nautilus-3.6.3/libnautilus-private/nautilus-progress-info.c

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 }