summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2019-02-15 22:58:40 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2019-02-15 22:58:40 (GMT)
commit16242be5838a77690946cbb2f30b2e89f2df0b94 (patch)
treed3c2b2725f41f2a0d45ae7f32e2ab9cb1886b230
parent5ed40f0afbd58fbfdf05be7b2a10ab8d0819759f (diff)
Displayed tooltips for graph view edges.
-rw-r--r--src/analysis/block-int.h4
-rw-r--r--src/analysis/block.c26
-rw-r--r--src/analysis/block.h3
-rw-r--r--src/analysis/disass/block.c349
-rw-r--r--src/analysis/routine.c2
-rw-r--r--src/glibext/gbuffercache.c12
-rw-r--r--src/gtkext/graph/cluster.c25
-rw-r--r--src/gtkext/graph/edge.c39
-rw-r--r--src/gtkext/graph/edge.h36
-rw-r--r--src/gtkext/gtkgraphdisplay.c71
10 files changed, 539 insertions, 28 deletions
diff --git a/src/analysis/block-int.h b/src/analysis/block-int.h
index 6759e7b..9eb99d7 100644
--- a/src/analysis/block-int.h
+++ b/src/analysis/block-int.h
@@ -47,6 +47,9 @@ typedef block_link_t * (* block_get_links_fc) (const GCodeBlock *, const GBlockL
/* Fournit la représentation graphique d'un bloc de code. */
typedef GBufferView * (* block_build_view_fc) (const GCodeBlock *, segcnt_list *);
+/* Construit un ensemble d'indications pour bloc. */
+typedef char *(* block_build_tooltip_fc) (const GCodeBlock *);
+
/* Description d'un bloc de code (instance) */
struct _GCodeBlock
@@ -75,6 +78,7 @@ struct _GCodeBlockClass
block_get_links_fc get_src; /* Obtention des origines */
block_get_links_fc get_dest; /* Obtention des destinations */
block_build_view_fc build; /* Construction d'une vue */
+ block_build_tooltip_fc build_tooltip; /* Construction d'une bulle */
};
diff --git a/src/analysis/block.c b/src/analysis/block.c
index a7172af..85c8017 100644
--- a/src/analysis/block.c
+++ b/src/analysis/block.c
@@ -357,6 +357,32 @@ GBufferView *g_code_block_get_view(GCodeBlock *block, segcnt_list *highlighted)
}
+/******************************************************************************
+* *
+* Paramètres : block = bloc de code à consulter. *
+* *
+* Description : Construit un ensemble d'indications pour bloc. *
+* *
+* Retour : Informations à présenter sous forme de bulle d'aide. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *g_code_block_build_tooltip(const GCodeBlock *block)
+{
+ char *result; /* Description à retourner */
+ GCodeBlockClass *class; /* Classe des blocs de code */
+
+ class = G_CODE_BLOCK_GET_CLASS(block);
+
+ result = class->build_tooltip(block);
+
+ return result;
+
+}
+
+
/* ---------------------------------------------------------------------------------- */
/* DEFINITION DE LIAISONS ENTRE BLOCS DE CODE */
diff --git a/src/analysis/block.h b/src/analysis/block.h
index 71092b2..bb3db3e 100644
--- a/src/analysis/block.h
+++ b/src/analysis/block.h
@@ -77,6 +77,9 @@ void g_code_block_set_rank(GCodeBlock *, size_t);
/* Fournit la représentation graphique d'un bloc de code. */
GBufferView *g_code_block_get_view(GCodeBlock *, segcnt_list *);
+/* Construit un ensemble d'indications pour bloc. */
+char *g_code_block_build_tooltip(const GCodeBlock *);
+
/* ------------------- DEFINITION DE LIAISONS ENTRE BLOCS DE CODE ------------------- */
diff --git a/src/analysis/disass/block.c b/src/analysis/disass/block.c
index 9fce202..3077dc2 100644
--- a/src/analysis/disass/block.c
+++ b/src/analysis/disass/block.c
@@ -28,7 +28,13 @@
#include <malloc.h>
+#include <i18n.h>
+
+
#include "../block-int.h"
+#include "../../arch/raw.h"
+#include "../../common/extstr.h"
+#include "../../core/params.h"
#include "../../glibext/gbinarycursor.h"
@@ -84,6 +90,9 @@ static block_link_t *g_basic_block_get_destinations(const GBasicBlock *, const G
/* Fournit la représentation graphique d'un bloc de code. */
static GBufferView *g_basic_block_build_view(const GBasicBlock *, segcnt_list *);
+/* Construit un ensemble d'indications pour bloc. */
+static char *g_basic_block_build_tooltip(const GBasicBlock *);
+
/* ---------------------------------------------------------------------------------- */
@@ -124,6 +133,7 @@ static void g_basic_block_class_init(GBasicBlockClass *class)
block->get_src = (block_get_links_fc)g_basic_block_get_sources;
block->get_dest = (block_get_links_fc)g_basic_block_get_destinations;
block->build = (block_build_view_fc)g_basic_block_build_view;
+ block->build_tooltip = (block_build_tooltip_fc)g_basic_block_build_tooltip;
}
@@ -499,6 +509,345 @@ static GBufferView *g_basic_block_build_view(const GBasicBlock *block, segcnt_li
/******************************************************************************
* *
+* Paramètres : block = bloc de code à consulter. *
+* *
+* Description : Construit un ensemble d'indications pour bloc. *
+* *
+* Retour : Informations à présenter sous forme de bulle d'aide. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static char *g_basic_block_build_tooltip(const GBasicBlock *block)
+{
+ char *result; /* Description à retourner */
+ const mrange_t *brange[2]; /* Emplacements d'instruction */
+ phys_t diff; /* Espacement entre adresses */
+ mrange_t range; /* Couverture du bloc */
+ char *name; /* Désignation de l'entête */
+ GBinFormat *format; /* Format associé au binaire */
+ GBinSymbol *symbol; /* Symbole lié au bloc */
+ char *label; /* Etiquette à insérer */
+ GBufferCache *cache; /* Tampon d'impression colorée */
+ GBufferLine *line; /* Ligne au contenu coloré */
+ VMPA_BUFFER(loc); /* Indication de position */
+ GGenConfig *config; /* Configuration à consulter */
+ unsigned int max_calls; /* Quantité d'appels à afficher*/
+ unsigned int max_strings; /* Nbre de chaînes à afficher */
+ unsigned int ins_count; /* Quantité d'instructions */
+ unsigned int call_count; /* Quantité d'appels */
+ char *call_info; /* Détails des appels */
+ unsigned int string_count; /* Quantité de chaînes */
+ char *string_info; /* Détails des chaînes */
+ GArchProcessor *proc; /* Architecture utilisée */
+ instr_iter_t *iter; /* Parcours local d'adresses */
+ GArchInstruction *instr; /* Instruction correspondante */
+ size_t dcount; /* Nombre de liens de dest. */
+ size_t i; /* Boucle de parcours */
+ const instr_link_t *dest; /* Instr. visée par une autre */
+ const mrange_t *irange; /* Emplacement d'instruction */
+ GLineCursor *cursor; /* Emplacement dans un tampon */
+ size_t index; /* Indice de ligne à traiter */
+ char *info; /* Ligne d'information créée */
+
+ /* Définition de la couverture du bloc */
+
+ brange[0] = g_arch_instruction_get_range(block->first);
+ brange[1] = g_arch_instruction_get_range(block->last);
+
+ diff = compute_vmpa_diff(get_mrange_addr(brange[0]), get_mrange_addr(brange[1]));
+
+ init_mrange(&range, get_mrange_addr(brange[0]), diff + get_mrange_length(brange[1]));
+
+ /* Recherche d'un symbole de départ */
+
+ name = NULL;
+
+ format = G_BIN_FORMAT(g_loaded_binary_get_format(block->binary));
+
+ if (g_binary_format_find_symbol_at(format, get_mrange_addr(brange[0]), &symbol))
+ {
+ label = g_binary_symbol_get_label(symbol);
+
+ if (label != NULL)
+ {
+ cache = g_buffer_cache_new(NULL);
+ g_buffer_cache_append(cache, G_LINE_GENERATOR(symbol), BLF_NONE);
+
+ line = g_buffer_cache_find_line_by_index(cache, 0);
+ name = g_buffer_line_get_text(line, BLC_ASSEMBLY_LABEL, BLC_COUNT, true);
+ g_object_unref(G_OBJECT(line));
+
+ g_object_unref(G_OBJECT(cache));
+
+ /* Suppression de la fin de l'étiquette... */
+ name = strrpl(name, ":", "");
+
+ }
+
+ else
+ name = NULL;
+
+ free(label);
+
+ g_object_unref(G_OBJECT(symbol));
+
+ }
+
+ if (name == NULL)
+ {
+ proc = g_loaded_binary_get_processor(block->binary);
+
+ if (g_arch_processor_has_virtual_space(proc) && has_virt_addr(get_mrange_addr(&range)))
+ vmpa2_virt_to_string(get_mrange_addr(&range), MDS_UNDEFINED, loc, NULL);
+ else
+ vmpa2_phys_to_string(get_mrange_addr(&range), MDS_UNDEFINED, loc, NULL);
+
+ name = strdup(loc);
+
+ g_object_unref(G_OBJECT(proc));
+
+ }
+
+ result = name;
+
+ /* Lecture des paramètres de configuration */
+
+ config = get_main_configuration();
+
+ if (!g_generic_config_get_value(config, MPK_TOOLTIP_MAX_CALLS, &max_calls))
+ max_calls = 0;
+
+ max_calls++;
+
+ if (!g_generic_config_get_value(config, MPK_TOOLTIP_MAX_STRINGS, &max_strings))
+ max_strings = 0;
+
+ max_strings++;
+
+ /* Parcours des instructions */
+
+ ins_count = 0;
+
+ call_count = 0;
+ call_info = NULL;
+
+ string_count = 0;
+ string_info = NULL;
+
+ proc = g_loaded_binary_get_processor(block->binary);
+ cache = g_loaded_binary_get_disassembled_cache(block->binary);
+
+ iter = g_arch_processor_get_iter_from_address(proc, get_mrange_addr(&range));
+ if (iter == NULL) goto no_iter;
+
+ restrict_instruction_iterator(iter, &range);
+
+ for (instr = get_instruction_iterator_current(iter);
+ instr != NULL;
+ instr = get_instruction_iterator_next(iter))
+ {
+ ins_count ++;
+
+ /* Appels ou références ? */
+
+ g_arch_instruction_lock_dest(instr);
+ dcount = g_arch_instruction_count_destinations(instr);
+
+ for (i = 0; i < dcount; i++)
+ {
+ dest = g_arch_instruction_get_destination(instr, i);
+
+ switch (dest->type)
+ {
+ case ILT_CALL:
+
+ call_count++;
+
+ if (call_count > max_calls)
+ goto next_dest;
+
+ if (call_count == max_calls)
+ {
+ call_info = stradd(call_info, "\n ...");
+ goto next_dest;
+ }
+
+ irange = g_arch_instruction_get_range(instr);
+
+ cursor = g_binary_cursor_new();
+ g_binary_cursor_update(G_BINARY_CURSOR(cursor), get_mrange_addr(irange));
+
+ index = g_buffer_cache_find_index_by_cursor(cache, cursor, true);
+
+ g_object_unref(G_OBJECT(cursor));
+
+ index = g_buffer_cache_look_for_flag(cache, index, BLF_HAS_CODE);
+
+ line = g_buffer_cache_find_line_by_index(cache, index);
+
+ if (line != NULL)
+ {
+ info = g_buffer_line_get_text(line, BLC_ASSEMBLY_HEAD, BLC_COUNT, true);
+ g_object_unref(G_OBJECT(line));
+ }
+
+ else
+ info = NULL;
+
+ if (call_info != NULL)
+ call_info = stradd(call_info, "\n");
+
+ if (info != NULL)
+ {
+ call_info = stradd(call_info, " - ");
+ call_info = stradd(call_info, info);
+ free(info);
+ }
+
+ else
+ call_info = stradd(call_info, " - ???");
+
+ break;
+
+ case ILT_REF:
+
+ if (!G_IS_RAW_INSTRUCTION(dest->linked))
+ goto next_dest;
+
+ if (!g_raw_instruction_is_string(G_RAW_INSTRUCTION(dest->linked)))
+ goto next_dest;
+
+ string_count++;
+
+ if (string_count > max_strings)
+ goto next_dest;
+
+ if (string_count == max_strings)
+ {
+ string_info = stradd(string_info, "\n ...");
+ goto next_dest;
+ }
+
+ irange = g_arch_instruction_get_range(dest->linked);
+
+ cursor = g_binary_cursor_new();
+ g_binary_cursor_update(G_BINARY_CURSOR(cursor), get_mrange_addr(irange));
+
+ index = g_buffer_cache_find_index_by_cursor(cache, cursor, true);
+
+ g_object_unref(G_OBJECT(cursor));
+
+ index = g_buffer_cache_look_for_flag(cache, index, BLF_HAS_CODE);
+
+ line = g_buffer_cache_find_line_by_index(cache, index);
+
+ if (line != NULL)
+ {
+ info = g_buffer_line_get_text(line, BLC_ASSEMBLY, BLC_COUNT, true);
+ g_object_unref(G_OBJECT(line));
+ }
+
+ else
+ info = NULL;
+
+ if (string_info != NULL)
+ string_info = stradd(string_info, "\n");
+
+ if (info != NULL)
+ {
+ string_info = stradd(string_info, " - ");
+ string_info = stradd(string_info, info);
+ free(info);
+ }
+
+ else
+ string_info = stradd(string_info, " - ???");
+
+ break;
+
+ default:
+ break;
+
+ }
+
+ next_dest:
+
+ unref_instr_link(dest);
+
+ }
+
+ g_arch_instruction_unlock_dest(instr);
+
+ g_object_unref(G_OBJECT(instr));
+
+ }
+
+ delete_instruction_iterator(iter);
+
+ no_iter:
+
+ g_object_unref(G_OBJECT(cache));
+ g_object_unref(G_OBJECT(proc));
+
+ /* Construction du résumé */
+
+ result = stradd(result, "\n");
+
+ if (ins_count > 1)
+ asprintf(&info, " - %u %s", ins_count, _("instructions"));
+ else
+ asprintf(&info, " - %u %s", ins_count, _("instruction"));
+
+ result = stradd(result, info);
+ free(info);
+
+ result = stradd(result, "\n");
+
+ if (call_count > 1)
+ asprintf(&info, " - %u %s", call_count, _("calls:"));
+ else if (call_count == 1)
+ asprintf(&info, " - 1 %s", _("call:"));
+ else
+ asprintf(&info, " - 0 %s", _("call"));
+
+ if (call_count > 0)
+ {
+ info = stradd(info, "\n");
+ info = stradd(info, call_info);
+ free(call_info);
+ }
+
+ info = stradd(info, "\n");
+
+ result = stradd(result, info);
+ free(info);
+
+ if (string_count > 1)
+ asprintf(&info, " - %u %s", string_count, _("strings:"));
+ else if (string_count == 1)
+ asprintf(&info, " - 1 %s", _("string:"));
+ else
+ asprintf(&info, " - 0 %s", _("string"));
+
+ if (string_count > 0)
+ {
+ info = stradd(info, "\n");
+ info = stradd(info, call_info);
+ free(call_info);
+ }
+
+ result = stradd(result, info);
+ free(info);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : block = bloc d'instructions à consulter. *
* first = instruction de départ du bloc. [OUT] *
* last = dernière instruction du bloc. [OUT] *
diff --git a/src/analysis/routine.c b/src/analysis/routine.c
index d206e5e..20b5162 100644
--- a/src/analysis/routine.c
+++ b/src/analysis/routine.c
@@ -1134,7 +1134,7 @@ char *g_binary_routine_build_tooltip(const GBinRoutine *routine, const GLoadedBi
{
ins_count ++;
- /* Appels ? */
+ /* Appels ou références ? */
g_arch_instruction_lock_dest(instr);
dcount = g_arch_instruction_count_destinations(instr);
diff --git a/src/glibext/gbuffercache.c b/src/glibext/gbuffercache.c
index e4ddf06..8d02dd8 100644
--- a/src/glibext/gbuffercache.c
+++ b/src/glibext/gbuffercache.c
@@ -609,26 +609,24 @@ static void g_buffer_cache_dispose(GBufferCache *cache)
cache_info *info; /* Accès direct à une ligne */
size_t j; /* Boucle de parcours #2 */
- if (cache->content != NULL)
- g_object_unref(G_OBJECT(cache->content));
+ g_clear_object(&cache->content);
for (i = 0; i < cache->used; i++)
{
info = &cache->lines[i];
if (info->count == 1)
- g_object_unref(G_OBJECT(info->generator.instance));
+ g_clear_object(&info->generator.instance);
else
for (j = 0; j < info->count; j++)
- g_object_unref(G_OBJECT(info->generators[j].instance));
+ g_clear_object(&info->generators[j].instance);
- if (info->line)
- g_object_unref(G_OBJECT(info->line));
+ g_clear_object(&info->line);
}
- g_object_unref(G_OBJECT(cache->tracker));
+ g_clear_object(&cache->tracker);
G_OBJECT_CLASS(g_buffer_cache_parent_class)->dispose(G_OBJECT(cache));
diff --git a/src/gtkext/graph/cluster.c b/src/gtkext/graph/cluster.c
index c4deeaa..765bbac 100644
--- a/src/gtkext/graph/cluster.c
+++ b/src/gtkext/graph/cluster.c
@@ -460,6 +460,8 @@ static gint compute_leaving_link_position(const leaving_link_t *link)
static incoming_link_t *create_incoming_link(GGraphCluster *owner, InstructionLinkType type, leaving_link_t *other)
{
incoming_link_t *result; /* Structure à retourner */
+ GCodeBlock *src; /* Bloc d'origine du lien */
+ GCodeBlock *dst; /* Bloc de destination du lien */
result = malloc(sizeof(incoming_link_t));
@@ -467,14 +469,23 @@ static incoming_link_t *create_incoming_link(GGraphCluster *owner, InstructionLi
result->type = type;
+ src = other->owner->block;
+ dst = owner->block;
+
if (type == ILT_JUMP_IF_TRUE)
- result->edge = g_graph_edge_new_true(&other->start[0], &other->start[1], &result->end[0], &result->end[1]);
+ result->edge = g_graph_edge_new_true(src, dst,
+ &other->start[0], &other->start[1],
+ &result->end[0], &result->end[1]);
else if (type == ILT_JUMP_IF_FALSE)
- result->edge = g_graph_edge_new_false(&other->start[0], &other->start[1], &result->end[0], &result->end[1]);
+ result->edge = g_graph_edge_new_false(src, dst,
+ &other->start[0], &other->start[1],
+ &result->end[0], &result->end[1]);
else
- result->edge = g_graph_edge_new(&other->start[0], &other->start[1], &result->end[0], &result->end[1]);
+ result->edge = g_graph_edge_new(src, dst,
+ &other->start[0], &other->start[1],
+ &result->end[0], &result->end[1]);
result->other = other;
@@ -499,6 +510,8 @@ static incoming_link_t *create_incoming_link(GGraphCluster *owner, InstructionLi
static incoming_link_t *create_incoming_loop_link(GGraphCluster *owner, const GdkPoint *midpts, leaving_link_t *other)
{
incoming_link_t *result; /* Structure à retourner */
+ GCodeBlock *src; /* Bloc d'origine du lien */
+ GCodeBlock *dst; /* Bloc de destination du lien */
result = malloc(sizeof(incoming_link_t));
@@ -506,7 +519,11 @@ static incoming_link_t *create_incoming_loop_link(GGraphCluster *owner, const Gd
result->type = ILT_LOOP;
- result->edge = g_graph_edge_new_loop(&other->start[0], &other->start[1],
+ src = other->owner->block;
+ dst = owner->block;
+
+ result->edge = g_graph_edge_new_loop(src, dst,
+ &other->start[0], &other->start[1],
&midpts[0], &midpts[1],
&result->end[0], &result->end[1]);
diff --git a/src/gtkext/graph/edge.c b/src/gtkext/graph/edge.c
index 9a2f848..20f1988 100644
--- a/src/gtkext/graph/edge.c
+++ b/src/gtkext/graph/edge.c
@@ -36,6 +36,9 @@ struct _GGraphEdge
{
GObject parent; /* A laisser en premier */
+ GCodeBlock *src; /* Bloc d'origine du lien */
+ GCodeBlock *dst; /* Bloc de destination du lien */
+
EdgeColor color; /* Couleur du rendu */
union
@@ -137,6 +140,9 @@ static void g_graph_edge_init(GGraphEdge *edge)
static void g_graph_edge_dispose(GGraphEdge *edge)
{
+ g_clear_object(&edge->src);
+ g_clear_object(&edge->dst);
+
G_OBJECT_CLASS(g_graph_edge_parent_class)->dispose(G_OBJECT(edge));
}
@@ -177,12 +183,18 @@ static void g_graph_edge_finalize(GGraphEdge *edge)
* *
******************************************************************************/
-GGraphEdge *_g_graph_edge_new(const GdkPoint **templates, size_t count, EdgeColor color)
+GGraphEdge *_g_graph_edge_new(GCodeBlock *src, GCodeBlock *dst, const GdkPoint **templates, size_t count, EdgeColor color)
{
GGraphEdge *result; /* Structure à retourner */
result = g_object_new(G_TYPE_GRAPH_EDGE, NULL);
+ result->src = src;
+ result->dst = dst;
+
+ g_object_ref(G_OBJECT(src));
+ g_object_ref(G_OBJECT(dst));
+
result->color = color;
assert(count == 4 || count == 6);
@@ -200,6 +212,31 @@ GGraphEdge *_g_graph_edge_new(const GdkPoint **templates, size_t count, EdgeColo
/******************************************************************************
* *
* Paramètres : edge = ligne de rendu à consulter. *
+* src = bloc d'origine du lien. [OUT] *
+* dst = bloc de destination du lien. [OUT] *
+* *
+* Description : Fournit les deux blocs aux extrémités d'un lien. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_graph_edge_get_boundaries(const GGraphEdge *edge, GCodeBlock **src, GCodeBlock **dst)
+{
+ *src = edge->src;
+ *dst = edge->dst;
+
+ g_object_ref(G_OBJECT(*src));
+ g_object_ref(G_OBJECT(*dst));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : edge = ligne de rendu à consulter. *
* x1 = abscisse du point de départ de la ligne. [OUT] *
* x2 = abscisse du point d'arrivée de la ligne. [OUT] *
* *
diff --git a/src/gtkext/graph/edge.h b/src/gtkext/graph/edge.h
index e905685..ec33544 100644
--- a/src/gtkext/graph/edge.h
+++ b/src/gtkext/graph/edge.h
@@ -30,13 +30,16 @@
#include <gtk/gtk.h>
+#include "../../analysis/disass/block.h"
-#define G_TYPE_GRAPH_EDGE g_graph_edge_get_type()
-#define G_GRAPH_EDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_GRAPH_EDGE, GGraphEdge))
-#define G_IS_GRAPH_EDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_GRAPH_EDGE))
-#define G_GRAPH_EDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GRAPH_EDGE, GGraphEdgeClass))
-#define G_IS_GRAPH_EDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GRAPH_EDGE))
-#define G_GRAPH_EDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GRAPH_EDGE, GGraphEdgeClass))
+
+
+#define G_TYPE_GRAPH_EDGE g_graph_edge_get_type()
+#define G_GRAPH_EDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_GRAPH_EDGE, GGraphEdge))
+#define G_IS_GRAPH_EDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_GRAPH_EDGE))
+#define G_GRAPH_EDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GRAPH_EDGE, GGraphEdgeClass))
+#define G_IS_GRAPH_EDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GRAPH_EDGE))
+#define G_GRAPH_EDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GRAPH_EDGE, GGraphEdgeClass))
/* Lien graphique entre deux noeuds graphiques (instance) */
@@ -68,19 +71,22 @@ typedef enum _EdgeColor
GType g_graph_edge_get_type(void);
/* Etablit un lien graphique entre deux noeuds graphiques. */
-GGraphEdge *_g_graph_edge_new(const GdkPoint **, size_t, EdgeColor);
+GGraphEdge *_g_graph_edge_new(GCodeBlock *, GCodeBlock *, const GdkPoint **, size_t, EdgeColor);
+
+#define g_graph_edge_new(src, dst, pts0, pts1, pte0, pte1) \
+ _g_graph_edge_new(src, dst, (const GdkPoint *[]) { pts0, pts1, pte0, pte1 }, 4, EGC_DEFAULT)
-#define g_graph_edge_new(pts0, pts1, pte0, pte1) \
- _g_graph_edge_new((const GdkPoint *[]) { pts0, pts1, pte0, pte1 }, 4, EGC_DEFAULT)
+#define g_graph_edge_new_true(src, dst, pts0, pts1, pte0, pte1) \
+ _g_graph_edge_new(src, dst, (const GdkPoint *[]) { pts0, pts1, pte0, pte1 }, 4, EGC_GREEN)
-#define g_graph_edge_new_true(pts0, pts1, pte0, pte1) \
- _g_graph_edge_new((const GdkPoint *[]) { pts0, pts1, pte0, pte1 }, 4, EGC_GREEN)
+#define g_graph_edge_new_false(src, dst, pts0, pts1, pte0, pte1) \
+ _g_graph_edge_new(src, dst, (const GdkPoint *[]) { pts0, pts1, pte0, pte1 }, 4, EGC_RED)
-#define g_graph_edge_new_false(pts0, pts1, pte0, pte1) \
- _g_graph_edge_new((const GdkPoint *[]) { pts0, pts1, pte0, pte1 }, 4, EGC_RED)
+#define g_graph_edge_new_loop(src, dst, pts0, pts1, ptl0, ptl1, pte0, pte1) \
+ _g_graph_edge_new(src, dst, (const GdkPoint *[]) { pts0, pts1, ptl0, ptl1, pte0, pte1 }, 6, EGC_BLUE)
-#define g_graph_edge_new_loop(pts0, pts1, ptl0, ptl1, pte0, pte1) \
- _g_graph_edge_new((const GdkPoint *[]) { pts0, pts1, ptl0, ptl1, pte0, pte1 }, 6, EGC_BLUE)
+/* Fournit les deux blocs aux extrémités d'un lien. */
+void g_graph_edge_get_boundaries(const GGraphEdge *, GCodeBlock **, GCodeBlock **);
/* Fournit les abscisses des points extrèmes de la ligne. */
void g_graph_edge_get_x_borders(const GGraphEdge *, gint *, gint *);
diff --git a/src/gtkext/gtkgraphdisplay.c b/src/gtkext/gtkgraphdisplay.c
index 0915f3a..0865e04 100644
--- a/src/gtkext/gtkgraphdisplay.c
+++ b/src/gtkext/gtkgraphdisplay.c
@@ -27,11 +27,15 @@
#include <assert.h>
+#include <i18n.h>
+
+
#include "gtkblockdisplay.h"
#include "gtkbufferdisplay.h"
#include "gtkdisplaypanel-int.h"
#include "graph/cluster.h"
#include "../analysis/routine.h"
+#include "../common/extstr.h"
#include "../format/format.h"
#include "../glibext/gbinarycursor.h"
#include "../glibext/gloadedpanel.h"
@@ -115,6 +119,9 @@ static gboolean gtk_graph_display_button_release(GtkWidget *, GdkEventButton *,
/* Assure la suivi des déplacements de souris sur le composant. */
static gboolean gtk_graph_display_motion_notify(GtkWidget *, GdkEventMotion *, GtkGraphDisplay *);
+/* Prépare l'affichage d'une astuce. */
+static gboolean gtk_graph_display_query_tooltip(GtkWidget *, gint, gint, gboolean, GtkTooltip *, GtkGraphDisplay *);
+
/* Ajuste au besoin la zone affichée pour un curseur. */
static void gtk_graph_display_prepare_for_cursor(GtkGraphDisplay *, const GLineCursor *);
@@ -213,6 +220,10 @@ static void gtk_graph_display_init(GtkGraphDisplay *display)
G_CALLBACK(gtk_graph_display_button_release), display);
g_signal_connect(G_OBJECT(display->support), "motion-notify-event",
G_CALLBACK(gtk_graph_display_motion_notify), display);
+ g_signal_connect(G_OBJECT(display->support), "query-tooltip",
+ G_CALLBACK(gtk_graph_display_query_tooltip), display);
+
+ g_object_set(G_OBJECT(display->support), "has-tooltip", TRUE, NULL);
gtk_widget_add_events(display->support,
GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
@@ -649,6 +660,66 @@ static gboolean gtk_graph_display_motion_notify(GtkWidget *widget, GdkEventMotio
/******************************************************************************
* *
+* Paramètres : widget = composant GTK visé par l'opération. *
+* x = abscisse de la position du message. *
+* y = ordonnée de la position du message. *
+* keyboard = indique une demande suite à obtiention du focus. *
+* tooltip = astuce à compléter. [OUT] *
+* display = support maître à consulter. *
+* *
+* Description : Prépare l'affichage d'une astuce. *
+* *
+* Retour : TRUE pour un affichage validé, FALSE sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static gboolean gtk_graph_display_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard, GtkTooltip *tooltip, GtkGraphDisplay *display)
+{
+ gboolean result; /* Bilan à retourner */
+ GCodeBlock *src; /* Bloc d'origine du lien */
+ GCodeBlock *dst; /* Bloc de destination du lien */
+ char *info; /* Information à faire paraître*/
+ char *desc; /* Description d'un bloc */
+
+ result = FALSE;
+
+ if (display->hl_edge_index < display->edges_count)
+ {
+ g_graph_edge_get_boundaries(display->edges[display->hl_edge_index], &src, &dst);
+
+ info = stradd(NULL, _("<b>Source:</b> "));
+
+ desc = g_code_block_build_tooltip(src);
+ info = stradd(info, desc);
+ free(desc);
+
+ info = stradd(info, "\n\n");
+
+ info = stradd(info, _("<b>Destination:</b> "));
+
+ desc = g_code_block_build_tooltip(dst);
+ info = stradd(info, desc);
+ free(desc);
+
+ gtk_tooltip_set_markup(tooltip, info);
+ free(info);
+
+ g_object_unref(G_OBJECT(src));
+ g_object_unref(G_OBJECT(dst));
+
+ result = TRUE;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : display = composant GTK à mettre à jour. *
* cursor = emplacement à présenter à l'écran. *
* *