From ec71086f6dcf69241b586f1e48e7d656dda11a6c Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 9 Sep 2024 10:41:59 +0200
Subject: Restore the status bar at the bottom of the main window.

---
 src/gtkext/Makefile.am       |   7 +-
 src/gtkext/gresource.xml     |   1 +
 src/gtkext/statusstack-int.h |  94 +++++
 src/gtkext/statusstack.c     | 904 +++++++++++++++++++++----------------------
 src/gtkext/statusstack.h     |  46 +--
 src/gtkext/statusstack.ui    | 193 +++++++++
 src/gui/window.c             |   3 +
 src/gui/window.ui            |  13 +
 8 files changed, 773 insertions(+), 488 deletions(-)
 create mode 100644 src/gtkext/statusstack-int.h
 create mode 100644 src/gtkext/statusstack.ui

diff --git a/src/gtkext/Makefile.am b/src/gtkext/Makefile.am
index 9f32c7c..2a2738a 100644
--- a/src/gtkext/Makefile.am
+++ b/src/gtkext/Makefile.am
@@ -14,7 +14,6 @@ libgtkext_la_SOURCES =						\
 	gtkdockable-int.h						\
 	gtkdockable.h gtkdockable.c				\
 	gtkgraphdisplay.h gtkgraphdisplay.c		\
-	statusstack.h statusstack.c		\
 	hexdisplay.h hexdisplay.c				\
 	named-int.h								\
 	named.h named.c							\
@@ -29,7 +28,8 @@ libgtkext_la_CFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS)
 
 RES_FILES =									\
 	hexview.css								\
-	hexview.ui
+	hexview.ui								\
+	statusstack.ui
 
 libgtkext4_la_SOURCES =						\
 	area-int.h								\
@@ -47,7 +47,8 @@ libgtkext4_la_SOURCES =						\
 	hexview.h hexview.c						\
 	panel-int.h								\
 	panel.h panel.c							\
-	resources.h resources.c
+	resources.h resources.c					\
+	statusstack.h statusstack.c
 
 libgtkext4_la_CFLAGS = $(LIBGTK4_CFLAGS)
 
diff --git a/src/gtkext/gresource.xml b/src/gtkext/gresource.xml
index 225b2a4..3bfd8c5 100644
--- a/src/gtkext/gresource.xml
+++ b/src/gtkext/gresource.xml
@@ -3,5 +3,6 @@
     <gresource prefix="/re/chrysalide/framework/gtkext">
         <file compressed="true">hexview.css</file>
         <file compressed="true">hexview.ui</file>
+        <file compressed="true">statusstack.ui</file>
     </gresource>
 </gresources>
diff --git a/src/gtkext/statusstack-int.h b/src/gtkext/statusstack-int.h
new file mode 100644
index 0000000..facc5af
--- /dev/null
+++ b/src/gtkext/statusstack-int.h
@@ -0,0 +1,94 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * statusstack-int.h - définitions internes pour l'empilement d'informations de statut
+ *
+ * Copyright (C) 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GTKEXT_STATUSSTACK_INT_H
+#define _GTKEXT_STATUSSTACK_INT_H
+
+
+#include "statusstack.h"
+
+
+
+/* Navigation au sein d'assemblage */
+typedef struct _navigation_info_t navigation_info_t;
+
+/* Mémorisation des progressions au sein d'activités */
+typedef struct _activity_info_t activity_info_t;
+
+
+#define NETWORK_UPDATE_COUNT 10
+#define NETWORK_UPDATE_INTERVAL (1000 / NETWORK_UPDATE_COUNT)
+
+
+/* Gestion de barre de statut adaptable (instance) */
+struct _GtkStatusStack
+{
+    GtkBox parent;                          /* A laisser en premier        */
+
+    GtkStack *main;                         /* Pile d'informations associée*/
+
+    GSourceFunc def_source;                 /* Appel en fin d'activité     */
+
+    /* Navigation */
+
+    GtkLabel *nav_segment;                  /* Désignation du segment      */
+    GtkLabel *nav_phys;                     /* Adresse physique            */
+    GtkLabel *nav_virt;                     /* Adresse virtuelle           */
+    GtkLabel *nav_offset;                   /* Position dans le binaire    */
+    GtkLabel *nav_format;                   /* Architecture du binaire     */
+    GtkLabel *nav_details;                  /* Détails sur l'architecture  */
+    GtkEntry *zoom;                         /* Degré de zoom courant       */
+
+    navigation_info_t *nav_info;            /* Informations brutes liées   */
+
+    /* Activité */
+
+    GtkLabel *activity_message;             /* Nature de l'activité        */
+    GtkProgressBar *activity_progress;      /* Barre de progression        */
+
+    activity_info_t *activity_info;         /* Informations brutes liées   */
+
+    /* Tronc commun */
+
+    GtkLabel *net_recv_speed;               /* Débit en réception          */
+    GtkLabel *net_send_speed;               /* Débit en émission           */
+
+    size_t last_bytes_received[NETWORK_UPDATE_COUNT]; /* Octets reçus      */
+    size_t last_bytes_sent[NETWORK_UPDATE_COUNT]; /* Octets émis           */
+    gint64 last_timestamps[NETWORK_UPDATE_COUNT]; /* Dates des mesures     */
+    size_t next_index;                      /* Indice d'écriture           */
+
+    guint network_update_tag;               /* Identifiant de mise à jour  */
+
+};
+
+/* Gestion de barre de statut adaptable (classe) */
+struct _GtkStatusStackClass
+{
+    GtkBoxClass parent;                     /* A laisser en premier        */
+
+};
+
+
+
+#endif  /* _GTKEXT_STATUSSTACK_INT_H */
diff --git a/src/gtkext/statusstack.c b/src/gtkext/statusstack.c
index fe4e4d5..0d8ef62 100644
--- a/src/gtkext/statusstack.c
+++ b/src/gtkext/statusstack.c
@@ -1,8 +1,8 @@
 
 /* Chrysalide - Outil d'analyse de fichiers binaires
- * gtkstatusstack.c - empilement d'informations de statut
+ * statusstack.c - empilement d'informations de statut
  *
- * Copyright (C) 2015-2019 Cyrille Bagard
+ * Copyright (C) 2015-2024 Cyrille Bagard
  *
  *  This file is part of Chrysalide.
  *
@@ -21,11 +21,10 @@
  */
 
 
