From b2a25c6add10c40eb6d369fdf504f2e7d13d2c97 Mon Sep 17 00:00:00 2001 From: Chris Lahey Date: Mon, 10 Jan 2000 04:55:48 +0000 Subject: Added minicard and text stuff. * widgets/Makefile.am: Added minicard and text stuff. * widgets/e-minicard.c, widgets/e-minicard.h, widgets/e-minicard-label.c, widgets/e-minicard-label.h: Added canvas items for the minicard view in the contact manager. * widgets/test-minicard.c, widgets/test-minicard-label.c: Tests for the minicard items. * widgets/e-text.h, widgets/e-text.c: New canvas item. Based on GnomeCanvasText. Adds ellipsis capabilities. Used in e-minicard*.[ch]. * widgets/.cvsignore: Added minicard-test and minicard-label-test. svn path=/trunk/; revision=1552 --- widgets/e-text.c | 1411 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1411 insertions(+) create mode 100644 widgets/e-text.c (limited to 'widgets/e-text.c') diff --git a/widgets/e-text.c b/widgets/e-text.c new file mode 100644 index 0000000000..b88130e3c8 --- /dev/null +++ b/widgets/e-text.c @@ -0,0 +1,1411 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* EText - Text item for evolution. + * Copyright (C) 2000 Helix Code, Inc. + * + * Author: Chris Lahey + * + * A majority of code taken from: + * + * Text item type for GnomeCanvas widget + * + * GnomeCanvas is basically a port of the Tk toolkit's most excellent + * canvas widget. Tk is copyrighted by the Regents of the University + * of California, Sun Microsystems, and other parties. + * + * Copyright (C) 1998 The Free Software Foundation + * + * Author: Federico Mena */ + +#include +#include +#include "e-text.h" +#include /* for BlackPixel */ +#include +#include +#include + + + +/* This defines a line of text */ +struct line { + char *text; /* Line's text, it is a pointer into the text->text string */ + int length; /* Line's length in characters */ + int width; /* Line's width in pixels */ + int ellipsis_length; /* Length before adding ellipsis */ +}; + + + +/* Object argument IDs */ +enum { + ARG_0, + ARG_TEXT, + ARG_X, + ARG_Y, + ARG_FONT, + ARG_FONTSET, + ARG_FONT_GDK, + ARG_ANCHOR, + ARG_JUSTIFICATION, + ARG_CLIP_WIDTH, + ARG_CLIP_HEIGHT, + ARG_CLIP, + ARG_X_OFFSET, + ARG_Y_OFFSET, + ARG_FILL_COLOR, + ARG_FILL_COLOR_GDK, + ARG_FILL_COLOR_RGBA, + ARG_FILL_STIPPLE, + ARG_TEXT_WIDTH, + ARG_TEXT_HEIGHT, + ARG_USE_ELLIPSIS, + ARG_ELLIPSIS +}; + + +static void e_text_class_init (ETextClass *class); +static void e_text_init (EText *text); +static void e_text_destroy (GtkObject *object); +static void e_text_set_arg (GtkObject *object, GtkArg *arg, guint arg_id); +static void e_text_get_arg (GtkObject *object, GtkArg *arg, guint arg_id); + +static void e_text_update (GnomeCanvasItem *item, double *affine, + ArtSVP *clip_path, int flags); +static void e_text_realize (GnomeCanvasItem *item); +static void e_text_unrealize (GnomeCanvasItem *item); +static void e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, + int x, int y, int width, int height); +static double e_text_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, + GnomeCanvasItem **actual_item); +static void e_text_bounds (GnomeCanvasItem *item, + double *x1, double *y1, double *x2, double *y2); +static void e_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf); + +static ETextSuckFont *e_suck_font (GdkFont *font); +static void e_suck_font_free (ETextSuckFont *suckfont); + + +static GnomeCanvasItemClass *parent_class; + + + +/** + * e_text_get_type: + * @void: + * + * Registers the &EText class if necessary, and returns the type ID + * associated to it. + * + * Return value: The type ID of the &EText class. + **/ +GtkType +e_text_get_type (void) +{ + static GtkType text_type = 0; + + if (!text_type) { + GtkTypeInfo text_info = { + "EText", + sizeof (EText), + sizeof (ETextClass), + (GtkClassInitFunc) e_text_class_init, + (GtkObjectInitFunc) e_text_init, + NULL, /* reserved_1 */ + NULL, /* reserved_2 */ + (GtkClassInitFunc) NULL + }; + + text_type = gtk_type_unique (gnome_canvas_item_get_type (), &text_info); + } + + return text_type; +} + +/* Class initialization function for the text item */ +static void +e_text_class_init (ETextClass *class) +{ + GtkObjectClass *object_class; + GnomeCanvasItemClass *item_class; + + object_class = (GtkObjectClass *) class; + item_class = (GnomeCanvasItemClass *) class; + + parent_class = gtk_type_class (gnome_canvas_item_get_type ()); + + gtk_object_add_arg_type ("EText::text", + GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_TEXT); + gtk_object_add_arg_type ("EText::x", + GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X); + gtk_object_add_arg_type ("EText::y", + GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y); + gtk_object_add_arg_type ("EText::font", + GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FONT); + gtk_object_add_arg_type ("EText::fontset", + GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FONTSET); + gtk_object_add_arg_type ("EText::font_gdk", + GTK_TYPE_GDK_FONT, GTK_ARG_READWRITE, ARG_FONT_GDK); + gtk_object_add_arg_type ("EText::anchor", + GTK_TYPE_ANCHOR_TYPE, GTK_ARG_READWRITE, ARG_ANCHOR); + gtk_object_add_arg_type ("EText::justification", + GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFICATION); + gtk_object_add_arg_type ("EText::clip_width", + GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_CLIP_WIDTH); + gtk_object_add_arg_type ("EText::clip_height", + GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_CLIP_HEIGHT); + gtk_object_add_arg_type ("EText::clip", + GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_CLIP); + gtk_object_add_arg_type ("EText::x_offset", + GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X_OFFSET); + gtk_object_add_arg_type ("EText::y_offset", + GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y_OFFSET); + gtk_object_add_arg_type ("EText::fill_color", + GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FILL_COLOR); + gtk_object_add_arg_type ("EText::fill_color_gdk", + GTK_TYPE_GDK_COLOR, GTK_ARG_READWRITE, ARG_FILL_COLOR_GDK); + gtk_object_add_arg_type ("EText::fill_color_rgba", + GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_FILL_COLOR_RGBA); + gtk_object_add_arg_type ("EText::fill_stipple", + GTK_TYPE_GDK_WINDOW, GTK_ARG_READWRITE, ARG_FILL_STIPPLE); + gtk_object_add_arg_type ("EText::text_width", + GTK_TYPE_DOUBLE, GTK_ARG_READABLE, ARG_TEXT_WIDTH); + gtk_object_add_arg_type ("EText::text_height", + GTK_TYPE_DOUBLE, GTK_ARG_READABLE, ARG_TEXT_HEIGHT); + gtk_object_add_arg_type ("EText::use_ellipsis", + GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_USE_ELLIPSIS); + gtk_object_add_arg_type ("EText::ellipsis", + GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_ELLIPSIS); + + object_class->destroy = e_text_destroy; + object_class->set_arg = e_text_set_arg; + object_class->get_arg = e_text_get_arg; + + item_class->update = e_text_update; + item_class->realize = e_text_realize; + item_class->unrealize = e_text_unrealize; + item_class->draw = e_text_draw; + item_class->point = e_text_point; + item_class->bounds = e_text_bounds; + item_class->render = e_text_render; +} + +/* Object initialization function for the text item */ +static void +e_text_init (EText *text) +{ + text->x = 0.0; + text->y = 0.0; + text->anchor = GTK_ANCHOR_CENTER; + text->justification = GTK_JUSTIFY_LEFT; + text->clip_width = 0.0; + text->clip_height = 0.0; + text->xofs = 0.0; + text->yofs = 0.0; + text->ellipsis = NULL; + text->use_ellipsis = FALSE; + text->ellipsis_width = 0; +} + +/* Destroy handler for the text item */ +static void +e_text_destroy (GtkObject *object) +{ + EText *text; + + g_return_if_fail (object != NULL); + g_return_if_fail (GNOME_IS_CANVAS_TEXT (object)); + + text = E_TEXT (object); + + if (text->text) + g_free (text->text); + + if (text->lines) + g_free (text->lines); + + if (text->font) + gdk_font_unref (text->font); + + if (text->suckfont) + e_suck_font_free (text->suckfont); + + if (text->stipple) + gdk_bitmap_unref (text->stipple); + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + +static void +get_bounds_item_relative (EText *text, double *px1, double *py1, double *px2, double *py2) +{ + GnomeCanvasItem *item; + double x, y; + double clip_x, clip_y; + + item = GNOME_CANVAS_ITEM (text); + + x = text->x; + y = text->y; + + clip_x = x; + clip_y = y; + + /* Calculate text dimensions */ + + if (text->text && text->font) + text->height = (text->font->ascent + text->font->descent) * text->num_lines; + else + text->height = 0; + + /* Anchor text */ + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_W: + case GTK_ANCHOR_SW: + break; + + case GTK_ANCHOR_N: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_S: + x -= text->max_width / 2; + clip_x -= text->clip_width / 2; + break; + + case GTK_ANCHOR_NE: + case GTK_ANCHOR_E: + case GTK_ANCHOR_SE: + x -= text->max_width; + clip_x -= text->clip_width; + break; + } + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_N: + case GTK_ANCHOR_NE: + break; + + case GTK_ANCHOR_W: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_E: + y -= text->height / 2; + clip_y -= text->clip_height / 2; + break; + + case GTK_ANCHOR_SW: + case GTK_ANCHOR_S: + case GTK_ANCHOR_SE: + y -= text->height; + clip_y -= text->clip_height; + break; + } + + /* Bounds */ + + if (text->clip) { + /* maybe do bbox intersection here? */ + *px1 = clip_x; + *py1 = clip_y; + *px2 = clip_x + text->clip_width; + *py2 = clip_y + text->clip_height; + } else { + *px1 = x; + *py1 = y; + *px2 = x + text->max_width; + *py2 = y + text->height; + } +} + +static void +get_bounds (EText *text, double *px1, double *py1, double *px2, double *py2) +{ + GnomeCanvasItem *item; + double wx, wy; + + item = GNOME_CANVAS_ITEM (text); + + /* Get canvas pixel coordinates for text position */ + + wx = text->x; + wy = text->y; + gnome_canvas_item_i2w (item, &wx, &wy); + gnome_canvas_w2c (item->canvas, wx + text->xofs, wy + text->yofs, &text->cx, &text->cy); + + /* Get canvas pixel coordinates for clip rectangle position */ + + gnome_canvas_w2c (item->canvas, wx, wy, &text->clip_cx, &text->clip_cy); + text->clip_cwidth = text->clip_width * item->canvas->pixels_per_unit; + text->clip_cheight = text->clip_height * item->canvas->pixels_per_unit; + + /* Calculate text dimensions */ + + if (text->text && text->font) + text->height = (text->font->ascent + text->font->descent) * text->num_lines; + else + text->height = 0; + + /* Anchor text */ + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_W: + case GTK_ANCHOR_SW: + break; + + case GTK_ANCHOR_N: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_S: + text->cx -= text->max_width / 2; + text->clip_cx -= text->clip_cwidth / 2; + break; + + case GTK_ANCHOR_NE: + case GTK_ANCHOR_E: + case GTK_ANCHOR_SE: + text->cx -= text->max_width; + text->clip_cx -= text->clip_cwidth; + break; + } + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_N: + case GTK_ANCHOR_NE: + break; + + case GTK_ANCHOR_W: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_E: + text->cy -= text->height / 2; + text->clip_cy -= text->clip_cheight / 2; + break; + + case GTK_ANCHOR_SW: + case GTK_ANCHOR_S: + case GTK_ANCHOR_SE: + text->cy -= text->height; + text->clip_cy -= text->clip_cheight; + break; + } + + /* Bounds */ + + if (text->clip) { + *px1 = text->clip_cx; + *py1 = text->clip_cy; + *px2 = text->clip_cx + text->clip_cwidth; + *py2 = text->clip_cy + text->clip_cheight; + } else { + *px1 = text->cx; + *py1 = text->cy; + *px2 = text->cx + text->max_width; + *py2 = text->cy + text->height; + } +} + +/* Recalculates the bounding box of the text item. The bounding box is defined + * by the text's extents if the clip rectangle is disabled. If it is enabled, + * the bounding box is defined by the clip rectangle itself. + */ +static void +recalc_bounds (EText *text) +{ + GnomeCanvasItem *item; + + item = GNOME_CANVAS_ITEM (text); + + get_bounds (text, &item->x1, &item->y1, &item->x2, &item->y2); + + gnome_canvas_group_child_bounds (GNOME_CANVAS_GROUP (item->parent), item); +} + +static void +calc_ellipsis (EText *text) +{ + if (text->font) + text->ellipsis_width = + gdk_text_width (text->font, + text->ellipsis ? text->ellipsis : "...", + text->ellipsis ? strlen (text->ellipsis) : 3); +} + +/* Calculates the line widths (in pixels) of the text's splitted lines */ +static void +calc_line_widths (EText *text) +{ + struct line *lines; + int i; + int j; + + lines = text->lines; + text->max_width = 0; + + if (!lines) + return; + + for (i = 0; i < text->num_lines; i++) { + if (lines->length != 0) { + if (text->font) { + lines->width = gdk_text_width (text->font, + lines->text, lines->length); + lines->ellipsis_length = 0; + } else { + lines->width = 0; + } + + if (text->clip && + text->use_ellipsis && + lines->width > text->clip_width) { + if (text->font) { + lines->ellipsis_length = 0; + for (j = 0; j < lines->length; j++ ) { + if (gdk_text_width (text->font, lines->text, j) + text->ellipsis_width <= text->clip_width) + lines->ellipsis_length = j; + else + break; + } + } + else + lines->ellipsis_length = 0; + lines->width = gdk_text_width (text->font, lines->text, lines->ellipsis_length) + + text->ellipsis_width; + } + else + lines->ellipsis_length = lines->length; + + if (lines->width > text->max_width) + text->max_width = lines->width; + } + + lines++; + } +} + +/* Splits the text of the text item into lines */ +static void +split_into_lines (EText *text) +{ + char *p; + struct line *lines; + int len; + + /* Free old array of lines */ + + if (text->lines) + g_free (text->lines); + + text->lines = NULL; + text->num_lines = 0; + + if (!text->text) + return; + + /* First, count the number of lines */ + + for (p = text->text; *p; p++) + if (*p == '\n') + text->num_lines++; + + text->num_lines++; + + /* Allocate array of lines and calculate split positions */ + + text->lines = lines = g_new0 (struct line, text->num_lines); + len = 0; + + for (p = text->text; *p; p++) { + if (*p == '\n') { + lines->length = len; + lines++; + len = 0; + } else if (len == 0) { + len++; + lines->text = p; + } else + len++; + } + + lines->length = len; + + calc_line_widths (text); +} + +/* Convenience function to set the text's GC's foreground color */ +static void +set_text_gc_foreground (EText *text) +{ + GdkColor c; + + if (!text->gc) + return; + + c.pixel = text->pixel; + gdk_gc_set_foreground (text->gc, &c); +} + +/* Sets the stipple pattern for the text */ +static void +set_stipple (EText *text, GdkBitmap *stipple, int reconfigure) +{ + if (text->stipple && !reconfigure) + gdk_bitmap_unref (text->stipple); + + text->stipple = stipple; + if (stipple && !reconfigure) + gdk_bitmap_ref (stipple); + + if (text->gc) { + if (stipple) { + gdk_gc_set_stipple (text->gc, stipple); + gdk_gc_set_fill (text->gc, GDK_STIPPLED); + } else + gdk_gc_set_fill (text->gc, GDK_SOLID); + } +} + +/* Set_arg handler for the text item */ +static void +e_text_set_arg (GtkObject *object, GtkArg *arg, guint arg_id) +{ + GnomeCanvasItem *item; + EText *text; + GdkColor color = { 0, 0, 0, 0, }; + GdkColor *pcolor; + gboolean color_changed; + int have_pixel; + + item = GNOME_CANVAS_ITEM (object); + text = E_TEXT (object); + + color_changed = FALSE; + have_pixel = FALSE; + + switch (arg_id) { + case ARG_TEXT: + if (text->text) + g_free (text->text); + + text->text = g_strdup (GTK_VALUE_STRING (*arg)); + split_into_lines (text); + recalc_bounds (text); + break; + + case ARG_X: + text->x = GTK_VALUE_DOUBLE (*arg); + recalc_bounds (text); + break; + + case ARG_Y: + text->y = GTK_VALUE_DOUBLE (*arg); + recalc_bounds (text); + break; + + case ARG_FONT: + if (text->font) + gdk_font_unref (text->font); + + text->font = gdk_font_load (GTK_VALUE_STRING (*arg)); + + if (item->canvas->aa) { + if (text->suckfont) + e_suck_font_free (text->suckfont); + + text->suckfont = e_suck_font (text->font); + } + + calc_ellipsis (text); + calc_line_widths (text); + recalc_bounds (text); + break; + + case ARG_FONTSET: + if (text->font) + gdk_font_unref (text->font); + + text->font = gdk_fontset_load (GTK_VALUE_STRING (*arg)); + + if (item->canvas->aa) { + if (text->suckfont) + e_suck_font_free (text->suckfont); + + text->suckfont = e_suck_font (text->font); + } + + calc_ellipsis (text); + calc_line_widths (text); + recalc_bounds (text); + break; + + case ARG_FONT_GDK: + if (text->font) + gdk_font_unref (text->font); + + text->font = GTK_VALUE_BOXED (*arg); + gdk_font_ref (text->font); + + if (item->canvas->aa) { + if (text->suckfont) + e_suck_font_free (text->suckfont); + + text->suckfont = e_suck_font (text->font); + } + calc_ellipsis (text); + calc_line_widths (text); + recalc_bounds (text); + break; + + case ARG_ANCHOR: + text->anchor = GTK_VALUE_ENUM (*arg); + recalc_bounds (text); + break; + + case ARG_JUSTIFICATION: + text->justification = GTK_VALUE_ENUM (*arg); + break; + + case ARG_CLIP_WIDTH: + text->clip_width = fabs (GTK_VALUE_DOUBLE (*arg)); + calc_ellipsis (text); + calc_line_widths (text); + recalc_bounds (text); + break; + + case ARG_CLIP_HEIGHT: + text->clip_height = fabs (GTK_VALUE_DOUBLE (*arg)); + recalc_bounds (text); + break; + + case ARG_CLIP: + text->clip = GTK_VALUE_BOOL (*arg); + calc_ellipsis (text); + calc_line_widths (text); + recalc_bounds (text); + break; + + case ARG_X_OFFSET: + text->xofs = GTK_VALUE_DOUBLE (*arg); + recalc_bounds (text); + break; + + case ARG_Y_OFFSET: + text->yofs = GTK_VALUE_DOUBLE (*arg); + recalc_bounds (text); + break; + + case ARG_FILL_COLOR: + if (GTK_VALUE_STRING (*arg)) + gdk_color_parse (GTK_VALUE_STRING (*arg), &color); + + text->rgba = ((color.red & 0xff00) << 16 | + (color.green & 0xff00) << 8 | + (color.blue & 0xff00) | + 0xff); + color_changed = TRUE; + break; + + case ARG_FILL_COLOR_GDK: + pcolor = GTK_VALUE_BOXED (*arg); + if (pcolor) { + color = *pcolor; + gdk_color_context_query_color (item->canvas->cc, &color); + have_pixel = TRUE; + } + + text->rgba = ((color.red & 0xff00) << 16 | + (color.green & 0xff00) << 8 | + (color.blue & 0xff00) | + 0xff); + color_changed = TRUE; + break; + + case ARG_FILL_COLOR_RGBA: + text->rgba = GTK_VALUE_UINT (*arg); + color_changed = TRUE; + break; + + case ARG_FILL_STIPPLE: + set_stipple (text, GTK_VALUE_BOXED (*arg), FALSE); + break; + + case ARG_USE_ELLIPSIS: + text->use_ellipsis = GTK_VALUE_BOOL (*arg); + calc_line_widths (text); + recalc_bounds (text); + break; + + case ARG_ELLIPSIS: + if (text->ellipsis) + g_free (text->ellipsis); + + text->ellipsis = g_strdup (GTK_VALUE_STRING (*arg)); + calc_ellipsis (text); + calc_line_widths (text); + recalc_bounds (text); + break; + + default: + break; + } + + if (color_changed) { + if (have_pixel) + text->pixel = color.pixel; + else + text->pixel = gnome_canvas_get_color_pixel (item->canvas, text->rgba); + + if (!item->canvas->aa) + set_text_gc_foreground (text); + + gnome_canvas_item_request_update (item); + } +} + +/* Get_arg handler for the text item */ +static void +e_text_get_arg (GtkObject *object, GtkArg *arg, guint arg_id) +{ + EText *text; + GdkColor *color; + + text = E_TEXT (object); + + switch (arg_id) { + case ARG_TEXT: + GTK_VALUE_STRING (*arg) = g_strdup (text->text); + break; + + case ARG_X: + GTK_VALUE_DOUBLE (*arg) = text->x; + break; + + case ARG_Y: + GTK_VALUE_DOUBLE (*arg) = text->y; + break; + + case ARG_FONT_GDK: + GTK_VALUE_BOXED (*arg) = text->font; + break; + + case ARG_ANCHOR: + GTK_VALUE_ENUM (*arg) = text->anchor; + break; + + case ARG_JUSTIFICATION: + GTK_VALUE_ENUM (*arg) = text->justification; + break; + + case ARG_CLIP_WIDTH: + GTK_VALUE_DOUBLE (*arg) = text->clip_width; + break; + + case ARG_CLIP_HEIGHT: + GTK_VALUE_DOUBLE (*arg) = text->clip_height; + break; + + case ARG_CLIP: + GTK_VALUE_BOOL (*arg) = text->clip; + break; + + case ARG_X_OFFSET: + GTK_VALUE_DOUBLE (*arg) = text->xofs; + break; + + case ARG_Y_OFFSET: + GTK_VALUE_DOUBLE (*arg) = text->yofs; + break; + + case ARG_FILL_COLOR_GDK: + color = g_new (GdkColor, 1); + color->pixel = text->pixel; + gdk_color_context_query_color (text->item.canvas->cc, color); + GTK_VALUE_BOXED (*arg) = color; + break; + + case ARG_FILL_COLOR_RGBA: + GTK_VALUE_UINT (*arg) = text->rgba; + break; + + case ARG_FILL_STIPPLE: + GTK_VALUE_BOXED (*arg) = text->stipple; + break; + + case ARG_TEXT_WIDTH: + GTK_VALUE_DOUBLE (*arg) = text->max_width / text->item.canvas->pixels_per_unit; + break; + + case ARG_TEXT_HEIGHT: + GTK_VALUE_DOUBLE (*arg) = text->height / text->item.canvas->pixels_per_unit; + break; + + case ARG_USE_ELLIPSIS: + GTK_VALUE_BOOL (*arg) = text->use_ellipsis; + break; + + case ARG_ELLIPSIS: + GTK_VALUE_STRING (*arg) = g_strdup (text->ellipsis); + break; + + default: + arg->type = GTK_TYPE_INVALID; + break; + } +} + +/* Update handler for the text item */ +static void +e_text_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags) +{ + EText *text; + double x1, y1, x2, y2; + ArtDRect i_bbox, c_bbox; + int i; + + text = E_TEXT (item); + + if (parent_class->update) + (* parent_class->update) (item, affine, clip_path, flags); + + if (!item->canvas->aa) { + set_text_gc_foreground (text); + set_stipple (text, text->stipple, TRUE); + get_bounds (text, &x1, &y1, &x2, &y2); + + gnome_canvas_update_bbox (item, x1, y1, x2, y2); + } else { + /* aa rendering */ + for (i = 0; i < 6; i++) + text->affine[i] = affine[i]; + get_bounds_item_relative (text, &i_bbox.x0, &i_bbox.y0, &i_bbox.x1, &i_bbox.y1); + art_drect_affine_transform (&c_bbox, &i_bbox, affine); + gnome_canvas_update_bbox (item, c_bbox.x0, c_bbox.y0, c_bbox.x1, c_bbox.y1); + + } +} + +/* Realize handler for the text item */ +static void +e_text_realize (GnomeCanvasItem *item) +{ + EText *text; + + text = E_TEXT (item); + + if (parent_class->realize) + (* parent_class->realize) (item); + + text->gc = gdk_gc_new (item->canvas->layout.bin_window); +} + +/* Unrealize handler for the text item */ +static void +e_text_unrealize (GnomeCanvasItem *item) +{ + EText *text; + + text = E_TEXT (item); + + gdk_gc_unref (text->gc); + text->gc = NULL; + + if (parent_class->unrealize) + (* parent_class->unrealize) (item); +} + +/* Calculates the x position of the specified line of text, based on the text's justification */ +static double +get_line_xpos_item_relative (EText *text, struct line *line) +{ + double x; + + x = text->x; + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_W: + case GTK_ANCHOR_SW: + break; + + case GTK_ANCHOR_N: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_S: + x -= text->max_width / 2; + break; + + case GTK_ANCHOR_NE: + case GTK_ANCHOR_E: + case GTK_ANCHOR_SE: + x -= text->max_width; + break; + } + + switch (text->justification) { + case GTK_JUSTIFY_RIGHT: + x += text->max_width - line->width; + break; + + case GTK_JUSTIFY_CENTER: + x += (text->max_width - line->width) * 0.5; + break; + + default: + /* For GTK_JUSTIFY_LEFT, we don't have to do anything. We do not support + * GTK_JUSTIFY_FILL, yet. + */ + break; + } + + return x; +} + +/* Calculates the y position of the first line of text. */ +static double +get_line_ypos_item_relative (EText *text) +{ + double y; + + y = text->y; + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_N: + case GTK_ANCHOR_NE: + break; + + case GTK_ANCHOR_W: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_E: + y -= text->height / 2; + break; + + case GTK_ANCHOR_SW: + case GTK_ANCHOR_S: + case GTK_ANCHOR_SE: + y -= text->height; + break; + } + + return y; +} + +/* Calculates the x position of the specified line of text, based on the text's justification */ +static int +get_line_xpos (EText *text, struct line *line) +{ + int x; + + x = text->cx; + + switch (text->justification) { + case GTK_JUSTIFY_RIGHT: + x += text->max_width - line->width; + break; + + case GTK_JUSTIFY_CENTER: + x += (text->max_width - line->width) / 2; + break; + + default: + /* For GTK_JUSTIFY_LEFT, we don't have to do anything. We do not support + * GTK_JUSTIFY_FILL, yet. + */ + break; + } + + return x; +} + +/* Draw handler for the text item */ +static void +e_text_draw (GnomeCanvasItem *item, GdkDrawable *drawable, + int x, int y, int width, int height) +{ + EText *text; + GdkRectangle rect; + struct line *lines; + int i; + int xpos, ypos; + + text = E_TEXT (item); + + if (!text->text || !text->font) + return; + + if (text->clip) { + rect.x = text->clip_cx - x; + rect.y = text->clip_cy - y; + rect.width = text->clip_cwidth; + rect.height = text->clip_cheight; + + gdk_gc_set_clip_rectangle (text->gc, &rect); + } + lines = text->lines; + ypos = text->cy + text->font->ascent; + + if (text->stipple) + gnome_canvas_set_stipple_origin (item->canvas, text->gc); + + for (i = 0; i < text->num_lines; i++) { + if (lines->length != 0) { + xpos = get_line_xpos (text, lines); + if ( text->clip && text->use_ellipsis && lines->ellipsis_length < lines->length) { + gdk_draw_text (drawable, + text->font, + text->gc, + xpos - x, + ypos - y, + lines->text, + lines->ellipsis_length); + gdk_draw_text (drawable, + text->font, + text->gc, + xpos - x + + lines->width - text->ellipsis_width, + ypos - y, + text->ellipsis ? text->ellipsis : "...", + text->ellipsis ? strlen (text->ellipsis) : 3); + } else + + gdk_draw_text (drawable, + text->font, + text->gc, + xpos - x, + ypos - y, + lines->text, + lines->length); + } + + ypos += text->font->ascent + text->font->descent; + lines++; + } + + if (text->clip) + gdk_gc_set_clip_rectangle (text->gc, NULL); +} + +/* Render handler for the text item */ +static void +e_text_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf) +{ + EText *text; + guint32 fg_color; + double xpos, ypos; + struct line *lines; + int i, j; + double affine[6]; + ETextSuckFont *suckfont; + int dx, dy; + ArtPoint start_i, start_c; + + text = E_TEXT (item); + + if (!text->text || !text->font || !text->suckfont) + return; + + suckfont = text->suckfont; + + fg_color = text->rgba; + + gnome_canvas_buf_ensure_buf (buf); + + lines = text->lines; + start_i.y = get_line_ypos_item_relative (text); + + art_affine_scale (affine, item->canvas->pixels_per_unit, item->canvas->pixels_per_unit); + for (i = 0; i < 6; i++) + affine[i] = text->affine[i]; + + for (i = 0; i < text->num_lines; i++) { + if (lines->length != 0) { + start_i.x = get_line_xpos_item_relative (text, lines); + art_affine_point (&start_c, &start_i, text->affine); + xpos = start_c.x; + ypos = start_c.y; + + for (j = 0; j < lines->length; j++) { + ETextSuckChar *ch; + + ch = &suckfont->chars[(unsigned char)((lines->text)[j])]; + + affine[4] = xpos; + affine[5] = ypos; + art_rgb_bitmap_affine ( + buf->buf, + buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, + buf->buf_rowstride, + suckfont->bitmap + (ch->bitmap_offset >> 3), + ch->width, + suckfont->bitmap_height, + suckfont->bitmap_width >> 3, + fg_color, + affine, + ART_FILTER_NEAREST, NULL); + + dx = ch->left_sb + ch->width + ch->right_sb; + xpos += dx * affine[0]; + ypos += dx * affine[1]; + } + } + + dy = text->font->ascent + text->font->descent; + start_i.y += dy; + lines++; + } + + buf->is_bg = 0; +} + +/* Point handler for the text item */ +static double +e_text_point (GnomeCanvasItem *item, double x, double y, + int cx, int cy, GnomeCanvasItem **actual_item) +{ + EText *text; + int i; + struct line *lines; + int x1, y1, x2, y2; + int font_height; + int dx, dy; + double dist, best; + + text = E_TEXT (item); + + *actual_item = item; + + /* The idea is to build bounding rectangles for each of the lines of + * text (clipped by the clipping rectangle, if it is activated) and see + * whether the point is inside any of these. If it is, we are done. + * Otherwise, calculate the distance to the nearest rectangle. + */ + + if (text->font) + font_height = text->font->ascent + text->font->descent; + else + font_height = 0; + + best = 1.0e36; + + lines = text->lines; + + for (i = 0; i < text->num_lines; i++) { + /* Compute the coordinates of rectangle for the current line, + * clipping if appropriate. + */ + + x1 = get_line_xpos (text, lines); + y1 = text->cy + i * font_height; + x2 = x1 + lines->width; + y2 = y1 + font_height; + + if (text->clip) { + if (x1 < text->clip_cx) + x1 = text->clip_cx; + + if (y1 < text->clip_cy) + y1 = text->clip_cy; + + if (x2 > (text->clip_cx + text->clip_width)) + x2 = text->clip_cx + text->clip_width; + + if (y2 > (text->clip_cy + text->clip_height)) + y2 = text->clip_cy + text->clip_height; + + if ((x1 >= x2) || (y1 >= y2)) + continue; + } + + /* Calculate distance from point to rectangle */ + + if (cx < x1) + dx = x1 - cx; + else if (cx >= x2) + dx = cx - x2 + 1; + else + dx = 0; + + if (cy < y1) + dy = y1 - cy; + else if (cy >= y2) + dy = cy - y2 + 1; + else + dy = 0; + + if ((dx == 0) && (dy == 0)) + return 0.0; + + dist = sqrt (dx * dx + dy * dy); + if (dist < best) + best = dist; + + /* Next! */ + + lines++; + } + + return best / item->canvas->pixels_per_unit; +} + +/* Bounds handler for the text item */ +static void +e_text_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2) +{ + EText *text; + double width, height; + + text = E_TEXT (item); + + *x1 = text->x; + *y1 = text->y; + + if (text->clip) { + width = text->clip_width; + height = text->clip_height; + } else { + width = text->max_width / item->canvas->pixels_per_unit; + height = text->height / item->canvas->pixels_per_unit; + } + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_W: + case GTK_ANCHOR_SW: + break; + + case GTK_ANCHOR_N: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_S: + *x1 -= width / 2.0; + break; + + case GTK_ANCHOR_NE: + case GTK_ANCHOR_E: + case GTK_ANCHOR_SE: + *x1 -= width; + break; + } + + switch (text->anchor) { + case GTK_ANCHOR_NW: + case GTK_ANCHOR_N: + case GTK_ANCHOR_NE: + break; + + case GTK_ANCHOR_W: + case GTK_ANCHOR_CENTER: + case GTK_ANCHOR_E: + *y1 -= height / 2.0; + break; + + case GTK_ANCHOR_SW: + case GTK_ANCHOR_S: + case GTK_ANCHOR_SE: + *y1 -= height; + break; + } + + *x2 = *x1 + width; + *y2 = *y1 + height; +} + + + +/* Routines for sucking fonts from the X server */ + +static ETextSuckFont * +e_suck_font (GdkFont *font) +{ + ETextSuckFont *suckfont; + int i; + int x, y; + char text[1]; + int lbearing, rbearing, ch_width, ascent, descent; + GdkPixmap *pixmap; + GdkColor black, white; + GdkImage *image; + GdkGC *gc; + guchar *bitmap, *line; + int width, height; + int black_pixel, pixel; + + if (!font) + return NULL; + + suckfont = g_new (ETextSuckFont, 1); + + height = font->ascent + font->descent; + x = 0; + for (i = 0; i < 256; i++) { + text[0] = i; + gdk_text_extents (font, text, 1, + &lbearing, &rbearing, &ch_width, &ascent, &descent); + suckfont->chars[i].left_sb = lbearing; + suckfont->chars[i].right_sb = ch_width - rbearing; + suckfont->chars[i].width = rbearing - lbearing; + suckfont->chars[i].ascent = ascent; + suckfont->chars[i].descent = descent; + suckfont->chars[i].bitmap_offset = x; + x += (ch_width + 31) & -32; + } + + width = x; + + suckfont->bitmap_width = width; + suckfont->bitmap_height = height; + suckfont->ascent = font->ascent; + + pixmap = gdk_pixmap_new (NULL, suckfont->bitmap_width, + suckfont->bitmap_height, 1); + gc = gdk_gc_new (pixmap); + gdk_gc_set_font (gc, font); + + black_pixel = BlackPixel (gdk_display, DefaultScreen (gdk_display)); + black.pixel = black_pixel; + white.pixel = WhitePixel (gdk_display, DefaultScreen (gdk_display)); + gdk_gc_set_foreground (gc, &white); + gdk_draw_rectangle (pixmap, gc, 1, 0, 0, width, height); + + gdk_gc_set_foreground (gc, &black); + for (i = 0; i < 256; i++) { + text[0] = i; + gdk_draw_text (pixmap, font, gc, + suckfont->chars[i].bitmap_offset - suckfont->chars[i].left_sb, + font->ascent, + text, 1); + } + + /* The handling of the image leaves me with distinct unease. But this + * is more or less copied out of gimp/app/text_tool.c, so it _ought_ to + * work. -RLL + */ + + image = gdk_image_get (pixmap, 0, 0, width, height); + suckfont->bitmap = g_malloc0 ((width >> 3) * height); + + line = suckfont->bitmap; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + pixel = gdk_image_get_pixel (image, x, y); + if (pixel == black_pixel) + line[x >> 3] |= 128 >> (x & 7); + } + line += width >> 3; + } + + gdk_image_destroy (image); + + /* free the pixmap */ + gdk_pixmap_unref (pixmap); + + /* free the gc */ + gdk_gc_destroy (gc); + + return suckfont; +} + +static void +e_suck_font_free (ETextSuckFont *suckfont) +{ + g_free (suckfont->bitmap); + g_free (suckfont); +} -- cgit v1.2.3