From a9c607a1be25b43a17ea55b21459a0433f4f3d5b Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 24 Oct 2019 23:59:55 +0200
Subject: Provided a dialog box to manage snapshots.

---
 pixmaps/Makefile.am                     |   8 +-
 pixmaps/snapshot.png                    | Bin 0 -> 1214 bytes
 pixmaps/snapshot.xcf                    | Bin 0 -> 210520 bytes
 pixmaps/snapshot_current.png            | Bin 0 -> 1525 bytes
 plugins/pychrysalide/analysis/db/item.c |   5 +-
 src/analysis/db/client.c                |  73 +++
 src/analysis/db/client.h                |   9 +
 src/gui/core/Makefile.am                |   4 +-
 src/gui/core/gresource.xml              |   2 +
 src/gui/dialogs/Makefile.am             |   2 +
 src/gui/dialogs/gresource.xml           |   1 +
 src/gui/dialogs/snapshots.c             | 873 ++++++++++++++++++++++++++++++++
 src/gui/dialogs/snapshots.h             |  40 ++
 src/gui/dialogs/snapshots.ui            | 427 ++++++++++++++++
 src/gui/menus/binary.c                  |  57 ++-
 15 files changed, 1495 insertions(+), 6 deletions(-)
 create mode 100644 pixmaps/snapshot.png
 create mode 100644 pixmaps/snapshot.xcf
 create mode 100644 pixmaps/snapshot_current.png
 create mode 100644 src/gui/dialogs/snapshots.c
 create mode 100644 src/gui/dialogs/snapshots.h
 create mode 100644 src/gui/dialogs/snapshots.ui

diff --git a/pixmaps/Makefile.am b/pixmaps/Makefile.am
index 2844877..ecf6a68 100644
--- a/pixmaps/Makefile.am
+++ b/pixmaps/Makefile.am
@@ -45,6 +45,11 @@ MISC =									\
 	chrysalide_text.png					\
 	welcome.png
 
+CORE =									\
+	snapshot.xcf						\
+	snapshot.png						\
+	snapshot_current.png
+
 EXTRA_DIST =							\
 	chrysalide.xcf						\
 	openida.xcf							\
@@ -55,7 +60,8 @@ EXTRA_DIST =							\
 	$(TOOLBAR_BUTTONS)					\
 	$(LIST_ICONS)						\
 	$(ERROR_ICONS)						\
-	$(MISC)
+	$(MISC)								\
+	$(CORE)
 
 pix_DATA = $(APP_ICONS) $(REVISION_PIX) $(TOOLBAR_BUTTONS) $(LIST_ICONS) $(ERROR_ICONS) $(OTHER_ICONS) $(MISC)
 
diff --git a/pixmaps/snapshot.png b/pixmaps/snapshot.png
new file mode 100644
index 0000000..925ff3c
Binary files /dev/null and b/pixmaps/snapshot.png differ
diff --git a/pixmaps/snapshot.xcf b/pixmaps/snapshot.xcf
new file mode 100644
index 0000000..0537bad
Binary files /dev/null and b/pixmaps/snapshot.xcf differ
diff --git a/pixmaps/snapshot_current.png b/pixmaps/snapshot_current.png
new file mode 100644
index 0000000..e601b5b
Binary files /dev/null and b/pixmaps/snapshot_current.png differ
diff --git a/plugins/pychrysalide/analysis/db/item.c b/plugins/pychrysalide/analysis/db/item.c
index fe51752..1d09923 100644
--- a/plugins/pychrysalide/analysis/db/item.c
+++ b/plugins/pychrysalide/analysis/db/item.c
@@ -62,7 +62,10 @@ static int py_db_item_set_flags(PyObject *, PyObject *, void *);
 #define DB_ITEM_DOC                                                         \
     "DbItem handles all kinds of updates applied to the disassebled code."  \
     "\n"                                                                    \