-#include "gtkstatusstack.h"
+#include "statusstack.h"
 
 
 #include <assert.h>
-#include <inttypes.h>
 #include <malloc.h>
 #include <string.h>
 
@@ -33,45 +32,13 @@
 #include <i18n.h>
 
 
-#include "easygtk.h"
-#include "../gui/agroup.h"
-#include "../format/format.h"
+#include "helpers.h"
+#include "statusstack-int.h"
+#include "../core/global.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        */
-
-};
+/* -------------------------- GESTION GENERALE DES STATUTS -------------------------- */
 
 
 /* Initialise la classe des barres de statut améliorées. */
@@ -86,45 +53,43 @@ 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 _assembly_info
+struct _navigation_info_t
 {
-    bool reset;                             /* Réinitialisation            */
+    char *segment;                          /* Segment d'appartenance      */
 
     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     */
+    char *format;                           /* Architecture et format      */
+    char *details;                          /* Encodage de l'instruction   */
 
 };
 
 
-/* Supprime l'empreinte mémoire d'informations d'assemblage. */
-static void reset_assembly_info(assembly_info *);
+/* Met en place le suivi d'informations de navigation. */
+static void init_navigation_info(navigation_info_t *);
 
-/* Construit une barre d'état pour language d'assemblage. */
-static GtkWidget *build_assembly_status_stack(GtkStatusStack *);
+/* Supprime l'empreinte mémoire d'informations de navigation. */
+static void fini_navigation_info(navigation_info_t *);
 
-/* Réagit à un redimensionnement de la barre de désassemblage. */
-static void on_size_allocate_for_asm_status(GtkWidget *, GdkRectangle *, GObject *);
+/* 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 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 *);
+static void gtk_status_stack_on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GtkStatusStack *);
 
 
 
@@ -132,7 +97,7 @@ static gboolean gtk_status_stack_show_current_location(GtkStatusStack *);
 
 
 /* Informations de progression */
-typedef struct _progress_status
+typedef struct _activity_status_t
 {
     activity_id_t id;                       /* Identifiant unique          */
 
@@ -143,30 +108,32 @@ typedef struct _progress_status
 
     double last_updated;                    /* Dernière valeur poussée     */
 
-} progress_status;
+} activity_status_t;
+
 
-/* Mémorisation des progressions */
-struct _progress_info
+/* 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      */
 
-    progress_status *statuses;              /* Statuts de progression      */
+    activity_status_t *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
-
+/* 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 reset_progress_info(progress_info *);
+static void fini_activity_info(activity_info_t *);
 
-/* Construit une barre d'état pour un suivi d'activité. */
-static GtkWidget *build_progress_status_stack(GtkStatusStack *);
+/* 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 *);
@@ -174,17 +141,17 @@ static gboolean gtk_status_stack_show_current_activity(GtkStatusStack *);
 
 
 /* ---------------------------------------------------------------------------------- */
-/*                           GESTION EXTERIEURE DE LA BARRE                           */
+/*                            GESTION GENERALE DES STATUTS                            */
 /* ---------------------------------------------------------------------------------- */
 
 
-/* Détermine le type de la barre de statut améliorée. */
-G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX)
+/* Détermine le type du composant d'affichage générique. */
+G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX);
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : klass = classe GTK à initialiser.                            *
+*  Paramètres  : class = classe GTK à initialiser.                            *
 *                                                                             *
 *  Description : Initialise la classe des barres de statut améliorées.        *
 *                                                                             *
@@ -194,21 +161,44 @@ G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX)
 *                                                                             *
 ******************************************************************************/
 
-static void gtk_status_stack_class_init(GtkStatusStackClass *klass)
+static void gtk_status_stack_class_init(GtkStatusStackClass *class)
 {
-    GObjectClass *object;                   /* Autre version de la classe  */
+    GObjectClass *object;                   /* Plus haut niveau équivalent */
+    GtkWidgetClass *widget;                 /* Classe de haut niveau       */
 
-    object = G_OBJECT_CLASS(klass);
+    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 = instance GTK à initialiser.                          *
+*  Paramètres  : stack = composant GTK à initialiser.                         *
 *                                                                             *
 *  Description : Initialise une instance de barre de statut améliorée.        *
 *                                                                             *
@@ -220,40 +210,27 @@ static void gtk_status_stack_class_init(GtkStatusStackClass *klass)
 
 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);
+    gtk_widget_init_template(GTK_WIDGET(stack));
 
-    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));
+    stack->def_source = NULL;
 
-    reset_assembly_info(stack->asm_info);
+    stack->nav_info = calloc(1, sizeof(navigation_info_t));
+    init_navigation_info(stack->nav_info);
 
-    layer = build_progress_status_stack(stack);
-    gtk_stack_add_named(stack->main, layer, "prog_info");
+    stack->activity_info = calloc(1, sizeof(activity_info_t));
+    init_activity_info(stack->activity_info);
 
-    stack->prog_ref = G_OBJECT(layer);
-    stack->prog_info = (progress_info *)calloc(1, sizeof(progress_info));
+    stack->next_index = 0;
 
-    reset_progress_info(stack->prog_info);
-
-    gtk_status_stack_reset_current_location(stack);
+    stack->network_update_tag = g_timeout_add(NETWORK_UPDATE_INTERVAL,
+                                              G_SOURCE_FUNC(gtk_status_stack_update_network_stats), stack);
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : view = instance d'objet GLib à traiter.                      *
+*  Paramètres  : stack = instance d'objet GLib à traiter.                     *
 *                                                                             *
 *  Description : Supprime toutes les références externes.                     *
 *                                                                             *
@@ -265,6 +242,10 @@ static void gtk_status_stack_init(GtkStatusStack *stack)
 
 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));
 
 }
@@ -272,7 +253,7 @@ static void gtk_status_stack_dispose(GtkStatusStack *stack)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : view = instance d'objet GLib à traiter.                      *
+*  Paramètres  : stack = instance d'objet GLib à traiter.                     *
 *                                                                             *
 *  Description : Procède à la libération totale de la mémoire.                *
 *                                                                             *
