From 33906ce366efc053dee0b76d5bd668797b99071e Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 27 Mar 2016 22:26:06 +0200
Subject: Added a section in the status bar to display activity progress.

---
 ChangeLog                   |  12 ++
 src/analysis/project.c      |  14 +-
 src/glibext/delayed-int.h   |   4 +-
 src/glibext/delayed.c       |  42 ++--
 src/glibext/gcodebuffer.c   |   8 +-
 src/gtkext/gtkstatusstack.c | 471 ++++++++++++++++++++++++++++++++++++++++++--
 src/gtkext/gtkstatusstack.h |  26 ++-
 7 files changed, 518 insertions(+), 59 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 87ff9df..846bc0a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+16-03-27  Cyrille Bagard <nocbos@gmail.com>
+
+	* src/analysis/project.c:
+	* src/glibext/delayed-int.h:
+	* src/glibext/delayed.c:
+	* src/glibext/gcodebuffer.c:
+	Update code.
+
+	* src/gtkext/gtkstatusstack.c:
+	* src/gtkext/gtkstatusstack.h:
+	Add a section in the status bar to display activity progress.
+
 16-03-26  Cyrille Bagard <nocbos@gmail.com>
 
 	* src/glibext/delayed.c:
diff --git a/src/analysis/project.c b/src/analysis/project.c
index cb7f4bd..1a73e74 100644
--- a/src/analysis/project.c
+++ b/src/analysis/project.c
@@ -148,7 +148,7 @@ static void g_delayed_study_dispose(GDelayedStudy *);
 static void g_delayed_study_finalize(GDelayedStudy *);
 
 /* Prépare une intégration de binaire au projet courant. */
-static void g_delayed_study_process(GDelayedStudy *, GtkExtStatusBar *);
+static void g_delayed_study_process(GDelayedStudy *, GtkStatusStack *);
 
 
 
