From 16242be5838a77690946cbb2f30b2e89f2df0b94 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Fri, 15 Feb 2019 23:58:40 +0100 Subject: Displayed tooltips for graph view edges. --- src/analysis/block-int.h | 4 + src/analysis/block.c | 26 ++++ src/analysis/block.h | 3 + src/analysis/disass/block.c | 349 +++++++++++++++++++++++++++++++++++++++++++ src/analysis/routine.c | 2 +- src/glibext/gbuffercache.c | 12 +- src/gtkext/graph/cluster.c | 25 +++- src/gtkext/graph/edge.c | 39 ++++- src/gtkext/graph/edge.h | 36 +++-- src/gtkext/gtkgraphdisplay.c | 71 +++++++++ 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 +#include + + #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 +#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 +#include + + #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, _("Source: ")); + + desc = g_code_block_build_tooltip(src); + info = stradd(info, desc); + free(desc); + + info = stradd(info, "\n\n"); + + info = stradd(info, _("Destination: ")); + + 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. * * * -- cgit v0.11.2-87-g4458