@@ -284,11 +265,11 @@ static void gtk_status_stack_dispose(GtkStatusStack *stack)
 
 static void gtk_status_stack_finalize(GtkStatusStack *stack)
 {
-    reset_assembly_info(stack->asm_info);
-    free(stack->asm_info);
+    fini_navigation_info(stack->nav_info);
+    free(stack->nav_info);
 
-    reset_progress_info(stack->prog_info);
-    free(stack->prog_info);
+    fini_activity_info(stack->activity_info);
+    free(stack->activity_info);
 
     G_OBJECT_CLASS(gtk_status_stack_parent_class)->finalize(G_OBJECT(stack));
 
@@ -318,17 +299,11 @@ GtkStatusStack *gtk_status_stack_new(void)
 }
 
 
-
-/* ---------------------------------------------------------------------------------- */
-/*                      STATUT DES INFORMATIONS DE DESASSEMBLAGE                      */
-/* ---------------------------------------------------------------------------------- */
-
-
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : info = informations à réinitialiser.                         *
+*  Paramètres  : stack = barre de statut à actualiser.                        *
 *                                                                             *
-*  Description : Supprime l'empreinte mémoire d'informations d'assemblage.    *
+*  Description : Réinitialise la barre de statut à son stade par défaut.      *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -336,110 +311,146 @@ GtkStatusStack *gtk_status_stack_new(void)
 *                                                                             *
 ******************************************************************************/
 
-static void reset_assembly_info(assembly_info *info)
+void gtk_status_stack_reset(GtkStatusStack *stack)
 {
-    info->reset = true;
-
-    if (info->segment != NULL)
-    {
-        free(info->segment);
-        info->segment = NULL;
-    }
+    gtk_stack_set_visible_child_name(stack->main, "default");
 
-    if (info->symbol != NULL)
-    {
-        free(info->symbol);
-        info->symbol = NULL;
-    }
+    stack->def_source = NULL;
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : stack = composant global en cours de construction.           *
+*  Paramètres  : stack = barre de statut à actualiser.                        *
 *                                                                             *
-*  Description : Construit une barre d'état pour language d'assemblage.       *
+*  Description : Met à jour dans la barre les débits réseau observés.         *
 *                                                                             *
-*  Retour      : Composant GTK mis en place.                                  *
+*  Retour      : G_SOURCE_CONTINUE pour poursuivre les mises à jour.          *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static GtkWidget *build_assembly_status_stack(GtkStatusStack *stack)
+static gboolean gtk_status_stack_update_network_stats(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   */
+    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           */
 
-    result = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
-    gtk_widget_show(result);
+    const char *units[] = { _("b/s"), _("kb/s"), _("Mb/s"), _("Gb/s"), _("Tb/s") };
 
-    ref = G_OBJECT(result);
+    result = G_SOURCE_CONTINUE;
 
-    g_signal_connect(result, "size-allocate", G_CALLBACK(on_size_allocate_for_asm_status), ref);
+    /* Mémorisation des données */
 
-    /* Première partie : navigation */
+    timestamp = g_get_monotonic_time();
 
-    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 16);
-    gtk_widget_show(hbox);
-    gtk_box_pack_start(GTK_BOX(result), hbox, TRUE, TRUE, 8);
+    get_network_stats(&received, &sent);
 
-    label = qck_create_label(ref, "segment", NULL);
-    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+    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;
 
-    label = qck_create_label(ref, "phys", NULL);
-    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+        stack->next_index++;
 
-    label = qck_create_label(ref, "virt", NULL);
-    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+    }
+    else
+    {
+        memcpy(stack->last_bytes_received, stack->last_bytes_received + 1,
+               (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t));
 
-    label = qck_create_label(ref, "offset", NULL);
-    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+        memcpy(stack->last_bytes_sent, stack->last_bytes_sent + 1,
+               (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t));
 
-    /* Seconde partie : architecture */
+        memcpy(stack->last_timestamps, stack->last_timestamps + 1,
+               (NETWORK_UPDATE_COUNT - 1) * sizeof(gint64));
 
-    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);
+        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;
 
-    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);
+    if (stack->next_index < NETWORK_UPDATE_COUNT)
+        goto done;
 
-    /* Troisième partie : affichage */
+    diff_time = stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] - stack->last_timestamps[0];
 
-    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
-    gtk_widget_show(hbox);
-    gtk_box_pack_start(GTK_BOX(result), hbox, FALSE, FALSE, 8);
+    /* Débit de réception */
 
-    zoom = qck_create_entry(ref, "zoom", "100%");
-    gtk_entry_set_icon_from_icon_name(GTK_ENTRY(zoom), GTK_ENTRY_ICON_SECONDARY, "go-up-symbolic");
+    diff_bytes = stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_received[0];
 
-    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);
+    speed = (diff_bytes * 1000000) / diff_time;
 
-    gtk_box_pack_start(GTK_BOX(hbox), zoom, FALSE, TRUE, 0);
+    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  : widget     = composant graphique qui vient d'évoluer.        *
-*                allocation = espace réservé pour le composant visé.          *
-*                ref        = espace de référencement global.                 *
+*  Paramètres  : info = informations à initialiser.                           *
 *                                                                             *
-*  Description : Réagit à un redimensionnement de la barre de désassemblage.  *
+*  Description : Met en place le suivi d'informations de navigation.          *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -447,33 +458,23 @@ static GtkWidget *build_assembly_status_stack(GtkStatusStack *stack)
 *                                                                             *
 ******************************************************************************/
 
-static void on_size_allocate_for_asm_status(GtkWidget *widget, GdkRectangle *allocation, GObject *ref)
+static void init_navigation_info(navigation_info_t *info)
 {
-    GtkWidget *hbox;                        /* Sous-division horizontale   */
+    info->segment = NULL;
 
-    hbox = GTK_WIDGET(g_object_get_data(ref, "arch_box"));
+    info->symbol = NULL;
 
-    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));
+    info->format = NULL;
+    info->details = NULL;
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  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.       *
+*  Paramètres  : info = informations à libérer de la mémoire.                 *
 *                                                                             *