-    "These items are managed using a client/server model."
+    "These items are managed using a client/server model."                  \
+    "\n"                                                                    \
+    "See the pychrysalide.analysis.db.items package for a full list of"     \
+    " existing items."
 
 
 
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index 9558f85..b4e856f 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -1569,3 +1569,76 @@ bool g_hub_client_set_snapshot_desc(GHubClient *client, const snapshot_id_t *id,
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client = client pour les accès distants à manipuler.         *
+*                id     = identifiant d'instantané à traiter.                 *
+*                                                                             *
+*  Description : Restaure un ancien instantané.                               *
+*                                                                             *
+*  Retour      : true si la commande a bien été envoyée, false sinon.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_hub_client_restore_snapshot(GHubClient *client, const snapshot_id_t *id)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = false;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client = client pour les accès distants à manipuler.         *
+*                id     = identifiant d'instantané à traiter.                 *
+*                                                                             *
+*  Description : Crée un nouvel instantané à partir d'un autre.               *
+*                                                                             *
+*  Retour      : true si la commande a bien été envoyée, false sinon.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_hub_client_create_snapshot(GHubClient *client, const snapshot_id_t *id)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = false;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client = client pour les accès distants à manipuler.         *
+*                id     = identifiant d'instantané à traiter.                 *
+*                rec    = programme une suppression récursive.                *
+*                                                                             *
+*  Description : Supprime un ancien instantané.                               *
+*                                                                             *
+*  Retour      : true si la commande a bien été envoyée, false sinon.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_hub_client_remove_snapshot(GHubClient *client, const snapshot_id_t *id, bool rec)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = false;
+
+    return result;
+
+}
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index 5ad4ed0..af47b5c 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -89,6 +89,15 @@ bool g_hub_client_set_snapshot_name(GHubClient *, const snapshot_id_t *, const c
 /* Définit la description d'un instantané donné. */
 bool g_hub_client_set_snapshot_desc(GHubClient *, const snapshot_id_t *, const char *);
 
+/* Restaure un ancien instantané. */
+bool g_hub_client_restore_snapshot(GHubClient *, const snapshot_id_t *);
+
+/* Crée un nouvel instantané à partir d'un autre. */
+bool g_hub_client_create_snapshot(GHubClient *, const snapshot_id_t *);
+
+/* Supprime un ancien instantané. */
+bool g_hub_client_remove_snapshot(GHubClient *, const snapshot_id_t *, bool);
+
 
 
 #endif  /* _ANALYSIS_DB_CLIENT_H */
diff --git a/src/gui/core/Makefile.am b/src/gui/core/Makefile.am
index d23dc07..db9fd47 100644
--- a/src/gui/core/Makefile.am
+++ b/src/gui/core/Makefile.am
@@ -8,7 +8,9 @@ RES_FILES =								\
 	../../../pixmaps/collapse.png		\
 	../../../pixmaps/collapse_dark.png	\
 	../../../pixmaps/expand.png			\
-	../../../pixmaps/expand_dark.png
+	../../../pixmaps/expand_dark.png	\
+	../../../pixmaps/snapshot.png		\
+	../../../pixmaps/snapshot_current.png
 
 libguicore_la_SOURCES =					\
 	core.h core.c						\
diff --git a/src/gui/core/gresource.xml b/src/gui/core/gresource.xml
index ad3688c..6f5cb94 100644
--- a/src/gui/core/gresource.xml
+++ b/src/gui/core/gresource.xml
@@ -6,5 +6,7 @@
         <file compressed="true" alias="collapse_dark.png">../../../pixmaps/collapse_dark.png</file>
         <file compressed="true" alias="expand.png">../../../pixmaps/expand.png</file>
         <file compressed="true" alias="expand_dark.png">../../../pixmaps/expand_dark.png</file>
+        <file compressed="true" alias="snapshot.png">../../../pixmaps/snapshot.png</file>
+        <file compressed="true" alias="snapshot_current.png">../../../pixmaps/snapshot_current.png</file>
     </gresource>
 </gresources>
diff --git a/src/gui/dialogs/Makefile.am b/src/gui/dialogs/Makefile.am
index ae5180b..575defd 100644
--- a/src/gui/dialogs/Makefile.am
+++ b/src/gui/dialogs/Makefile.am
@@ -10,6 +10,7 @@ UI_FILES =								\
 	preferences.ui						\
 	prefs_fgraph.ui						\
 	prefs_labels.ui						\
+	snapshots.ui						\
 	storage.ui
 
 libguidialogs_la_SOURCES =				\
@@ -25,6 +26,7 @@ libguidialogs_la_SOURCES =				\
 	prefs_fgraph.h prefs_fgraph.c		\
 	prefs_labels.h prefs_labels.c		\
 	resources.h resources.c				\
+	snapshots.h snapshots.c				\
 	storage.h storage.c
 
 libguidialogs_la_LDFLAGS = 
diff --git a/src/gui/dialogs/gresource.xml b/src/gui/dialogs/gresource.xml
index 1d18e39..bf81af4 100644
--- a/src/gui/dialogs/gresource.xml
+++ b/src/gui/dialogs/gresource.xml
@@ -7,6 +7,7 @@
         <file compressed="true">preferences.ui</file>
         <file compressed="true">prefs_fgraph.ui</file>
         <file compressed="true">prefs_labels.ui</file>
+        <file compressed="true">snapshots.ui</file>
         <file compressed="true">storage.ui</file>
     </gresource>
 </gresources>
diff --git a/src/gui/dialogs/snapshots.c b/src/gui/dialogs/snapshots.c
new file mode 100644
index 0000000..1fcea67
--- /dev/null
+++ b/src/gui/dialogs/snapshots.c
@@ -0,0 +1,873 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * snapshots.c - gestion des instantanés de base de données
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "snapshots.h"
+
+
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+
+
+#include <i18n.h>
+
+
+
+/* Colonnes de l'arborescence d'instantanés */
+typedef enum _SnapshotTreeColumn
+{
+    STC_ICON,                               /* Image de représentation     */
+    STC_TITLE,                              /* Meilleure représentation    */
+
+    STC_ID,                                 /* Identifiant de l'instantané */
+    STC_TIMESTAMP,                          /* Valeur brute d'horodatage   */
+    STC_NAME,                               /* Désignation de l'instantané */
+    STC_DESC,                               /* Description de l'instantané */
+
+} SnapshotTreeColumn;
+
+
+/* Réagit à une fermeture de la boîte de dialogue. */
+static void on_dialog_destroy(GtkWidget *, GtkBuilder *);
+
+/* Réagit à un changement dans le choix du serveur. */
+static void on_server_selection_changed(GtkComboBox *, GtkBuilder *);
+
+/* Recherche un élément d'arborescence selon un identifiant. */
+static bool find_suitable_parent(GtkTreeModel *, GtkTreeIter *, const char *, GtkTreeIter *);
+
+/* Met à jour l'affichage avec une nouvelle liste d'instantanés. */
+static void on_snapshots_updated(GHubClient *, GtkBuilder *);
+
+/* Réinitialise la zone d'affichage des informations. */
+static void reset_information_area(GtkBuilder *);
+
+/* Active ou non l'accès à la zone d'affichage des informations. */
+static void update_information_area_access(GtkBuilder *, bool);
+
+/* Active ou non l'accès à la zone de contrôle des instantanés. */
+static void update_control_area_access(GtkBuilder *, bool, bool, bool);
+
+/* Met à jour l'affichage des informations d'un instantané. */
+static void on_tree_selection_changed(GtkTreeSelection *, GtkBuilder *);
+
+/* Restaure sur demande un nouvel instantané. */
+static void restore_old_snapshot(GtkToolButton *, GtkBuilder *);
+
+/* Crée sur demande un nouvel instantané. */
+static void create_new_snapshot(GtkToolButton *, GtkBuilder *);
+
+/* Supprime sur demande un instantané. */
+static void remove_old_snapshot(GtkToolButton *, GtkBuilder *);
+
+/* Supprime sur demande un instantané et ses successeurs. */
+static void delete_old_snapshot(GtkToolButton *, GtkBuilder *);
+
+/* Applique à un instantané ses nouvelles informations. */
+static void apply_new_snapshot_info(GtkButton *, GtkBuilder *);
+
+/* Ferme la boîte de dialogue. */
+static void close_dialog_box(GtkButton *, GtkBuilder *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : binary = binaire chargé en mémoire à traiter.                *
+*                parent = fenêtre principale de l'éditeur.                    *
+*                outb   = constructeur à détruire après usage. [OUT]          *
+*                                                                             *
+*  Description : Affiche un gestionnaire d'instantanés de base de données.    *
+*                                                                             *
+*  Retour      : Adresse de la fenêtre mise en place.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GtkWidget *create_snapshots_dialog(GLoadedBinary *binary, GtkWindow *parent, GtkBuilder **outb)
+{
+    GtkWidget *result;                      /* Fenêtre à renvoyer          */
+    GtkBuilder *builder;                    /* Constructeur utilisé        */
+    GFile *file;                            /* Accès à une image interne   */
+    GIcon *icon;                            /* Image de représentation     */
+    GtkComboBox *combo;                     /* Liste de serveurs           */
+
+    builder = gtk_builder_new_from_resource("/org/chrysalide/gui/dialogs/snapshots.ui");
+    *outb = builder;
+
+    result = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
+
+    gtk_window_set_transient_for(GTK_WINDOW(result), parent);
+
+    g_object_ref(G_OBJECT(result));
+    g_object_set_data_full(G_OBJECT(builder), "window", result, g_object_unref);
+
+    g_object_ref(G_OBJECT(binary));
+    g_object_set_data_full(G_OBJECT(builder), "binary", binary, g_object_unref);
+
+    file = g_file_new_for_uri("resource:///org/chrysalide/gui/core/images/snapshot.png");
+    icon = g_file_icon_new(file);
+    g_object_set_data_full(G_OBJECT(builder), "icon", icon, g_object_unref);
+
+    file = g_file_new_for_uri("resource:///org/chrysalide/gui/core/images/snapshot_current.png");
+    icon = g_file_icon_new(file);
+    g_object_set_data_full(G_OBJECT(builder), "current_icon", icon, g_object_unref);
+
+    /* Connexion des signaux */
+
+    gtk_builder_add_callback_symbols(builder,
+                                     "on_dialog_destroy", G_CALLBACK(on_dialog_destroy),
+                                     "on_server_selection_changed", G_CALLBACK(on_server_selection_changed),
+                                     "on_tree_selection_changed", G_CALLBACK(on_tree_selection_changed),
+                                     "restore_old_snapshot", G_CALLBACK(restore_old_snapshot),
+                                     "create_new_snapshot", G_CALLBACK(create_new_snapshot),
+                                     "remove_old_snapshot", G_CALLBACK(remove_old_snapshot),
+                                     "delete_old_snapshot", G_CALLBACK(delete_old_snapshot),
+                                     "apply_new_snapshot_info", G_CALLBACK(apply_new_snapshot_info),
+                                     "close_dialog_box", G_CALLBACK(close_dialog_box),
+                                     NULL);
+
+    gtk_builder_connect_signals(builder, builder);
+
+    /* Mise à jour de l'interface */
+
+    reset_information_area(builder);
+    update_control_area_access(builder, false, false, false);
+
+    combo = GTK_COMBO_BOX(gtk_builder_get_object(builder, "servers"));
+
+    gtk_combo_box_set_active(combo, 0);
+
+    return result;
+
+}
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : dialog  = fenêtre en cours de suppression.                   *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Réagit à une fermeture de la boîte de dialogue.              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_dialog_destroy(GtkWidget *dialog, GtkBuilder *builder)
+{
+    GHubClient *client;                     /* Cible des interactions      */
+
+    /* Déconnexion de l'ancien */
+
+    client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
+
+    if (client != NULL)
+        g_signal_handlers_disconnect_by_func(client, on_snapshots_updated, builder);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : combo   = liste de sélection à l'origine de la procédure.    *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Réagit à un changement dans le choix du serveur.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_server_selection_changed(GtkComboBox *combo, GtkBuilder *builder)
+{
+    GHubClient *client;                     /* Cible des interactions      */
+    gint active;                            /* Indice du serveur retenu    */
+    GLoadedBinary *binary;                  /* Binaire en cours d'étude    */
+    GtkTreeStore *store;                    /* Modèle de gestion           */
+
+    /* Déconnexion de l'ancien */
+
+    client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
+
+    if (client != NULL)
+        g_signal_handlers_disconnect_by_func(client, on_snapshots_updated, builder);
+
+    /* Connexion nouvelle */
+
+    active = gtk_combo_box_get_active(combo);
+
+    binary = G_LOADED_BINARY(g_object_get_data(G_OBJECT(builder), "binary"));
+
+    client = g_loaded_binary_get_client(binary, active == 0);
+
+    if (client == NULL)
+    {
+        g_object_set_data(G_OBJECT(builder), "client", NULL);
+
+        store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store"));
+        gtk_tree_store_clear(store);
+
+        reset_information_area(builder);
+        update_control_area_access(builder, false, false, false);
+
+    }
+
+    else
+    {
+        g_object_ref(G_OBJECT(client));
+        g_object_set_data_full(G_OBJECT(builder), "client", client, g_object_unref);
+
+        g_signal_connect(client, "snapshots-updated", G_CALLBACK(on_snapshots_updated), builder);
+
+        on_snapshots_updated(client, builder);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : model = modèle de gestion de données à consulter.            *
+*                iter  = point de départ des recherches.                      *
+*                id    = identifiant de l'instantané recherché.               *
+*                found = emplacement de l'éventuel noeud trouvé. [OUT]        *
+*                                                                             *
+*  Description : Recherche un élément d'arborescence selon un identifiant.    *
+*                                                                             *
+*  Retour      : true pour indiquer une recherche fructueuse, false sinon.    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool find_suitable_parent(GtkTreeModel *model, GtkTreeIter *iter, const char *id, GtkTreeIter *found)
+{
+    bool result;                            /* Bilan à retourner           */
+    gchar *value;                           /* Identifiant courant         */
+    gint count;                             /* Quantité de fils présents   */
+    gint i;                                 /* Boucle de parcours          */
+    GtkTreeIter child;                      /* Localisation d'un fils      */
+#ifndef NDEBUG
+    gboolean status;                        /* Bilan d'une consultation    */
+#endif
+
+    gtk_tree_model_get(model, iter, STC_ID, &value, -1);
+
+    result = (strcmp(value, id) == 0);
+
+    g_free(value);
+
+    if (result)
+        *found = *iter;
+
+    else
+    {
+        count = gtk_tree_model_iter_n_children(model, iter);
+
+        for (i = 0; i < count && !result; i++)
+        {
+#ifndef NDEBUG
+            status = gtk_tree_model_iter_nth_child(model, &child, iter, i);
+            assert(status);
+#else
+            gtk_tree_model_iter_nth_child(model, &child, iter, i);
+#endif
+
+            result = find_suitable_parent(model, &child, id, found);
+
+        }
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client  = client connecté à l'origine de la procédure.       *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Met à jour l'affichage avec une nouvelle liste d'instantanés.*
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_snapshots_updated(GHubClient *client, GtkBuilder *builder)
+{
+    GtkTreeStore *store;                    /* Modèle de gestion           */
+    snapshot_info_t *info;                  /* Liste d'instantanés présents*/
+    size_t count;                           /* Taille de cette liste       */
+    bool status;                            /* Validité de cet identifiant */
+    size_t i;                               /* Boucle de parcours          */
+    char *id;                               /* Identifiant du parent       */
+    GtkTreeIter iter;                       /* Point d'insertion           */
+#ifndef NDEBUG
+    gboolean check;                         /* Vérification par principe   */
+#endif
+    GtkTreeIter parent;                     /* Parent du point d'insertion */
+    GIcon *icon;                            /* Image de représentation     */
+    char *name;                             /* Désignation d'instantané    */
+    snapshot_id_t current;                  /* Instantané courant          */
+    GtkTreeView *treeview;                  /* Arborescence graphique      */
+    const char *raw;                        /* Identifiant brut            */
+    GtkTreeSelection *selection;            /* Gestionnaire de sélection   */
+
+    store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store"));
+
+    gtk_tree_store_clear(store);
+
+    status = g_hub_client_get_snapshots(client, &info, &count);
+
+    if (!status)
+    {
+        reset_information_area(builder);
+        update_control_area_access(builder, false, false, false);
+    }
+
+    else
+    {
+        /* Remplissage */
+
+        for (i = 0; i < count; i++)
+        {
+            id = snapshot_id_as_string(get_snapshot_info_parent_id(&info[i]));
+
+            if (strcmp(id, NO_SNAPSHOT_ROOT) == 0)
+                gtk_tree_store_append(store, &iter, NULL);
+
+            else
+            {
+#ifndef NDEBUG
+                check = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
+                assert(check);
+#else
+                gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
+#endif
+
+                status = find_suitable_parent(GTK_TREE_MODEL(store), &iter, id, &parent);
+                assert(status);
+
+                gtk_tree_store_append(store, &iter, &parent);
+
+            }
+
+            id = snapshot_id_as_string(get_snapshot_info_id(&info[i]));
+
+            icon = G_ICON(g_object_get_data(G_OBJECT(builder), "icon"));
+
+            name = get_snapshot_info_name(&info[i]);
+
+            gtk_tree_store_set(store, &iter,
+                               STC_ICON, icon,
+                               STC_TITLE, name != NULL ? name : id,
+                               STC_ID, id,
+                               STC_TIMESTAMP, get_snapshot_info_created(&info[i]),
+                               STC_NAME, name,
+                               STC_DESC, get_snapshot_info_desc(&info[i]),
+                               -1);
+
+            exit_snapshot_info(&info[i]);
+
+        }
+
+        free(info);
+
+        /* Ajout de l'instantané courant */
+
+        status = g_hub_client_get_current_snapshot(client, &current);
+
+        if (status)
+        {
+            id = snapshot_id_as_string(&current);
+
+#ifndef NDEBUG
+            check = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
+            assert(check);
+#else
+            gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
+#endif
+
+            status = find_suitable_parent(GTK_TREE_MODEL(store), &iter, id, &parent);
+
+            if (status)
+            {
+                gtk_tree_store_append(store, &iter, &parent);
+
+                icon = G_ICON(g_object_get_data(G_OBJECT(builder), "current_icon"));
+
+                gtk_tree_store_set(store, &iter,
+                                   STC_ICON, icon,
+                                   STC_TITLE, _("Current"),
+                                   -1);
+
+            }
+
+        }
+
+        /* Plein affichage */
+
+        treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview"));
+
+        gtk_tree_view_expand_all(treeview);
+
+        /* Remise en place de la dernière sélection */
+
+        raw = g_object_get_data(G_OBJECT(builder), "selected");
+
+        if (raw != NULL)
+        {
+#ifndef NDEBUG
+            check = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
+            assert(check);
+#else
+            gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
+#endif
+
+            status = find_suitable_parent(GTK_TREE_MODEL(store), &iter, raw, &iter);
+
+            if (status)
+            {
+                selection = gtk_tree_view_get_selection(treeview);
+
+                gtk_tree_selection_select_iter(selection, &iter);
+
+            }
+
+            else
+            {
+                reset_information_area(builder);
+
+                update_control_area_access(builder, false, false, false);
+
+                g_object_set_data(G_OBJECT(builder), "selected", NULL);
+
+            }
+
+        }
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Réinitialise la zone d'affichage des informations.           *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void reset_information_area(GtkBuilder *builder)
+{
+    GtkLabel *label;                        /* Etiquette à faire évoluer   */
+    GtkEntry *entry;                        /* Zone de saisie à actualiser */
+
+    label = GTK_LABEL(gtk_builder_get_object(builder, "identifier"));
+    gtk_label_set_text(label, "-");
+
+    label = GTK_LABEL(gtk_builder_get_object(builder, "timestamp"));
+    gtk_label_set_text(label, "-");
+
+    entry = GTK_ENTRY(gtk_builder_get_object(builder, "name"));
+    gtk_entry_set_text(entry, "");
+
+    entry = GTK_ENTRY(gtk_builder_get_object(builder, "description"));
+    gtk_entry_set_text(entry, "");
+
+    update_information_area_access(builder, false);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : builder = espace de référencement global.                    *
+*                state   = état des possibilités d'interactions à appliquer.  *
+*                                                                             *
+*  Description : Active ou non l'accès à la zone d'affichage des informations.*
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void update_information_area_access(GtkBuilder *builder, bool state)
+{
+    GtkWidget *widget;                      /* Composant à traiter         */
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "identifier"));
+    gtk_widget_set_sensitive(widget, state);
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "timestamp"));
+    gtk_widget_set_sensitive(widget, state);
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "name"));
+    gtk_widget_set_sensitive(widget, state);
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "description"));
+    gtk_widget_set_sensitive(widget, state);
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "apply"));
+    gtk_widget_set_sensitive(widget, state);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : builder = espace de référencement global.                    *
+*                restore = accès à la restauration d'un instantané.           *
+*                create  = accès à la création d'un nouvel instantané.        *
+*                delete  = accès à la suppression d'instantanés.              *
+*                                                                             *
+*  Description : Active ou non l'accès à la zone de contrôle des instantanés. *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void update_control_area_access(GtkBuilder *builder, bool restore, bool create, bool delete)
+{
+    GtkWidget *widget;                      /* Composant à traiter         */
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "restore"));
+    gtk_widget_set_sensitive(widget, restore);
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "create"));
+    gtk_widget_set_sensitive(widget, create);
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "remove"));
+    gtk_widget_set_sensitive(widget, delete);
+
+    widget = GTK_WIDGET(gtk_builder_get_object(builder, "delete"));
+    gtk_widget_set_sensitive(widget, delete);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : selection = nouvelle sélection à appliquer.                  *
+*                builder   = espace de référencement global.                  *
+*                                                                             *
+*  Description : Met à jour l'affichage des informations d'un instantané.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_tree_selection_changed(GtkTreeSelection *selection, GtkBuilder *builder)
+{
+    GtkTreeModel *model;                    /* Modèle de gestion           */
+    GtkTreeIter iter;                       /* Point de sélection          */
+    gchar *id;                              /* Identifiant d'instantané    */
+    guint64 timestamp;                      /* Horodatage associé          */
+    gchar *name;                            /* Eventuelle désignation      */
+    gchar *desc;                            /* Eventuelle description      */
+    GtkLabel *label;                        /* Etiquette à faire évoluer   */
+    char buf[27];                           /* Tampon pour la date         */
+    GtkEntry *entry;                        /* Zone de saisie à actualiser */
+    gboolean is_not_root;                   /* Particularité de sélection  */
+
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+    {
+        gtk_tree_model_get(model, &iter,
+                           STC_ID, &id,
+                           STC_TIMESTAMP, &timestamp,
+                           STC_NAME, &name,
+                           STC_DESC, &desc,
+                           -1);
+
+        if (id == NULL)
+        {
+            assert(name == NULL);
+            assert(desc == NULL);
+
+            reset_information_area(builder);
+
+            update_control_area_access(builder, false, true, false);
+
+            g_object_set_data(G_OBJECT(builder), "selected", NULL);
+
+        }
+
+        else
+        {
+            label = GTK_LABEL(gtk_builder_get_object(builder, "identifier"));
+            gtk_label_set_text(label, id);
+
+            ctime_r((time_t []) { timestamp /  1000000 }, buf);
+            buf[strlen(buf) - 1] = 0;
+
+            label = GTK_LABEL(gtk_builder_get_object(builder, "timestamp"));
+            gtk_label_set_text(label, buf);
+
+            entry = GTK_ENTRY(gtk_builder_get_object(builder, "name"));
+            gtk_entry_set_text(entry, name != NULL ? name : "");
+
+            if (name != NULL)
+                g_free(name);
+
+            entry = GTK_ENTRY(gtk_builder_get_object(builder, "description"));
+            gtk_entry_set_text(entry, desc != NULL ? desc : "");
+
+            if (desc != NULL)
+                g_free(desc);
+
+            update_information_area_access(builder, true);
+
+            is_not_root = gtk_tree_model_iter_parent(model, (GtkTreeIter []) { { 0 } }, &iter);
+
+            update_control_area_access(builder, true, false, is_not_root);
+
+            g_object_set_data_full(G_OBJECT(builder), "selected", id, g_free);
+
+        }
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button  = composant GTK à l'origine de la procédure.         *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Restaure sur demande un nouvel instantané.                   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void restore_old_snapshot(GtkToolButton *button, GtkBuilder *builder)
+{
+    const char *raw;                        /* Identifiant brut            */
+    snapshot_id_t id;                       /* Identifiant utilisable      */
+    bool status;                            /* Bilan d'une conversion      */
+    GHubClient *client;                     /* Cible des interactions      */
+
+    raw = g_object_get_data(G_OBJECT(builder), "selected");
+
+    status = init_snapshot_id_from_text(&id, raw);
+
+    if (status)
+    {
+        client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
+
+        g_hub_client_restore_snapshot(client, &id);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button  = composant GTK à l'origine de la procédure.         *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Crée sur demande un nouvel instantané.                       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void create_new_snapshot(GtkToolButton *button, GtkBuilder *builder)
+{
+    const char *raw;                        /* Identifiant brut            */
+    snapshot_id_t id;                       /* Identifiant utilisable      */
+    bool status;                            /* Bilan d'une conversion      */
+    GHubClient *client;                     /* Cible des interactions      */
+
+    raw = g_object_get_data(G_OBJECT(builder), "selected");
+
+    status = init_snapshot_id_from_text(&id, raw);
+
+    if (status)
+    {
+        client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
+
+        g_hub_client_create_snapshot(client, &id);
+
+    }
+
+}
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button  = composant GTK à l'origine de la procédure.         *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Supprime sur demande un instantané.                          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void remove_old_snapshot(GtkToolButton *button, GtkBuilder *builder)
+{
+    const char *raw;                        /* Identifiant brut            */
+    snapshot_id_t id;                       /* Identifiant utilisable      */
+    bool status;                            /* Bilan d'une conversion      */
+    GHubClient *client;                     /* Cible des interactions      */
+
+    raw = g_object_get_data(G_OBJECT(builder), "selected");
+
+    status = init_snapshot_id_from_text(&id, raw);
+
+    if (status)
+    {
+        client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
+
+        g_hub_client_remove_snapshot(client, &id, false);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button  = composant GTK à l'origine de la procédure.         *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Supprime sur demande un instantané et ses successeurs.       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void delete_old_snapshot(GtkToolButton *button, GtkBuilder *builder)
+{
+    const char *raw;                        /* Identifiant brut            */
+    snapshot_id_t id;                       /* Identifiant utilisable      */
+    bool status;                            /* Bilan d'une conversion      */
+    GHubClient *client;                     /* Cible des interactions      */
+
+    raw = g_object_get_data(G_OBJECT(builder), "selected");
+
+    status = init_snapshot_id_from_text(&id, raw);
+
+    if (status)
+    {
+        client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
+
+        g_hub_client_remove_snapshot(client, &id, true);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button  = composant GTK à l'origine de la procédure.         *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Applique à un instantané ses nouvelles informations.         *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void apply_new_snapshot_info(GtkButton *button, GtkBuilder *builder)
+{
+    const char *raw;                        /* Identifiant brut            */
+    snapshot_id_t id;                       /* Identifiant utilisable      */
+    bool status;                            /* Bilan d'une conversion      */
+    GHubClient *client;                     /* Cible des interactions      */
+    GtkEntry *entry;                        /* Zone de saisie à actualiser */
+
+    raw = g_object_get_data(G_OBJECT(builder), "selected");
+
+    status = init_snapshot_id_from_text(&id, raw);
+
+    if (status)
+    {
+        client = G_HUB_CLIENT(g_object_get_data(G_OBJECT(builder), "client"));
+
+        entry = GTK_ENTRY(gtk_builder_get_object(builder, "name"));
+        g_hub_client_set_snapshot_name(client, &id, gtk_entry_get_text(entry));
+
+        entry = GTK_ENTRY(gtk_builder_get_object(builder, "description"));
+        g_hub_client_set_snapshot_desc(client, &id, gtk_entry_get_text(entry));
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button  = composant GTK à l'origine de la procédure.         *
+*                builder = espace de référencement global.                    *
+*                                                                             *
+*  Description : Ferme la boîte de dialogue.                                  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void close_dialog_box(GtkButton *button, GtkBuilder *builder)
+{
+    GtkDialog *dialog;                      /* Fenêtre à manipuler         */
+
+    dialog = GTK_DIALOG(gtk_builder_get_object(builder, "window"));
+
+    gtk_dialog_response(dialog, GTK_RESPONSE_CLOSE);
+
+}
diff --git a/src/gui/dialogs/snapshots.h b/src/gui/dialogs/snapshots.h
new file mode 100644
index 0000000..05f3ac3
--- /dev/null
+++ b/src/gui/dialogs/snapshots.h
@@ -0,0 +1,40 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * snapshots.h - prototypes pour la gestion des instantanés de base de données
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _GUI_DIALOGS_SNAPSHOTS_H
+#define _GUI_DIALOGS_SNAPSHOTS_H
+
+
+#include <gtk/gtk.h>
+
+
+#include "../../analysis/binary.h"
+
+
+
+/* Affiche un gestionnaire d'instantanés de base de données. */
+GtkWidget *create_snapshots_dialog(GLoadedBinary *, GtkWindow *, GtkBuilder **);
+
+
+
+#endif  /* _GUI_DIALOGS_SNAPSHOTS_H */
diff --git a/src/gui/dialogs/snapshots.ui b/src/gui/dialogs/snapshots.ui
new file mode 100644
index 0000000..7c6d472
--- /dev/null
+++ b/src/gui/dialogs/snapshots.ui
@@ -0,0 +1,427 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.21.0 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <object class="GtkImage" id="image-add">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-add</property>
+  </object>
+  <object class="GtkImage" id="image-delete">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-delete</property>
+    <property name="icon_size">3</property>
+  </object>
+  <object class="GtkImage" id="image-remove">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-remove</property>
+  </object>
+  <object class="GtkImage" id="image-restore">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-media-play</property>
+    <property name="icon_size">3</property>
+  </object>
+  <object class="GtkTreeStore" id="store">
+    <columns>
+      <!-- column-name icon -->
+      <column type="GIcon"/>
+      <!-- column-name title -->
+      <column type="gchararray"/>
+      <!-- column-name id -->
+      <column type="gchararray"/>
+      <!-- column-name timestamp -->
+      <column type="guint64"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name desc -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkDialog" id="window">
+    <property name="width_request">530</property>
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">Snapshot management</property>
+    <property name="modal">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <signal name="destroy" handler="on_dialog_destroy" swapped="no"/>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="can_focus">False</property>
+        <property name="margin_left">8</property>
+        <property name="margin_right">8</property>
+        <property name="margin_top">8</property>
+        <property name="margin_bottom">8</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="can_focus">False</property>
+            <property name="margin_top">8</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="apply">
+                <property name="label">gtk-apply</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="apply_new_snapshot_info" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton">
+                <property name="label">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="close_dialog_box" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">8</property>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">8</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Server:</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkComboBoxText" id="servers">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <items>
+                      <item translatable="yes">Internal</item>
+                    </items>
+                    <signal name="changed" handler="on_server_selection_changed" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkScrolledWindow">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="shadow_type">in</property>
+                    <child>
+                      <object class="GtkTreeView" id="treeview">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="model">store</property>
+                        <property name="headers_visible">False</property>
+                        <child internal-child="selection">
+                          <object class="GtkTreeSelection">
+                            <signal name="changed" handler="on_tree_selection_changed" swapped="no"/>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkTreeViewColumn">
+                            <property name="title" translatable="yes">column</property>
+                            <child>
+                              <object class="GtkCellRendererPixbuf" id="snapicon"/>
+                              <attributes>
+                                <attribute name="gicon">0</attribute>
+                              </attributes>
+                            </child>
+                            <child>
+                              <object class="GtkCellRendererText"/>
+                              <attributes>
+                                <attribute name="text">1</attribute>
+                              </attributes>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolbar">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="orientation">vertical</property>
+                    <property name="show_arrow">False</property>
+                    <child>
+                      <object class="GtkToolButton" id="restore">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="tooltip_text" translatable="yes">Restore an old snapshot</property>
+                        <property name="label_widget">image-restore</property>
+                        <signal name="clicked" handler="restore_old_snapshot" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">False</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkSeparatorToolItem">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">True</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkToolButton" id="create">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="tooltip_text" translatable="yes">Create a new snapshot</property>
+                        <property name="label_widget">image-add</property>
+                        <signal name="clicked" handler="create_new_snapshot" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">True</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkToolButton" id="remove">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="tooltip_text" translatable="yes">Remove a snapshot from the snapshot tree</property>
+                        <property name="label_widget">image-remove</property>
+                        <signal name="clicked" handler="remove_old_snapshot" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">True</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkToolButton" id="delete">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="tooltip_text" translatable="yes">Delete a snapshot with all its children</property>
+                        <property name="label_widget">image-delete</property>
+                        <signal name="clicked" handler="delete_old_snapshot" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="homogeneous">True</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkFrame">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label_xalign">0</property>
+                <property name="shadow_type">none</property>
+                <child>
+                  <object class="GtkAlignment">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkGrid">
+                        <property name="name">œ</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="row_spacing">8</property>
+                        <property name="column_spacing">8</property>
+                        <property name="row_homogeneous">True</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Identifier:</property>
+                            <property name="xalign">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Timestamp:</property>
+                            <property name="xalign">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Name:</property>
+                            <property name="xalign">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Description:</property>
+                            <property name="xalign">1</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">3</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="identifier">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">-</property>
+                            <property name="selectable">True</property>
+                            <property name="xalign">0</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="timestamp">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="hexpand">True</property>
+                            <property name="label" translatable="yes">-</property>
+                            <property name="selectable">True</property>
+                            <property name="xalign">0</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="name">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="hexpand">True</property>
+                            <property name="placeholder_text" translatable="yes">No name</property>
+                            <property name="input_hints">GTK_INPUT_HINT_SPELLCHECK | GTK_INPUT_HINT_NONE</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="description">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="hexpand">True</property>
+                            <property name="placeholder_text" translatable="yes">No description</property>
+                            <property name="input_hints">GTK_INPUT_HINT_SPELLCHECK | GTK_INPUT_HINT_NONE</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child type="label">
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">Description</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child>
+      <placeholder/>
+    </child>
+  </object>
+</interface>
diff --git a/src/gui/menus/binary.c b/src/gui/menus/binary.c
index cb131d7..aeaf6f6 100644
--- a/src/gui/menus/binary.c
+++ b/src/gui/menus/binary.c
@@ -34,6 +34,7 @@
 #include "../dialogs/export_disass.h"
 #include "../dialogs/export_graph.h"
 #include "../dialogs/gotox.h"
+#include "../dialogs/snapshots.h"
 #include "../dialogs/storage.h"
 #include "../../gtkext/easygtk.h"
 #include "../../gtkext/gtkdisplaypanel.h"
@@ -47,9 +48,12 @@ static void mcb_binary_entry_points(GtkMenuItem *, GMenuBar *);
 /* Réagit au menu "Binaire -> Attacher un débogueur". */
 static void mcb_binary_attach_debugger(GtkMenuItem *, GMenuBar *);
 
-/* Réagit au menu "Binaire -> Enregistrements...". */
+/* Réagit au menu "Binaire -> Enregistrements". */
 static void mcb_binary_storage(GtkMenuItem *, GMenuBar *);
 
+/* Réagit au menu "Binaire -> Instantanés". */
+static void mcb_binary_snapshots(GtkMenuItem *, GMenuBar *);
+
 /* Réagit au menu "Binaire -> Exporter -> Désassemblage". */
 static void mcb_binary_export_disass(GtkMenuItem *, gpointer);
 
@@ -96,13 +100,21 @@ GtkWidget *build_menu_binary(GObject *ref, GMenuBar *bar)
                                        G_CALLBACK(mcb_binary_attach_debugger), bar);
     gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
 
+    /* Séparation */
+
     submenuitem = qck_create_menu_separator();
     gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
 
-    submenuitem = qck_create_menu_item(ref, "mnu_binary_storage", _("Storage..."),
+    /* Bases de données */
+
+    submenuitem = qck_create_menu_item(ref, "mnu_binary_storage", _("Storage"),
                                        G_CALLBACK(mcb_binary_storage), bar);
     gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
 
+    submenuitem = qck_create_menu_item(ref, "mnu_binary_snapshots", _("Snapshots"),
+                                       G_CALLBACK(mcb_binary_snapshots), bar);
+    gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
+
     /* Séparation */
 
     submenuitem = qck_create_menu_separator();
@@ -271,7 +283,7 @@ static void mcb_binary_attach_debugger(GtkMenuItem *menuitem, GMenuBar *bar)
 *  Paramètres  : menuitem = élément de menu sélectionné.                      *
 *                bar      = barre de menu parente.                            *
 *                                                                             *
-*  Description : Réagit au menu "Binaire -> Enregistrements...".              *
+*  Description : Réagit au menu "Binaire -> Enregistrements".                 *
 *                                                                             *
 *  Retour      : -                                                            *
 *                                                                             *
@@ -312,6 +324,45 @@ static void mcb_binary_storage(GtkMenuItem *menuitem, GMenuBar *bar)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : menuitem = élément de menu sélectionné.                      *
+*                bar      = barre de menu parente.                            *
+*                                                                             *
+*  Description : Réagit au menu "Binaire -> Instantanés".                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void mcb_binary_snapshots(GtkMenuItem *menuitem, GMenuBar *bar)
+{
+    GLoadedBinary *binary;                  /* Edition courante            */
+    GtkBuilder *builder;                    /* Constructeur utilisé        */
+    GtkWindow *editor;                      /* Fenêtre graphique principale*/
+    GtkWidget *dialog;                      /* Boîte de dialogue à montrer */
+
+    binary = G_LOADED_BINARY(get_current_content());
+
+    editor = get_editor_window();
+
+    dialog = create_snapshots_dialog(binary, editor, &builder);
+
+    gtk_dialog_run(GTK_DIALOG(dialog));
+
+    gtk_widget_destroy(dialog);
+
+    g_object_unref(G_OBJECT(builder));
+
+    g_object_unref(G_OBJECT(editor));
+
+    g_object_unref(G_OBJECT(binary));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : menuitem = élément de menu sélectionné.                      *
 *                unused   = adresse non utilisée ici.                         *
 *                                                                             *
 *  Description : Réagit au menu "Binaire -> Exporter -> Désassemblage".       *
-- 
cgit v0.11.2-87-g4458