From ea40f74566cd813722f49ae740ca3df04e522bb2 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 20 Apr 2015 23:09:13 +0000
Subject: Transmitted the focus when a limit has been reached while using he
 keyboard.

git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@514 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
---
 ChangeLog                      |  29 +++++++
 src/arch/processor.c           |  54 ++++++++++++
 src/arch/processor.h           |   8 +-
 src/glibext/gcodebuffer.c      |  74 ++++++++--------
 src/gtkext/graph/node.c        |   2 +-
 src/gtkext/gtkbufferview-int.h |   1 +
 src/gtkext/gtkbufferview.c     | 186 +++++++++++++++++++++++++++--------------
 src/gtkext/gtkbufferview.h     |   3 +
 src/gtkext/gtkgraphview.c      | 173 ++++++++++++++++++++++++++++++++++++--
 src/gtkext/gtkviewpanel.c      |  23 +++++
 src/gui/status.c               |  11 +++
 11 files changed, 454 insertions(+), 110 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 652e345..4ace600 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
+15-04-21  Cyrille Bagard <nocbos@gmail.com>
+
+	* src/arch/processor.c:
+	* src/arch/processor.h:
+	Provide the previous and next instructions from an internal list.
+
+	* src/glibext/gcodebuffer.c:
+	Begin to used fast indexes when dealing with view ranges. Always provide
+	an updated address when moving the caret with success. Fix a bug when
+	highlighting segments by providing a return value, as expected.
+
+	* src/gtkext/graph/node.c:
+	Update code.
+
+	* src/gtkext/gtkbufferview.c:
+	* src/gtkext/gtkbufferview.h:
+	* src/gtkext/gtkbufferview-int.h:
+	Handle focus changes properly. Notify when a limit has been reached when
+	navigating using the keyboard.
+
+	* src/gtkext/gtkgraphview.c:
+	Transmit the focus when a limit has been reached while using he keyboard.
+
+	* src/gtkext/gtkviewpanel.c:
+	Receive focus event. Hack the scrolling routine for encapsulated buffer views.
+
+	* src/gui/status.c:
+	Only reset the status bar if there is no current selected address.
+
 15-04-17  Cyrille Bagard <nocbos@gmail.com>
 
 	* src/gtkext/gtkviewpanel.c:
