From acc7b5f33e93bae3bf43e8f029976b7f74260b52 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 9 Apr 2017 09:52:59 +0200
Subject: Provided a tree panel to inspect binary content.

---
 ChangeLog                    |  31 ++
 src/glibext/gbinportion.c    |  55 ++++
 src/glibext/gbinportion.h    |   6 +
 src/gtkext/Makefile.am       |   3 +-
 src/gtkext/tmgt.c            | 204 ++++++++++++
 src/gtkext/tmgt.h            |  45 +++
 src/gui/core/panels.c        |   4 +
 src/gui/panels/Makefile.am   |   2 +
 src/gui/panels/bintree.c     | 724 +++++++++++++++++++++++++++++++++++++++++++
 src/gui/panels/bintree.h     |  62 ++++
 src/gui/panels/bintree.ui    | 191 ++++++++++++
 src/gui/panels/gresource.xml |   3 +
 src/gui/panels/symbols.c     | 144 +--------
 13 files changed, 1338 insertions(+), 136 deletions(-)
 create mode 100644 src/gtkext/tmgt.c
 create mode 100644 src/gtkext/tmgt.h
 create mode 100644 src/gui/panels/bintree.c
 create mode 100644 src/gui/panels/bintree.h
 create mode 100644 src/gui/panels/bintree.ui

diff --git a/ChangeLog b/ChangeLog
index b141036..5e243a0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+17-04-09  Cyrille Bagard <nocbos@gmail.com>
+
+	* src/glibext/gbinportion.c:
+	* src/glibext/gbinportion.h:
+	Enable icon for portions.
+
+	* src/gtkext/Makefile.am:
+	Add the 'tmgt.[ch]' files to libgtkext_la_SOURCES.
+
+	* src/gtkext/tmgt.c:
+	* src/gtkext/tmgt.h:
+	New entries: provide common helpers for managing treeviews.
+
+	* src/gui/core/panels.c:
+	Register the new binary tree panel.
+
+	* src/gui/panels/Makefile.am:
+	Add the 'bintree.ui' file to UI_FILES and the 'bintree.[ch]' files
+	to libguipanels_la_SOURCES.
+
+	* src/gui/panels/bintree.c:
+	* src/gui/panels/bintree.h:
+	* src/gui/panels/bintree.ui:
+	New entries: provide a tree panel to inspect binary content.
+
+	* src/gui/panels/gresource.xml:
+	Register the new UI and its icons.
+
+	* src/gui/panels/symbols.c:
+	Update code.
+
 17-03-31  Cyrille Bagard <nocbos@gmail.com>
 
 	* plugins/readelf/Makefile.am:
diff --git a/src/glibext/gbinportion.c b/src/glibext/gbinportion.c
index caaec43..b94b0f6 100644
--- a/src/glibext/gbinportion.c
+++ b/src/glibext/gbinportion.c
@@ -51,6 +51,8 @@ struct _GBinPortion
 
     char *code;                             /* Code de la couleur de fond  */
 
+    cairo_surface_t *icon;                  /* Image de représentation     */
+
     char *desc;                             /* Désignation humaine         */
     char **text;                            /* Lignes brutes à représenter */
     size_t lcount;                          /* Quantité de ces lignes      */
@@ -239,6 +241,9 @@ static void g_binary_portion_finalize(GBinPortion *portion)
 
     free(portion->code);
 
+    if (portion->icon != NULL)
+        cairo_surface_destroy(portion->icon);
+
     if (portion->desc != NULL)
         free(portion->desc);
 