@@ -1089,8 +1089,8 @@ GDelayedStudy *g_delayed_study_new(GStudyProject *project, GBinContent *content,
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : dstudy    = intégration à mener.                             *
-*                statusbar = barre de statut à tenir informée.                *
+*  Paramètres  : dstudy = intégration à mener.                                *
+*                status = barre de statut à tenir informée.                   *
 *                                                                             *
 *  Description : Prépare une intégration de binaire au projet courant.        *
 *                                                                             *
@@ -1100,15 +1100,15 @@ GDelayedStudy *g_delayed_study_new(GStudyProject *project, GBinContent *content,
 *                                                                             *
 ******************************************************************************/
 
-static void g_delayed_study_process(GDelayedStudy *dstudy, GtkExtStatusBar *statusbar)
+static void g_delayed_study_process(GDelayedStudy *dstudy, GtkStatusStack *status)
 {
-    FormatMatchStatus status;               /* Statut d'une reconnaissance */
+    FormatMatchStatus mstatus;              /* Statut d'une reconnaissance */
     char *target;                           /* Sous-traitance requise      */
     GLoadedBinary *binary;                  /* Représentation chargée      */
 
-    status = find_matching_format(dstudy->content, NULL, &target);
+    mstatus = find_matching_format(dstudy->content, NULL, &target);
 
-    switch (status)
+    switch (mstatus)
     {
         case FMS_MATCHED:
 
diff --git a/src/glibext/delayed-int.h b/src/glibext/delayed-int.h
index ecd9b4e..2f6608f 100644
--- a/src/glibext/delayed-int.h
+++ b/src/glibext/delayed-int.h
@@ -25,7 +25,7 @@
 
 
 #include "../common/dllist.h"
-#include "../gtkext/gtkextstatusbar.h"
+#include "../gtkext/gtkstatusstack.h"
 
 
 
@@ -33,7 +33,7 @@
 
 
 /* Traite un travail programmé. */
-typedef void (* run_task_fc) (GDelayedWork *, GtkExtStatusBar *);
+typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *);
 
 
 /* Travail différé (instance) */
diff --git a/src/glibext/delayed.c b/src/glibext/delayed.c
index 1d27448..37b3ed7 100644
--- a/src/glibext/delayed.c
+++ b/src/glibext/delayed.c
@@ -50,7 +50,7 @@ static void g_delayed_work_dispose(GDelayedWork *);
 static void g_delayed_work_finalize(GDelayedWork *);
 
 /* Mène l'opération programmée. */
-static void g_delayed_work_process(GDelayedWork *, GtkExtStatusBar *);
+static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *);
 
 
 
@@ -72,7 +72,7 @@ typedef struct _GWorkGroup
 
     wgroup_id_t id;                         /* Identifiant de travaux menés*/
 
-    GtkExtStatusBar *statusbar;             /* Barre de statut principale  */
+    GtkStatusStack *status;                 /* Barre de statut principale  */
 
     GDelayedWork *works;                    /* Tâches à mener à bien       */
     GMutex mutex;                           /* Verrou pour l'accès         */
@@ -113,7 +113,7 @@ static void g_work_group_dispose(GWorkGroup *);
 static void g_work_group_finalize(GWorkGroup *);
 
 /* Crée un nouveau thread dédié à un type de travaux donné. */
-static GWorkGroup *g_work_group_new(wgroup_id_t, GtkExtStatusBar *);
+static GWorkGroup *g_work_group_new(wgroup_id_t, GtkStatusStack *);
 
 /* Fournit l'identifiant associé à un groupe de travail. */
 static wgroup_id_t g_work_group_get_id(const GWorkGroup *);
@@ -143,7 +143,7 @@ struct _GWorkQueue
 {
     GObject parent;                         /* A laisser en premier        */
 
-    GtkExtStatusBar *statusbar;             /* Barre de statut principale  */
+    GtkStatusStack *status;                 /* Barre de statut principale  */
 
     wgroup_id_t generator;                  /* Générateur d'identifiants   */
 
@@ -286,8 +286,8 @@ static void g_delayed_work_finalize(GDelayedWork *work)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : work      = travail à effectuer.                             *
-*                statusbar = barre de statut à tenir informée.                *
+*  Paramètres  : work   = travail à effectuer.                                *
+*                status = barre de statut à tenir informée.                   *
 *                                                                             *
 *  Description : Mène l'opération programmée.                                 *
 *                                                                             *
@@ -297,9 +297,9 @@ static void g_delayed_work_finalize(GDelayedWork *work)
 *                                                                             *
 ******************************************************************************/
 
-static void g_delayed_work_process(GDelayedWork *work, GtkExtStatusBar *statusbar)
+static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status)
 {
-    G_DELAYED_WORK_GET_CLASS(work)->run(work, statusbar);
+    G_DELAYED_WORK_GET_CLASS(work)->run(work, status);
 
     g_mutex_lock(&work->mutex);
 
@@ -446,7 +446,7 @@ static void g_work_group_dispose(GWorkGroup *group)
     for (i = 0; i < group->threads_count; i++)
         g_thread_join(group->threads[i]);
 
-    g_object_unref(group->statusbar);
+    g_object_unref(group->status);
 
     while (!dl_list_empty(group->works))
     {
@@ -490,8 +490,8 @@ static void g_work_group_finalize(GWorkGroup *group)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : type      = type dont seront marqués tous les travaux donnés.*
-*                statusbar = barre de statut à tenir informée.                *
+*  Paramètres  : type   = type dont seront marqués tous les travaux donnés.   *
+*                status = barre de statut à tenir informée.                   *
 *                                                                             *
 *  Description : Crée un nouveau thread dédié à un type de travaux donné.     *
 *                                                                             *
@@ -501,7 +501,7 @@ static void g_work_group_finalize(GWorkGroup *group)
 *                                                                             *
 ******************************************************************************/
 
-static GWorkGroup *g_work_group_new(wgroup_id_t id, GtkExtStatusBar *statusbar)
+static GWorkGroup *g_work_group_new(wgroup_id_t id, GtkStatusStack *status)
 {
     GWorkGroup *result;                    /* Traiteur à retourner        */
 
@@ -509,10 +509,10 @@ static GWorkGroup *g_work_group_new(wgroup_id_t id, GtkExtStatusBar *statusbar)
 
     result->id = id;
 
-    if (statusbar != NULL)
+    if (status != NULL)
     {
-        result->statusbar = statusbar;
-        g_object_ref(statusbar);
+        result->status = status;
+        g_object_ref(status);
     }
 
     return result;
@@ -601,7 +601,7 @@ static void *g_work_group_process(GWorkGroup *group)
 
         g_mutex_unlock(&group->mutex);
 
-        g_delayed_work_process(work, group->statusbar);
+        g_delayed_work_process(work, group->status);
 
         //g_object_unref(G_OBJECT(work)); // FIXME
 
@@ -774,7 +774,7 @@ static void g_work_queue_dispose(GWorkQueue *queue)
 {
     size_t i;                               /* Boucle de parcours          */
 
-    g_object_unref(G_OBJECT(queue->statusbar));
+    g_object_unref(G_OBJECT(queue->status));
 
     g_mutex_lock(&queue->mutex);
 
@@ -832,11 +832,11 @@ bool init_work_queue(GObject *ref)
 
     if (ref != NULL)
     {
-        queue->statusbar = g_object_get_data(ref, "statusbar");
-        g_object_ref(G_OBJECT(queue->statusbar));
+        queue->status = g_object_get_data(ref, "statusbar");
+        g_object_ref(G_OBJECT(queue->status));
     }
     else
-        queue->statusbar = NULL;
+        queue->status = NULL;
 
     if (queue != NULL)
         _get_work_queue(queue);
@@ -903,7 +903,7 @@ static GWorkGroup *g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id
         queue->groups = (GWorkGroup **)realloc(queue->groups,
                                                queue->groups_count * sizeof(GWorkGroup *));
 
-        result = g_work_group_new(id, queue->statusbar);
+        result = g_work_group_new(id, queue->status);
         queue->groups[queue->groups_count - 1] = result;
 
     }
diff --git a/src/glibext/gcodebuffer.c b/src/glibext/gcodebuffer.c
index be7fa37..8979d2f 100644
--- a/src/glibext/gcodebuffer.c
+++ b/src/glibext/gcodebuffer.c
@@ -93,7 +93,7 @@ static void g_buffer_scan_finalize(GBufferScan *);
 static GBufferScan *g_buffer_scan_new(GCodeBuffer *, const vmpa2t *, const vmpa2t *, const char *, process_line_fc, void *);
 
 /* Assure l'exportation en différé. */
-static void g_buffer_scan_process(GBufferScan *, GtkExtStatusBar *);
+static void g_buffer_scan_process(GBufferScan *, GtkStatusStack *);
 
 
 
@@ -387,8 +387,8 @@ static GBufferScan *g_buffer_scan_new(GCodeBuffer *buffer, const vmpa2t *start,
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : scan      = parcours à mener.                                *
-*                statusbar = barre de statut à tenir informée.                *
+*  Paramètres  : scan   = parcours à mener.                                   *
+*                status = barre de statut à tenir informée.                   *
 *                                                                             *
 *  Description : Assure l'exportation en différé.                             *
 *                                                                             *
@@ -398,7 +398,7 @@ static GBufferScan *g_buffer_scan_new(GCodeBuffer *buffer, const vmpa2t *start,
 *                                                                             *
 ******************************************************************************/
 
-static void g_buffer_scan_process(GBufferScan *scan, GtkExtStatusBar *statusbar)
+static void g_buffer_scan_process(GBufferScan *scan, GtkStatusStack *status)
 {
     size_t first;                           /* Première ligne visée        */
     size_t last;                            /* Dernière ligne visée + 1    */
diff --git a/src/gtkext/gtkstatusstack.c b/src/gtkext/gtkstatusstack.c
index 8cc85cc..9177399 100644
--- a/src/gtkext/gtkstatusstack.c
+++ b/src/gtkext/gtkstatusstack.c
@@ -41,7 +41,10 @@
 
 
 /* Navigation au sein d'assemblage */
-typedef union _assembly_info assembly_info;
+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) */
@@ -52,6 +55,9 @@ struct _GtkStatusStack
     GtkWidget *asm_status;                  /* Barre de status d'assemblage*/
     assembly_info *asm_info;                /* Informations courantes      */
 
+    GtkWidget *prog_status;                 /* Barre de status d'activité  */
+    progress_info *prog_info;               /* Informations courantes      */
+
 };
 
 /* Abstration d'une gestion de barre de statut (classe) */
@@ -83,25 +89,21 @@ static void gtk_status_stack_switch(GtkStatusStack *, GtkWidget *);
 
 
 /* Navigation au sein d'assemblage */
-union _assembly_info
+struct _assembly_info
 {
     bool reset;                             /* Réinitialisation            */
 
-    struct
-    {
-        mrange_t current;                   /* Emplacement correspondant   */
-
-        char *segment;                      /* Segment d'appartenance      */
+    mrange_t current;                       /* Emplacement correspondant   */
 
-        VMPA_BUFFER(phys);                  /* Localisation physique       */
-        VMPA_BUFFER(virt);                  /* Localisation virtuelle      */
+    char *segment;                          /* Segment d'appartenance      */
 
-        char *symbol;                       /* Eventuel symbole concerné   */
+    VMPA_BUFFER(phys);                      /* Localisation physique       */
+    VMPA_BUFFER(virt);                      /* Localisation virtuelle      */
 
-        const char *encoding;               /* Encodage de l'instruction   */
-        phys_t size;                        /* Taille de l'instruction     */
+    char *symbol;                           /* Eventuel symbole concerné   */
 
-    };
+    const char *encoding;                   /* Encodage de l'instruction   */
+    phys_t size;                            /* Taille de l'instruction     */
 
 };
 
@@ -119,7 +121,45 @@ static void on_size_allocate_for_asm_status(GtkWidget *, GdkRectangle *, GObject
 static void on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GdkEventButton *, GtkStatusStack *);
 
 /* S'assure de l'affichage à jour de la partie "assemblage". */
-static void gtk_status_stack_show_current_instruction(GtkStatusStack *);
+static gboolean gtk_status_stack_show_current_instruction(GtkStatusStack *);
+
+
+
+/* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */
+
+
+/* Informations de progression */
+typedef struct _progress_status
+{
+    activity_id_t id;                       /* Identifiant unique          */
+
+    char *message;                          /* Indication à faire valoir   */
+    double value;                           /* Centième de pourcentage     */
+
+} 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  */
+
+};
+
+
+/* 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 *);
 
 
 
@@ -175,6 +215,11 @@ static void gtk_status_stack_init(GtkStatusStack *stack)
 
     reset_assembly_info(stack->asm_info);
 
+    stack->prog_status = build_progress_status_stack(stack);
+    stack->prog_info = (progress_info *)calloc(1, sizeof(progress_info));
+
+    reset_progress_info(stack->prog_info);
+
 }
 
 
@@ -214,9 +259,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);
 
+    reset_progress_info(stack->prog_info);
+    free(stack->prog_info);
+
     G_OBJECT_CLASS(gtk_status_stack_parent_class)->finalize(G_OBJECT(stack));
 
 }
@@ -564,7 +611,7 @@ void gtk_status_stack_update_current_instruction(GtkStatusStack *stack, const GL
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : stack  = barre de statut à actualiser.                       *
+*  Paramètres  : stack = barre de statut à actualiser.                        *
 *                                                                             *
 *  Description : Réinitialise les informations associées une position.        *
 *                                                                             *
@@ -593,13 +640,13 @@ void gtk_status_stack_reset_current_instruction(GtkStatusStack *stack)
 *                                                                             *
 *  Description : S'assure de l'affichage à jour de la partie "assemblage".    *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : G_SOURCE_REMOVE pour une exécution unique.                   *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static void gtk_status_stack_show_current_instruction(GtkStatusStack *stack)
+static gboolean gtk_status_stack_show_current_instruction(GtkStatusStack *stack)
 {
     GObject *ref;                           /* Espace de référencements    */
     assembly_info *info;                    /* Informations à consulter    */
@@ -677,4 +724,392 @@ static void gtk_status_stack_show_current_instruction(GtkStatusStack *stack)
 
     }
 
+    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, 200, -1);
+    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.                  *
+*                value = nouvelle valeur pour une progression donnée.         *
+*                                                                             *
+*  Description : Démarre le suivi d'une nouvelle activité.                    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *msg, double value)
+{
+    activity_id_t result;                   /* Numéro unique à renvoyer    */
+    progress_info *info;                    /* Informations à consulter    */
+    size_t new;                             /* Indice de l'activité créée  */
+
+    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 = new;
+
+    /* Intitulé */
+
+    if (msg == NULL)
+        info->statuses[new].message = NULL;
+    else
+        info->statuses[new].message = strdup(msg);
+
+    /* Valeur */
+
+    info->statuses[new].value = value;
+
+    /* Actualisation */
+
+    if (info->tag != 0)
+        g_source_remove(info->tag);
+
+    info->tag = g_idle_add((GSourceFunc)gtk_status_stack_show_current_activity, stack);
+
+    g_mutex_unlock(&info->access);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : stack = barre de statut à actualiser.                        *
+*                id    = identifiant de l'activité à cibler.                  *
+*                msg   = nouveau message de statut à copier.                  *
+*                value = nouvelle valeur pour une progression donnée.         *
+*                                                                             *
+*  Description : Actualise les informations concernant une activité.          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void gtk_status_stack_update_activity(GtkStatusStack *stack, activity_id_t id, const char *msg, double value)
+{
+    progress_info *info;                    /* Informations à consulter    */
+    size_t i;                               /* Boucle de parcours          */
+    bool msg_changed;                       /* Changement d'intitulé       */
+    double old;                             /* Conservation pour la diff.  */
+
+    info = stack->prog_info;
+
+    g_mutex_lock(&info->access);
+
+    for (i = 0; i < info->count; i++)
+        if (info->statuses[i].id == id)
+            break;
+
+    if (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);
+
+        /* Valeur */
+
+        old = info->statuses[i].value;
+
+        info->statuses[i].value = value;
+
+        /* On n'actualise que le sommet de la pile */
+
+        if ((i + 1) == info->count && (msg_changed || (value - old) > 1.0))
+        {
+            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.                  *
+*                value = 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, double value)
+{
+    progress_info *info;                    /* Informations à consulter    */
+    size_t i;                               /* Boucle de parcours          */
+    double old;                             /* Conservation pour la diff.  */
+
+    info = stack->prog_info;
+
+    g_mutex_lock(&info->access);
+
+    for (i = 0; i < info->count; i++)
+        if (info->statuses[i].id == id)
+            break;
+
+    if (i < info->count)
+    {
+        /* Valeur */
+
+        old = info->statuses[i].value;
+
+        info->statuses[i].value = value;
+
+        /* On n'actualise que le sommet de la pile */
+
+        if ((i + 1) == info->count && (value - old) > 1.0)
+        {
+            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          */
+
+    info = stack->prog_info;
+
+    g_mutex_lock(&info->access);
+
+    for (i = 0; i < info->count; i++)
+        if (info->statuses[i].id == id)
+            break;
+
+    if (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 = g_idle_add((GSourceFunc)gtk_status_stack_show_current_instruction, 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_status_stack_switch(stack, stack->prog_status);
+
+        ref = G_OBJECT(stack->prog_status);
+        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->value);
+
+            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;
+
 }
diff --git a/src/gtkext/gtkstatusstack.h b/src/gtkext/gtkstatusstack.h
index 8e93708..afc3213 100644
--- a/src/gtkext/gtkstatusstack.h
+++ b/src/gtkext/gtkstatusstack.h
@@ -25,7 +25,6 @@
 #define _GTKEXT_GTKSTATUSSTACK_H
 
 
-
 #include <gtk/gtk.h>
 
 
@@ -33,12 +32,6 @@
 
 
 
-/* FIXME */
-typedef int bstatus_id_t;
-
-
-
-
 /* ------------------------- GESTION EXTERIEURE DE LA BARRE ------------------------- */
 
 
@@ -76,6 +69,25 @@ void gtk_status_stack_reset_current_instruction(GtkStatusStack *);
 
 
 
+/* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */
+
+
+/* Identifiant unique de rapport de progression */
+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 *, double);
+
+/* Actualise les informations concernant une activité. */
+void gtk_status_stack_update_activity(GtkStatusStack *, activity_id_t, const char *, double);
+
+/* Actualise la progression d'une activité. */
+void gtk_status_stack_update_activity_value(GtkStatusStack *, activity_id_t, double);
+
+/* Met fin au suivi d'une activité donnée. */
+void gtk_status_stack_remove_activity(GtkStatusStack *, activity_id_t);
+
 
 
 #endif  /* _GTKEXT_GTKSTATUSSTACK_H */
-- 
cgit v0.11.2-87-g4458