From 41efd099244b53a0edb40d097b34bf28a05b6367 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 28 Jul 2015 21:32:57 +0000
Subject: Begun to manage collection items as active or inactive using
 timestamps.

git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@558 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
---
 ChangeLog                        |  42 ++++
 src/Makefile.am                  |   2 +-
 src/analysis/binary.c            |  46 ++++-
 src/analysis/binary.h            |   8 +-
 src/analysis/db/Makefile.am      |   1 -
 src/analysis/db/cdb.c            |  22 +-
 src/analysis/db/cdb.h            |   5 -
 src/analysis/db/client.c         |  30 +++
 src/analysis/db/client.h         |   3 +
 src/analysis/db/collection.c     | 429 +++++++++++++++++++++++++++++++++++----
 src/analysis/db/collection.h     |  26 ++-
 src/analysis/db/core.c           | 207 -------------------
 src/analysis/db/core.h           |  70 -------
 src/analysis/db/item-int.h       |  22 +-
 src/analysis/db/item.c           | 260 ++++++++++++++++--------
 src/analysis/db/item.h           |  24 ++-
 src/analysis/db/items/bookmark.c |  40 +++-
 src/analysis/db/items/comment.c  |  13 +-
 src/analysis/db/items/switcher.c |  22 +-
 src/analysis/db/misc/Makefile.am |   3 +-
 src/analysis/db/misc/timestamp.c | 263 ++++++++++++++++++++++++
 src/analysis/db/misc/timestamp.h |  80 ++++++++
 src/analysis/db/protocol.h       |  33 ++-
 src/gui/panels/bookmarks.c       |  64 ++++--
 src/gui/panels/history.c         | 335 +++++++++++++++++++++++++++++-
 25 files changed, 1577 insertions(+), 473 deletions(-)
 delete mode 100644 src/analysis/db/core.c
 delete mode 100644 src/analysis/db/core.h
 create mode 100644 src/analysis/db/misc/timestamp.c
 create mode 100644 src/analysis/db/misc/timestamp.h

diff --git a/ChangeLog b/ChangeLog
index 5c8bbd4..02dc216 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+15-07-28  Cyrille Bagard <nocbos@gmail.com>
+
+	* src/analysis/binary.c:
+	* src/analysis/binary.h:
+	Provide a direct access to the connected client.
+
+	* src/analysis/db/cdb.c:
+	* src/analysis/db/cdb.h:
+	* src/analysis/db/client.c:
+	* src/analysis/db/client.h:
+	* src/analysis/db/collection.c:
+	* src/analysis/db/collection.h:
+	Begin to manage collection items as active or inactive using timestamps.
+
+	* src/analysis/db/core.c:
+	* src/analysis/db/core.h:
+	Deleted entries.
+
+	* src/analysis/db/item.c:
+	* src/analysis/db/item.h:
+	* src/analysis/db/item-int.h:
+	* src/analysis/db/items/bookmark.c:
+	* src/analysis/db/items/comment.c:
+	* src/analysis/db/items/switcher.c:
+	* src/analysis/db/Makefile.am:
+	Remove the 'core.[ch]' files from libanalysisdb_la_SOURCES.
+
+	* src/analysis/db/misc/Makefile.am:
+	Add the 'timestamp.[ch]' files to libanalysisdbmisc_la_SOURCES.
+
+	* src/analysis/db/misc/timestamp.c:
+	* src/analysis/db/misc/timestamp.h:
+	New entries: manage timestamps.
+
+	* src/analysis/db/protocol.h:
+	* src/gui/panels/bookmarks.c:
+	* src/gui/panels/history.c:
+	Begin to manage collection items as active or inactive using timestamps.
+
+	* src/Makefile.am:
+	Add the rt library to libchrysadisass_la_LDFLAGS.
+
 15-07-25  Cyrille Bagard <nocbos@gmail.com>
 
 	* src/gtkext/easygtk.c:
diff --git a/src/Makefile.am b/src/Makefile.am
index e82ecfb..30772c9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -27,7 +27,7 @@ libchrysacore_la_LIBADD =				\
 libchrysadisass_la_SOURCES =
 
 libchrysadisass_la_LDFLAGS = $(LIBGTK_LIBS) $(LIBXML_LIBS)	\
-	-Lcommon/.libs -lcommon -L.libs -lchrysaglibext
+	-Lcommon/.libs -lcommon -L.libs -lchrysaglibext -lrt
 
 libchrysadisass_la_LIBADD =				\
 	analysis/libanalysis.la				\
