/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
nautilus-progress-info.h: file operation progress info.
Copyright (C) 2007 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with this program; if not, see .
Author: Alexander Larsson
*/
#include
#include
#include
#include
#include
#include "nautilus-progress-info.h"
#include "nautilus-progress-info-manager.h"
#include "nautilus-icon-info.h"
enum {
CHANGED,
PROGRESS_CHANGED,
STARTED,
FINISHED,
CANCELLED,
LAST_SIGNAL
};
#define SIGNAL_DELAY_MSEC 100
static guint signals[LAST_SIGNAL] = { 0 };
struct _NautilusProgressInfo
{
GObject parent_instance;
GCancellable *cancellable;
guint cancellable_id;
GCancellable *details_in_thread_cancellable;
GTimer *progress_timer;
char *status;
char *details;
double progress;
gdouble remaining_time;
gdouble elapsed_time;
gboolean activity_mode;
gboolean started;
gboolean finished;
gboolean paused;
GSource *idle_source;
gboolean source_is_now;
gboolean start_at_idle;
gboolean finish_at_idle;
gboolean cancel_at_idle;
gboolean changed_at_idle;
gboolean progress_at_idle;
GFile *destination;
};
struct _NautilusProgressInfoClass
{
GObjectClass parent_class;
};
G_LOCK_DEFINE_STATIC(progress_info);
G_DEFINE_TYPE (NautilusProgressInfo, nautilus_progress_info, G_TYPE_OBJECT)
static void
nautilus_progress_info_finalize (GObject *object)
{
NautilusProgressInfo *info;
info = NAUTILUS_PROGRESS_INFO (object);
g_free (info->status);
g_free (info->details);
g_clear_object (&info->progress_timer);
g_cancellable_disconnect (info->cancellable, info->cancellable_id);
g_object_unref (info->cancellable);
g_cancellable_cancel (info->details_in_thread_cancellable);
g_clear_object (&info->details_in_thread_cancellable);
g_clear_object (&info->destination);
if (G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) {
(*G_OBJECT_CLASS (nautilus_progress_info_parent_class)->finalize) (object);
}
}
static void
nautilus_progress_info_dispose (GObject *object)
{
NautilusProgressInfo *info;
info = NAUTILUS_PROGRESS_INFO (object);
G_LOCK (progress_info);
/* Destroy source in dispose, because the callback
could come here before the destroy, which should
ressurect the object for a while */
if (info->idle_source) {
g_source_destroy (info->idle_source);
g_source_unref (info->idle_source);
info->idle_source = NULL;
}
G_UNLOCK (progress_info);
}
static void
nautilus_progress_info_class_init (NautilusProgressInfoClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = nautilus_progress_info_finalize;
gobject_class->dispose = nautilus_progress_info_dispose;
signals[CHANGED] =
g_signal_new ("changed",
NAUTILUS_TYPE_PROGRESS_INFO,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[PROGRESS_CHANGED] =
g_signal_new ("progress-changed",
NAUTILUS_TYPE_PROGRESS_INFO,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[STARTED] =
g_signal_new ("started",
NAUTILUS_TYPE_PROGRESS_INFO,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[FINISHED] =
g_signal_new ("finished",
NAUTILUS_TYPE_PROGRESS_INFO,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[CANCELLED] =
g_signal_new ("cancelled",
NAUTILUS_TYPE_PROGRESS_INFO,
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static gboolean
idle_callback (gpointer data)
{
NautilusProgressInfo *info = data;
gboolean start_at_idle;
gboolean finish_at_idle;
gboolean changed_at_idle;
gboolean progress_at_idle;
gboolean cancelled_at_idle;
GSource *source;
source = g_main_current_source ();
G_LOCK (progress_info);
/* Protect agains races where the source has
been destroyed on another thread while it
was being dispatched.
Similar to what gdk_threads_add_idle does.
*/
if (g_source_is_destroyed (source)) {
G_UNLOCK (progress_info);
return FALSE;
}
/* We hadn't destroyed the source, so take a ref.
* This might ressurect the object from dispose, but
* that should be ok.
*/
g_object_ref (info);
g_assert (source == info->idle_source);
g_source_unref (source);
info->idle_source = NULL;
start_at_idle = info->start_at_idle;
finish_at_idle = info->finish_at_idle;
changed_at_idle = info->changed_at_idle;
progress_at_idle = info->progress_at_idle;
cancelled_at_idle = info->cancel_at_idle;
info->start_at_idle = FALSE;
info->finish_at_idle = FALSE;
info->changed_at_idle = FALSE;
info->progress_at_idle = FALSE;
info->cancel_at_idle = FALSE;
G_UNLOCK (progress_info);
if (start_at_idle) {
g_signal_emit (info,
signals[STARTED],
0);
}
if (changed_at_idle) {
g_signal_emit (info,
signals[CHANGED],
0);
}
if (progress_at_idle) {
g_signal_emit (info,
signals[PROGRESS_CHANGED],
0);
}
if (finish_at_idle) {
g_signal_emit (info,
signals[FINISHED],
0);
}
if (cancelled_at_idle) {
g_signal_emit (info,
signals[CANCELLED],
0);
}
g_object_unref (info);
return FALSE;
}
/* Called with lock held */
static void
queue_idle (NautilusProgressInfo *info, gboolean now)
{
if (info->idle_source == NULL ||
(now && !info->source_is_now)) {
if (info->idle_source) {
g_source_destroy (info->idle_source);
g_source_unref (info->idle_source);
info->idle_source = NULL;
}
info->source_is_now = now;
if (now) {
info->idle_source = g_idle_source_new ();
} else {
info->idle_source = g_timeout_source_new (SIGNAL_DELAY_MSEC);
}
g_source_set_callback (info->idle_source, idle_callback, info, NULL);
g_source_attach (info->idle_source, NULL);
}
}
static void
set_details_in_thread (GTask *task,
NautilusProgressInfo *info,
gpointer user_data,
GCancellable *cancellable)
{
if (!g_cancellable_is_cancelled (cancellable)) {
nautilus_progress_info_set_details (info, _("Cancelled"));
G_LOCK (progress_info);
info->cancel_at_idle = TRUE;
g_timer_stop (info->progress_timer);
queue_idle (info, TRUE);
G_UNLOCK (progress_info);
}
}
static void
on_canceled (GCancellable *cancellable,
NautilusProgressInfo *info)
{
GTask *task;
/* We can't do any lock operaton here, since this is probably the main
* thread, so modify the details in another thread. Also it can happens
* that we were finalizing the object, so create a new cancellable here
* so it can be cancelled in finalize */
info->details_in_thread_cancellable = g_cancellable_new ();
task = g_task_new (info, info->details_in_thread_cancellable, NULL, NULL);
g_task_run_in_thread (task, (GTaskThreadFunc) set_details_in_thread);
g_object_unref (task);
}
static void
nautilus_progress_info_init (NautilusProgressInfo *info)
{
NautilusProgressInfoManager *manager;
info->cancellable = g_cancellable_new ();
info->cancellable_id = g_cancellable_connect (info->cancellable,
G_CALLBACK (on_canceled),
info,
NULL);
manager = nautilus_progress_info_manager_dup_singleton ();
nautilus_progress_info_manager_add_new_info (manager, info);
g_object_unref (manager);
info->progress_timer = g_timer_new ();
}
NautilusProgressInfo *
nautilus_progress_info_new (void)
{
NautilusProgressInfo *info;
info = g_object_new (NAUTILUS_TYPE_PROGRESS_INFO, NULL);
return info;
}
char *
nautilus_progress_info_get_status (NautilusProgressInfo *info)
{
char *res;
G_LOCK (progress_info);
if (info->status) {
res = g_strdup (info->status);
} else {
res = g_strdup (_("Preparing"));
}
G_UNLOCK (progress_info);
return res;
}
char *
nautilus_progress_info_get_details (NautilusProgressInfo *info)
{
char *res;
G_LOCK (progress_info);
if (info->details) {
res = g_strdup (info->details);
} else {
res = g_strdup (_("Preparing"));
}
G_UNLOCK (progress_info);
return res;
}
double
nautilus_progress_info_get_progress (NautilusProgressInfo *info)
{
double res;
G_LOCK (progress_info);
if (info->activity_mode) {
res = -1.0;
} else {
res = info->progress;
}
G_UNLOCK (progress_info);
return res;
}
void
nautilus_progress_info_cancel (NautilusProgressInfo *info)
{
G_LOCK (progress_info);
g_cancellable_cancel (info->cancellable);
g_timer_stop (info->progress_timer);
G_UNLOCK (progress_info);
}
GCancellable *
nautilus_progress_info_get_cancellable (NautilusProgressInfo *info)
{
GCancellable *c;
G_LOCK (progress_info);
c = g_object_ref (info->cancellable);
G_UNLOCK (progress_info);
return c;
}
gboolean
nautilus_progress_info_get_is_cancelled (NautilusProgressInfo *info)
{
gboolean cancelled;
G_LOCK (progress_info);
cancelled = g_cancellable_is_cancelled (info->cancellable);
G_UNLOCK (progress_info);
return cancelled;
}
gboolean
nautilus_progress_info_get_is_started (NautilusProgressInfo *info)
{
gboolean res;
G_LOCK (progress_info);
res = info->started;
G_UNLOCK (progress_info);
return res;
}
gboolean
nautilus_progress_info_get_is_finished (NautilusProgressInfo *info)
{
gboolean res;
G_LOCK (progress_info);
res = info->finished;
G_UNLOCK (progress_info);
return res;
}
gboolean
nautilus_progress_info_get_is_paused (NautilusProgressInfo *info)
{
gboolean res;
G_LOCK (progress_info);
res = info->paused;
G_UNLOCK (progress_info);
return res;
}
void
nautilus_progress_info_pause (NautilusProgressInfo *info)
{
G_LOCK (progress_info);
if (!info->paused) {
info->paused = TRUE;
g_timer_stop (info->progress_timer);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_resume (NautilusProgressInfo *info)
{
G_LOCK (progress_info);
if (info->paused) {
info->paused = FALSE;
g_timer_continue (info->progress_timer);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_start (NautilusProgressInfo *info)
{
G_LOCK (progress_info);
if (!info->started) {
info->started = TRUE;
g_timer_start (info->progress_timer);
info->start_at_idle = TRUE;
queue_idle (info, TRUE);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_finish (NautilusProgressInfo *info)
{
G_LOCK (progress_info);
if (!info->finished) {
info->finished = TRUE;
g_timer_stop (info->progress_timer);
info->finish_at_idle = TRUE;
queue_idle (info, TRUE);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_take_status (NautilusProgressInfo *info,
char *status)
{
G_LOCK (progress_info);
if (g_strcmp0 (info->status, status) != 0) {
g_free (info->status);
info->status = status;
info->changed_at_idle = TRUE;
queue_idle (info, FALSE);
} else {
g_free (status);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_set_status (NautilusProgressInfo *info,
const char *status)
{
G_LOCK (progress_info);
if (g_strcmp0 (info->status, status) != 0) {
g_free (info->status);
info->status = g_strdup (status);
info->changed_at_idle = TRUE;
queue_idle (info, FALSE);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_take_details (NautilusProgressInfo *info,
char *details)
{
G_LOCK (progress_info);
if (g_strcmp0 (info->details, details) != 0) {
g_free (info->details);
info->details = details;
info->changed_at_idle = TRUE;
queue_idle (info, FALSE);
} else {
g_free (details);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_set_details (NautilusProgressInfo *info,
const char *details)
{
G_LOCK (progress_info);
if (g_strcmp0 (info->details, details) != 0) {
g_free (info->details);
info->details = g_strdup (details);
info->changed_at_idle = TRUE;
queue_idle (info, FALSE);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_pulse_progress (NautilusProgressInfo *info)
{
G_LOCK (progress_info);
info->activity_mode = TRUE;
info->progress = 0.0;
info->progress_at_idle = TRUE;
queue_idle (info, FALSE);
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_set_progress (NautilusProgressInfo *info,
double current,
double total)
{
double current_percent;
if (total <= 0) {
current_percent = 1.0;
} else {
current_percent = current / total;
if (current_percent < 0) {
current_percent = 0;
}
if (current_percent > 1.0) {
current_percent = 1.0;
}
}
G_LOCK (progress_info);
if (info->activity_mode || /* emit on switch from activity mode */
fabs (current_percent - info->progress) > 0.005 /* Emit on change of 0.5 percent */
) {
info->activity_mode = FALSE;
info->progress = current_percent;
info->progress_at_idle = TRUE;
queue_idle (info, FALSE);
}
G_UNLOCK (progress_info);
}
void
nautilus_progress_info_set_remaining_time (NautilusProgressInfo *info,
gdouble time)
{
G_LOCK (progress_info);
info->remaining_time = time;
G_UNLOCK (progress_info);
}
gdouble
nautilus_progress_info_get_remaining_time (NautilusProgressInfo *info)
{
gint remaining_time;
G_LOCK (progress_info);
remaining_time = info->remaining_time;
G_UNLOCK (progress_info);
return remaining_time;
}
void
nautilus_progress_info_set_elapsed_time (NautilusProgressInfo *info,
gdouble time)
{
G_LOCK (progress_info);
info->elapsed_time = time;
G_UNLOCK (progress_info);
}
gdouble
nautilus_progress_info_get_elapsed_time (NautilusProgressInfo *info)
{
gint elapsed_time;
G_LOCK (progress_info);
elapsed_time = info->elapsed_time;
G_UNLOCK (progress_info);
return elapsed_time;
}
gdouble
nautilus_progress_info_get_total_elapsed_time (NautilusProgressInfo *info)
{
gdouble elapsed_time;
G_LOCK (progress_info);
elapsed_time = g_timer_elapsed (info->progress_timer, NULL);
G_UNLOCK (progress_info);
return elapsed_time;
}
void
nautilus_progress_info_set_destination (NautilusProgressInfo *info,
GFile *file)
{
G_LOCK (progress_info);
g_clear_object (&info->destination);
info->destination = g_object_ref (file);
G_UNLOCK (progress_info);
}
GFile *
nautilus_progress_info_get_destination (NautilusProgressInfo *info)
{
GFile *destination;
G_LOCK (progress_info);
destination = g_object_ref (info->destination);
G_UNLOCK (progress_info);
return destination;
}