@@ -314,6 +319,56 @@ int g_binary_portion_compare(const GBinPortion **a, const GBinPortion **b)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : portion = description de partie à mettre à jour.             *
+*                icon    = image miniature de représentation à associer.      *
+*                                                                             *
+*  Description : Attribue à la portion une éventuelle image de représentation.*
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_binary_portion_set_icon(GBinPortion *portion, cairo_surface_t *icon)
+{
+    if (icon != NULL)
+        portion->icon = cairo_surface_reference(icon);
+
+    else
+        portion->icon = NULL;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : portion = description de partie à consulter.                 *
+*                                                                             *
+*  Description : Fournit une éventuelle image de représentation de portion.   *
+*                                                                             *
+*  Retour      : Image miniature de représentation associée.                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+cairo_surface_t *g_binary_portion_get_icon(const GBinPortion *portion)
+{
+    cairo_surface_t *result;
+
+    result = portion->icon;
+
+    if (result != NULL)
+        cairo_surface_reference(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : portion = description de partie à mettre à jour.             *
 *                desc    = nom à donner à la partie.                          *
 *                                                                             *
 *  Description : Attribue une description humaine à une partie de code.       *
diff --git a/src/glibext/gbinportion.h b/src/glibext/gbinportion.h
index 27e7dc5..9f22e3d 100644
--- a/src/glibext/gbinportion.h
+++ b/src/glibext/gbinportion.h
@@ -84,6 +84,12 @@ GBinPortion *g_binary_portion_new(const char *, const vmpa2t *, phys_t);
 /* Etablit la comparaison ascendante entre deux portions. */
 int g_binary_portion_compare(const GBinPortion **, const GBinPortion **);
 
+/* Attribue à la portion une éventuelle image de représentation. */
+void g_binary_portion_set_icon(GBinPortion *, cairo_surface_t *);
+
+/* Fournit une éventuelle image de représentation de portion. */
+cairo_surface_t *g_binary_portion_get_icon(const GBinPortion *);
+
 /* Attribue une description humaine à une partie de code. */
 void g_binary_portion_set_desc(GBinPortion *, const char *);
 
diff --git a/src/gtkext/Makefile.am b/src/gtkext/Makefile.am
index d874ac1..07fe3a9 100644
--- a/src/gtkext/Makefile.am
+++ b/src/gtkext/Makefile.am
@@ -15,7 +15,8 @@ libgtkext_la_SOURCES =						\
 	gtkdockstation.h gtkdockstation.c		\
 	gtkgraphdisplay.h gtkgraphdisplay.c		\
 	gtkstatusstack.h gtkstatusstack.c		\
-	support.h support.c
+	support.h support.c						\
+	tmgt.h tmgt.c
 
 libgtkext_la_LIBADD = 					\
 	graph/libgtkextgraph.la
diff --git a/src/gtkext/tmgt.c b/src/gtkext/tmgt.c
new file mode 100644
index 0000000..ce6f9a8
--- /dev/null
+++ b/src/gtkext/tmgt.c
@@ -0,0 +1,204 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tmgt.c - compléments utiles à la mise en place d'arborescences
+ *
+ * Copyright (C) 2017 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "tmgt.h"
+
+
+#include <malloc.h>
+#include <string.h>
+
+
+#include "../common/extstr.h"
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : entry  = zone de texte avec un nouveau filtre d'affichage.   *
+*                filter = expression régulière de filtrage à constituer. [OUT]*
+*                                                                             *
+*  Description : Met à jour un filtre selon un contenu recherché.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void update_regex_on_search_entry_changed(GtkSearchEntry *entry, regex_t **filter)
+{
+    const gchar *text;                      /* Texte de l'utilisateur      */
+    int ret;                                /* Bilan de mise en place      */
+    GdkRGBA error;                          /* Couleur d'erreur            */
+
+    if (*filter != NULL)
+    {
+        regfree(*filter);
+        free(*filter);
+        *filter = NULL;
+    }
+
+    text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+    if (strlen(text) > 0)
+    {
+        *filter = (regex_t *)calloc(1, sizeof(regex_t));
+        ret = regcomp(*filter, text, REG_EXTENDED | REG_ICASE);
+
+        if (ret != 0)
+        {
+            free(*filter);
+            *filter = NULL;
+
+            error.red = 1.0;
+            error.green = 0.0;
+            error.blue = 0.0;
+            error.alpha = 1.0;
+            gtk_widget_override_color(GTK_WIDGET(entry), GTK_STATE_NORMAL, &error);
+
+            return;
+
+        }
+
+    }
+
+    gtk_widget_override_color(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : filter  = expression régulière servant de filtre.            *
+*                content = contenu textuel à analyser.                        *
+*                match   = zone pointant vers une correspondance. [OUT]       *
+*                                                                             *
+*  Description : Détermine si un contenu correspond à un filtre donné.        *
+*                                                                             *
+*  Retour      : true si le contenu est qualifié pour un affichage, ou false. *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool is_content_matching(const regex_t *filter, const char *content, regmatch_t *match)
+{
+    bool result;                            /* Bilan à retourner           */
+    int ret;                                /* Bilan du filtrage           */
+
+    memset( match, 0, sizeof(regmatch_t));
+
+    if (filter == NULL)
+        result = true;
+
+    else
+    {
+        ret = regexec(filter, content, 1, match, 0);
+        result = (ret != REG_NOMATCH);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : raw   = bribe de texte à traiter.                            *
+*                match = portion de texte à mettre en évidence.               *
+*                start = position du texte brute dans l'étiquette complète.   *
+*                                                                             *
+*  Description : Met en évidence le texte recherché en cas de correspondance. *
+*                                                                             *
+*  Retour      : Texte final destiné à être inséré, à libérer après usage.    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *build_highlighted_name(const char *raw, const regmatch_t *match, size_t start)
+{
+    char *result;                           /* Construction à retourner    */
+    size_t len;                             /* Taille du texte d'entrée    */
+    regmatch_t selection;                   /* Sélection relative au texte */
+    char *valid;                            /* Retouche partielle          */
+
+    len = strlen(raw);
+
+    /* Si aucune sélection ou texte hors champ... */
+
+    if ((match->rm_eo - match->rm_so) == 0 || (start + len) <= match->rm_so || match->rm_eo < start)
+    {
+        result = strdup(raw);
+        result = strrpl(result, "<", "&lt;");
+    }
+
+    /* Sinon, il y a forcément correspondance quelque part ! */
+
+    else
+    {
+        /* Adaptations */
+
+        if (match->rm_so < start)
+            selection.rm_so = 0;
+        else
+            selection.rm_so = match->rm_so - start;
+
+        selection.rm_eo = match->rm_eo - start;
+
+        if (selection.rm_eo > len)
+            selection.rm_eo = len;
+
+        /* Impressions */
+
+        if (selection.rm_so > 0)
+        {
+            result = strndup(raw, selection.rm_so);
+            result = strrpl(result, "<", "&lt;");
+        }
+        else
+            result = NULL;
+
+        result = stradd(result, "<b>");
+
+        valid = strndup(&raw[selection.rm_so], selection.rm_eo - selection.rm_so);
+        valid = strrpl(valid, "<", "&lt;");
+
+        result = stradd(result, valid);
+
+        free(valid);
+
+        result = stradd(result, "</b>");
+
+        valid = strdup(&raw[selection.rm_eo]);
+        valid = strrpl(valid, "<", "&lt;");
+
+        result = stradd(result, valid);
+
+        free(valid);
+
+    }
+
+    return result;
+
+}
diff --git a/src/gtkext/tmgt.h b/src/gtkext/tmgt.h
new file mode 100644
index 0000000..eeeb848
--- /dev/null
+++ b/src/gtkext/tmgt.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * tmgt.h - prototypes pour des compléments utiles à la mise en place d'arborescences
+ *
+ * Copyright (C) 2017 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _COMMON_TMGT_H
+#define _COMMON_TMGT_H
+
+
+#include <regex.h>
+#include <stdbool.h>
+#include <gtk/gtk.h>
+
+
+
+/* Met à jour un filtre selon un contenu recherché. */
+void update_regex_on_search_entry_changed(GtkSearchEntry *, regex_t **);
+
+/* Détermine si un contenu correspond à un filtre donné. */
+bool is_content_matching(const regex_t *, const char *, regmatch_t *);
+
+/* Met en évidence le texte recherché en cas de correspondance. */
+char *build_highlighted_name(const char *, const regmatch_t *, size_t);
+
+
+
+#endif  /* _COMMON_TMGT_H */
diff --git a/src/gui/core/panels.c b/src/gui/core/panels.c
index bd85be9..3d1a512 100644
--- a/src/gui/core/panels.c
+++ b/src/gui/core/panels.c
@@ -25,6 +25,7 @@
 #include "panels.h"
 
 
+#include "../panels/bintree.h"
 #include "../panels/bookmarks.h"
 #include "../panels/glance.h"
 #include "../panels/history.h"
@@ -87,6 +88,9 @@ void load_main_panels(GObject *ref)
     item = g_bookmarks_panel_new();
     register_panel_item(item, ref, config);
 
+    item = g_bintree_panel_new();
+    register_panel_item(item, ref, config);
+
 }
 
 
diff --git a/src/gui/panels/Makefile.am b/src/gui/panels/Makefile.am
index bd4572d..7dc1cc5 100644
--- a/src/gui/panels/Makefile.am
+++ b/src/gui/panels/Makefile.am
@@ -4,9 +4,11 @@ BUILT_SOURCES = resources.h resources.c
 noinst_LTLIBRARIES  = libguipanels.la
 
 UI_FILES =								\
+	bintree.ui							\
 	welcome.ui
 
 libguipanels_la_SOURCES =				\
+	bintree.h bintree.c					\
 	bookmarks.h bookmarks.c				\
 	glance.h glance.c					\
 	history.h history.c					\
diff --git a/src/gui/panels/bintree.c b/src/gui/panels/bintree.c
new file mode 100644
index 0000000..9963f94
--- /dev/null
+++ b/src/gui/panels/bintree.c
@@ -0,0 +1,724 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bintree.c - panneau d'accueil par défaut
+ *
+ * Copyright (C) 2012-2017 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "bintree.h"
+
+
+#include <malloc.h>
+#include <regex.h>
+
+
+#include <i18n.h>
+
+
+#include "panel-int.h"
+#include "../../gtkext/tmgt.h"
+
+
+
+/* Origine de la dernière ouverture/fermeture reproductible */
+typedef enum _UserActionType
+{
+    UAT_COLLAPSE,                           /* Fermeture totale            */
+    UAT_EXPAND,                             /* Ouverture totale            */
+    UAT_DEPTH,                              /* Descente contrôlée          */
+
+} UserActionType;
+
+
+/* Panneau de présentation des portions (instance) */
+struct _GBintreePanel
+{
+    GPanelItem parent;                      /* A laisser en premier        */
+
+    GtkBuilder *builder;                    /* Constructeur utilisé        */
+
+    GLoadedBinary *binary;                  /* Binaire représenté          */
+    regex_t *filter;                        /* Filtre appliqué ou NULL     */
+
+    UserActionType last;                    /* Dernière action             */
+
+    GtkTreeIter *top;                       /* Transfert de racine         */
+
+};
+
+/* Panneau de présentation des portions (classe) */
+struct _GBintreePanelClass
+{
+    GPanelItemClass parent;                 /* A laisser en premier        */
+
+};
+
+
+/* Colonnes de la liste des messages */
+typedef enum _BinaryTreeColumn
+{
+    BTC_ICON,                               /* Image de représentation     */
+    BTC_CAPTION,                            /* Désignation de l'élément    */
+    BTC_START,                              /* Position de départ          */
+    BTC_END,                                /* Position d'arrivée          */
+    BTC_RIGHTS,                             /* Droits d'accès              */
+
+    BTC_MATCHED,                            /* Correspondance établie ?    */
+    BTC_PORTION                             /* Elément interne représenté  */
+
+} BinaryTreeColumn;
+
+
+/* Initialise la classe des panneaux d'affichage des portions. */
+static void g_bintree_panel_class_init(GBintreePanelClass *);
+
+/* Initialise une instance de panneau d'affichage des portions. */
+static void g_bintree_panel_init(GBintreePanel *);
+
+/* Supprime toutes les références externes. */
+static void g_bintree_panel_dispose(GBintreePanel *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_bintree_panel_finalize(GBintreePanel *);
+
+/* Parcourt un ensemble de portions. */
+static bool populate_tree_with_portion(GBinPortion *, GBinPortion *, BinaryPortionVisit, GBintreePanel *);
+
+/* Réagit à un changement d'affichage principal de contenu. */
+static void update_panel_with_binary_portions(GBintreePanel *, GLoadedBinary *);
+
+/* Modifie la profondeur affichée des portions présentes. */
+static void on_depth_spin_value_changed(GtkSpinButton *, GtkTreeView *);
+
+/* Prend note du changement de filtre sur les portions. */
+static void on_search_entry_changed(GtkSearchEntry *, GBintreePanel *);
+
+/* Parcourt un arbre en place et retire les branches filtrées. */
+static void apply_filter_on_portions(GtkTreeStore *);
+
+/* Réagit au changement de sélection des portions. */
+static void on_bintree_selection_changed(GtkTreeSelection *, GBintreePanel *);
+
+
+
+/* Indique le type défini pour un panneau d'affichage des portions. */
+G_DEFINE_TYPE(GBintreePanel, g_bintree_panel, G_TYPE_PANEL_ITEM);
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : klass = classe à initialiser.                                *
+*                                                                             *
+*  Description : Initialise la classe des panneaux d'affichage des portions.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bintree_panel_class_init(GBintreePanelClass *klass)
+{
+    GObjectClass *object;                   /* Autre version de la classe  */
+    GEditorItemClass *editem;               /* Encore une autre vision...  */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_bintree_panel_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_bintree_panel_finalize;
+
+    editem = G_EDITOR_ITEM_CLASS(klass);
+
+    editem->update_binary = (update_item_binary_fc)update_panel_with_binary_portions;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = instance à initialiser.                              *
+*                                                                             *
+*  Description : Initialise une instance de panneau d'affichage des portions. *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bintree_panel_init(GBintreePanel *panel)
+{
+    GEditorItem *base;                      /* Version basique d'instance  */
+    GPanelItem *pitem;                      /* Version parente du panneau  */
+    GtkTreeView *treeview;                  /* Affichage de la liste       */
+    GtkCellRenderer *renderer;              /* Moteur de rendu de colonne  */
+    GtkTreeViewColumn *column;              /* Colonne de la liste         */
+
+    /* Eléments de base */
+
+    base = G_EDITOR_ITEM(panel);
+
+    base->name = PANEL_BINTREE_ID;
+
+    pitem = G_PANEL_ITEM(panel);
+
+    pitem->personality = PIP_SINGLETON;
+    pitem->lname = _("Binary tree");
+    pitem->dock_at_startup = true;
+    pitem->path = strdup("eN");
+
+    /* Compléments propres */
+
+    panel->binary = NULL;
+    panel->filter = NULL;
+
+    panel->last = UAT_EXPAND;
+
+    /* Représentation graphique */
+
+    panel->builder = gtk_builder_new_from_resource("/org/chrysalide/gui/panels/bintree.ui");
+
+    base->widget = GTK_WIDGET(gtk_builder_get_object(panel->builder, "box"));
+    g_object_ref(G_OBJECT(base->widget));
+    gtk_widget_unparent(base->widget);
+
+    /* Liste des projets récents */
+
+    treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview"));
+
+    column = gtk_tree_view_column_new();
+    gtk_tree_view_append_column(treeview, column);
+    gtk_tree_view_set_expander_column(treeview, column);
+
+    renderer = gtk_cell_renderer_pixbuf_new();
+    gtk_tree_view_column_pack_start(column, renderer, FALSE);
+    gtk_tree_view_column_add_attribute(column, renderer, "surface", BTC_ICON);
+
+    renderer = gtk_cell_renderer_text_new();
+    gtk_tree_view_column_pack_start(column, renderer, TRUE);
+    gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_CAPTION);
+
+    column = gtk_tree_view_column_new();
+    gtk_tree_view_append_column(treeview, column);
+
+    renderer = gtk_cell_renderer_text_new();
+    g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+    gtk_tree_view_column_pack_start(column, renderer, TRUE);
+    gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_START);
+
+    column = gtk_tree_view_column_new();
+    gtk_tree_view_append_column(treeview, column);
+
+    renderer = gtk_cell_renderer_text_new();
+    g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+    gtk_tree_view_column_pack_start(column, renderer, TRUE);
+    gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_END);
+
+    column = gtk_tree_view_column_new();
+    gtk_tree_view_append_column(treeview, column);
+
+    renderer = gtk_cell_renderer_text_new();
+    gtk_tree_view_column_pack_start(column, renderer, TRUE);
+    gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_RIGHTS);
+
+    /* Connexion des signaux */
+
+    gtk_builder_add_callback_symbols(panel->builder,
+                                     "gtk_tree_view_collapse_all", G_CALLBACK(gtk_tree_view_collapse_all),
+                                     "gtk_tree_view_expand_all", G_CALLBACK(gtk_tree_view_expand_all),
+                                     "on_depth_spin_value_changed", G_CALLBACK(on_depth_spin_value_changed),
+                                     "on_search_entry_changed", G_CALLBACK(on_search_entry_changed),
+                                     "on_bintree_selection_changed", G_CALLBACK(on_bintree_selection_changed),
+                                     NULL);
+
+    gtk_builder_connect_signals(panel->builder, panel);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bintree_panel_dispose(GBintreePanel *panel)
+{
+    g_object_unref(G_OBJECT(panel->builder));
+
+    if (panel->binary != NULL)
+        g_object_unref(G_OBJECT(panel->binary));
+
+    G_OBJECT_CLASS(g_bintree_panel_parent_class)->dispose(G_OBJECT(panel));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_bintree_panel_finalize(GBintreePanel *panel)
+{
+    if (panel->filter != NULL)
+    {
+        regfree(panel->filter);
+        free(panel->filter);
+    }
+
+    G_OBJECT_CLASS(g_bintree_panel_parent_class)->finalize(G_OBJECT(panel));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Crée un panneau présentant l'arborescence des portions.      *
+*                                                                             *
+*  Retour      : Adresse de la structure mise en place.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GPanelItem *g_bintree_panel_new(void)
+{
+    GBintreePanel *result;                  /* Structure à retourner       */
+
+    result = g_object_new(G_TYPE_BINTREE_PANEL, NULL);
+
+    return G_PANEL_ITEM(result);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel  = portion de binaire à traiter.                       *
+*                parent = portion parent de la portion visitée.               *
+*                visit  = indication sur le sens de la visite.                *
+*                panel  = lien vers toutes les autres informations utiles.    *
+*                                                                             *
+*  Description : Parcourt un ensemble de portions.                            *
+*                                                                             *
+*  Retour      : true pour continuer la visite.                               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool populate_tree_with_portion(GBinPortion *portion, GBinPortion *parent, BinaryPortionVisit visit, GBintreePanel *panel)
+{
+    const char *desc;               /* Description d'origine       */
+    bool fmatched;                  /* Correspondance rencontrée ? */
+    regmatch_t match;               /* Position d'un filtre        */
+    char *node_caption;             /* Etiquette de nouveau noeud  */
+    const mrange_t *range;          /* Espace de portion à traiter */
+    VMPA_BUFFER(offset);            /* Décalage physique           */
+    char *node_start;               /* Position pour nouveau noeud */
+    vmpa2t end;                     /* Zone de construction temp.  */
+    char *node_end;                 /* Bordure pour nouveau noeud  */
+    PortionAccessRights rights;     /* Droits d'accès à analyser   */
+    char hrights[4];                /* Version humainement lisible */
+    char *node_rights;              /* Droits pour nouveau noeud   */
+    cairo_surface_t *icon;          /* Miniature de décoration     */
+    GtkTreeStore *store;            /* Modèle de gestion           */
+    GtkTreeIter iter;               /* Point d'insertion           */
+    GtkTreeIter *save;              /* Sauvegarde d'une position   */
+
+    if (parent == NULL)
+        return true;
+
+    /* Insertion de la portion courante */
+
+    if (visit == BPV_ENTER || visit == BPV_SHOW)
+    {
+        /* Etiquette */
+
+        desc = g_binary_portion_get_desc(portion);
+
+        fmatched = is_content_matching(panel->filter, desc, &match);
+        node_caption = build_highlighted_name(desc, &match, 0);
+
+        /* Point de départ */
+
+        range = g_binary_portion_get_range(portion);
+
+        vmpa2_phys_to_string(get_mrange_addr(range), MDS_UNDEFINED, offset, NULL);
+
+        fmatched |= is_content_matching(panel->filter, offset, &match);
+        node_start = build_highlighted_name(offset, &match, 0);
+
+        /* Point d'arrivée */
+
+        compute_mrange_end_addr(range, &end);
+
+        vmpa2_phys_to_string(&end, MDS_UNDEFINED, offset, NULL);
+
+        fmatched |= is_content_matching(panel->filter, offset, &match);
+        node_end = build_highlighted_name(offset, &match, 0);
+
+        /* Droits nominaux */
+
+        rights = g_binary_portion_get_rights(portion);
+
+        hrights[0] = (rights & PAC_READ ? 'r' : '-');
+        hrights[1] = (rights & PAC_WRITE ? 'w' : '-');
+        hrights[2] = (rights & PAC_EXEC ? 'x' : '-');
+        hrights[3] = '\0';
+
+        fmatched |= is_content_matching(panel->filter, hrights, &match);
+        node_rights = build_highlighted_name(hrights, &match, 0);
+
+        /* Intégration */
+
+        icon = NULL;
+
+        store = GTK_TREE_STORE(gtk_builder_get_object(panel->builder, "store"));
+
+        gtk_tree_store_append(store, &iter, panel->top);
+
+        gtk_tree_store_set(store, &iter,
+                           BTC_ICON, icon,
+                           BTC_CAPTION, node_caption,
+                           BTC_START, node_start,
+                           BTC_END, node_end,
+                           BTC_RIGHTS, node_rights,
+                           BTC_MATCHED, fmatched,
+                           BTC_PORTION, portion,
+                           -1);
+
+        free(node_caption);
+        free(node_start);
+        free(node_end);
+        free(node_rights);
+
+        if (icon != NULL)
+            cairo_surface_destroy(icon);
+
+    }
+
+    /* Définition de la hiérarchie */
+
+    if (visit == BPV_ENTER)
+    {
+        save = gtk_tree_iter_copy(panel->top);
+
+        g_object_set_data_full(G_OBJECT(portion), "_save", save, (GDestroyNotify)gtk_tree_iter_free);
+
+        *panel->top = iter;
+
+    }
+
+    else if (visit == BPV_EXIT)
+    {
+        save = g_object_get_data(G_OBJECT(portion), "_save");
+
+        *panel->top = *save;
+
+        g_object_set_data(G_OBJECT(portion), "_save", NULL);
+
+    }
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel  = panneau à mettre à jour.                            *
+*                binary = nouvelle instance de binaire analysé.               *
+*                                                                             *
+*  Description : Réagit à un changement d'affichage principal de contenu.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void update_panel_with_binary_portions(GBintreePanel *panel, GLoadedBinary *binary)
+{
+    GtkTreeStore *store;                    /* Modèle de gestion           */
+    GExeFormat *format;                     /* Format du binaire           */
+    GBinPortion *portions;                  /* Couche première de portions */
+    GtkTreeIter top;                        /* Racine de l'arborescence    */
+    gint max_depth;                         /* Profondeur maximale         */
+    GtkSpinButton *depth_spin;              /* Bouton de variation         */
+    GtkTreeView *treeview;                  /* Arborescence constituée     */
+
+    /* Réinitialisation */
+
+    if (panel->binary != NULL)
+        g_object_unref(G_OBJECT(panel->binary));
+
+    panel->binary = binary;
+
+    store = GTK_TREE_STORE(gtk_builder_get_object(panel->builder, "store"));
+
+    gtk_tree_store_clear(store);
+
+    /* Chargement */
+
+    if (binary != NULL)
+    {
+        g_object_ref(G_OBJECT(binary));
+
+        format = g_loaded_binary_get_format(binary);
+
+        portions = g_exe_format_get_portions(format);
+
+        gtk_tree_store_append(store, &top, NULL);
+
+        gtk_tree_store_set(store, &top,
+                           BTC_ICON, NULL,
+                           BTC_CAPTION, g_loaded_binary_get_name(binary, false),
+                           -1);
+
+        panel->top = &top;
+        g_binary_portion_visit(portions, (visit_portion_fc)populate_tree_with_portion, panel);
+
+        g_object_unref(G_OBJECT(portions));
+
+        g_object_unref(G_OBJECT(format));
+
+    }
+
+    /* Détermination de la profondeur maximale */
+
+    gboolean compute_max_depth(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gint *max)
+    {
+        gint depth;                         /* Profondeur du point courant */
+
+        depth = gtk_tree_store_iter_depth(GTK_TREE_STORE(model), iter);
+
+        if (depth > *max)
+            *max = depth;
+
+        return FALSE;
+
+    }
+
+    max_depth = 0;
+
+    gtk_tree_model_foreach(GTK_TREE_MODEL(store), (GtkTreeModelForeachFunc)compute_max_depth, &max_depth);
+
+    depth_spin = GTK_SPIN_BUTTON(gtk_builder_get_object(panel->builder, "depth_spin"));
+
+    gtk_spin_button_set_range(depth_spin, 0, max_depth);
+
+    /* Restauration au mieux de l'affichage */
+
+    treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview"));
+
+    switch (panel->last)
+    {
+        case UAT_COLLAPSE:
+            gtk_tree_view_collapse_all(treeview);
+            break;
+
+        case UAT_EXPAND:
+            gtk_tree_view_expand_all(treeview);
+            break;
+
+        case UAT_DEPTH:
+            on_depth_spin_value_changed(depth_spin, treeview);
+            break;
+
+    }
+
+    if (panel->filter != NULL)
+        apply_filter_on_portions(store);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button   = bouton de réglage de l'affichage.                 *
+*                treeview = arborescence dont l'affichage est à moduler.      *
+*                                                                             *
+*  Description : Modifie la profondeur affichée des portions présentes.       *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_depth_spin_value_changed(GtkSpinButton *button, GtkTreeView *treeview)
+{
+    gint max_depth;                         /* Profondeur maximale         */
+    GtkTreeModel *model;                    /* Modèle de gestion           */
+
+    max_depth = gtk_spin_button_get_value_as_int(button);
+
+    gboolean apply_max_depth(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer unused)
+    {
+        gint depth;                         /* Profondeur du point courant */
+
+        depth = gtk_tree_store_iter_depth(GTK_TREE_STORE(model), iter);
+
+        if (depth < max_depth)
+            gtk_tree_view_expand_to_path(treeview, path);
+
+        return FALSE;
+
+    }
+
+    gtk_tree_view_collapse_all(treeview);
+
+    model = gtk_tree_view_get_model(treeview);
+
+    gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)apply_max_depth, NULL);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : entry = zone de texte avec un nouveau filtre d'affichage.    *
+*                panel = panneau contenant les informations globales.         *
+*                                                                             *
+*  Description : Prend note du changement de filtre sur les portions.         *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_search_entry_changed(GtkSearchEntry *entry, GBintreePanel *panel)
+{
+    update_regex_on_search_entry_changed(entry, &panel->filter);
+
+    if (panel->binary != NULL)
+    {
+        g_object_ref(G_OBJECT(panel->binary));
+        update_panel_with_binary_portions(panel, panel->binary);
+        g_object_unref(G_OBJECT(panel->binary));
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : store = gestionnaire de contenu d'une arborescence donnée.   *
+*                                                                             *
+*  Description : Parcourt un arbre en place et retire les branches filtrées.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void apply_filter_on_portions(GtkTreeStore *store)
+{
+    GtkTreeIter root;                       /* Racine de l'arboresence     */
+
+    void check_portion_iter(GtkTreeIter *iter)
+    {
+        GtkTreeModel *model;                /* Version alternative         */
+        gint children_count;                /* Nombre d'enfants présents   */
+        gint i;                             /* Boucle de parcours          */
+        GtkTreeIter child;                  /* Pointeur vers la descendance*/
+        gboolean fmatched;                  /* Correspondance immédiate ?  */
+
+        model = GTK_TREE_MODEL(store);
+
+        children_count = gtk_tree_model_iter_n_children(model, iter);
+
+        for (i = children_count; i > 0; i--)
+            if (gtk_tree_model_iter_nth_child(model, &child, iter, i - 1))
+                check_portion_iter(&child);
+
+        children_count = gtk_tree_model_iter_n_children(model, iter);
+
+        gtk_tree_model_get(model, iter, BTC_MATCHED, &fmatched, -1);
+
+        if (!fmatched && children_count == 0)
+            gtk_tree_store_remove(store, iter);
+
+    }
+
+    if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &root))
+        check_portion_iter(&root);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : selection = sélection modifiée.                              *
+*                panel     = structure contenant les informations maîtresses. *
+*                                                                             *
+*  Description : Réagit au changement de sélection des portions.              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_bintree_selection_changed(GtkTreeSelection *selection, GBintreePanel *panel)
+{
+    GtkTreeIter iter;                       /* Point de sélection          */
+    GtkTreeModel *model;                    /* Modèle de gestion           */
+    GBinPortion *portion;                   /* Portion à traiter           */
+    const mrange_t *range;                  /* Couverture dudit symbole    */
+    GtkDisplayPanel *display;               /* Afficheur effectif de code  */
+
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+    {
+        gtk_tree_model_get(model, &iter, BTC_PORTION, &portion, -1);
+
+        if (portion != NULL)
+        {
+            range = g_binary_portion_get_range(portion);
+
+            display = g_editor_item_get_current_view(G_EDITOR_ITEM(panel));
+            gtk_display_panel_request_move(display, get_mrange_addr(range));
+
+            g_object_unref(G_OBJECT(portion));
+
+        }
+
+    }
+
+}
diff --git a/src/gui/panels/bintree.h b/src/gui/panels/bintree.h
new file mode 100644
index 0000000..8168481
--- /dev/null
+++ b/src/gui/panels/bintree.h
@@ -0,0 +1,62 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bintree.h - prototypes pour le panneau d'accueil par défaut
+ *
+ * Copyright (C) 2012-2017 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _GUI_PANELS_BINTREE_H
+#define _GUI_PANELS_BINTREE_H
+
+
+#include <i18n.h>
+
+
+#include "panel.h"
+
+
+
+#define PANEL_BINTREE_ID _("Bintree")
+
+
+#define G_TYPE_BINTREE_PANEL               g_bintree_panel_get_type()
+#define G_BINTREE_PANEL(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_bintree_panel_get_type(), GBintreePanel))
+#define G_IS_BINTREE_PANEL(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_bintree_panel_get_type()))
+#define G_BINTREE_PANEL_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BINTREE_PANEL, GBintreePanelClass))
+#define G_IS_BINTREE_PANEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BINTREE_PANEL))
+#define G_BINTREE_PANEL_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BINTREE_PANEL, GBintreePanelClass))
+
+
+/* Panneau de présentation des portions (instance) */
+typedef struct _GBintreePanel GBintreePanel;
+
+/* Panneau de présentation des portions (classe) */
+typedef struct _GBintreePanelClass GBintreePanelClass;
+
+
+/* Indique le type défini pour un panneau d'affichage des portions. */
+GType g_bintree_panel_get_type(void);
+
+/* Crée un panneau présentant l'arborescence des portions. */
+GPanelItem *g_bintree_panel_new(void);
+
+
+
+#endif  /* _GUI_PANELS_BINTREE_H */
diff --git a/src/gui/panels/bintree.ui b/src/gui/panels/bintree.ui
new file mode 100644
index 0000000..2348e8d
--- /dev/null
+++ b/src/gui/panels/bintree.ui
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">2</property>
+  </object>
+  <object class="GtkImage" id="collapse_img">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="pixbuf">../../../pixmaps/tbutton_collapse.png</property>
+  </object>
+  <object class="GtkImage" id="expand_img">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="pixbuf">../../../pixmaps/tbutton_expand.png</property>
+  </object>
+  <object class="GtkTreeStore" id="store">
+    <columns>
+      <!-- column-name icon -->
+      <column type="CairoSurface"/>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+      <!-- column-name start -->
+      <column type="gchararray"/>
+      <!-- column-name end -->
+      <column type="gchararray"/>
+      <!-- column-name rights -->
+      <column type="gchararray"/>
+      <!-- column-name matched -->
+      <column type="gboolean"/>
+      <!-- column-name portion -->
+      <column type="GObject"/>
+    </columns>
+  </object>
+  <object class="GtkOffscreenWindow" id="offscreenwindow1">
+    <property name="can_focus">False</property>
+    <property name="margin_left">8</property>
+    <property name="margin_right">8</property>
+    <property name="margin_top">8</property>
+    <property name="margin_bottom">8</property>
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">8</property>
+        <child>
+          <object class="GtkToolbar" id="toolbar1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkToolButton" id="toolbutton1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="use_underline">True</property>
+                <property name="label_widget">collapse_img</property>
+                <signal name="clicked" handler="gtk_tree_view_collapse_all" object="treeview" swapped="yes"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolButton" id="toolbutton2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="use_underline">True</property>
+                <property name="label_widget">expand_img</property>
+                <signal name="clicked" handler="gtk_tree_view_expand_all" object="treeview" swapped="yes"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSeparatorToolItem" id="toolbutton3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem" id="toolbutton4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="label1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_right">8</property>
+                    <property name="label" translatable="yes">Depth:</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem" id="toolbutton5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkSpinButton" id="depth_spin">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="width_chars">2</property>
+                    <property name="progress_fraction">1</property>
+                    <property name="progress_pulse_step">1</property>
+                    <property name="adjustment">adjustment1</property>
+                    <property name="numeric">True</property>
+                    <signal name="value-changed" handler="on_depth_spin_value_changed" object="treeview" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSeparatorToolItem" id="toolbutton6">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem" id="toolbutton7">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkSearchEntry" id="search">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="primary_icon_name">edit-find-symbolic</property>
+                    <property name="primary_icon_activatable">False</property>
+                    <property name="primary_icon_sensitive">False</property>
+                    <property name="placeholder_text" translatable="yes">filter...</property>
+                    <signal name="search-changed" handler="on_search_entry_changed" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkTreeView" id="treeview">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="model">store</property>
+            <property name="headers_visible">False</property>
+            <child internal-child="selection">
+              <object class="GtkTreeSelection" id="treeview-selection1">
+                <signal name="changed" handler="on_bintree_selection_changed" swapped="no"/>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/gui/panels/gresource.xml b/src/gui/panels/gresource.xml
index 58d47e2..a14b8ae 100644
--- a/src/gui/panels/gresource.xml
+++ b/src/gui/panels/gresource.xml
@@ -1,6 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
     <gresource prefix="/org/chrysalide/gui/panels">
+        <file compressed="true">../../../pixmaps/tbutton_collapse.png</file>
+        <file compressed="true">../../../pixmaps/tbutton_expand.png</file>
+        <file compressed="true">bintree.ui</file>
         <file compressed="true">welcome.ui</file>
     </gresource>
 </gresources>
diff --git a/src/gui/panels/symbols.c b/src/gui/panels/symbols.c
index 8c95bf6..e73a847 100644
--- a/src/gui/panels/symbols.c
+++ b/src/gui/panels/symbols.c
@@ -37,10 +37,10 @@
 
 
 #include "panel-int.h"
-#include "../../common/extstr.h"
 #include "../../format/format.h"
 #include "../../gtkext/easygtk.h"
 #include "../../gtkext/support.h"
+#include "../../gtkext/tmgt.h"
 
 
 
@@ -153,10 +153,7 @@ static void on_symbols_filter_changed(GtkSearchEntry *, GSymbolsPanel *);
 static void do_filtering_on_symbols(GSymbolsPanel *);
 
 /* Détermine si un nom de symbole doit être filtré ou non. */
-static bool is_symbol_filtered(GSymbolsPanel *, const GBinSymbol *, regmatch_t *);
-
-/* Met en évidence le texte recherché en cas de correspondance. */
-static char *build_highlighted_name(const char *, const regmatch_t *, size_t);
+static bool is_symbol_matching(GSymbolsPanel *, const GBinSymbol *, regmatch_t *);
 
 
 
@@ -670,7 +667,7 @@ static void reload_symbols_for_new_list_view(GSymbolsPanel *panel)
 
     for (i = 0; i < sym_count; i++)
     {
-        if (is_symbol_filtered(panel, symbols[i], &match))
+        if (!is_symbol_matching(panel, symbols[i], &match))
             continue;
 
         switch (g_binary_symbol_get_target_type(symbols[i]))
@@ -872,7 +869,7 @@ static void reload_symbols_for_new_tree_view(GSymbolsPanel *panel)
 
     for (i = 0; i < sym_count; i++)
     {
-        if (is_symbol_filtered(panel, symbols[i], &match))
+        if (!is_symbol_matching(panel, symbols[i], &match))
             continue;
 
         if (find_parent_for_symbol(panel, symbols[i], &parent, &match, &last))
@@ -1017,42 +1014,7 @@ static gboolean show_all_classes_in_tree_view(GtkTreeModel *model, GtkTreePath *
 
 static void on_symbols_filter_changed(GtkSearchEntry *entry, GSymbolsPanel *panel)
 {
-    const gchar *text;                      /* Texte de l'utilisateur      */
-    int ret;                                /* Bilan de mise en place      */
-    GdkRGBA error;                          /* Couleur d'erreur            */
-
-    if (panel->filter != NULL)
-    {
-        regfree(panel->filter);
-        free(panel->filter);
-        panel->filter = NULL;
-    }
-
-    text = gtk_entry_get_text(GTK_ENTRY(entry));
-
-    if (strlen(text) > 0)
-    {
-        panel->filter = (regex_t *)calloc(1, sizeof(regex_t));
-        ret = regcomp(panel->filter, text, REG_EXTENDED | REG_ICASE);
-
-        if (ret != 0)
-        {
-            free(panel->filter);
-            panel->filter = NULL;
-
-            error.red = 1.0;
-            error.green = 0.0;
-            error.blue = 0.0;
-            error.alpha = 1.0;
-            gtk_widget_override_color(GTK_WIDGET(entry), GTK_STATE_NORMAL, &error);
-
-            return;
-
-        }
-
-    }
-
-    gtk_widget_override_color(GTK_WIDGET(entry), GTK_STATE_NORMAL, NULL);
+    update_regex_on_search_entry_changed(entry, &panel->filter);
 
     do_filtering_on_symbols(panel);
 
@@ -1094,106 +1056,18 @@ static void do_filtering_on_symbols(GSymbolsPanel *panel)
 *                                                                             *
 ******************************************************************************/
 
-static bool is_symbol_filtered(GSymbolsPanel *panel, const GBinSymbol *symbol, regmatch_t *match)
+static bool is_symbol_matching(GSymbolsPanel *panel, const GBinSymbol *symbol, regmatch_t *match)
 {
+    bool result;                            /* Bilan à retourner           */
     SymbolType type;                        /* Type associé au symbole     */
-    int ret;                                /* Bilan du filtrage           */
 
     type = g_binary_symbol_get_target_type(symbol);
 
     if (type != STP_ROUTINE && type != STP_ENTRY_POINT && type != STP_OBJECT)
-        return true;
-
-    memset(match, 0, sizeof(regmatch_t));
-
-    if (panel->filter == NULL)
-        return false;
-
-    ret = regexec(panel->filter, g_binary_symbol_get_label(symbol), 1, match, 0);
-    if (ret == REG_NOMATCH)
-        return true;
-
-    return false;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : raw   = bribe de texte à traiter.                            *
-*                match = portion de texte à mettre en évidence.               *
-*                start = position du texte brute dans l'étiquette complète.   *
-*                                                                             *
-*  Description : Met en évidence le texte recherché en cas de correspondance. *
-*                                                                             *
-*  Retour      : Texte final destiné à être inséré, à libérer après usage.    *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static char *build_highlighted_name(const char *raw, const regmatch_t *match, size_t start)
-{
-    char *result;                           /* Construction à retourner    */
-    size_t len;                             /* Taille du texte d'entrée    */
-    regmatch_t selection;                   /* Sélection relative au texte */
-    char *valid;                            /* Retouche partielle          */
-
-    len = strlen(raw);
-
-    /* Si aucune sélection ou texte hors champ... */
-
-    if ((match->rm_eo - match->rm_so) == 0 || (start + len) <= match->rm_so || match->rm_eo < start)
-    {
-        result = strdup(raw);
-        result = strrpl(result, "<", "&lt;");
-    }
-
-    /* Sinon, il y a forcément correspondance quelque part ! */
+        result = false;
 
     else
-    {
-        /* Adaptations */
-
-        if (match->rm_so < start)
-            selection.rm_so = 0;
-        else
-            selection.rm_so = match->rm_so - start;
-
-        selection.rm_eo = match->rm_eo - start;
-
-        if (selection.rm_eo > len)
-            selection.rm_eo = len;
-
-        /* Impressions */
-
-        if (selection.rm_so > 0)
-        {
-            result = strndup(raw, selection.rm_so);
-            result = strrpl(result, "<", "&lt;");
-        }
-        else
-            result = NULL;
-
-        result = stradd(result, "<b>");
-
-        valid = strndup(&raw[selection.rm_so], selection.rm_eo - selection.rm_so);
-        valid = strrpl(valid, "<", "&lt;");
-
-        result = stradd(result, valid);
-
-        free(valid);
-
-        result = stradd(result, "</b>");
-
-        valid = strdup(&raw[selection.rm_eo]);
-        valid = strrpl(valid, "<", "&lt;");
-
-        result = stradd(result, valid);
-
-        free(valid);
-
-    }
+        result = is_content_matching(panel->filter, g_binary_symbol_get_label(symbol), match);
 
     return result;
 
-- 
cgit v0.11.2-87-g4458