/* Chrysalide - Outil d'analyse de fichiers binaires
* gcodebuffer.h - prototypes pour l'affichage d'un fragment de code d'assemblage
*
* Copyright (C) 2010-2014 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* OpenIDA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* OpenIDA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar. If not, see .
*/
#include "gcodebuffer.h"
#include
#include
#include
#include
#include "chrysamarshal.h"
#include "delayed-int.h"
#include "../common/extstr.h"
/* -------------------------- PARCOURS DU CODE D'UN TAMPON -------------------------- */
#define G_TYPE_BUFFER_SCAN g_buffer_scan_get_type()
#define G_BUFFER_SCAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_buffer_scan_get_type(), GDelayedExport))
#define G_IS_BUFFER_SCAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_buffer_scan_get_type()))
#define G_BUFFER_SCAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BUFFER_SCAN, GDelayedExportClass))
#define G_IS_BUFFER_SCAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BUFFER_SCAN))
#define G_BUFFER_SCAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BUFFER_SCAN, GDelayedExportClass))
/* Ensembles binaires à désassembler (instance) */
typedef struct _GBufferScan
{
GDelayedWork parent; /* A laisser en premier */
GCodeBuffer *buffer; /* Tampon à manipuler */
vmpa2t start; /* Début du parcours */
bool has_start; /* Validité des données #1 */
vmpa2t end; /* Fin du parcours */
bool has_end; /* Validité des données #2 */
char *message; /* Message de progression */
process_line_fc process; /* Fonction de traitement réel */
void *user_data; /* Données à faire suivre */
} GBufferScan;
/* Ensembles binaires à désassembler (classe) */
typedef struct _GBufferScanClass
{
GDelayedWorkClass parent; /* A laisser en premier */
} GBufferScanClass;
/* Indique le type défini pour les tâches d'exportation différée. */
static GType g_buffer_scan_get_type(void);
/* Initialise la classe des tâches d'exportation différée. */
static void g_buffer_scan_class_init(GBufferScanClass *);
/* Initialise une tâche d'exportation différée. */
static void g_buffer_scan_init(GBufferScan *);
/* Supprime toutes les références externes. */
static void g_buffer_scan_dispose(GBufferScan *);
/* Procède à la libération totale de la mémoire. */
static void g_buffer_scan_finalize(GBufferScan *);
/* Crée une tâche d'exportation différée. */
static GBufferScan *g_buffer_scan_new(GCodeBuffer *, const vmpa2t *, const vmpa2t *, const char *, process_line_fc, void *);
/* Assure l'exportation en différé. */
static void g_buffer_scan_process(GBufferScan *, GtkStatusStack *);
/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */
/* Tampon pour code désassemblé (instance) */
struct _GCodeBuffer
{
GObject parent; /* A laisser en premier */
BufferLineColumn main_column; /* Colonne principale */
GBufferLine **lines; /* Liste des lignes intégrées */
size_t count; /* Quantité en cache */
size_t used; /* Quantité utilisée */
size_t indent; /* Indentation des lignes */
};
/* Tampon pour code désassemblé (classe) */
struct _GCodeBufferClass
{
GObjectClass parent; /* A laisser en premier */
/* Signaux */
void (* line_changed) (GCodeBuffer *, GBufferLine *, GBufferSegment *);
};
/* Taille des allocations de masse */
#define LINE_ALLOC_BULK 20
/* Procède à l'initialisation d'une classe de tampon de code. */
static void g_code_buffer_class_init(GCodeBufferClass *);
/* Procède à l'initialisation d'un tampon pour code désassemblé. */
static void g_code_buffer_init(GCodeBuffer *);
/* Convertit une adresse en indice de ligne. */
static size_t g_code_buffer_get_index_from_address(const GCodeBuffer *, const vmpa2t *, bool);
/* Actualise les largeurs maximales par groupes de lignes. */
static void g_code_buffer_update_line_max_widths(const GCodeBuffer *, size_t, size_t);
/* Réagit à un changement de contenu d'une ligne donnée. */
static void on_line_content_change(GBufferLine *, GBufferSegment *, GCodeBuffer *);
/* Réagit à un changement de propriété rattachée à une ligne. */
static void on_line_flag_flip(GBufferLine *, BufferLineFlags, BufferLineFlags, GCodeBuffer *);
/* Ajoute de nouvelles lignes à une position donnée. */
static void g_code_buffer_insert_lines_at(GCodeBuffer *, GBufferLine **, size_t, size_t);
/* ------------------------- CONFORTS POUR LES COMMENTAIRES ------------------------- */
/* Affiche un commentaire sur une ligne de tampon donnée. */
static bool _g_code_buffer_write_inlined_comment(GCodeBuffer *, GBufferLine *, const char *, GObject *);
/* Affiche un commentaire sur une ligne de tampon dédiée. */
static bool _g_code_buffer_write_comment_area(GCodeBuffer *, GBufferLine *, const char *, bool, GObject *);
/* Retrouve la première ligne d'une zone de commentaire. */
static size_t g_code_buffer_find_first_line_comment(const GCodeBuffer *, size_t);
/* Retrouve la dernière ligne d'une zone de commentaire. */
static size_t g_code_buffer_find_last_line_comment(const GCodeBuffer *, size_t);
/* Supprime un commentaire existant. */
static bool _g_code_buffer_delete_lines_comment(GCodeBuffer *, GBufferLine *);
/* ---------------------- VUE PARTICULIERE D'UN TAMPON DE CODE ---------------------- */
/* Vue d'un tampon pour code désassemblé (instance) */
struct _GBufferView
{
GObject parent; /* A laisser en premier */
GCodeBuffer *buffer; /* Tampon de code visualisé */
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 */ /* FIXME : utiliser partout ? */
size_t last_index; /* Indice de la dernière ligne */ /* FIXME : idem */
gint line_height; /* Hauteur maximale des lignes */
gint left_margin; /* Marge gauche + espace */
gint left_text; /* Début d'impression du code */
gint max_widths[BLC_COUNT]; /* Taille cachée des colonnes */
gint merged_width; /* Plus grande taille de fusion*/
segcnt_list *highlighted; /* Segments mis en évidence */
bool external; /* Note l'origine de la liste */
};
/* Vue d'un tampon pour code désassemblé (classe) */
struct _GBufferViewClass
{
GObjectClass parent; /* A laisser en premier */
/* Signaux */
void (* need_redraw) (GBufferView *);
};
#define HEIGHT_CACHED(view) ((view)->line_height != -1)
#define WIDTHS_CACHED(view) ((view)->max_widths[0] != -1)
/* Procède à l'initialisation d'une classe de vue de tampon. */
static void g_buffer_view_class_init(GBufferViewClass *);
/* Procède à l'initialisation d'une vue d'un tampon pour code. */
static void g_buffer_view_init(GBufferView *);
/* Supprime toutes les références externes. */
static void g_buffer_view_dispose(GBufferView *);
/* Procède à la libération totale de la mémoire. */
static void g_buffer_view_finalize(GBufferView *);
/* Réagit à un changement de contenu d'une ligne donnée. */
static void on_buffer_line_changed(GCodeBuffer *, GBufferLine *, GBufferSegment *, GBufferView *);
/* Réinitialise le cache de la hauteur des lignes. */
static void g_buffer_view_reset_required_height(GBufferView *);
/* Réinitialise le cache des largeurs de colonne calculées. */
static void g_buffer_view_reset_required_widths(GBufferView *);
/* Calcule les largeurs requises par une visualisation. */
static void g_buffer_view_compute_required_widths(GBufferView *, const bool *);
/* ---------------------------------------------------------------------------------- */
/* PARCOURS DU CODE D'UN TAMPON */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les tâches d'exportation différée. */
G_DEFINE_TYPE(GBufferScan, g_buffer_scan, G_TYPE_DELAYED_WORK);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des tâches d'exportation différée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_class_init(GBufferScanClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GDelayedWorkClass *work; /* Version en classe parente */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_scan_dispose;
object->finalize = (GObjectFinalizeFunc)g_buffer_scan_finalize;
work = G_DELAYED_WORK_CLASS(klass);
work->run = (run_task_fc)g_buffer_scan_process;
}
/******************************************************************************
* *
* Paramètres : scan = instance à initialiser. *
* *
* Description : Initialise une tâche d'exportation différée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_init(GBufferScan *scan)
{
}
/******************************************************************************
* *
* Paramètres : scan = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_dispose(GBufferScan *scan)
{
g_object_unref(G_OBJECT(scan->buffer));
G_OBJECT_CLASS(g_buffer_scan_parent_class)->dispose(G_OBJECT(scan));
}
/******************************************************************************
* *
* Paramètres : scan = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_finalize(GBufferScan *scan)
{
free(scan->message);
G_OBJECT_CLASS(g_buffer_scan_parent_class)->finalize(G_OBJECT(scan));
}
/******************************************************************************
* *
* Paramètres : buffer = tampon à manipuler. *
* start = première adresse visée ou NULL. *
* end = dernière adresse visée ou NULL. *
* message = message à afficher lors de la progression. *
* process = fonction assurant le traitement effectif. *
* data = données utilisateur à faire suivre. *
* *
* Description : Crée une tâche d'exportation différée. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
static GBufferScan *g_buffer_scan_new(GCodeBuffer *buffer, const vmpa2t *start, const vmpa2t *end, const char *message, process_line_fc process, void *data)
{
GBufferScan *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_BUFFER_SCAN, NULL);
result->buffer = buffer;
g_object_ref(G_OBJECT(buffer));
result->has_start = (start != NULL);
if (result->has_start)
copy_vmpa(&result->start, start);
result->has_end = (end != NULL);
if (result->has_end)
copy_vmpa(&result->end, end);
result->message = strdup(message);
result->process = process;
result->user_data = data;
return result;
}
/******************************************************************************
* *
* Paramètres : scan = parcours à mener. *
* status = barre de statut à tenir informée. *
* *
* Description : Assure l'exportation en différé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_process(GBufferScan *scan, GtkStatusStack *status)
{
size_t first; /* Première ligne visée */
size_t last; /* Dernière ligne visée + 1 */
GBufferLine **lines; /* Liste des lignes à traiter */
//bstatus_id_t id; /* Identifiant de statut */
size_t i; /* Boucle de parcours */
/* TODO : lock scan->buffer->lines */
if (scan->has_start)
first = g_code_buffer_get_index_from_address(scan->buffer, &scan->start, true);
else
first = 0;
if (scan->has_end)
last = g_code_buffer_get_index_from_address(scan->buffer, &scan->end, false);
else
last = scan->buffer->used - 1;
lines = scan->buffer->lines;
//id = gtk_extended_status_bar_push(statusbar, scan->message, true);
if (scan->buffer->used > 0)
for (i = first; i <= last; i++)
{
if (!scan->process(scan->buffer, lines[i], scan->user_data))
break;
/*
gtk_extended_status_bar_update_activity(statusbar, id,
(i - first) * 1.0 / (last - first));
*/
}
/* TODO : unlock scan->buffer->lines */
//gtk_extended_status_bar_remove(statusbar, id);
}
/* ---------------------------------------------------------------------------------- */
/* CONFORTS POUR LES COMMENTAIRES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* line = ligne à l'intérieur d'un commentaire. *
* comment = nouveau commentaire à inscrire à la ligne donnée. *
* creator = créateur à l'origine de la construction. *
* *
* Description : Affiche un commentaire sur une ligne de tampon donnée. *
* *
* Retour : Bilan de l'opération : ajout ou non ? *
* *
* Remarques : - *
* *
******************************************************************************/
static bool _g_code_buffer_write_inlined_comment(GCodeBuffer *buffer, GBufferLine *line, const char *comment, GObject *creator)
{
bool result; /* Bilan à retourner */
const mrange_t *range; /* Emplace de ligne à utiliser */
char *wcopy; /* Copie de travail */
GBufferLine **extra; /* Lignes supplémentaires */
size_t extra_count; /* Quantité de ces lignes */
char *saveptr; /* Sauvegarde pour la sécurité */
char *token; /* Fragment à insérer */
size_t len; /* Taille dudit fragment */
GBufferSegment *segment; /* Segment à marquer au fer */
GBufferLine *new; /* Nouvelle ligne créée */
size_t i; /* Boucle de parcours */
assert(!g_buffer_line_has_comment(line));
result = false;
range = g_buffer_line_get_range(line);
wcopy = strdup(comment);
extra = NULL;
extra_count = 0;
for (token = strtok_r(wcopy, COMMENT_LINE_SEP, &saveptr);
token != NULL;
token = strtok_r(NULL, COMMENT_LINE_SEP, &saveptr))
{
len = strlen(token);
if (!result)
{
segment = g_buffer_line_insert_text(line, BLC_COMMENTS, token, len, RTT_COMMENT);
g_buffer_segment_set_creator(segment, creator);
}
else
{
new = g_code_buffer_prepare_new_line(buffer, range);
segment = g_buffer_line_insert_text(new, BLC_COMMENTS, token, len, RTT_COMMENT);
g_buffer_segment_set_creator(segment, creator);
extra = (GBufferLine **)realloc(extra, ++extra_count * sizeof(GBufferLine *));
extra[extra_count - 1] = new;
}
result = true;
}
free(wcopy);
if (extra_count > 0)
{
result &= g_code_buffer_insert_lines(buffer, extra, extra_count, line, false);
if (!result)
for (i = 0; i < extra_count; i++)
g_object_unref(G_OBJECT(extra[i]));
free(extra);
}
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* line = ligne à l'intérieur d'un commentaire. *
* comment = nouveau commentaire à inscrire à la ligne donnée. *
* creator = créateur à l'origine de la construction. *
* *
* Description : Affiche un commentaire sur une ligne de tampon donnée. *
* *
* Retour : Bilan de l'opération : ajout ou non ? *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_code_buffer_update_inlined_comment(GCodeBuffer *buffer, GBufferLine *line, const char *comment, GObject *creator)
{
bool result; /* Bilan à retourner */
if (g_buffer_line_has_comment(line))
result = _g_code_buffer_delete_lines_comment(buffer, line);
else
result = true;
if (result)
result = _g_code_buffer_write_inlined_comment(buffer, line, comment, creator);
/* TODO : emit() */
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* line = ligne à l'intérieur d'un commentaire. *
* comment = nouveau commentaire à inscrire à la ligne donnée. *
* before = précise la position du commentaire. *
* creator = créateur à l'origine de la construction. *
* *
* Description : Affiche un commentaire sur une ligne de tampon dédiée. *
* *
* Retour : Bilan de l'opération : ajout ou non ? *
* *
* Remarques : - *
* *
******************************************************************************/
static bool _g_code_buffer_write_comment_area(GCodeBuffer *buffer, GBufferLine *line, const char *comment, bool before, GObject *creator)
{
bool result; /* Bilan à retourner */
const mrange_t *range; /* Emplace de ligne à utiliser */
char *wcopy; /* Copie de travail */
GBufferLine **extra; /* Lignes supplémentaires */
size_t extra_count; /* Quantité de ces lignes */
char *saveptr; /* Sauvegarde pour la sécurité */
char *token; /* Fragment à insérer */
size_t len; /* Taille dudit fragment */
GBufferLine *new; /* Nouvelle ligne créée */
GBufferSegment *segment; /* Segment à marquer au fer */
size_t i; /* Boucle de parcours */
assert(!g_buffer_line_has_comment(line));
result = false;
range = g_buffer_line_get_range(line);
wcopy = strdup(comment);
extra = NULL;
extra_count = 0;
for (token = strtok_r(wcopy, COMMENT_LINE_SEP, &saveptr);
token != NULL;
token = strtok_r(NULL, COMMENT_LINE_SEP, &saveptr))
{
len = strlen(token);
new = g_code_buffer_prepare_new_line(buffer, range);
g_buffer_line_start_merge_at(new, BLC_DISPLAY);
segment = g_buffer_line_insert_text(new, BLC_DISPLAY, token, len, RTT_COMMENT);
g_buffer_segment_set_creator(segment, creator);
extra = (GBufferLine **)realloc(extra, ++extra_count * sizeof(GBufferLine *));
extra[extra_count - 1] = new;
result = true;
}
free(wcopy);
if (extra_count > 0)
{
result &= g_code_buffer_insert_lines(buffer, extra, extra_count, line, before);
if (!result)
for (i = 0; i < extra_count; i++)
g_object_unref(G_OBJECT(extra[i]));
free(extra);
}
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* line = ligne à l'intérieur d'un commentaire. *
* comment = nouveau commentaire à inscrire à la ligne donnée. *
* before = précise la position du commentaire. *
* creator = créateur à l'origine de la construction. *
* *
* Description : Affiche un commentaire sur une ligne de tampon dédiée. *
* *
* Retour : Bilan de l'opération : ajout ou non ? *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_code_buffer_update_comment_area(GCodeBuffer *buffer, GBufferLine *line, const char *comment, bool before, GObject *creator)
{
bool result; /* Bilan à retourner */
if (g_buffer_line_has_comment(line))
result = _g_code_buffer_delete_lines_comment(buffer, line);
else
result = true;
if (result)
result = _g_code_buffer_write_comment_area(buffer, line, comment, before, creator);
/* TODO : emit() */
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* index = indice de ligne à l'intérieur d'un commentaire. *
* *
* Description : Retrouve la première ligne d'une zone de commentaire. *
* *
* Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static size_t g_code_buffer_find_first_line_comment(const GCodeBuffer *buffer, size_t index)
{
size_t result; /* Indice trouvé à retourner */
GBufferLine *prev; /* Ligne précédente */
assert(index < buffer->used);
bool is_first_line_of_comment(const GBufferLine *ln, const GBufferLine *pv)
{
bool first; /* Statut à renvoyer */
BufferLineColumn merge_col; /* Colonne de fusion #1 */
BufferLineColumn prev_merge_col; /* Colonne de fusion #2 */
merge_col = g_buffer_line_get_merge_start(ln);
/**
* La ligne consultée contient toujours un commentaire.
*
* Deux cas de figures sont possibles ici :
*
* - soit du texte est présent dans la colonne "commentaires".
* Si du texte est présent avant, alors il s'agit forcément de
* la première (et unique ?) ligne de commentaire.
*
* - soit la ligne effectue une fusion des colonnes depuis BLC_DISPLAY.
* Si la ligne qui précède fait de même, il s'agit alors d'une étiquette
* ou de l'amont du commentaire.
*
*/
if (g_buffer_line_has_text(ln, BLC_COMMENTS, BLC_COUNT))
{
first = g_buffer_line_has_text(ln, BLC_DISPLAY, BLC_COMMENTS);
if (!first)
{
/* La ligne d'avant doit avoir un commentaire ! */
first = !g_buffer_line_has_text(pv, BLC_COMMENTS, BLC_COUNT);
}
}
else
{
/**
* Le prologue "merge_col == BLC_FIRST" n'étant pas éditable,
* la seule fusion possible ici est la suivante.
*/
assert(merge_col == BLC_DISPLAY);
/**
* La première ligne d'un tampon est toujours un prologue.
*/
assert(pv != NULL);
prev_merge_col = g_buffer_line_get_merge_start(pv);
first = (prev_merge_col != BLC_DISPLAY);
if (!first)
first = (g_buffer_line_get_flags(pv) & BLF_IS_LABEL);
}
return first;
}
for (result = index; result > 0; result--)
{
prev = (result > 0 ? buffer->lines[result - 1] : NULL);
if (is_first_line_of_comment(buffer->lines[result], prev))
break;
}
if (result == 0)
{
if (!is_first_line_of_comment(buffer->lines[0], NULL))
result = buffer->used;
}
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* index = indice de ligne à l'intérieur d'un commentaire. *
* *
* Description : Retrouve la dernière ligne d'une zone de commentaire. *
* *
* Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static size_t g_code_buffer_find_last_line_comment(const GCodeBuffer *buffer, size_t index)
{
size_t result; /* Indice trouvé à retourner */
GBufferLine *next; /* Ligne suivante */
assert(index < buffer->used);
bool is_last_line_of_comment(const GBufferLine *ln, const GBufferLine *nx)
{
bool last; /* Statut à renvoyer */
BufferLineColumn merge_col; /* Colonne de fusion #1 */
BufferLineColumn next_merge_col; /* Colonne de fusion #2 */
merge_col = g_buffer_line_get_merge_start(ln);
/**
* La ligne consultée contient toujours un commentaire.
*
* Deux cas de figures sont possibles ici :
*
* - soit du texte est présent dans la colonne "commentaires".
* Si la ligne suivante est similaire et si du texte est présent avant,
* alors il s'agit forcément de d'un nouveau commentaire. S'il n'y a
* aucun texte, il s'agit de la suite du commentaire.
*
* - soit la ligne effectue une fusion des colonnes depuis BLC_DISPLAY.
* Si la ligne qui suit fait de même, il s'agit alors d'une étiquette
* ou de l'aval du commentaire.
*
*/
if (g_buffer_line_has_text(ln, BLC_COMMENTS, BLC_COUNT))
{
last = !g_buffer_line_has_text(nx, BLC_COMMENTS, BLC_COUNT);
if (!last)
last = g_buffer_line_has_text(nx, BLC_DISPLAY, BLC_COMMENTS);
}
else
{
/**
* Le prologue "merge_col == BLC_FIRST" n'étant pas éditable,
* la seule fusion possible ici est la suivante.
*/
assert(merge_col == BLC_DISPLAY);
if (nx == NULL)
last = true;
else
{
next_merge_col = g_buffer_line_get_merge_start(nx);
last = (next_merge_col != BLC_DISPLAY);
if (!last)
last = (g_buffer_line_get_flags(nx) & BLF_IS_LABEL);
}
}
return last;
}
for (result = index; result < buffer->used; result++)
{
next = ((result + 1) < buffer->used ? buffer->lines[result + 1] : NULL);
if (is_last_line_of_comment(buffer->lines[result], next))
break;
}
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* line = ligne à l'intérieur d'un commentaire. *
* *
* Description : Retrouve le créateur d'un commentaire existant. *
* *
* Retour : Instance trouvée à déréférencer ensuite ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GObject *g_code_buffer_get_comment_creator(const GCodeBuffer *buffer, const GBufferLine *line)
{
GObject *result; /* Instance à retourner */
BufferLineColumn merge_col; /* Colonne de fusion */
if (g_buffer_line_has_comment(line))
{
merge_col = g_buffer_line_get_merge_start(line);
if (merge_col == BLC_DISPLAY)
result = g_buffer_line_find_first_segment_creator(line, BLC_DISPLAY);
else
result = g_buffer_line_find_first_segment_creator(line, BLC_COMMENTS);
}
else
result = NULL;
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* line = ligne à l'intérieur d'un commentaire. *
* *
* Description : Récupère le contenu d'un commentaire existant. *
* *
* Retour : Commentaire retrouver à libérer ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
char *g_code_buffer_get_lines_comment(const GCodeBuffer *buffer, const GBufferLine *line)
{
char *result; /* Contenu à retourner */
size_t index; /* Indice de la ligne fournie */
size_t start; /* Ligne de départ */
size_t end; /* Ligne d'arrivée */
BufferLineColumn merge_col; /* Colonne de fusion */
size_t i; /* Boucle de parcours */
char *extra; /* Commentaire supplémentaire */
/* Pas de prologue ici ! */
assert(g_buffer_line_has_comment(line));
result = NULL;
index = g_code_buffer_find_index_by_line(buffer, line);
if (index == buffer->used)
goto gcbglc_exit;
start = g_code_buffer_find_first_line_comment(buffer, index);
if (start == buffer->used)
goto gcbglc_exit;
end = g_code_buffer_find_last_line_comment(buffer, index);
if (end == buffer->used)
goto gcbglc_exit;
merge_col = g_buffer_line_get_merge_start(line);
for (i = start; i <= end; i++)
{
if (merge_col == BLC_DISPLAY)
extra = g_buffer_line_get_text(buffer->lines[i], BLC_DISPLAY, BLC_COUNT, false);
else
extra = g_buffer_line_get_text(buffer->lines[i], BLC_COMMENTS, BLC_COUNT, false);
assert(extra != NULL);
if (result == NULL)
result = extra;
else
{
result = stradd(result, extra);
free(extra);
}
}
gcbglc_exit:
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à modifier. *
* line = ligne à l'intérieur d'un commentaire. *
* *
* Description : Supprime un commentaire existant. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool _g_code_buffer_delete_lines_comment(GCodeBuffer *buffer, GBufferLine *line)
{
bool result; /* Bilan à retourner */
size_t index; /* Indice de la ligne fournie */
size_t start; /* Ligne de départ */
size_t end; /* Ligne d'arrivée */
BufferLineColumn merge_col; /* Colonne de fusion */
/* Pas de prologue ici ! */
assert(g_buffer_line_has_comment(line));
result = false;
index = g_code_buffer_find_index_by_line(buffer, line);
if (index == buffer->used)
goto gcbdlc_exit;
start = g_code_buffer_find_first_line_comment(buffer, index);
if (start == buffer->used)
goto gcbdlc_exit;
end = g_code_buffer_find_last_line_comment(buffer, index);
if (end == buffer->used)
goto gcbdlc_exit;
result = true;
merge_col = g_buffer_line_get_merge_start(line);
if (merge_col == BLC_DISPLAY)
g_code_buffer_delete_lines(buffer, start, end);
else
{
g_buffer_line_delete_text(buffer->lines[start], BLC_COMMENTS, BLC_COUNT);
if (end > start)
g_code_buffer_delete_lines(buffer, start + 1, end);
}
gcbdlc_exit:
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à modifier. *
* line = ligne à l'intérieur d'un commentaire. *
* *
* Description : Supprime un commentaire existant. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_code_buffer_delete_lines_comment(GCodeBuffer *buffer, GBufferLine *line)
{
bool result; /* Bilan à retourner */
result = _g_code_buffer_delete_lines_comment(buffer, line);
/* TODO : emit() */
return result;
}
/* ---------------------------------------------------------------------------------- */
/* TAMPON POUR CODE DESASSEMBLE */
/* ---------------------------------------------------------------------------------- */
/* Détermine le type du composant de tampon pour code désassemblé. */
G_DEFINE_TYPE(GCodeBuffer, g_code_buffer, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe de tampon de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_code_buffer_class_init(GCodeBufferClass *class)
{
g_signal_new("line-changed",
G_TYPE_CODE_BUFFER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GCodeBufferClass, line_changed),
NULL, NULL,
g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'un tampon pour code désassemblé.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_code_buffer_init(GCodeBuffer *buffer)
{
}
/******************************************************************************
* *
* Paramètres : main = colonne à référencer comme étant la principale. *
* *
* Description : Crée un nouveau composant de tampon pour code désassemblé. *
* *
* Retour : Composant GTK créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GCodeBuffer *g_code_buffer_new(BufferLineColumn main)
{
GCodeBuffer *result; /* Composant à retourner */
result = g_object_new(G_TYPE_CODE_BUFFER, NULL);
result->main_column = main;
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* addr = adresse où va se situer la ligne. *
* first = indique si on l'arrête à la première ou la dernière.*
* *
* Description : Convertit une adresse en indice de ligne. *
* *
* Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static size_t g_code_buffer_get_index_from_address(const GCodeBuffer *buffer, const vmpa2t *addr, bool first)
{
size_t result; /* Indice à retourner */
GBufferLine **found; /* Renvoi vers une trouvaille */
const mrange_t *range; /* Couverture d'une ligne */
/**
* Si aucune adresse (ie. aucune limite ?) n'est précisée, on se base sur
* la direction pour trouver le bon indice.
*/
if (addr == NULL)
result = (first ? 0 : buffer->used - 1);
/**
* Sinon on parcourt méthodiquement toutes les lignes !
*/
else
{
/* Recherche dichotomique grossière */
int cmp_addr_and_line(const vmpa2t *addr, const GBufferLine **line)
{
int status; /* Bilan d'une comparaison */
const mrange_t *lrange; /* Couverture d'une ligne */
lrange = g_buffer_line_get_range(*line);
status = cmp_mrange_with_vmpa(lrange, addr);
return status;
}
found = bsearch(addr, buffer->lines, buffer->used, sizeof(GBufferLine *),
(__compar_fn_t)cmp_addr_and_line);
/* Dernier raffinage pour approcher la cible réelle */
if (found == NULL)
result = buffer->used;
else
{
result = found - buffer->lines;
if (first)
for (; result > 0; result--)
{
range = g_buffer_line_get_range(buffer->lines[result - 1]);
if (!mrange_contains_addr(range, addr)) break;
}
else
for (; (result + 1) < buffer->used; result++)
{
range = g_buffer_line_get_range(buffer->lines[result + 1]);
if (!mrange_contains_addr(range, addr)) break;
}
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon contentenant un ensemble de lignes. *
* first = première ligne modifiée à considérer. *
* last = dernière ligne modifiée à considérer. *
* *
* Description : Actualise les largeurs maximales par groupes de lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_code_buffer_update_line_max_widths(const GCodeBuffer *buffer, size_t first, size_t last)
{
GBufferLine **lines; /* Liste des lignes à traiter */
size_t start; /* Début de groupe de largeurs */
size_t end; /* Fin de groupe de largeurs */
GBufferLine *manager; /* Ligne de gestion de largeurs*/
size_t i; /* Boucle de parcours */
assert(buffer->used > 0);
lines = buffer->lines;
/* Recherche des bornes du groupe de largeurs courant */
for (start = first; start > 0; start--)
if (g_buffer_line_get_flags(lines[start]) & BLF_WIDTH_MANAGER)
break;
for (end = last; end < (buffer->used - 1); end++)
if (g_buffer_line_get_flags(lines[end + 1]) & BLF_WIDTH_MANAGER)
break;
/* Réinitialisation ciblée des largeurs */
assert(g_buffer_line_get_flags(lines[start]) & BLF_WIDTH_MANAGER);
manager = NULL;
for (i = start; i <= end; i++)
{
if (g_buffer_line_get_flags(lines[i]) & BLF_WIDTH_MANAGER)
manager = lines[i];
g_buffer_line_update_max_widths(lines[i], manager);
}
}
/******************************************************************************
* *
* Paramètres : buffer = composant GLib à consulter. *
* range = emplacement où va se situer la ligne. *
* *
* Description : Initie une nouvelle ligne devant être insérée dans le tampon.*
* *
* Retour : Nouvelle ligne vierge à écrire. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_code_buffer_prepare_new_line(GCodeBuffer *buffer, const mrange_t *range)
{
GBufferLine *result; /* Instance à retourner */
size_t i; /* Boucle de parcours */
result = g_buffer_line_new(range, buffer->main_column);
for (i = 0; i < buffer->indent; i++)
g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, " ", 4, RTT_RAW);
return result;
}
/******************************************************************************
* *
* Paramètres : line = ligne dont la définition vient d'évoluer. *
* segment = éventuel segment qui vient d'évoluer ou NULL. *
* buffer = tampon de lignes cohérentes à manipuler. *
* *
* Description : Réagit à un changement de contenu d'une ligne donnée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void on_line_content_change(GBufferLine *line, GBufferSegment *segment, GCodeBuffer *buffer)
{
g_signal_emit_by_name(buffer, "line-changed", line, segment);
}
/******************************************************************************
* *
* Paramètres : line = ligne dont la définition vient d'évoluer. *
* old = ancien groupe de propriétés associées. *
* old = nouveau groupe de propriétés associées. *
* buffer = tampon de lignes cohérentes à manipuler. *
* *
* Description : Réagit à un changement de propriété rattachée à une ligne. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void on_line_flag_flip(GBufferLine *line, BufferLineFlags old, BufferLineFlags new, GCodeBuffer *buffer)
{
g_signal_emit_by_name(buffer, "line-changed", line, NULL);
}
/******************************************************************************
* *
* Paramètres : buffer = composant GLib à mettre à jour. *
* lines = liste de lignes à insérer. *
* count = taille de cette liste. *
* index = point d'insertion de la première ligne. *
* *
* Description : Ajoute de nouvelles lignes à une position donnée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_code_buffer_insert_lines_at(GCodeBuffer *buffer, GBufferLine **lines, size_t count, size_t index)
{
size_t i; /* Boucle de parcours */
/* Elaboration d'un espace suffisant */
if ((buffer->used + count) > buffer->count)
{
if (count > LINE_ALLOC_BULK)
buffer->count += count;
else
buffer->count += LINE_ALLOC_BULK;
buffer->lines = (GBufferLine **)realloc(buffer->lines,
buffer->count * sizeof(GBufferLine *));
}
/* Insertion des lignes */
if (index < buffer->used)
{
memmove(&buffer->lines[index + count], &buffer->lines[index],
(buffer->used - index) * sizeof(GBufferLine *));
}
buffer->used += count;
for (i = 0; i < count; i++)
{
assert((index + i) < buffer->used);
buffer->lines[index + i] = lines[i];
g_signal_connect(lines[i], "content-changed", G_CALLBACK(on_line_content_change), buffer);
g_signal_connect(lines[i], "flip-flag", G_CALLBACK(on_line_flag_flip), buffer);
}
/* Recueil initial des largeurs */
/**
* Comme des optimisations reposant sur des cas particuliers peuvent être
* réalisées ici, c'est à l'appelant de prendre cette charge de calculs à
* son compte !
*/
}
/******************************************************************************
* *
* Paramètres : buffer = composant GLib à mettre à jour. *
* line = lign à insérer à la fin du tampon. *
* *
* Description : Ajoute une nouvelle ligne en fin de tampon. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_code_buffer_append_new_line(GCodeBuffer *buffer, GBufferLine *line)
{
GBufferLine *manager; /* Ligne de gestion de largeurs*/
g_code_buffer_insert_lines_at(buffer, (GBufferLine *[]) { line }, 1, buffer->used);
/* Recueil initial des largeurs */
if (g_buffer_line_get_flags(line) & BLF_WIDTH_MANAGER)
manager = line;
else
{
assert(buffer->used > 1);
manager = g_buffer_line_get_width_manager(buffer->lines[buffer->used - 2]);
}
g_buffer_line_update_max_widths(line, manager);
}
/******************************************************************************
* *
* Paramètres : buffer = composant GLib à mettre à jour. *
* lines = liste de lignes à insérer. *
* count = taille de cette liste. *
* point = point d'insertion du bloc de ligne. *
* before = emplacement de l'insertion par rapport au point. *
* *
* Description : Ajoute de nouvelles lignes par rapport à une ligne donnée. *
* *
* Retour : Bilan : insertion réussie ou non ? *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_code_buffer_insert_lines(GCodeBuffer *buffer, GBufferLine **lines, size_t count, const GBufferLine *point, bool before)
{
bool result; /* Bilan à retourner */
size_t index; /* Indice d'insertion final */
result = false;
index = g_code_buffer_find_index_by_line(buffer, point);
if (index == buffer->used)
goto gcbil_exit;
if (!before)
index++;
g_code_buffer_insert_lines_at(buffer, lines, count, index);
g_code_buffer_update_line_max_widths(buffer, index, index + count);
result = true;
gcbil_exit:
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GLib à mettre à jour. *
* start = première ligne devant être supprimée. *
* end = dernière ligne devant être supprimée. *
* *
* Description : Supprime une ou plusieurs lignes du tampon indiqué. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_code_buffer_delete_lines(GCodeBuffer *buffer, size_t start, size_t end)
{
size_t i; /* Boucle de parcours */
GBufferLine *line; /* Ligne en cours de traitement*/
assert(start < buffer->used);
assert(end < buffer->used);
for (i = start; i <= end; i++)
{
line = buffer->lines[i];
g_signal_handlers_disconnect_by_func(line, G_CALLBACK(on_line_content_change), buffer);
g_signal_handlers_disconnect_by_func(line, G_CALLBACK(on_line_flag_flip), buffer);
g_object_unref(G_OBJECT(line));
}
memmove(&buffer->lines[start], &buffer->lines[end + 1],
(buffer->used - end - 1) * sizeof(GBufferLine *));
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* addr = adresse où retrouver la ligne recherchée. *
* flags = propriétés à vérifier en tout ou partie. *
* idx = indice de la ligne trouvée ou NULL. [OUT] *
* *
* Description : Retrouve une ligne au sein d'un tampon avec une adresse. *
* *
* Retour : Line retrouvée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_code_buffer_find_line_by_addr(const GCodeBuffer *buffer, const vmpa2t *addr, BufferLineFlags flags, size_t *idx)
{
GBufferLine *result; /* Instance à retourner */
size_t index; /* Indice de la ligne visée */
index = g_code_buffer_get_index_from_address(buffer, addr, true);
if (index == buffer->used)
result = NULL;
else
{
if (idx != NULL)
*idx = index;
result = buffer->lines[index];
if (flags != BLF_NONE)
while ((g_buffer_line_get_flags(result) & flags) != flags)
{
if ((index + 1) == buffer->used) break;
/* FIXME : vérifier que l'adresse est toujours celle recherchée ! */
if (idx != NULL)
(*idx)++;
result = buffer->lines[++index];
}
g_object_ref(G_OBJECT(result));
}
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* index = indice de la ligne recherchée. *
* *
* Description : Retrouve une ligne au sein d'un tampon avec un indice. *
* *
* Retour : Line retrouvée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_code_buffer_find_line_by_index(const GCodeBuffer *buffer, size_t index)
{
GBufferLine *result; /* Ligne trouvée à retourner */
if (index < buffer->used)
result = buffer->lines[index];
else
result = NULL;
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes à consulter. *
* line = ligne dont l'indice est à retrouver. *
* *
* Description : Retrouve l'indice associé à une ligne au sein d'un tampon. *
* *
* Retour : Indice de l'adresse trouvée, ou le nombre de lignes sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_code_buffer_find_index_by_line(const GCodeBuffer *buffer, const GBufferLine *line)
{
size_t result; /* Indice trouvé à retourner */
const mrange_t *range; /* Emplacement de la ligne */
const mrange_t *next; /* Emplacement suivant */
range = g_buffer_line_get_range(line);
result = g_code_buffer_get_index_from_address(buffer, get_mrange_addr(range), true);
/**
* Comme plusieurs lignes consécutives peuvent avoir la même adresse,
* on parcourt les lignes suivantes pour retrouver la ligne recherchée.
*/
if (result < buffer->used)
{
while (buffer->lines[result] != line)
{
if (++result == buffer->used)
break;
next = g_buffer_line_get_range(buffer->lines[result]);
if (cmp_vmpa(get_mrange_addr(range), get_mrange_addr(next)) != 0)
{
result = buffer->used;
break;
}
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* *
* Description : Augmente l'indentation des prochaines lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_code_buffer_inc_indentation(GCodeBuffer *buffer)
{
buffer->indent++;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* *
* Description : Diminue l'indentation des prochaines lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_code_buffer_dec_indentation(GCodeBuffer *buffer)
{
/* BUG_ON(buffer->indent == 0) */
buffer->indent--;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de données à utiliser. *
* start = première adresse visée ou 0. *
* end = dernière adresse visée ou VMPA_MAX. *
* message = message à afficher lors de la progression. *
* process = fonction assurant le traitement effectif. *
* data = données utilisateur à faire suivre. *
* *
* Description : Lance un parcours des différentes lignes du tampon de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
GDelayedWork *g_buffer_code_scan(GCodeBuffer *buffer, const vmpa2t *start, const vmpa2t *end, const char *message, process_line_fc process, void *data)
{
GBufferScan *result; /* Procédure à créer / renvoyer*/
GWorkQueue *queue; /* Gestionnaire de différés */
result = g_buffer_scan_new(buffer, start, end, message, process, data);
g_object_ref(G_OBJECT(result));
queue = get_work_queue();
g_work_queue_schedule_work(queue, G_DELAYED_WORK(result), DEFAULT_WORK_GROUP);
return G_DELAYED_WORK(result);
}
/* ---------------------------------------------------------------------------------- */
/* VUE PARTICULIERE D'UN TAMPON DE CODE */
/* ---------------------------------------------------------------------------------- */
/* Détermine le type de la vue d'un tampon pour code désassemblé. */
G_DEFINE_TYPE(GBufferView, g_buffer_view, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe de vue de tampon. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_class_init(GBufferViewClass *class)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(class);
object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_view_dispose;
object->finalize = (GObjectFinalizeFunc)g_buffer_view_finalize;
g_signal_new("need-redraw",
G_TYPE_BUFFER_VIEW,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBufferViewClass, need_redraw),
NULL, NULL,
g_cclosure_user_marshal_VOID__OBJECT,
G_TYPE_NONE, 0);
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une vue d'un tampon pour code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_init(GBufferView *buffer)
{
buffer->first_index = 0;
g_buffer_view_reset_required_height(buffer);
g_buffer_view_reset_required_widths(buffer);
}
/******************************************************************************
* *
* Paramètres : view = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_dispose(GBufferView *view)
{
g_object_unref(G_OBJECT(view->buffer));
G_OBJECT_CLASS(g_buffer_view_parent_class)->dispose(G_OBJECT(view));
}
/******************************************************************************
* *
* Paramètres : view = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_finalize(GBufferView *view)
{
if (!view->external)
exit_segment_content_list(view->highlighted);
G_OBJECT_CLASS(g_buffer_view_parent_class)->finalize(G_OBJECT(view));
}
/******************************************************************************
* *
* Paramètres : buffer = tampon à représenter à l'écran. *
* widget = composant GTK de destination pour le rendu. *
* *
* Description : Crée une nouvelle vue d'un tampon pour code désassemblé. *
* *
* Retour : Composant GTK créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferView *g_buffer_view_new(GCodeBuffer *buffer, segcnt_list *highlighted)
{
GBufferView *result; /* Composant à retourner */
result = g_object_new(G_TYPE_BUFFER_VIEW, NULL);
g_object_ref(G_OBJECT(buffer));
result->buffer = buffer;
g_buffer_view_restrict(result, NULL, NULL);
g_signal_connect(buffer, "line-changed", G_CALLBACK(on_buffer_line_changed), result);
if (highlighted != NULL)
result->highlighted = highlighted;
else
result->highlighted = init_segment_content_list();
result->external = (highlighted != NULL);
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de lignes cohérentes à manipuler. *
* line = ligne dont la définition vient d'évoluer. *
* segment = éventuel segment qui vient d'évoluer ou NULL. *
* view = vue active du tampon de lignes concerné. *
* *
* Description : Réagit à un changement de contenu d'une ligne donnée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void on_buffer_line_changed(GCodeBuffer *buffer, GBufferLine *line, GBufferSegment *segment, GBufferView *view)
{
/* TODO : regarder si la vue et concernée et cibler d'avantage l'actualisation */
g_signal_emit_by_name(view, "need-redraw");
}
/******************************************************************************
* *
* Paramètres : view = visualisateur à mettre à jour. *
* first = première ligne à imprimer. *
* last = première ligne hors cadre. *
* *
* Description : Restreint le champ d'application de l'affichage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_restrict(GBufferView *view, const vmpa2t *start, const vmpa2t *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);
}
/******************************************************************************
* *
* Paramètres : view = visualisateur à mettre à jour. *
* first = première ligne à imprimer ou NULL. [OUT] *
* last = première ligne hors cadre ou NULL. [OUT] *
* *
* Description : Indique le champ d'application de l'affichage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
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);
}
/******************************************************************************
* *
* Paramètres : view = visualisateur à consulter. *
* *
* Description : Fournit le tampon de code lié à un visualisateur donné. *
* *
* Retour : Tampon de code associé au gestionnaire d'affichage. *
* *
* Remarques : - *
* *
******************************************************************************/
GCodeBuffer *g_buffer_view_get_buffer(const GBufferView *view)
{
return view->buffer;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* *
* Description : Réinitialise le cache de la hauteur des lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_reset_required_height(GBufferView *view)
{
view->line_height = -1;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* *
* Description : Réinitialise le cache des largeurs de colonne calculées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_reset_required_widths(GBufferView *view)
{
unsigned int i; /* Boucle de parcours */
for (i = 0; i < BLC_COUNT; i++)
view->max_widths[i] = -1;
view->merged_width = 0;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à mettre à jour. *
* *
* Description : Calcule la hauteur requise par une visualisation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_compute_required_height(GBufferView *view)
{
view->line_height = 17;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à mettre à jour. *
* display = règles d'affichage des colonnes modulables. *
* *
* Description : Calcule les largeurs requises par une visualisation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_compute_required_widths(GBufferView *view, const bool *display)
{
GBufferLine **lines; /* Liste des lignes à traiter */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
size_t i; /* Boucle de parcours */
if (!HEIGHT_CACHED(view))
g_buffer_view_compute_required_height(view);
lines = view->buffer->lines;
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);
view->left_margin = 2 * view->line_height;
view->left_text = 2.5 * view->line_height;
if (view->buffer->used > 0)
{
//g_code_buffer_update_line_max_widths(view->buffer, first, last);
for (i = first; i <= last; i++)
g_buffer_line_apply_max_widths(lines[i], view->max_widths, &view->merged_width);
}
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* *
* Description : Fournit la hauteur d'impression d'une ligne visualisée. *
* *
* Retour : Hauteur de ligne en pixel. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_buffer_view_get_line_height(GBufferView *view)
{
if (!HEIGHT_CACHED(view))
g_buffer_view_compute_required_height(view);
return view->line_height;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* display = règles d'affichage des colonnes modulables. *
* *
* Description : Fournit la largeur requise par une visualisation. *
* *
* Retour : Dimension calculée. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_buffer_view_get_width(GBufferView *view, const bool *display)
{
gint result; /* Taille à retourner */
gint col_width; /* Calcul selon les colonnes */
gint full_width; /* Calcul selon les fusions */
BufferLineColumn i; /* Boucle de parcours */
if (!WIDTHS_CACHED(view))
g_buffer_view_compute_required_widths(view, display);
result = view->left_text;
col_width = 0;
full_width = 0;
/* Première méthode */
for (i = 0; i < BLC_COUNT; i++)
{
if (i < BLC_DISPLAY && !display[i]) continue;
col_width += view->max_widths[i];
if ((i + 1) < BLC_COUNT)
col_width += COL_MARGIN;
}
/* Seconde méthode */
for (i = 0; i < BLC_DISPLAY; i++)
{
if (!display[i]) continue;
full_width += view->max_widths[i] + COL_MARGIN;
}
full_width += view->merged_width;
/* Mise en concurrence et poursuite... */
result += + MAX(col_width, full_width);
return result;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* display = règles d'affichage des colonnes modulables. *
* *
* Description : Fournit la largeur requise pour dépasser les marges gauches. *
* *
* Retour : Dimension calculée. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_buffer_view_get_margin(GBufferView *view, const bool *display)
{
gint result; /* Taille à retourner */
BufferLineColumn i; /* Boucle de parcours */
if (!WIDTHS_CACHED(view))
g_buffer_view_compute_required_widths(view, display);
result = view->left_text;
for (i = 0; i < BLC_DISPLAY; i++)
{
if (!display[i]) continue;
result += view->max_widths[i] + COL_MARGIN;
}
return result;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* *
* Description : Fournit la hauteur requise par une visualisation. *
* *
* Retour : Dimension calculée. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_buffer_view_get_height(const GBufferView *view)
{
gint result; /* Taille à retourner */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
result = view->line_height;
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);
result *= (last - first + 1);
return result;
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* x = abscisse de la zone principale à traiter. *
* y = ordonnée de la zone principale à traiter. *
* display = règles d'affichage des colonnes modulables. *
* caret = position du curseur à construire. [OUT] *
* *
* Description : Calcule la position idéale de curseur pour un point donné. *
* *
* Retour : Adresse si une a pu être déterminée, NULL sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
const vmpa2t *g_buffer_view_compute_caret(GBufferView *view, gint x, gint y, const bool *display, GdkRectangle *caret)
{
gint remaining; /* Copie de travail modifiable */
size_t index; /* Indice de ligne de tampon */
GBufferLine *line; /* Ligne à la position courante*/
GBufferSegment *segment; /* Segment présent sur la place*/
remaining = x;
line = g_buffer_view_find_line_and_segment_at(view, &remaining, y, &index, display, &segment);
if (line == NULL) return NULL;
if (segment == NULL) printf(" -- no segment\n");
if (segment == NULL) return NULL;
printf("\n[BASE] orig = %d tronc = %d reste = %d dernier = %d largeur = %d\n",
x, x - remaining, remaining, g_buffer_segment_get_caret_position(segment, remaining),
g_buffer_segment_get_width(segment));
printf(" '%s'\n", g_buffer_segment_get_text(segment, false));
caret->x = /*view->left_text +*/ (x - remaining) + g_buffer_segment_get_caret_position(segment, remaining);
caret->y = (index - view->first_index) * view->line_height;
caret->width = 2;
caret->height = view->line_height;
return get_mrange_addr(g_buffer_line_get_range(line));
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* line = ligne correspondant à la position. *
* index = indice de cette même ligne dans le tampon. *
* x = abscisse de la zone principale à traiter. *
* display = règles d'affichage des colonnes modulables. *
* caret = position du curseur à construire. [OUT] *
* *
* Description : Calcule la position idéale de curseur pour un point donné. *
* *
* Retour : Adresse si une a pu être déterminée, NULL sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
const vmpa2t *g_buffer_view_compute_caret_full(GBufferView *view, GBufferLine *line, size_t index, gint x, const bool *display, GdkRectangle *caret)
{
gint offset; /* Point de travail modifiable */
gint base; /* Position absolue de segment */
GBufferSegment *segment; /* Segment visé par le pointeur*/
offset = x;
offset -= view->left_text;
if (offset < 0) return NULL;
segment = g_buffer_line_get_segment_at(line, view->max_widths, display, &base, &offset, GDK_SCROLL_LEFT, true);
if (segment == NULL) return NULL;
caret->x = view->left_text + base + offset;
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;
return get_mrange_addr(g_buffer_line_get_range(line));
}
/******************************************************************************
* *
* Paramètres : line = ligne à venir consulter. *
* caret = position du curseur à faire évoluer. *
* ctrl = indique la demande d'un parcours rapide. *
* dir = direction du parcours. *
* display = règles d'affichage des colonnes modulables. *
* *
* Description : Déplace le curseur au sein d'une vue de tampon. *
* *
* Retour : true si un déplacement a été effectué, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool _g_buffer_view_move_caret(GBufferView *view, const GBufferLine *line, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, const bool *display)
{
bool result; /* Bilan à retourner */
gint offset; /* Point de travail modifiable */
gint base; /* Position absolue de segment */
GBufferSegment *segment; /* Segment visé par le pointeur*/
offset = caret->x;
offset -= view->left_text;
if (offset < 0) return false;
segment = g_buffer_line_get_segment_at(line, view->max_widths, display, &base, &offset, dir, false);
if (segment == NULL) printf(" ===== NO SEG...\n");
if (segment == NULL) return false;
printf(" ====== FIRST SEG :: %p ('%s')\n", segment, g_buffer_segment_get_text(segment, false));
//if (dir == GDK_SCROLL_LEFT || dir == GDK_SCROLL_RIGHT)
result = g_buffer_segment_move_caret(segment, &offset, ctrl, dir);
//else
//result = true;
printf(" ====== MOVE 1 ? %d\n", result);
///////////////////
if (!result)
{
base = 0;
segment = g_buffer_line_find_near_segment(line, segment, view->max_widths, display, dir, &offset);
printf(" ====== NEAR SEG :: %p ('%s')\n", segment, segment ? g_buffer_segment_get_text(segment, false) : NULL);
if (segment != NULL)
{
result = true;
//result = g_buffer_segment_move_caret(segment, &offset, ctrl, dir);
/*
if (result)
caret->x -= COL_MARGIN;
*/
printf(" ====== MOVE 2 ? %d (offset=%d)\n", result, offset);
}
}
if (result)
printf(" ====== NEW CARET: %d -> %d\n", caret->x, view->left_text + base + offset);
else
printf(" ====== NO NEW CARET!\n");
if (result)
caret->x = view->left_text + base + offset;
return result;
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* caret = position du curseur à faire évoluer. *
* ctrl = indique la demande d'un parcours rapide. *
* dir = direction du parcours. *
* display = règles d'affichage des colonnes modulables. *
* *
* Description : Déplace le curseur au sein d'une vue de tampon. *
* *
* Retour : Adresse si une a pu être déterminée, VMPA_INVALID sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
const vmpa2t *g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, const bool *display)
{
const vmpa2t *result; /* Actualisation à renvoyer */
size_t index; /* Indice de ligne de tampon */
GBufferLine *line; /* Ligne sous le pointeur */
bool computed; /* Récursivité pris en compte */
gint lheight; /* Hauteur d'une ligne */
gint left_pos; /* Retour à la ligne */
gint right_pos; /* Position d'extrème droite */
BufferLineColumn i; /* Boucle de parcours */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
bool moved; /* Mémorisation d'une évolut° */
result = NULL;
computed = false;
line = g_buffer_view_find_line_at(view, caret->y, &index);
if (line == NULL) return NULL;
lheight = g_buffer_view_get_line_height(view);
switch (dir)
{
case GDK_SCROLL_UP:
case GDK_SCROLL_DOWN:
lheight = g_buffer_view_get_line_height(view);
break;
case GDK_SCROLL_LEFT:
case GDK_SCROLL_RIGHT:
left_pos = view->left_text;
if (display[BLC_PHYSICAL]) left_pos += view->max_widths[BLC_PHYSICAL] + COL_MARGIN;
if (display[BLC_VIRTUAL]) left_pos += view->max_widths[BLC_VIRTUAL] + COL_MARGIN;
if (display[BLC_BINARY]) left_pos += view->max_widths[BLC_BINARY] + COL_MARGIN;
right_pos = left_pos;
for (i = BLC_ASSEMBLY_HEAD; i < BLC_COUNT; i++)
right_pos += view->max_widths[i] + COL_MARGIN;
/*
gint g_buffer_line_compute_max_width(const GBufferLine *line, BufferLineColumn index, const gint *max_widths)
BufferLineColumn g_buffer_line_get_merge_start(const GBufferLine *line)
*/
left_pos = view->left_text;
break;
default: /* GDK_SCROLL_SMOOTH */
break;
}
first = view->first_index;
last = view->last_index;
switch (dir)
{
case GDK_SCROLL_UP:
if (index > first)
{
line = view->buffer->lines[index - 1];
result = g_buffer_view_compute_caret_full(view, line, index - 1, caret->x, display, caret);
}
break;
case GDK_SCROLL_DOWN:
if (index < last)
{
line = view->buffer->lines[index + 1];
result = g_buffer_view_compute_caret_full(view, line, index + 1, caret->x, display, caret);
}
break;
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)
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);
}
break;
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)
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);
}
break;
default: /* GDK_SCROLL_SMOOTH */
break;
}
/*
printf(" --- CARET --- moved = %d index = %d result = %p\n",
moved, index, result);
*/
/*
if (result && !computed)
result = g_buffer_view_compute_caret_full(view, caret->x, caret->y, caret, display, NULL);
*/
return result;
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* *
* Description : Supprime toute mise en évidence de segments. *
* *
* Retour : true si un besoin d'actualisation d'affichage se fait sentir.*
* *
* Remarques : - *
* *
******************************************************************************/
bool g_buffer_view_unhighlight_segments(GBufferView *view)
{
bool result; /* Bilan d'action à renvoyer */
result = reset_segment_content_list(view->highlighted);
return result;
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* x = abscisse de la zone principale à traiter. *
* y = ordonnée de la zone principale à traiter. *
* display = règles d'affichage des colonnes modulables. *
* *
* Description : Surligne tous les segments similaires à celui sous la souris.*
* *
* Retour : true si un besoin d'actualisation d'affichage se fait sentir.*
* *
* Remarques : - *
* *
******************************************************************************/
bool g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y, const bool *display)
{
bool need_redraw; /* Besoin d'actualisation ? */
GBufferSegment *segment; /* Segment sélectionnable */
if (view->highlighted != NULL)
need_redraw = g_buffer_view_unhighlight_segments(view);
else
need_redraw = false;
g_buffer_view_find_line_and_segment_at(view, &x, y, NULL, display, &segment);
if (segment)
need_redraw |= add_segment_content_to_selection_list(view->highlighted, segment);
if (need_redraw)
g_signal_emit_by_name(view, "need-redraw");
return true;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à représenter. *
* cr = contexte graphique dédié à la procédure. *
* fake_x = abscisse réelle du point 0 à l'écran. *
* fake_y = ordonnée réelle du point 0 à l'écran. *
* area = position et surface à traiter. *
* display = règles d'affichage des colonnes modulables. *
* selected = ordonnée d'une ligne sélectionnée ou NULL. *
* *
* Description : Imprime la visualisation du tampon de code désassemblé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_draw(const GBufferView *view, cairo_t *cr, gint fake_x, gint fake_y, const cairo_rectangle_int_t *area, const bool *display, const gint *selected)
{
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 last; /* Dernière ligne visée + 1 */
gint y; /* Point de départ + décallage */
GBufferLine **lines; /* Liste des lignes à traiter */
bool wait_selection; /* Sélection déjà passée ? */
gint rel_selected; /* Position relative de sélect°*/
size_t i; /* Boucle de parcours */
real_x = fake_x + view->left_text;
real_y = fake_y + area->y;
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++;
last = MIN(last, view->last_index);
y = area->y - (real_y % view->line_height);
lines = view->buffer->lines;
wait_selection = true;
if (selected != NULL)
rel_selected = *selected - fake_y;
if (view->buffer->used > 0)
for (i = first; i <= last; i++)
{
/* Si sélection, on sousligne la ligne concernée */
if (wait_selection && selected != NULL && rel_selected == y)
{
cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.05);
cairo_rectangle(cr, area->x, y, area->width, view->line_height);
cairo_fill(cr);
wait_selection = false;
}
g_buffer_line_draw(lines[i], cr, view->max_widths, real_x, y, display, view->highlighted);
y += view->line_height;
}
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* addr = adresse où retrouver la ligne recherchée. *
* flags = propriétés à vérifier en tout ou partie. *
* idx = indice de la ligne trouvée ou NULL. [OUT] *
* *
* Description : Retrouve une ligne au sein d'un tampon avec une adresse. *
* *
* Retour : Line retrouvée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_buffer_view_find_line_by_addr(const GBufferView *view, const vmpa2t *addr, BufferLineFlags flags, size_t *idx)
{
GBufferLine *result; /* Ligne trouvée à retourner */
phys_t length; /* Taille de la vue */
mrange_t vrange; /* Couverture de la vue */
bool allowed; /* Rechercher validée ? */
/* Vérification des bornes */
if (view->start != NULL/* && view->end != NULL*/)
{
length = compute_vmpa_diff(view->start, view->end);
init_mrange(&vrange, view->start, length);
allowed = mrange_contains_addr_inclusive(&vrange, addr);
}
else allowed = true;
/* Lancement des recherches ? */
if (allowed)
result = g_code_buffer_find_line_by_addr(view->buffer, addr, flags, idx);
else
result = NULL;
return result;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* index = indice de la ligne recherchée. *
* *
* Description : Retrouve une ligne au sein d'un tampon avec un indice. *
* *
* Retour : Line retrouvée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_buffer_view_find_line_by_index(const GBufferView *view, size_t index)
{
GBufferLine *result; /* Ligne trouvée à retourner */
bool allowed; /* Rechercher validée ? */
/* Vérification des bornes */
allowed = (view->first_index <= index && index < view->last_index);
/* Lancement des recherches ? */
if (allowed)
result = view->buffer->lines[index];
else
result = NULL;
return result;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* y = ordonnée comprise dans la ligne recherchée. *
* idx = indice de la ligne trouvée ou NULL. [OUT] *
* *
* Description : Fournit la ligne présente à une ordonnée donnée. *
* *
* Retour : Ligne retrouvée ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_buffer_view_find_line_at(GBufferView *view, gint y, size_t *idx)
{
gint lheight; /* Hauteur d'une ligne */
size_t index; /* Indice attendu */
lheight = g_buffer_view_get_line_height(view);
index = y / lheight;
index += view->first_index;
if (idx != NULL)
*idx = index;
return (index < view->buffer->used ? view->buffer->lines[index] : NULL);
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* x = abscisse comprise dans le segment recherché. [OUT] *
* y = ordonnée comprise dans la ligne recherchée. *
* idx = indice de la ligne trouvée ou NULL. [OUT] *
* display = règles d'affichage des colonnes modulables. *
* segment = portion de texte recherchée ou NULL. [OUT] *
* *
* Description : Fournit la ligne et son segment présents à une position. *
* *
* Retour : Ligne retrouvée ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_buffer_view_find_line_and_segment_at(GBufferView *view, gint *x, gint y, size_t *idx, const bool *display, GBufferSegment **segment)
{
GBufferLine *result; /* Ligne trouvée à retourner */
/* Recherche d'une ligne correspondante */
result = g_buffer_view_find_line_at(view, y, idx);
/* Recherche du segment visé éventuel */
if (result != NULL && segment != NULL)
{
if (*x < view->left_text)
*segment = NULL;
else
{
*x -= view->left_text;
*segment = g_buffer_line_get_segment_at(result, view->max_widths, display,
(gint []) { 0 }, x, GDK_SCROLL_LEFT, true);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à consulter. *
* addr = adresse à présenter à l'écran. *
* x = position horizontale au sein du composant. [OUT] *
* y = position verticale au sein du composant. [OUT] *
* code = s'arrête si possible à une ligne avec code. *
* *
* Description : Indique la position d'affichage d'une adresse donnée. *
* *
* Retour : true si l'adresse fait partie du composant, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_buffer_view_get_address_coordinates(GBufferView *view, const vmpa2t *addr, gint *x, gint *y, bool code)
{
bool result; /* Bilan à retourner */
gint lheight; /* Hauteur d'une ligne */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
size_t i; /* Boucle de parcours */
const mrange_t *range; /* Emplacement parcouru */
result = false;
*x = 0;
*y = 0;
lheight = g_buffer_view_get_line_height(view);
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);
for (i = first; i <= last; i++)
{
/**
* Si l'adresse recherchée est plus petite que l'adresse de départ,
* on va effectuer un parcours complet pour rien.
*
* On considère cependant que le seul cas où celà peut arriver
* est lorsque que des découpages en blocs sont impliqués.
*
* Les découpages conduisent alors à la formation de petites zones,
* rapides à parcourir.
*/
range = g_buffer_line_get_range(view->buffer->lines[i]);
result = mrange_contains_addr(range, addr);
if (result) break;
*y += lheight;
}
if (result && code)
for (; i <= last; i++)
{
if (g_buffer_line_get_flags(view->buffer->lines[i]) & BLF_HAS_CODE) break;
if (i == last) break;
range = g_buffer_line_get_range(view->buffer->lines[i + 1]);
if (!mrange_contains_addr(range, addr)) break;
*y += lheight;
}
return result;
}