*
* Copyright 2025 Adam Masciola
*
* 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 3 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 <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "config.h"
#include <glib/gi18n.h>
#include "bz-transaction.h"
typedef struct
{
GListStore *installs;
GListStore *updates;
GListStore *removals;
char *name;
gboolean pending;
GListStore *current_tasks;
GListStore *finished_tasks;
GListStore *trackers;
char *status;
double progress;
gboolean finished;
gboolean success;
char *error;
} BzTransactionPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (BzTransaction, bz_transaction, G_TYPE_OBJECT)
enum
{
PROP_0,
PROP_NAME,
PROP_INSTALLS,
PROP_UPDATES,
PROP_REMOVALS,
PROP_PENDING,
PROP_CURRENT_OPS,
PROP_FINISHED_OPS,
PROP_TRACKERS,
PROP_STATUS,
PROP_PROGRESS,
PROP_FINISHED,
PROP_SUCCESS,
PROP_ERROR,
LAST_PROP
};
static GParamSpec *props[LAST_PROP] = { 0 };
static void
finish (BzTransactionPrivate *priv);
static gboolean
find_payload_eq_func (BzTransactionTask *task,
gpointer none,
BzBackendTransactionOpPayload *payload);
static gboolean
find_entry_eq_func (BzTransactionEntryTracker *tracker,
gpointer none,
BzEntry *entry);
static gboolean
find_and_maybe_transfer (GListStore *from,
GListStore *to,
gpointer lookup,
GEqualFuncFull eql,
gpointer *out);
static void
tracker_update (BzTransactionPrivate *priv,
BzBackendTransactionOpPayload *payload,
BzBackendTransactionOpProgressPayload *progress_payload,
gboolean transfer,
gboolean set_done);
static void
bz_transaction_dispose (GObject *object)
{
BzTransaction *self = BZ_TRANSACTION (object);
BzTransactionPrivate *priv = bz_transaction_get_instance_private (self);
g_clear_pointer (&priv->name, g_free);
g_clear_object (&priv->installs);
g_clear_object (&priv->updates);
g_clear_object (&priv->removals);
g_clear_object (&priv->current_tasks);
g_clear_object (&priv->finished_tasks);
g_clear_object (&priv->trackers);
g_clear_pointer (&priv->status, g_free);
g_clear_pointer (&priv->error, g_free);
G_OBJECT_CLASS (bz_transaction_parent_class)->dispose (object);
}
static void
bz_transaction_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
BzTransaction *self = BZ_TRANSACTION (object);
BzTransactionPrivate *priv = bz_transaction_get_instance_private (self);
switch (prop_id)
{
case PROP_NAME:
g_value_set_string (value, priv->name);
break;
case PROP_INSTALLS:
g_value_set_object (value, priv->installs);
break;
case PROP_UPDATES:
g_value_set_object (value, priv->updates);
break;
case PROP_REMOVALS:
g_value_set_object (value, priv->removals);
break;
case PROP_PENDING:
g_value_set_boolean (value, priv->pending);
break;
case PROP_CURRENT_OPS:
g_value_set_object (value, priv->current_tasks);
break;
case PROP_FINISHED_OPS:
g_value_set_object (value, priv->finished_tasks);
break;
case PROP_TRACKERS:
g_value_set_object (value, priv->trackers);
break;
case PROP_STATUS:
g_value_set_string (value, priv->status);
break;
case PROP_PROGRESS:
g_value_set_double (value, priv->progress);
break;
case PROP_FINISHED:
g_value_set_boolean (value, priv->finished);
break;
case PROP_SUCCESS:
g_value_set_boolean (value, priv->success);
break;
case PROP_ERROR:
g_value_set_string (value, priv->error);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bz_transaction_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
BzTransaction *self = BZ_TRANSACTION (object);
BzTransactionPrivate *priv = bz_transaction_get_instance_private (self);
switch (prop_id)
{
case PROP_NAME:
g_clear_pointer (&priv->name, g_free);
priv->name = g_value_dup_string (value);
break;
case PROP_INSTALLS:
g_clear_object (&priv->installs);
priv->installs = g_value_dup_object (value);
break;
case PROP_UPDATES:
g_clear_object (&priv->updates);
priv->updates = g_value_dup_object (value);
break;
case PROP_REMOVALS:
g_clear_object (&priv->removals);
priv->removals = g_value_dup_object (value);
break;
case PROP_PENDING:
priv->pending = g_value_get_boolean (value);
break;
case PROP_STATUS:
g_clear_pointer (&priv->status, g_free);
priv->status = g_value_dup_string (value);
break;
case PROP_PROGRESS:
priv->progress = g_value_get_double (value);
break;
case PROP_FINISHED:
priv->finished = g_value_get_boolean (value);
if (priv->finished)
finish (priv);
break;
case PROP_SUCCESS:
priv->success = g_value_get_boolean (value);
break;
case PROP_ERROR:
g_clear_pointer (&priv->error, g_free);
priv->error = g_value_dup_string (value);
break;
case PROP_CURRENT_OPS:
case PROP_FINISHED_OPS:
case PROP_TRACKERS:
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
bz_transaction_class_init (BzTransactionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = bz_transaction_set_property;
object_class->get_property = bz_transaction_get_property;
object_class->dispose = bz_transaction_dispose;
props[PROP_NAME] =
g_param_spec_string (
"name",
NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_INSTALLS] =
g_param_spec_object (
"installs",
NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_UPDATES] =
g_param_spec_object (
"updates",
NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_REMOVALS] =
g_param_spec_object (
"removals",
NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_PENDING] =
g_param_spec_boolean (
"pending",
NULL, NULL, TRUE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_CURRENT_OPS] =
g_param_spec_object (
"current-ops",
NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
props[PROP_FINISHED_OPS] =
g_param_spec_object (
"finished-ops",
NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
props[PROP_TRACKERS] =
g_param_spec_object (
"trackers",
NULL, NULL,
G_TYPE_LIST_MODEL,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
props[PROP_STATUS] =
g_param_spec_string (
"status",
NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_PROGRESS] =
g_param_spec_double (
"progress",
NULL, NULL,
0.0, 1.0, 0.0,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_FINISHED] =
g_param_spec_boolean (
"finished",
NULL, NULL, FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_SUCCESS] =
g_param_spec_boolean (
"success",
NULL, NULL, FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
props[PROP_ERROR] =
g_param_spec_string (
"error",
NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, LAST_PROP, props);
}
static void
bz_transaction_init (BzTransaction *self)
{
BzTransactionPrivate *priv = NULL;
g_autoptr (GDateTime) now = NULL;
priv = bz_transaction_get_instance_private (self);
now = g_date_time_new_now_local ();
priv->name = g_date_time_format (now, "%X");
priv->installs = g_list_store_new (BZ_TYPE_ENTRY);
priv->updates = g_list_store_new (BZ_TYPE_ENTRY);
priv->removals = g_list_store_new (BZ_TYPE_ENTRY);
priv->pending = TRUE;
priv->current_tasks = g_list_store_new (BZ_TYPE_TRANSACTION_TASK);
priv->finished_tasks = g_list_store_new (BZ_TYPE_TRANSACTION_TASK);
priv->trackers = g_list_store_new (BZ_TYPE_TRANSACTION_ENTRY_TRACKER);
priv->status = g_strdup (_ ("Pending"));
priv->success = TRUE;
}
BzTransaction *
bz_transaction_new_full (BzEntry **installs,
guint n_installs,
BzEntry **updates,
guint n_updates,
BzEntry **removals,
guint n_removals)
{
g_autoptr (BzTransaction) self = NULL;
BzTransactionPrivate *priv = NULL;
g_return_val_if_fail ((installs != NULL && n_installs > 0) ||
(updates != NULL && n_updates > 0) ||
(removals != NULL && n_removals > 0),
NULL);
for (guint i = 0; i < n_installs; i++)
g_return_val_if_fail (BZ_IS_ENTRY (installs[i]), NULL);
for (guint i = 0; i < n_updates; i++)
g_return_val_if_fail (BZ_IS_ENTRY (updates[i]), NULL);
for (guint i = 0; i < n_removals; i++)
g_return_val_if_fail (BZ_IS_ENTRY (removals[i]), NULL);
self = g_object_new (BZ_TYPE_TRANSACTION, NULL);
priv = bz_transaction_get_instance_private (self);
#define ADD_ENTRY(type, entry, transaction_type) \
G_STMT_START \
{ \
g_autoptr (BzTransactionEntryTracker) tracker = NULL; \
g_autoptr (GListStore) current_ops = NULL; \
g_autoptr (GListStore) finished_ops = NULL; \
\
g_list_store_append (priv->type, (entry)); \
\
tracker = bz_transaction_entry_tracker_new (); \
current_ops = g_list_store_new (BZ_TYPE_TRANSACTION_TASK); \
finished_ops = g_list_store_new (BZ_TYPE_TRANSACTION_TASK); \
\
bz_transaction_entry_tracker_set_entry (tracker, (entry)); \
bz_transaction_entry_tracker_set_current_ops (tracker, G_LIST_MODEL (current_ops)); \
bz_transaction_entry_tracker_set_finished_ops (tracker, G_LIST_MODEL (finished_ops)); \
bz_transaction_entry_tracker_set_kind (tracker, transaction_type); \
bz_transaction_entry_tracker_set_status (tracker, BZ_TRANSACTION_ENTRY_STATUS_QUEUED); \
bz_transaction_entry_tracker_set_pending (tracker, TRUE); \
if ((transaction_type) == BZ_TRANSACTION_ENTRY_KIND_REMOVAL) \
bz_transaction_entry_tracker_set_active (tracker, TRUE); \
\
g_list_store_append (priv->trackers, tracker); \
} \
G_STMT_END
for (guint i = 0; i < n_installs; i++)
ADD_ENTRY (installs, installs[i], BZ_TRANSACTION_ENTRY_KIND_INSTALL);
for (guint i = 0; i < n_updates; i++)
ADD_ENTRY (updates, updates[i], BZ_TRANSACTION_ENTRY_KIND_UPDATE);
for (guint i = 0; i < n_removals; i++)
ADD_ENTRY (removals, removals[i], BZ_TRANSACTION_ENTRY_KIND_REMOVAL);
#undef ADD_ENTRY
return g_steal_pointer (&self);
}
BzTransaction *
bz_transaction_new_merged (BzTransaction **transactions,
guint n_transactions)
{
g_autoptr (GPtrArray) installs_pa = NULL;
g_autoptr (GPtrArray) updates_pa = NULL;
g_autoptr (GPtrArray) removals_pa = NULL;
g_return_val_if_fail (transactions != NULL, NULL);
g_return_val_if_fail (n_transactions >= 2, NULL);
installs_pa = g_ptr_array_new_with_free_func (g_object_unref);
updates_pa = g_ptr_array_new_with_free_func (g_object_unref);
removals_pa = g_ptr_array_new_with_free_func (g_object_unref);
for (guint i = 0; i < n_transactions; i++)
{
GListModel *installs = NULL;
GListModel *updates = NULL;
GListModel *removals = NULL;
guint n_installs = 0;
guint n_updates = 0;
guint n_removals = 0;
installs = bz_transaction_get_installs (transactions[i]);
updates = bz_transaction_get_updates (transactions[i]);
removals = bz_transaction_get_removals (transactions[i]);
if (installs != NULL)
n_installs = g_list_model_get_n_items (installs);
if (updates != NULL)
n_updates = g_list_model_get_n_items (updates);
if (removals != NULL)
n_removals = g_list_model_get_n_items (removals);
for (guint j = 0; j < n_installs; j++)
g_ptr_array_add (installs_pa, g_list_model_get_item (installs, j));
for (guint j = 0; j < n_updates; j++)
g_ptr_array_add (updates_pa, g_list_model_get_item (updates, j));
for (guint j = 0; j < n_removals; j++)
g_ptr_array_add (removals_pa, g_list_model_get_item (removals, j));
}
return bz_transaction_new_full (
(BzEntry **) installs_pa->pdata,
installs_pa->len,
(BzEntry **) updates_pa->pdata,
updates_pa->len,
(BzEntry **) removals_pa->pdata,
removals_pa->len);
}
GListModel *
bz_transaction_get_installs (BzTransaction *self)
{
BzTransactionPrivate *priv = NULL;
g_return_val_if_fail (BZ_IS_TRANSACTION (self), NULL);
priv = bz_transaction_get_instance_private (self);
return G_LIST_MODEL (priv->installs);
}
GListModel *
bz_transaction_get_updates (BzTransaction *self)
{
BzTransactionPrivate *priv = NULL;
g_return_val_if_fail (BZ_IS_TRANSACTION (self), NULL);
priv = bz_transaction_get_instance_private (self);
return G_LIST_MODEL (priv->updates);
}
GListModel *
bz_transaction_get_removals (BzTransaction *self)
{
BzTransactionPrivate *priv = NULL;
g_return_val_if_fail (BZ_IS_TRANSACTION (self), NULL);
priv = bz_transaction_get_instance_private (self);
return G_LIST_MODEL (priv->removals);
}
GListModel *
bz_transaction_get_trackers (BzTransaction *self)
{
BzTransactionPrivate *priv = NULL;
g_return_val_if_fail (BZ_IS_TRANSACTION (self), NULL);
priv = bz_transaction_get_instance_private (self);
return G_LIST_MODEL (priv->trackers);
}
void
bz_transaction_hold (BzTransaction *self)
{
BzTransactionPrivate *priv = NULL;
g_return_if_fail (BZ_IS_TRANSACTION (self));
priv = bz_transaction_get_instance_private (self);
#define HOLD_MODEL(_model) \
if ((_model) != NULL) \
{ \
guint n_items = 0; \
\
n_items = g_list_model_get_n_items (G_LIST_MODEL ((_model))); \
for (guint i = 0; i < n_items; i++) \
{ \
g_autoptr (BzEntry) entry = NULL; \
\
entry = g_list_model_get_item (G_LIST_MODEL ((_model)), i); \
bz_entry_hold (entry); \
} \
}
HOLD_MODEL (priv->installs);
HOLD_MODEL (priv->updates);
HOLD_MODEL (priv->removals);
#undef HOLD_MODEL
}
void
bz_transaction_release (BzTransaction *self)
{
BzTransactionPrivate *priv = NULL;
g_return_if_fail (BZ_IS_TRANSACTION (self));
priv = bz_transaction_get_instance_private (self);
#define RELEASE_MODEL(_model) \
if ((_model) != NULL) \
{ \
guint n_items = 0; \
\
n_items = g_list_model_get_n_items (G_LIST_MODEL ((_model))); \
for (guint i = 0; i < n_items; i++) \
{ \
g_autoptr (BzEntry) entry = NULL; \
\
entry = g_list_model_get_item (G_LIST_MODEL ((_model)), i); \
bz_entry_release (entry); \
} \
}
RELEASE_MODEL (priv->installs);
RELEASE_MODEL (priv->updates);
RELEASE_MODEL (priv->removals);
#undef RELEASE_MODEL
}
void
bz_transaction_add_task (BzTransaction *self,
BzBackendTransactionOpPayload *payload)
{
BzTransactionPrivate *priv = NULL;
g_autoptr (BzTransactionTask) task = NULL;
BzEntry *entry = NULL;
g_autoptr (BzTransactionEntryTracker) tracker = NULL;
gboolean result = FALSE;
g_return_if_fail (BZ_IS_TRANSACTION (self));
g_return_if_fail (BZ_IS_BACKEND_TRANSACTION_OP_PAYLOAD (payload));
priv = bz_transaction_get_instance_private (self);
task = bz_transaction_task_new ();
bz_transaction_task_set_op (task, payload);
g_list_store_append (priv->current_tasks, task);
entry = bz_backend_transaction_op_payload_get_entry (payload);
result = find_and_maybe_transfer (
priv->trackers,
NULL,
entry,
(GEqualFuncFull) find_entry_eq_func,
(gpointer *) &tracker);
if (result)
{
GListModel *current_ops = NULL;
if (bz_transaction_entry_tracker_get_kind (tracker) == BZ_TRANSACTION_ENTRY_KIND_UPDATE)
bz_backend_transaction_op_payload_set_download_size (payload, (guint64) -1);
current_ops = bz_transaction_entry_tracker_get_current_ops (tracker);
g_list_store_append (G_LIST_STORE (current_ops), task);
g_object_notify (G_OBJECT (tracker), "current-ops");
}
}
void
bz_transaction_update_task (BzTransaction *self,
BzBackendTransactionOpProgressPayload *payload)
{
BzTransactionPrivate *priv = NULL;
BzBackendTransactionOpPayload *op = NULL;
g_autoptr (BzTransactionTask) task = NULL;
gboolean result = FALSE;
g_return_if_fail (BZ_IS_TRANSACTION (self));
g_return_if_fail (BZ_IS_BACKEND_TRANSACTION_OP_PROGRESS_PAYLOAD (payload));
priv = bz_transaction_get_instance_private (self);
op = bz_backend_transaction_op_progress_payload_get_op (payload);
result = find_and_maybe_transfer (
priv->current_tasks,
NULL,
op,
(GEqualFuncFull) find_payload_eq_func,
(gpointer *) &task);
if (result)
bz_transaction_task_set_last_progress (task, payload);
tracker_update (priv, op, payload, FALSE, FALSE);
}
void
bz_transaction_finish_task (BzTransaction *self,
BzBackendTransactionOpPayload *payload)
{
BzTransactionPrivate *priv = NULL;
g_return_if_fail (BZ_IS_TRANSACTION (self));
g_return_if_fail (BZ_IS_BACKEND_TRANSACTION_OP_PAYLOAD (payload));
priv = bz_transaction_get_instance_private (self);
find_and_maybe_transfer (
priv->current_tasks,
priv->finished_tasks,
payload,
(GEqualFuncFull) find_payload_eq_func,
NULL);
tracker_update (priv, payload, NULL, TRUE, FALSE);
}
void
bz_transaction_error_out_task (BzTransaction *self,
BzBackendTransactionOpPayload *payload,
const char *message)
{
BzTransactionPrivate *priv = NULL;
gboolean result = FALSE;
g_autoptr (BzTransactionTask) task = NULL;
g_return_if_fail (BZ_IS_TRANSACTION (self));
g_return_if_fail (BZ_IS_BACKEND_TRANSACTION_OP_PAYLOAD (payload));
g_return_if_fail (message != NULL);
priv = bz_transaction_get_instance_private (self);
result = find_and_maybe_transfer (
priv->current_tasks,
priv->finished_tasks,
payload,
(GEqualFuncFull) find_payload_eq_func,
(gpointer *) &task);
if (result)
bz_transaction_task_set_error (task, message);
tracker_update (priv, payload, NULL, TRUE, TRUE);
}
static void
finish (BzTransactionPrivate *priv)
{
guint n_trackers = 0;
n_trackers = g_list_model_get_n_items (G_LIST_MODEL (priv->trackers));
for (guint i = 0; i < n_trackers; i++)
{
g_autoptr (BzTransactionEntryTracker) tracker = NULL;
tracker = g_list_model_get_item (G_LIST_MODEL (priv->trackers), i);
bz_transaction_entry_tracker_set_active (tracker, FALSE);
bz_transaction_entry_tracker_set_pending (tracker, FALSE);
if (bz_transaction_entry_tracker_get_status (tracker) == BZ_TRANSACTION_ENTRY_STATUS_CANCELLED)
continue;
bz_transaction_entry_tracker_set_status (
tracker,
priv->success
? BZ_TRANSACTION_ENTRY_STATUS_DONE
: BZ_TRANSACTION_ENTRY_STATUS_CANCELLED);
}
}
static gboolean
find_payload_eq_func (BzTransactionTask *task,
gpointer none,
BzBackendTransactionOpPayload *payload)
{
return bz_transaction_task_get_op (task) == payload;
}
static gboolean
find_entry_eq_func (BzTransactionEntryTracker *tracker,
gpointer none,
BzEntry *entry)
{
return bz_transaction_entry_tracker_get_entry (tracker) == entry;
}
static gboolean
find_and_maybe_transfer (GListStore *from,
GListStore *to,
gpointer lookup,
GEqualFuncFull eql,
gpointer *out)
{
guint op_pos = 0;
gboolean found = FALSE;
g_autoptr (GObject) object = NULL;
found = g_list_store_find_with_equal_func_full (from, NULL, eql, lookup, &op_pos);
if (!found)
return FALSE;
object = g_list_model_get_item (G_LIST_MODEL (from), op_pos);
if (to != NULL)
{
g_list_store_remove (from, op_pos);
g_list_store_append (to, object);
}
if (out != NULL)
*out = g_steal_pointer (&object);
return TRUE;
}
static void
tracker_update (BzTransactionPrivate *priv,
BzBackendTransactionOpPayload *payload,
BzBackendTransactionOpProgressPayload *progress_payload,
gboolean transfer,
gboolean set_done)
{
BzEntry *entry = NULL;
g_autoptr (BzTransactionEntryTracker) tracker = NULL;
gboolean result = FALSE;
entry = bz_backend_transaction_op_payload_get_entry (payload);
result = find_and_maybe_transfer (
priv->trackers,
NULL,
entry,
(GEqualFuncFull) find_entry_eq_func,
(gpointer *) &tracker);
if (result)
{
BzTransactionEntryStatus existing_status = 0;
existing_status = bz_transaction_entry_tracker_get_status (tracker);
if (transfer)
{
GListModel *from = NULL;
GListModel *to = NULL;
if (set_done)
{
bz_transaction_entry_tracker_set_active (tracker, FALSE);
bz_transaction_entry_tracker_set_status (tracker, BZ_TRANSACTION_ENTRY_STATUS_DONE);
bz_transaction_entry_tracker_set_pending (tracker, FALSE);
}
from = bz_transaction_entry_tracker_get_current_ops (tracker);
to = bz_transaction_entry_tracker_get_finished_ops (tracker);
result = find_and_maybe_transfer (
G_LIST_STORE (from),
G_LIST_STORE (to),
payload,
(GEqualFuncFull) find_payload_eq_func,
NULL);
g_object_notify (G_OBJECT (tracker), "current-ops");
g_object_notify (G_OBJECT (tracker), "finished-ops");
}
else
{
if (progress_payload != NULL)
{
double progress = 0.0;
double scaled = 0.0;
gboolean estimating = FALSE;
progress = bz_backend_transaction_op_progress_payload_get_total_progress (progress_payload);
scaled = progress >= 1.0 ? 1.0 : progress * 0.9;
estimating = bz_backend_transaction_op_progress_payload_get_is_estimating (progress_payload);
g_object_set (tracker,
"progress", scaled,
"pending", estimating && progress == 0.0,
NULL);
}
bz_transaction_entry_tracker_set_active (tracker, TRUE);
if (existing_status != BZ_TRANSACTION_ENTRY_STATUS_CANCELLED)
bz_transaction_entry_tracker_set_status (tracker, BZ_TRANSACTION_ENTRY_STATUS_ONGOING);
}
}
}