-*  Description : Réagit à un clic sur l'icône de zoom.                        *
+*  Description : Supprime l'empreinte mémoire d'informations de navigation.   *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -481,31 +482,31 @@ static void on_size_allocate_for_asm_status(GtkWidget *widget, GdkRectangle *all
 *                                                                             *
 ******************************************************************************/
 
-static void on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEventButton *event, GtkStatusStack *stack)
+static void fini_navigation_info(navigation_info_t *info)
 {
-    GtkWidget *popup;                       /* Popup à faire surgir        */
-    GdkRectangle rect;                      /* Zone précise à cibler       */
-
-    if (event->button != GDK_BUTTON_PRIMARY)
-        return;
+    if (info->segment != NULL)
+        free(info->segment);
 
-    popup = gtk_popover_new(GTK_WIDGET(entry));
+    if (info->symbol != NULL)
+        free(info->symbol);
 
-    gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect);
-    gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect);
+    if (info->format != NULL)
+        free(info->format);
 
-    gtk_widget_show(popup);
+    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.     *
-*                encoding = encodage d'une éventuelle instruction ou NULL.    *
+*  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.  *
 *                                                                             *
@@ -515,51 +516,50 @@ static void on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, G
 *                                                                             *
 ******************************************************************************/
 
-void gtk_status_stack_update_current_location(GtkStatusStack *stack, const mrange_t *range, const char *segment, const char *symbol, const char *encoding)
+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)
 {
-    assembly_info *info;                    /* Informations à constituer   */
+    navigation_info_t *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 ? */
+    info = stack->nav_info;
 
-    addr = get_mrange_addr(range);
-    size = get_mrange_length(range);
-
-    if (cmp_mrange(&info->current, range) == 0
-        && info->size == size
-        && info->encoding == encoding)
+    if (cmp_mrange(&info->current, range) == 0)
         goto useless;
 
-    /* Réinitialisation */
-
-    reset_assembly_info(info);
-
-    copy_mrange(&info->current, range);
-
     /* Zone d'appartenance */
 
-    info->segment = strdup(segment);
+    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);
 
-    info->encoding = encoding;
-    info->size = size;
-
     /* Symbole concerné */
 
     if (symbol != NULL)
         info->symbol = strdup(symbol);
+    else
+        info->symbol = NULL;
 
-    /* Nettoyage et conclusion */
+    /* Architecture & format */
 
-    info->reset = false;
+    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);
 
@@ -572,34 +572,9 @@ void gtk_status_stack_update_current_location(GtkStatusStack *stack, const mrang
 
 /******************************************************************************
 *                                                                             *
-*  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".    *
+*  Description : S'assure de l'affichage à jour de la partie "navigation".    *
 *                                                                             *
 *  Retour      : G_SOURCE_REMOVE pour une exécution unique.                   *
 *                                                                             *
@@ -609,98 +584,104 @@ void gtk_status_stack_reset_current_location(GtkStatusStack *stack)
 
 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      */
+    navigation_info_t *info;                /* Informations à constituer   */
     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");
+    gtk_stack_set_visible_child_name(stack->main, "navigation");
 
-    ref = stack->asm_ref;
-    info = stack->asm_info;
+    info = stack->nav_info;
 
     /* Première partie : navigation */
 
-    if (info->reset)
-    {
-        label = GTK_LABEL(g_object_get_data(ref, "segment"));
-        gtk_label_set_text(label, NULL);
+    gtk_label_set_text(stack->nav_segment, info->segment != NULL ? info->segment : "");
 
-        label = GTK_LABEL(g_object_get_data(ref, "phys"));
-        gtk_label_set_text(label, NULL);
+    snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys);
 
-        label = GTK_LABEL(g_object_get_data(ref, "virt"));
-        gtk_label_set_text(label, NULL);
+    gtk_label_set_text(stack->nav_phys, raw_pos);
 
-        label = GTK_LABEL(g_object_get_data(ref, "offset"));
-        gtk_label_set_text(label, NULL);
+    snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt);
 
-    }
-    else
-    {
-        label = GTK_LABEL(g_object_get_data(ref, "segment"));
-        gtk_label_set_text(label, info->segment);
+    gtk_label_set_text(stack->nav_virt, raw_pos);
 
-        snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys);
+    gtk_label_set_text(stack->nav_offset, info->symbol != NULL ? info->symbol : "");
 
-        label = GTK_LABEL(g_object_get_data(ref, "phys"));
-        gtk_label_set_text(label, raw_pos);
+    /* Seconde partie : format & architecture */
 
-        snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt);
+    gtk_label_set_text(stack->nav_format, info->format);
 
-        label = GTK_LABEL(g_object_get_data(ref, "virt"));
-        gtk_label_set_text(label, raw_pos);
+    gtk_label_set_text(stack->nav_details, info->details != NULL ? info->details : "");
 
-        label = GTK_LABEL(g_object_get_data(ref, "offset"));
-        gtk_label_set_text(label, info->symbol != NULL ? info->symbol : "");
+    return G_SOURCE_REMOVE;
 
-    }
+}
 
-    /* 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);
+/******************************************************************************
+*                                                                             *
+*  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   : -                                                            *
+*                                                                             *
+******************************************************************************/
 
-        label = GTK_LABEL(g_object_get_data(ref, "size"));
-        gtk_label_set_text(label, NULL);
+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       */
 
-    }
-    else
-    {
-        label = GTK_LABEL(g_object_get_data(ref, "arch"));
-        gtk_label_set_text(label, info->encoding);
+    popup = gtk_popover_new();
 
-        if (info->size > 1)
-            asprintf(&content, "%" PRIu64 " %s", (uint64_t)info->size, _("bytes"));
-        else
-            asprintf(&content, "%" PRIu64 " %s", (uint64_t)info->size, _("byte"));
+    gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect);
+    gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect);
 
-        label = GTK_LABEL(g_object_get_data(ref, "size"));
-        gtk_label_set_text(label, content);
+    gtk_widget_show(popup);
+#endif
+}
 
