diff options
Diffstat (limited to 'widgets/e-text')
-rw-r--r-- | widgets/e-text/e-text.c | 1411 | ||||
-rw-r--r-- | widgets/e-text/e-text.h | 145 |
2 files changed, 1556 insertions, 0 deletions
diff --git a/widgets/e-text/e-text.c b/widgets/e-text/e-text.c new file mode 100644 index 0000000000..b88130e3c8 --- /dev/null +++ b/widgets/e-text/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 <clahey@umich.edu> + * + * 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 <federico@nuclecu.unam.mx> */ + +#include <config.h> +#include <math.h> +#include "e-text.h" +#include <gdk/gdkx.h> /* for BlackPixel */ +#include <libart_lgpl/art_affine.h> +#include <libart_lgpl/art_rgb.h> +#include <libart_lgpl/art_rgb_bitmap_affine.h> + + + +/* 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); +} diff --git a/widgets/e-text/e-text.h b/widgets/e-text/e-text.h new file mode 100644 index 0000000000..e14199f16e --- /dev/null +++ b/widgets/e-text/e-text.h @@ -0,0 +1,145 @@ +/* -*- 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 <clahey@umich.edu> + * + * 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 <federico@nuclecu.unam.mx> */ + +#ifndef E_TEXT_H +#define E_TEXT_H + +#include <gnome.h> + + +BEGIN_GNOME_DECLS + + +/* Text item for the canvas. Text items are positioned by an anchor point and an anchor direction. + * + * A clipping rectangle may be specified for the text. The rectangle is anchored at the text's anchor + * point, and is specified by clipping width and height parameters. If the clipping rectangle is + * enabled, it will clip the text. + * + * In addition, x and y offset values may be specified. These specify an offset from the anchor + * position. If used in conjunction with the clipping rectangle, these could be used to implement + * simple scrolling of the text within the clipping rectangle. + * + * The following object arguments are available: + * + * name type read/write description + * ------------------------------------------------------------------------------------------ + * text string RW The string of the text label + * x double RW X coordinate of anchor point + * y double RW Y coordinate of anchor point + * font string W X logical font descriptor + * fontset string W X logical fontset descriptor + * font_gdk GdkFont* RW Pointer to a GdkFont + * anchor GtkAnchorType RW Anchor side for the text + * justification GtkJustification RW Justification for multiline text + * fill_color string W X color specification for text + * fill_color_gdk GdkColor* RW Pointer to an allocated GdkColor + * fill_stipple GdkBitmap* RW Stipple pattern for filling the text + * clip_width double RW Width of clip rectangle + * clip_height double RW Height of clip rectangle + * clip boolean RW Use clipping rectangle? + * x_offset double RW Horizontal offset distance from anchor position + * y_offset double RW Vertical offset distance from anchor position + * text_width double R Used to query the width of the rendered text + * text_height double R Used to query the rendered height of the text + * + * These are ignored in the AA version: + * use_ellipsis boolean RW Whether to use ellipsises if text gets cut off. Meaningless if clip == false. + * ellipsis string RW The characters to use as ellipsis. NULL = "...". + */ + +#define E_TYPE_TEXT (e_text_get_type ()) +#define E_TEXT(obj) (GTK_CHECK_CAST ((obj), E_TYPE_TEXT, EText)) +#define E_TEXT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_TEXT, ETextClass)) +#define E_IS_TEXT(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_TEXT)) +#define E_IS_TEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), E_TYPE_TEXT)) + + +typedef struct _EText EText; +typedef struct _ETextClass ETextClass; +typedef struct _ETextSuckFont ETextSuckFont; +typedef struct _ETextSuckChar ETextSuckChar; + +struct _ETextSuckChar { + int left_sb; + int right_sb; + int width; + int ascent; + int descent; + int bitmap_offset; /* in pixels */ +}; + +struct _ETextSuckFont { + guchar *bitmap; + gint bitmap_width; + gint bitmap_height; + gint ascent; + ETextSuckChar chars[256]; +}; + +struct _EText { + GnomeCanvasItem item; + + char *text; /* Text to display */ + gpointer lines; /* Text split into lines (private field) */ + int num_lines; /* Number of lines of text */ + + double x, y; /* Position at anchor */ + GdkFont *font; /* Font for text */ + GtkAnchorType anchor; /* Anchor side for text */ + GtkJustification justification; /* Justification for text */ + + double clip_width; /* Width of optional clip rectangle */ + double clip_height; /* Height of optional clip rectangle */ + + double xofs, yofs; /* Text offset distance from anchor position */ + + gulong pixel; /* Fill color */ + GdkBitmap *stipple; /* Stipple for text */ + GdkGC *gc; /* GC for drawing text */ + + int cx, cy; /* Top-left canvas coordinates for text */ + int clip_cx, clip_cy; /* Top-left canvas coordinates for clip rectangle */ + int clip_cwidth, clip_cheight; /* Size of clip rectangle in pixels */ + int max_width; /* Maximum width of text lines */ + int height; /* Rendered text height in pixels */ + + guint clip : 1; /* Use clip rectangle? */ + + /* Antialiased specific stuff follows */ + ETextSuckFont *suckfont; /* Sucked font */ + guint32 rgba; /* RGBA color for text */ + double affine[6]; /* The item -> canvas affine */ + + char *ellipsis; /* The ellipsis characters. NULL = "...". */ + double ellipsis_width; /* The width of the ellipsis. */ + gboolean use_ellipsis; /* Whether to use the ellipsis. */ +}; + +struct _ETextClass { + GnomeCanvasItemClass parent_class; +}; + + +/* Standard Gtk function */ +GtkType e_text_get_type (void); + + +END_GNOME_DECLS + +#endif |