/* Chrysalide - Outil d'analyse de fichiers binaires
* gbuffersegment.c - concentration d'un fragment de caractères aux propriétés communes
*
* 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 "gbuffersegment.h"
#include
#include
#include
#include "../common/extstr.h"
#include "../common/fnv1a.h"
#include "../gtkext/gtkblockview.h"
#include "../gtkext/support.h"
/* Propriétés de rendu */
typedef struct _rendering_color_t
{
GdkRGBA color; /* Couleur de rendu */
bool has_color; /* Définition en place ? */
} rendering_color_t;
typedef struct _rendering_pattern_t
{
rendering_color_t foreground; /* Couleur d'impression */
cairo_font_slant_t slant; /* Style d'impression */
cairo_font_weight_t weight; /* Poids de la police */
} rendering_pattern_t;
/* Nom des éléments CSS */
#define SEGMENT_NAME(s) "segment-" s
static const char *_segment_names[RTT_COUNT] = {
[RTT_RAW] = SEGMENT_NAME("raw"),
[RTT_COMMENT] = SEGMENT_NAME("comment"),
[RTT_INDICATION] = SEGMENT_NAME("indication"),
[RTT_PHYS_ADDR_PAD] = SEGMENT_NAME("phys-addr-padding"),
[RTT_PHYS_ADDR] = SEGMENT_NAME("phys-addr"),
[RTT_VIRT_ADDR_PAD] = SEGMENT_NAME("virt-addr-padding"),
[RTT_VIRT_ADDR] = SEGMENT_NAME("virt-addr"),
[RTT_RAW_CODE] = SEGMENT_NAME("raw-code"),
[RTT_LABEL] = SEGMENT_NAME("label"),
[RTT_INSTRUCTION] = SEGMENT_NAME("instruction"),
[RTT_IMMEDIATE] = SEGMENT_NAME("immediate"),
[RTT_REGISTER] = SEGMENT_NAME("register"),
[RTT_PUNCT] = SEGMENT_NAME("punct"),
[RTT_HOOK] = SEGMENT_NAME("hooks"),
[RTT_SIGNS] = SEGMENT_NAME("signs"),
[RTT_LTGT] = SEGMENT_NAME("ltgt"),
[RTT_SECTION] = SEGMENT_NAME("section"),
[RTT_SEGMENT] = SEGMENT_NAME("segment"),
[RTT_STRING] = SEGMENT_NAME("string"),
[RTT_VAR_NAME] = SEGMENT_NAME("var-name"),
[RTT_KEY_WORD] = SEGMENT_NAME("keyword"),
[RTT_ERROR] = SEGMENT_NAME("error"),
};
/* Compléments à Cairo */
#define CAIRO_FONT_SLANT_COUNT 3
#define CAIRO_FONT_WEIGHT_COUNT 2
#define CAIRO_FONTS_COUNT (CAIRO_FONT_SLANT_COUNT * CAIRO_FONT_WEIGHT_COUNT)
#define CAIRO_FONT_INDEX(s, w) ((s) + (w) * CAIRO_FONT_WEIGHT_COUNT)
/* Fragment de caractères aux propriétés communes (instance) */
struct _GBufferSegment
{
GObject parent; /* A laisser en premier */
char *text; /* Texte brut conservé */
fnv64_t hash; /* Empreinte pour comparaisons */
rendering_pattern_t *pattern; /* Propriétés du rendu */
SegRenderingStyle style; /* Apparence du segment */
rendering_color_t cache_bg; /* Fond d'impression */
rendering_color_t alt_fg; /* Couleur d'impression bis */
const rendering_color_t *used_fg; /* Couleur d'impression utile */
gint x_advance; /* Dimensions du texte */
};
/* Fragment de caractères aux propriétés communes (classe) */
struct _GBufferSegmentClass
{
GObjectClass parent; /* A laisser en premier */
cairo_t *font_ctxts[CAIRO_FONTS_COUNT]; /* Contextes de police */
double x_advances[CAIRO_FONTS_COUNT]; /* Largeurs par caractère */
rendering_pattern_t patterns[RTT_COUNT];/* Modèles d'impression */
};
/* Procède à l'initialisation d'une classe de fragment de texte. */
static void g_buffer_segment_class_init(GBufferSegmentClass *);
/* Procède à l'initialisation d'un fragment de texte. */
static void g_buffer_segment_init(GBufferSegment *);
/* Définit les dernières propriétés de rendu restantes. */
static void g_buffer_segment_prepare(GBufferSegment *, size_t);
/* Détermine le type du fragment de caractères aux propriétés communes. */
G_DEFINE_TYPE(GBufferSegment, g_buffer_segment, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe de fragment de texte.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_segment_class_init(GBufferSegmentClass *class)
{
GtkStyleContext *context; /* Contexte pour les styles */
GtkWidgetPath *path; /* Chemin d'accès aux thèmes */
gchar *filename; /* Accès à une image 1x1 */
cairo_font_slant_t s; /* Boucle de parcours #1 */
cairo_font_weight_t w; /* Boucle de parcours #2 */
cairo_t **cr; /* Contexte à créer */
cairo_surface_t *surface; /* Surface pour dessin Cairo */
cairo_text_extents_t extents; /* Couverture des caractères */
RenderingTagType i; /* Boucle de parcours */
/* Création d'un contexte d'accès */
path = gtk_widget_path_new();
gtk_widget_path_append_type(path, G_TYPE_OBJECT);
context = gtk_style_context_new();
gtk_style_context_set_path(context, path);
gtk_style_context_set_screen(context, gdk_screen_get_default());
/* Contextes pour les mesures initiales */
filename = find_pixmap_file("nil.png");
if (filename == NULL) abort();
for (s = CAIRO_FONT_SLANT_NORMAL; s < CAIRO_FONT_SLANT_COUNT; s++)
for (w = CAIRO_FONT_WEIGHT_NORMAL; w < CAIRO_FONT_WEIGHT_COUNT; w++)
{
cr = &class->font_ctxts[CAIRO_FONT_INDEX(s, w)];
surface = cairo_image_surface_create_from_png(filename);
*cr = cairo_create(surface);
cairo_surface_destroy(surface);
cairo_select_font_face(*cr, "mono", s, w);
cairo_set_font_size(*cr, 13);
cairo_text_extents(*cr, "A", &extents);
class->x_advances[CAIRO_FONT_INDEX(s, w)] = extents.x_advance;
}
g_free(filename);
/* Chargement des définitions utiles */
void define_rendering_pattern(GtkStyleContext *ctx, const char *name, rendering_pattern_t *pattern)
{
GdkRGBA *tmp_color; /* Description d'une couleur */
PangoFontDescription *font_desc; /* Description d'une police */
gtk_style_context_save(ctx);
gtk_style_context_add_class(context, name);
gtk_style_context_get(ctx, GTK_STATE_NORMAL, GTK_STYLE_PROPERTY_COLOR, &tmp_color, NULL);
pattern->foreground.has_color = true;
pattern->foreground.color = *tmp_color;
gdk_rgba_free(tmp_color);
gtk_style_context_get(context, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_FONT, &font_desc, NULL);
switch (pango_font_description_get_style(font_desc))
{
case PANGO_STYLE_NORMAL:
pattern->slant = CAIRO_FONT_SLANT_NORMAL;
break;
case PANGO_STYLE_ITALIC:
pattern->slant = CAIRO_FONT_SLANT_ITALIC;
break;
case PANGO_STYLE_OBLIQUE:
pattern->slant = CAIRO_FONT_SLANT_OBLIQUE;
break;
}
switch (pango_font_description_get_weight(font_desc))
{
case PANGO_WEIGHT_THIN:
case PANGO_WEIGHT_ULTRALIGHT:
case PANGO_WEIGHT_LIGHT:
case PANGO_WEIGHT_SEMILIGHT:
case PANGO_WEIGHT_BOOK:
case PANGO_WEIGHT_NORMAL:
case PANGO_WEIGHT_MEDIUM:
pattern->weight = CAIRO_FONT_WEIGHT_NORMAL;
break;
case PANGO_WEIGHT_SEMIBOLD:
case PANGO_WEIGHT_BOLD:
case PANGO_WEIGHT_ULTRABOLD:
case PANGO_WEIGHT_HEAVY:
case PANGO_WEIGHT_ULTRAHEAVY:
pattern->weight = CAIRO_FONT_WEIGHT_BOLD;
break;
}
pango_font_description_free(font_desc);
gtk_style_context_restore(context);
}
for (i = 0; i < RTT_COUNT; i++)
define_rendering_pattern(context, _segment_names[i], &class->patterns[i]);
/* Nettoyages finaux... */
gtk_widget_path_free(path);
g_object_unref(context);
}
/******************************************************************************
* *
* Paramètres : segment = composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'un fragment de texte. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_segment_init(GBufferSegment *segment)
{
}
/******************************************************************************
* *
* Paramètres : type = propriétés de la zone de texte. *
* text = chaîne de caractères à traiter. *
* length = quantité de ces caractères. *
* *
* Description : Crée un nouveau fragment de texte avec des propriétés. *
* *
* Retour : Composant GTK créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferSegment *g_buffer_segment_new(RenderingTagType type, const char *text, size_t length)
{
GBufferSegment *result; /* Composant à retourner */
GBufferSegmentClass *class; /* Stockage de styles préparés */
result = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL);
result->text = strndup(text, length);
result->hash = fnv_64a_hash(text);
class = G_BUFFER_SEGMENT_GET_CLASS(result);
result->pattern = &class->patterns[type];
g_buffer_segment_prepare(result, length);
g_buffer_segment_set_style(result, SRS_CLASSIC);
return result;
}
/******************************************************************************
* *
* Paramètres : segment = instance de segment à affiner. *
* text = chaîne de caractères à traiter. *
* *
* Description : Définit les dernières propriétés de rendu restantes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_segment_prepare(GBufferSegment *segment, size_t length)
{
GBufferSegmentClass *class; /* Classe associée à l'instance*/
cairo_font_slant_t slant; /* Style d'impression */
cairo_font_weight_t weight; /* Poids de la police */
/* Taille */
class = G_BUFFER_SEGMENT_GET_CLASS(segment);
slant = segment->pattern->slant;
weight = segment->pattern->weight;
segment->x_advance = class->x_advances[CAIRO_FONT_INDEX(slant, weight)] * length;
/* Couleurs */
segment->alt_fg.color.red = 1.0 - segment->pattern->foreground.color.red;
segment->alt_fg.color.green = 1.0 - segment->pattern->foreground.color.green;
segment->alt_fg.color.blue = 1.0 - segment->pattern->foreground.color.blue;
segment->alt_fg.has_color = segment->pattern->foreground.has_color;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* ref = segment de référence servant à la comparaison. *
* *
* Description : Indique si les textes de deux segments sont identiques. *
* *
* Retour : Bilan de la comparaison. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_buffer_segment_compare(const GBufferSegment *segment, const GBufferSegment *ref)
{
bool result; /* Bilan à retourner */
result = (cmp_fnv_64a(segment->hash, ref->hash) == 0);
result &= (strcmp(segment->text, ref->text) == 0);
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* markup = indique si le texte doit être décoré ou non. *
* *
* Description : Fournit le texte brut conservé dans le segment. *
* *
* Retour : Texte conservé en interne. *
* *
* Remarques : - *
* *
******************************************************************************/
char *g_buffer_segment_get_text(const GBufferSegment *segment, bool markup)
{
char *result; /* Description à renvoyer */
char color[7]; /* Couleur hexadécimale */
/* Résolution du cas simple */
if (!markup)
return strdup(segment->text);
result = strdup("pattern->foreground.color.red * 255),
(unsigned char)(segment->pattern->foreground.color.green * 255),
(unsigned char)(segment->pattern->foreground.color.blue * 255));
result = stradd(result, color);
result = stradd(result, "\"");
/* Style */
result = stradd(result, "style=\"");
switch (segment->pattern->slant)
{
case CAIRO_FONT_SLANT_NORMAL:
result = stradd(result, "normal");
break;
case CAIRO_FONT_SLANT_ITALIC:
result = stradd(result, "italic");
break;
case CAIRO_FONT_SLANT_OBLIQUE:
result = stradd(result, "oblique");
break;
}
result = stradd(result, "\"");
/* Epaisseur */
result = stradd(result, "weight=\"");
switch (segment->pattern->weight)
{
case CAIRO_FONT_WEIGHT_NORMAL:
result = stradd(result, "normal");
break;
case CAIRO_FONT_WEIGHT_BOLD:
result = stradd(result, "bold");
break;
}
result = stradd(result, "\"");
/* Conclusion */
result = stradd(result, ">");
result = stradd(result, segment->text);
result = stradd(result, "");
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* *
* Description : Fournit la quantité de pixels requise pour l'impression. *
* *
* Retour : Largeur requise par la colonne, en pixel. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_buffer_segment_get_width(const GBufferSegment *segment)
{
return segment->x_advance;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à consulter. *
* x = position horizontale au niveau du segment. *
* *
* Description : Fournit la position idéale pour un marqueur. *
* *
* Retour : Position dans le segment donné. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_buffer_segment_get_caret_position(const GBufferSegment *segment, gint x)
{
gint result; /* Position à retourner */
gint width; /* Largeur du segment */
gint char_width; /* Largeur de police fixe */
width = g_buffer_segment_get_width(segment);
if (x <= 0)
result = 0;
else if (x >= width)
result = width;
else
{
char_width = width / strlen(segment->text);
result = (x / char_width) * char_width;
if ((x % char_width) > (char_width / 2))
result += char_width;
}
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à manipuler. *
* x = position du curseur à faire évoluer. [OUT] *
* ctrl = indique la demande d'un parcours rapide. *
* dir = direction du parcours. *
* *
* Description : Déplace le curseur au sein d'un segment de tampon. *
* *
* Retour : true si un déplacement a été effectué, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_buffer_segment_move_caret(const GBufferSegment *segment, gint *x, bool ctrl, GdkScrollDirection dir)
{
bool result; /* Bilan d'opération à renvoyer*/
gint width; /* Largeur du segment */
gint char_width; /* Largeur de police fixe */
result = false;
width = g_buffer_segment_get_width(segment);
char_width = width / strlen(segment->text);
if (dir == GDK_SCROLL_LEFT)
{
printf(">>>>> left ::: x=%d width=%d char=%d\n", *x, width, char_width);
if (*x > width) *x = width + char_width;
if (*x == 0) goto gbsmc_done;
if (ctrl) *x = 0;
else *x = MAX(0, *x - char_width);
result = true;
}
else if (dir == GDK_SCROLL_RIGHT)
{
printf(">>>>> right ::: x=%d width=%d char=%d\n", *x, width, char_width);
if (*x == width) goto gbsmc_done;
if (ctrl) *x = width;
else *x = MIN(width, *x + char_width);
result = true;
}
gbsmc_done:
printf(">>>>> result ::: %d\n", result);
return result;
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à manipuler. *
* style = style de rendu pour le segment. *
* *
* Description : Module l'apparence finale du composant. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_segment_set_style(GBufferSegment *segment, SegRenderingStyle style)
{
segment->style = style;
switch (style)
{
default:
case SRS_CLASSIC:
segment->used_fg = &segment->pattern->foreground;
break;
case SRS_HIGHLIGHT_SAME:
segment->cache_bg.color.red = 0.5;
segment->cache_bg.color.green = 0.5;
segment->cache_bg.color.blue = 0.5;
segment->used_fg = &segment->alt_fg;
break;
}
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à manipuler. *
* cairo = contexte graphique à utiliser pour les pinceaux. *
* x = abscisse du point d'impression (à maj). [OUT] *
* y = ordonnée du point d'impression. *
* *
* Description : Imprime le fragment de texte représenté. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_segment_draw(GBufferSegment *segment, cairo_t *cairo, gint *x, gint y)
{
cairo_font_extents_t extents;
/* Fond du texte */
if (segment->style != SRS_CLASSIC)
{
cairo_set_source_rgb(cairo,
segment->cache_bg.color.red,
segment->cache_bg.color.green,
segment->cache_bg.color.blue);
cairo_rectangle(cairo, *x, y, segment->x_advance, 17);
cairo_set_operator(cairo, CAIRO_OPERATOR_DIFFERENCE);
cairo_fill(cairo);
cairo_set_operator(cairo, CAIRO_OPERATOR_OVER);
}
/* Couleur d'impression */
if (segment->used_fg->has_color)
cairo_set_source_rgb(cairo,
segment->used_fg->color.red,
segment->used_fg->color.green,
segment->used_fg->color.blue);
else
cairo_set_source_rgb(cairo, 0, 0, 0);
/* Impression du texte */
cairo_select_font_face(cairo, "mono", segment->pattern->slant, segment->pattern->weight);
cairo_set_font_size(cairo, 13);
cairo_move_to(cairo, *x, y + 17 - 3);
cairo_font_extents(cairo, &extents);
#if 1
if (extents.descent != 3)
printf("FONT : %g, %g\n", extents.ascent, extents.descent);
#endif
cairo_show_text(cairo, segment->text);
//printf(">> %s >> %f %f\n", segment->text, segment->extents.width, segment->extents.x_advance);
*x += segment->x_advance;
}
/******************************************************************************
* *
* Paramètres : ctx = éléments à disposition pour l'exportation. *
* type = type d'exportation attendue. *
* *
* Description : Exporte tous les styles utilisés par des segments. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_segment_export_style(buffer_export_context *ctx, BufferExportType type)
{
GBufferSegment *dummy; /* Segment servant de sujet */
GBufferSegmentClass *class; /* Classe des segments */
size_t i; /* Boucle de parcours */
const rendering_pattern_t *pattern; /* Modèle à transcrire */
dummy = g_object_new(G_TYPE_BUFFER_SEGMENT, NULL);
class = G_BUFFER_SEGMENT_GET_CLASS(dummy);
for (i = 0; i < RTT_COUNT; i++)
{
pattern = &class->patterns[i];
switch (type)
{
case BET_HTML:
dprintf(ctx->fd, ".%s {\n", _segment_names[i]);
if (pattern->foreground.has_color)
dprintf(ctx->fd, "\tcolor: #%02hhx%02hhx%02hhx;\n",
(unsigned char)(pattern->foreground.color.red * 255),
(unsigned char)(pattern->foreground.color.green * 255),
(unsigned char)(pattern->foreground.color.blue * 255));
switch (pattern->slant)
{
case CAIRO_FONT_SLANT_ITALIC:
dprintf(ctx->fd, "\tfont-style: italic;\n");
break;
case CAIRO_FONT_SLANT_OBLIQUE:
dprintf(ctx->fd, "\tfont-style: oblique;\n");
break;
default:
dprintf(ctx->fd, "\tfont-style: normal;\n");
break;
}
switch (pattern->weight)
{
case CAIRO_FONT_WEIGHT_BOLD:
dprintf(ctx->fd, "\tfont-weight: bold;\n");
break;
default:
dprintf(ctx->fd, "\tfont-weight: normal;\n");
break;
}
dprintf(ctx->fd, "}\n");
break;
default:
break;
}
}
g_object_unref(G_OBJECT(dummy));
}
/******************************************************************************
* *
* Paramètres : segment = fragment de texte à manipuler. *
* ctx = éléments à disposition pour l'exportation. *
* type = type d'exportation attendue. *
* *
* Description : Exporte le fragment de texte représenté. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_segment_export(const GBufferSegment *segment, buffer_export_context *ctx, BufferExportType type)
{
GBufferSegmentClass *class; /* Classe des segments */
size_t index; /* Indice du modèle de rendu */
switch (type)
{
case BET_HTML:
class = G_BUFFER_SEGMENT_GET_CLASS(segment);
index = (segment->pattern - class->patterns);
dprintf(ctx->fd, "", _segment_names[index]);
break;
default:
break;
}
dprintf(ctx->fd, "%s", segment->text);
switch (type)
{
case BET_HTML:
dprintf(ctx->fd, "");
break;
default:
break;
}
}