-        free(content);
 
-    }
+/* ---------------------------------------------------------------------------------- */
+/*                            STATUT DES SUIVIS D'ACTIVITE                            */
+/* ---------------------------------------------------------------------------------- */
 
-    return G_SOURCE_REMOVE;
 
-}
+/******************************************************************************
+*                                                                             *
+*  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;
 
-/* ---------------------------------------------------------------------------------- */
-/*                            STATUT DES SUIVIS D'ACTIVITE                            */
-/* ---------------------------------------------------------------------------------- */
+    info->statuses = NULL;
+    info->count = 0;
+
+    info->tag = 0;
+
+}
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : info = informations à réinitialiser.                         *
+*  Paramètres  : info = informations à libérer de la mémoire.                 *
 *                                                                             *
 *  Description : Supprime l'empreinte mémoire d'informations d'activité.      *
 *                                                                             *
@@ -710,7 +691,7 @@ static gboolean gtk_status_stack_show_current_location(GtkStatusStack *stack)
 *                                                                             *
 ******************************************************************************/
 
-static void reset_progress_info(progress_info *info)
+static void fini_activity_info(activity_info_t *info)
 {
     size_t i;                               /* Boucle de parcours          */
 
@@ -733,44 +714,39 @@ static void reset_progress_info(progress_info *info)
 
     info->count = 0;
 
-    g_mutex_init(&info->access);
+    g_mutex_clear(&info->access);
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : stack = composant global en cours de construction.           *
+*  Paramètres  : info = informations relatives aux activités à consulter.     *
+*                id   = identifiant de l'activité à cibler.                   *
 *                                                                             *
-*  Description : Construit une barre d'état pour un suivi d'activité.         *
+*  Description : Recherche les indications de statut d'une activité donnée.   *
 *                                                                             *
-*  Retour      : Composant GTK mis en place.                                  *
+*  Retour      : Structure d'encadrement trouvée ou NULL.                     *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static GtkWidget *build_progress_status_stack(GtkStatusStack *stack)
+static activity_status_t *find_activity_status_by_id(activity_info_t *info, activity_id_t id)
 {
-    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);
+    activity_status_t *result;              /* Statut trouvé à renvoyer    */
+    size_t i;                               /* Boucle de parcours          */
 
-    ref = G_OBJECT(result);
+    result = NULL;
 
-    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);
+    assert(!g_mutex_trylock(&info->access));
 
-    label = qck_create_label(ref, "message", NULL);
-    gtk_box_pack_start(GTK_BOX(result), label, TRUE, TRUE, 0);
+    for (i = 0; i < info->count; i++)
+        if (info->statuses[i].id == id)
+        {
+            result = info->statuses + i;
+            break;
+        }
 
     return result;
 
@@ -794,36 +770,43 @@ static GtkWidget *build_progress_status_stack(GtkStatusStack *stack)
 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;
+    activity_info_t *info;                  /* Informations à consulter    */
+    activity_status_t *new;                 /* Nouveau suivi d'activité    */
 
-    info = stack->prog_info;
+    info = stack->activity_info;
 
     g_mutex_lock(&info->access);
 
-    result = ++info->generator;
+    while (1)
+    {
+        result = ++info->generator;
+
+        if (find_activity_status_by_id(info, result) == NULL)
+            break;
+
+    }
+    while (0);
 
-    new = info->count++;
+    info->statuses = realloc(info->statuses, ++info->count * sizeof(activity_status_t));
 
-    info->statuses = (progress_status *)realloc(info->statuses,
-                                                info->count * sizeof(progress_status));
+    new = info->statuses + info->count - 1;
 
-    info->statuses[new].id = result;
+    /* Identifiant */
+
+    new->id = result;
 
     /* Intitulé */
 
     if (msg == NULL)
-        info->statuses[new].message = NULL;
+        new->message = NULL;
     else
-        info->statuses[new].message = strdup(msg);
+        new->message = strdup(msg);
 
     /* Valeur */
 
-    info->statuses[new].current = 0;
-    info->statuses[new].max = max;
-    info->statuses[new].last_updated = 0;
+    new->current = 0;
+    new->max = max;
+    new->last_updated = 0;
 
     /* Actualisation */
 
@@ -843,9 +826,9 @@ activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *m
 *                                                                             *
 *  Paramètres  : stack = barre de statut à actualiser.                        *
 *                id    = identifiant de l'activité à cibler.                  *
-*                extra = nouvelle échéance supplémentaire des traitements.    *
+*                msg   = nouveau message de statut à copier.                  *
 *                                                                             *
-*  Description : Etend la portée des travaux d'une nouvelle activité.         *
+*  Description : Actualise les informations concernant une activité.          *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -853,24 +836,55 @@ activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *m
 *                                                                             *
 ******************************************************************************/
 
-void gtk_status_stack_extend_activity(GtkStatusStack *stack, activity_id_t id, unsigned long extra)
+void gtk_status_stack_update_activity_message(GtkStatusStack *stack, activity_id_t id, const char *msg)
 {
-    progress_info *info;                    /* Informations à consulter    */
-    size_t i;                               /* Boucle de parcours          */
-
-    if (stack == NULL) return;
+    activity_info_t *info;                  /* Informations à consulter    */
+    activity_status_t *status;              /* Suivi d'activité à traiter  */
+    bool msg_changed;                       /* Changement d'intitulé       */
 
-    info = stack->prog_info;
+    info = stack->activity_info;
 
     g_mutex_lock(&info->access);
 
-    for (i = 0; i < info->count; i++)
-        if (info->statuses[i].id == id)
-            break;
+    status = find_activity_status_by_id(info, id);
 
-    assert(i < info->count);
+    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);
 
-    info->statuses[i].max += extra;
+        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);
 