diff --git a/src/arch/processor.c b/src/arch/processor.c
index c2d190b..25a38b4 100644
--- a/src/arch/processor.c
+++ b/src/arch/processor.c
@@ -441,3 +441,57 @@ GArchInstruction *g_arch_processor_find_instr_by_address(const GArchProcessor *p
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : proc  = processeur recensant diverses instructions.          *
+*                instr = instruction de référence pour un parcours.           *
+*                                                                             *
+*  Description : Fournit l'instruction qui en précède une autre.              *
+*                                                                             *
+*  Retour      : Instruction précédente trouvée, ou NULL.                     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GArchInstruction *g_arch_processor_get_prev_instr(const GArchProcessor *proc, const GArchInstruction *instr)
+{
+    GArchInstruction *result;               /* Instruction à retourner     */
+    GArchInstruction *list;                 /* Ensemble des instructions   */
+
+    list = g_arch_processor_get_disassembled_instructions(proc);
+
+    result = g_arch_instruction_get_prev_iter(list, instr);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : proc  = processeur recensant diverses instructions.          *
+*                instr = instruction de référence pour un parcours.           *
+*                                                                             *
+*  Description : Fournit l'instruction qui en suit une autre.                 *
+*                                                                             *
+*  Retour      : Instruction suivante trouvée, ou NULL.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GArchInstruction *g_arch_processor_get_next_instr(const GArchProcessor *proc, const GArchInstruction *instr)
+{
+    GArchInstruction *result;               /* Instruction à retourner     */
+    GArchInstruction *list;                 /* Ensemble des instructions   */
+
+    list = g_arch_processor_get_disassembled_instructions(proc);
+
+    result = g_arch_instruction_get_next_iter(list, instr, ~0);
+
+    return result;
+
+}
diff --git a/src/arch/processor.h b/src/arch/processor.h
index 3eeefd5..d650266 100644
--- a/src/arch/processor.h
+++ b/src/arch/processor.h
@@ -87,11 +87,15 @@ void g_arch_processor_set_disassembled_instructions(GArchProcessor *, GArchInstr
 /* Fournit les instructions désassemblées pour une architecture. */
 GArchInstruction *g_arch_processor_get_disassembled_instructions(const GArchProcessor *);
 
-
-
 /* Recherche une instruction d'après son adresse. */
 GArchInstruction *g_arch_processor_find_instr_by_address(const GArchProcessor *, const vmpa2t *);
 
+/* Fournit l'instruction qui en précède une autre. */
+GArchInstruction *g_arch_processor_get_prev_instr(const GArchProcessor *, const GArchInstruction *);
+
+/* Fournit l'instruction qui en suit une autre. */
+GArchInstruction *g_arch_processor_get_next_instr(const GArchProcessor *, const GArchInstruction *);
+
 
 
 #endif  /* _ARCH_PROCESSOR_H */
diff --git a/src/glibext/gcodebuffer.c b/src/glibext/gcodebuffer.c
index 171ba09..a7e522c 100644
--- a/src/glibext/gcodebuffer.c
+++ b/src/glibext/gcodebuffer.c
@@ -146,6 +146,7 @@ struct _GBufferView
     vmpa2t *start;                          /* Première ligne intégrée     */
     vmpa2t *end;                            /* Dernière ligne intégrée     */
     size_t first_index;                     /* Indice de la première ligne */
+    size_t last_index;                      /* Indice de la dernière ligne */
 
     gint line_height;                       /* Hauteur maximale des lignes */
     gint max_widths[BLC_COUNT];             /* Taille cachée des colonnes  */
@@ -623,25 +624,6 @@ static size_t g_code_buffer_get_index_from_address(GCodeBuffer *buffer, const vm
 
     return result;
 
-
-
-
-
-#if 0
-    size_t result;                          /* Indice à retourner          */
-
-    result = _g_code_buffer_get_index_from_address(buffer, addr, first);
-
-    /**
-     * Par commodités, on évite certaines instructions en cas d'échec dans les
-     * fonctions d'appels : la condition des boucles utilisant l'indice retourné (0)
-     * fait son office directement !
-     */
-    if (result == buffer->used)
-        result = 0;
-
-    return result;
-#endif
 }
 
 
@@ -1002,8 +984,8 @@ GBufferView *g_buffer_view_new(GCodeBuffer *buffer)
     g_object_ref(G_OBJECT(buffer));
 
     result->buffer = buffer;
-    result->start = NULL;
-    result->end = NULL;
+
+    g_buffer_view_restrict(result, NULL, NULL);
 
     return result;
 
@@ -1026,8 +1008,11 @@ GBufferView *g_buffer_view_new(GCodeBuffer *buffer)
 
 void g_buffer_view_restrict(GBufferView *view, const vmpa2t *start, const vmpa2t *end)
 {
-    view->start = dup_vmpa(start);
-    view->end = dup_vmpa(end);
+    view->start = (start != NULL ? dup_vmpa(start) : NULL);
+    view->end = (end != NULL ? dup_vmpa(end) : NULL);
+
+    view->first_index = g_code_buffer_get_index_from_address(view->buffer, start, true);
+    view->last_index = g_code_buffer_get_index_from_address(view->buffer, end, false);
 
 }
 
@@ -1048,6 +1033,8 @@ void g_buffer_view_restrict(GBufferView *view, const vmpa2t *start, const vmpa2t
 
 void g_buffer_view_get_restrictions(GBufferView *view, vmpa2t *start, vmpa2t *end)
 {
+    /* FIXME view->xxx == NULL -> plantage */
+
     if (start != NULL) copy_vmpa(start, view->start);
     if (end != NULL) copy_vmpa(end, view->end);
 
@@ -1447,7 +1434,6 @@ const vmpa2t *g_buffer_view_compute_caret_full(GBufferView *view, GBufferLine *l
     gint offset;                            /* Point de travail modifiable */
     gint base;                              /* Position absolue de segment */
     GBufferSegment *segment;                /* Segment visé par le pointeur*/
-    size_t first;                           /* Première ligne intégrée     */
 
     offset = x;
 
@@ -1459,8 +1445,9 @@ const vmpa2t *g_buffer_view_compute_caret_full(GBufferView *view, GBufferLine *l
 
     caret->x = view->left_text + base + offset;
 
-    first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
-    caret->y = (index - first) * view->line_height;
+    printf("caret Y : %zu -> %zu\n", view->first_index, index);
+
+    caret->y = (index - view->first_index) * view->line_height;
 
     caret->width = 2;
     caret->height = view->line_height;
@@ -1588,7 +1575,8 @@ static bool _g_buffer_view_move_caret(GBufferView *view, const GBufferLine *line
 
 const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, const bool *display)
 {
-    vmpa2t *result;                         /* Actualisation à renvoyer    */
+    const vmpa2t *result;                   /* Actualisation à renvoyer    */
+    size_t index;                           /* Indice de ligne de tampon   */
     GBufferLine *line;                      /* Ligne sous le pointeur      */
 
 
@@ -1600,7 +1588,7 @@ const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, b
     size_t first;                           /* Première ligne intégrée     */
     size_t last;                            /* Dernière ligne intégrée     */
 
-    size_t index;                           /* Indice de ligne de tampon   */
+
 
 
     bool moved;                             /* Mémorisation d'une évolut°  */
@@ -1652,10 +1640,8 @@ const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, b
             break;
     }
 
-    first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
-    last = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
-
-    first = 0;
+    first = view->first_index;
+    last = view->last_index;
 
     switch (dir)
     {
@@ -1681,12 +1667,17 @@ const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, b
 
         case GDK_SCROLL_LEFT:
 
+            /*
             line = g_buffer_view_find_line_at(view, caret->y, &index);
             if (line == NULL) break;
+            */
 
             moved = _g_buffer_view_move_caret(view, line, caret, ctrl, GDK_SCROLL_LEFT, display);
 
-            if (!moved && index > first)
+            if (moved)
+                result = get_mrange_addr(g_buffer_line_get_range(line));
+
+            else if (index > first)
             {
                 line = view->buffer->lines[index - 1];
                 result = g_buffer_view_compute_caret_full(view, line, index - 1, INT_MAX, display, caret);
@@ -1696,12 +1687,17 @@ const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, b
 
         case GDK_SCROLL_RIGHT:
 
+            /*
             line = g_buffer_view_find_line_at(view, caret->y, &index);
             if (line == NULL) break;
+            */
 
             moved = _g_buffer_view_move_caret(view, line, caret, ctrl, GDK_SCROLL_RIGHT, display);
 
-            if (!moved && index < last)
+            if (moved)
+                result = get_mrange_addr(g_buffer_line_get_range(line));
+
+            else if (index < last)
             {
                 line = view->buffer->lines[index + 1];
                 result = g_buffer_view_compute_caret_full(view, line, index + 1, left_pos, display, caret);
@@ -1787,6 +1783,8 @@ bool g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y, const b
     if (need_redraw)
         g_signal_emit_by_name(view, "need-redraw");
 
+    return true;
+
 }
 
 
@@ -1813,7 +1811,6 @@ void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint fake_x, gint
     gint real_x;                            /* Abscisse réelle pour tampon */
     gint real_y;                            /* Ordonnée réelle pour tampon */
     size_t first;                           /* Première ligne visée        */
-    size_t end;                             /* Dernière ligne avant limite */
     size_t last;                            /* Dernière ligne visée + 1    */
     gint y;                                 /* Point de départ + décallage */
     GBufferLine **lines;                    /* Liste des lignes à traiter  */
@@ -1824,14 +1821,13 @@ void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint fake_x, gint
     real_x = fake_x + view->left_text;
     real_y = fake_y + area->y;
 
-    first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
+    first = view->first_index;
     first += (real_y / view->line_height);
 
     last = first + (area->height / view->line_height);
     if (area->height % view->line_height > 0) last++;
 
-    end = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
-    last = MIN(last, end);
+    last = MIN(last, view->last_index);
 
     y = area->y - (real_y % view->line_height);
 
@@ -1960,6 +1956,8 @@ GBufferLine *g_buffer_view_find_line_at(GBufferView *view, gint y, size_t *idx)
     lheight = g_buffer_view_get_line_height(view);
     index = y / lheight;
 
+    index += view->first_index;
+
     if (idx != NULL)
         *idx = index;
 
diff --git a/src/gtkext/graph/node.c b/src/gtkext/graph/node.c
index 4fd7a8e..9ff66b3 100644
--- a/src/gtkext/graph/node.c
+++ b/src/gtkext/graph/node.c
@@ -507,7 +507,7 @@ GtkBufferView *find_graph_view_by_start_address(GtkBufferView **views, size_t co
 
     for (i = 0; i < count && result == NULL; i++)
     {
-        buffer = gtk_buffer_view_get_buffer(GTK_BUFFER_VIEW(views[i]));
+        buffer = gtk_buffer_view_get_buffer(views[i]);
         g_buffer_view_get_restrictions(buffer, &start, NULL);
 
         if (cmp_vmpa(&start, addr) == 0)
diff --git a/src/gtkext/gtkbufferview-int.h b/src/gtkext/gtkbufferview-int.h
index e8e11a0..105d32b 100644
--- a/src/gtkext/gtkbufferview-int.h
+++ b/src/gtkext/gtkbufferview-int.h
@@ -65,6 +65,7 @@ struct _GtkBufferViewClass
     /* Signaux */
 
     void (* caret_moved) (GtkBufferView *, const vmpa2t *);
+    void (* reach_limit) (GtkBufferView *, GdkScrollDirection);
 
 };
 
diff --git a/src/gtkext/gtkbufferview.c b/src/gtkext/gtkbufferview.c
index 1a1f6f1..54c521e 100644
--- a/src/gtkext/gtkbufferview.c
+++ b/src/gtkext/gtkbufferview.c
@@ -48,7 +48,7 @@ static void gtk_buffer_view_dispose(GtkBufferView *);
 static void gtk_buffer_view_finalize(GtkBufferView *);
 
 /* Intègre le focus dans le rendu du composant. */
-static gboolean gtk_buffer_view_focus(GtkWidget *, GtkDirectionType);
+static gboolean gtk_buffer_view_focus(GtkWidget *, GdkEventFocus *);
 
 /* Assure la gestion des clics de souris sur le composant. */
 static gboolean gtk_buffer_view_button_press(GtkWidget *, GdkEventButton *);
@@ -82,6 +82,9 @@ static void gtk_buffer_view_cache_glance(GtkBufferView *, cairo_t *, const GtkAl
 /* ------------------------------ ANIMATION DU CURSEUR ------------------------------ */
 
 
+/* Déplace le curseur à un emplacement défini. */
+static bool _gtk_buffer_view_move_caret_to(GtkBufferView *, gint, gint);
+
 /* Déplace le curseur en effaçant son éventuelle position. */
 static void gtk_buffer_view_relocate_caret(GtkBufferView *, const GdkRectangle *, const vmpa2t *);
 
@@ -127,7 +130,8 @@ static void gtk_buffer_view_class_init(GtkBufferViewClass *class)
     object->dispose = (GObjectFinalizeFunc/* ! */)gtk_buffer_view_dispose;
     object->finalize = (GObjectFinalizeFunc)gtk_buffer_view_finalize;
 
-    widget_class->focus = gtk_buffer_view_focus;
+    widget_class->focus_in_event = gtk_buffer_view_focus;
+    widget_class->focus_out_event = gtk_buffer_view_focus;
     widget_class->button_press_event = gtk_buffer_view_button_press;
     widget_class->draw = gtk_buffer_view_draw;
     widget_class->key_press_event = gtk_buffer_view_key_press;
@@ -147,6 +151,14 @@ static void gtk_buffer_view_class_init(GtkBufferViewClass *class)
                  g_cclosure_user_marshal_VOID__UINT64,
                  G_TYPE_NONE, 1, G_TYPE_UINT64);
 
+    g_signal_new("reach-limit",
+                 GTK_TYPE_BUFFER_VIEW,
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET(GtkBufferViewClass, reach_limit),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__ENUM,
+                 G_TYPE_NONE, 1, GTK_TYPE_SCROLL_TYPE);
+
 }
 
 
@@ -213,7 +225,7 @@ static void gtk_buffer_view_finalize(GtkBufferView *view)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : widget = composant GTK visé par l'opération.                 *
-*                dir    = sens de l'opération : perte ou gain de focus.       *
+*                event  = informations liées à l'événement.                   *
 *                                                                             *
 *  Description : Intègre le focus dans le rendu du composant.                 *
 *                                                                             *
@@ -223,13 +235,13 @@ static void gtk_buffer_view_finalize(GtkBufferView *view)
 *                                                                             *
 ******************************************************************************/
 
-static gboolean gtk_buffer_view_focus(GtkWidget *widget, GtkDirectionType direction)
+static gboolean gtk_buffer_view_focus(GtkWidget *widget, GdkEventFocus *event)
 {
     GtkBufferView *view;                    /* Autre version du composant  */
     gboolean has_focus;                     /* Etat courant                */
 
     view = GTK_BUFFER_VIEW(widget);
-    has_focus = gtk_widget_is_focus(widget);
+    has_focus = event->in;
 
     if (has_focus)
         restart_caret_blinking(view);
@@ -265,16 +277,11 @@ static gboolean gtk_buffer_view_focus(GtkWidget *widget, GtkDirectionType direct
 static gboolean gtk_buffer_view_button_press(GtkWidget *widget, GdkEventButton *event)
 {
     GtkBufferView *view;                    /* Autre version du composant  */
-    GtkViewPanel *pview;                    /* Autre version du composant  */
     gint real_x;                            /* Abscisse absolue réelle     */
     gint real_y;                            /* Ordonnée absolue réelle     */
-    size_t index;                           /* Indice de ligne de tampon   */
     GBufferLine *line;                      /* Ligne à la position courante*/
-    const vmpa2t *addr;                     /* Position mémoire associée   */
-    GdkRectangle new;                       /* Nouvel emplacement calculé  */
 
     view = GTK_BUFFER_VIEW(widget);
-    pview = GTK_VIEW_PANEL(widget);
 
     gtk_widget_grab_focus(widget);
 
@@ -287,32 +294,17 @@ static gboolean gtk_buffer_view_button_press(GtkWidget *widget, GdkEventButton *
            real_x, real_y);
 
 
-
-    line = g_buffer_view_find_line_at(view->buffer_view, real_y, &index);
-    if (line == NULL) return FALSE;
-
-
-
-    printf(" [init ] %p - line = %p (y=%d)\n", view->buffer_view, line, real_y);
-
-
-
     if (real_x < view->left_margin)
     {
+        line = g_buffer_view_find_line_at(view->buffer_view, real_y, NULL);
+        if (line == NULL) return FALSE;
 
-
-
+        /* TODO */
         printf("Border Line :: %p\n", line);
 
-
     }
     else
-    {
-        //addr = g_buffer_view_compute_caret_old(view->buffer_view, line, index, real_x, pview->display, &new);
-        //addr = g_buffer_view_compute_caret(view->buffer_view, real_x, real_y, pview->display, &new);
-        addr = g_buffer_view_compute_caret_full(view->buffer_view, line, index, real_x, pview->display, &new);
-        gtk_buffer_view_relocate_caret(view, &new, addr);
-    }
+        _gtk_buffer_view_move_caret_to(view, real_x, real_y);
 
     return FALSE;
 
@@ -472,77 +464,67 @@ static gboolean gtk_buffer_view_draw(GtkWidget *widget, cairo_t *cr)
 static gboolean gtk_buffer_view_key_press(GtkWidget *widget, GdkEventKey *event)
 {
     gboolean result;                        /* Suites à renvoyer           */
-
     GtkBufferView *view;                    /* Autre version du composant  */
     GtkViewPanel *pview;                    /* Autre version du composant  */
-
-
     bool ctrl;                              /* Statut de la touche Contrôle*/
-
-
-    GdkRectangle area;
-
-    const vmpa2t *addr;
-
+    GdkScrollDirection dir;                 /* Direction du déplacement    */
+    GdkRectangle area;                      /* Emplacement de curseur      */
+    const vmpa2t *addr;                     /* Adresse du nouveau curseur  */
 
     result = FALSE;
 
-
-
     view = GTK_BUFFER_VIEW(widget);
     pview = GTK_VIEW_PANEL(widget);
 
-
-    area = view->caret;
-
-
-    ctrl = (event->state & GDK_CONTROL_MASK);
-
     switch (event->keyval)
     {
         case GDK_KEY_Left:
-            printf("LEFT\n");
-            addr = g_buffer_view_move_caret(view->buffer_view, &area, ctrl, GDK_SCROLL_LEFT, pview->display);
-            gtk_buffer_view_relocate_caret(view, &area, view->caret_addr);
+            dir = GDK_SCROLL_LEFT;
             result = TRUE;
             break;
 
         case GDK_KEY_Up:
-            printf("UP\n");
-            addr = g_buffer_view_move_caret(view->buffer_view, &area, ctrl, GDK_SCROLL_UP, pview->display);
-            gtk_buffer_view_relocate_caret(view, &area, view->caret_addr);
+            dir = GDK_SCROLL_UP;
             result = TRUE;
             break;
 
         case GDK_KEY_Right:
-            printf("RIGHT\n");
-            addr = g_buffer_view_move_caret(view->buffer_view, &area, ctrl, GDK_SCROLL_RIGHT, pview->display);
-            gtk_buffer_view_relocate_caret(view, &area, view->caret_addr);
+            dir = GDK_SCROLL_RIGHT;
             result = TRUE;
             break;
 
         case GDK_KEY_Down:
-            printf("DOWN\n");
-            addr = g_buffer_view_move_caret(view->buffer_view, &area, ctrl, GDK_SCROLL_DOWN, pview->display);
-            gtk_buffer_view_relocate_caret(view, &area, view->caret_addr);
+            dir = GDK_SCROLL_DOWN;
             result = TRUE;
             break;
 
         default:
-            addr = NULL;
             break;
 
     }
 
+    if (result)
+    {
+        area = view->caret;
+        ctrl = (event->state & GDK_CONTROL_MASK);
+
+        addr = g_buffer_view_move_caret(view->buffer_view, &area, ctrl, dir, pview->display);
+
+        if (addr != NULL)
+        {
+            gtk_buffer_view_relocate_caret(view, &area, addr);
+            gtk_view_panel_scroll_to_address(pview, addr, SPT_RAW);
+        }
+        else
+            g_signal_emit_by_name(view, "reach-limit", dir);
 
-    printf("ctrl ? %d -- keyval = %d -->> %d\n", ctrl, event->keyval, result);
 
 
-    if (addr != NULL) gtk_view_panel_scroll_to_address(pview, addr, SPT_RAW);
+        //if (addr == NULL) return FALSE;
 
 
-    printf("\n");
 
+    }
 
     return result;
 
@@ -815,6 +797,8 @@ void gtk_buffer_view_attach_buffer(GtkBufferView *view, GBufferView *buffer)
 
 GBufferView *gtk_buffer_view_get_buffer(const GtkBufferView *view)
 {
+    /* TODO : ref... */
+
     return view->buffer_view;
 
 }
@@ -862,6 +846,84 @@ void gtk_buffer_view_compute_relative_coords(GtkBufferView *view, gint *x, gint
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : view = composant GTK à manipuler.                            *
+*                x    = abscisse proposée pour le nouvel emplacement.         *
+*                y    = ordonnée proposée pour le nouvel emplacement.         *
+*                                                                             *
+*  Description : Déplace le curseur à un emplacement défini.                  *
+*                                                                             *
+*  Retour      : true si un traitement a été effectué, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool _gtk_buffer_view_move_caret_to(GtkBufferView *view, gint x, gint y)
+{
+    size_t index;                           /* Indice de ligne de tampon   */
+    GBufferLine *line;                      /* Ligne à la position courante*/
+    GtkViewPanel *pview;                    /* Autre version du composant  */
+    const vmpa2t *addr;                     /* Position mémoire associée   */
+    GdkRectangle new;                       /* Nouvel emplacement calculé  */
+
+    if (x < view->left_margin) return false;
+
+    line = g_buffer_view_find_line_at(view->buffer_view, y, &index);
+    if (line == NULL) return false;
+
+    pview = GTK_VIEW_PANEL(view);
+
+    addr = g_buffer_view_compute_caret_full(view->buffer_view, line, index, x, pview->display, &new);
+
+    if (addr != NULL)
+        gtk_buffer_view_relocate_caret(view, &new, addr);
+
+    return (addr != NULL);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : view      = composant GTK à manipuler.                       *
+*                beginning = précise le coin où se retrouvera le curseur.     *
+*                same_x    = tente de conserver une même abscisse ou NULL ?   *
+*                                                                             *
+*  Description : Déplace le curseur à un emplacement en extrémité.            *
+*                                                                             *
+*  Retour      : true si un traitement a été effectué, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool gtk_buffer_view_move_caret_to(GtkBufferView *view, bool beginning, gint *same_x)
+{
+    bool result;                            /* Bilan à remonter            */
+    gint x;                                 /* Abscisse d'emplacement      */
+    gint y;                                 /* Ordonnée d'emplacement      */
+
+    if (beginning)
+    {
+        x = same_x != NULL ? *same_x : view->left_margin * 2;
+        y = 0;
+    }
+    else
+    {
+        if (same_x != NULL) x = *same_x;
+        gtk_buffer_view_compute_requested_size(view, same_x != NULL ? NULL : &x, &y);
+        y--;
+    }
+
+    result = _gtk_buffer_view_move_caret_to(view, x, y);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : view = composant GTK à manipuler.                            *
 *                area = emplacement pour le dessin d'un curseur.              *
 *                addr = position dans la mémoire représentée du curseur.      *
 *                                                                             *
diff --git a/src/gtkext/gtkbufferview.h b/src/gtkext/gtkbufferview.h
index 0cdcf4f..ed26fd5 100644
--- a/src/gtkext/gtkbufferview.h
+++ b/src/gtkext/gtkbufferview.h
@@ -70,6 +70,9 @@ void gtk_buffer_view_compute_relative_coords(GtkBufferView *, gint *, gint *);
 /* ------------------------------ ANIMATION DU CURSEUR ------------------------------ */
 
 
+/* Déplace le curseur à un emplacement en extrémité. */
+bool gtk_buffer_view_move_caret_to(GtkBufferView *, bool, gint *);
+
 /* Indique la position courante du curseur. */
 const vmpa2t *gtk_buffer_view_get_caret_location(const GtkBufferView *);
 
diff --git a/src/gtkext/gtkgraphview.c b/src/gtkext/gtkgraphview.c
index 875c1b4..6d7ab0d 100644
--- a/src/gtkext/gtkgraphview.c
+++ b/src/gtkext/gtkgraphview.c
@@ -24,6 +24,9 @@
 #include "gtkgraphview.h"
 
 
+#include <assert.h>
+
+
 #include "gtkblockview.h"
 #include "gtkbufferview.h"
 #include "gtkviewpanel-int.h"
@@ -43,7 +46,7 @@ struct _GtkGraphView
     vmpa_t start;                           /* Début de la portion vue     */ /* FIXME : à garder ? */
     vmpa_t end;                             /* Fin de la portion affichée  */ /* FIXME : à garder ? */
 
-    GtkViewPanel **children;                /* Liste des sous-blocs        */
+    GtkBufferView **children;               /* Liste des sous-blocs        */
     GtkAllocation *allocs;                  /* Emplacements prévisibles    */
     size_t children_count;                  /* Taille de cette liste       */
 
@@ -93,10 +96,11 @@ static void gtk_graph_view_cache_glance(GtkGraphView *, cairo_t *, const GtkAllo
 /* Supprime tout contenu de l'afficheur de code en graphique. */
 static void gtk_graph_view_reset(GtkGraphView *);
 
-/* Définit la liste complète des éléments du futur graphique.   *
-*                                                                             *
-*  Retour      : Liste d'éléments du graphique à placer. */
-static GtkViewPanel **gtk_graph_view_load_nodes(GtkGraphView *, GLoadedBinary *, const GBinRoutine *);
+/* Définit la liste complète des éléments du futur graphique. */
+static GtkBufferView **gtk_graph_view_load_nodes(GtkGraphView *, GLoadedBinary *, const GBinRoutine *);
+
+/* Notifie une incapacité de déplacement au sein d'un noeud. */
+static void gtk_graph_view_reach_caret_limit(GtkBufferView *, GdkScrollDirection, GtkGraphView *);
 
 
 
@@ -423,7 +427,7 @@ static void gtk_graph_view_cache_glance(GtkGraphView *view, cairo_t *cairo, cons
         sub_area.width = view->allocs[i].width * scale + 1;
         sub_area.height = view->allocs[i].height * scale + 1;
 
-        gtk_view_panel_cache_glance(view->children[i], cairo, &sub_area, scale);
+        gtk_view_panel_cache_glance(GTK_VIEW_PANEL(view->children[i]), cairo, &sub_area, scale);
 
     }
 
@@ -554,7 +558,10 @@ static void gtk_graph_view_reset(GtkGraphView *view)
     */
 
     for (i = 0; i < view->children_count; i++)
+    {
+        g_signal_handlers_disconnect_by_func(view->children[i], gtk_graph_view_reach_caret_limit, view);
         gtk_widget_destroy(GTK_WIDGET(view->children[i]));
+    }
 
     if (view->children_count > 0)
     {
@@ -583,7 +590,7 @@ static void gtk_graph_view_reset(GtkGraphView *view)
 *                                                                             *
 ******************************************************************************/
 
-static GtkViewPanel **gtk_graph_view_load_nodes(GtkGraphView *view, GLoadedBinary *binary, const GBinRoutine *routine)
+static GtkBufferView **gtk_graph_view_load_nodes(GtkGraphView *view, GLoadedBinary *binary, const GBinRoutine *routine)
 {
     GtkViewPanel **result;                  /* Liste à retourner           */
     GCodeBuffer *buffer;                    /* Tampon brut à découper      */
@@ -610,6 +617,8 @@ static GtkViewPanel **gtk_graph_view_load_nodes(GtkGraphView *view, GLoadedBinar
     for (i = 0; i < *count; i++)
     {
         result[i] = GTK_VIEW_PANEL(gtk_block_view_new());
+        g_signal_connect(result[i], "reach-limit", G_CALLBACK(gtk_graph_view_reach_caret_limit), view);
+
         gtk_widget_show(GTK_WIDGET(result[i]));
         gtk_view_panel_attach_binary(result[i], binary, BVW_BLOCK);
 
@@ -626,3 +635,153 @@ static GtkViewPanel **gtk_graph_view_load_nodes(GtkGraphView *view, GLoadedBinar
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : node = composant d'affichage GTK impliqué dans la procédure. *
+*                dir  = direction du déplacement souhaité et impossible.      *
+*                view = support graphique de tous les noeuds.                 *
+*                                                                             *
+*  Description : Notifie une incapacité de déplacement au sein d'un noeud.    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void gtk_graph_view_reach_caret_limit(GtkBufferView *node, GdkScrollDirection dir, GtkGraphView *view)
+{
+    GBufferView *bview;                     /* Vue d'un tampon global      */
+    vmpa2t first;                           /* Début d'un groupe de lignes */
+    vmpa2t last;                            /* Fin d'un groupe de lignes   */
+    const mrange_t *range;                  /* Couverture courante         */
+    GArchProcessor *proc;                   /* Processeur pour instructions*/
+    GArchInstruction *ref;                  /* Point de référence          */
+#ifndef NDEBUG
+    bool is_return;                         /* Est-ce une instruc. finale ?*/
+#endif
+    vmpa2t iaddr;                           /* Position de l'instructin    */
+    size_t i;                               /* Boucle de parcours          */
+    bool updated;                           /* Besoin d'une mise à jour ?  */
+
+    /* Détermination de l'instruction à cibler */
+
+    bview = gtk_buffer_view_get_buffer(node);
+    g_buffer_view_get_restrictions(bview, &first, &last);
+
+    range = g_binary_routine_get_range(view->routine);
+
+    proc = g_loaded_binary_get_processor(GTK_VIEW_PANEL(view)->binary);
+
+    ref = NULL;
+
+#ifndef NDEBUG
+    is_return = false;
+#endif
+
+    switch (dir)
+    {
+        case GDK_SCROLL_LEFT:
+        case GDK_SCROLL_UP:
+            if (cmp_vmpa(get_mrange_addr(range), &first) != 0)
+            {
+                ref = g_arch_processor_find_instr_by_address(proc, &first);
+
+                if (ref != NULL)
+                    ref = g_arch_processor_get_prev_instr(proc, ref);
+
+                /* TODO : boucler si !HAS_CODE */
+
+                if (ref != NULL)
+                    copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(ref)));
+
+            }
+            break;
+
+        case GDK_SCROLL_RIGHT:
+        case GDK_SCROLL_DOWN:
+
+            ref = g_arch_processor_find_instr_by_address(proc, &last);
+
+#ifndef NDEBUG
+            if (ref != NULL)
+                is_return = g_arch_instruction_is_return(ref);
+#endif
+
+            if (ref != NULL)
+                ref = g_arch_processor_get_next_instr(proc, ref);
+
+            /* TODO : boucler si !HAS_CODE */
+
+            if (ref != NULL)
+            {
+                copy_vmpa(&iaddr, get_mrange_addr(g_arch_instruction_get_range(ref)));
+
+                if (!mrange_contains_addr(range, &iaddr))
+                    ref = NULL;
+
+            }
+
+            break;
+
+    }
+
+    g_object_unref(G_OBJECT(proc));
+
+    /* Recherche du bloc parent */
+
+    if (ref == NULL)
+        return;
+
+    for (i = 0; i < view->children_count; i++)
+    {
+        bview = gtk_buffer_view_get_buffer(view->children[i]);
+        g_buffer_view_get_restrictions(bview, &first, &last);
+
+        if (cmp_vmpa(&first, &iaddr) <= 0 && cmp_vmpa(&iaddr, &last) <= 0)
+        {
+            assert(node != view->children[i]);
+            break;
+        }
+
+    }
+
+    assert(i < view->children_count || is_return);
+
+    /* Affichage du nouveau curseur */
+
+    /**
+     * Il se peut qu'aucune adresse suivante ne soit disponible : c'est typiquement
+     * le cas sous ARM, avec les valeurs brutes référencées dans le code. Ces valeurs
+     * sont incluses dans la surface couverte par la routine concernée, mais ne sont
+     * pas intégrées dans les blocs basiques associés.
+     */
+
+    if (i == view->children_count)
+        return;
+
+    gtk_widget_grab_focus(GTK_WIDGET(view->children[i]));
+
+    switch (dir)
+    {
+        case GDK_SCROLL_LEFT:
+            updated = gtk_buffer_view_move_caret_to(view->children[i], false, NULL);
+            break;
+
+        case GDK_SCROLL_UP:
+            break;
+
+        case GDK_SCROLL_RIGHT:
+            updated = gtk_buffer_view_move_caret_to(view->children[i], true, NULL);
+            break;
+
+        case GDK_SCROLL_DOWN:
+            break;
+
+    }
+
+    /* TODO : scrolling... */
+
+}
diff --git a/src/gtkext/gtkviewpanel.c b/src/gtkext/gtkviewpanel.c
index 7f29574..e36da71 100644
--- a/src/gtkext/gtkviewpanel.c
+++ b/src/gtkext/gtkviewpanel.c
@@ -297,6 +297,7 @@ static void gtk_view_panel_realize(GtkWidget *widget)
     attributes.event_mask = gtk_widget_get_events(widget)
         | GDK_EXPOSURE_MASK
         | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK
+        | GDK_FOCUS_CHANGE_MASK
         | GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK;
 
     attributes_mask = GDK_WA_X | GDK_WA_Y;
@@ -792,6 +793,8 @@ static void on_view_panel_binary_display_change(GLoadedBinary *binary, BinaryVie
 
 GLoadedBinary *gtk_view_panel_get_binary(const GtkViewPanel *panel)
 {
+    /* TODO : ref */
+
     return panel->binary;
 
 }
@@ -836,6 +839,7 @@ bool gtk_view_panel_contain_address(const GtkViewPanel *panel, vmpa_t addr)
 
 void gtk_view_panel_scroll_to_address(GtkViewPanel *panel, const vmpa2t *addr, ScrollPositionTweak tweak)
 {
+    GtkWidget *parent;                      /* Support parent à valider    */
     gint x;                                 /* Abscisse à garantir         */
     gint y;                                 /* Ordonnée à garantir         */
     GtkWidget *viewport;                    /* Parent avec défilement      */
@@ -844,6 +848,25 @@ void gtk_view_panel_scroll_to_address(GtkViewPanel *panel, const vmpa2t *addr, S
     gdouble page_size;                      /* Taille de l'affichage       */
     double value;                           /* Valeur courante             */
 
+    /**
+     * Si une vue partielle se déplacer via cette fonction, il faut potentiellement
+     * rediriger l'appel vers la vue en graphiques parente.
+     */
+
+    parent = gtk_widget_get_parent(GTK_WIDGET(panel));
+    parent = gtk_widget_get_parent(GTK_WIDGET(parent));
+
+    printf(" Widgets : %s -> %s\n",
+           G_OBJECT_TYPE_NAME(parent), G_OBJECT_TYPE_NAME(panel));
+
+    if (GTK_IS_VIEW_PANEL(parent))
+    {
+        printf("reparent !\n");
+        panel = GTK_VIEW_PANEL(parent);
+    }
+    else
+        printf("no need reparent !\n");
+
     if (GTK_VIEW_PANEL_GET_CLASS(panel)->define != NULL)
         GTK_VIEW_PANEL_GET_CLASS(panel)->define(panel, addr);
 
diff --git a/src/gui/status.c b/src/gui/status.c
index 07e8254..2637f28 100644
--- a/src/gui/status.c
+++ b/src/gui/status.c
@@ -288,6 +288,17 @@ static void track_caret_address_on_buffer_views(GtkBufferView *view, const vmpa2
     msize = g_arch_processor_get_memory_size(proc);
     g_object_unref(G_OBJECT(proc));
 
+    /* Réinitiation seule si il n'y a plus d'adresse... */
+
+    if (addr == NULL)
+    {
+        if (info->msg_id > 0)
+            gtk_extended_status_bar_remove(GTK_EXT_STATUS_BAR(item->widget), info->msg_id);
+
+        return;
+
+    }
+
     /* Adresse brute */
 
     msize = g_arch_processor_get_memory_size(proc);
-- 
cgit v0.11.2-87-g4458