/* 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/fnv1a.h"
#include "../gtkext/gtkblockview.h"
#include "../gtkext/support.h"
/* Utilisation du champ pixel des couleurs cachées */
#define COLOR_NOT_SET 0
#define COLOR_SET 1
/* Propriétés de rendu */
typedef struct _rendering_pattern_t
{
GdkColor foreground; /* Couleur d'impression */
cairo_font_slant_t slant; /* Style d'impression */
cairo_font_weight_t weight; /* Poids de la police */
} rendering_pattern_t;
/* 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 */
GdkColor cache_bg; /* Fond d'impression */
GdkColor alt_fg; /* Couleur d'impression bis */
GdkColor *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)
{
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 */
/* 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);
/* Propriétés d'impression */
#define GET_RESET_PATTERN(tp) \
({ \
rendering_pattern_t *__p; \
__p = &class->patterns[RTT_ ## tp]; \
memset(__p, 0, sizeof(rendering_pattern_t)); \
__p; \
})
#define DEFINE_SIMPLE_PATTERN(rtt) \
do \
{ \
rendering_pattern_t *__ptn; \
__ptn = GET_RESET_PATTERN(rtt); \
__ptn->slant = CAIRO_FONT_SLANT_NORMAL; \
__ptn->weight = CAIRO_FONT_WEIGHT_NORMAL; \
__ptn->foreground.pixel = COLOR_NOT_SET; \
} \
while (0)
#define DEFINE_PATTERN(rtt, s, w, r, g, b) \
do \
{ \
rendering_pattern_t *__ptn; \
__ptn = GET_RESET_PATTERN(rtt); \
__ptn->slant = CAIRO_FONT_SLANT_ ## s; \
__ptn->weight = CAIRO_FONT_WEIGHT_ ## w; \
__ptn->foreground.red = r; \
__ptn->foreground.green = g; \
__ptn->foreground.blue = b; \
__ptn->foreground.pixel = COLOR_SET; \
} \
while (0)
DEFINE_PATTERN(RAW, NORMAL, NORMAL, 0, 0, 0);
DEFINE_PATTERN(COMMENT, NORMAL, NORMAL, 14335, 45311, 23551);
DEFINE_PATTERN(INDICATION, ITALIC, NORMAL, 33410, 33410, 33410);
DEFINE_PATTERN(RAW_CODE, NORMAL, NORMAL, 48895, 48895, 48895);
DEFINE_PATTERN(INSTRUCTION, NORMAL, NORMAL, 0, 0, 0);
DEFINE_PATTERN(IMMEDIATE, NORMAL, NORMAL, 41215, 8447, 61695);
DEFINE_PATTERN(REGISTER, NORMAL, NORMAL, 16895, 16895, 53759);
DEFINE_PATTERN(PUNCT, NORMAL, BOLD, 0, 0, 0);
DEFINE_PATTERN(HOOK, NORMAL, BOLD, 0, 0, 0);
DEFINE_PATTERN(SIGNS, NORMAL, BOLD, 0, 0, 0);
DEFINE_SIMPLE_PATTERN(LTGT);
DEFINE_PATTERN(SECTION, NORMAL, NORMAL, 51200, 2560, 2560);
DEFINE_SIMPLE_PATTERN(SEGMENT);
DEFINE_PATTERN(STRING, NORMAL, NORMAL, 52224, 32256, 0);
DEFINE_PATTERN(VAR_NAME, NORMAL, NORMAL, 0, 0, 0);
DEFINE_PATTERN(KEY_WORD, NORMAL, NORMAL, 0, 0, 0);
DEFINE_PATTERN(ERROR, NORMAL, BOLD, 65535, 0, 0);
}
/******************************************************************************
* *
* 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 = strdup(text);
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.red = 65535 - segment->pattern->foreground.red;
segment->alt_fg.green = 65535 - segment->pattern->foreground.green;
segment->alt_fg.blue = 65535 - segment->pattern->foreground.blue;
segment->alt_fg.pixel = segment->pattern->foreground.pixel;
}
/******************************************************************************
* *
* 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. *
* *
* Description : Fournit le texte brut conservé dans le segment. *
* *
* Retour : Texte conservé en interne. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_buffer_segment_get_text(const GBufferSegment *segment)
{
return segment->text;
}
/******************************************************************************
* *
* 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.red = 32768;
segment->cache_bg.green = 32768;
segment->cache_bg.blue = 32768;
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.red / 65535.0,
segment->cache_bg.green / 65535.0,
segment->cache_bg.blue / 65535.0);
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->pixel == COLOR_SET)
cairo_set_source_rgb(cairo,
segment->used_fg->red / 65535.0,
segment->used_fg->green / 65535.0,
segment->used_fg->blue / 65535.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;
}