@@ -881,9 +895,9 @@ void gtk_status_stack_extend_activity(GtkStatusStack *stack, activity_id_t id, u
 *                                                                             *
 *  Paramètres  : stack = barre de statut à actualiser.                        *
 *                id    = identifiant de l'activité à cibler.                  *
-*                msg   = nouveau message de statut à copier.                  *
+*                inc   = nouvelle valeur pour une progression donnée.         *
 *                                                                             *
-*  Description : Actualise les informations concernant une activité.          *
+*  Description : Actualise la progression d'une activité.                     *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -891,48 +905,35 @@ void gtk_status_stack_extend_activity(GtkStatusStack *stack, activity_id_t id, u
 *                                                                             *
 ******************************************************************************/
 
-void gtk_status_stack_update_activity(GtkStatusStack *stack, activity_id_t id, const char *msg)
+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          */
-    bool msg_changed;                       /* Changement d'intitulé       */
-
-    if (stack == NULL) return;
+    activity_info_t *info;                  /* Informations à consulter    */
+    activity_status_t *status;              /* Suivi d'activité à traiter  */
+    double new;                             /* Nouvelle progression        */
 
-    info = stack->prog_info;
+    info = stack->activity_info;
 
     g_mutex_lock(&info->access);
 
-    for (i = 0; i < info->count; i++)
-        if (info->statuses[i].id == id)
-            break;
+    status = find_activity_status_by_id(info, id);
 
-    assert(i < info->count);
+    assert(status != NULL);
 
-    /* Intitulé */
+    if (status == NULL)
+        goto exit;
 
-    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);
+    /* Valeur */
 
-    }
-    else
-        msg_changed = (msg != NULL);
+    status->current += inc;
 
-    if (msg == NULL)
-        info->statuses[i].message = NULL;
-    else
-        info->statuses[i].message = strdup(msg);
+    new = (status->current * 1.0) / status->max;
 
     /* On n'actualise que le sommet de la pile */
 
-    if ((i + 1) == info->count && msg_changed)
+    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);
 
@@ -940,6 +941,8 @@ void gtk_status_stack_update_activity(GtkStatusStack *stack, activity_id_t id, c
 
     }
 
+ exit:
+
     g_mutex_unlock(&info->access);
 
 }
@@ -949,9 +952,9 @@ void gtk_status_stack_update_activity(GtkStatusStack *stack, activity_id_t id, c
 *                                                                             *
 *  Paramètres  : stack = barre de statut à actualiser.                        *
 *                id    = identifiant de l'activité à cibler.                  *
-*                inc   = nouvelle valeur pour une progression donnée.         *
+*                extra = nouvelle échéance supplémentaire des traitements.    *
 *                                                                             *
-*  Description : Actualise la progression d'une activité.                     *
+*  Description : Etend la portée des travaux d'une nouvelle activité.         *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -959,39 +962,30 @@ void gtk_status_stack_update_activity(GtkStatusStack *stack, activity_id_t id, c
 *                                                                             *
 ******************************************************************************/
 
-void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t id, unsigned long inc)
+void gtk_status_stack_extend_activity_max(GtkStatusStack *stack, activity_id_t id, unsigned long extra)
 {
-    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;
+    activity_info_t *info;                  /* Informations à consulter    */
+    activity_status_t *status;              /* Suivi d'activité à traiter  */
 
-    info = stack->prog_info;
+    info = stack->activity_info;
 
     g_mutex_lock(&info->access);
 
-    for (i = 0; i < info->count; i++)
-        if (info->statuses[i].id == id)
-            break;
+    status = find_activity_status_by_id(info, id);
 
-    assert(i < info->count);
+    assert(status != NULL);
 
-    status = &info->statuses[i];
+    if (status == NULL)
+        goto exit;
 
     /* Valeur */
 
-    status->current += inc;
-
-    new = (status->current * 1.0) / status->max;
+    status->max += extra;
 
     /* On n'actualise que le sommet de la pile */
 
-    if ((i + 1) == info->count && (new - status->last_updated) > (1.0 / PROGRESS_SIZE))
+    if ((status - info->statuses + 1) == info->count)
     {
-        status->last_updated = new;
-
         if (info->tag != 0)
             g_source_remove(info->tag);
 
@@ -999,6 +993,8 @@ void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t
 
     }
 
+ exit:
+
     g_mutex_unlock(&info->access);
 
 }
@@ -1018,52 +1014,54 @@ void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t
 
 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;
+    activity_info_t *info;                  /* Informations à consulter    */
+    activity_status_t *status;              /* Suivi d'activité à traiter  */
+    bool is_last;                           /* Dernière position ?         */
 
-    info = stack->prog_info;
+    info = stack->activity_info;
 
     g_mutex_lock(&info->access);
 
-    for (i = 0; i < info->count; i++)
-        if (info->statuses[i].id == id)
-            break;
+    status = find_activity_status_by_id(info, id);
 
-    assert(i < info->count);
+    assert(status != NULL);
 
-    if (info->tag != 0)
-        g_source_remove(info->tag);
+    if (status == NULL)
+        goto exit;
+
+    is_last = ((status - info->statuses + 1) == info->count);
 
-    if (info->statuses[i].message != NULL)
-        free(info->statuses[i].message);
+    /* Suppression des données */
 
-    if (info->count == 1)
+    if (is_last)
     {
-        free(info->statuses);
-        info->statuses = NULL;
+        if (info->tag != 0)
+            g_source_remove(info->tag);
     }
-    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));
+    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->count--;
+    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
+    else if (is_last)
         info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
 
+ exit:
+
     g_mutex_unlock(&info->access);
 
 }
@@ -1083,39 +1081,33 @@ void gtk_status_stack_remove_activity(GtkStatusStack *stack, activity_id_t id)
 
 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é   */
+    activity_info_t *info;                  /* Informations à consulter    */
+    activity_status_t *last;                /* Dernier statut à traiter    */
 
-    if (!g_source_is_destroyed(g_main_current_source()))
-    {
-        gtk_stack_set_visible_child_name(stack->main, "prog_info");
+    info = stack->activity_info;
 
-        ref = stack->prog_ref;
-        info = stack->prog_info;
-
-        g_mutex_lock(&info->access);
-
-        info->tag = 0;
+    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];
 
-            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);
+            gtk_label_set_text(stack->activity_message, last->message);
 
-            label = GTK_LABEL(g_object_get_data(ref, "message"));
-            gtk_label_set_text(label, last->message);
+            gtk_progress_bar_set_fraction(stack->activity_progress, (last->current * 1.0) / last->max);
 
         }
 
