summaryrefslogtreecommitdiff
path: root/src/gtkext/statusstack.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gtkext/statusstack.c')
-rw-r--r--src/gtkext/statusstack.c1121
1 files changed, 1121 insertions, 0 deletions
diff --git a/src/gtkext/statusstack.c b/src/gtkext/statusstack.c
new file mode 100644
index 0000000..fe4e4d5
--- /dev/null
+++ b/src/gtkext/statusstack.c
@@ -0,0 +1,1121 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * gtkstatusstack.c - empilement d'informations de statut
+ *
+ * Copyright (C) 2015-2019 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "gtkstatusstack.h"
+
+
+#include <assert.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <string.h>
+
+
+#include <i18n.h>
+
+
+#include "easygtk.h"
+#include "../gui/agroup.h"
+#include "../format/format.h"
+
+
+
+/* ------------------------- GESTION EXTERIEURE DE LA BARRE ------------------------- */
+
+
+/* Navigation au sein d'assemblage */
+typedef struct _assembly_info assembly_info;
+
+/* Mémorisation des progressions */
+typedef struct _progress_info progress_info;
+
+
+/* Abstration d'une gestion de barre de statut (instance) */
+struct _GtkStatusStack
+{
+ GtkBox parent; /* A laisser en premier */
+
+ GtkStack *main; /* Pile d'informations associée*/
+
+ GSourceFunc def_source; /* Appel en fin d'activité */
+
+ GObject *asm_ref; /* Espace de référencements #1 */
+ assembly_info *asm_info; /* Informations courantes #1 */
+
+ GObject *prog_ref; /* Espace de référencements #2 */
+ progress_info *prog_info; /* Informations courantes #2 */
+
+};
+
+/* Abstration d'une gestion de barre de statut (classe) */
+struct _GtkStatusStackClass
+{
+ GtkBoxClass parent; /* A laisser en premier */
+
+};
+
+
+/* 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 *);
+
+
+
+/* -------------------- STATUT DES INFORMATIONS DE DESASSEMBLAGE -------------------- */
+
+
+/* Navigation au sein d'assemblage */
+struct _assembly_info
+{
+ bool reset; /* Réinitialisation */
+
+ mrange_t current; /* Emplacement correspondant */
+
+ char *segment; /* Segment d'appartenance */
+
+ VMPA_BUFFER(phys); /* Localisation physique */
+ VMPA_BUFFER(virt); /* Localisation virtuelle */
+
+ char *symbol; /* Eventuel symbole concerné */
+
+ const char *encoding; /* Encodage de l'instruction */
+ phys_t size; /* Taille de l'instruction */
+
+};
+
+
+/* Supprime l'empreinte mémoire d'informations d'assemblage. */
+static void reset_assembly_info(assembly_info *);
+
+/* Construit une barre d'état pour language d'assemblage. */
+static GtkWidget *build_assembly_status_stack(GtkStatusStack *);
+
+/* Réagit à un redimensionnement de la barre de désassemblage. */
+static void on_size_allocate_for_asm_status(GtkWidget *, GdkRectangle *, GObject *);
+
+/* Réagit à un clic sur l'icône de zoom. */
+static void on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GdkEventButton *, GtkStatusStack *);
+
+/* S'assure de l'affichage à jour de la partie "assemblage". */
+static gboolean gtk_status_stack_show_current_location(GtkStatusStack *);
+
+
+
+/* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */
+
+
+/* Informations de progression */
+typedef struct _progress_status
+{
+ 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 */
+
+} progress_status;
+
+/* Mémorisation des progressions */
+struct _progress_info
+{
+ activity_id_t generator; /* Générateur de séquence */
+
+ progress_status *statuses; /* Statuts de progression */
+ size_t count; /* Nombre de ces statuts */
+ GMutex access; /* Accès à la pile */
+
+ guint tag; /* Identifiant de mise à jour */
+
+};
+
+
+#define PROGRESS_SIZE 200
+
+
+/* Supprime l'empreinte mémoire d'informations d'activité. */
+static void reset_progress_info(progress_info *);
+
+/* Construit une barre d'état pour un suivi d'activité. */
+static GtkWidget *build_progress_status_stack(GtkStatusStack *);
+
+/* S'assure de l'affichage à jour de la partie "activité". */
+static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GESTION EXTERIEURE DE LA BARRE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Détermine le type de la barre de statut améliorée. */
+G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX)
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe GTK à initialiser. *
+* *
+* Description : Initialise la classe des barres de statut améliorées. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_status_stack_class_init(GtkStatusStackClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)gtk_status_stack_dispose;
+ object->finalize = (GObjectFinalizeFunc)gtk_status_stack_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stack = instance GTK à initialiser. *
+* *
+* Description : Initialise une instance de barre de statut améliorée. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_status_stack_init(GtkStatusStack *stack)
+{
+ GtkWidget *layer; /* Couche à empiler */
+
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(stack), GTK_ORIENTATION_HORIZONTAL);
+
+ stack->main = GTK_STACK(gtk_stack_new());
+ gtk_widget_show(GTK_WIDGET(stack->main));
+ gtk_box_pack_start(GTK_BOX(stack), GTK_WIDGET(stack->main), TRUE, TRUE, 8);
+
+ stack->def_source = (GSourceFunc)gtk_status_stack_show_current_location;
+
+ layer = build_assembly_status_stack(stack);
+ gtk_stack_add_named(stack->main, layer, "asm_info");
+
+ stack->asm_ref = G_OBJECT(layer);
+ stack->asm_info = (assembly_info *)calloc(1, sizeof(assembly_info));
+
+ reset_assembly_info(stack->asm_info);
+
+ layer = build_progress_status_stack(stack);
+ gtk_stack_add_named(stack->main, layer, "prog_info");
+
+ stack->prog_ref = G_OBJECT(layer);
+ stack->prog_info = (progress_info *)calloc(1, sizeof(progress_info));
+
+ reset_progress_info(stack->prog_info);
+
+ gtk_status_stack_reset_current_location(stack);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : view = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void gtk_status_stack_dispose(GtkStatusStack *stack)
+{
+ G_OBJECT_CLASS(gtk_status_stack_parent_class)->dispose(G_OBJECT(stack));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : view = 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)
+{
+ reset_assembly_info(stack->asm_info);
+ free(stack->asm_info);
+
+ reset_progress_info(stack->prog_info);
+ free(stack->prog_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;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* STATUT DES INFORMATIONS DE DESASSEMBLAGE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations à réinitialiser. *
+* *
+* Description : Supprime l'empreinte mémoire d'informations d'assemblage. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void reset_assembly_info(assembly_info *info)
+{
+ info->reset = true;
+
+ if (info->segment != NULL)
+ {
+ free(info->segment);
+ info->segment = NULL;
+ }
+
+ if (info->symbol != NULL)
+ {
+ free(info->symbol);
+ info->symbol = NULL;
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stack = composant global en cours de construction. *
+* *
+* Description : Construit une barre d'état pour language d'assemblage. *
+* *
+* Retour : Composant GTK mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GtkWidget *build_assembly_status_stack(GtkStatusStack *stack)
+{
+ GtkWidget *result; /* Support à retourner */
+ GObject *ref; /* Espace de référencements */
+ GtkWidget *hbox; /* Sous-division horizontale */
+ GtkWidget *label; /* Etiquette pour impression */
+ GtkWidget *zoom; /* Sélection du zoom courant */
+
+ result = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_widget_show(result);
+
+ ref = G_OBJECT(result);
+
+ g_signal_connect(result, "size-allocate", G_CALLBACK(on_size_allocate_for_asm_status), ref);
+
+ /* Première partie : navigation */
+
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16);
+ gtk_widget_show(hbox);
+ gtk_box_pack_start(GTK_BOX(result), hbox, TRUE, TRUE, 8);
+
+ label = qck_create_label(ref, "segment", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ label = qck_create_label(ref, "phys", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ label = qck_create_label(ref, "virt", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ label = qck_create_label(ref, "offset", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ /* Seconde partie : architecture */
+
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+ g_object_set_data(ref, "arch_box", hbox);
+ gtk_widget_show(hbox);
+ gtk_box_pack_start(GTK_BOX(result), hbox, FALSE, TRUE, 8);
+
+ label = qck_create_label(ref, "arch", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ label = qck_create_label(ref, "size", NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ /* Troisième partie : affichage */
+
+ hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+ gtk_widget_show(hbox);
+ gtk_box_pack_start(GTK_BOX(result), hbox, FALSE, FALSE, 8);
+
+ zoom = qck_create_entry(ref, "zoom", "100%");
+ gtk_entry_set_icon_from_icon_name(GTK_ENTRY(zoom), GTK_ENTRY_ICON_SECONDARY, "go-up-symbolic");
+
+ g_signal_connect(zoom, "focus-in-event", G_CALLBACK(track_focus_change_in_text_area), NULL);
+ g_signal_connect(zoom, "focus-out-event", G_CALLBACK(track_focus_change_in_text_area), NULL);
+ g_signal_connect(zoom, "icon-press", G_CALLBACK(on_zoom_icon_press), stack);
+
+ gtk_box_pack_start(GTK_BOX(hbox), zoom, FALSE, TRUE, 0);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : widget = composant graphique qui vient d'évoluer. *
+* allocation = espace réservé pour le composant visé. *
+* ref = espace de référencement global. *
+* *
+* Description : Réagit à un redimensionnement de la barre de désassemblage. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void on_size_allocate_for_asm_status(GtkWidget *widget, GdkRectangle *allocation, GObject *ref)
+{
+ GtkWidget *hbox; /* Sous-division horizontale */
+
+ hbox = GTK_WIDGET(g_object_get_data(ref, "arch_box"));
+
+ gtk_widget_set_size_request(hbox, (allocation->width * 40) / 100, -1);
+
+ /**
+ * On intervient après que le containeur soit passé collecter les tailles
+ * de ses enfants lors de son redimensionnement.
+ *
+ * Donc on force un prise en compte des changements.
+ */
+ gtk_container_check_resize(GTK_CONTAINER(widget));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : entry = zone de texte visée par la procédure. *
+* icon_pos = position de l'image associée à l'entrée. *
+* event = informations liées à l'événement. *
+* stack = composant graphique de gestion des statuts. *
+* *
+* Description : Réagit à un clic sur l'icône de zoom. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEventButton *event, GtkStatusStack *stack)
+{
+ GtkWidget *popup; /* Popup à faire surgir */
+ GdkRectangle rect; /* Zone précise à cibler */
+
+ if (event->button != GDK_BUTTON_PRIMARY)
+ return;
+
+ popup = gtk_popover_new(GTK_WIDGET(entry));
+
+ gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect);
+ gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect);
+
+ gtk_widget_show(popup);
+
+}
+
+
+/******************************************************************************
+* *
+* 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. *
+* encoding = encodage d'une éventuelle instruction ou NULL. *
+* *
+* 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 *encoding)
+{
+ assembly_info *info; /* Informations à constituer */
+ const vmpa2t *addr; /* Localisation de départ */
+ phys_t size; /* Taille de l'emplacement */
+
+ info = stack->asm_info;
+
+ /* Bascule vers une zone courante nouvelle ? */
+
+ addr = get_mrange_addr(range);
+ size = get_mrange_length(range);
+
+ if (cmp_mrange(&info->current, range) == 0
+ && info->size == size
+ && info->encoding == encoding)
+ goto useless;
+
+ /* Réinitialisation */
+
+ reset_assembly_info(info);
+
+ copy_mrange(&info->current, range);
+
+ /* Zone d'appartenance */
+
+ info->segment = strdup(segment);
+
+ /* Adresses de base */
+
+ vmpa2_phys_to_string(addr, MDS_UNDEFINED, info->phys, NULL);
+
+ vmpa2_virt_to_string(addr, MDS_UNDEFINED, info->virt, NULL);
+
+ info->encoding = encoding;
+ info->size = size;
+
+ /* Symbole concerné */
+
+ if (symbol != NULL)
+ info->symbol = strdup(symbol);
+
+ /* Nettoyage et conclusion */
+
+ info->reset = false;
+
+ gtk_status_stack_show_current_location(stack);
+
+ useless:
+
+ ;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stack = barre de statut à actualiser. *
+* *
+* Description : Réinitialise les informations associées une position. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void gtk_status_stack_reset_current_location(GtkStatusStack *stack)
+{
+ assembly_info *info; /* Informations à constituer */
+
+ info = stack->asm_info;
+
+ reset_assembly_info(info);
+
+ gtk_status_stack_show_current_location(stack);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stack = pile de statuts à manipuler. *
+* *
+* Description : S'assure de l'affichage à jour de la partie "assemblage". *
+* *
+* Retour : G_SOURCE_REMOVE pour une exécution unique. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static gboolean gtk_status_stack_show_current_location(GtkStatusStack *stack)
+{
+ GObject *ref; /* Espace de référencements */
+ assembly_info *info; /* Informations à consulter */
+ GtkLabel *label; /* Etiquette à actualiser */
+ char raw_pos[6 + VMPA_MAX_LEN + 1]; /* Formatage final en direct */
+ char *content; /* Contenu dynamique */
+
+ stack->def_source = (GSourceFunc)gtk_status_stack_show_current_location;
+
+ gtk_stack_set_visible_child_name(stack->main, "asm_info");
+
+ ref = stack->asm_ref;
+ info = stack->asm_info;
+
+ /* Première partie : navigation */
+
+ if (info->reset)
+ {
+ label = GTK_LABEL(g_object_get_data(ref, "segment"));
+ gtk_label_set_text(label, NULL);
+
+ label = GTK_LABEL(g_object_get_data(ref, "phys"));
+ gtk_label_set_text(label, NULL);
+
+ label = GTK_LABEL(g_object_get_data(ref, "virt"));
+ gtk_label_set_text(label, NULL);
+
+ label = GTK_LABEL(g_object_get_data(ref, "offset"));
+ gtk_label_set_text(label, NULL);
+
+ }
+ else
+ {
+ label = GTK_LABEL(g_object_get_data(ref, "segment"));
+ gtk_label_set_text(label, info->segment);
+
+ snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys);
+
+ label = GTK_LABEL(g_object_get_data(ref, "phys"));
+ gtk_label_set_text(label, raw_pos);
+
+ snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt);
+
+ label = GTK_LABEL(g_object_get_data(ref, "virt"));
+ gtk_label_set_text(label, raw_pos);
+
+ label = GTK_LABEL(g_object_get_data(ref, "offset"));
+ gtk_label_set_text(label, info->symbol != NULL ? info->symbol : "");
+
+ }
+
+ /* Seconde partie : architecture */
+
+ if (info->reset || info->encoding == NULL || info->size == VMPA_NO_PHYSICAL)
+ {
+ label = GTK_LABEL(g_object_get_data(ref, "arch"));
+ gtk_label_set_text(label, NULL);
+
+ label = GTK_LABEL(g_object_get_data(ref, "size"));
+ gtk_label_set_text(label, NULL);
+
+ }
+ else
+ {
+ label = GTK_LABEL(g_object_get_data(ref, "arch"));
+ gtk_label_set_text(label, info->encoding);
+
+ if (info->size > 1)
+ asprintf(&content, "%" PRIu64 " %s", (uint64_t)info->size, _("bytes"));
+ else
+ asprintf(&content, "%" PRIu64 " %s", (uint64_t)info->size, _("byte"));
+
+ label = GTK_LABEL(g_object_get_data(ref, "size"));
+ gtk_label_set_text(label, content);
+
+ free(content);
+
+ }
+
+ return G_SOURCE_REMOVE;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* STATUT DES SUIVIS D'ACTIVITE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : info = informations à réinitialiser. *
+* *
+* Description : Supprime l'empreinte mémoire d'informations d'activité. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void reset_progress_info(progress_info *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_init(&info->access);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stack = composant global en cours de construction. *
+* *
+* Description : Construit une barre d'état pour un suivi d'activité. *
+* *
+* Retour : Composant GTK mis en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GtkWidget *build_progress_status_stack(GtkStatusStack *stack)
+{
+ GtkWidget *result; /* Support à retourner */
+ GObject *ref; /* Espace de référencements */
+ GtkWidget *progress; /* Barre de progression */
+ GtkWidget *label; /* Désignation de l'activité */
+
+ result = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_widget_show(result);
+
+ ref = G_OBJECT(result);
+
+ progress = gtk_progress_bar_new();
+ g_object_set_data(ref, "progress", progress);
+ gtk_widget_set_size_request(progress, PROGRESS_SIZE, -1);
+ gtk_widget_set_valign(progress, GTK_ALIGN_CENTER);
+ gtk_widget_show(progress);
+ gtk_box_pack_start(GTK_BOX(result), progress, FALSE, TRUE, 8);
+
+ label = qck_create_label(ref, "message", NULL);
+ gtk_box_pack_start(GTK_BOX(result), label, TRUE, TRUE, 0);
+
+ 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 */
+ progress_info *info; /* Informations à consulter */
+ size_t new; /* Indice de l'activité créée */
+
+ if (stack == NULL) return NO_ACTIVITY_ID;
+
+ info = stack->prog_info;
+
+ g_mutex_lock(&info->access);
+
+ result = ++info->generator;
+
+ new = info->count++;
+
+ info->statuses = (progress_status *)realloc(info->statuses,
+ info->count * sizeof(progress_status));
+
+ info->statuses[new].id = result;
+
+ /* Intitulé */
+
+ if (msg == NULL)
+ info->statuses[new].message = NULL;
+ else
+ info->statuses[new].message = strdup(msg);
+
+ /* Valeur */
+
+ info->statuses[new].current = 0;
+ info->statuses[new].max = max;
+ info->statuses[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. *
+* 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(GtkStatusStack *stack, activity_id_t id, unsigned long extra)
+{
+ progress_info *info; /* Informations à consulter */
+ size_t i; /* Boucle de parcours */
+
+ if (stack == NULL) return;
+
+ info = stack->prog_info;
+
+ g_mutex_lock(&info->access);
+
+ for (i = 0; i < info->count; i++)
+ if (info->statuses[i].id == id)
+ break;
+
+ assert(i < info->count);
+
+ info->statuses[i].max += extra;
+
+ g_mutex_unlock(&info->access);
+
+}
+
+
+/******************************************************************************
+* *
+* 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(GtkStatusStack *stack, activity_id_t id, const char *msg)
+{
+ progress_info *info; /* Informations à consulter */
+ size_t i; /* Boucle de parcours */
+ bool msg_changed; /* Changement d'intitulé */
+
+ if (stack == NULL) return;
+
+ info = stack->prog_info;
+
+ g_mutex_lock(&info->access);
+
+ for (i = 0; i < info->count; i++)
+ if (info->statuses[i].id == id)
+ break;
+
+ assert(i < info->count);
+
+ /* Intitulé */
+
+ if (info->statuses[i].message != NULL)
+ {
+ if (msg == NULL)
+ msg_changed = true;
+ else
+ msg_changed = (strcmp(info->statuses[i].message, msg) != 0);
+
+ free(info->statuses[i].message);
+
+ }
+ else
+ msg_changed = (msg != NULL);
+
+ if (msg == NULL)
+ info->statuses[i].message = NULL;
+ else
+ info->statuses[i].message = strdup(msg);
+
+ /* On n'actualise que le sommet de la pile */
+
+ if ((i + 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);
+
+ }
+
+ 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)
+{
+ progress_info *info; /* Informations à consulter */
+ size_t i; /* Boucle de parcours */
+ progress_status *status; /* Raccourci de confort */
+ double new; /* Nouvelle progression */
+
+ if (stack == NULL) return;
+
+ info = stack->prog_info;
+
+ g_mutex_lock(&info->access);
+
+ for (i = 0; i < info->count; i++)
+ if (info->statuses[i].id == id)
+ break;
+
+ assert(i < info->count);
+
+ status = &info->statuses[i];
+
+ /* Valeur */
+
+ status->current += inc;
+
+ new = (status->current * 1.0) / status->max;
+
+ /* On n'actualise que le sommet de la pile */
+
+ if ((i + 1) == info->count && (new - status->last_updated) > (1.0 / PROGRESS_SIZE))
+ {
+ 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);
+
+ }
+
+ 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)
+{
+ progress_info *info; /* Informations à consulter */
+ size_t i; /* Boucle de parcours */
+
+ if (stack == NULL) return;
+
+ info = stack->prog_info;
+
+ g_mutex_lock(&info->access);
+
+ for (i = 0; i < info->count; i++)
+ if (info->statuses[i].id == id)
+ break;
+
+ assert(i < info->count);
+
+ if (info->tag != 0)
+ g_source_remove(info->tag);
+
+ if (info->statuses[i].message != NULL)
+ free(info->statuses[i].message);
+
+ if (info->count == 1)
+ {
+ free(info->statuses);
+ info->statuses = NULL;
+ }
+ else
+ {
+ memmove(&info->statuses[i], &info->statuses[i + 1],
+ (info->count - i - 1) * sizeof(progress_status));
+
+ info->statuses = (progress_status *)realloc(info->statuses,
+ (info->count - 1) * sizeof(progress_status));
+
+ }
+
+ info->count--;
+
+ if (info->count == 0)
+ {
+ info->tag = 0;
+ g_idle_add(stack->def_source, stack);
+ }
+ else
+ info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
+
+ 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)
+{
+ GObject *ref; /* Espace de référencements */
+ progress_info *info; /* Informations à consulter */
+ progress_status *last; /* Dernier statut à traiter */
+ GtkProgressBar *progress; /* Barre de progression */
+ GtkLabel *label; /* Désignation de l'activité */
+
+ if (!g_source_is_destroyed(g_main_current_source()))
+ {
+ gtk_stack_set_visible_child_name(stack->main, "prog_info");
+
+ ref = stack->prog_ref;
+ info = stack->prog_info;
+
+ g_mutex_lock(&info->access);
+
+ info->tag = 0;
+
+ if (info->count > 0)
+ {
+ last = &info->statuses[info->count - 1];
+
+ progress = GTK_PROGRESS_BAR(g_object_get_data(ref, "progress"));
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), (last->current * 1.0) / last->max);
+
+ label = GTK_LABEL(g_object_get_data(ref, "message"));
+ gtk_label_set_text(label, last->message);
+
+ }
+
+ g_mutex_unlock(&info->access);
+
+ }
+
+ return G_SOURCE_REMOVE;
+
+}