From 1d79469f69bba33a2280d4bd531652b71148029f Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 9 Feb 2015 22:07:42 +0000
Subject: Created a dialog box to create new bookmarks.

git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@468 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
---
 ChangeLog                        |  64 +++++++
 src/analysis/binary.c            |  67 +------
 src/analysis/binary.h            |   4 +-
 src/analysis/db/Makefile.am      |   1 +
 src/analysis/db/cdb.c            |  44 +++--
 src/analysis/db/collection-int.h |  78 ++++++++
 src/analysis/db/collection.c     | 272 ++++++++++++++++++++++++----
 src/analysis/db/collection.h     |  28 ++-
 src/analysis/db/items/bookmark.c | 377 +++++++++++++++++++++++++++++++++++----
 src/analysis/db/items/bookmark.h |  27 +++
 src/analysis/db/server.c         |  11 +-
 src/arch/vmpa.c                  | 130 ++++++++++++--
 src/arch/vmpa.h                  |   6 +
 src/common/Makefile.am           |   2 +-
 src/common/sqlite.c              |  58 ++++++
 src/common/sqlite.h              |   6 +
 src/core/collections.c           |   5 +-
 src/dialogs/Makefile.am          |   1 +
 src/dialogs/bookmark.c           | 331 ++++++++++++++++++++++++++++++++++
 src/dialogs/bookmark.h           |  44 +++++
 src/dialogs/goto.c               |   2 +-
 src/dlg_sections.c               |   0
 src/dlg_sections.h               |   0
 src/gtkext/easygtk.c             |  40 +++++
 src/gtkext/easygtk.h             |   5 +
 src/gtkext/gtkbufferview.c       |  19 ++
 src/gtkext/gtkbufferview.h       |   8 +
 src/gui/menus/edition.c          | 122 ++++++++++++-
 src/gui/panels/bookmarks.c       |  89 ++++++++-
 29 files changed, 1678 insertions(+), 163 deletions(-)
 create mode 100644 src/analysis/db/collection-int.h
 create mode 100644 src/common/sqlite.c
 create mode 100644 src/dialogs/bookmark.c
 create mode 100644 src/dialogs/bookmark.h
 delete mode 100644 src/dlg_sections.c
 delete mode 100644 src/dlg_sections.h

diff --git a/ChangeLog b/ChangeLog
index 9b3fccd..96684d0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,69 @@
 15-02-09  Cyrille Bagard <nocbos@gmail.com>
 
+	* src/analysis/binary.c:
+	* src/analysis/binary.h:
+	Remove the old debug code registering one bookmark. Update code.
+
+	* src/analysis/db/cdb.c:
+	* src/analysis/db/collection.c:
+	* src/analysis/db/collection.h:
+	Reload collections from archives and send loaded items to clients.
+
+	* src/analysis/db/collection-int.h:
+	New entry: make the definitions external to allow inheritance.
+
+	* src/analysis/db/items/bookmark.c:
+	* src/analysis/db/items/bookmark.h:
+	Define collections dedicated to bookmarks.
+
+	* src/analysis/db/Makefile.am:
+	Add the 'collection-int.h' file to libanalysisdb_la_SOURCES.
+
+	* src/analysis/db/server.c:
+	Send all updates to new clients using debug code.
+
+	* src/arch/vmpa.c:
+	* src/arch/vmpa.h:
+	Fix the rendering of empty physical offsets. Load addresses from databases.
+
+	* src/common/Makefile.am:
+	Add the 'sqlite.c' file to libcommon_la_SOURCES.
+
+	* src/common/sqlite.c:
+	New entry: look for a given bound value.
+
+	* src/common/sqlite.h:
+	Update declarations.
+
+	* src/core/collections.c:
+	Write the first steps towards a new loading of collections.
+
+	* src/dialogs/bookmark.c:
+	* src/dialogs/bookmark.h:
+	New entries: create a dialog box to create new bookmarks.
+
+	* src/dialogs/goto.c:
+	Typo.
+
+	* src/dialogs/Makefile.am:
+	Add the 'bookmark.[ch]' files to libdialogs_la_SOURCES.
+
+	* src/gtkext/easygtk.c:
+	* src/gtkext/easygtk.h:
+	Provide a way to to quickly build message windows.
+
+	* src/gtkext/gtkbufferview.c:
+	* src/gtkext/gtkbufferview.h:
+	Provide the caret location on demand.
+
+	* src/gui/menus/edition.c:
+	Add a menu to toggle bookmarks.
+
+	* src/gui/panels/bookmarks.c:
+	Ask to get informed about each collection content change.
+
+15-02-09  Cyrille Bagard <nocbos@gmail.com>
+
 	* configure.ac:
 	Put the Python library ABI flags into LIBPYTHON_ABI_FLAGS.
 
