From 5375008cdfdc15fef8dd0a5080de782c7ece7204 Mon Sep 17 00:00:00 2001 From: lecoanet Date: Mon, 26 Jan 2004 13:53:26 +0000 Subject: * Added the scaling/rotation capabilities both in X and GL. * (ComputeCursorAndSel): Corrected an incorrect selection extent offset. The last character of the selection was never displayed as part of the selection. * The insertion cursor wasn't displayed on an the last line when empty. --- generic/Text.c | 882 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 570 insertions(+), 312 deletions(-) (limited to 'generic/Text.c') diff --git a/generic/Text.c b/generic/Text.c index edae1c3..7a2b1f8 100644 --- a/generic/Text.c +++ b/generic/Text.c @@ -69,33 +69,35 @@ static const char compile_id[] = "$Compile: " __FILE__ " " __DATE__ " " __TIME__ typedef struct _TextLineInfo { char *start; /* Index of first char in line */ - unsigned int num_bytes; /* Number of displayed bytes in line (NOT chars)*/ - ZnPoint text_origin; /* X pos for drawing the line */ - unsigned int width; /* Line width in pixels */ + unsigned short num_bytes; /* Number of displayed bytes in line (NOT chars)*/ + unsigned short width; /* Line width in pixels */ + unsigned short origin_x; /* X pos for drawing the line */ + unsigned short origin_y; } TextLineInfoStruct, *TextLineInfo; typedef struct _TextItemStruct { ZnItemStruct header; /* Public data */ - ZnPoint pos; - Tk_Anchor anchor; - Tk_Anchor connection_anchor; + ZnPoint pos; /* Position at anchor */ ZnGradient *color; char *text; ZnImage fill_pattern; Tk_Font font; - Tk_Justify alignment; - unsigned int width; - unsigned int spacing; + unsigned short width; + short spacing; unsigned short flags; + Tk_Anchor anchor; + Tk_Anchor connection_anchor; + Tk_Justify alignment; /* Private data */ - ZnPoint pos_dev; - unsigned int num_chars; - unsigned int insert_index; + unsigned short num_chars; + unsigned short insert_index; ZnList text_info; - unsigned int max_width; + unsigned short max_width; + unsigned short height; + ZnPoint poly[4]; #ifdef GL ZnTexFontInfo *tfi; #endif @@ -139,7 +141,7 @@ static ZnAttrConfig text_attrs[] = { { ZN_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(TextItemStruct, header.flags), ZN_SENSITIVE_BIT, ZN_REPICK_FLAG, False }, - { ZN_CONFIG_INT, "-spacing", NULL, + { ZN_CONFIG_SHORT, "-spacing", NULL, Tk_Offset(TextItemStruct, spacing), 0, ZN_COORDS_FLAG|ZN_LAYOUT_FLAG, False }, { ZN_CONFIG_TAG_LIST, "-tags", NULL, @@ -152,7 +154,7 @@ static ZnAttrConfig text_attrs[] = { { ZN_CONFIG_BOOL, "-visible", NULL, Tk_Offset(TextItemStruct, header.flags), ZN_VISIBLE_BIT, ZN_DRAW_FLAG|ZN_REPICK_FLAG|ZN_VIS_FLAG, False }, - { ZN_CONFIG_INT, "-width", NULL, + { ZN_CONFIG_USHORT, "-width", NULL, Tk_Offset(TextItemStruct, width), 0, ZN_COORDS_FLAG|ZN_LAYOUT_FLAG, False }, @@ -175,14 +177,17 @@ Init(ZnItem item, ZnWInfo *wi = item->wi; TextItem text = (TextItem) item; + /*printf("size of a text(header) = %d(%d) %d\n", + sizeof(TextItemStruct), sizeof(ZnItemStruct), sizeof(TextLineInfoStruct));*/ + text->text_info = NULL; /* Init attributes */ SET(item->flags, ZN_VISIBLE_BIT); SET(item->flags, ZN_SENSITIVE_BIT); SET(item->flags, ZN_COMPOSE_ALPHA_BIT); - SET(item->flags, ZN_COMPOSE_ROTATION_BIT); - SET(item->flags, ZN_COMPOSE_SCALE_BIT); + CLEAR(item->flags, ZN_COMPOSE_ROTATION_BIT); + CLEAR(item->flags, ZN_COMPOSE_SCALE_BIT); item->priority = 1; text->pos.x = text->pos.y = 0.0; @@ -380,6 +385,150 @@ Query(ZnItem item, /* + * Compute the transformation to be used and the origin + * of the text (upper left point in item coordinates). + */ +static ZnTransfo * +ComputeTransfoAndOrigin(ZnItem item, + ZnPoint *origin) +{ + TextItem text = (TextItem) item; + + /* + * The connected item support anchors, this is checked by configure. + */ + if (item->connected_item != ZN_NO_ITEM) { + ZnTransfo inv; + + item->connected_item->class->GetAnchor(item->connected_item, + text->connection_anchor, + origin); + + /* GetAnchor return a position in device coordinates not in + * the item coordinate space. To compute the text origin + * (upper left corner), we must apply the inverse transform + * to the ref point before calling anchor2origin. + */ + ZnTransfoInvert(item->transfo, &inv); + ZnTransformPoint(&inv, origin, origin); + ZnAnchor2Origin(origin, (ZnReal) text->max_width, + (ZnReal) text->height, text->anchor, origin); + + /* + * The relevant transform in case of an attachment is the item + * transform alone. This is case of local coordinate space where + * only the translation is a function of the whole transform + * stack, scale and rotation are reset. + */ + return item->transfo; + } + else { + ZnAnchor2Origin(&text->pos, (ZnReal) text->max_width, + (ZnReal) text->height, text->anchor, origin); + return item->wi->current_transfo; + } +} + + +/* + * Compute the selection and the cursor geometry. + */ +void +ComputeCursor(ZnItem item, + int *cursor_line, + unsigned int *cursor_offset) +{ + TextItem text = (TextItem) item; + ZnWInfo *wi = item->wi; + ZnTextInfo *ti = &wi->text_info; + TextLineInfo lines, lines_ptr; + unsigned int i, line_index, insert_index, num_lines; + + num_lines = ZnListSize(text->text_info); + if (num_lines == 0) { + *cursor_line = 0; + } + + lines = ZnListArray(text->text_info); + if ((wi->focus_item == item) && ISSET(wi->flags, ZN_GOT_FOCUS) && ti->cursor_on) { + insert_index = Tcl_UtfAtIndex(text->text, (int) text->insert_index)-text->text; + for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { + /* + * Mark the line with the cursor and compute its + * position along the X axis. + */ + line_index = lines_ptr->start - text->text; + if ((insert_index >= line_index) && + (insert_index <= line_index + lines_ptr->num_bytes)) { + *cursor_line = i; + *cursor_offset = Tk_TextWidth(text->font, (char *) lines_ptr->start, + insert_index - line_index); + } + } + } + +} + +static void +ComputeSelection(ZnItem item, + int *sel_first_line, + int *sel_last_line, + unsigned int *sel_start_offset, + unsigned int *sel_stop_offset) +{ + TextItem text = (TextItem) item; + ZnWInfo *wi = item->wi; + ZnTextInfo *ti = &wi->text_info; + TextLineInfo lines_ptr, lines; + int i, num_lines, byte_index; + unsigned int line_index; + unsigned int sel_first, sel_last; + + num_lines = ZnListSize(text->text_info); + + if ((ti->sel_item != item) || !num_lines) { + return; + } + + lines = ZnListArray(text->text_info); + + sel_first = Tcl_UtfAtIndex(text->text, ti->sel_first)-text->text; + sel_last = Tcl_UtfAtIndex(text->text, ti->sel_last+1)-text->text; + for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { + /* + * Compute the selection first and last line as well as + * the positions along the X axis. + */ + line_index = lines_ptr->start - text->text; + if ((sel_last >= line_index) && + (sel_first <= (line_index + lines_ptr->num_bytes))) { + if (*sel_first_line < 0) { + byte_index = sel_first - line_index; + if (byte_index <= 0) { + *sel_first_line = i; + *sel_start_offset = 0; + /*printf("sel_start_offset 1 : %d\n", *sel_start_offset);*/ + } + else if (byte_index <= lines_ptr->num_bytes) { + *sel_first_line = i; + *sel_start_offset = Tk_TextWidth(text->font, (char *) lines_ptr->start, + byte_index); + /*printf("sel_start_offset 2 : %d\n", *sel_start_offset);*/ + } + } + byte_index = ti->sel_last+1 - line_index; + *sel_last_line = i; + if (byte_index == lines_ptr->num_bytes+1) + *sel_stop_offset = lines_ptr->width; + else if (byte_index <= lines_ptr->num_bytes) + *sel_stop_offset = Tk_TextWidth(text->font, (char *) lines_ptr->start, + byte_index); + } + } +} + + +/* ********************************************************************************** * * ComputeCoordinates -- @@ -394,9 +543,8 @@ ComputeCoordinates(ZnItem item, TextItem text = (TextItem) item; TextLineInfo infos; Tk_FontMetrics fm; - int i, fuzz, num_lines; - int cur_dy; - int font_height, height; + ZnTransfo *transfo; + int i, num_lines, cur_dy, font_height; ZnResetBBox(&item->item_bounding_box); @@ -429,35 +577,28 @@ ComputeCoordinates(ZnItem item, } if ((scan = text->text) != NULL) { + TextLineInfoStruct info; + while (*scan) { - TextLineInfoStruct info; - char *special; - int num; + char *special; + int num, w; /* * Limit the excursion of Tk_MeasureChars to the end * of the line. Do not include \n in the measure done. */ - num = strcspn(scan, "\r\n\t"); + num = strcspn(scan, "\r\n"); special = scan + num; info.num_bytes = Tk_MeasureChars(text->font, scan, num, wrap, - TK_WHOLE_WORDS|TK_AT_LEAST_ONE, - &info.width); + TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &w); + info.width = w; info.start = scan; text->max_width = MAX(info.width, text->max_width); scan += info.num_bytes; /* - * Add a text info describing the tab span (to be completed). - * The entries in the text_info list should be interpreted - * as text chunk not lines before going further.. - */ - while (*scan == '\t') { - scan++; - } - /* * Skip the newline line character. */ if ((*scan == '\r') || (*scan == '\n')) { @@ -468,19 +609,25 @@ ComputeCoordinates(ZnItem item, * Skip white spaces occuring after an * automatic line break. */ - while ((*scan == ' ') || (*scan == '\t')) { + while (*scan == ' ') { scan++; } } + ZnListAdd(text->text_info, &info, ZnListTail); + /*printf("adding a text info : %s, num_bytes : %d, width : %d\n", + info.start, info.num_bytes, info.width);*/ + } + if (*text->text && ((scan[-1] == '\r') || (scan[-1] == '\n'))) { /* Build a text info even for an empty line * at the end of text or for an empty text. * It is needed to enable selection and cursor * insertion to behave correctly. */ + info.num_bytes = 0; + info.width = 0; + info.start = scan; ZnListAdd(text->text_info, &info, ZnListTail); - /*printf("adding a text info : %s, num_bytes : %d, width : %d\n", - info.start, info.num_bytes, info.width);*/ } } @@ -495,60 +642,72 @@ ComputeCoordinates(ZnItem item, for (i = 0; i < num_lines; i++) { switch (text->alignment) { case TK_JUSTIFY_LEFT: - infos[i].text_origin.x = 0; + infos[i].origin_x = 0; break; case TK_JUSTIFY_CENTER: - infos[i].text_origin.x = (text->max_width + 1 - infos[i].width)/2; + infos[i].origin_x = (text->max_width + 1 - infos[i].width)/2; break; case TK_JUSTIFY_RIGHT: - infos[i].text_origin.x = text->max_width + 1 - infos[i].width; + infos[i].origin_x = text->max_width + 1 - infos[i].width; break; } - infos[i].text_origin.y = cur_dy; + infos[i].origin_y = cur_dy; cur_dy += font_height + text->spacing; - /*printf("fixing line %d x : %f, y : %f\n", i, infos[i].text_origin.x, - infos[i].text_origin.y);*/ + /*printf("fixing line %d x : %d, y : %d\n", i, infos[i].origin_x, + infos[i].origin_y);*/ } } /* ISSET(item->inv_flags, INV_TEXT_LAYOUT) */ - height = font_height; + text->height = font_height; if (text->text_info) { - int h; + unsigned int h, cursor_offset; + int cursor_line; + ZnPoint origin, box[4]; + num_lines = ZnListSize(text->text_info); + infos = ZnListArray(text->text_info); h = num_lines * font_height + (num_lines-1) * text->spacing; - height = MAX(height, h); - } + text->height = MAX(text->height, h); - /* - * The connected item support anchors, this is checked by - * configure. - */ - if (item->connected_item != ZN_NO_ITEM) { - item->connected_item->class->GetAnchor(item->connected_item, - text->connection_anchor, - &text->pos_dev); - } - else { - ZnTransformPoint(wi->current_transfo, &text->pos, &text->pos_dev); - text->pos_dev.x = ZnNearestInt(text->pos_dev.x); - text->pos_dev.y = ZnNearestInt(text->pos_dev.y); + transfo = ComputeTransfoAndOrigin(item, &origin); + + text->poly[0].x = origin.x; + text->poly[0].y = origin.y; + text->poly[3].x = text->poly[0].x + text->max_width; + text->poly[3].y = text->poly[0].y + text->height; + text->poly[1].x = text->poly[0].x; + text->poly[1].y = text->poly[3].y; + text->poly[2].x = text->poly[3].x; + text->poly[2].y = text->poly[0].y; + ZnTransformPoints(transfo, text->poly, text->poly, 4); + /* + * Add to the bounding box. + */ + ZnAddPointsToBBox(&item->item_bounding_box, text->poly, 4); + + /* + * Add the cursor shape to the bbox. + */ + cursor_line = -1; + ComputeCursor(item, &cursor_line, &cursor_offset); + if (cursor_line >= 0) { + box[0].x = origin.x + infos[cursor_line].origin_x + cursor_offset - + wi->text_info.insert_width/2; + box[0].y = origin.y + infos[cursor_line].origin_y - fm.ascent + 1; + box[2].x = box[0].x + wi->text_info.insert_width; + box[2].y = box[0].y + font_height - 1; + box[1].x = box[2].x; + box[1].y = box[0].y; + box[3].x = box[0].x; + box[3].y = box[2].y; + ZnTransformPoints(transfo, box, box, 4); + ZnAddPointsToBBox(&item->item_bounding_box, box, 4); + } } - - ZnAnchor2Origin(&text->pos_dev, (ZnReal) text->max_width, (ZnReal) height, text->anchor, - &text->pos_dev); - - /* - * Compute the bounding box. - */ - ZnAddPointToBBox(&item->item_bounding_box, text->pos_dev.x, text->pos_dev.y); - ZnAddPointToBBox(&item->item_bounding_box, text->pos_dev.x+text->max_width+1, - text->pos_dev.y+height); - fuzz = 1+(wi->text_info.insert_width/2); - item->item_bounding_box.orig.x -= fuzz; - item->item_bounding_box.orig.y -= fuzz; - item->item_bounding_box.corner.x += fuzz; - item->item_bounding_box.corner.y += fuzz; + /*printf("bbox origin: %g %g corner %g %g\n", + item->item_bounding_box.orig.x, item->item_bounding_box.orig.y, + item->item_bounding_box.corner.x, item->item_bounding_box.corner.y);*/ /* * Update connected items. */ @@ -577,12 +736,23 @@ ToArea(ZnItem item, Tk_FontMetrics fm; int font_height; ZnBBox line_bbox, *area = ta->area; - ZnPoint o; + ZnPoint box[4], p, origin; + ZnTransfo inv, *transfo; if (!text->text_info || !text->text) { return -1; } + transfo = ComputeTransfoAndOrigin(item, &origin); + box[0] = area->orig; + box[2] = area->corner; + box[1].x = box[2].x; + box[1].y = box[0].y; + box[3].x = box[0].x; + box[3].y = box[2].y; + ZnTransfoInvert(transfo, &inv); + ZnTransformPoints(&inv, box, box, 4); + lines = (TextLineInfo) ZnListArray(text->text_info); num_lines = ZnListSize(text->text_info); Tk_GetFontMetrics(text->font, &fm); @@ -594,19 +764,19 @@ ToArea(ZnItem item, /*printf("text %d, num lines=%d\n", item->id, num_lines);*/ for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { ZnResetBBox(&line_bbox); - o.x = text->pos_dev.x + lines_ptr->text_origin.x; - o.y = text->pos_dev.y + lines_ptr->text_origin.y - fm.ascent; - ZnAddPointToBBox(&line_bbox, o.x, o.y); - ZnAddPointToBBox(&line_bbox, o.x + lines_ptr->width, o.y + font_height); + p.x = origin.x + lines_ptr->origin_x; + p.y = origin.y + lines_ptr->origin_y - fm.ascent; + ZnAddPointToBBox(&line_bbox, p.x, p.y); + ZnAddPointToBBox(&line_bbox, p.x + lines_ptr->width, p.y + font_height); if (!first_done) { first_done = True; - inside = ZnBBoxInBBox(&line_bbox, area); + inside = ZnPolygonInBBox(box, 4, &line_bbox, NULL); if (inside == 0) { return 0; } } else { - if (ZnBBoxInBBox(&line_bbox, area) == 0) { + if (ZnPolygonInBBox(box, 4, &line_bbox, NULL) == 0) { return 0; } } @@ -615,83 +785,6 @@ ToArea(ZnItem item, return inside; } -/* - * Compute the selection and the cursor geometry. - */ -static void -ComputeCursorAndSel(ZnItem item, - TextLineInfo lines, - unsigned int num_lines, - int *sel_first_line, - int *sel_last_line, - int *cursor_line, - unsigned int *sel_start_offset, - unsigned int *sel_stop_offset, - unsigned int *cursor_offset) -{ - TextItem text = (TextItem) item; - ZnWInfo *wi = item->wi; - ZnTextInfo *ti = &wi->text_info; - TextLineInfo lines_ptr; - unsigned int i, line_index, byte_index; - unsigned int insert_index, sel_first, sel_last; - - if (num_lines == 0) { - *cursor_line = 0; - } - if ((wi->focus_item == item) && ISSET(wi->flags, ZN_GOT_FOCUS) && ti->cursor_on) { - insert_index = Tcl_UtfAtIndex(text->text, (int) text->insert_index)-text->text; - for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { - /* - * Mark the line with the cursor and compute its - * position along the X axis. - */ - line_index = lines_ptr->start - text->text; - if ((insert_index >= line_index) && - (insert_index <= line_index + lines_ptr->num_bytes)) { - *cursor_line = i; - *cursor_offset = Tk_TextWidth(text->font, (char *) lines_ptr->start, - insert_index - line_index); - } - } - } - - if (ti->sel_item == item) { - sel_first = Tcl_UtfAtIndex(text->text, ti->sel_first)-text->text; - sel_last = Tcl_UtfAtIndex(text->text, ti->sel_last)-text->text; - for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { - /* - * Compute the selection first and last line as well as - * the positions along the X axis. - */ - line_index = lines_ptr->start - text->text; - if ((sel_last >= line_index) && - (sel_first <= (line_index + lines_ptr->num_bytes))) { - if (*sel_first_line < 0) { - byte_index = sel_first - line_index; - if (byte_index <= 0) { - *sel_first_line = i; - *sel_start_offset = 0; - /*printf("sel_start_offset 1 : %d\n", *sel_start_offset);*/ - } - else if (byte_index <= lines_ptr->num_bytes) { - *sel_first_line = i; - *sel_start_offset = Tk_TextWidth(text->font, (char *) lines_ptr->start, - byte_index); - /*printf("sel_start_offset 2 : %d\n", *sel_start_offset);*/ - } - } - byte_index = ti->sel_last - line_index; - *sel_last_line = i; - if (byte_index == lines_ptr->num_bytes+1) - *sel_stop_offset = lines_ptr->width; - else if (byte_index <= lines_ptr->num_bytes) - *sel_stop_offset = Tk_TextWidth(text->font, (char *) lines_ptr->start, - byte_index); - } - } - } -} /* ********************************************************************************** @@ -706,6 +799,12 @@ Draw(ZnItem item) ZnWInfo *wi = item->wi; TextItem text = (TextItem) item; XGCValues values; + ZnPoint pos, box[4], origin; + ZnTransfo *transfo; + Drawable drw; + GC gc; + XImage *src_im, *dest_im=NULL; + unsigned int dest_im_width=0, dest_im_height=0; unsigned int gc_mask = 0; Tk_FontMetrics fm; unsigned int font_height; @@ -724,14 +823,18 @@ Draw(ZnItem item) num_lines = ZnListSize(text->text_info); Tk_GetFontMetrics(text->font, &fm); font_height = fm.ascent+fm.descent; - + + transfo = ComputeTransfoAndOrigin(item, &origin); + /* * Compute the selection and the cursor geometry. */ - ComputeCursorAndSel(item, lines, num_lines, - &sel_first_line, &sel_last_line, - &cursor_line, &sel_start_offset, - &sel_stop_offset, &cursor_offset); + ComputeCursor(item, &cursor_line, &cursor_offset); + ComputeSelection(item, &sel_first_line, &sel_last_line, + &sel_start_offset, &sel_stop_offset); + + + ZnTransformPoint(transfo, &origin, &pos); /*printf("sel 1st : %d offset : %d, sel last : %d offset : %d\n", sel_first_line, sel_start_offset, sel_last_line, sel_stop_offset);*/ @@ -739,63 +842,147 @@ Draw(ZnItem item) * Setup the gc for the selection and fill the selection. */ if ((ti->sel_item == item) && (sel_first_line >= 0)) { - int x, y; - + XPoint xp[4]; + values.foreground = ZnGetGradientPixel(ti->sel_color, 0.0); values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle | GCForeground, &values); if (sel_first_line == sel_last_line) { - x = (int)(text->pos_dev.x + lines[sel_first_line].text_origin.x + sel_start_offset); - y = (int)(text->pos_dev.y + lines[sel_first_line].text_origin.y - fm.ascent); - XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, - sel_stop_offset - sel_start_offset, font_height); + box[0].x = origin.x + + lines[sel_first_line].origin_x + sel_start_offset; + box[0].y = origin.y + + lines[sel_first_line].origin_y - fm.ascent; + box[2].x = box[0].x + sel_stop_offset - sel_start_offset; + box[2].y = box[0].y + font_height; + box[1].x = box[2].x; + box[1].y = box[0].y; + box[3].x = box[0].x; + box[3].y = box[2].y; + ZnTransformPoints(transfo, box, box, 4); + for (i = 0; i < 4; i++) { + xp[i].x = box[i].x; + xp[i].y = box[i].y; + } + XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xp, 4, Convex, CoordModeOrigin); } else { - x = (int)(text->pos_dev.x + lines[sel_first_line].text_origin.x + sel_start_offset); - y = (int)(text->pos_dev.y + lines[sel_first_line].text_origin.y - fm.ascent); - XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, - x, y, - text->max_width-(int)lines[sel_first_line].text_origin.x-sel_start_offset, - font_height); + box[0].x = origin.x + + lines[sel_first_line].origin_x + sel_start_offset; + box[0].y = origin.y + + lines[sel_first_line].origin_y - fm.ascent; + box[2].x = box[0].x + + text->max_width-lines[sel_first_line].origin_x-sel_start_offset; + box[2].y = box[0].y + font_height; + box[1].x = box[2].x; + box[1].y = box[0].y; + box[3].x = box[0].x; + box[3].y = box[2].y; + ZnTransformPoints(transfo, box, box, 4); + for (i = 0; i < 4; i++) { + xp[i].x = box[i].x; + xp[i].y = box[i].y; + } + XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xp, 4, Convex, CoordModeOrigin); for (i = sel_first_line+1, lines_ptr = &lines[sel_first_line+1]; i < sel_last_line; i++, lines_ptr++) { - x = (int) text->pos_dev.x; - y = (int) (text->pos_dev.y + lines_ptr->text_origin.y - fm.ascent); - XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, - x, y, text->max_width, font_height); + box[0].x = origin.x; + box[0].y = origin.y + lines_ptr->origin_y - fm.ascent; + box[2].x = box[0].x + text->max_width; + box[2].y = box[0].y + font_height; + box[1].x = box[2].x; + box[1].y = box[0].y; + box[3].x = box[0].x; + box[3].y = box[2].y; + ZnTransformPoints(transfo, box, box, 4); + for (i = 0; i < 4; i++) { + xp[i].x = box[i].x; + xp[i].y = box[i].y; + } + XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xp, 4, Convex, CoordModeOrigin); + } + box[0].x = origin.x; + box[0].y = origin.y + lines[sel_last_line].origin_y - fm.ascent; + box[2].x = box[0].x + lines[sel_last_line].origin_x + sel_stop_offset; + box[2].y = box[0].y + font_height; + box[1].x = box[2].x; + box[1].y = box[0].y; + box[3].x = box[0].x; + box[3].y = box[2].y; + ZnTransformPoints(transfo, box, box, 4); + for (i = 0; i < 4; i++) { + xp[i].x = box[i].x; + xp[i].y = box[i].y; } - x = (int)text->pos_dev.x; - y = (int)(text->pos_dev.y + lines[sel_last_line].text_origin.y - fm.ascent); - XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, - x, y, (int)lines[sel_last_line].text_origin.x+sel_stop_offset, - font_height); + XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, xp, 4, Convex, CoordModeOrigin); } } - //printf("cursor line : %d, cursor offset : %d\n", cursor_line, cursor_offset); + /*printf("cursor line : %d, cursor offset : %d\n", cursor_line, cursor_offset);*/ /* * Setup the gc for the cursor and draw it. */ if (cursor_line >= 0 && (wi->focus_item == item) && ti->cursor_on) { - int xs, ys; - values.fill_style = FillSolid; values.line_width = ti->insert_width; values.foreground = ZnGetGradientPixel(ti->insert_color, 0.0); XChangeGC(wi->dpy, wi->gc, GCForeground|GCFillStyle|GCLineWidth, &values); - xs = (int)(text->pos_dev.x + lines[cursor_line].text_origin.x + cursor_offset); - ys = (int)(text->pos_dev.y + lines[cursor_line].text_origin.y - fm.ascent + 1); - XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, xs, ys, xs, ys + (int) font_height - 1); + box[0].x = origin.x + lines[cursor_line].origin_x + cursor_offset; + box[0].y = origin.y + lines[cursor_line].origin_y - fm.ascent + 1; + box[1].x = box[0].x; + box[1].y = box[0].y + font_height - 1; + ZnTransformPoints(transfo, box, box, 2); + XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, + box[0].x, box[0].y, box[1].x, box[1].y); + } + + + /* + * If the current transform is a pure translation, it is + * possible to optimize by directly drawing to the X back + * buffer. Else, we draw in a temporary buffer, get + * its content as an image, transform the image into another + * one and use this last image as a mask to draw in the X + * back buffer. + */ + if (ZnTransfoIsTranslation(transfo)) { + drw = wi->draw_buffer; + + gc = wi->gc; + values.foreground = ZnGetGradientPixel(text->color, 0.0); + } + else { + dest_im_width = (item->item_bounding_box.corner.x - + item->item_bounding_box.orig.x); + dest_im_height = (item->item_bounding_box.corner.y - + item->item_bounding_box.orig.y); + + drw = Tk_GetPixmap(wi->dpy, wi->draw_buffer, + MAX(dest_im_width, text->max_width), + MAX(dest_im_height, text->height), 1); + gc = XCreateGC(wi->dpy, drw, 0, NULL); + XSetForeground(wi->dpy, gc, 0); + XFillRectangle(wi->dpy, drw, gc, 0, 0, + MAX(dest_im_width, text->max_width), + MAX(dest_im_height, text->height)); + dest_im = XCreateImage(wi->dpy, Tk_Visual(wi->win), 1, + XYPixmap, 0, NULL, dest_im_width, dest_im_height, + 8, 0); + dest_im->data = ZnMalloc(dest_im->bytes_per_line * dest_im->height); + memset(dest_im->data, 0, dest_im->bytes_per_line * dest_im->height); + + values.foreground = 1; + + pos.x = 0; + pos.y = 0; } /* * Setup the gc to render the text and draw it. */ values.font = Tk_FontId(text->font); - values.foreground = ZnGetGradientPixel(text->color, 0.0); gc_mask = GCFont | GCForeground; if (text->fill_pattern != ZnUnspecifiedImage) { values.fill_style = FillStippled; @@ -819,28 +1006,72 @@ Draw(ZnItem item) values.line_width = underline_thickness; gc_mask |= GCLineStyle | GCLineWidth; } - XChangeGC(wi->dpy, wi->gc, gc_mask, &values); + XChangeGC(wi->dpy, gc, gc_mask, &values); for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { int tmp_x, tmp_y; - tmp_x = (int)(text->pos_dev.x + lines_ptr->text_origin.x); - tmp_y = (int)(text->pos_dev.y + lines_ptr->text_origin.y); - Tk_DrawChars(wi->dpy, wi->draw_buffer, wi->gc, + tmp_x = (int)(pos.x + lines_ptr->origin_x); + tmp_y = (int)(pos.y + lines_ptr->origin_y); + Tk_DrawChars(wi->dpy, drw, gc, text->font, (char *) lines_ptr->start, (int) lines_ptr->num_bytes, tmp_x, tmp_y); if (ISSET(text->flags, UNDERLINED)) { int y_under = tmp_y + underline_pos; - XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, + XDrawLine(wi->dpy, drw, gc, tmp_x, y_under, tmp_x + (int) lines_ptr->width, y_under); } if (ISSET(text->flags, OVERSTRIKED)) { int y_over = tmp_y-overstrike_pos; - XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, + XDrawLine(wi->dpy, drw, gc, tmp_x, y_over, tmp_x + (int) lines_ptr->width, y_over); } } + + if (dest_im != NULL) { + src_im = XGetImage(wi->dpy, drw, 0, 0, text->max_width, text->height, + 1, XYPixmap); + + box[0].x = origin.x; + box[0].y = origin.y; + box[3].x = box[0].x + text->max_width; + box[3].y = box[0].y + text->height; + box[1].x = box[0].x; + box[1].y = box[3].y; + box[2].x = box[3].x; + box[2].y = box[0].y; + ZnTransformPoints(transfo, box, box, 4); + for (i = 0; i < 4; i++) { + box[i].x -= item->item_bounding_box.orig.x; + box[i].y -= item->item_bounding_box.orig.y; + box[i].x = ZnNearestInt(box[i].x); + box[i].y = ZnNearestInt(box[i].y); + } + + ZnMapImage(src_im, dest_im, box); + + XPutImage(wi->dpy, drw, gc, dest_im, + 0, 0, 0, 0, dest_im_width, dest_im_height); + + values.foreground = ZnGetGradientPixel(text->color, 0.0); + values.stipple = drw; + values.ts_x_origin = item->item_bounding_box.orig.x; + values.ts_y_origin = item->item_bounding_box.orig.y; + values.fill_style = FillStippled; + XChangeGC(wi->dpy, wi->gc, + GCFillStyle|GCStipple|GCTileStipXOrigin|GCTileStipYOrigin|GCForeground, + &values); + XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, + item->item_bounding_box.orig.x, + item->item_bounding_box.orig.y, + dest_im_width, dest_im_height); + + XFreeGC(wi->dpy, gc); + Tk_FreePixmap(wi->dpy, drw); + XDestroyImage(src_im); + XDestroyImage(dest_im); + } } @@ -859,6 +1090,9 @@ Render(ZnItem item) TextItem text = (TextItem) item; TextLineInfo lines, lines_ptr; ZnTextInfo *ti = &wi->text_info; + ZnPoint o, c, origin; + ZnTransfo *transfo; + GLdouble m[16]; int i, num_lines; XColor *color; unsigned short alpha; @@ -897,64 +1131,73 @@ Render(ZnItem item) underline_pos = fm.descent/2; overstrike_pos = fm.ascent*3/10; + transfo = ComputeTransfoAndOrigin(item, &origin); + /* * Compute the selection and the cursor geometry. */ - ComputeCursorAndSel(item, lines, num_lines, - &sel_first_line, &sel_last_line, - &cursor_line, &sel_start_offset, - &sel_stop_offset, &cursor_offset); - + ComputeCursor(item, &cursor_line, &cursor_offset); + ComputeSelection(item, &sel_first_line, &sel_last_line, + &sel_start_offset, &sel_stop_offset); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - + glPushMatrix(); + memset(m, 0, sizeof(m)); + m[0] = m[5] = m[15] = 1.0; + if (transfo) { + m[0] = transfo->_[0][0]; m[1] = transfo->_[0][1]; + m[4] = transfo->_[1][0]; m[5] = transfo->_[1][1]; + m[12] = transfo->_[2][0]; m[13] = transfo->_[2][1]; + } + glLoadMatrixd(m); + glTranslated(origin.x, origin.y, 0.0); + glPushMatrix(); + /* * Render the selection. */ if ((ti->sel_item == item) && (sel_first_line >= 0)) { - ZnReal xo, yo, xc, yc; - color = ZnGetGradientColor(ti->sel_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); + o.x = lines[sel_first_line].origin_x + sel_start_offset; + o.y = lines[sel_first_line].origin_y - fm.ascent; glBegin(GL_QUADS); if (sel_first_line == sel_last_line) { - xo = text->pos_dev.x + lines[sel_first_line].text_origin.x + sel_start_offset; - yo = text->pos_dev.y + lines[sel_first_line].text_origin.y - fm.ascent; - xc = xo + sel_stop_offset - sel_start_offset; - yc = yo + font_height; - glVertex2d(xo, yo); - glVertex2d(xc, yo); - glVertex2d(xc, yc); - glVertex2d(xo, yc); + c.x = o.x + sel_stop_offset - sel_start_offset; + c.y = o.y + font_height; + glVertex2d(o.x, o.y); + glVertex2d(o.x, c.y); + glVertex2d(c.x, c.y); + glVertex2d(c.x, o.y); } else { - xo = text->pos_dev.x + lines[sel_first_line].text_origin.x + sel_start_offset; - yo = text->pos_dev.y + lines[sel_first_line].text_origin.y - fm.ascent; - xc = xo + text->max_width-lines[sel_first_line].text_origin.x-sel_start_offset; - yc = yo + font_height; - glVertex2d(xo, yo); - glVertex2d(xc, yo); - glVertex2d(xc, yc); - glVertex2d(xo, yc); + c.x = o.x + (text->max_width - + lines[sel_first_line].origin_x - sel_start_offset); + c.y = o.y + font_height; + glVertex2d(o.x, o.y); + glVertex2d(o.x, c.y); + glVertex2d(c.x, c.y); + glVertex2d(c.x, o.y); for (i = sel_first_line+1, lines_ptr = &lines[sel_first_line+1]; i < sel_last_line; i++, lines_ptr++) { - xo = text->pos_dev.x; - yo = text->pos_dev.y + lines_ptr->text_origin.y - fm.ascent; - xc = xo + text->max_width; - yc = yo + font_height; - glVertex2d(xo, yo); - glVertex2d(xc, yo); - glVertex2d(xc, yc); - glVertex2d(xo, yc); + o.x = 0; + o.y = lines_ptr->origin_y - fm.ascent; + c.x = o.x + text->max_width; + c.y = o.y + font_height; + glVertex2d(o.x, o.y); + glVertex2d(o.x, c.y); + glVertex2d(c.x, c.y); + glVertex2d(c.x, o.y); } - xo = text->pos_dev.x; - yo = text->pos_dev.y + lines[sel_last_line].text_origin.y - fm.ascent; - xc = xo + lines[sel_last_line].text_origin.x+sel_stop_offset; - yc = yo + font_height; - glVertex2d(xo, yo); - glVertex2d(xc, yo); - glVertex2d(xc, yc); - glVertex2d(xo, yc); + o.x = 0; + o.y = lines[sel_last_line].origin_y - fm.ascent; + c.x = o.x + lines[sel_last_line].origin_x + sel_stop_offset; + c.y = o.y + font_height; + glVertex2d(o.x, o.y); + glVertex2d(o.x, c.y); + glVertex2d(c.x, c.y); + glVertex2d(c.x, o.y); } glEnd(); } @@ -964,20 +1207,22 @@ Render(ZnItem item) */ if ((cursor_line >= 0) && (wi->focus_item == item) && ti->cursor_on) { - int xs, ys; - color = ZnGetGradientColor(ti->insert_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); glLineWidth((GLfloat) ti->insert_width); - xs = (int) (text->pos_dev.x + lines[cursor_line].text_origin.x) + cursor_offset; - ys = (int) (text->pos_dev.y + lines[cursor_line].text_origin.y) - fm.ascent + 1; + + o.x = lines[cursor_line].origin_x + cursor_offset; + o.y = lines[cursor_line].origin_y - fm.ascent + 1; + c.x = o.x; + c.y = o.y + font_height - 1; + glBegin(GL_LINES); - glVertex2i(xs, ys); - glVertex2i(xs, ys + font_height - 1); + glVertex2d(o.x, o.y); + glVertex2d(c.x, c.y); glEnd(); } - + /* * Render the text. */ @@ -989,13 +1234,26 @@ Render(ZnItem item) glColor4us(color->red, color->green, color->blue, alpha); for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { - ZnReal xs, ys; - - xs = text->pos_dev.x + lines_ptr->text_origin.x; - ys = text->pos_dev.y + lines_ptr->text_origin.y; - - glPushMatrix(); - glTranslated(xs, ys, 0.0); + glTranslated(lines_ptr->origin_x, lines_ptr->origin_y, 0.0); + + if (ISSET(text->flags, UNDERLINED) || ISSET(text->flags, OVERSTRIKED)) { + glLineWidth((GLfloat) underline_thickness); + glDisable(GL_TEXTURE_2D); + if (ISSET(text->flags, UNDERLINED)) { + glBegin(GL_LINES); + glVertex2d(0, underline_pos); + glVertex2d(lines_ptr->width, underline_pos); + glEnd(); + } + if (ISSET(text->flags, OVERSTRIKED)) { + glBegin(GL_LINES); + glVertex2d(0, -overstrike_pos); + glVertex2d(lines_ptr->width, -overstrike_pos); + glEnd(); + } + glEnable(GL_TEXTURE_2D); + } + #ifndef PTK /* * Temporary ack to fix utf8 display. @@ -1021,25 +1279,12 @@ Render(ZnItem item) ZnRenderString(text->tfi, lines_ptr->start, lines_ptr->num_bytes); #endif glPopMatrix(); - - if (ISSET(text->flags, UNDERLINED) || ISSET(text->flags, OVERSTRIKED)) { - glLineWidth((GLfloat) underline_thickness); - glDisable(GL_TEXTURE_2D); - if (ISSET(text->flags, UNDERLINED)) { - glBegin(GL_LINES); - glVertex2d(xs, ys+underline_pos); - glVertex2d(xs+lines_ptr->width, ys+underline_pos); - glEnd(); - } - if (ISSET(text->flags, OVERSTRIKED)) { - glBegin(GL_LINES); - glVertex2d(xs, ys-overstrike_pos); - glVertex2d(xs+lines_ptr->width, ys-overstrike_pos); - glEnd(); - } - glEnable(GL_TEXTURE_2D); - } + glPushMatrix(); } + + glPopMatrix(); + glPopMatrix(); + glDisable(GL_TEXTURE_2D); #ifdef GL_LIST glEndList(); @@ -1089,28 +1334,34 @@ Pick(ZnItem item, TextLineInfo lines, lines_ptr; Tk_FontMetrics fm; int font_height; - ZnBBox line_bbox; - ZnPoint o, *p = ps->point; - + ZnPoint box[4], origin, *p = ps->point; + ZnTransfo *transfo; + if (!text->text_info || !text->text) { return dist; } - lines = (TextLineInfo) ZnListArray(text->text_info); + transfo = ComputeTransfoAndOrigin(item, &origin); + + lines = ZnListArray(text->text_info); num_lines = ZnListSize(text->text_info); Tk_GetFontMetrics(text->font, &fm); font_height = fm.descent + fm.ascent; if (text->spacing > 0) { font_height += text->spacing; } - + for (i = 0, lines_ptr = lines; i < num_lines; i++, lines_ptr++) { - ZnResetBBox(&line_bbox); - o.x = text->pos_dev.x + lines_ptr->text_origin.x; - o.y = text->pos_dev.y + lines_ptr->text_origin.y - fm.ascent; - ZnAddPointToBBox(&line_bbox, o.x, o.y); - ZnAddPointToBBox(&line_bbox, o.x + lines_ptr->width, o.y + font_height); - new_dist = ZnRectangleToPointDist(&line_bbox, p); + box[0].x = origin.x + lines_ptr->origin_x; + box[0].y = origin.y + lines_ptr->origin_y - fm.ascent; + box[2].x = box[0].x + lines_ptr->width; + box[2].y = box[0].y + font_height; + box[1].x = box[2].x; + box[1].y = box[0].y; + box[3].x = box[0].x; + box[3].y = box[2].y; + ZnTransformPoints(transfo, box, box, 4); + new_dist = ZnPolygonToPointDist(box, 4, p); dist = MIN(dist, new_dist); if (dist <= 0.0) { dist = 0.0; @@ -1149,15 +1400,12 @@ GetAnchor(ZnItem item, ZnPoint *p) { TextItem text = (TextItem) item; - + if (text->num_chars != 0) { - ZnOrigin2Anchor(&text->pos_dev, - item->item_bounding_box.corner.x-item->item_bounding_box.orig.x, - item->item_bounding_box.corner.y-item->item_bounding_box.orig.y, - anchor, p); + ZnRectOrigin2Anchor(text->poly, anchor, p); } else { - p->x = p->y = 0.0; + *p = *text->poly; } } @@ -1175,15 +1423,11 @@ static ZnBool GetClipVertices(ZnItem item, ZnTriStrip *tristrip) { - ZnPoint *points; - - ZnListAssertSize(item->wi->work_pts, 2); - points = (ZnPoint *) ZnListArray(item->wi->work_pts); - ZnTriStrip1(tristrip, points, 2, False); - points[0] = item->item_bounding_box.orig; - points[1] = item->item_bounding_box.corner; + TextItem text = (TextItem) item; + + ZnTriStrip1(tristrip, text->poly, 4, False); - return True; + return False; } @@ -1245,21 +1489,31 @@ PointToChar(TextItem text, int y) { int i, n, num_lines, dummy, byte_index; - TextLineInfo lines, p; + ZnPoint p; + TextLineInfo ti; Tk_FontMetrics fm; + ZnReal a, b; byte_index = 0; if (!text->text_info) { return 0; } - x -= (int) text->pos_dev.x; - y -= (int) text->pos_dev.y; - + p.x = x; + p.y = y; + a = ZnLineToPointDist(&text->poly[0], &text->poly[2], &p, NULL); + b = ZnLineToPointDist(&text->poly[0], &text->poly[1], &p, NULL); + p.x = (text->max_width * b / + hypot(text->poly[2].x - text->poly[0].x, text->poly[2].y - text->poly[0].y)); + p.y = (text->height * a / + hypot(text->poly[1].x - text->poly[0].x, text->poly[1].y - text->poly[0].y)); + p.x = ZnNearestInt(p.x); + p.y = ZnNearestInt(p.y); + /* * Point above text, returns index 0. */ - if (y < 0) { + if (p.y < 0) { return 0; } @@ -1267,29 +1521,30 @@ PointToChar(TextItem text, * Find the text line under point. */ num_lines = ZnListSize(text->text_info); - lines = p = ZnListArray(text->text_info); + ti = ZnListArray(text->text_info); Tk_GetFontMetrics(text->font, &fm); - for (i = 0; i < num_lines; i++, p++) { - if (y < p->text_origin.y + fm.descent) { - if (x < p->text_origin.x) { + for (i = 0; i < num_lines; i++, ti++) { + if (p.y < ti->origin_y + fm.descent) { + if (p.x < ti->origin_x) { /* * Point to the left of the current line, returns * index of first char. */ - byte_index = p->start - text->text; + byte_index = ti->start - text->text; break; } - if (x >= (p->text_origin.x + p->width)) { + if (p.x >= (ti->origin_x + ti->width)) { /* * Point to the right of the current line, returns * index past the last char. */ - byte_index = p->start + p->num_bytes - text->text; + byte_index = ti->start + ti->num_bytes - text->text; break; } - n = Tk_MeasureChars(text->font, p->start, (int) p->num_bytes, - x + 2 - (int) p->text_origin.x, TK_PARTIAL_OK, &dummy); - byte_index = (p->start + n - 1) - text->text; + n = Tk_MeasureChars(text->font, ti->start, (int) ti->num_bytes, + p.x + 2 - (int) ti->origin_x, TK_PARTIAL_OK, + &dummy); + byte_index = (ti->start + n - 1) - text->text; break; } } @@ -1298,8 +1553,8 @@ PointToChar(TextItem text, * Point below all lines, return the index after * the last char in text. */ - p--; - byte_index = p->start + p->num_bytes - text->text; + ti--; + byte_index = ti->start + ti->num_bytes - text->text; } return Tcl_NumUtfChars(text->text, byte_index); @@ -1615,6 +1870,9 @@ DeleteChars(ZnItem item, if (text->insert_index < (unsigned int) *first) { text->insert_index = *first; } + else if (*first == 0) { + text->insert_index = 0; + } } if (ti->sel_item == item) { if (ti->sel_first > *first) { -- cgit v1.1