-        g_mutex_unlock(&info->access);
+        info->tag = 0;
 
     }
 
+    g_mutex_unlock(&info->access);
+
     return G_SOURCE_REMOVE;
 
 }
diff --git a/src/gtkext/statusstack.h b/src/gtkext/statusstack.h
index f419014..66ad6db 100644
--- a/src/gtkext/statusstack.h
+++ b/src/gtkext/statusstack.h
@@ -1,8 +1,8 @@
 
 /* Chrysalide - Outil d'analyse de fichiers binaires
- * gtkstatusstack.h - prototypes pour un empilement d'informations de statut
+ * statusstack.h - prototypes pour l'empilement d'informations de statut
  *
- * Copyright (C) 2015-2019 Cyrille Bagard
+ * Copyright (C) 2015-2024 Cyrille Bagard
  *
  *  This file is part of Chrysalide.
  *
@@ -21,51 +21,39 @@
  */
 
 
-#ifndef _GTKEXT_GTKSTATUSSTACK_H
-#define _GTKEXT_GTKSTATUSSTACK_H
+#ifndef _GTKEXT_STATUSSTACK_H
+#define _GTKEXT_STATUSSTACK_H
 
 
 #include <gtk/gtk.h>
 
 
 #include "../arch/vmpa.h"
+#include "../glibext/helpers.h"
 
 
 
-/* ------------------------- GESTION EXTERIEURE DE LA BARRE ------------------------- */
+/* -------------------------- GESTION GENERALE DES STATUTS -------------------------- */
 
 
-#define GTK_TYPE_STATUS_STACK            (gtk_status_stack_get_type())
-#define GTK_STATUS_STACK(obj)            (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_STATUS_STACK, GtkStatusStack))
-#define GTK_STATUS_STACK_CLASS(klass)    (G_LOADED_BINARY_GET_CLASS(klass, GTK_TYPE_STATUS_STACK, GtkStatusStackClass))
-#define GTK_IS_STATUS_STACK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_STATUS_STACK))
-#define GTK_IS_STATUS_STACK_CLASS(obj)   (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_STATUS_STACK))
-#define GTK_STATUS_STACK_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_STATUS_STACK, GtkStatusStackClass))
+#define GTK_TYPE_STATUS_STACK (gtk_status_stack_get_type())
 
+DECLARE_GTYPE(GtkStatusStack, gtk_status_stack, GTK, STATUS_STACK);
 
-/* Abstration d'une gestion de barre de statut (instance) */
-typedef struct _GtkStatusStack GtkStatusStack;
-
-/* Abstration d'une gestion de barre de statut (classe) */
-typedef struct _GtkStatusStackClass GtkStatusStackClass;
-
-
-/* Détermine le type de la barre de statut améliorée. */
-GType gtk_status_stack_get_type(void);
 
 /* Crée une nouvelle instance de barre de statut. */
 GtkStatusStack *gtk_status_stack_new(void);
 
+/* Réinitialise la barre de statut à son stade par défaut. */
+void gtk_status_stack_reset(GtkStatusStack *);
+
 
 
 /* -------------------- STATUT DES INFORMATIONS DE DESASSEMBLAGE -------------------- */
 
 
 /* Actualise les informations liées une position d'assemblage. */
-void gtk_status_stack_update_current_location(GtkStatusStack *, const mrange_t *, const char *, const char *, const char *);
-
-/* Réinitialise les informations associées une position. */
-void gtk_status_stack_reset_current_location(GtkStatusStack *);
+void gtk_status_stack_update_current_location(GtkStatusStack *, const mrange_t *, const char *, const char *, const char *, const char *);
 
 
 
@@ -82,18 +70,18 @@ typedef unsigned long activity_id_t;
 /* Démarre le suivi d'une nouvelle activité. */
 activity_id_t gtk_status_stack_add_activity(GtkStatusStack *, const char *, unsigned long);
 
-/* Etend la portée des travaux d'une nouvelle activité. */
-void gtk_status_stack_extend_activity(GtkStatusStack *, activity_id_t, unsigned long);
-
 /* Actualise les informations concernant une activité. */
-void gtk_status_stack_update_activity(GtkStatusStack *, activity_id_t, const char *);
+void gtk_status_stack_update_activity_message(GtkStatusStack *, activity_id_t, const char *);
 
 /* Actualise la progression d'une activité. */
 void gtk_status_stack_update_activity_value(GtkStatusStack *, activity_id_t, unsigned long);
 
+/* Etend la portée des travaux d'une nouvelle activité. */
+void gtk_status_stack_extend_activity_max(GtkStatusStack *, activity_id_t, unsigned long);
+
 /* Met fin au suivi d'une activité donnée. */
 void gtk_status_stack_remove_activity(GtkStatusStack *, activity_id_t);
 
 
 
