/* Chrysalide - Outil d'analyse de fichiers binaires
* statusstack.c - empilement d'informations de statut
*
* Copyright (C) 2015-2024 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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.
*
* Chrysalide 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 Chrysalide. If not, see .
*/
#include "statusstack.h"
#include
#include
#include
#include
#include "helpers.h"
#include "statusstack-int.h"
#include "../core/global.h"
/* -------------------------- GESTION GENERALE DES STATUTS -------------------------- */
/* Initialise la classe des barres de statut améliorées. */
static void gtk_status_stack_class_init(GtkStatusStackClass *);
/* Initialise une instance de barre de statut améliorée. */
static void gtk_status_stack_init(GtkStatusStack *);
/* Supprime toutes les références externes. */
static void gtk_status_stack_dispose(GtkStatusStack *);
/* Procède à la libération totale de la mémoire. */
static void gtk_status_stack_finalize(GtkStatusStack *);
/* Met à jour dans la barre les débits réseau observés. */
static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *);
/* -------------------- STATUT DES INFORMATIONS DE DESASSEMBLAGE -------------------- */
/* Navigation au sein d'assemblage */
struct _navigation_info_t
{
char *segment; /* Segment d'appartenance */
mrange_t current; /* Emplacement correspondant */
VMPA_BUFFER(phys); /* Localisation physique */
VMPA_BUFFER(virt); /* Localisation virtuelle */
char *symbol; /* Eventuel symbole concerné */
char *format; /* Architecture et format */
char *details; /* Encodage de l'instruction */
};
/* Met en place le suivi d'informations de navigation. */
static void init_navigation_info(navigation_info_t *);
/* Supprime l'empreinte mémoire d'informations de navigation. */
static void fini_navigation_info(navigation_info_t *);
/* S'assure de l'affichage à jour de la partie "navigation". */
static gboolean gtk_status_stack_show_current_location(GtkStatusStack *);
/* Réagit à un clic sur l'icône de zoom. */
static void gtk_status_stack_on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GtkStatusStack *);
/* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */
/* Informations de progression */
typedef struct _activity_status_t
{
activity_id_t id; /* Identifiant unique */
char *message; /* Indication à faire valoir */
unsigned long current; /* Position courante */
unsigned long max; /* Couverture à parcourir */
double last_updated; /* Dernière valeur poussée */
} activity_status_t;
/* Mémorisation des progressions au sein d'activités */
struct _activity_info_t
{
GMutex access; /* Accès à la pile */
activity_id_t generator; /* Générateur de séquence */
activity_status_t *statuses; /* Statuts de progression */
size_t count; /* Nombre de ces statuts */
guint tag; /* Identifiant de mise à jour */
};
/* Met en place le suivi d'informations d'activité. */
static void init_activity_info(activity_info_t *);
/* Supprime l'empreinte mémoire d'informations d'activité. */
static void fini_activity_info(activity_info_t *);
/* Recherche les indications de statut d'une activité donnée. */
static activity_status_t *find_activity_status_by_id(activity_info_t *, activity_id_t);
/* S'assure de l'affichage à jour de la partie "activité". */
static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *);
/* ---------------------------------------------------------------------------------- */
/* GESTION GENERALE DES STATUTS */
/* ---------------------------------------------------------------------------------- */
/* Détermine le type du composant d'affichage générique. */
G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX);
/******************************************************************************
* *
* Paramètres : class = classe GTK à initialiser. *
* *
* Description : Initialise la classe des barres de statut améliorées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_status_stack_class_init(GtkStatusStackClass *class)
{
GObjectClass *object; /* Plus haut niveau équivalent */
GtkWidgetClass *widget; /* Classe de haut niveau */
object = G_OBJECT_CLASS(class);
object->dispose = (GObjectFinalizeFunc/* ! */)gtk_status_stack_dispose;
object->finalize = (GObjectFinalizeFunc)gtk_status_stack_finalize;
widget = GTK_WIDGET_CLASS(class);
gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gtkext/statusstack.ui");
gtk_widget_class_bind_template_callback_full(widget, BUILDER_CB(gtk_status_stack_on_zoom_icon_press));
gtk_widget_class_bind_template_child(widget, GtkStatusStack, main);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_segment);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_phys);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_virt);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_offset);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_format);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, nav_details);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, zoom);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_message);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, activity_progress);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_recv_speed);
gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_send_speed);
}
/******************************************************************************
* *
* Paramètres : stack = composant GTK à initialiser. *
* *
* Description : Initialise une instance de barre de statut améliorée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_status_stack_init(GtkStatusStack *stack)
{
gtk_widget_init_template(GTK_WIDGET(stack));
stack->def_source = NULL;
stack->nav_info = calloc(1, sizeof(navigation_info_t));
init_navigation_info(stack->nav_info);
stack->activity_info = calloc(1, sizeof(activity_info_t));
init_activity_info(stack->activity_info);
stack->next_index = 0;
stack->network_update_tag = g_timeout_add(NETWORK_UPDATE_INTERVAL,
G_SOURCE_FUNC(gtk_status_stack_update_network_stats), stack);
}
/******************************************************************************
* *
* Paramètres : stack = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_status_stack_dispose(GtkStatusStack *stack)
{
g_source_remove(stack->network_update_tag);
gtk_widget_dispose_template(GTK_WIDGET(stack), GTK_TYPE_STATUS_STACK);
G_OBJECT_CLASS(gtk_status_stack_parent_class)->dispose(G_OBJECT(stack));
}
/******************************************************************************
* *
* Paramètres : stack = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_status_stack_finalize(GtkStatusStack *stack)
{
fini_navigation_info(stack->nav_info);
free(stack->nav_info);
fini_activity_info(stack->activity_info);
free(stack->activity_info);
G_OBJECT_CLASS(gtk_status_stack_parent_class)->finalize(G_OBJECT(stack));
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Crée une nouvelle instance de barre de statut. *
* *
* Retour : Composant GTK mis en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GtkStatusStack *gtk_status_stack_new(void)
{
GtkStatusStack *result; /* Instance à retourner */
result = g_object_new(GTK_TYPE_STATUS_STACK, NULL);
return result;
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* *
* Description : Réinitialise la barre de statut à son stade par défaut. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_status_stack_reset(GtkStatusStack *stack)
{
gtk_stack_set_visible_child_name(stack->main, "default");
stack->def_source = NULL;
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* *
* Description : Met à jour dans la barre les débits réseau observés. *
* *
* Retour : G_SOURCE_CONTINUE pour poursuivre les mises à jour. *
* *
* Remarques : - *
* *
******************************************************************************/
static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *stack)
{
gboolean result; /* Indication à retourner */
gint64 timestamp; /* Position temporelle */
size_t received; /* Quantité d'octets reçus */
size_t sent; /* Quantité d'octets émis */
gint64 diff_time; /* Différentiel de temps */
size_t diff_bytes; /* Différentiel de volume */
double speed; /* Débit de transfert constaté */
size_t i; /* Boucle de parcours */
char *value; /* Valeur à afficher */
const char *units[] = { _("b/s"), _("kb/s"), _("Mb/s"), _("Gb/s"), _("Tb/s") };
result = G_SOURCE_CONTINUE;
/* Mémorisation des données */
timestamp = g_get_monotonic_time();
get_network_stats(&received, &sent);
if (stack->next_index < NETWORK_UPDATE_COUNT)
{
stack->last_bytes_received[stack->next_index] = received;
stack->last_bytes_sent[stack->next_index] = sent;
stack->last_timestamps[stack->next_index] = timestamp;
stack->next_index++;
}
else
{
memcpy(stack->last_bytes_received, stack->last_bytes_received + 1,
(NETWORK_UPDATE_COUNT - 1) * sizeof(size_t));
memcpy(stack->last_bytes_sent, stack->last_bytes_sent + 1,
(NETWORK_UPDATE_COUNT - 1) * sizeof(size_t));
memcpy(stack->last_timestamps, stack->last_timestamps + 1,
(NETWORK_UPDATE_COUNT - 1) * sizeof(gint64));
stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] = received;
stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] = sent;
stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] = timestamp;
}
if (stack->next_index < NETWORK_UPDATE_COUNT)
goto done;
diff_time = stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] - stack->last_timestamps[0];
/* Débit de réception */
diff_bytes = stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_received[0];
speed = (diff_bytes * 1000000) / diff_time;
for (i = 0; i < G_N_ELEMENTS(units); i++)
{
if (speed < 1024)
break;
speed /= 1024;
}
if (i == 0)
asprintf(&value, "%d %s", (int)speed, units[i]);
else
asprintf(&value, "%.1f %s", speed, units[i]);
gtk_label_set_label(stack->net_recv_speed, value);
free(value);
/* Débit de émission */
diff_bytes = stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_sent[0];
speed = (diff_bytes * 1000000) / diff_time;
for (i = 0; i < G_N_ELEMENTS(units); i++)
{
if (speed < 1024)
break;
speed /= 1024;
}
if (i == 0)
asprintf(&value, "%d %s", (int)speed, units[i]);
else
asprintf(&value, "%.1f %s", speed, units[i]);
gtk_label_set_label(stack->net_send_speed, value);
free(value);
done:
return result;
}
/* ---------------------------------------------------------------------------------- */
/* STATUT DES INFORMATIONS DE DESASSEMBLAGE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : info = informations à initialiser. *
* *
* Description : Met en place le suivi d'informations de navigation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void init_navigation_info(navigation_info_t *info)
{
info->segment = NULL;
info->symbol = NULL;
info->format = NULL;
info->details = NULL;
}
/******************************************************************************
* *
* Paramètres : info = informations à libérer de la mémoire. *
* *
* Description : Supprime l'empreinte mémoire d'informations de navigation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void fini_navigation_info(navigation_info_t *info)
{
if (info->segment != NULL)
free(info->segment);
if (info->symbol != NULL)
free(info->symbol);
if (info->format != NULL)
free(info->format);
if (info->details != NULL)
free(info->details);
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* range = emplacement à mettre en valeur. *
* segment = zone de binaire d'appartenance. *
* symbol = éventuelle position par rapport à un symbole. *
* format = format du binaire manipulé *
* details = détails supplémentaires (liés à l'encodage ?) *
* *
* Description : Actualise les informations liées une position d'assemblage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_status_stack_update_current_location(GtkStatusStack *stack, const mrange_t *range, const char *segment, const char *symbol, const char *format, const char *details)
{
navigation_info_t *info; /* Informations à constituer */
const vmpa2t *addr; /* Localisation de départ */
info = stack->nav_info;
if (cmp_mrange(&info->current, range) == 0)
goto useless;
/* Zone d'appartenance */
if (segment != NULL)
info->segment = strdup(segment);
else
info->segment = NULL;
/* Adresses de base */
copy_mrange(&info->current, range);
addr = get_mrange_addr(range);
vmpa2_phys_to_string(addr, MDS_UNDEFINED, info->phys, NULL);
vmpa2_virt_to_string(addr, MDS_UNDEFINED, info->virt, NULL);
/* Symbole concerné */
if (symbol != NULL)
info->symbol = strdup(symbol);
else
info->symbol = NULL;
/* Architecture & format */
info->format = strdup(format);
if (details != NULL)
info->details = strdup(details);
else
info->details = NULL;
/* Nettoyage et conclusion */
gtk_status_stack_show_current_location(stack);
useless:
;
}
/******************************************************************************
* *
* Paramètres : stack = pile de statuts à manipuler. *
* *
* Description : S'assure de l'affichage à jour de la partie "navigation". *
* *
* Retour : G_SOURCE_REMOVE pour une exécution unique. *
* *
* Remarques : - *
* *
******************************************************************************/
static gboolean gtk_status_stack_show_current_location(GtkStatusStack *stack)
{
navigation_info_t *info; /* Informations à constituer */
char raw_pos[6 + VMPA_MAX_LEN + 1]; /* Formatage final en direct */
stack->def_source = (GSourceFunc)gtk_status_stack_show_current_location;
gtk_stack_set_visible_child_name(stack->main, "navigation");
info = stack->nav_info;
/* Première partie : navigation */
gtk_label_set_text(stack->nav_segment, info->segment != NULL ? info->segment : "");
snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys);
gtk_label_set_text(stack->nav_phys, raw_pos);
snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt);
gtk_label_set_text(stack->nav_virt, raw_pos);
gtk_label_set_text(stack->nav_offset, info->symbol != NULL ? info->symbol : "");
/* Seconde partie : format & architecture */
gtk_label_set_text(stack->nav_format, info->format);
gtk_label_set_text(stack->nav_details, info->details != NULL ? info->details : "");
return G_SOURCE_REMOVE;
}
/******************************************************************************
* *
* Paramètres : entry = zone de texte visée par la procédure. *
* icon_pos = position de l'image associée à l'entrée. *
* stack = composant graphique de gestion des statuts. *
* *
* Description : Réagit à un clic sur l'icône de zoom. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void gtk_status_stack_on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GtkStatusStack *stack)
{
#if 0
GtkWidget *popup; /* Popup à faire surgir */
GdkRectangle rect; /* Zone précise à cibler */
popup = gtk_popover_new();
gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect);
gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect);
gtk_widget_show(popup);
#endif
}
/* ---------------------------------------------------------------------------------- */
/* STATUT DES SUIVIS D'ACTIVITE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : info = informations à initialiser. *
* *
* Description : Met en place le suivi d'informations d'activité. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void init_activity_info(activity_info_t *info)
{
g_mutex_init(&info->access);
info->generator = NO_ACTIVITY_ID;
info->statuses = NULL;
info->count = 0;
info->tag = 0;
}
/******************************************************************************
* *
* Paramètres : info = informations à libérer de la mémoire. *
* *
* Description : Supprime l'empreinte mémoire d'informations d'activité. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void fini_activity_info(activity_info_t *info)
{
size_t i; /* Boucle de parcours */
if (info->tag != 0)
g_source_remove(info->tag);
info->tag = 0;
for (i = 0; i < info->count; i++)
{
if (info->statuses[i].message != NULL)
free(info->statuses[i].message);
}
if (info->statuses != NULL)
{
free(info->statuses);
info->statuses = NULL;
}
info->count = 0;
g_mutex_clear(&info->access);
}
/******************************************************************************
* *
* Paramètres : info = informations relatives aux activités à consulter. *
* id = identifiant de l'activité à cibler. *
* *
* Description : Recherche les indications de statut d'une activité donnée. *
* *
* Retour : Structure d'encadrement trouvée ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
static activity_status_t *find_activity_status_by_id(activity_info_t *info, activity_id_t id)
{
activity_status_t *result; /* Statut trouvé à renvoyer */
size_t i; /* Boucle de parcours */
result = NULL;
assert(!g_mutex_trylock(&info->access));
for (i = 0; i < info->count; i++)
if (info->statuses[i].id == id)
{
result = info->statuses + i;
break;
}
return result;
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* msg = nouveau message de statut à copier. *
* max = taille de la plage à parcourir. *
* *
* Description : Démarre le suivi d'une nouvelle activité. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *msg, unsigned long max)
{
activity_id_t result; /* Numéro unique à renvoyer */
activity_info_t *info; /* Informations à consulter */
activity_status_t *new; /* Nouveau suivi d'activité */
info = stack->activity_info;
g_mutex_lock(&info->access);
while (1)
{
result = ++info->generator;
if (find_activity_status_by_id(info, result) == NULL)
break;
}
while (0);
info->statuses = realloc(info->statuses, ++info->count * sizeof(activity_status_t));
new = info->statuses + info->count - 1;
/* Identifiant */
new->id = result;
/* Intitulé */
if (msg == NULL)
new->message = NULL;
else
new->message = strdup(msg);
/* Valeur */
new->current = 0;
new->max = max;
new->last_updated = 0;
/* Actualisation */
if (info->tag != 0)
g_source_remove(info->tag);
info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
g_mutex_unlock(&info->access);
return result;
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* id = identifiant de l'activité à cibler. *
* msg = nouveau message de statut à copier. *
* *
* Description : Actualise les informations concernant une activité. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_status_stack_update_activity_message(GtkStatusStack *stack, activity_id_t id, const char *msg)
{
activity_info_t *info; /* Informations à consulter */
activity_status_t *status; /* Suivi d'activité à traiter */
bool msg_changed; /* Changement d'intitulé */
info = stack->activity_info;
g_mutex_lock(&info->access);
status = find_activity_status_by_id(info, id);
assert(status != NULL);
if (status == NULL)
goto exit;
/* Intitulé */
if (status->message != NULL)
{
if (msg == NULL)
msg_changed = true;
else
msg_changed = (strcmp(status->message, msg) != 0);
free(status->message);
}
else
msg_changed = (msg != NULL);
if (msg == NULL)
status->message = NULL;
else
status->message = strdup(msg);
/* On n'actualise que le sommet de la pile */
if ((status - info->statuses + 1) == info->count && msg_changed)
{
if (info->tag != 0)
g_source_remove(info->tag);
info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
}
exit:
g_mutex_unlock(&info->access);
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* id = identifiant de l'activité à cibler. *
* inc = nouvelle valeur pour une progression donnée. *
* *
* Description : Actualise la progression d'une activité. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t id, unsigned long inc)
{
activity_info_t *info; /* Informations à consulter */
activity_status_t *status; /* Suivi d'activité à traiter */
double new; /* Nouvelle progression */
info = stack->activity_info;
g_mutex_lock(&info->access);
status = find_activity_status_by_id(info, id);
assert(status != NULL);
if (status == NULL)
goto exit;
/* Valeur */
status->current += inc;
new = (status->current * 1.0) / status->max;
/* On n'actualise que le sommet de la pile */
if ((status - info->statuses + 1) == info->count && (new - status->last_updated) > 0.1)
{
status->last_updated = new;
if (info->tag != 0)
g_source_remove(info->tag);
info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
}
exit:
g_mutex_unlock(&info->access);
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* id = identifiant de l'activité à cibler. *
* extra = nouvelle échéance supplémentaire des traitements. *
* *
* Description : Etend la portée des travaux d'une nouvelle activité. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_status_stack_extend_activity_max(GtkStatusStack *stack, activity_id_t id, unsigned long extra)
{
activity_info_t *info; /* Informations à consulter */
activity_status_t *status; /* Suivi d'activité à traiter */
info = stack->activity_info;
g_mutex_lock(&info->access);
status = find_activity_status_by_id(info, id);
assert(status != NULL);
if (status == NULL)
goto exit;
/* Valeur */
status->max += extra;
/* On n'actualise que le sommet de la pile */
if ((status - info->statuses + 1) == info->count)
{
if (info->tag != 0)
g_source_remove(info->tag);
info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
}
exit:
g_mutex_unlock(&info->access);
}
/******************************************************************************
* *
* Paramètres : stack = barre de statut à actualiser. *
* *
* Description : Met fin au suivi d'une activité donnée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void gtk_status_stack_remove_activity(GtkStatusStack *stack, activity_id_t id)
{
activity_info_t *info; /* Informations à consulter */
activity_status_t *status; /* Suivi d'activité à traiter */
bool is_last; /* Dernière position ? */
info = stack->activity_info;
g_mutex_lock(&info->access);
status = find_activity_status_by_id(info, id);
assert(status != NULL);
if (status == NULL)
goto exit;
is_last = ((status - info->statuses + 1) == info->count);
/* Suppression des données */
if (is_last)
{
if (info->tag != 0)
g_source_remove(info->tag);
}
if (status->message != NULL)
free(status->message);
/* Réajustement des enregistrements */
if (!is_last)
memmove(status, status + 1,
(((info->statuses + info->count) - status) - 1) * sizeof(activity_status_t));
info->statuses = realloc(info->statuses, --info->count * sizeof(activity_status_t));
/* Bascule vers un autre affichage ou actualisation ? */
if (info->count == 0)
{
info->tag = 0;
g_idle_add(stack->def_source, stack);
}
else if (is_last)
info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
exit:
g_mutex_unlock(&info->access);
}
/******************************************************************************
* *
* Paramètres : stack = pile de statuts à manipuler. *
* *
* Description : S'assure de l'affichage à jour de la partie "activité". *
* *
* Retour : G_SOURCE_REMOVE pour une exécution unique. *
* *
* Remarques : - *
* *
******************************************************************************/
static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *stack)
{
activity_info_t *info; /* Informations à consulter */
activity_status_t *last; /* Dernier statut à traiter */
info = stack->activity_info;
g_mutex_lock(&info->access);
if (!g_source_is_destroyed(g_main_current_source()))
{
if (info->count > 0)
{
gtk_stack_set_visible_child_name(stack->main, "activity");
last = &info->statuses[info->count - 1];
gtk_label_set_text(stack->activity_message, last->message);
gtk_progress_bar_set_fraction(stack->activity_progress, (last->current * 1.0) / last->max);
}
info->tag = 0;
}
g_mutex_unlock(&info->access);
return G_SOURCE_REMOVE;
}