diff --git a/src/analysis/binary.c b/src/analysis/binary.c
index ea18425..c6ae4e5 100644
--- a/src/analysis/binary.c
+++ b/src/analysis/binary.c
@@ -728,6 +728,50 @@ bool g_loaded_binary_connect(GLoadedBinary *binary)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : binary = élément binaire à consulter.                        *
+*                                                                             *
+*  Description : Fournit le client assurant la liaison avec un serveur.       *
+*                                                                             *
+*  Retour      : Client connecté ou NULL.                                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GDbClient *g_loaded_binary_get_db_client(const GLoadedBinary *binary)
+{
+    GDbClient *result;                      /* Instance à retourner        */
+
+    result = binary->local;
+
+    if (result != NULL)
+        g_object_ref(G_OBJECT(result));
+
+    return result;
+
+}
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : binary = élément binaire à consulter.                        *
+*                                                                             *
+*  Description : Fournit l'ensemble des collections utilisées par un binaire. *
+*                                                                             *
+*  Retour      : Collections en place.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GList *g_loaded_binary_get_all_collections(const GLoadedBinary *binary)
+{
+    return binary->collections;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : binary  = élément binaire à consulter.                       *
 *                feature = fonctionnalité assurée par la collection visée.    *
 *                                                                             *
@@ -739,7 +783,7 @@ bool g_loaded_binary_connect(GLoadedBinary *binary)
 *                                                                             *
 ******************************************************************************/
 
-GDbCollection *g_loaded_binary_find_collection(GLoadedBinary *binary, DBFeatures feature)
+GDbCollection *g_loaded_binary_find_collection(const GLoadedBinary *binary, DBFeatures feature)
 {
     GDbCollection *result;                  /* Collection à retourner      */
 
diff --git a/src/analysis/binary.h b/src/analysis/binary.h
index b67881c..fb37310 100644
--- a/src/analysis/binary.h
+++ b/src/analysis/binary.h
@@ -29,7 +29,7 @@
 #include <stdbool.h>
 
 #include "db/collection.h"
-#include "db/item.h"
+#include "db/client.h"
 #include "db/protocol.h"
 #include "../arch/processor.h"
 #include "../common/xml.h"
@@ -115,10 +115,14 @@ bool g_loaded_binary_connect(GLoadedBinary *);
 /* -------------------------- MANIPULATION DES COLLECTIONS -------------------------- */
 
 
+/* Fournit le client assurant la liaison avec un serveur. */
+GDbClient *g_loaded_binary_get_db_client(const GLoadedBinary *);
 
+/* Fournit l'ensemble des collections utilisées par un binaire. */
+GList *g_loaded_binary_get_all_collections(const GLoadedBinary *);
 
 /* Trouve une collection assurant une fonctionnalité donnée. */
-GDbCollection *g_loaded_binary_find_collection(GLoadedBinary *, DBFeatures);
+GDbCollection *g_loaded_binary_find_collection(const GLoadedBinary *, DBFeatures);
 
 /* Demande l'intégration d'une modification dans une collection. */
 bool _g_loaded_binary_add_to_collection(GLoadedBinary *, DBFeatures, GDbItem *, bool);
diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am
index 2e8e0f1..797025c 100755
--- a/src/analysis/db/Makefile.am
+++ b/src/analysis/db/Makefile.am
@@ -6,7 +6,6 @@ libanalysisdb_la_SOURCES =				\
 	client.h client.c					\
 	collection-int.h					\
 	collection.h collection.c			\
-	core.h core.c						\
 	item-int.h							\
 	item.h item.c						\
 	protocol.h							\
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index 96e69d5..70e0dc5 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -140,8 +140,9 @@ static bool g_cdb_archive_check_xml_version(const GCdbArchive *);
 
 /* ------------------------- ACCES A LA BASE DE DONNEES SQL ------------------------- */
 
+
 /* Crée la base de données correspondant à l'archive. */
-static bool g_cdb_archive_create_db(const GCdbArchive *, const core_db_info *);
+static bool g_cdb_archive_create_db(const GCdbArchive *);
 
 
 
@@ -333,7 +334,7 @@ GCdbArchive *g_cdb_archive_new(const char *owner, const rle_string *hash, const
         if (errno != ENOENT) goto gcan_error;
 
         g_cdb_archive_create_xml_desc(result, user);
-        g_cdb_archive_create_db(result, NULL);
+        g_cdb_archive_create_db(result);
 
         *error = g_cdb_archive_write(result);
 
@@ -623,7 +624,8 @@ int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *has
 static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive, const rle_string *user)
 {
     bool result;                            /* Bilan à retourner           */
-    char tmp[sizeof(XSTR(UINT32_MAX))];     /* Stockage temporaire        */
+    timestamp_t timestamp;                  /* Date de création            */
+    char tmp[sizeof(XSTR(UINT64_MAX))];     /* Stockage temporaire         */
 
     result = create_new_xml_file(&archive->xdoc, &archive->context);
     if (!result) return false;
@@ -640,7 +642,8 @@ static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive, const rle_string
     result &= add_content_to_node(archive->xdoc, archive->context,
                                   "/ChrysalideBinary/Creation/Author", user->data);
 
-    snprintf(tmp, sizeof(tmp), "%" PRIu64, (uint64_t)time(NULL));
+    init_timestamp(&timestamp);
+    snprintf(tmp, sizeof(tmp), "%" PRIu64, timestamp);
 
     result &= add_content_to_node(archive->xdoc, archive->context,
                                   "/ChrysalideBinary/Creation/Date", tmp);
@@ -694,7 +697,6 @@ static bool g_cdb_archive_check_xml_version(const GCdbArchive *archive)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : archive = archive à constituer.                              *
-*                info    = informations de base associées à la requête.       *
 *                                                                             *
 *  Description : Crée la base de données correspondant à l'archive.           *
 *                                                                             *
@@ -704,7 +706,7 @@ static bool g_cdb_archive_check_xml_version(const GCdbArchive *archive)
 *                                                                             *
 ******************************************************************************/
 
-static bool g_cdb_archive_create_db(const GCdbArchive *archive, const core_db_info *info)
+static bool g_cdb_archive_create_db(const GCdbArchive *archive)
 {
     bool result;                            /* Bilan à retourner           */
     sqlite3 *db;                            /* Base de données à constituer*/
@@ -728,7 +730,6 @@ static bool g_cdb_archive_create_db(const GCdbArchive *archive, const core_db_in
     {
         collec = G_DB_COLLECTION(iter->data);
         result = g_db_collection_create_db_table(collec, db);
-        fprintf(stderr, "STATUS :: %d\n", result);
     }
 
     sqlite3_close(db);
@@ -953,6 +954,13 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
                         break;
 
+                    case DBC_SET_LAST_ACTIVE:
+
+                        status = update_activity_in_collections(archive->collections, fds[i].fd, archive->db);
+                        if (!status) goto gcap_bad_exchange;
+
+                        break;
+
                     default:
                         printf("bad command :: 0x%08x\n", command);
                         goto gcap_bad_exchange;
diff --git a/src/analysis/db/cdb.h b/src/analysis/db/cdb.h
index e9abb02..d6b03fb 100644
--- a/src/analysis/db/cdb.h
+++ b/src/analysis/db/cdb.h
@@ -32,11 +32,6 @@
 #include "protocol.h"
 #include "misc/rlestr.h"
 
-//////
-#include "client.h"
-#include "core.h"
-//////////
-
 
 
 #define G_TYPE_CDB_ARCHIVE               g_cdb_archive_get_type()
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index f0d3d4b..2a5a185 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -525,3 +525,33 @@ bool g_db_client_save(GDbClient *client)
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client    = client pour les accès distants à manipuler.      *
+*                timestamp = date du dernier élément à garder comme actif.    *
+*                                                                             *
+*  Description : Active les éléments en amont d'un horodatage donné.          *
+*                                                                             *
+*  Retour      : true si la commande a bien été envoyée, false sinon.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_db_client_set_last_active(GDbClient *client, timestamp_t timestamp)
+{
+    bool result;                            /* Bilan partiel à remonter    */
+
+    g_db_client_get_fd(client);
+
+    result = safe_send(client->fd, (uint32_t []) { htobe32(DBC_SET_LAST_ACTIVE) }, sizeof(uint32_t), 0);
+
+    result &= send_timestamp(&timestamp, client->fd, MSG_MORE);
+
+    g_db_client_put_fd(client);
+
+    return result;
+
+}
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index 6e9f10b..0304786 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -68,6 +68,9 @@ void g_db_client_put_fd(GDbClient *);
 /* Effectue une demande de sauvegarde de l'état courant. */
 bool g_db_client_save(GDbClient *);
 
+/* Active les éléments en amont d'un horodatage donné. */
+bool g_db_client_set_last_active(GDbClient *, timestamp_t);
+
 
 
 #endif  /* _ANALYSIS_DB_CLIENT_H */
diff --git a/src/analysis/db/collection.c b/src/analysis/db/collection.c
index c3b9f2c..91f9872 100644
--- a/src/analysis/db/collection.c
+++ b/src/analysis/db/collection.c
@@ -68,6 +68,8 @@ 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 *);
 
+/* Met à jour un élément de collection dans une base de données. */
+static bool g_db_collection_store_updated_item(const GDbCollection *, const GDbItem *, sqlite3 *);
 
 
 
@@ -285,6 +287,9 @@ bool g_db_collection_recv(GDbCollection *collec, int fd, sqlite3 *db)
 
     item = g_object_new(collec->type, NULL);
 
+    if (db != NULL)
+        g_db_item_set_server_side(item);
+
     status = g_db_item_recv(item, fd, 0);
     if (!status) return false;
 
@@ -312,8 +317,13 @@ bool g_db_collection_recv(GDbCollection *collec, int fd, sqlite3 *db)
         case DBA_REM_ITEM:
             break;
 
-        case DBA_MOD_ITEM:
-            result = g_db_collection_modify_item(collec, item);
+        case DBA_CHANGE_STATE:
+
+            if (db == NULL)
+                result = g_db_collection_update_item_activity(collec, item);
+            else
+                result = false;
+
             break;
 
         default:
@@ -322,6 +332,8 @@ bool g_db_collection_recv(GDbCollection *collec, int fd, sqlite3 *db)
 
     }
 
+    g_object_unref(G_OBJECT(item));
+
     return result;
 
 }
@@ -511,7 +523,7 @@ bool g_db_collection_has_item(GDbCollection *collec, GDbItem *item)
 
     printf(" --- has\n");
 
-    found = g_list_find_custom(collec->items, item, (GCompareFunc)g_db_item_compare);
+    found = g_list_find_custom(collec->items, item, (GCompareFunc)g_db_item_compare_with_timestamp);
 
     printf(" --- has: %p\n", found);
 
@@ -539,33 +551,23 @@ bool g_db_collection_has_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  */
+
+    result = true;
 
     if (lock)
         g_db_collection_wlock(collec);
 
-    found = g_list_find_custom(collec->items, item, (GCompareFunc)g_db_item_compare);
-
-    if (found != NULL)
-        result = g_db_collection_modify_item(collec, item);
-
-    else
-    {
-        g_object_ref(G_OBJECT(item));
-        collec->items = g_list_append(collec->items, item);
-
-        g_object_ref(G_OBJECT(item));
-        collec->sorted = g_list_insert_sorted(collec->sorted, item, (GCompareFunc)g_db_item_compare);
+    g_object_ref(G_OBJECT(item));
+    collec->items = g_list_append(collec->items, item);
 
-        g_signal_emit_by_name(collec, "content-changed", DBA_ADD_ITEM, item);
+    g_object_ref(G_OBJECT(item));
+    collec->sorted = g_list_insert_sorted(collec->sorted, item, (GCompareFunc)g_db_item_compare_with_timestamp);
 
+    g_signal_emit_by_name(collec, "content-changed", DBA_ADD_ITEM, item);
 
-        printf(" ==== CONTENT CHANGED (-> %u) !!!\n", g_list_length(collec->items));
 
+    printf(" ==== CONTENT CHANGED (-> %u) !!!\n", g_list_length(collec->items));
 
-        result = true;
-
-    }
 
     if (lock)
         g_db_collection_wunlock(collec);
@@ -578,10 +580,10 @@ bool _g_db_collection_add_item(GDbCollection *collec, GDbItem *item, bool lock)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : collec = ensemble d'éléments à considérer.                   *
-*                item   = élément de collection à copier.                     *
+*                item   = élément de collection à manipuler.                  *
 *                lock   = indique si le verrou d'écriture doit être posé.     *
 *                                                                             *
-*  Description : Procède à la modification d'un élément dans la collection.   *
+*  Description : Met à jour le statut d'activité d'un élément de collection.  *
 *                                                                             *
 *  Retour      : Bilan de l'exécution de l'opération.                         *
 *                                                                             *
@@ -589,26 +591,127 @@ bool _g_db_collection_add_item(GDbCollection *collec, GDbItem *item, bool lock)
 *                                                                             *
 ******************************************************************************/
 
-bool _g_db_collection_modify_item(GDbCollection *collec, GDbItem *item, bool lock)
+bool _g_db_collection_update_item_activity(GDbCollection *collec, GDbItem *item, bool lock)
 {
     bool result;                            /* Bilan à faire remonter      */
     GList *found;                           /* Test de présence existante  */
+    GDbItem *internal;                      /* Elément interne à modifier  */
 
-    found = g_list_find_custom(collec->items, item, (GCompareFunc)g_db_item_compare);
+    if (lock)
+        g_db_collection_wlock(collec);
 
+    found = g_list_find_custom(collec->items, item, (GCompareFunc)g_db_item_compare_without_timestamp);
 
+    result = (found != NULL);
 
-    result = true;
+    if (result)
+    {
+        internal = G_DB_ITEM(found->data);
+
+        g_db_item_set_activity(internal, (timestamp_t []) { g_db_item_get_timestamp(item) + 1 });
+
+        g_signal_emit_by_name(collec, "content-changed", DBA_CHANGE_STATE, internal);
+
+    }
+
+    if (lock)
+        g_db_collection_wunlock(collec);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec    = ensemble d'éléments à considérer.                *
+*                timestamp = date du dernier élément à garder comme actif.    *
+*                inactive  = date du premier élément inactif rencontré. [OUT] *
+*                db        = base de données à mettre à jour.                 *
+*                                                                             *
+*  Description : Active les éléments en amont d'un horodatage donné.          *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GList *g_db_collection_set_last_active(GDbCollection *collec, timestamp_t timestamp, timestamp_t *inactive, sqlite3 *db)
+{
+    GList *result;                          /* Liste d'inactifs à renvoyer */
+    GList *i;                               /* Parcours des éléments       */
+    GDbItem *item;                          /* Elément à traiter           */
+    timestamp_t stamp;                      /* Horodatage de l'élément     */
 
+    result = NULL;
 
+    for (i = g_list_first(collec->items); i != NULL; i = g_list_next(i))
+    {
+        item = G_DB_ITEM(i->data);
+        stamp = g_db_item_get_timestamp(item);
 
+        if (timestamp_is_younger(stamp, timestamp))
+        {
+            if (!g_db_item_is_active(item))
+            {
+                g_db_item_set_activity(item, NULL);
+                /* ... */g_db_collection_store_updated_item(collec, item, db);
+                g_signal_emit_by_name(collec, "content-changed", DBA_CHANGE_STATE, item);
+            }
 
+        }
+
+        else
+        {
+            if (!g_db_item_is_active(item))
+            {
+                if (timestamp_is_younger(stamp, *inactive))
+                    *inactive = stamp;
+            }
+
+            else
+                result = g_list_append(result, item);
+
+        }
+
+    }
 
     return result;
 
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec    = ensemble d'éléments à considérer.                *
+*                item      = élément à désactiver.                            *
+*                timestamp = date du premier élément inactif rencontré. [OUT] *
+*                                                                             *
+*  Description : Désactive les éléments en aval d'un horodatage donné.        *
+*                                                                             *
+*  Retour      : true si l'élément a été traité, false sinon.                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_db_collection_set_inactive(GDbCollection *collec, GDbItem *item, timestamp_t *timestamp)
+{
+    /* Si cette collection n'est pas concernée, on ne bouge pas ! */
+    if (G_OBJECT_TYPE(G_OBJECT(item)) != collec->type) return false;
+
+    assert(g_db_item_is_active(item));
+
+    g_db_item_set_activity(item, timestamp);
+
+    g_signal_emit_by_name(collec, "content-changed", DBA_CHANGE_STATE, item);
+
+    return true;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                       MANIPULATIONS AVEC UNE BASE DE DONNEES                       */
@@ -630,8 +733,6 @@ bool _g_db_collection_modify_item(GDbCollection *collec, GDbItem *item, bool loc
 
 bool g_db_collection_create_db_table(const GDbCollection *collec, sqlite3 *db)
 {
-    fprintf(stderr, "CREATE '%s'\n", collec->name);
-
     return G_DB_COLLECTION_GET_CLASS(collec)->create_table(collec, db);
 
 }
@@ -653,12 +754,11 @@ bool g_db_collection_create_db_table(const GDbCollection *collec, sqlite3 *db)
 
 static bool _g_db_collection_setup_load(GDbCollection *collec, bound_value **values, size_t *count)
 {
-    *count += 2;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
-
-    (*values)[*count - 2].name = "created";
+    if (!setup_load_of_timestamp(NULL, "created", values, count))
+        return false;
 
-    (*values)[*count - 1].name = "modified";
+    if (!setup_load_of_timestamp(NULL, "timestamp", values, count))
+        return false;
 
     if (!setup_load_of_rle_string(NULL, "author", values, count))
         return false;
@@ -666,9 +766,6 @@ static bool _g_db_collection_setup_load(GDbCollection *collec, bound_value **val
     if (!setup_load_of_rle_string(NULL, "tool", values, count))
         return false;
 
-    if (!setup_load_of_rle_string(NULL, "label", values, count))
-        return false;
-
     return true;
 
 }
@@ -796,6 +893,8 @@ bool g_db_collection_load_all_items(GDbCollection *collec, sqlite3 *db)
 
         new = g_object_new(G_DB_COLLECTION(collec)->type, NULL);
 
+        g_db_item_set_server_side(new);
+
         result = g_db_item_load(new, values, count);
         result &= g_db_collection_add_item(G_DB_COLLECTION(collec), new);
 
@@ -931,13 +1030,157 @@ static bool g_db_collection_store_item(const GDbCollection *collec, const GDbIte
 
     free(sql);
 
-    printf("INSERT ? %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.                    *
+*                                                                             *
+*  Description : Met à jour un élément de collection dans une base de données.*
+*                                                                             *
+*  Retour      : Bilan de l'exécution de l'opération.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_db_collection_store_updated_item(const GDbCollection *collec, const GDbItem *item, 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    */
+    bool first;                             /* Première valeur ?           */
+    size_t i;                               /* Boucle de parcours          */
+    sqlite3_stmt *stmt;                     /* Déclaration mise en place   */
+    int ret;                                /* Bilan d'un appel à SQLite   */
+    int index;                              /* Indice de valeur attachée   */
+    const bound_value *timestamp;           /* Valeur de l'horodatage      */
+
+    if (!g_db_item_prepare_db_statement(item, &values, &count))
+        return false;
+
+    result = false;
+
+    /* Préparation de la requête */
+
+    sql = strdup("UPDATE ");
+    sql = stradd(sql, collec->name);
+
+    sql = stradd(sql, " SET timestamp = ? ");
+
+    sql = stradd(sql, "WHERE ");
+
+    first = true;
+
+    for (i = 0; i < count; i++)
+    {
+        if (strcmp(values[i].name, "timestamp") == 0)
+            continue;
+
+        if (first)
+            first = false;
+        else
+            sql = stradd(sql, " AND ");
+
+        sql = stradd(sql, values[i].name);
+
+        sql = stradd(sql, " = ");
+
+        if (values[i].type == SQLITE_RAW)
+            sql = stradd(sql, values[i].cstring);
+        else
+            sql = stradd(sql, "?");
+
+    }
+
+    sql = stradd(sql, ";");
+
+    fprintf(stderr, "UPDATE SQL '%s'\n", sql);
+
+	ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+	if (ret != SQLITE_OK)
+    {
+		fprintf(stderr, "Can't prepare INSERT statment '%s' (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+        goto gdcsi_exit;
+	}
+
+    /* Attribution des valeurs */
+
+    index = 1;
+
+    timestamp = find_bound_value(values, count, "timestamp");
+    assert(timestamp->type == SQLITE_INT64);
+
+    ret = sqlite3_bind_int64(stmt, index, values[i].integer64);
+    index++;
+
+    if (ret != SQLITE_OK)
+    {
+        fprintf(stderr, "Can't bind value for parameter nb %d in '%s' (ret=%d): %s\n",
+                index - 1, sql, ret, sqlite3_errmsg(db));
+        goto gdcsi_exit;
+    }
+
+    for (i = 0; i < count; i++)
+    {
+        if (strcmp(values[i].name, "timestamp") == 0)
+            continue;
+
+        switch (values[i].type)
+        {
+            case SQLITE_INT64:
+                ret = sqlite3_bind_int64(stmt, index, values[i].integer64);
+                index++;
+                break;
+
+            case SQLITE_TEXT:
+                ret = sqlite3_bind_text(stmt, index, values[i].string, -1, values[i].delete);
+                index++;
+                break;
+
+            default:
+                ret = SQLITE_OK;
+                break;
+
+        }
+
+        if (ret != SQLITE_OK)
+        {
+            fprintf(stderr, "Can't bind value for parameter nb %d in '%s' (ret=%d): %s\n",
+                    index - 1, sql, ret, sqlite3_errmsg(db));
+            goto gdcsi_exit;
+        }
+
+    }
+
+    /* Exécution finale */
+
+	ret = sqlite3_step(stmt);
+
+    if (ret != SQLITE_DONE)
+    {
+		fprintf(stderr, "UPDATE statement '%s' didn't return DONE (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+        goto gdcsi_exit;
+    }
+
+    sqlite3_finalize(stmt);
+
+    result = true;
+
+ gdcsi_exit:
+
+    free(sql);
+
+    return result;
+
+}
+
 
 
 /* ---------------------------------------------------------------------------------- */
@@ -1007,3 +1250,113 @@ GDbCollection *find_collection_in_list(GList *list, uint32_t id)
     return (iter != NULL ? result : NULL);
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : list  = ensemble de collectons à parcourir.                  *
+*                write = précise le type d'accès prévu (lecture/écriture).    *
+*                lock  = indique le sens du verrouillage à mener.             *
+*                                                                             *
+*  Description : Met à disposition un encadrement des accès aux éléments.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void lock_unlock_collections(GList *list, bool write, bool lock)
+{
+    GList *iter;                            /* Boucle de parcours          */
+
+    for (iter = g_list_first(list);
+         iter != NULL;
+         iter = g_list_next(iter))
+    {
+        g_db_collection_lock_unlock(G_DB_COLLECTION(iter->data), write, lock);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : list = ensemble de collectons à traiter.                     *
+*                fd   = canal de communication ouvert en lecture.             *
+*                db   = base de données à mettre à jour.                      *
+*                                                                             *
+*  Description : Met à jour les statuts d'activité des éléments.              *
+*                                                                             *
+*  Retour      : Bilan du déroulement des opérations.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool update_activity_in_collections(GList *list, int fd, sqlite3 *db)
+{
+    bool result;                            /* Résultat global à renvoyer  */
+    bool status;                            /* Bilan de lecture initiale   */
+    timestamp_t timestamp;                  /* Horodatage de limite        */
+    timestamp_t inactive;                   /* Date du premier inactif     */
+    GList *remaining;                       /* Eléments restants à traiter */
+    GList *c;                               /* Boucle de parcours #1       */
+    GDbCollection *collec;                  /* Collection à manipuler      */
+    GList *got;                             /* Eléments restants à traiter */
+    GList *i;                               /* Boucle de parcours #2       */
+    GDbItem *item;                          /* Elément collecté à manipuler*/
+
+    status = recv_timestamp(&timestamp, fd, 0);
+    if (!status) return false;
+
+    inactive = TIMESTAMP_ALL_ACTIVE;
+
+    remaining = NULL;
+
+    wlock_collections(list);
+
+    for (c = g_list_first(list); c != NULL; c = g_list_next(c))
+    {
+        collec = G_DB_COLLECTION(c->data);
+
+        got = g_db_collection_set_last_active(collec, timestamp, &inactive, db);
+        remaining = g_list_concat(remaining, got);
+
+    }
+
+    gint sort_with_timestamp(GDbItem *a, GDbItem *b)
+    {
+        return g_db_item_cmp(a, b, false);
+    }
+
+    remaining = g_list_sort(remaining, (GCompareFunc)sort_with_timestamp);
+
+    result = true;
+
+    for (i = g_list_last(remaining); i != NULL && result; i = g_list_previous(i))
+    {
+        item = G_DB_ITEM(i->data);
+
+        for (c = g_list_first(list); c != NULL && result; c = g_list_next(c))
+        {
+            collec = G_DB_COLLECTION(c->data);
+
+            if (g_db_collection_set_inactive(collec, item, &inactive))
+            {
+                result = g_db_collection_store_updated_item(collec, item, db);
+                break;
+            }
+
+        }
+
+    }
+
+    wunlock_collections(list);
+
+    g_list_free(remaining);
+
+    return result;
+
+}
diff --git a/src/analysis/db/collection.h b/src/analysis/db/collection.h
index e550009..ac3e60f 100644
--- a/src/analysis/db/collection.h
+++ b/src/analysis/db/collection.h
@@ -84,14 +84,12 @@ bool g_db_collection_send_all_updates(GDbCollection *, int);
 /* Met à disposition un encadrement des accès aux éléments. */
 void g_db_collection_lock_unlock(GDbCollection *, bool, bool);
 
-
 #define g_db_collection_wlock(col) g_db_collection_lock_unlock(col, true, true);
 #define g_db_collection_wunlock(col) g_db_collection_lock_unlock(col, true, false);
 
 #define g_db_collection_rlock(col) g_db_collection_lock_unlock(col, false, true);
 #define g_db_collection_runlock(col) g_db_collection_lock_unlock(col, false, false);
 
-
 /* Renvoie la liste des éléments rassemblés. */
 GList *g_db_collection_list_items(const GDbCollection *);
 
@@ -104,11 +102,17 @@ 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);
 
-/* Procède à la modification d'un élément dans la collection. */
-bool _g_db_collection_modify_item(GDbCollection *, GDbItem *, bool);
+/* Met à jour le statut d'activité d'un élément de collection. */
+bool _g_db_collection_update_item_activity(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)
+#define g_db_collection_update_item_activity(c, i) _g_db_collection_update_item_activity(c, i, true)
+
+/* Active les éléments en amont d'un horodatage donné. */
+GList *g_db_collection_set_last_active(GDbCollection *, timestamp_t, timestamp_t *, sqlite3 *);
+
+/* Désactive les éléments en aval d'un horodatage donné. */
+bool g_db_collection_set_inactive(GDbCollection *, GDbItem *, timestamp_t *);
 
 
 
@@ -132,6 +136,18 @@ void attach_binary_to_collections(GList *, GLoadedBinary *);
 /* Recherche une collection correspondant à un type donné. */
 GDbCollection *find_collection_in_list(GList *, uint32_t);
 
+/* Met à disposition un encadrement des accès aux éléments. */
+void lock_unlock_collections(GList *, bool, bool);
+
+#define wlock_collections(lst) lock_unlock_collections(lst, true, true);
+#define wunlock_collections(lst) lock_unlock_collections(lst, true, false);
+
+#define rlock_collections(lst) lock_unlock_collections(lst, false, true);
+#define runlock_collections(lst) lock_unlock_collections(lst, false, false);
+
+/* Met à jour les statuts d'activité des éléments. */
+bool update_activity_in_collections(GList *, int, sqlite3 *);
+
 
 
 #endif  /* _ANALYSIS_DB_COLLECTION_H */
diff --git a/src/analysis/db/core.c b/src/analysis/db/core.c
deleted file mode 100644
index 81ea117..0000000
--- a/src/analysis/db/core.c
+++ /dev/null
@@ -1,207 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * core.c - prototypes pour les informations de base pour tout élément ajouté
- *
- * Copyright (C) 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 "core.h"
-
-
-#include <endian.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-
-#include "../../common/io.h"
-
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : -                                                            *
-*                                                                             *
-*  Description : Détermine une fois pour toute la désignation de l'usager.    *
-*                                                                             *
-*  Retour      : Nom statique déterminé.                                      *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-const char *get_local_author_name(void)
-{
-    static char result[MAX_DB_AUTHOR_LEN];  /* Désignation à retourner     */
-    char *chrysalide_user;                  /* Eventuel nom spécifique     */
-    char *logname;                          /* Nom depuis l'environnement  */
-    char hostname[HOST_NAME_MAX];           /* Nom de la machine courante  */
-    int ret;                                /* Bilan d'un appel            */
-
-    if (result[0] == '\0')
-    {
-        chrysalide_user = getenv("CHRYSALIDE_USER");
-
-        if (chrysalide_user != NULL)
-        {
-            strncpy(result, chrysalide_user, MAX_DB_AUTHOR_LEN - 1);
-            result[MAX_DB_AUTHOR_LEN - 1] = '\0';
-        }
-        else
-        {
-            logname = getenv("LOGNAME");
-            if (logname == NULL)
-                logname = "";
-
-            ret = gethostname(hostname, HOST_NAME_MAX);
-            if (ret != 0)
-                hostname[0] = '\0';
-
-            if (logname != NULL && hostname[0] != '\0')
-                snprintf(result, MAX_DB_AUTHOR_LEN, "%s@%s", logname, hostname);
-
-            else if (logname != NULL && hostname[0] == '\0')
-                snprintf(result, MAX_DB_AUTHOR_LEN, "%s", logname);
-
-            else if (logname == NULL && hostname[0] != '\0')
-                snprintf(result, MAX_DB_AUTHOR_LEN, "@%s", hostname);
-
-            else
-                snprintf(result, MAX_DB_AUTHOR_LEN, "anonymous");
-
-        }
-
-    }
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : info = informations à constituer. [OUT]                      *
-*                type = type de l'élément à mettre en place.                  *
-*                user = provenance des informations ou NULL si machine.       *
-*                                                                             *
-*  Description : Initialise le coeur des informations d'un élément ajouté.    *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-void init_core_db_info(core_db_info *info, uint64_t type, const char *user)
-{
-    info->type = type;
-
-    strncpy(info->user, user, MAX_DB_AUTHOR_LEN - 1);
-    info->user[MAX_DB_AUTHOR_LEN - 1] = '\0';
-
-    info->created = time(NULL);
-    info->modified = info->created;
-
-    info->saved = 0;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : info = informations à constituer. [OUT]                      *
-*                type = type de l'élément, lu en avance de phase.             *
-*                fd   = flux ouvert en lecture pour l'importation.            *
-*                                                                             *
-*  Description : Importe le coeur des informations d'un élément ajouté.       *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-bool load_core_db_info(core_db_info *info, uint64_t type, int fd)
-{
-#if 0
-    ssize_t got;                            /* Quantité de données reçues  */
-    uint64_t val64;                         /* Valeur sur 64 bits          */
-
-    info->type = type;
-
-    got = safe_recv(fd, info->user, MAX_DB_AUTHOR_LEN, MSG_WAITALL);
-    if (got != MAX_DB_AUTHOR_LEN) return false;
-
-    info->user[MAX_DB_AUTHOR_LEN - 1] = '\0';
-
-    got = safe_recv(fd, &val64, sizeof(uint64_t), MSG_WAITALL);
-    if (got != sizeof(uint64_t)) return false;
-
-    info->created = be64toh(val64);
-
-    got = safe_recv(fd, &val64, sizeof(uint64_t), MSG_WAITALL);
-    if (got != sizeof(uint64_t)) return false;
-
-    info->modified = be64toh(val64);
-
-    info->saved = info->modified;
-#endif
-    return true;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : info = informations à sauvegarer.                            *
-*                fd   = flux ouvert en écriture pour l'exportation.           *
-*                                                                             *
-*  Description : Exporte le coeur des informations d'un élément ajouté.       *
-*                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-bool store_core_db_info(core_db_info *info, int fd)
-{
-#if 0
-    ssize_t got;                            /* Quantité de données reçues  */
-
-    got = safe_send(fd, (uint64_t []) { htobe64(info->type) }, sizeof(uint64_t), MSG_WAITALL);
-    if (got != sizeof(uint64_t)) return false;
-
-    got = safe_send(fd, info->user, MAX_DB_AUTHOR_LEN, MSG_WAITALL);
-    if (got != MAX_DB_AUTHOR_LEN) return false;
-
-    got = safe_send(fd, (uint64_t []) { htobe64(info->created) }, sizeof(uint64_t), MSG_WAITALL);
-    if (got != sizeof(uint64_t)) return false;
-
-    got = safe_send(fd, (uint64_t []) { htobe64(info->modified) }, sizeof(uint64_t), MSG_WAITALL);
-    if (got != sizeof(uint64_t)) return false;
-
-    info->saved = time(NULL);
-#endif
-    return true;
-
-}
diff --git a/src/analysis/db/core.h b/src/analysis/db/core.h
deleted file mode 100644
index b53b250..0000000
--- a/src/analysis/db/core.h
+++ /dev/null
@@ -1,70 +0,0 @@
-
-/* Chrysalide - Outil d'analyse de fichiers binaires
- * core.h - prototypes pour les informations de base pour tout élément ajouté
- *
- * Copyright (C) 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/>.
- */
-
-
-#ifndef _ANALYSIS_DB_CORE_H
-#define _ANALYSIS_DB_CORE_H
-
-
-#include <inttypes.h>
-#include <stdbool.h>
-
-
-
-/**
- * Taille maximale pour un nom d'auteur.
- * On considère comme exemple large '"Nom Prénom" xxx@yyy.tld'
- */
-#define MAX_DB_AUTHOR_LEN 128
-
-
-/* Informations de base pour tout élément ajouté */
-typedef struct _core_db_info
-{
-    uint64_t type;                          /* Type de l'élément           */
-    char hash[65];                          /* Empreinte SHA256            */
-
-    char user[MAX_DB_AUTHOR_LEN];           /* Auteur humain ou NULL       */
-
-    uint64_t created;                       /* Date de création            */
-    uint64_t modified;                      /* Date de modification        */
-
-    uint64_t saved;                         /* Statut de l'élément         */
-
-} core_db_info;
-
-
-/* Détermine une fois pour toute la désignation de l'usager. */
-const char *get_local_author_name(void);
-
-/* Initialise le coeur des informations d'un élément ajouté. */
-void init_core_db_info(core_db_info *, uint64_t, const char *);
-
-/* Importe le coeur des informations d'un élément ajouté. */
-bool load_core_db_info(core_db_info *, uint64_t, int);
-
-/* Exporte le coeur des informations d'un élément ajouté. */
-bool store_core_db_info(core_db_info *, int);
-
-
-
-#endif  /* _ANALYSIS_DB_CORE_H */
diff --git a/src/analysis/db/item-int.h b/src/analysis/db/item-int.h
index 12c69f3..1af1048 100644
--- a/src/analysis/db/item-int.h
+++ b/src/analysis/db/item-int.h
@@ -36,12 +36,18 @@
 
 
 
+/* Effectue la comparaison entre deux éléments de collection. */
+typedef gint (* cmp_db_item_fc) (GDbItem *, GDbItem *, bool);
+
 /* Importe la définition d'une base d'éléments pour collection. */
 typedef bool (* recv_db_item_fc) (GDbItem *, int, int);
 
 /* Exporte la définition d'une base d'éléments pour collection. */
 typedef bool (* send_db_item_fc) (const GDbItem *, int, int);
 
+/* Construit la description humaine d'un signet sur un tampon. */
+typedef void (* build_item_label_fc) (GDbItem *);
+
 /* Exécute un élément de collection sur un binaire. */
 typedef bool (* run_item_fc) (GDbItem *, GLoadedBinary *);
 
@@ -57,13 +63,15 @@ struct _GDbItem
 {
     GObject parent;                         /* A laisser en premier        */
 
-    uint64_t created;                       /* Date de création            */
-    uint64_t modified;                      /* Date de modification        */
+    bool server_side;                       /* Instance côté serveur       */
+
+    timestamp_t created;                    /* Date de création            */
+    timestamp_t timestamp;                  /* Date dernière activité      */
 
     rle_string author;                      /* Utilisateur d'origine       */
     rle_string tool;                        /* Eventuel outil automatique ?*/
 
-    rle_string label;                       /* Représentation humaine      */
+    char *label;                            /* Représentation humaine      */
 
     bool is_volatile;                       /* Pas besoin de sauvegarde ?  */
 
@@ -78,10 +86,12 @@ struct _GDbItemClass
 {
     GObjectClass parent;                    /* A laisser en premier        */
 
-    GCompareFunc cmp;                       /* Comparaison entre éléments  */
+    cmp_db_item_fc cmp;                     /* Comparaison entre éléments  */
 
     recv_db_item_fc recv;                   /* Réception depuis le réseau  */
     send_db_item_fc send;                   /* Emission depuis le réseau   */
+
+    build_item_label_fc build_label;        /* Construction de description */
     run_item_fc apply;                      /* Application de l'élément    */
     run_item_fc cancel;                     /* Retrait de l'élément        */
 
@@ -93,8 +103,8 @@ struct _GDbItemClass
 
 /* Définition du tronc commun pour les créations SQLite */
 #define SQLITE_DB_ITEM_CREATE                   \
-    "created INTEGER, "                         \
-    "modified INTEGER, "                        \
+    SQLITE_TIMESTAMP_CREATE("created") ", "     \
+    SQLITE_TIMESTAMP_CREATE("timestamp") ", "   \
     SQLITE_RLESTR_CREATE("author") ", "         \
     SQLITE_RLESTR_CREATE("tool") ", "           \
     SQLITE_RLESTR_CREATE("label")
diff --git a/src/analysis/db/item.c b/src/analysis/db/item.c
index d2f87c3..5463e85 100644
--- a/src/analysis/db/item.c
+++ b/src/analysis/db/item.c
@@ -26,6 +26,7 @@
 
 #include <assert.h>
 #include <malloc.h>
+#include <string.h>
 #include <sqlite3.h>
 
 
@@ -47,9 +48,6 @@ static void g_db_item_dispose(GDbItem *);
 /* Procède à la libération totale de la mémoire. */
 static void g_db_item_finalize(GDbItem *);
 
-/* Effectue la comparaison entre deux éléments de collection. */
-static gint g_db_item_cmp(GDbItem *, GDbItem *);
-
 /* Importe la définition d'une base d'éléments pour collection. */
 static bool g_db_item_recv_from_fd(GDbItem *, int, int);
 
@@ -94,7 +92,7 @@ static void g_db_item_class_init(GDbItemClass *klass)
     object->dispose = (GObjectFinalizeFunc/* ! */)g_db_item_dispose;
     object->finalize = (GObjectFinalizeFunc)g_db_item_finalize;
 
-    klass->cmp = (GCompareFunc)g_db_item_cmp;
+    klass->cmp = (cmp_db_item_fc)g_db_item_cmp;
 
     klass->recv = (recv_db_item_fc)g_db_item_recv_from_fd;
     klass->send = (send_db_item_fc)g_db_item_send_to_fd;
@@ -165,6 +163,12 @@ static void g_db_item_dispose(GDbItem *item)
 
 static void g_db_item_finalize(GDbItem *item)
 {
+    exit_rle_string(&item->author);
+    exit_rle_string(&item->tool);
+
+    if (item->label != NULL)
+        free(item->label);
+
     G_OBJECT_CLASS(g_db_item_parent_class)->finalize(G_OBJECT(item));
 
 }
@@ -172,8 +176,31 @@ static void g_db_item_finalize(GDbItem *item)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : a = premier élément à analyser.                              *
-*                b = second élément à analyser.                               *
+*  Paramètres  : item = élément de collection à traiter.                      *
+*                                                                             *
+*  Description : Indique à l'élément qu'il se trouve du côté serveur.         *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_db_item_set_server_side(GDbItem *item)
+{
+    init_timestamp(&item->created);
+    item->timestamp = item->created;
+
+    item->server_side = true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : a    = premier élément à analyser.                           *
+*                b    = second élément à analyser.                            *
+*                with = précise les horodatages à prendre en compte.          *
 *                                                                             *
 *  Description : Effectue la comparaison entre deux éléments de collection.   *
 *                                                                             *
@@ -183,35 +210,42 @@ static void g_db_item_finalize(GDbItem *item)
 *                                                                             *
 ******************************************************************************/
 
-static gint g_db_item_cmp(GDbItem *a, GDbItem *b)
+gint g_db_item_cmp(GDbItem *a, GDbItem *b, bool with)
 {
     gint result;                            /* Bilan à retourner           */
 
-    /**
-     * A n'utiliser qu'en dernier recours, pour départager deux
-     * éléments par un serveur NTP...
-     */
+    if (with)
+        result = cmp_timestamp(&a->timestamp, &b->timestamp);
+    else
+        result = 0;
 
-    if (a->modified > b->modified)
-        result = 1;
+    if (result == 0)
+        result = cmp_timestamp(&a->created, &b->created);
 
-    else if (a->modified < b->modified)
-        result = -1;
+    if (result == 0)
+        result = strcmp(a->label, b->label);
 
-    else
-    {
-        if (a->created > b->created)
-            result = 1;
+    return result;
 
-        else if (a->created < b->created)
-            result = -1;
+}
 
-        else
-            result = cmp_rle_string(&a->label, &b->label);
 
-    }
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : a = premier élément à analyser.                              *
+*                b = second élément à analyser.                               *
+*                                                                             *
+*  Description : Effectue la comparaison entre deux éléments de collection.   *
+*                                                                             *
+*  Retour      : Bilan de la comparaison : -1, 0 ou 1.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
 
-    return result;
+gint g_db_item_compare_with_timestamp(GDbItem *a, GDbItem *b)
+{
+    return G_DB_ITEM_GET_CLASS(a)->cmp(a, b, true);
 
 }
 
@@ -229,18 +263,18 @@ static gint g_db_item_cmp(GDbItem *a, GDbItem *b)
 *                                                                             *
 ******************************************************************************/
 
-gint g_db_item_compare(GDbItem *a, GDbItem *b)
+gint g_db_item_compare_without_timestamp(GDbItem *a, GDbItem *b)
 {
-    return G_DB_ITEM_GET_CLASS(a)->cmp(a, b);
+    return G_DB_ITEM_GET_CLASS(a)->cmp(a, b, false);
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : item  = base d'éléments à charger. [OUT]                     *
-*                fd    = flux ouvert en lecture pour l'importation.           *
-*                flags = éventuelles options d'envoi supplémentaires.         *
+*  Paramètres  : item   = base d'éléments à charger. [OUT]                    *
+*                fd     = flux ouvert en lecture pour l'importation.          *
+*                flags  = éventuelles options d'envoi supplémentaires.        *
 *                                                                             *
 *  Description : Importe la définition d'une base d'éléments pour collection. *
 *                                                                             *
@@ -252,18 +286,17 @@ gint g_db_item_compare(GDbItem *a, GDbItem *b)
 
 static bool g_db_item_recv_from_fd(GDbItem *item, int fd, int flags)
 {
-    uint64_t val64;                         /* Valeur sur 64 bits          */
     bool status;                            /* Bilan d'une réception       */
 
-    status = safe_recv(fd, &val64, sizeof(uint64_t), MSG_WAITALL | flags);
-    if (!status) return false;
-
-    item->created = be64toh(val64);
+    if (!item->server_side)
+    {
+        status = recv_timestamp(&item->created, fd, flags);
+        if (!status) return false;
 
-    status = safe_recv(fd, &val64, sizeof(uint64_t), MSG_WAITALL | flags);
-    if (!status) return false;
+        status = recv_timestamp(&item->timestamp, fd, flags);
+        if (!status) return false;
 
-    item->modified = be64toh(val64);
+    }
 
     status = recv_rle_string(&item->author, fd, flags);
     if (!status) return false;
@@ -271,9 +304,6 @@ static bool g_db_item_recv_from_fd(GDbItem *item, int fd, int flags)
     status = recv_rle_string(&item->tool, fd, flags);
     if (!status) return false;
 
-    status = recv_rle_string(&item->label, fd, flags);
-    if (!status) return false;
-
     return true;
 
 }
@@ -295,7 +325,13 @@ static bool g_db_item_recv_from_fd(GDbItem *item, int fd, int flags)
 
 bool g_db_item_recv(GDbItem *item, int fd, int flags)
 {
-    return G_DB_ITEM_GET_CLASS(item)->recv(item, fd, flags);
+    bool result;                            /* Bilan à retourner           */
+
+    result = G_DB_ITEM_GET_CLASS(item)->recv(item, fd, flags);
+
+    G_DB_ITEM_GET_CLASS(item)->build_label(item);
+
+    return result;
 
 }
 
@@ -318,19 +354,20 @@ static bool g_db_item_send_to_fd(const GDbItem *item, int fd, int flags)
 {
     bool status;                            /* Bilan d'une émission        */
 
-    status = safe_send(fd, (uint64_t []) { htobe64(item->created) }, sizeof(uint64_t), MSG_MORE | flags);
-    if (!status) return false;
+    if (item->server_side)
+    {
+        status = send_timestamp(&item->created, fd, MSG_MORE | flags);
+        if (!status) return false;
 
-    status = safe_send(fd, (uint64_t []) { htobe64(item->modified) }, sizeof(uint64_t), MSG_MORE | flags);
-    if (!status) return false;
+        status = send_timestamp(&item->timestamp, fd, MSG_MORE | flags);
+        if (!status) return false;
 
-    status = send_rle_string(&item->author, fd, MSG_MORE | flags);
-    if (!status) return false;
+    }
 
-    status = send_rle_string(&item->tool, fd, MSG_MORE | flags);
+    status = send_rle_string(&item->author, fd, MSG_MORE | flags);
     if (!status) return false;
 
-    status = send_rle_string(&item->label, fd, flags);
+    status = send_rle_string(&item->tool, fd, flags);
     if (!status) return false;
 
     return true;
@@ -417,6 +454,86 @@ bool g_db_item_cancel(GDbItem *item, GLoadedBinary *binary)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : item = élément de collection à consulter.                    *
+*                                                                             *
+*  Description : Décrit l'élément de collection en place.                     *
+*                                                                             *
+*  Retour      : Chaîne de caractère correspondante.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+const char *g_db_item_get_label(const GDbItem *item)
+{
+    return item->label;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : item = élément de collection à consulter.                    *
+*                                                                             *
+*  Description : Fournit l'horodatage associé à l'élément de collection.      *
+*                                                                             *
+*  Retour      : Date d'activité de l'élément.                                *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+timestamp_t g_db_item_get_timestamp(const GDbItem *item)
+{
+    return item->timestamp;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : item  = élément de collection à mettre à jour.               *
+*                first = horodatage du premier élément désactivé ou NULL.     *
+*                                                                             *
+*  Description : Active ou désactive un élément de collection en place.       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_db_item_set_activity(GDbItem *item, timestamp_t *first)
+{
+    if (first == NULL)
+        item->timestamp = item->created;
+    else
+        item->timestamp = --(*first);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : item = élément de collection à consulter.                    *
+*                                                                             *
+*  Description : Indique si l'élément est activé ou désactivé.                *
+*                                                                             *
+*  Retour      : Etat de l'activité de l'élément.                             *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_db_item_is_active(const GDbItem *item)
+{
+    return (cmp_timestamp(&item->created, &item->timestamp) == 0);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : item        = base d'éléments à modifier.                    *
 *                is_volatile = état du besoin en sauvegarde.                  *
 *                                                                             *
@@ -477,31 +594,17 @@ bool g_db_item_is_volatile(const GDbItem *item)
 static bool _g_db_item_prepare_db_statement(const GDbItem *item, bound_value **values, size_t *count)
 {
     bool result;                            /* Bilan à retourner           */
-    bound_value *value;                     /* Valeur à éditer / définir   */
 
     result = true;
 
-    *count += 2;
-    *values = (bound_value *)realloc(*values, *count * sizeof(bound_value));
-
-    value = &(*values)[*count - 2];
-
-    value->name = "created";
-    value->type = SQLITE_INT64;
-    value->integer64 = item->created;
+    result &= prepare_db_statement_for_timestamp(&item->created, "created", values, count);
 
-    value = &(*values)[*count - 1];
-
-    value->name = "modified";
-    value->type = SQLITE_INT64;
-    value->integer64 = item->modified;
+    result &= prepare_db_statement_for_timestamp(&item->timestamp, "timestamp", values, count);
 
     result &= prepare_db_statement_for_rle_string(&item->author, "author", values, count);
 
     result &= prepare_db_statement_for_rle_string(&item->tool, "tool", values, count);
 
-    result &= prepare_db_statement_for_rle_string(&item->label, "label", values, count);
-
     return result;
 
 }
@@ -548,28 +651,17 @@ bool g_db_item_prepare_db_statement(const GDbItem *item, bound_value **values, s
 static bool _g_db_item_load(GDbItem *item, const bound_value *values, size_t count)
 {
     bool result;                            /* Bilan global à retourner    */
-    const bound_value *value;               /* Valeur à éditer / définir   */
-
-    value = find_bound_value(values, count, "created");
-    if (value == NULL) return false;
-    if (value->type != SQLITE_INT64) return false;
-
-    item->created = value->integer64;
 
-    value = find_bound_value(values, count, "modified");
-    if (value == NULL) return false;
-    if (value->type != SQLITE_INT64) return false;
+    result = true;
 
-    item->modified = value->integer64;
+    result &= load_timestamp(&item->created, "created", values, count);
 
-    result = true;
+    result &= load_timestamp(&item->timestamp, "timestamp", values, count);
 
     result &= load_rle_string(&item->author, "author", values, count);
 
     result &= load_rle_string(&item->tool, "tool", values, count);
 
-    result &= load_rle_string(&item->label, "label", values, count);
-
     return result;
 
 }
@@ -591,6 +683,12 @@ static bool _g_db_item_load(GDbItem *item, const bound_value *values, size_t cou
 
 bool g_db_item_load(GDbItem *item, const bound_value *values, size_t count)
 {
-    return G_DB_ITEM_GET_CLASS(item)->load(item, values, count);
+    bool result;                            /* Bilan à retourner           */
+
+    result = G_DB_ITEM_GET_CLASS(item)->load(item, values, count);
+
+    G_DB_ITEM_GET_CLASS(item)->build_label(item);
+
+    return result;
 
 }
diff --git a/src/analysis/db/item.h b/src/analysis/db/item.h
index 09ac45c..7d3392c 100644
--- a/src/analysis/db/item.h
+++ b/src/analysis/db/item.h
@@ -29,6 +29,7 @@
 #include <stdbool.h>
 
 
+#include "misc/timestamp.h"
 #include "../../common/sqlite.h"
 
 
@@ -55,8 +56,17 @@ typedef struct _GDbItemClass GDbItemClass;
 /* Indique le type défini pour une base d'élément de collection générique. */
 GType g_db_item_get_type(void);
 
+/* Indique à l'élément qu'il se trouve du côté serveur. */
+void g_db_item_set_server_side(GDbItem *);
+
+/* Effectue la comparaison entre deux éléments de collection. */
+gint g_db_item_cmp(GDbItem *, GDbItem *, bool);
+
+/* Effectue la comparaison entre deux éléments de collection. */
+gint g_db_item_compare_with_timestamp(GDbItem *, GDbItem *);
+
 /* Effectue la comparaison entre deux éléments de collection. */
-gint g_db_item_compare(GDbItem *, GDbItem *);
+gint g_db_item_compare_without_timestamp(GDbItem *, GDbItem *);
 
 /* Importe la définition d'une base d'éléments pour collection. */
 bool g_db_item_recv(GDbItem *, int, int);
@@ -70,6 +80,18 @@ bool g_db_item_apply(GDbItem *, GLoadedBinary *);
 /* Annule une bascule d'affichage d'opérande sur un binaire. */
 bool g_db_item_cancel(GDbItem *, GLoadedBinary *);
 
+/* Décrit l'élément de collection en place. */
+const char *g_db_item_get_label(const GDbItem *);
+
+/* Fournit l'horodatage associé à l'élément de collection. */
+timestamp_t g_db_item_get_timestamp(const GDbItem *);
+
+/*  Active ou désactive un élément de collection en place. */
+void g_db_item_set_activity(GDbItem *, timestamp_t *);
+
+/* Indique si l'élément est activé ou désactivé. */
+bool g_db_item_is_active(const GDbItem *);
+
 /* Définit si l'élément contient des données à oublier ou non. */
 void g_db_item_set_volatile(GDbItem *, bool);
 
diff --git a/src/analysis/db/items/bookmark.c b/src/analysis/db/items/bookmark.c
index 8512406..ba64491 100644
--- a/src/analysis/db/items/bookmark.c
+++ b/src/analysis/db/items/bookmark.c
@@ -28,6 +28,9 @@
 #include <sys/socket.h>
 
 
+#include <i18n.h>
+
+
 #include "../collection-int.h"
 #include "../item-int.h"
 
@@ -69,7 +72,7 @@ static void g_db_bookmark_dispose(GDbBookmark *);
 static void g_db_bookmark_finalize(GDbBookmark *);
 
 /* Effectue la comparaison entre deux signets de collection. */
-static gint g_db_bookmark_cmp(GDbBookmark *, GDbBookmark *);
+static gint g_db_bookmark_cmp(GDbBookmark *, GDbBookmark *, bool);
 
 /* Importe la définition d'un signet dans un flux réseau. */
 static bool g_db_bookmark_recv_from_fd(GDbBookmark *, int, int);
@@ -80,6 +83,9 @@ static bool g_db_bookmark_send_to_fd(const GDbBookmark *, int, int);
 /* Exécute un signet sur un tampon de binaire chargé. */
 static bool g_db_bookmark_run(GDbBookmark *, GLoadedBinary *, bool *, bool);
 
+/* Construit la description humaine d'un signet sur un tampon. */
+static void g_db_bookmark_build_label(GDbBookmark *);
+
 /* Applique un signet sur un tampon de binaire chargé. */
 static bool g_db_bookmark_apply(GDbBookmark *, GLoadedBinary *);
 
@@ -168,10 +174,12 @@ static void g_db_bookmark_class_init(GDbBookmarkClass *klass)
 
     item = G_DB_ITEM_CLASS(klass);
 
-    item->cmp = (GCompareFunc)g_db_bookmark_cmp;
+    item->cmp = (cmp_db_item_fc)g_db_bookmark_cmp;
 
     item->recv = (recv_db_item_fc)g_db_bookmark_recv_from_fd;
     item->send = (send_db_item_fc)g_db_bookmark_send_to_fd;
+
+    item->build_label = (build_item_label_fc)g_db_bookmark_build_label; 
     item->apply = (run_item_fc)g_db_bookmark_apply;
     item->cancel = (run_item_fc)g_db_bookmark_cancel;
 
@@ -269,8 +277,9 @@ GDbBookmark *g_db_bookmark_new(const vmpa2t *addr, const char *comment)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : a = premier élément à analyser.                              *
-*                b = second élément à analyser.                               *
+*  Paramètres  : a    = premier élément à analyser.                           *
+*                b    = second élément à analyser.                            *
+*                with = précise les horodatages à prendre en compte.          *
 *                                                                             *
 *  Description : Effectue la comparaison entre deux signets de collection.    *
 *                                                                             *
@@ -280,7 +289,7 @@ GDbBookmark *g_db_bookmark_new(const vmpa2t *addr, const char *comment)
 *                                                                             *
 ******************************************************************************/
 
-static gint g_db_bookmark_cmp(GDbBookmark *a, GDbBookmark *b)
+static gint g_db_bookmark_cmp(GDbBookmark *a, GDbBookmark *b, bool with)
 {
     gint result;                            /* Bilan de la comparaison     */
 
@@ -290,7 +299,7 @@ static gint g_db_bookmark_cmp(GDbBookmark *a, GDbBookmark *b)
         result = cmp_rle_string(&a->comment, &b->comment);
 
     if (result == 0)
-        result = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->cmp(G_DB_ITEM(a), G_DB_ITEM(b));
+        result = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->cmp(G_DB_ITEM(a), G_DB_ITEM(b), with);
 
     return result;
 
@@ -413,6 +422,25 @@ static bool g_db_bookmark_run(GDbBookmark *bookmark, GLoadedBinary *binary, bool
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : bookmark = signet à manipuler.                               *
+*                                                                             *
+*  Description : Construit la description humaine d'un signet sur un tampon.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_db_bookmark_build_label(GDbBookmark *bookmark)
+{
+    asprintf(&G_DB_ITEM(bookmark)->label, _("Bookmark \"%s\""), get_rle_string(&bookmark->comment));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : bookmark = signet à manipuler.                               *
 *                binary   = binaire chargé en mémoire à modifier.             *
 *                                                                             *
 *  Description : Applique un signet sur un tampon de binaire chargé.          *
diff --git a/src/analysis/db/items/comment.c b/src/analysis/db/items/comment.c
index 1bed0ab..249620b 100644
--- a/src/analysis/db/items/comment.c
+++ b/src/analysis/db/items/comment.c
@@ -67,7 +67,7 @@ static void g_db_comment_dispose(GDbComment *);
 static void g_db_comment_finalize(GDbComment *);
 
 /* Effectue la comparaison entre deux commentaires enregistrés. */
-static gint g_db_comment_cmp(GDbComment *, GDbComment *);
+static gint g_db_comment_cmp(GDbComment *, GDbComment *, bool);
 
 /* Importe la définition d'un commentaire dans un flux réseau. */
 static bool g_db_comment_recv_from_fd(GDbComment *, int, int);
@@ -157,7 +157,7 @@ static void g_db_comment_class_init(GDbCommentClass *klass)
 
     item = G_DB_ITEM_CLASS(klass);
 
-    item->cmp = (GCompareFunc)g_db_comment_cmp;
+    item->cmp = (cmp_db_item_fc)g_db_comment_cmp;
 
     item->recv = (recv_db_item_fc)g_db_comment_recv_from_fd;
     item->send = (send_db_item_fc)g_db_comment_send_to_fd;
@@ -259,8 +259,9 @@ GDbComment *g_db_comment_new(const vmpa2t *addr, const char *text, bool is_volat
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : a = premier élément à analyser.                              *
-*                b = second élément à analyser.                               *
+*  Paramètres  : a    = premier élément à analyser.                           *
+*                b    = second élément à analyser.                            *
+*                with = précise les horodatages à prendre en compte.          *
 *                                                                             *
 *  Description : Effectue la comparaison entre deux commentaires enregistrés. *
 *                                                                             *
@@ -270,7 +271,7 @@ GDbComment *g_db_comment_new(const vmpa2t *addr, const char *text, bool is_volat
 *                                                                             *
 ******************************************************************************/
 
-static gint g_db_comment_cmp(GDbComment *a, GDbComment *b)
+static gint g_db_comment_cmp(GDbComment *a, GDbComment *b, bool with)
 {
     gint result;                            /* Bilan de la comparaison     */
 
@@ -280,7 +281,7 @@ static gint g_db_comment_cmp(GDbComment *a, GDbComment *b)
         result = cmp_rle_string(&a->text, &b->text);
 
     if (result == 0)
-        result = G_DB_ITEM_CLASS(g_db_comment_parent_class)->cmp(G_DB_ITEM(a), G_DB_ITEM(b));
+        result = G_DB_ITEM_CLASS(g_db_comment_parent_class)->cmp(G_DB_ITEM(a), G_DB_ITEM(b), with);
 
     return 0;
 
diff --git a/src/analysis/db/items/switcher.c b/src/analysis/db/items/switcher.c
index 2168e6b..67187aa 100644
--- a/src/analysis/db/items/switcher.c
+++ b/src/analysis/db/items/switcher.c
@@ -75,7 +75,7 @@ static void g_db_switcher_dispose(GDbSwitcher *);
 static void g_db_switcher_finalize(GDbSwitcher *);
 
 /* Effectue la comparaison entre deux signets de collection. */
-static gint g_db_switcher_cmp(GDbSwitcher *, GDbSwitcher *);
+static gint g_db_switcher_cmp(GDbSwitcher *, GDbSwitcher *, bool);
 
 /* Importe la définition d'un signet dans un flux réseau. */
 static bool g_db_switcher_recv_from_fd(GDbSwitcher *, int, int);
@@ -175,10 +175,11 @@ static void g_db_switcher_class_init(GDbSwitcherClass *klass)
 
     item = G_DB_ITEM_CLASS(klass);
 
-    item->cmp = (GCompareFunc)g_db_switcher_cmp;
+    item->cmp = (cmp_db_item_fc)g_db_switcher_cmp;
 
     item->recv = (recv_db_item_fc)g_db_switcher_recv_from_fd;
     item->send = (send_db_item_fc)g_db_switcher_send_to_fd;
+
     item->apply = (run_item_fc)g_db_switcher_apply;
     item->cancel = (run_item_fc)g_db_switcher_cancel;
 
@@ -190,7 +191,7 @@ static void g_db_switcher_class_init(GDbSwitcherClass *klass)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : switcher = instance à initialiser.                         *
+*  Paramètres  : switcher = instance à initialiser.                           *
 *                                                                             *
 *  Description : Initialise une bascule d'affichage pour opérande numérique.  *
 *                                                                             *
@@ -208,7 +209,7 @@ static void g_db_switcher_init(GDbSwitcher *switcher)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : switcher = instance d'objet GLib à traiter.                *
+*  Paramètres  : switcher = instance d'objet GLib à traiter.                  *
 *                                                                             *
 *  Description : Supprime toutes les références externes.                     *
 *                                                                             *
@@ -227,7 +228,7 @@ static void g_db_switcher_dispose(GDbSwitcher *switcher)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : switcher = instance d'objet GLib à traiter.                *
+*  Paramètres  : switcher = instance d'objet GLib à traiter.                  *
 *                                                                             *
 *  Description : Procède à la libération totale de la mémoire.                *
 *                                                                             *
@@ -259,7 +260,7 @@ static void g_db_switcher_finalize(GDbSwitcher *switcher)
 
 GDbSwitcher *g_db_switcher_new(const GArchInstruction *instr, const GImmOperand *imm, ImmOperandDisplay display)
 {
-    GDbSwitcher *result;                      /* Instance à retourner        */
+    GDbSwitcher *result;                    /* Instance à retourner        */
     size_t count;                           /* Nombre d'opérandes à visiter*/
     size_t i;                               /* Boucle de parcours          */
     const mrange_t *range;                  /* Localisation de l'instruct° */
@@ -299,8 +300,9 @@ GDbSwitcher *g_db_switcher_new(const GArchInstruction *instr, const GImmOperand
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : a = premier élément à analyser.                              *
-*                b = second élément à analyser.                               *
+*  Paramètres  : a    = premier élément à analyser.                           *
+*                b    = second élément à analyser.                            *
+*                with = précise les horodatages à prendre en compte.          *
 *                                                                             *
 *  Description : Effectue la comparaison entre deux signets de collection.    *
 *                                                                             *
@@ -310,7 +312,7 @@ GDbSwitcher *g_db_switcher_new(const GArchInstruction *instr, const GImmOperand
 *                                                                             *
 ******************************************************************************/
 
-static gint g_db_switcher_cmp(GDbSwitcher *a, GDbSwitcher *b)
+static gint g_db_switcher_cmp(GDbSwitcher *a, GDbSwitcher *b, bool with)
 {
     gint result;                            /* Bilan de la comparaison     */
 
@@ -337,7 +339,7 @@ static gint g_db_switcher_cmp(GDbSwitcher *a, GDbSwitcher *b)
     }
 
     if (result == 0)
-        result = G_DB_ITEM_CLASS(g_db_switcher_parent_class)->cmp(G_DB_ITEM(a), G_DB_ITEM(b));
+        result = G_DB_ITEM_CLASS(g_db_switcher_parent_class)->cmp(G_DB_ITEM(a), G_DB_ITEM(b), with);
 
     return result;
 
diff --git a/src/analysis/db/misc/Makefile.am b/src/analysis/db/misc/Makefile.am
index b3829e3..dde6026 100755
--- a/src/analysis/db/misc/Makefile.am
+++ b/src/analysis/db/misc/Makefile.am
@@ -2,7 +2,8 @@
 noinst_LTLIBRARIES  = libanalysisdbmisc.la
 
 libanalysisdbmisc_la_SOURCES =			\
-	rlestr.h rlestr.c
+	rlestr.h rlestr.c					\
+	timestamp.h timestamp.c
 
 libanalysisdbmisc_la_LIBADD = 
 
diff --git a/src/analysis/db/misc/timestamp.c b/src/analysis/db/misc/timestamp.c
new file mode 100644
index 0000000..624c811
--- /dev/null
+++ b/src/analysis/db/misc/timestamp.c
@@ -0,0 +1,263 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * rlestr.c - encodage par plage unique d'une chaîne de caractères
+ *
+ * Copyright (C) 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 "timestamp.h"
+
+
+#include <endian.h>
+#include <malloc.h>
+#include <sqlite3.h>
+#include <time.h>
+
+
+#include "../../../common/io.h"
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = horodatage à initialiser. [OUT]                  *
+*                                                                             *
+*  Description : Obtient un horodatage initialisé au moment même.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void init_timestamp(timestamp_t *timestamp)
+{
+    struct timespec info;                   /* Détails sur l'époque        */
+
+    clock_gettime(CLOCK_REALTIME, &info);
+
+    *timestamp = info.tv_sec * 1000000 + info.tv_nsec / 1000;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : stamp = horodatage d'un élément à tester.                    *
+*                limit = horodatage en limite d'activité (incluse).           *
+*                                                                             *
+*  Description : Définit si un horodatage est plus récent qu'un autre ou non. *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool timestamp_is_younger(timestamp_t stamp, timestamp_t limit)
+{
+    if (limit == TIMESTAMP_ALL_ACTIVE)
+        return true;
+
+    if (limit == TIMESTAMP_ALL_INACTIVE)
+        return false;
+
+    return (stamp <= limit);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : t1 = première chaîne à comparer.                             *
+*                t2 = seconde chaîne à comparer.                              *
+*                                                                             *
+*  Description : Effectue la comparaison entre deux horodatages.              *
+*                                                                             *
+*  Retour      : Résultat de la comparaison : -1, 0 ou 1.                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int cmp_timestamp(const timestamp_t *t1, const timestamp_t *t2)
+{
+    int result;                             /* Bilan à retourner           */
+
+    if (*t1 < *t2)
+        result = -1;
+
+    else if (*t1 > *t2)
+        result = 1;
+
+    else
+        result = 0;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = informations à constituer. [OUT]                 *
+*                fd        = flux ouvert en lecture pour l'importation.       *
+*                flags     = éventuelles options de réception supplémentaires.*
+*                                                                             *
+*  Description : Importe la définition d'un horodatage.                       *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool recv_timestamp(timestamp_t *timestamp, int fd, int flags)
+{
+    uint64_t val64;                         /* Valeur sur 64 bits          */
+    bool status;                            /* Bilan d'une opération       */
+
+    status = safe_recv(fd, &val64, sizeof(uint64_t), MSG_WAITALL | flags);
+    if (!status) return false;
+
+    *timestamp = be64toh(val64);
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = informations à sauvegarer.                       *
+*                fd        = flux ouvert en écriture pour l'exportation.      *
+*                flags     = éventuelles options d'envoi supplémentaires.     *
+*                                                                             *
+*  Description : Exporte la définition d'un horodatage.                       *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool send_timestamp(const timestamp_t *timestamp, int fd, int flags)
+{
+    bool status;                            /* Bilan d'une opération       */
+
+    status = safe_send(fd, (uint64_t []) { htobe64(*timestamp) }, sizeof(uint64_t), flags);
+    if (!status) return false;
+
+    return true;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                       MANIPULATIONS AVEC UNE BASE DE DONNEES                       */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = horodatage aux informations inutiles.            *
+*                name      = désignation personnalisée du champ dans la BD.   *
+*                values    = couples de champs et de valeurs à lier. [OUT]    *
+*                count     = nombre de ces couples. [OUT]                     *
+*                                                                             *
+*  Description : Constitue les champs destinés à une insertion / modification.*
+*                                                                             *
+*  Retour      : Bilan de l'opération : succès ou non.                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool prepare_db_statement_for_timestamp(const timestamp_t *timestamp, const char *name, bound_value **values, size_t *count)
+{
+    bound_value *value;                     /* Valeur à éditer / définir   */
+
+    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+    value = &(*values)[*count - 1];
+
+    value->name = name;
+    value->type = SQLITE_INT64;
+    value->integer64 = *timestamp;
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = horodatage aux informations inutiles.            *
+*                name      = désignation personnalisée du champ dans la BD.   *
+*                values    = tableau d'éléments à compléter. [OUT]            *
+*                count     = nombre de descriptions renseignées. [OUT]        *
+*                                                                             *
+*  Description : Décrit les colonnes utiles à un horodatage.                  *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool setup_load_of_timestamp(const timestamp_t *timestamp, const char *name, bound_value **values, size_t *count)
+{
+    *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+
+    (*values)[*count - 1].name = name;
+
+    return true;
+
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = horodatage à compléter.                          *
+*                name      = désignation personnalisée du champ dans la BD.   *
+*                values    = tableau d'éléments à consulter.                  *
+*                count     = nombre de descriptions renseignées.              *
+*                                                                             *
+*  Description : Charge les valeurs utiles pour un horodatage.                *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool load_timestamp(timestamp_t *timestamp, const char *name, const bound_value *values, size_t count)
+{
+    const bound_value *value;               /* Valeur à intégrer           */
+
+    value = find_bound_value(values, count, name);
+    if (value == NULL) return false;
+    if (value->type != SQLITE_INT64) return false;
+
+    *timestamp = value->integer64;
+
+    return true;
+
+}
diff --git a/src/analysis/db/misc/timestamp.h b/src/analysis/db/misc/timestamp.h
new file mode 100644
index 0000000..1efbb15
--- /dev/null
+++ b/src/analysis/db/misc/timestamp.h
@@ -0,0 +1,80 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * timestamp.h - prototypes pour l'encodage par plage unique d'une chaîne de caractères
+ *
+ * Copyright (C) 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/>.
+ */
+
+
+#ifndef _ANALYSIS_DB_MISC_TIMESTAMP_H
+#define _ANALYSIS_DB_MISC_TIMESTAMP_H
+
+
+#include <stdbool.h>
+#include <stdint.h>
+
+
+#include "../../../common/sqlite.h"
+
+
+
+/* Représentation d'un horodatage */
+typedef uint64_t timestamp_t;
+
+
+/* Valeurs particulières */
+#define TIMESTAMP_ALL_ACTIVE 0
+#define TIMESTAMP_ALL_INACTIVE 1
+
+
+/* Obtient un horodatage initialisé au moment même. */
+void init_timestamp(timestamp_t *);
+
+/* Définit si un horodatage est plus récent qu'un autre ou non. */
+bool timestamp_is_younger(timestamp_t, timestamp_t);
+
+/* Effectue la comparaison entre deux horodatages. */
+int cmp_timestamp(const timestamp_t *, const timestamp_t *);
+
+/* Importe la définition d'un horodatage. */
+bool recv_timestamp(timestamp_t *, int, int);
+
+/* Exporte la définition d'un horodatage. */
+bool send_timestamp(const timestamp_t *, int, int);
+
+
+
+/* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
+
+
+/* Définition du tronc commun pour les créations SQLite */
+#define SQLITE_TIMESTAMP_CREATE(n)      \
+    n " INTEGER"
+
+/* Constitue les champs destinés à une insertion / modification. */
+bool prepare_db_statement_for_timestamp(const timestamp_t *, const char *, bound_value **, size_t *);
+
+/* Décrit les colonnes utiles à un horodatage. */
+bool setup_load_of_timestamp(const timestamp_t *, const char *, bound_value **, size_t *);
+
+/* Charge les valeurs utiles pour un horodatage. */
+bool load_timestamp(timestamp_t *, const char *, const bound_value *, size_t);
+
+
+
+#endif  /* _ANALYSIS_DB_MISC_TIMESTAMP_H */
diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h
index 04bbd33..ab12654 100644
--- a/src/analysis/db/protocol.h
+++ b/src/analysis/db/protocol.h
@@ -95,7 +95,8 @@ typedef enum _DBAction
 {
     DBA_ADD_ITEM,                           /* Ajout d'un élément          */
     DBA_REM_ITEM,                           /* Suppression d'un élément    */
-    DBA_MOD_ITEM,                           /* Modification de l'existant  */
+
+    DBA_CHANGE_STATE,                       /* Changement d'activité       */
 
     DBA_COUNT
 
@@ -116,6 +117,34 @@ typedef enum _DBCommand
     DBC_SAVE,                               /* Enregistrement de l'archive */
     DBC_COLLECTION,                         /* Implication d'une collection*/
 
+
+
+    /**
+     * Gestion de la commande 'DBC_SET_LAST_ACTIVE'.
+     *
+     * Le client connecté envoie un paquet de la forme suivante :
+     *
+     *    [ Statut d'historique : DBC_SET_LAST_ACTIVE ]
+     *    [ <horodatage du dernier élément actif      ]
+     *
+     * Le serveur s'exécute et notifie le client d'éventuels changements,
+     * avec une série de paquets de la forme :
+     *
+     *    [ Traitement de collection : DBC_COLLECTION ]
+     *    [ Action : DBC_SET_LAST_ACTIVE              ]
+     *    [ <élément dont le statut a évolué>         ]
+     *
+     * Les traitements se réalisent dans :
+     *  - g_db_collection_set_last_active() pour la partie serveur.
+     *  - g_db_client_set_last_active() pour la partie client.
+     *
+     */
+
+    DBC_SET_LAST_ACTIVE,                    /* Définition du dernier actif */
+
+
+
+
     DBC_COUNT
 
 } DBCommand;
@@ -174,6 +203,4 @@ typedef enum _DBError
 
 
 
-
-
 #endif  /* _ANALYSIS_DB_PROTOCOL_H */
diff --git a/src/gui/panels/bookmarks.c b/src/gui/panels/bookmarks.c
index 60daf67..b3af70c 100644
--- a/src/gui/panels/bookmarks.c
+++ b/src/gui/panels/bookmarks.c
@@ -579,41 +579,71 @@ static void on_collection_content_changed(GDbCollection *collec, DBAction action
     VMPA_BUFFER(virt);                      /* Adresse virtuelle           */
     GtkTreeIter iter;                       /* Point d'insertion           */
 
+    GtkTreeModel *model;                    /* Modèle de gestion courant   */
+    GDbBookmark *displayed;                 /* Elément de collection       */
 
 
+    if (action == DBA_ADD_ITEM || (action == DBA_CHANGE_STATE && g_db_item_is_active(G_DB_ITEM(bookmark))))
+    {
+        store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
 
-    printf(" Passage :: %d\n", action);
+        format = g_loaded_binary_get_format(panel->binary);
 
 
-    store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
+        proc = g_loaded_binary_get_processor(panel->binary);
+        msize = g_arch_processor_get_memory_size(proc);
+        g_object_unref(G_OBJECT(proc));
 
-    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);
 
-    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);
 
+    }
+
+    else /*if (action == DBA_CHANGE_STATE && g_db_item_is_active(G_DB_ITEM(bookmark)))*/
+    {
+
+        store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
+        model = GTK_TREE_MODEL(store);
+
+
+        if (gtk_tree_model_get_iter_first(model, &iter))
+            do
+            {
+                gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &displayed, -1);
 
+                if (bookmark == displayed)
+                {
+                    gtk_tree_store_remove(store, &iter);
+                    break;
+                }
+
+                g_object_unref(G_OBJECT(displayed));
+
+            }
+            while (gtk_tree_model_iter_next(model, &iter));
+
+
+
+    }
 
 
 }
diff --git a/src/gui/panels/history.c b/src/gui/panels/history.c
index a827961..25ee39f 100644
--- a/src/gui/panels/history.c
+++ b/src/gui/panels/history.c
@@ -32,6 +32,7 @@
 
 
 #include "panel-int.h"
+#include "../../analysis/db/collection.h"
 #include "../../gtkext/easygtk.h"
 
 
@@ -62,6 +63,7 @@ typedef enum _HistoryColumn
     HTC_ITEM,                               /* Elément d'évolution         */
 
     HTC_PICTURE,                            /* Image de représentation     */
+    HTC_FOREGROUND,                         /* Couleur d'impression        */
     HTC_LABEL,                              /* Désignation humaine         */
 
     HTC_COUNT                               /* Nombre de colonnes          */
@@ -84,6 +86,24 @@ static void g_history_panel_finalize(GHistoryPanel *);
 /* Réagit à un changement d'affichage principal de contenu. */
 static void change_history_panel_current_binary(GHistoryPanel *, GLoadedBinary *);
 
+/* Réagit à une modification au sein d'une collection donnée. */
+static void on_history_changed(GDbCollection *, DBAction, GDbItem *, GHistoryPanel *);
+
+/* Compare deux lignes entre elles pour le tri des évolutions. */
+static gint sort_history_lines(GtkTreeModel *, GtkTreeIter *, GtkTreeIter *, gpointer);
+
+/* Réagit à une validation d'une ligne affichée. */
+static void on_history_row_activated(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, GHistoryPanel *);
+
+/* Annule l'élément d'évolution courant. */
+static void do_history_undo(GtkButton *, GHistoryPanel *);
+
+/* Restaure l'élément d'évolution suivant. */
+static void do_history_redo(GtkButton *, GHistoryPanel *);
+
+/* Effectue un nettoyage de l'historique. */
+static void do_history_clean(GtkButton *, GHistoryPanel *);
+
 
 
 /* Indique le type définit pour un panneau d'aperçu de graphiques. */
@@ -137,6 +157,8 @@ static void g_history_panel_init(GHistoryPanel *panel)
     GObject *ref;                           /* Espace de référencement     */
     GtkWidget *scrollwnd;                   /* Support défilant            */
     GtkWidget *treeview;                    /* Affichage de la liste       */
+    GtkCellRenderer *renderer;              /* Moteur de rendu de colonne  */
+    GtkTreeViewColumn *column;              /* Colonne de la liste         */
     GtkWidget *box;                         /* Séparation horizontale      */
     GtkWidget *button;                      /* Bouton de cette même barre  */
 
@@ -159,7 +181,8 @@ static void g_history_panel_init(GHistoryPanel *panel)
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwnd), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwnd), GTK_SHADOW_IN);
 
-    panel->store = gtk_tree_store_new(HTC_COUNT, G_TYPE_OBJECT, CAIRO_GOBJECT_TYPE_SURFACE, G_TYPE_STRING);
+    panel->store = gtk_tree_store_new(HTC_COUNT, G_TYPE_OBJECT, CAIRO_GOBJECT_TYPE_SURFACE,
+                                      G_TYPE_STRING, G_TYPE_STRING);
 
     treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(panel->store));
     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
@@ -167,11 +190,32 @@ static void g_history_panel_init(GHistoryPanel *panel)
 
     panel->treeview = GTK_TREE_VIEW(treeview);
 
+    gtk_tree_sortable_set_default_sort_func(GTK_TREE_SORTABLE(panel->store), sort_history_lines, NULL, NULL);
+
+    g_signal_connect(treeview, "row-activated", G_CALLBACK(on_history_row_activated), panel);
+
     gtk_widget_show(treeview);
     gtk_container_add(GTK_CONTAINER(scrollwnd), treeview);
 
     g_object_unref(G_OBJECT(panel->store));
 
+    /* Cellules d'affichage */
+
+    renderer = gtk_cell_renderer_pixbuf_new();
+    column = gtk_tree_view_column_new_with_attributes("", renderer,
+                                                      "surface", HTC_PICTURE,
+                                                      NULL);
+    gtk_tree_view_column_set_sort_column_id(column, GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+
+    renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes(_("Label"), renderer,
+                                                      "foreground", HTC_FOREGROUND,
+                                                      "text", HTC_LABEL,
+                                                      NULL);
+    gtk_tree_view_column_set_sort_column_id(column, GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+
     /* Eléments de contrôle inférieurs */
 
     box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
@@ -180,15 +224,15 @@ static void g_history_panel_init(GHistoryPanel *panel)
     gtk_box_pack_start(GTK_BOX(base->widget), box, FALSE, TRUE, 0);
 
     button = qck_create_button_with_css_img(NULL, NULL, "img-undo", _("Undo"),
-                                            G_CALLBACK(NULL), NULL);
+                                            G_CALLBACK(do_history_undo), panel);
     gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
 
     button = qck_create_button_with_css_img(NULL, NULL, "img-redo", _("Redo"),
-                                            G_CALLBACK(NULL), NULL);
+                                            G_CALLBACK(do_history_redo), panel);
     gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
 
     button = qck_create_button_with_css_img(NULL, NULL, "img-clean", _("Clean"),
-                                            G_CALLBACK(NULL), NULL);
+                                            G_CALLBACK(do_history_clean), panel);
     gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
 
 }
@@ -303,6 +347,287 @@ GPanelItem *create_history_panel(GObject *ref)
 static void change_history_panel_current_binary(GHistoryPanel *panel, GLoadedBinary *binary)
 {
 
-    /* TODO */
+
+    GtkTreeStore *store;                    /* Modèle de gestion           */
+    GList *collections;                     /* Ensemble de collections     */
+    GList *c;                               /* Boucle de parcours #1       */
+    GDbCollection *collec;                  /* Collection visée manipulée  */
+    GList *items;                           /* Liste des éléments groupés  */
+    GList *i;                               /* Boucle de parcours #2       */
+    GDbItem *item;                          /* Elément à intégrer          */
+    GtkTreeIter iter;                       /* Point d'insertion           */
+
+
+    /* Basculement du binaire utilisé */
+
+
+
+    panel->binary = binary;
+
+
+
+
+
+    store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
+    gtk_tree_store_clear(store);
+
+    /* Si le panneau actif ne représente pas un binaire... */
+
+    if (binary == NULL) return;
+
+    /* Actualisation de l'affichage */
+
+    collections = g_loaded_binary_get_all_collections(binary);
+
+    rlock_collections(collections);
+
+    for (c = g_list_first(collections); c != NULL; c = g_list_next(c))
+    {
+        collec = G_DB_COLLECTION(c->data);
+        items = g_db_collection_list_items(collec);
+
+        for (i = g_list_first(items); i != NULL; i = g_list_next(i))
+        {
+            item = G_DB_ITEM(i->data);
+
+            gtk_tree_store_append(store, &iter, NULL);
+            gtk_tree_store_set(store, &iter,
+                               HTC_ITEM, item,
+                               //HTC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img,
+                               HTC_FOREGROUND, g_db_item_is_active(item) ? NULL : "grey",
+                               HTC_LABEL, g_db_item_get_label(item),
+                               -1);
+
+        }
+
+        g_signal_connect(collec, "content-changed", G_CALLBACK(on_history_changed), panel);
+
+    }
+
+    runlock_collections(collections);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : collec = collection dont le contenu a évolué.                *
+*                action = type d'évolution rencontrée.                        *
+*                item   = élément ajouté, modifié ou supprimé.                *
+*                panel  = panneau d'historique concerné par la procédure.     *
+*                                                                             *
+*  Description : Réagit à une modification au sein d'une collection donnée.   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_history_changed(GDbCollection *collec, DBAction action, GDbItem *item, GHistoryPanel *panel)
+{
+    GtkTreeModel *model;                    /* Modèle de gestion courant   */
+    GtkTreeIter iter;                       /* Boucle de parcours          */
+    GDbItem *displayed;                     /* Elément de collection       */
+
+    model = GTK_TREE_MODEL(panel->store);
+
+    switch (action)
+    {
+        case DBA_CHANGE_STATE:
+
+            if (gtk_tree_model_get_iter_first(model, &iter))
+                do
+                {
+                    gtk_tree_model_get(model, &iter, HTC_ITEM, &displayed, -1);
+
+                    if (item == displayed)
+                    {
+                        gtk_tree_store_set(panel->store, &iter,
+                                           HTC_FOREGROUND, g_db_item_is_active(item) ? NULL : "grey",
+                                           -1);
+                        gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(panel->store),
+                                                             GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
+                                                             GTK_SORT_ASCENDING);
+                        break;
+                    }
+
+                    g_object_unref(G_OBJECT(displayed));
+
+                }
+                while (gtk_tree_model_iter_next(model, &iter));
+
+
+            break;
+
+
+    }
+
+
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : model = gestionnaire de données pour la liste traitée.       *
+*                a     = premier point de comparaison.                        *
+*                b     = second point de comparaison.                         *
+*                dummy = adresse non utilisée ici.                            *
+*                                                                             *
+*  Description : Compare deux lignes entre elles pour le tri des évolutions.  *
+*                                                                             *
+*  Retour      : -1, 0 ou 1 selon le résultat de la comparaison.              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static gint sort_history_lines(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer dummy)
+{
+    gint result;                            /* Bilan à retourner           */
+    GDbItem *item_a;                        /* Elément de collection A     */
+    GDbItem *item_b;                        /* Elément de collection B     */
+
+    gtk_tree_model_get(model, a, HTC_ITEM, &item_a, -1);
+    gtk_tree_model_get(model, b, HTC_ITEM, &item_b, -1);
+
+    result = g_db_item_compare_with_timestamp(item_a, item_b);
+
+    g_object_unref(G_OBJECT(item_a));
+    g_object_unref(G_OBJECT(item_b));
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : treeview = composant graphique manipulé par l'utilisateur.   *
+*                path     = chemin d'accès à la ligne activée.                *
+*                column   = colonne impactée par l'action.                    *
+*                panel    = panneau d'historique concerné par la procédure.   *
+*                                                                             *
+*  Description : Réagit à une validation d'une ligne affichée.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_history_row_activated(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GHistoryPanel *panel)
+{
+
+
+
+
+
+
+}
+
+
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton d'édition de l'historique d'évolution.       *
+*                panel  = panneau d'affichage de l'historique.                *
+*                                                                             *
+*  Description : Annule l'élément d'évolution courant.                        *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void do_history_undo(GtkButton *button, GHistoryPanel *panel)
+{
+    GtkTreeSelection *selection;            /* Sélection courante          */
+    GtkTreeModel *model;                    /* Modèle de gestion de données*/
+    GtkTreeIter iter;                       /* Pointeur vers la ligne visée*/
+    GDbItem *item;                          /* Elément de collection       */
+    GDbClient *client;                      /* Connexion vers la base      */
+
+    selection = gtk_tree_view_get_selection(panel->treeview);
+
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+    {
+        if (!gtk_tree_model_iter_previous(model, &iter))
+            return;
+
+        gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1);
+
+        client = g_loaded_binary_get_db_client(panel->binary);
+        g_db_client_set_last_active(client, g_db_item_get_timestamp(item));
+        g_object_unref(G_OBJECT(client));
+
+        g_object_unref(G_OBJECT(item));
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton d'édition de l'historique d'évolution.       *
+*                panel  = panneau d'affichage de l'historique.                *
+*                                                                             *
+*  Description : Restaure l'élément d'évolution suivant.                      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void do_history_redo(GtkButton *button, GHistoryPanel *panel)
+{
+    GtkTreeSelection *selection;            /* Sélection courante          */
+    GtkTreeModel *model;                    /* Modèle de gestion de données*/
+    GtkTreeIter iter;                       /* Pointeur vers la ligne visée*/
+    GDbItem *item;                          /* Elément de collection       */
+    GDbClient *client;                      /* Connexion vers la base      */
+
+    selection = gtk_tree_view_get_selection(panel->treeview);
+
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+    {
+        gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1);
+
+        client = g_loaded_binary_get_db_client(panel->binary);
+        g_db_client_set_last_active(client, g_db_item_get_timestamp(item));
+        g_object_unref(G_OBJECT(client));
+
+        g_object_unref(G_OBJECT(item));
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton d'édition de l'historique d'évolution.       *
+*                panel  = panneau d'affichage de l'historique.                *
+*                                                                             *
+*  Description : Effectue un nettoyage de l'historique.                       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void do_history_clean(GtkButton *button, GHistoryPanel *panel)
+{
+
+
+
+
 
 }
-- 
cgit v0.11.2-87-g4458