diff options
Diffstat (limited to 'src/gtkext/statusstack.c')
-rw-r--r-- | src/gtkext/statusstack.c | 1383 |
1 files changed, 1383 insertions, 0 deletions
diff --git a/src/gtkext/statusstack.c b/src/gtkext/statusstack.c new file mode 100644 index 0000000..92c296a --- /dev/null +++ b/src/gtkext/statusstack.c @@ -0,0 +1,1383 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * statusstack.c - empilement d'informations de statut + * + * Copyright (C) 2015-2024 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrysalide. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "statusstack.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> + + +#include <i18n.h> + + +#include "helpers.h" +#include "statusstack-int.h" +#include "../core/global.h" + + + +/* -------------------------- GESTION GENERALE DES STATUTS -------------------------- */ + + +/* Liste des propriétés */ + +typedef enum _StatusStackProperty { + + PROP_0, /* Réservé */ + + PROP_SHOW_BOTTOM, /* Affichage de la zone inf. */ + + N_PROPERTIES + +} StatusStackProperty; + +static GParamSpec *_status_stack_properties[N_PROPERTIES] = { NULL, }; + + +/* Source d'affichage par défaut */ +#define gtk_status_stack_default_source gtk_status_stack_show_simple_message + + +/* Initialise la classe des barres de statut améliorées. */ +static void gtk_status_stack_class_init(GtkStatusStackClass *); + +/* Initialise une instance de barre de statut améliorée. */ +static void gtk_status_stack_init(GtkStatusStack *); + +/* Supprime toutes les références externes. */ +static void gtk_status_stack_dispose(GtkStatusStack *); + +/* Procède à la libération totale de la mémoire. */ +static void gtk_status_stack_finalize(GtkStatusStack *); + +/* Note le changement de verrouillage du stockage sécurisé. */ +static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *, GtkStatusStack *); + +/* Met à jour dans la barre les débits réseau observés. */ +static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Met à jour une propriété d'instance GObject. */ +static void gtk_status_stack_set_property(GObject *, guint, const GValue *, GParamSpec *); + +/* Fournit la valeur d'une propriété d'instance GObject. */ +static void gtk_status_stack_get_property(GObject *, guint, GValue *, GParamSpec *); + + + +/* ----------------------- MISE EN AVANT DES MESSAGES SIMPLES ----------------------- */ + + +/* S'assure de l'affichage à jour de la partie "default". */ +static gboolean gtk_status_stack_show_simple_message(gpointer); + + + +/* -------------------- STATUT DES INFORMATIONS DE DESASSEMBLAGE -------------------- */ + + +/* Navigation au sein d'assemblage */ +struct _navigation_info_t +{ + char *segment; /* Segment d'appartenance */ + + mrange_t current; /* Emplacement correspondant */ + + VMPA_BUFFER(phys); /* Localisation physique */ + VMPA_BUFFER(virt); /* Localisation virtuelle */ + + char *symbol; /* Eventuel symbole concerné */ + + char *format; /* Architecture et format */ + char *details; /* Encodage de l'instruction */ + +}; + + +/* Met en place le suivi d'informations de navigation. */ +static void init_navigation_info(navigation_info_t *); + +/* Supprime l'empreinte mémoire d'informations de navigation. */ +static void fini_navigation_info(navigation_info_t *); + +/* S'assure de l'affichage à jour de la partie "navigation". */ +static gboolean gtk_status_stack_show_current_location(gpointer); + +/* Réagit à un clic sur l'icône de zoom. */ +static void gtk_status_stack_on_zoom_icon_press(GtkEntry *, GtkEntryIconPosition, GtkStatusStack *); + + + +/* -------------------------- STATUT DES SUIVIS D'ACTIVITE -------------------------- */ + + +/* Informations de progression */ +typedef struct _activity_status_t +{ + activity_id_t id; /* Identifiant unique */ + + char *message; /* Indication à faire valoir */ + + unsigned long current; /* Position courante */ + unsigned long max; /* Couverture à parcourir */ + + double last_updated; /* Dernière valeur poussée */ + +} activity_status_t; + + +/* Mémorisation des progressions au sein d'activités */ +struct _activity_info_t +{ + GMutex access; /* Accès à la pile */ + + activity_id_t generator; /* Générateur de séquence */ + + activity_status_t *statuses; /* Statuts de progression */ + size_t count; /* Nombre de ces statuts */ + + guint tag; /* Identifiant de mise à jour */ + +}; + + +/* Met en place le suivi d'informations d'activité. */ +static void init_activity_info(activity_info_t *); + +/* Supprime l'empreinte mémoire d'informations d'activité. */ +static void fini_activity_info(activity_info_t *); + +/* Recherche les indications de statut d'une activité donnée. */ +static activity_status_t *find_activity_status_by_id(activity_info_t *, activity_id_t); + +/* S'assure de l'affichage à jour de la partie "activité". */ +static gboolean gtk_status_stack_show_current_activity(gpointer); + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION GENERALE DES STATUTS */ +/* ---------------------------------------------------------------------------------- */ + + +/* Détermine le type du composant d'affichage générique. */ +G_DEFINE_TYPE(GtkStatusStack, gtk_status_stack, GTK_TYPE_BOX); + + +/****************************************************************************** +* * +* Paramètres : class = classe GTK à initialiser. * +* * +* Description : Initialise la classe des barres de statut améliorées. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_class_init(GtkStatusStackClass *class) +{ + GObjectClass *object; /* Plus haut niveau équivalent */ + GtkWidgetClass *widget; /* Classe de haut niveau */ + + object = G_OBJECT_CLASS(class); + + object->dispose = (GObjectFinalizeFunc/* ! */)gtk_status_stack_dispose; + object->finalize = (GObjectFinalizeFunc)gtk_status_stack_finalize; + object->set_property = gtk_status_stack_set_property; + object->get_property = gtk_status_stack_get_property; + + _status_stack_properties[PROP_SHOW_BOTTOM] = + g_param_spec_boolean("show-bottom", NULL, NULL, + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object, N_PROPERTIES, _status_stack_properties); + + 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, def_label); + + 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, lock_update); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_recv_speed); + gtk_widget_class_bind_template_child(widget, GtkStatusStack, net_send_speed); + + gtk_widget_class_bind_template_child(widget, GtkStatusStack, bottom_toggler); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = composant GTK à initialiser. * +* * +* Description : Initialise une instance de barre de statut améliorée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_init(GtkStatusStack *stack) +{ + gtk_widget_init_template(GTK_WIDGET(stack)); + + stack->def_source = gtk_status_stack_default_source; + + stack->msg = NULL; + + stack->nav_info = calloc(1, sizeof(navigation_info_t)); + init_navigation_info(stack->nav_info); + + stack->activity_info = calloc(1, sizeof(activity_info_t)); + init_activity_info(stack->activity_info); + + /* Suivi des évolutions relatives au stockage sécurisé */ + + stack->storage = get_secret_storage(); + + g_signal_connect(stack->storage, "lock-update", + G_CALLBACK(gtk_status_stack_on_secret_storage_lock_update), stack); + + gtk_status_stack_on_secret_storage_lock_update(stack->storage, stack); + + /* Suivi des débits de connexion */ + + stack->next_index = 0; + + stack->network_update_tag = g_timeout_add(NETWORK_UPDATE_INTERVAL, + G_SOURCE_FUNC(gtk_status_stack_update_network_stats), stack); + + /* Premier affichage... */ + + gtk_status_stack_reset_to_default(stack); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_dispose(GtkStatusStack *stack) +{ + if (stack->storage != NULL) + g_signal_handlers_disconnect_by_func(stack->storage, + gtk_status_stack_on_secret_storage_lock_update, stack); + + g_clear_object(&stack->storage); + + g_source_remove(stack->network_update_tag); + + gtk_widget_dispose_template(GTK_WIDGET(stack), GTK_TYPE_STATUS_STACK); + + G_OBJECT_CLASS(gtk_status_stack_parent_class)->dispose(G_OBJECT(stack)); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_finalize(GtkStatusStack *stack) +{ + if (stack->msg != NULL) + free(stack->msg); + + fini_navigation_info(stack->nav_info); + free(stack->nav_info); + + fini_activity_info(stack->activity_info); + free(stack->activity_info); + + G_OBJECT_CLASS(gtk_status_stack_parent_class)->finalize(G_OBJECT(stack)); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée une nouvelle instance de barre de statut. * +* * +* Retour : Composant GTK mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GtkStatusStack *gtk_status_stack_new(void) +{ + GtkStatusStack *result; /* Instance à retourner */ + + result = g_object_new(GTK_TYPE_STATUS_STACK, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* * +* Description : Réinitialise la barre de statut à son stade par défaut. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_reset_to_default(GtkStatusStack *stack) +{ + /** + * Une amélioration possible serait de passer à g_idle_add_once(), + * et de s'affranchir des retours G_SOURCE_REMOVE systématiques, + * mais la fonction n'est disponible qu'à partir de la GLib 2.74. + */ + + g_idle_add(stack->def_source, stack); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gardien des secrets impliqué. * +* stack = barre de statut à actualiser. * +* * +* Description : Note le changement de verrouillage du stockage sécurisé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_on_secret_storage_lock_update(GSecretStorage *storage, GtkStatusStack *stack) +{ + if (!g_secret_storage_has_key(stack->storage)) + { + gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), false); + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "nolock-symbolic"); + } + else + { + gtk_widget_set_sensitive(GTK_WIDGET(stack->lock_update), true); + + if (g_secret_storage_is_locked(stack->storage)) + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "locked-symbolic"); + else + gtk_button_set_icon_name(GTK_BUTTON(stack->lock_update), "unlocked-symbolic"); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* * +* Description : Met à jour dans la barre les débits réseau observés. * +* * +* Retour : G_SOURCE_CONTINUE pour poursuivre les mises à jour. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_status_stack_update_network_stats(GtkStatusStack *stack) +{ + gboolean result; /* Indication à retourner */ + gint64 timestamp; /* Position temporelle */ + size_t received; /* Quantité d'octets reçus */ + size_t sent; /* Quantité d'octets émis */ + gint64 diff_time; /* Différentiel de temps */ + size_t diff_bytes; /* Différentiel de volume */ + double speed; /* Débit de transfert constaté */ + size_t i; /* Boucle de parcours */ + char *value; /* Valeur à afficher */ + + const char *units[] = { _("b/s"), _("kb/s"), _("Mb/s"), _("Gb/s"), _("Tb/s") }; + + result = G_SOURCE_CONTINUE; + + /* Mémorisation des données */ + + timestamp = g_get_monotonic_time(); + + get_network_stats(&received, &sent); + + if (stack->next_index < NETWORK_UPDATE_COUNT) + { + stack->last_bytes_received[stack->next_index] = received; + stack->last_bytes_sent[stack->next_index] = sent; + stack->last_timestamps[stack->next_index] = timestamp; + + stack->next_index++; + + } + else + { + memcpy(stack->last_bytes_received, stack->last_bytes_received + 1, + (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t)); + + memcpy(stack->last_bytes_sent, stack->last_bytes_sent + 1, + (NETWORK_UPDATE_COUNT - 1) * sizeof(size_t)); + + memcpy(stack->last_timestamps, stack->last_timestamps + 1, + (NETWORK_UPDATE_COUNT - 1) * sizeof(gint64)); + + stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] = received; + stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] = sent; + stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] = timestamp; + + } + + if (stack->next_index < NETWORK_UPDATE_COUNT) + goto done; + + diff_time = stack->last_timestamps[NETWORK_UPDATE_COUNT - 1] - stack->last_timestamps[0]; + + /* Débit de réception */ + + diff_bytes = stack->last_bytes_received[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_received[0]; + + speed = (diff_bytes * 1000000) / diff_time; + + for (i = 0; i < G_N_ELEMENTS(units); i++) + { + if (speed < 1024) + break; + + speed /= 1024; + + } + + if (i == 0) + asprintf(&value, "%d %s", (int)speed, units[i]); + else + asprintf(&value, "%.1f %s", speed, units[i]); + + gtk_label_set_label(stack->net_recv_speed, value); + + free(value); + + /* Débit de émission */ + + diff_bytes = stack->last_bytes_sent[NETWORK_UPDATE_COUNT - 1] - stack->last_bytes_sent[0]; + + speed = (diff_bytes * 1000000) / diff_time; + + for (i = 0; i < G_N_ELEMENTS(units); i++) + { + if (speed < 1024) + break; + + speed /= 1024; + + } + + if (i == 0) + asprintf(&value, "%d %s", (int)speed, units[i]); + else + asprintf(&value, "%.1f %s", speed, units[i]); + + gtk_label_set_label(stack->net_send_speed, value); + + free(value); + + done: + + return result; + +} + + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à prendre en compte. * +* pspec = définition de la propriété. * +* * +* Description : Met à jour une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + GtkStatusStack *stack; /* Version spécialisée */ + + stack = GTK_STATUS_STACK(object); + + switch (prop_id) + { + case PROP_SHOW_BOTTOM: + gtk_toggle_button_set_active(stack->bottom_toggler, g_value_get_boolean(value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à transmettre. [OUT] * +* pspec = définition de la propriété. * +* * +* Description : Fournit la valeur d'une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GtkStatusStack *stack; /* Version spécialisée */ + + stack = GTK_STATUS_STACK(object); + + switch (prop_id) + { + case PROP_SHOW_BOTTOM: + g_value_set_boolean(value, gtk_toggle_button_get_active(stack->bottom_toggler)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + + } + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MISE EN AVANT DES MESSAGES SIMPLES */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* msg = simple message à faire paraître. * +* * +* Description : Inscrit un message simple dans la barre de statut. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_display_message(GtkStatusStack *stack, const char *msg) +{ + if (stack->msg != NULL) + free(stack->msg); + + if (msg != NULL) + stack->msg = strdup(msg); + else + stack->msg = NULL; + + gtk_status_stack_show_simple_message(stack); + +} + + +/****************************************************************************** +* * +* Paramètres : data = pile de statuts à manipuler. * +* * +* Description : S'assure de l'affichage à jour de la partie "default". * +* * +* Retour : G_SOURCE_REMOVE pour une exécution unique. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_status_stack_show_simple_message(gpointer data) +{ + GtkStatusStack *stack; /* Version spécialisée */ + + stack = GTK_STATUS_STACK(data); + + stack->def_source = gtk_status_stack_show_simple_message; + + gtk_label_set_text(stack->def_label, stack->msg != NULL ? stack->msg : ""); + + gtk_stack_set_visible_child_name(stack->main, "default"); + + return G_SOURCE_REMOVE; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* STATUT DES INFORMATIONS DE DESASSEMBLAGE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : info = informations à initialiser. * +* * +* Description : Met en place le suivi d'informations de navigation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void init_navigation_info(navigation_info_t *info) +{ + info->segment = NULL; + + info->symbol = NULL; + + info->format = NULL; + info->details = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations à libérer de la mémoire. * +* * +* Description : Supprime l'empreinte mémoire d'informations de navigation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void fini_navigation_info(navigation_info_t *info) +{ + if (info->segment != NULL) + free(info->segment); + + if (info->symbol != NULL) + free(info->symbol); + + if (info->format != NULL) + free(info->format); + + if (info->details != NULL) + free(info->details); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* range = emplacement à mettre en valeur. * +* segment = zone de binaire d'appartenance. * +* symbol = éventuelle position par rapport à un symbole. * +* format = format du binaire manipulé * +* details = détails supplémentaires (liés à l'encodage ?) * +* * +* Description : Actualise les informations liées une position d'assemblage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_update_current_location(GtkStatusStack *stack, const mrange_t *range, const char *segment, const char *symbol, const char *format, const char *details) +{ + navigation_info_t *info; /* Informations à constituer */ + const vmpa2t *addr; /* Localisation de départ */ + + info = stack->nav_info; + + if (cmp_mrange(&info->current, range) == 0) + goto useless; + + /* Zone d'appartenance */ + + if (segment != NULL) + info->segment = strdup(segment); + else + info->segment = NULL; + + /* Adresses de base */ + + copy_mrange(&info->current, range); + + addr = get_mrange_addr(range); + + vmpa2_phys_to_string(addr, MDS_UNDEFINED, info->phys, NULL); + + vmpa2_virt_to_string(addr, MDS_UNDEFINED, info->virt, NULL); + + /* Symbole concerné */ + + if (symbol != NULL) + info->symbol = strdup(symbol); + else + info->symbol = NULL; + + /* Architecture & format */ + + info->format = strdup(format); + + if (details != NULL) + info->details = strdup(details); + else + info->details = NULL; + + /* Nettoyage et conclusion */ + + gtk_status_stack_show_current_location(stack); + + useless: + + ; + +} + + +/****************************************************************************** +* * +* Paramètres : data = pile de statuts à manipuler. * +* * +* Description : S'assure de l'affichage à jour de la partie "navigation". * +* * +* Retour : G_SOURCE_REMOVE pour une exécution unique. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static gboolean gtk_status_stack_show_current_location(gpointer data) +{ + GtkStatusStack *stack; /* Version spécialisée */ + navigation_info_t *info; /* Informations à constituer */ + char raw_pos[6 + VMPA_MAX_LEN + 1]; /* Formatage final en direct */ + + stack = GTK_STATUS_STACK(data); + + stack->def_source = gtk_status_stack_show_current_location; + + info = stack->nav_info; + + /* Première partie : navigation */ + + gtk_label_set_text(stack->nav_segment, info->segment != NULL ? info->segment : ""); + + snprintf(raw_pos, sizeof(raw_pos), "phys: %s", info->phys); + + gtk_label_set_text(stack->nav_phys, raw_pos); + + snprintf(raw_pos, sizeof(raw_pos), "virt: %s", info->virt); + + gtk_label_set_text(stack->nav_virt, raw_pos); + + gtk_label_set_text(stack->nav_offset, info->symbol != NULL ? info->symbol : ""); + + /* Seconde partie : format & architecture */ + + gtk_label_set_text(stack->nav_format, info->format); + + gtk_label_set_text(stack->nav_details, info->details != NULL ? info->details : ""); + + /* Conclusion */ + + gtk_stack_set_visible_child_name(stack->main, "navigation"); + + return G_SOURCE_REMOVE; + +} + + +/****************************************************************************** +* * +* Paramètres : entry = zone de texte visée par la procédure. * +* icon_pos = position de l'image associée à l'entrée. * +* stack = composant graphique de gestion des statuts. * +* * +* Description : Réagit à un clic sur l'icône de zoom. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_status_stack_on_zoom_icon_press(GtkEntry *entry, GtkEntryIconPosition icon_pos, GtkStatusStack *stack) +{ +#if 0 + GtkWidget *popup; /* Popup à faire surgir */ + GdkRectangle rect; /* Zone précise à cibler */ + + popup = gtk_popover_new(); + + gtk_entry_get_icon_area(entry, GTK_ENTRY_ICON_SECONDARY, &rect); + gtk_popover_set_pointing_to(GTK_POPOVER(popup), &rect); + + gtk_widget_show(popup); +#endif +} + + +/* ---------------------------------------------------------------------------------- */ +/* STATUT DES SUIVIS D'ACTIVITE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : info = informations à initialiser. * +* * +* Description : Met en place le suivi d'informations d'activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void init_activity_info(activity_info_t *info) +{ + g_mutex_init(&info->access); + + info->generator = NO_ACTIVITY_ID; + + info->statuses = NULL; + info->count = 0; + + info->tag = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations à libérer de la mémoire. * +* * +* Description : Supprime l'empreinte mémoire d'informations d'activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void fini_activity_info(activity_info_t *info) +{ + size_t i; /* Boucle de parcours */ + + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = 0; + + for (i = 0; i < info->count; i++) + { + if (info->statuses[i].message != NULL) + free(info->statuses[i].message); + } + + if (info->statuses != NULL) + { + free(info->statuses); + info->statuses = NULL; + } + + info->count = 0; + + g_mutex_clear(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : info = informations relatives aux activités à consulter. * +* id = identifiant de l'activité à cibler. * +* * +* Description : Recherche les indications de statut d'une activité donnée. * +* * +* Retour : Structure d'encadrement trouvée ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static activity_status_t *find_activity_status_by_id(activity_info_t *info, activity_id_t id) +{ + activity_status_t *result; /* Statut trouvé à renvoyer */ + size_t i; /* Boucle de parcours */ + + result = NULL; + + assert(!g_mutex_trylock(&info->access)); + + for (i = 0; i < info->count; i++) + if (info->statuses[i].id == id) + { + result = info->statuses + i; + break; + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* msg = nouveau message de statut à copier. * +* max = taille de la plage à parcourir. * +* * +* Description : Démarre le suivi d'une nouvelle activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +activity_id_t gtk_status_stack_add_activity(GtkStatusStack *stack, const char *msg, unsigned long max) +{ + activity_id_t result; /* Numéro unique à renvoyer */ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *new; /* Nouveau suivi d'activité */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + while (1) + { + result = ++info->generator; + + if (find_activity_status_by_id(info, result) == NULL) + break; + + } + while (0); + + info->statuses = realloc(info->statuses, ++info->count * sizeof(activity_status_t)); + + new = info->statuses + info->count - 1; + + /* Identifiant */ + + new->id = result; + + /* Intitulé */ + + if (msg == NULL) + new->message = NULL; + else + new->message = strdup(msg); + + /* Valeur */ + + new->current = 0; + new->max = max; + new->last_updated = 0; + + /* Actualisation */ + + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + g_mutex_unlock(&info->access); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* id = identifiant de l'activité à cibler. * +* msg = nouveau message de statut à copier. * +* * +* Description : Actualise les informations concernant une activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_update_activity_message(GtkStatusStack *stack, activity_id_t id, const char *msg) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + bool msg_changed; /* Changement d'intitulé */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + /* Intitulé */ + + if (status->message != NULL) + { + if (msg == NULL) + msg_changed = true; + else + msg_changed = (strcmp(status->message, msg) != 0); + + free(status->message); + + } + else + msg_changed = (msg != NULL); + + if (msg == NULL) + status->message = NULL; + else + status->message = strdup(msg); + + /* On n'actualise que le sommet de la pile */ + + if ((status - info->statuses + 1) == info->count && msg_changed) + { + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + } + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* id = identifiant de l'activité à cibler. * +* inc = nouvelle valeur pour une progression donnée. * +* * +* Description : Actualise la progression d'une activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_update_activity_value(GtkStatusStack *stack, activity_id_t id, unsigned long inc) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + double new; /* Nouvelle progression */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + /* Valeur */ + + status->current += inc; + + new = (status->current * 1.0) / status->max; + + /* On n'actualise que le sommet de la pile */ + + if ((status - info->statuses + 1) == info->count && (new - status->last_updated) > 0.1) + { + status->last_updated = new; + + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + } + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* id = identifiant de l'activité à cibler. * +* extra = nouvelle échéance supplémentaire des traitements. * +* * +* Description : Etend la portée des travaux d'une nouvelle activité. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_extend_activity_max(GtkStatusStack *stack, activity_id_t id, unsigned long extra) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + /* Valeur */ + + status->max += extra; + + /* On n'actualise que le sommet de la pile */ + + if ((status - info->statuses + 1) == info->count) + { + if (info->tag != 0) + g_source_remove(info->tag); + + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + } + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : stack = barre de statut à actualiser. * +* * +* Description : Met fin au suivi d'une activité donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void gtk_status_stack_remove_activity(GtkStatusStack *stack, activity_id_t id) +{ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *status; /* Suivi d'activité à traiter */ + bool is_last; /* Dernière position ? */ + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + status = find_activity_status_by_id(info, id); + + assert(status != NULL); + + if (status == NULL) + goto exit; + + is_last = ((status - info->statuses + 1) == info->count); + + /* Suppression des données */ + + if (is_last) + { + if (info->tag != 0) + g_source_remove(info->tag); + } + + if (status->message != NULL) + free(status->message); + + /* Réajustement des enregistrements */ + + if (!is_last) + memmove(status, status + 1, + (((info->statuses + info->count) - status) - 1) * sizeof(activity_status_t)); + + info->statuses = realloc(info->statuses, --info->count * sizeof(activity_status_t)); + + /* Bascule vers un autre affichage ou actualisation ? */ + + if (info->count == 0) + { + info->tag = 0; + gtk_status_stack_reset_to_default(stack); + } + else if (is_last) + info->tag = g_idle_add(gtk_status_stack_show_current_activity, stack); + + exit: + + g_mutex_unlock(&info->access); + +} + + +/****************************************************************************** +* * +* Paramètres : data = 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(gpointer data) +{ + GtkStatusStack *stack; /* Version spécialisée */ + activity_info_t *info; /* Informations à consulter */ + activity_status_t *last; /* Dernier statut à traiter */ + + stack = GTK_STATUS_STACK(data); + + info = stack->activity_info; + + g_mutex_lock(&info->access); + + if (!g_source_is_destroyed(g_main_current_source())) + { + if (info->count > 0) + { + last = &info->statuses[info->count - 1]; + + gtk_label_set_text(stack->activity_message, last->message); + + gtk_progress_bar_set_fraction(stack->activity_progress, (last->current * 1.0) / last->max); + + gtk_stack_set_visible_child_name(stack->main, "activity"); + + } + + info->tag = 0; + + } + + g_mutex_unlock(&info->access); + + return G_SOURCE_REMOVE; + +} |