-#endif  /* _GTKEXT_GTKSTATUSSTACK_H */
+#endif  /* _GTKEXT_STATUSSTACK_H */
diff --git a/src/gtkext/statusstack.ui b/src/gtkext/statusstack.ui
new file mode 100644
index 0000000..422f95d
--- /dev/null
+++ b/src/gtkext/statusstack.ui
@@ -0,0 +1,193 @@
+<interface>
+    <template class="GtkStatusStack" parent="GtkBox">
+
+        <child>
+            <object class="GtkStack" id="main">
+                <property name="margin-start">8</property>
+                <property name="margin-end">8</property>
+                <property name="hexpand">true</property>
+
+                <!-- Vide par défaut -->
+                <child>
+                    <object class="GtkStackPage" id="stack">
+                        <property name="name">default</property>
+                        <property name="child">
+
+                            <object class="GtkLabel">
+                                <property name="hexpand">true</property>
+                                <property name="halign">fill</property>
+                                <property name="valign">center</property>
+                                <property name="xalign">0</property>
+                                <property name="label"></property>
+                            </object>
+
+                        </property>
+                    </object>
+                </child>
+
+                <!-- Informations pendant une navigation dans du contenu -->
+                <child>
+                    <object class="GtkStackPage">
+                        <property name="name">navigation</property>
+                        <property name="child">
+
+                            <object class="GtkBox">
+                                <property name="orientation">horizontal</property>
+                                <property name="hexpand">true</property>
+                                <property name="halign">fill</property>
+                                <property name="valign">center</property>
+
+                                <!-- Première partie : navigation -->
+
+                                <child>
+                                    <object class="GtkLabel" id="nav_segment">
+                                        <property name="xalign">0</property>
+                                        <property name="label">-</property>
+                                    </object>
+                                </child>
+
+                                <child>
+                                    <object class="GtkLabel" id="nav_phys">
+                                        <property name="xalign">0</property>
+                                        <property name="label">-</property>
+                                    </object>
+                                </child>
+
+                                <child>
+                                    <object class="GtkLabel" id="nav_virt">
+                                        <property name="xalign">0</property>
+                                        <property name="label">-</property>
+                                    </object>
+                                </child>
+
+                                <child>
+                                    <object class="GtkLabel" id="nav_offset">
+                                        <property name="xalign">0</property>
+                                        <property name="hexpand">true</property>
+                                        <property name="halign">fill</property>
+                                        <property name="label">-</property>
+                                    </object>
+                                </child>
+
+                                <!-- Seconde partie : architecture -->
+
+                                <child>
+                                    <object class="GtkLabel" id="nav_format">
+                                        <property name="xalign">0</property>
+                                        <property name="label">-</property>
+                                    </object>
+                                </child>
+
+                                <child>
+                                    <object class="GtkLabel" id="nav_details">
+                                        <property name="xalign">0</property>
+                                        <property name="label">-</property>
+                                    </object>
+                                </child>
+
+                                <!-- Troisième partie : affichage -->
+
+                                <child>
+                                    <object class="GtkEntry" id="zoom">
+                                        <property name="secondary-icon-name">go-up-symbolic</property>
+                                        <signal name="icon-press" handler="gtk_status_stack_on_zoom_icon_press"/>
+                                    </object>
+                                </child>
+
+                            </object>
+
+                        </property>
+                    </object>
+                </child>
+
+                <!-- Informations liées à une activité en cours -->
+                <child>
+                    <object class="GtkStackPage">
+                        <property name="name">activity</property>
+                        <property name="child">
+
+                            <object class="GtkBox">
+                                <property name="orientation">horizontal</property>
+                                <property name="hexpand">true</property>
+                                <property name="halign">fill</property>
+                                <property name="valign">center</property>
+
+                                <child>
+                                    <object class="GtkLabel" id="activity_message">
+                                        <property name="xalign">0</property>
+                                        <property name="margin-end">8</property>
+                                        <property name="label">-</property>
+                                    </object>
+                                </child>
+
+                                <child>
+                                    <object class="GtkProgressBar" id="activity_progress">
+                                        <property name="hexpand">true</property>
+                                        <property name="halign">fill</property>
+                                        <property name="valign">center</property>
+                                        <property name="fraction">0</property>
+                                    </object>
+                                </child>
+
+                            </object>
+
+                        </property>
+                    </object>
+                </child>
+
+                <property name="visible-child-name">default</property>
+            </object>
+        </child>
+
+        <!-- Tronc commun -->
+
+        <child>
+            <object class="GtkSeparator">
+                <property name="orientation">vertical</property>
+            </object>
+        </child>
+
+        <child>
+            <object class="GtkImage">
+                <property name="margin-start">8</property>
+                <property name="icon-name">pan-down-symbolic</property>
+            </object>
+        </child>
+        <child>
+            <object class="GtkLabel" id="net_recv_speed">
+                <property name="margin-start">8</property>
+                <property name="margin-end">8</property>
+                <property name="xalign">0</property>
+                <property name="label">0 b/s</property>
+            </object>
+        </child>
+
+        <child>
+            <object class="GtkImage">
+                <property name="icon-name">pan-up-symbolic</property>
+            </object>
+        </child>
+        <child>
+            <object class="GtkLabel" id="net_send_speed">
+                <property name="margin-start">8</property>
+                <property name="margin-end">8</property>
+                <property name="xalign">0</property>
+                <property name="label">0 b/s</property>
+            </object>
+        </child>
+
+        <child>
+            <object class="GtkSeparator">
+                <property name="orientation">vertical</property>
+            </object>
+        </child>
+
+        <child>
+            <object class="GtkToggleButton" id="bottom_toggler">
+                <property name="has-frame">false</property>
+                <property name="icon-name">panel-bottom-symbolic</property>
+            </object>
+        </child>
+
+    </template>
+</interface>
diff --git a/src/gui/window.c b/src/gui/window.c
index 1f10a15..193f1bf 100644
--- a/src/gui/window.c
+++ b/src/gui/window.c
@@ -30,6 +30,7 @@
 #include "dialogs/about.h"
 #include "panels/welcome.h"
 #include "../gtkext/helpers.h"
+#include "../gtkext/statusstack.h"
 
 
 
@@ -78,6 +79,8 @@ static void gtk_framework_window_class_init(GtkFrameworkWindowClass *class)
 
     widget = GTK_WIDGET_CLASS(class);
 
+    g_type_ensure(GTK_TYPE_STATUS_STACK);
+
     gtk_widget_class_set_template_from_resource(widget, "/re/chrysalide/framework/gui/window.ui");
 
     gtk_widget_class_bind_template_child(widget, GtkFrameworkWindow, grid);
diff --git a/src/gui/window.ui b/src/gui/window.ui
index 7055695..59b8b2c 100644
--- a/src/gui/window.ui
+++ b/src/gui/window.ui
@@ -34,11 +34,24 @@
         <child>
             <object class="GtkBox">
                 <property name="orientation">vertical</property>
+
                 <child>
                     <object class="GtkStack" id="grid">
                         <property name="vexpand">TRUE</property>
                     </object>
                 </child>
+
+                <child>
+                    <object class="GtkSeparator">
+                        <property name="orientation">horizontal</property>
+                    </object>
+                </child>
+
+                <child>
+                    <object class="GtkStatusStack" id="status">
+                    </object>
+                </child>
+
             </object>
         </child>
 
-- 
cgit v0.11.2-87-g4458