diff --git a/src/analysis/binary.c b/src/analysis/binary.c
index 55b984a..96e774c 100644
--- a/src/analysis/binary.c
+++ b/src/analysis/binary.c
@@ -232,7 +232,7 @@ static void g_loaded_binary_finalize(GLoadedBinary *binary)
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
-#include "db/items/bookmark.h"
+
 GLoadedBinary *g_loaded_binary_new_from_xml(xmlXPathContextPtr context, const char *path)
 {
     GLoadedBinary *result;                  /* Adresse à retourner         */
@@ -303,61 +303,6 @@ GLoadedBinary *g_loaded_binary_new_from_xml(xmlXPathContextPtr context, const ch
 
     status = g_db_client_start(result->local, host, port, author);
 
-
-
-
-
-    /* --------- %< --------- %< --------- %< --------- %< --------- */
-
-    do
-    {
-        vmpa2t addr;
-        GDbBookmark *bm;
-        bool status;
-
-
-        init_vmpa(&addr, 0xaeb4, VMPA_NO_VIRTUAL);
-
-        bm = g_db_bookmark_new(&addr, "Premier commentaire");
-
-
-
-        status = g_loaded_binary_add_to_collection(result, DBF_BOOKMARKS, G_DB_ITEM(bm));
-
-        if (status)
-            printf("send OK\n");
-        else
-            printf("send nok\n");
-
-
-        g_db_client_save(result->local);
-
-
-
-        /*
-        safe_send(client->fd, (uint32_t []) { htobe32(DBC_COLLECTION) }, sizeof(uint32_t), MSG_MORE);
-        safe_send(client->fd, (uint32_t []) { htobe32(DBF_BOOKMARKS) }, sizeof(uint32_t), MSG_MORE);
-        safe_send(client->fd, (uint32_t []) { htobe32(DBA_ADD_ITEM) }, sizeof(uint32_t), MSG_MORE);
-
-        if (g_db_item_send(G_DB_ITEM(bm), client->fd, 0))
-            printf("send OK\n");
-        else
-            printf("send nok\n");
-
-        */
-
-
-    }
-    while (0);
-
-    /* --------- %< --------- %< --------- %< --------- %< --------- */
-
-
-
-
-
-
-
     printf("DB status :: %d\n", status);
 
     return result;
@@ -394,6 +339,11 @@ bool g_loaded_binary_save(const GLoadedBinary *binary, xmlDocPtr xdoc, xmlXPathC
 
     result = g_loaded_binary_save_storage(binary, xdoc, context, path);
 
+
+    ////
+    g_db_client_save(binary->local);
+
+
     return result;
 
 }
@@ -797,6 +747,7 @@ GDbCollection *g_loaded_binary_find_collection(GLoadedBinary *binary, DBFeatures
 *  Paramètres  : binary  = élément binaire à consulter.                       *
 *                feature = fonctionnalité visée par la requête.               *
 *                item    = élémnent à pousser vers un serveur de collection.  *
+*                lock    = indique si le verrou d'écriture doit être posé.    *
 *                                                                             *
 *  Description : Demande l'intégration d'une modification dans une collection.*
 *                                                                             *
@@ -806,7 +757,7 @@ GDbCollection *g_loaded_binary_find_collection(GLoadedBinary *binary, DBFeatures
 *                                                                             *
 ******************************************************************************/
 
-bool g_loaded_binary_add_to_collection(GLoadedBinary *binary, DBFeatures feature, GDbItem *item)
+bool _g_loaded_binary_add_to_collection(GLoadedBinary *binary, DBFeatures feature, GDbItem *item, bool lock)
 {
     bool result;                            /* Bilan à faire remonter      */
     GDbCollection *collec;                  /* Collection visée au final   */
@@ -819,7 +770,7 @@ bool g_loaded_binary_add_to_collection(GLoadedBinary *binary, DBFeatures feature
 
     /* S'il n'y a pas besoin de sauvegarde... */
     if (g_db_item_is_volatile(item))
-        g_db_collection_add_item(collec, item);
+        _g_db_collection_add_item(collec, item, lock);
 
     /* Sinon on envoie par le réseau ! */
     else
diff --git a/src/analysis/binary.h b/src/analysis/binary.h
index 625ff4a..26367c4 100644
--- a/src/analysis/binary.h
+++ b/src/analysis/binary.h
@@ -119,8 +119,10 @@ void g_loaded_binary_set_storage(GLoadedBinary *, DBFeatures, DBStorage);
 GDbCollection *g_loaded_binary_find_collection(GLoadedBinary *, DBFeatures);
 
 /* Demande l'intégration d'une modification dans une collection. */
-bool g_loaded_binary_add_to_collection(GLoadedBinary *, DBFeatures, GDbItem *);
+bool _g_loaded_binary_add_to_collection(GLoadedBinary *, DBFeatures, GDbItem *, bool);
 
+#define g_loaded_binary_add_to_collection(b, f, i) \
+    _g_loaded_binary_add_to_collection(b, f, i, true);
 
 
 
diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am
index 6852821..2e8e0f1 100755
--- a/src/analysis/db/Makefile.am
+++ b/src/analysis/db/Makefile.am
@@ -4,6 +4,7 @@ noinst_LTLIBRARIES  = libanalysisdb.la
 libanalysisdb_la_SOURCES =				\
 	cdb.h cdb.c							\
 	client.h client.c					\
+	collection-int.h					\
 	collection.h collection.c			\
 	core.h core.c						\
 	item-int.h							\
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index 24da20e..014a6d8 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -328,7 +328,7 @@ GCdbArchive *g_cdb_archive_new(const char *owner, const rle_string *hash, const
 
     /* Ouverture de l'archive */
 
-    if (!g_cdb_archive_read(result) && 0)
+    if (!g_cdb_archive_read(result))
         goto gcan_error;
 
     /* Chargement des éléments sauvegardés */
@@ -712,6 +712,7 @@ static bool g_cdb_archive_create_db(const GCdbArchive *archive, const core_db_in
 static bool g_cdb_archive_load_collections(GCdbArchive *archive)
 {
     GList *iter;                            /* Boucle de parcours          */
+    GDbCollection *collec;                  /* Collection visée manipulée  */
 
     archive->collections = create_collections_list();
 
@@ -719,7 +720,12 @@ static bool g_cdb_archive_load_collections(GCdbArchive *archive)
          iter != NULL;
          iter = g_list_next(iter))
     {
-        g_signal_connect(iter->data, "content-changed", G_CALLBACK(on_collection_changed), archive);
+        collec = G_DB_COLLECTION(iter->data);
+        g_signal_connect(collec, "content-changed", G_CALLBACK(on_collection_changed), archive);
+
+        if (!g_db_collection_load_all_items(collec, archive->db))
+            return false;
+
     }
 
     return true;
@@ -762,7 +768,7 @@ static void on_collection_changed(GDbCollection *collec, DBAction action, GDbIte
 
     g_mutex_unlock(&archive->clients_access);
 
-    printf("CHANGED !!\n");
+    printf("CHANGED for %d clients !!\n", (int)archive->count);
 
 
 
@@ -947,6 +953,8 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 DBError g_cdb_archive_add_client(GCdbArchive *archive, int fd, const rle_string *user)
 {
 
+    GList *iter;                            /* Boucle de parcours          */
+    GDbCollection *collec;                  /* Collection visée manipulée  */
     volatile pthread_t *process_id;         /* Identifiant de la procédure */
 
 
@@ -964,6 +972,28 @@ DBError g_cdb_archive_add_client(GCdbArchive *archive, int fd, const rle_string
     archive->clients[archive->count - 1].fd = fd;
     dup_rle_string(&archive->clients[archive->count - 1].user, user);
 
+
+
+    /* Envoi des mises à jour au nouveau client... */
+
+
+    for (iter = g_list_first(archive->collections);
+         iter != NULL;
+         iter = g_list_next(iter))
+    {
+        collec = G_DB_COLLECTION(iter->data);
+
+        if (!g_db_collection_send_all_updates(collec, fd))
+            /* TODO */;
+
+
+
+
+
+    }
+
+
+
     /* Démarrage ou redémarrage du processus d'écoute */
 
     if (archive->process == NULL)
@@ -979,14 +1009,8 @@ DBError g_cdb_archive_add_client(GCdbArchive *archive, int fd, const rle_string
 
     g_mutex_unlock(&archive->clients_access);
 
-    /* Envoi des mises à jour au nouveau client... */
-
-
-    /* TODO */
-
-
 
-    return DBE_NONE;
+    return DBE_NONE;    ////
 
 }
 
diff --git a/src/analysis/db/collection-int.h b/src/analysis/db/collection-int.h
new file mode 100644
index 0000000..6ca9ab4
--- /dev/null
+++ b/src/analysis/db/collection-int.h
@@ -0,0 +1,78 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * collection-int.h - prototypes et définitions internes pour les collections d'éléments
+ *
+ * Copyright (C) 2015 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA 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.
+ *
+ *  OpenIDA 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _ANALYSIS_DB_COLLECTION_INT_H
+#define _ANALYSIS_DB_COLLECTION_INT_H
+
+
+#include "collection.h"
+
+
+#include <stdarg.h>
+
+
+
+/* Décrit les colonnes utiles à un chargement de données. */
+typedef bool (* collec_setup_load_fc) (GDbCollection *, bound_value **, size_t *);
+
+/* Charge les valeurs utiles pour une localisation. */
+typedef bool (* collec_load_item) (GDbCollection *, const bound_value *, size_t);
+
+/* Détermine si un élément est déjà présent ou non. */
+typedef GDbItem * (* collec_has_key_fc) (GDbCollection *, va_list *);
+
+
+
+/* Collection générique d'éléments (instance) */
+struct _GDbCollection
+{
+    GObject parent;                         /* A laisser en premier        */
+
+    uint32_t featuring;                     /* Fonctionnalité représentée  */
+    GType type;                             /* Identifiant GLib équivalent */
+    const char *name;                       /* Nom en base de données      */
+
+    GList *items;                           /* Eléments rassemblés         */
+    GList *sorted;                          /* Eléments triés              */
+    GRWLock params_access;                  /* Verrou de protection        */
+
+};
+
+/* Collection générique d'éléments (classe) */
+struct _GDbCollectionClass
+{
+    GObjectClass parent;                    /* A laisser en premier        */
+
+    collec_setup_load_fc setup_load;        /* Prépare le chargement       */
+    collec_load_item load_item;             /* Charge un élément           */
+    collec_has_key_fc has_key;              /* Recherche de présence       */
+
+    /* Signaux */
+
+    void (* content_changed) (GDbCollection *, DBAction, GDbItem *);
+
+};
+
+
+
+#endif  /* _ANALYSIS_DB_COLLECTION_INT_H */
diff --git a/src/analysis/db/collection.c b/src/analysis/db/collection.c
index 5c25720..84acc58 100644
--- a/src/analysis/db/collection.c
+++ b/src/analysis/db/collection.c
@@ -24,41 +24,19 @@
 #include "collection.h"
 
 
+#include <assert.h>
+#include <malloc.h>
 #include <stdio.h>
 #include <string.h>
 
 
+#include "collection-int.h"
 #include "../../common/extstr.h"
 #include "../../common/io.h"
 #include "../../glibext/chrysamarshal.h"
 
 
 
-/* Collection générique d'éléments (instance) */
-struct _GDbCollection
-{
-    GObject parent;                         /* A laisser en premier        */
-
-    uint32_t featuring;                     /* Fonctionnalité représentée  */
-    GType type;                             /* Identifiant GLib équivalent */
-    const char *name;                       /* Nom en base de données      */
-
-    GList *items;                           /* Eléments rassemblés         */
-    GList *sorted;                          /* Eléments triés              */
-    GRWLock params_access;                  /* Verrou de protection        */
-
-};
-
-/* Collection générique d'éléments (classe) */
-struct _GDbCollectionClass
-{
-    GObjectClass parent;                    /* A laisser en premier        */
-
-    /* Signaux */
-
-    void (* content_changed) (GDbCollection *, DBAction, GDbItem *);
-
-};
 
 
 
@@ -80,6 +58,9 @@ static void g_db_collection_finalize(GDbCollection *);
 /* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
 
 
+/* Décrit les colonnes utiles à un chargement de données. */
+static bool g_db_collection_setup_load(GDbCollection *, bound_value **, size_t *);
+
 /* Enregistre un élément de collection dans une base de données. */
 static bool g_db_collection_store_item(const GDbCollection *, const GDbItem *, sqlite3 *);
 
@@ -345,8 +326,37 @@ bool g_db_collection_send(GDbCollection *collec, int fd, DBAction action, GDbIte
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à considérer.                   *
+*                fd     = flux ouvert en écriture pour l'émission de données. *
+*                                                                             *
+*  Description : Envoie pour mise à jour tous les éléments courants.          *
+*                                                                             *
+*  Retour      : Bilan de l'exécution de l'opération.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
 
+bool g_db_collection_send_all_updates(GDbCollection *collec, int fd)
+{
+    bool result;                            /* Bilan à renvoyer            */
+    GList *iter;                            /* Boucle de parcours          */
+
+    result = true;
+
+    for (iter = g_list_first(collec->items);
+         iter != NULL && result;
+         iter = g_list_next(iter))
+    {
+        result = g_db_collection_send(collec, fd, DBA_ADD_ITEM, G_DB_ITEM(iter->data));
+
+    }
+
+    return result;
 
+}
 
 
 
@@ -408,17 +418,76 @@ GList *g_db_collection_list_items(const GDbCollection *collec)
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à consulter.                    *
+*                ...    = clef identifiant de manière unique un élément.      *
+*                                                                             *
+*  Description : Détermine si un élément est déjà présent ou non.             *
+*                                                                             *
+*  Retour      : Elément trouvé ou NULL si aucun.                             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GDbItem *g_db_collection_has_key(GDbCollection *collec, ...)
+{
+    GDbItem *result;                        /* Bilan à retourner           */
+    va_list ap;                             /* Liste d'arguments en plus   */
+
+    va_start(ap, collec);
+
+    result = G_DB_COLLECTION_GET_CLASS(collec)->has_key(collec, &ap);
+
+    va_end(ap);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à consulter.                    *
+*                item   = élément complet dont un double est à rechercher.    *
+*                                                                             *
+*  Description : Détermine si un élément est déjà présent ou non.             *
+*                                                                             *
+*  Retour      : true si un élément similaire est présent dans la collection. *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
 
+bool g_db_collection_has_item(GDbCollection *collec, GDbItem *item)
+{
+    bool result;                            /* Bilan à retourner           */
+    GList *found;                           /* Test de présence existante  */
+
+    /**
+     * Un verrou doit être posé !
+     * Il n'y a pas d'assert() possible pour le vérifier...
+     */
+
+    printf(" --- has\n");
+
+    found = g_list_find_custom(collec->items, item, (GCompareFunc)g_db_item_compare);
 
+    printf(" --- has: %p\n", found);
 
+    result = (found != NULL);
 
+    return result;
 
+}
 
 
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : collec = ensemble d'éléments à considérer.                   *
 *                item   = élément de collection à manipuler.                  *
+*                lock   = indique si le verrou d'écriture doit être posé.     *
 *                                                                             *
 *  Description : Procède à l'ajout d'un nouvel élément dans la collection.    *
 *                                                                             *
@@ -428,12 +497,13 @@ GList *g_db_collection_list_items(const GDbCollection *collec)
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_collection_add_item(GDbCollection *collec, GDbItem *item)
+bool _g_db_collection_add_item(GDbCollection *collec, GDbItem *item, bool lock)
 {
     bool result;                            /* Bilan à faire remonter      */
     GList *found;                           /* Test de présence existante  */
 
-    g_db_collection_wlock(collec);
+    if (lock)
+        g_db_collection_wlock(collec);
 
     found = g_list_find_custom(collec->items, item, (GCompareFunc)g_db_item_compare);
 
@@ -451,14 +521,15 @@ bool g_db_collection_add_item(GDbCollection *collec, GDbItem *item)
         g_signal_emit_by_name(collec, "content-changed", DBA_ADD_ITEM, item);
 
 
-        printf(" ==== CONTENT CHANGED !!!\n");
+        printf(" ==== CONTENT CHANGED (-> %u) !!!\n", g_list_length(collec->items));
 
 
         result = true;
 
     }
 
-    g_db_collection_wunlock(collec);
+    if (lock)
+        g_db_collection_wunlock(collec);
 
     return result;
 
@@ -469,6 +540,7 @@ bool g_db_collection_add_item(GDbCollection *collec, GDbItem *item)
 *                                                                             *
 *  Paramètres  : collec = ensemble d'éléments à considérer.                   *
 *                item   = élément de collection à copier.                     *
+*                lock   = indique si le verrou d'écriture doit être posé.     *
 *                                                                             *
 *  Description : Procède à la modification d'un élément dans la collection.   *
 *                                                                             *
@@ -478,7 +550,7 @@ bool g_db_collection_add_item(GDbCollection *collec, GDbItem *item)
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_collection_modify_item(GDbCollection *collec, GDbItem *item)
+bool _g_db_collection_modify_item(GDbCollection *collec, GDbItem *item, bool lock)
 {
     bool result;                            /* Bilan à faire remonter      */
     GList *found;                           /* Test de présence existante  */
@@ -499,15 +571,151 @@ bool g_db_collection_modify_item(GDbCollection *collec, GDbItem *item)
 
 
 
-
 /* ---------------------------------------------------------------------------------- */
-/*                     CREATION DE L'ABSTRACTION POUR COLLECTIONS                     */
 /*                       MANIPULATIONS AVEC UNE BASE DE DONNEES                       */
 /* ---------------------------------------------------------------------------------- */
 
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à consulter.                    *
+*                values = tableau d'éléments à compléter. [OUT]               *
+*                count  = nombre de descriptions renseignées. [OUT]           *
+*                                                                             *
+*  Description : Décrit les colonnes utiles à un chargement de données.       *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_db_collection_setup_load(GDbCollection *collec, bound_value **values, size_t *count)
+{
+    *values = NULL;
+    *count = 0;
+    
+    return G_DB_COLLECTION_GET_CLASS(collec)->setup_load(collec, values, count);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à peupler.                      *
+*                db     = base de données repondant aux requêtes.             *
+*                                                                             *
+*  Description : Charge un ensemble d'éléments à partir d'une base de données.*
+*                                                                             *
+*  Retour      : Bilan de l'exécution de l'opération.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_db_collection_load_all_items(GDbCollection *collec, sqlite3 *db)
+{
+    bool result;                            /* Conclusion à faire remonter */
+    bound_value *values;                    /* Champs de table à inclure   */
+    size_t count;                           /* Nombre de ces champs        */
+    char *sql;                              /* Requête SQL à construire    */
+    size_t i;                               /* Boucle de parcours          */
+    sqlite3_stmt *stmt;                     /* Déclaration mise en place   */
+    int ret;                                /* Bilan d'un appel à SQLite   */
+
+    if (!g_db_collection_setup_load(collec, &values, &count))
+        return false;
+
+    result = false;
+
+    /* Préparation de la requête */
+
+    sql = strdup("SELECT ");
+
+    for (i = 0; i < count; i++)
+    {
+        if (i > 0) sql = stradd(sql, ", ");
+
+        sql = stradd(sql, values[i].name);
+
+    }
+
+    sql = stradd(sql, " FROM ");
+    sql = stradd(sql, collec->name);
+    sql = stradd(sql, ";");
+
+	ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+	if (ret != SQLITE_OK)
+    {
+		fprintf(stderr, "Can't prepare SELECT statment '%s' (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+        goto gdclai_exit;
+	}
+
+    /* Chargement des valeurs existantes */
+
+    result = true;
+
+    for (ret = sqlite3_step(stmt); ret == SQLITE_ROW && result; ret = sqlite3_step(stmt))
+    {
+        /* Conversion des valeurs */
+
+        for (i = 0; i < count; i++)
+        {
+            values[i].type = sqlite3_column_type(stmt, i);
+
+            switch (values[i].type)
+            {
+                case SQLITE_INTEGER:
+                    values[i].type = SQLITE_INT64;
+                    values[i].integer64 = sqlite3_column_int64(stmt, i);
+                    break;
+
+                case SQLITE_FLOAT:
+                    assert(0); /* TODO */
+                    break;
+
+                case SQLITE_TEXT:
+                    values[i].cstring = (const char *)sqlite3_column_text(stmt, i);
+                    break;
+
+                case SQLITE_BLOB:
+                    assert(0); /* TODO */
+                    break;
+
+                case SQLITE_NULL:
+                    break;
+
+                default:
+                    assert(0);
+                    break;
+
+            }
+
+        }
+
+        /* Chargement d'un nouvel élément */
+
+        result = G_DB_COLLECTION_GET_CLASS(collec)->load_item(collec, values, count);
+
+    }
+
+    /* Sortie propre */
+
+    sqlite3_finalize(stmt);
+
+ gdclai_exit:
+
+    free(sql);
+
+    printf("LOAD ? %d\n", result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : collec = ensemble d'éléments à considérer.                   *
 *                item   = élément de collection à enregistrer.                *
 *                db     = base de données à mettre à jour.                    *
diff --git a/src/analysis/db/collection.h b/src/analysis/db/collection.h
index b400820..268c2be 100644
--- a/src/analysis/db/collection.h
+++ b/src/analysis/db/collection.h
@@ -39,10 +39,14 @@
 #define G_TYPE_DB_COLLECTION               g_db_collection_get_type()
 #define G_DB_COLLECTION(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_db_collection_get_type(), GDbCollection))
 #define G_IS_DB_COLLECTION(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_db_collection_get_type()))
-#define G_DB_COLLECTION_GET_IFACE(inst)    (G_TYPE_INSTANCE_GET_INTERFACE((inst), g_db_collection_get_type(), GDbCollectionIface))
+#define G_DB_COLLECTION_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DB_COLLECTION, GDbCollectionClass))
+#define G_IS_DB_COLLECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DB_COLLECTION))
 #define G_DB_COLLECTION_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DB_COLLECTION, GDbCollectionClass))
 
 
+
+
+
 /* Collection générique d'éléments (instance) */
 typedef struct _GDbCollection GDbCollection;
 
@@ -70,7 +74,8 @@ bool g_db_collection_recv(GDbCollection *, int, sqlite3 *);
 /* Envoie pour traitement une requête réseau pour collection. */
 bool g_db_collection_send(GDbCollection *, int, DBAction, GDbItem *);
 
-
+/* Envoie pour mise à jour tous les éléments courants. */
+bool g_db_collection_send_all_updates(GDbCollection *, int);
 
 
 
@@ -89,15 +94,28 @@ void g_db_collection_lock_unlock(GDbCollection *, bool, bool);
 /* Renvoie la liste des éléments rassemblés. */
 GList *g_db_collection_list_items(const GDbCollection *);
 
+/* Détermine si un élément est déjà présent ou non. */
+GDbItem *g_db_collection_has_key(GDbCollection *, ...);
 
-
+/* Détermine si un élément est déjà présent ou non. */
+bool g_db_collection_has_item(GDbCollection *, GDbItem *);
 
 /* Procède à l'ajout d'un nouvel élément dans la collection. */
-bool g_db_collection_add_item(GDbCollection *, GDbItem *);
+bool _g_db_collection_add_item(GDbCollection *, GDbItem *, bool);
 
 /* Procède à la modification d'un élément dans la collection. */
-bool g_db_collection_modify_item(GDbCollection *, GDbItem *);
+bool _g_db_collection_modify_item(GDbCollection *, GDbItem *, bool);
+
+#define g_db_collection_add_item(c, i) _g_db_collection_add_item(c, i, true)
+#define g_db_collection_modify_item(c, i) _g_db_collection_modify_item(c, i, true)
+
+
+
+/* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
+
 
+/* Charge un ensemble d'éléments à partir d'une base de données. */
+bool g_db_collection_load_all_items(GDbCollection *, sqlite3 *);
 
 
 
diff --git a/src/analysis/db/items/bookmark.c b/src/analysis/db/items/bookmark.c
index 6ff386c..a8182ce 100644
--- a/src/analysis/db/items/bookmark.c
+++ b/src/analysis/db/items/bookmark.c
@@ -28,11 +28,18 @@
 #include <sys/socket.h>
 
 
+#include "../collection-int.h"
 #include "../item-int.h"
 #include "../misc/rlestr.h"
 
 
 
+
+
+
+/* --------------------- ELABORATION D'UN ELEMENT DE COLLECTION --------------------- */
+
+
 /* Signet à l'intérieur d'une zone de texte (instance) */
 struct _GDbBookmark
 {
@@ -81,48 +88,54 @@ static bool g_db_bookmark_prepare_db_statement(const GDbBookmark *, bool, bound_
 
 
 
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : db = accès à la base de données.                             *
-*                                                                             *
-*  Description : Crée la table des signets dans une base de données.          *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
 
-bool create_bookmark_db_table(sqlite3 *db)
+
+/* ---------------------- DEFINITION DE LA COLLECTION ASSOCIEE ---------------------- */
+
+
+
+
+/* Collection dédiée aux signets (instance) */
+struct _GBookmarkCollection
 {
-    char *sql;                              /* Requête à exécuter          */
-    int ret;                                /* Bilan de la création        */
-    char *msg;                              /* Message d'erreur            */
+    GDbCollection parent;                   /* A laisser en premier        */
 
-    sql = "CREATE TABLE Bookmarks ("            \
-             SQLITE_DB_ITEM_CREATE              \
-             SQLITE_VMPA_CREATE                 \
-             "comment TEXT"                     \
-          ");";
+};
 
-    ret = sqlite3_exec(db, sql, NULL, NULL, &msg);
-    if (ret != SQLITE_OK)
-    {
-        fprintf(stderr, "sqlite3_exec(): %s\n", msg);
-        sqlite3_free(msg);
-    }
+/* Collection dédiée aux signets (classe) */
+struct _GBookmarkCollectionClass
+{
+    GDbCollectionClass parent;              /* A laisser en premier        */
 
-    return (ret == SQLITE_OK);
+};
 
-}
 
+/* Initialise la classe des signets dans une zone de texte. */
+static void g_bookmark_collection_class_init(GBookmarkCollectionClass *);
 
+/* Initialise un signet dans une zone de texte. */
+static void g_bookmark_collection_init(GBookmarkCollection *);
 
+/* Supprime toutes les références externes. */
+static void g_bookmark_collection_dispose(GBookmarkCollection *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_bookmark_collection_finalize(GBookmarkCollection *);
+
+/* Décrit les colonnes utiles à un chargement de données. */
+static bool g_bookmark_collection_setup_load(GBookmarkCollection *, bound_value **, size_t *);
 
+/* Charge les valeurs utiles pour une localisation. */
+static bool g_bookmark_collection_load_item(GBookmarkCollection *, const bound_value *, size_t);
 
+/* Détermine si un élément est déjà présent ou non. */
+static GDbItem *g_bookmark_collection_has_key(GBookmarkCollection *, va_list *);
 
 
 
+/* ---------------------------------------------------------------------------------- */
+/*                       ELABORATION D'UN ELEMENT DE COLLECTION                       */
+/* ---------------------------------------------------------------------------------- */
 
 
 /* Indique le type défini pour un signet à l'intérieur d'une zone de texte. */
@@ -269,12 +282,12 @@ static gint g_db_bookmark_cmp(GDbBookmark *a, GDbBookmark *b)
 {
     gint result;                            /* Bilan de la comparaison     */
 
-    result = cmp_vmpa_by_phy(&a->addr, &b->addr);
+    result = cmp_vmpa(&a->addr, &b->addr);
 
     if (result == 0)
         result = cmp_rle_string(&a->comment, &b->comment);
 
-    return 0;
+    return result;
 
 
 }
@@ -451,3 +464,307 @@ void g_db_bookmark_set_comment(GDbBookmark *bookmark, const char *comment)
     set_rle_string(&bookmark->comment, comment);
 
 }
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                        DEFINITION DE LA COLLECTION ASSOCIEE                        */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une collection de signets. */
+G_DEFINE_TYPE(GBookmarkCollection, g_bookmark_collection, G_TYPE_DB_COLLECTION);
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : klass = classe à initialiser.                                *
+*                                                                             *
+*  Description : Initialise la classe des signets dans une zone de texte.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bookmark_collection_class_init(GBookmarkCollectionClass *klass)
+{
+    GObjectClass *object;                   /* Autre version de la classe  */
+    GDbCollectionClass *collec;             /* Encore une autre vision...  */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_bookmark_collection_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_bookmark_collection_finalize;
+
+    collec = G_DB_COLLECTION_CLASS(klass);
+
+    collec->setup_load = (collec_setup_load_fc)g_bookmark_collection_setup_load;
+    collec-> load_item = (collec_load_item)g_bookmark_collection_load_item;
+    collec->has_key = (collec_has_key_fc)g_bookmark_collection_has_key;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = instance à initialiser.                             *
+*                                                                             *
+*  Description : Initialise un signet dans une zone de texte.                 *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bookmark_collection_init(GBookmarkCollection *collec)
+{
+
+
+    G_DB_COLLECTION(collec)->featuring = 0;
+    G_DB_COLLECTION(collec)->type = G_TYPE_DB_BOOKMARK;
+    G_DB_COLLECTION(collec)->name = "Bookmarks";
+
+
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = instance d'objet GLib à traiter.                    *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bookmark_collection_dispose(GBookmarkCollection *collec)
+{
+    G_OBJECT_CLASS(g_bookmark_collection_parent_class)->dispose(G_OBJECT(collec));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : bookmark = instance d'objet GLib à traiter.                  *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bookmark_collection_finalize(GBookmarkCollection *collec)
+{
+    G_OBJECT_CLASS(g_bookmark_collection_parent_class)->finalize(G_OBJECT(collec));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Crée une collection dédiée aux signets.                      *
+*                                                                             *
+*  Retour      : Collection mise en place.                                    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GBookmarkCollection *g_bookmark_collection_new(void)
+{
+    GBookmarkCollection *result;            /* Instance à retourner        */
+
+    result = g_object_new(G_TYPE_BM_COLLECTION, NULL);
+
+    return result;
+
+}
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : db = accès à la base de données.                             *
+*                                                                             *
+*  Description : Crée la table des signets dans une base de données.          *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool create_bookmark_db_table(sqlite3 *db)
+{
+    char *sql;                              /* Requête à exécuter          */
+    int ret;                                /* Bilan de la création        */
+    char *msg;                              /* Message d'erreur            */
+
+    sql = "CREATE TABLE Bookmarks ("            \
+             SQLITE_DB_ITEM_CREATE              \
+             SQLITE_VMPA_CREATE                 \
+             "comment TEXT"                     \
+          ");";
+
+    ret = sqlite3_exec(db, sql, NULL, NULL, &msg);
+    if (ret != SQLITE_OK)
+    {
+        fprintf(stderr, "sqlite3_exec(): %s\n", msg);
+        sqlite3_free(msg);
+    }
+
+    return (ret == SQLITE_OK);
+
+}
+
+
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à consulter.                    *
+*                values = tableau d'éléments à compléter. [OUT]               *
+*                count  = nombre de descriptions renseignées. [OUT]           *
+*                                                                             *
+*  Description : Décrit les colonnes utiles à un chargement de données.       *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_bookmark_collection_setup_load(GBookmarkCollection *collec, bound_value **values, size_t *count)
+{
+    // TODO : classe supérieure
+
+    if (!setup_load_for_vmpa(NULL, values, count))
+        return false;
+
+    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+
+    (*values)[*count - 1].name = "comment";
+
+    return true;
+
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à compléter.                    *
+*                values = tableau d'éléments à consulter.                     *
+*                count  = nombre de descriptions renseignées.                 *
+*                                                                             *
+*  Description : Charge les valeurs utiles pour une localisation.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_bookmark_collection_load_item(GBookmarkCollection *collec, const bound_value *values, size_t count)
+{
+    vmpa2t addr;                            /* Localisation d'un signet    */
+    const char *comment;                    /* Eventuel commentaire associé*/
+    const bound_value *value;               /* Valeur à intégrer           */
+    GDbBookmark *new;                       /* Nouvel élément à intégrer   */
+
+    if (!load_vmpa(&addr, values, count))
+        return false;
+
+    value = find_bound_value(values, count, "comment");
+    if (value == NULL) return false;
+
+    switch (value->type)
+    {
+        case SQLITE_TEXT:
+            comment = value->cstring;
+            break;
+
+        case SQLITE_NULL:
+            comment = NULL;
+            break;
+
+        default:
+            return false;
+            break;
+
+    }
+
+    new = g_db_bookmark_new(&addr, comment);
+
+    printf(" LOAD new bm :: %p\n", new);
+
+    return g_db_collection_add_item(G_DB_COLLECTION(collec), G_DB_ITEM(new));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = ensemble d'éléments à consulter.                    *
+*                ap     = clef identifiant de manière unique un élément.      *
+*                                                                             *
+*  Description : Détermine si un élément est déjà présent ou non.             *
+*                                                                             *
+*  Retour      : Elément trouvé ou NULL si aucun.                             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static GDbItem *g_bookmark_collection_has_key(GBookmarkCollection *collec, va_list *ap)
+{
+    GDbItem *result;                        /* Bilan à retourner           */
+    vmpa2t *ref;                            /* Adresse de référence        */
+    GList *items;                           /* Eléments déjà en place      */
+    GList *iter;                            /* Boucle de parcours          */
+    GDbBookmark *bm;                        /* Signet à consulter          */
+
+    result = NULL;
+
+    ref = va_arg(ap, vmpa2t *);
+
+    items = g_db_collection_list_items(G_DB_COLLECTION(collec));
+
+    for (iter = g_list_first(items); iter != NULL && result == NULL; iter = g_list_next(iter))
+    {
+        bm = G_DB_BOOKMARK(iter->data);
+
+        if (cmp_vmpa(&bm->addr, ref) != 0)
+
+            /**
+             * Un verrou est sensé être posé, donc il n'y a pas besoin
+             * de toucher au décompte des références car l'élément trouvé
+             * ne peut pas être supprimé.
+             */
+            result = G_DB_ITEM(bm);
+
+    }
+
+    return result;
+
+}
diff --git a/src/analysis/db/items/bookmark.h b/src/analysis/db/items/bookmark.h
index cd5e202..de12b1b 100644
--- a/src/analysis/db/items/bookmark.h
+++ b/src/analysis/db/items/bookmark.h
@@ -42,6 +42,7 @@ bool create_bookmark_db_table(sqlite3 *);
 
 
 
+/* --------------------- ELABORATION D'UN ELEMENT DE COLLECTION --------------------- */
 
 
 #define G_TYPE_DB_BOOKMARK               g_db_bookmark_get_type()
@@ -76,4 +77,30 @@ void g_db_bookmark_set_comment(GDbBookmark *, const char *);
 
 
 
+/* ---------------------- DEFINITION DE LA COLLECTION ASSOCIEE ---------------------- */
+
+
+#define G_TYPE_BM_COLLECTION               g_bookmark_collection_get_type()
+#define G_BM_COLLECTION(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_bookmark_collection_get_type(), GBookmarkCollection))
+#define G_IS_BM_COLLECTION(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_bookmark_collection_get_type()))
+#define G_BM_COLLECTION_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BM_COLLECTION, GBookmarkCollectionClass))
+#define G_IS_BM_COLLECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BM_COLLECTION))
+#define G_BM_COLLECTION_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BM_COLLECTION, GBookmarkCollectionClass))
+
+
+/* Collection dédiée aux signets (instance) */
+typedef struct _GBookmarkCollection GBookmarkCollection;
+
+/* Collection dédiée aux signets (classe) */
+typedef struct _GBookmarkCollectionClass GBookmarkCollectionClass;
+
+
+/* Indique le type défini pour une collection de signets. */
+GType g_bookmark_collection_get_type(void);
+
+/* Crée une collection dédiée aux signets. */
+GBookmarkCollection *g_bookmark_collection_new(void);
+
+
+
 #endif  /* _ANALYSIS_DB_ITEMS_BOOKMARK_H */
diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c
index 42688c8..04b0dc4 100644
--- a/src/analysis/db/server.c
+++ b/src/analysis/db/server.c
@@ -365,8 +365,10 @@ static void *g_db_server_listener(GDbServer *server)
 
             }
 
+            /*
             if (archive != NULL)
                 error = g_cdb_archive_add_client(archive, fd, &user);
+            */
 
             /**
              * Le serveur doit répondre pour un message type :
@@ -382,7 +384,14 @@ static void *g_db_server_listener(GDbServer *server)
             if (!safe_send(fd, (uint32_t []) { htobe32(error) }, sizeof(uint32_t), 0))
                 goto gdsl_error;
 
-            if (error == DBE_NONE) continue;
+            //if (error == DBE_NONE) continue;
+
+
+            if (archive != NULL)
+                error = g_cdb_archive_add_client(archive, fd, &user);
+
+
+
 
  gdsl_error:
 
diff --git a/src/arch/vmpa.c b/src/arch/vmpa.c
index 46d1a04..c479319 100644
--- a/src/arch/vmpa.c
+++ b/src/arch/vmpa.c
@@ -26,6 +26,7 @@
 
 #include <inttypes.h>
 #include <malloc.h>
+#include <sqlite3.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -356,29 +357,33 @@ char *vmpa2_phys_to_string(const vmpa2t *addr, MemoryDataSize msize, char buffer
 {
     size_t ret;                             /* Retour de l'impression      */
 
-    switch (msize)
-    {
-        case MDS_8_BITS:
-            ret = snprintf(buffer, VMPA_MAX_LEN,"0x%02" PRIx64, (uint64_t)addr->physical);
-            break;
+	if (addr->physical == VMPA_NO_PHYSICAL)
+		ret = snprintf(buffer, VMPA_MAX_LEN, _("(none)"));
 
-        case MDS_16_BITS:
-            ret = snprintf(buffer, VMPA_MAX_LEN, "0x%04" PRIx64, (uint64_t)addr->physical);
-            break;
+	else
+        switch (msize)
+        {
+            case MDS_8_BITS:
+                ret = snprintf(buffer, VMPA_MAX_LEN,"0x%02" PRIx64, (uint64_t)addr->physical);
+                break;
 
-        case MDS_32_BITS:
-            ret = snprintf(buffer, VMPA_MAX_LEN, "0x%08" PRIx64, (uint64_t)addr->physical);
-            break;
+            case MDS_16_BITS:
+                ret = snprintf(buffer, VMPA_MAX_LEN, "0x%04" PRIx64, (uint64_t)addr->physical);
+                break;
 
-        case MDS_64_BITS:
-            ret = snprintf(buffer, VMPA_MAX_LEN, "0x%016" PRIx64, (uint64_t)addr->physical);
-            break;
+            case MDS_32_BITS:
+                ret = snprintf(buffer, VMPA_MAX_LEN, "0x%08" PRIx64, (uint64_t)addr->physical);
+                break;
 
-        default:
-            ret = snprintf(buffer, VMPA_MAX_LEN, "0x%" PRIx64, (uint64_t)addr->physical);
-            break;
+            case MDS_64_BITS:
+                ret = snprintf(buffer, VMPA_MAX_LEN, "0x%016" PRIx64, (uint64_t)addr->physical);
+                break;
 
-    }
+            default:
+                ret = snprintf(buffer, VMPA_MAX_LEN, "0x%" PRIx64, (uint64_t)addr->physical);
+                break;
+
+        }
 
     if (length != NULL)
         *length = ret;
@@ -491,6 +496,95 @@ vmpa2t *string_to_vmpa_virt(const char *buffer)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : addr   = localisation dont la définition est à consulter.    *
+*                values = tableau d'éléments à compléter. [OUT]               *
+*                count  = nombre de descriptions renseignées. [OUT]           *
+*                                                                             *
+*  Description : Décrit les colonnes utiles à un chargement de données.       *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool setup_load_for_vmpa(const vmpa2t *addr, bound_value **values, size_t *count)
+{
+    (*count) += 2;
+
+    *values = (bound_value *)realloc(*values, (*count) * sizeof(bound_value));
+
+    (*values)[*count - 2].name = "phys";
+    (*values)[*count - 1].name = "virt";
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : addr   = localisation dont la définition est à définir.      *
+*                values = tableau d'éléments à consulter.                     *
+*                count  = nombre de descriptions renseignées.                 *
+*                                                                             *
+*  Description : Charge les valeurs utiles pour une localisation.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool load_vmpa(vmpa2t *addr, const bound_value *values, size_t count)
+{
+    const bound_value *value;               /* Valeur à intégrer           */
+
+    value = find_bound_value(values, count, "phys");
+    if (value == NULL) return false;
+
+    switch (value->type)
+    {
+        case SQLITE_INT64:
+            addr->physical = value->integer64;
+            break;
+
+        case SQLITE_NULL:
+            addr->physical = VMPA_NO_PHYSICAL;
+            break;
+
+        default:
+            return false;
+            break;
+
+    }
+
+    value = find_bound_value(values, count, "virt");
+    if (value == NULL) return false;
+
+    switch (value->type)
+    {
+        case SQLITE_INT64:
+            addr->virtual = value->integer64;
+            break;
+
+        case SQLITE_NULL:
+            addr->virtual = VMPA_NO_VIRTUAL;
+            break;
+
+        default:
+            return false;
+            break;
+
+    }
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : addr   = adresse virtuelle ou physique à traiter.            *
 *                create = indique si la préparation vise une création ou non. *
 *                values = couples de champs et de valeurs à lier. [OUT]       *
diff --git a/src/arch/vmpa.h b/src/arch/vmpa.h
index 97b2610..ec356c8 100644
--- a/src/arch/vmpa.h
+++ b/src/arch/vmpa.h
@@ -126,6 +126,12 @@ vmpa2t *string_to_vmpa_virt(const char *);
     "phys INTEGER, "        \
     "virt INTEGER, "
 
+/* Décrit les colonnes utiles à un chargement de données. */
+bool setup_load_for_vmpa(const vmpa2t *, bound_value **, size_t *);
+
+/* Charge les valeurs utiles pour une localisation. */
+bool load_vmpa(vmpa2t *, const bound_value *, size_t);
+
 /* Constitue les champs destinés à une insertion / modification. */
 bool prepare_vmpa_db_statement(const vmpa2t *, bool, bound_value **, size_t *);
 
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 7615f43..1ab4768 100755
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -14,7 +14,7 @@ libcommon_la_SOURCES =					\
 	leb128.h leb128.c					\
 	macros.h							\
 	net.h net.c							\
-	sqlite.h							\
+	sqlite.h sqlite.c					\
 	xdg.h xdg.c							\
 	xml.h xml.c
 
diff --git a/src/common/sqlite.c b/src/common/sqlite.c
new file mode 100644
index 0000000..ec3c280
--- /dev/null
+++ b/src/common/sqlite.c
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * sqlite.c - extension des définitions propres à SQLite
+ *
+ * Copyright (C) 2015 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA 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.
+ *
+ *  OpenIDA 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "sqlite.h"
+
+
+#include <string.h>
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : values = tableau d'éléments à consulter.                     *
+*                count  = nombre de descriptions renseignées.                 *
+*                name   = désignation de la valeur recherchée.                *
+*                                                                             *
+*  Description : Effectue une recherche au sein d'un ensemble de valeurs.     *
+*                                                                             *
+*  Retour      : Elément retrouvé ou NULL en cas d'échec.                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+const bound_value *find_bound_value(const bound_value *values, size_t count, const char *name)
+{
+    const bound_value *result;              /* Trouvaille à retourner      */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = NULL;
+
+    for (i = 0; i < count && result == NULL; i++)
+        if (strcmp(values[i].name, name) == 0)
+            result = &values[i];
+
+    return result;
+
+}
diff --git a/src/common/sqlite.h b/src/common/sqlite.h
index 66fb773..559af38 100644
--- a/src/common/sqlite.h
+++ b/src/common/sqlite.h
@@ -26,6 +26,8 @@
 
 
 #include <stdint.h>
+#include <sys/types.h>
+
 
 
 /* Type pour les insertions brutes */
@@ -52,5 +54,9 @@ typedef struct _bound_value
 } bound_value;
 
 
+/* Effectue une recherche au sein d'un ensemble de valeurs. */
+const bound_value *find_bound_value(const bound_value *, size_t, const char *);
+
+
 
 #endif  /* _COMMON_SQLITE_H */
diff --git a/src/core/collections.c b/src/core/collections.c
index 80d2ae3..c867d57 100644
--- a/src/core/collections.c
+++ b/src/core/collections.c
@@ -109,7 +109,7 @@ bool load_hard_coded_collection_definitions(void)
      * afin de garder la correspondance entre les identifiants.
      */
 
-    id = register_collection_type(G_TYPE_DB_BOOKMARK, create_bookmark_db_table);
+    id = register_collection_type(G_TYPE_BM_COLLECTION, create_bookmark_db_table);
     assert(id == DBF_BOOKMARKS);
 
     return true;
@@ -164,7 +164,8 @@ GList *create_collections_list(void)
     for (i = 0; i < _collection_definitions_count; i++)
     {
         def = &_collection_definitions[i];
-        collec = g_db_collection_new(i, def->items, "Bookmarks");
+        //collec = g_db_collection_new(i, def->items, "Bookmarks");
+        collec = g_object_new(def->items, NULL);
 
         result = g_list_append(result, collec);
 
diff --git a/src/dialogs/Makefile.am b/src/dialogs/Makefile.am
index 9adb0f6..54b6fa2 100644
--- a/src/dialogs/Makefile.am
+++ b/src/dialogs/Makefile.am
@@ -3,6 +3,7 @@ noinst_LTLIBRARIES  = libdialogs.la
 
 libdialogs_la_SOURCES =					\
 	about.h about.c						\
+	bookmark.h bookmark.c				\
 	export.h export.c					\
 	goto.h goto.c						\
 	plugins.h plugins.c					\
diff --git a/src/dialogs/bookmark.c b/src/dialogs/bookmark.c
new file mode 100644
index 0000000..abff500
--- /dev/null
+++ b/src/dialogs/bookmark.c
@@ -0,0 +1,331 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bookmark.c - boîte de dialogue pour les sauts à une adresse donnée
+ *
+ * Copyright (C) 2012-2014 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA 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.
+ *
+ *  OpenIDA 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "bookmark.h"
+
+
+#include <ctype.h>
+#include <string.h>
+
+
+#include <i18n.h>
+
+
+#include "..//analysis/db/items/bookmark.h"
+#include "../gtkext/easygtk.h"
+
+
+
+/* Filtre les adresses en hexadécimal pendant l'édition. */
+static void filter_addresses(GtkEntry *, const gchar *, gint, gint *, gpointer);
+
+/* Clôture l'édition d'une adresse. */
+static void validate_addresses(GtkEntry *, GtkDialog *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : entry    = composant GTK concerné par la procédure.          *
+*                text     = nouveau texte inséré.                             *
+*                length   = taille de ce texte.                               *
+*                position = point d'insertion.                                *
+*                data     = adresse non utilisée ici.                         *
+*                                                                             *
+*  Description : Filtre les adresses en hexadécimal pendant l'édition.        *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void filter_addresses(GtkEntry *entry, const gchar *text, gint length, gint *position, gpointer data)
+{
+    gboolean has_hex;                       /* Préfixe '0x' déjà présent ? */
+    gchar *filtered;                        /* Contenu nouveau approuvé    */
+    gint count;                             /* Nouvelle taille validée     */
+    gint i;                                 /* Boucle de parcours          */
+
+    /**
+     * On cherche à empêcher l'édition avant un '0x' présent,
+     * ce qui viendrait fausser le fitrage.
+     */
+    has_hex = g_str_has_prefix(gtk_entry_get_text(entry), "0x");
+
+    filtered = g_new(gchar, length);
+
+    count = 0;
+
+    for (i = 0; i < length; i++)
+        switch (text[i])
+        {
+            case '0' ... '9':
+            case 'a' ... 'f':
+                if (!has_hex || ((i + *position) >= 2))
+                    filtered[count++] = text[i];
+                break;
+            case 'A' ... 'F':
+                if (!has_hex || ((i + *position) >= 2))
+                    filtered[count++] = tolower(text[i]);
+                break;
+            case 'x':
+            case 'X':
+                if ((i + *position) == 1)
+                    filtered[count++] = 'x';
+                break;
+        }
+
+    if (count > 0)
+    {
+        g_signal_handlers_block_by_func(G_OBJECT(entry), G_CALLBACK(filter_addresses), data);
+        gtk_editable_insert_text(GTK_EDITABLE(entry), filtered, count, position);
+        g_signal_handlers_unblock_by_func(G_OBJECT(entry), G_CALLBACK(filter_addresses), data);
+    }
+
+    g_signal_stop_emission_by_name(G_OBJECT(entry), "insert_text");
+
+    g_free(filtered);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : entry  = composant GTK concerné par la procédure.            *
+*                dialog = boîte de dialogue à valider.                        *
+*                                                                             *
+*  Description : Clôture l'édition d'une adresse.                             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void validate_addresses(GtkEntry *entry, GtkDialog *dialog)
+{
+    gtk_dialog_response(dialog, GTK_RESPONSE_OK);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : parent = fenêtre parente à surpasser.                        *
+*                addr   = localisation du point à consigner.                  *
+*                                                                             *
+*  Description : Construit la fenêtre de création de signet.                  *
+*                                                                             *
+*  Retour      : Adresse de la fenêtre mise en place.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GtkWidget *create_bookmark_dialog(GtkWindow *parent, const vmpa2t *addr)
+{
+    GtkWidget *result;                      /* Fenêtre à renvoyer          */
+    GtkWidget *dlgvbox;                     /* Zone principale de la boîte */
+    GtkWidget *vbox;                        /* Support à construire #1     */
+    GtkWidget *alignment;                   /* Support véritable interne   */
+    GtkWidget *frame;                       /* Support avec encadrement    */
+    GtkWidget *sub_vbox;                    /* Support à construire #2     */
+    GtkWidget *hbox;                        /* Support à construire #3     */
+    GtkWidget *label;                       /* Message d'introduction      */
+    GtkWidget *entry;                       /* Zone de saisie principale   */
+    GtkWidget *radio;                       /* Définition de localisation  */
+    VMPA_BUFFER(target);                    /* Désignation humaine de cible*/
+
+    result = gtk_dialog_new();
+    gtk_window_set_title(GTK_WINDOW(result), _("Add a bookmark"));
+    gtk_window_set_position(GTK_WINDOW(result), GTK_WIN_POS_CENTER);
+    gtk_window_set_modal(GTK_WINDOW(result), TRUE);
+    gtk_window_set_type_hint(GTK_WINDOW(result), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+    dlgvbox = gtk_dialog_get_content_area(GTK_DIALOG(result));
+    gtk_widget_show(dlgvbox);
+
+    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
+    gtk_widget_show(vbox);
+    gtk_box_pack_start(GTK_BOX(dlgvbox), vbox, TRUE, TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
+
+    /* Localisation dans l'espace */
+
+    frame = qck_create_frame(_("<b>Localisation</b>"), &alignment, 8, 0, 12, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    sub_vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
+    gtk_widget_show(sub_vbox);
+    gtk_container_add(GTK_CONTAINER(alignment), sub_vbox);
+
+    /* 1) Adresse */
+
+    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+    gtk_widget_show(hbox);
+    gtk_box_pack_start(GTK_BOX(sub_vbox), hbox, FALSE, TRUE, 0);
+
+    label = qck_create_label(NULL, NULL, _("Target:"));
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+    entry = qck_create_entry(G_OBJECT(result), "addr", NULL);
+    //g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(forbid_text_empty_entry), assistant);
+    gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, TRUE, 0);
+
+    /* 2) Type d'adresse */
+
+    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
+    gtk_widget_show(hbox);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);
+    gtk_container_set_border_width(GTK_CONTAINER(hbox), 8);
+
+    radio = qck_create_radio_button(G_OBJECT(result), "phys", _("Value is physical offset"),
+                                          NULL, NULL, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), radio, TRUE, TRUE, 0);
+
+    radio = qck_create_radio_button(G_OBJECT(result), "virt", _("Value is virtual address"),
+                                          GTK_RADIO_BUTTON(radio), NULL, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), radio, TRUE, TRUE, 0);
+
+    /* Commentaire éventuel */
+
+    frame = qck_create_frame(_("<b>Optional comment</b>"), &alignment, 8, 0, 12, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
+
+    entry = qck_create_entry(G_OBJECT(result), "comment", NULL);
+    gtk_container_add(GTK_CONTAINER(alignment), entry);
+
+    /* Zone de validation */
+
+    gtk_dialog_add_button(GTK_DIALOG(result), _("_Cancel"), GTK_RESPONSE_CANCEL);
+    gtk_dialog_add_button(GTK_DIALOG(result), _("_Ok"), GTK_RESPONSE_OK);
+
+    /* Remplissage avec les valeurs fournies */
+
+    entry = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "addr"));
+
+    if (addr != NULL && has_virt_addr(addr))
+    {
+        vmpa2_virt_to_string(addr, MDS_UNDEFINED, target, NULL);
+        radio = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "virt"));
+    }
+
+    else if (addr != NULL && has_phys_addr(addr))
+    {
+        vmpa2_virt_to_string(addr, MDS_UNDEFINED, target, NULL);
+        radio = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "virt"));
+    }
+
+    else
+        radio = NULL;
+
+    if (radio == NULL)
+    {
+        gtk_entry_set_text(GTK_ENTRY(entry), "0x");
+        gtk_editable_set_position(GTK_EDITABLE(entry), -1);
+        gtk_widget_grab_focus(entry);
+    }
+
+    else
+    {
+        gtk_entry_set_text(GTK_ENTRY(entry), target);
+        gtk_editable_set_position(GTK_EDITABLE(entry), -1);
+
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
+
+    }
+
+    if (radio != NULL)
+    {
+        entry = GTK_WIDGET(g_object_get_data(G_OBJECT(result), "comment"));
+        gtk_widget_grab_focus(entry);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : dialog = boîte de dialogue ayant reçu une validation.        *
+*                                                                             *
+*  Description : Fournit le signet conçu via la saisie de l'utilisateur.      *
+*                                                                             *
+*  Retour      : Adresse reccueillie par la boîte de dialogue.                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GDbItem *get_item_from_bookmark_dialog(GtkWidget *dialog)
+{
+    GDbItem *result;                        /* Signet nouveau à retourner  */
+    GObject *ref;                           /* Espace de référencements    */
+    vmpa2t *orig;                           /* Copie de valeur originale   */
+    vmpa2t addr;                            /* Localisation finale utilisée*/
+    GtkWidget *entry;                       /* Zone de saisie principale   */
+    const gchar *text;                      /* Adresse en version texte    */
+    GtkToggleButton *radio;                 /* Définition de localisation  */
+
+    ref = G_OBJECT(dialog);
+
+    /* Si la valeur d'origine a été conservée intacte... */
+
+    orig = (vmpa2t *)g_object_get_data(ref, "orig");
+
+    if (orig == NULL)
+    {
+        entry = GTK_WIDGET(g_object_get_data(ref, "addr"));
+        text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+        radio = GTK_TOGGLE_BUTTON(g_object_get_data(ref, "phys"));
+
+        if (gtk_toggle_button_get_active(radio))
+            orig = string_to_vmpa_phy(text);
+        else
+            orig = string_to_vmpa_virt(text);
+
+
+
+    }
+
+    copy_vmpa(&addr, orig);
+    delete_vmpa(orig);
+
+    /* Récupération du commentaire éventuel */
+
+    entry = GTK_WIDGET(g_object_get_data(ref, "comment"));
+    text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+    /* Mise en place du signet défini */
+
+    if (strlen(text) > 0)
+        result = G_DB_ITEM(g_db_bookmark_new(&addr, text));
+    else
+        result = G_DB_ITEM(g_db_bookmark_new(&addr, NULL));
+
+    return result;
+
+}
diff --git a/src/dialogs/bookmark.h b/src/dialogs/bookmark.h
new file mode 100644
index 0000000..0afdb4c
--- /dev/null
+++ b/src/dialogs/bookmark.h
@@ -0,0 +1,44 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bookmark.h - prototypes pour la boîte de dialogue pour les sauts à une adresse donnée
+ *
+ * Copyright (C) 2012 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA 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.
+ *
+ *  OpenIDA 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 Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _DIALOGS_BOOKMARK_H
+#define _DIALOGS_BOOKMARK_H
+
+
+#include <gtk/gtk.h>
+
+
+#include "../arch/vmpa.h"
+#include "../analysis/db/item.h"
+
+
+
+/* Construit la fenêtre de création de signet. */
+GtkWidget *create_bookmark_dialog(GtkWindow *, const vmpa2t *);
+
+/* Fournit le signet conçu via la saisie de l'utilisateur. */
+GDbItem *get_item_from_bookmark_dialog(GtkWidget *);
+
+
+
+#endif  /* _DIALOGS_BOOKMARK_H */
diff --git a/src/dialogs/goto.c b/src/dialogs/goto.c
index dc607d0..847fa9a 100644
--- a/src/dialogs/goto.c
+++ b/src/dialogs/goto.c
@@ -231,8 +231,8 @@ vmpa2t *get_address_from_goto_dialog(GtkWidget *dialog)
     vmpa2t *result;                         /* Adresse à retourner         */
     GtkWidget *combobox;                    /* Liste de sélection          */
     GtkWidget *entry;                       /* Zone de saisie principale   */
-    GtkToggleButton *radio;                 /* Définition de localisation  */
     const gchar *text;                      /* Adresse en version texte    */
+    GtkToggleButton *radio;                 /* Définition de localisation  */
 
     combobox = GTK_WIDGET(g_object_get_data(G_OBJECT(dialog), "combobox"));
     entry = gtk_bin_get_child(GTK_BIN(combobox));
diff --git a/src/dlg_sections.c b/src/dlg_sections.c
deleted file mode 100644
index e69de29..0000000
diff --git a/src/dlg_sections.h b/src/dlg_sections.h
deleted file mode 100644
index e69de29..0000000
diff --git a/src/gtkext/easygtk.c b/src/gtkext/easygtk.c
index 11e17a4..fa99f38 100644
--- a/src/gtkext/easygtk.c
+++ b/src/gtkext/easygtk.c
@@ -876,3 +876,43 @@ GtkWidget *qck_create_tool_separator(GObject *object, const char *name)
 	return result;
 
 }
+
+
+
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : parent   = fenêtre parente pour la modalité d'affichage.     *
+*                title    = titre de la boîte de dialogue.                    *
+*                question = teneur de la question posée.                      *
+*                                                                             *
+*  Description : Affiche une boîte de dialogue offrant un choix "Oui/Non".    *
+*                                                                             *
+*  Retour      : Identifiant de la réponse choisie.                           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+gint qck_show_question(GtkWindow *parent, const char *title, const char *question)
+{
+    gint result;                            /* Choix arrêté à renvoyer     */
+    GtkWidget *dialog;                      /* Boîte de dialogue affichée  */
+
+    dialog = gtk_message_dialog_new(parent,
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_MESSAGE_QUESTION,
+                                    GTK_BUTTONS_YES_NO,
+                                    question);
+
+    gtk_window_set_title(GTK_WINDOW(dialog), title);
+
+    result = gtk_dialog_run(GTK_DIALOG(dialog));
+
+    gtk_widget_destroy(dialog);
+
+    return result;
+
+}
diff --git a/src/gtkext/easygtk.h b/src/gtkext/easygtk.h
index 5f8fb29..65d1127 100644
--- a/src/gtkext/easygtk.h
+++ b/src/gtkext/easygtk.h
@@ -106,4 +106,9 @@ GtkWidget *qck_create_tool_separator(GObject *, const char *);
 
 
 
+/* Affiche une boîte de dialogue offrant un choix "Oui/Non". */
+gint qck_show_question(GtkWindow *, const char *, const char *);
+
+
+
 #endif  /* _EASYGTK_H */
diff --git a/src/gtkext/gtkbufferview.c b/src/gtkext/gtkbufferview.c
index 8b9b83b..f179845 100644
--- a/src/gtkext/gtkbufferview.c
+++ b/src/gtkext/gtkbufferview.c
@@ -746,6 +746,25 @@ static void gtk_buffer_view_relocate_caret(GtkBufferView *view, const GdkRectang
 *                                                                             *
 *  Paramètres  : view = composant GTK à manipuler.                            *
 *                                                                             *
+*  Description : Indique la position courante du curseur.                     *
+*                                                                             *
+*  Retour      : Emplacement courant du curseur.                              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+const vmpa2t *gtk_buffer_view_get_caret_location(const GtkBufferView *view)
+{
+    return view->caret_addr;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : view = composant GTK à manipuler.                            *
+*                                                                             *
 *  Description : Redémarre l'affichage du curseur à l'emplacement courant.    *
 *                                                                             *
 *  Retour      : -                                                            *
diff --git a/src/gtkext/gtkbufferview.h b/src/gtkext/gtkbufferview.h
index 1ee17fe..0cdcf4f 100644
--- a/src/gtkext/gtkbufferview.h
+++ b/src/gtkext/gtkbufferview.h
@@ -67,4 +67,12 @@ void gtk_buffer_view_compute_relative_coords(GtkBufferView *, gint *, gint *);
 
 
 
+/* ------------------------------ ANIMATION DU CURSEUR ------------------------------ */
+
+
+/* Indique la position courante du curseur. */
+const vmpa2t *gtk_buffer_view_get_caret_location(const GtkBufferView *);
+
+
+
 #endif  /* _GTK_BUFFERVIEW_H */
diff --git a/src/gui/menus/edition.c b/src/gui/menus/edition.c
index fc23f49..eec3016 100644
--- a/src/gui/menus/edition.c
+++ b/src/gui/menus/edition.c
@@ -28,11 +28,16 @@
 #include <i18n.h>
 
 
+#include "../../dialogs/bookmark.h"
 #include "../../dialogs/goto.h"
 #include "../../gtkext/easygtk.h"
+#include "../../gtkext/gtkbufferview.h"
 
 
 
+/* Réagit avec le menu "Edition -> Signets -> Basculer...". */
+static void mcb_edition_bookmarks_toggle(GtkMenuItem *, GMenuBar *);
+
 /* Réagit avec le menu "Edition -> Aller à l'adresse...". */
 static void mcb_edition_goto(GtkMenuItem *, GMenuBar *);
 
@@ -56,7 +61,9 @@ GtkWidget *build_menu_edition(GObject *ref, GtkAccelGroup *accgroup, GMenuBar *b
 {
     GtkWidget *result;                      /* Support à retourner         */
     GtkWidget *menubar;                     /* Support pour éléments       */
-    GtkWidget *submenuitem;                 /* Sous-élément de menu        */
+    GtkWidget *submenuitem;                 /* Sous-élément de menu #1     */
+    GtkWidget *deepmenubar;                 /* Support pour éléments #2    */
+    GtkWidget *deepmenuitem;                /* Sous-élément de menu #2     */
 
     result = gtk_menu_item_new_with_mnemonic(_("_Edition"));
     gtk_widget_show(result);
@@ -64,6 +71,20 @@ GtkWidget *build_menu_edition(GObject *ref, GtkAccelGroup *accgroup, GMenuBar *b
     menubar = gtk_menu_new();
     gtk_menu_item_set_submenu(GTK_MENU_ITEM(result), menubar);
 
+    submenuitem = qck_create_menu_item(NULL, NULL, _("Bookmarks"), NULL, NULL);
+    gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
+
+    deepmenubar = gtk_menu_new();
+    gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenuitem), deepmenubar);
+
+    deepmenuitem = qck_create_menu_item(NULL, NULL, _("Toggle at current location"),
+                                        G_CALLBACK(mcb_edition_bookmarks_toggle), bar);
+    add_accelerator_to_menu_item(deepmenuitem, "<Ctrl>D", accgroup);
+    gtk_container_add(GTK_CONTAINER(deepmenubar), deepmenuitem);
+
+    submenuitem = qck_create_menu_separator();
+    gtk_container_add(GTK_CONTAINER(menubar), submenuitem);
+
     submenuitem = qck_create_menu_item(NULL, NULL, _("Go to address..."),
                                        G_CALLBACK(mcb_edition_goto), bar);
     add_accelerator_to_menu_item(submenuitem, "<Ctrl>G", accgroup);
@@ -79,6 +100,105 @@ GtkWidget *build_menu_edition(GObject *ref, GtkAccelGroup *accgroup, GMenuBar *b
 *  Paramètres  : menuitem = élément de menu sélectionné.                      *
 *                bar      = barre de menu parente.                            *
 *                                                                             *
+*  Description : Réagit avec le menu "Edition -> Signets -> Basculer...".     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void mcb_edition_bookmarks_toggle(GtkMenuItem *menuitem, GMenuBar *bar)
+{
+    GEditorItem *editem;                    /* Autre version de la barre   */
+    GtkViewPanel *panel;                    /* Vue offrant l'affichage     */
+    const vmpa2t *curloc;                   /* Localisation d'un curseur   */
+    GLoadedBinary *binary;                  /* Binaire en cours d'étude    */
+    GDbCollection *collec;                  /* Collection à manipuler      */
+    GDbItem *exist;                         /* Sens du basculement courant */
+    GObject *ref;                           /* Espace de référencements    */
+    GtkWidget *dialog;                      /* Boîte de dialogue à montrer */
+    GDbItem *bookmark;                      /* Nouveau signet défini       */
+    gint ret;                               /* Retour de confirmation      */
+
+    editem = G_EDITOR_ITEM(bar);
+
+    /* Détermination de l'adresse visée */
+
+    panel = g_editor_item_get_current_view(editem);
+
+    if (!GTK_IS_BUFFER_VIEW(panel))
+        curloc = NULL;
+    else
+        curloc = gtk_buffer_view_get_caret_location(GTK_BUFFER_VIEW(panel));
+
+    /* Accès à la collection */
+
+    binary = g_editor_item_get_current_binary(editem);
+    collec = g_loaded_binary_find_collection(binary, DBF_BOOKMARKS);
+
+    /**
+     * On choisit de se passer de verrou ici :
+     *  - si l'élément existe, la suppression prend en compte le fait
+     *    que l'élément puisse disparaître entre temps.
+     *  - si l'élément n'existe pas, une boîte de dialogue est prévue
+     *    au moment de l'insertion finale. Dans ce cas, l'utilisateur
+     *    peut de plus modifier la position pendant la définition.
+     */
+
+    if (curloc == NULL)
+        exist = NULL;
+    else
+        exist = NULL;//g_db_collection_has_key(collec, curloc);
+
+    if (exist != NULL)
+        g_loaded_binary_remove_from_collection(binary, DBF_BOOKMARKS, exist);
+
+    else
+    {
+        ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(bar));
+        dialog = create_bookmark_dialog(GTK_WINDOW(ref), curloc);
+
+        if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
+        {
+            bookmark = get_item_from_bookmark_dialog(dialog);
+
+            g_db_collection_wlock(G_DB_COLLECTION(collec));
+
+            if (g_db_collection_has_item(collec, G_DB_ITEM(bookmark)))
+            {
+                ret = qck_show_question(GTK_WINDOW(ref),
+                                        _("Location  already bookmarked!"),
+                                        _("A bookmark has been defined at the same location.\n" \
+                                          "Do you want to replace it ?"));
+
+                if (ret != GTK_RESPONSE_YES)
+                    goto mcb_ebt_add_finish;
+
+            }
+
+            _g_loaded_binary_add_to_collection(binary, DBF_BOOKMARKS, G_DB_ITEM(bookmark), false);
+
+        mcb_ebt_add_finish:
+
+            g_db_collection_wunlock(G_DB_COLLECTION(collec));
+
+        }
+
+        gtk_widget_destroy(dialog);
+
+    }
+
+    g_object_unref(G_OBJECT(collec));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : menuitem = élément de menu sélectionné.                      *
+*                bar      = barre de menu parente.                            *
+*                                                                             *
 *  Description : Réagit avec le menu "Edition -> Aller à l'adresse...".       *
 *                                                                             *
 *  Retour      : -                                                            *
diff --git a/src/gui/panels/bookmarks.c b/src/gui/panels/bookmarks.c
index 9239203..7397ef5 100644
--- a/src/gui/panels/bookmarks.c
+++ b/src/gui/panels/bookmarks.c
@@ -110,6 +110,9 @@ static void g_bookmarks_panel_finalize(GBookmarksPanel *);
 /* Recharge une collection de signets à l'affichage. */
 static void reload_bookmarks_into_treeview(GBookmarksPanel *, GLoadedBinary *);
 
+/* Met à jour une collection suite à une modification. */
+static void on_collection_content_changed(GDbCollection *, DBAction, GDbBookmark *, GBookmarksPanel *);
+
 /* Réagit au changement de sélection des signets. */
 static void on_bookmarks_selection_change(GtkTreeSelection *, GBookmarksPanel *);
 
@@ -471,8 +474,8 @@ GPanelItem *create_bookmarks_panel(GObject *ref)
 
 static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary *binary)
 {
-    GtkTreeStore *store;                    /* Modèle de gestion           */
     GDbCollection *collec;                  /* Collection à lister ici     */
+    GtkTreeStore *store;                    /* Modèle de gestion           */
     GArchProcessor *proc;                   /* Architecture du binaire     */
     MemoryDataSize msize;                   /* Taille par défaut           */
     GList *items;                           /* Liste des éléments groupés  */
@@ -486,13 +489,25 @@ static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary
     /* Basculement du binaire utilisé */
 
     if (panel->binary != NULL)
+    {
+        collec = g_loaded_binary_find_collection(panel->binary, DBF_BOOKMARKS);
+        g_signal_handlers_disconnect_by_func(collec, G_CALLBACK(on_collection_content_changed), panel);
+
         g_object_unref(G_OBJECT(panel->binary));
 
+    }
+
     panel->binary = binary;
 
     if (panel->binary != NULL)
+    {
         g_object_ref(G_OBJECT(binary));
 
+        collec = g_loaded_binary_find_collection(binary, DBF_BOOKMARKS);
+        g_signal_connect(collec, "content-changed", G_CALLBACK(on_collection_content_changed), panel);
+
+    }
+
     store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
     gtk_tree_store_clear(store);
 
@@ -506,8 +521,6 @@ static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary
     msize = g_arch_processor_get_memory_size(proc);
     g_object_unref(G_OBJECT(proc));
 
-    collec = g_loaded_binary_find_collection(binary, DBF_BOOKMARKS);
-
     g_db_collection_rlock(collec);
 
     items = g_db_collection_list_items(collec);
@@ -539,6 +552,76 @@ static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : collec   = collection dont le contenu vient de changer.      *
+*                action   = type de modification notifiée par la collection.  *
+*                bookmark = élément en cause dans le changement survenu.      *
+*                pane     = structure contenant les informations maîtresses.  *
+*                                                                             *
+*  Description : Met à jour une collection suite à une modification.          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_collection_content_changed(GDbCollection *collec, DBAction action, GDbBookmark *bookmark, GBookmarksPanel *panel)
+{
+
+    GtkTreeStore *store;                    /* Modèle de gestion           */
+
+
+    GExeFormat *format;                     /* Format du fichier binaire   */
+    GArchProcessor *proc;                   /* Architecture du binaire     */
+    MemoryDataSize msize;                   /* Taille par défaut           */
+
+    const vmpa2t *addr;                     /* Adressse associée au signet */
+    VMPA_BUFFER(phys);                      /* Position physique           */
+    VMPA_BUFFER(virt);                      /* Adresse virtuelle           */
+    GtkTreeIter iter;                       /* Point d'insertion           */
+
+
+
+
+    printf(" Passage :: %d\n", action);
+
+
+    store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
+
+    format = g_loaded_binary_get_format(panel->binary);
+
+
+    proc = g_loaded_binary_get_processor(panel->binary);
+    msize = g_arch_processor_get_memory_size(proc);
+    g_object_unref(G_OBJECT(proc));
+
+
+
+
+
+
+    addr = g_db_bookmark_get_address(bookmark);
+
+    vmpa2_phys_to_string(addr, msize, phys, NULL);
+    vmpa2_virt_to_string(addr, msize, virt, NULL);
+
+    gtk_tree_store_append(store, &iter, NULL);
+    gtk_tree_store_set(store, &iter,
+                       BMC_BOOKMARK, bookmark,
+                       BMC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img,
+                       BMC_PHYSICAL, phys,
+                       BMC_VIRTUAL, virt,
+                       BMC_COMMENT, g_db_bookmark_get_comment(bookmark),
+                       -1);
+
+
+
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : selection = sélection modifiée.                              *
 *                panel     = structure contenant les informations maîtresses. *
 *                                                                             *
-- 
cgit v0.11.2-87-g4458