|
|
1.1 ! root 1: /* ! 2: * tkCanvText.c -- ! 3: * ! 4: * This file implements text items for canvas widgets. ! 5: * ! 6: * Copyright 1991-1992 Regents of the University of California. ! 7: * Permission to use, copy, modify, and distribute this ! 8: * software and its documentation for any purpose and without ! 9: * fee is hereby granted, provided that the above copyright ! 10: * notice appear in all copies. The University of California ! 11: * makes no representations about the suitability of this ! 12: * software for any purpose. It is provided "as is" without ! 13: * express or implied warranty. ! 14: */ ! 15: ! 16: #ifndef lint ! 17: static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkCanvText.c,v 1.12 92/08/19 08:34:12 ouster Exp $ SPRITE (Berkeley)"; ! 18: #endif ! 19: ! 20: #include <stdio.h> ! 21: #include "tkint.h" ! 22: #include "tkcanvas.h" ! 23: #include "tkconfig.h" ! 24: ! 25: /* ! 26: * One of the following structures is kept for each line of text ! 27: * in a text item. It contains geometry and display information ! 28: * for that line. ! 29: */ ! 30: ! 31: typedef struct TextLine { ! 32: char *firstChar; /* Pointer to the first character in this ! 33: * line (in the "text" field of enclosing ! 34: * text item). */ ! 35: int numChars; /* Number of characters displayed in this ! 36: * line. */ ! 37: int totalChars; /* Total number of characters included as ! 38: * part of this line (may include an extra ! 39: * space character at the end that isn't ! 40: * displayed). */ ! 41: int x, y; /* Origin at which to draw line on screen ! 42: * (in integer pixel units, but in canvas ! 43: * coordinates, not screen coordinates). */ ! 44: int x1, y1; /* Upper-left pixel that is part of text ! 45: * line on screen (again, in integer canvas ! 46: * pixel units). */ ! 47: int x2, y2; /* Lower-left pixel that is part of text ! 48: * line on screen (again, in integer canvas ! 49: * pixel units). */ ! 50: } TextLine; ! 51: ! 52: /* ! 53: * The structure below defines the record for each text item. ! 54: */ ! 55: ! 56: typedef struct TextItem { ! 57: Tk_Item header; /* Generic stuff that's the same for all ! 58: * types. MUST BE FIRST IN STRUCTURE. */ ! 59: char *text; /* Text for item (malloc-ed). */ ! 60: int numChars; /* Number of non-NULL characters in text. */ ! 61: double x, y; /* Positioning point for text. */ ! 62: Tk_Anchor anchor; /* Where to anchor text relative to (x,y). */ ! 63: int width; /* Width of lines for word-wrap, pixels. ! 64: * Zero means no word-wrap. */ ! 65: Tk_Justify justify; /* Justification mode for text. */ ! 66: int rightEdge; /* Pixel just to right of right edge of ! 67: * area of text item. Used for selecting ! 68: * up to end of line. */ ! 69: XFontStruct *fontPtr; /* Font for drawing text. */ ! 70: XColor *color; /* Color for text. */ ! 71: Pixmap stipple; /* Stipple bitmap for text, or None. */ ! 72: GC gc; /* Graphics context for drawing text. */ ! 73: TextLine *linePtr; /* Pointer to array of structures describing ! 74: * individual lines of text item (malloc-ed). */ ! 75: int numLines; /* Number of structs at *linePtr. */ ! 76: int cursorPos; /* Insertion cursor is displayed just to left ! 77: * of character with this index. */ ! 78: GC selTextGC; /* Graphics context for selected text. */ ! 79: } TextItem; ! 80: ! 81: /* ! 82: * Information used for parsing configuration specs: ! 83: */ ! 84: ! 85: static Tk_ConfigSpec configSpecs[] = { ! 86: {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL, ! 87: "center", Tk_Offset(TextItem, anchor), ! 88: TK_CONFIG_DONT_SET_DEFAULT}, ! 89: {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL, ! 90: "black", Tk_Offset(TextItem, color), 0}, ! 91: {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL, ! 92: "-Adobe-Helvetica-Bold-R-Normal-*-120-*", ! 93: Tk_Offset(TextItem, fontPtr), 0}, ! 94: {TK_CONFIG_JUSTIFY, "-justify", (char *) NULL, (char *) NULL, ! 95: "left", Tk_Offset(TextItem, justify), ! 96: TK_CONFIG_DONT_SET_DEFAULT}, ! 97: {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL, ! 98: (char *) NULL, Tk_Offset(TextItem, stipple), TK_CONFIG_NULL_OK}, ! 99: {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL, ! 100: (char *) NULL, 0, TK_CONFIG_NULL_OK, &tkCanvasTagsOption}, ! 101: {TK_CONFIG_STRING, "-text", (char *) NULL, (char *) NULL, ! 102: "", Tk_Offset(TextItem, text), 0}, ! 103: {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL, ! 104: "0", Tk_Offset(TextItem, width), TK_CONFIG_DONT_SET_DEFAULT}, ! 105: {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, ! 106: (char *) NULL, 0, 0} ! 107: }; ! 108: ! 109: /* ! 110: * Prototypes for procedures defined in this file: ! 111: */ ! 112: ! 113: static void ComputeTextBbox _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 114: TextItem *textPtr)); ! 115: static int ConfigureText _ANSI_ARGS_(( ! 116: Tk_Canvas *canvasPtr, Tk_Item *itemPtr, int argc, ! 117: char **argv, int flags)); ! 118: static int CreateText _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 119: struct Tk_Item *itemPtr, int argc, char **argv)); ! 120: static void DeleteText _ANSI_ARGS_((Tk_Item *itemPtr)); ! 121: static void DisplayText _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 122: Tk_Item *itemPtr, Drawable dst)); ! 123: static int GetSelText _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 124: Tk_Item *itemPtr, int offset, char *buffer, ! 125: int maxBytes)); ! 126: static int GetTextIndex _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 127: Tk_Item *itemPtr, char *indexString, ! 128: int *indexPtr)); ! 129: static void ScaleText _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 130: Tk_Item *itemPtr, double originX, double originY, ! 131: double scaleX, double scaleY)); ! 132: static void SetTextCursor _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 133: Tk_Item *itemPtr, int index)); ! 134: static int TextCoords _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 135: Tk_Item *itemPtr, int argc, char **argv)); ! 136: static int TextDeleteChars _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 137: Tk_Item *itemPtr, int first, int last)); ! 138: static int TextInsert _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 139: Tk_Item *itemPtr, int beforeThis, char *string)); ! 140: static int TextToArea _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 141: Tk_Item *itemPtr, double *rectPtr)); ! 142: static double TextToPoint _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 143: Tk_Item *itemPtr, double *pointPtr)); ! 144: static void TranslateText _ANSI_ARGS_((Tk_Canvas *canvasPtr, ! 145: Tk_Item *itemPtr, double deltaX, double deltaY)); ! 146: ! 147: /* ! 148: * The structures below defines the rectangle and oval item types ! 149: * by means of procedures that can be invoked by generic item code. ! 150: */ ! 151: ! 152: Tk_ItemType TkTextType = { ! 153: "text", /* name */ ! 154: sizeof(TextItem), /* itemSize */ ! 155: CreateText, /* createProc */ ! 156: configSpecs, /* configSpecs */ ! 157: ConfigureText, /* configureProc */ ! 158: TextCoords, /* coordProc */ ! 159: DeleteText, /* deleteProc */ ! 160: DisplayText, /* displayProc */ ! 161: 0, /* alwaysRedraw */ ! 162: TextToPoint, /* pointProc */ ! 163: TextToArea, /* areaProc */ ! 164: (Tk_ItemPostscriptProc *) NULL, /* postscriptProc */ ! 165: ScaleText, /* scaleProc */ ! 166: TranslateText, /* translateProc */ ! 167: GetTextIndex, /* indexProc */ ! 168: SetTextCursor, /* cursorProc */ ! 169: GetSelText, /* selectionProc */ ! 170: TextInsert, /* insertProc */ ! 171: TextDeleteChars, /* dTextProc */ ! 172: (Tk_ItemType *) NULL /* nextPtr */ ! 173: }; ! 174: ! 175: /* ! 176: *-------------------------------------------------------------- ! 177: * ! 178: * CreateText -- ! 179: * ! 180: * This procedure is invoked to create a new text item ! 181: * in a canvas. ! 182: * ! 183: * Results: ! 184: * A standard Tcl return value. If an error occurred in ! 185: * creating the item then an error message is left in ! 186: * canvasPtr->interp->result; in this case itemPtr is ! 187: * left uninitialized so it can be safely freed by the ! 188: * caller. ! 189: * ! 190: * Side effects: ! 191: * A new text item is created. ! 192: * ! 193: *-------------------------------------------------------------- ! 194: */ ! 195: ! 196: static int ! 197: CreateText(canvasPtr, itemPtr, argc, argv) ! 198: register Tk_Canvas *canvasPtr; /* Canvas to hold new item. */ ! 199: Tk_Item *itemPtr; /* Record to hold new item; header ! 200: * has been initialized by caller. */ ! 201: int argc; /* Number of arguments in argv. */ ! 202: char **argv; /* Arguments describing rectangle. */ ! 203: { ! 204: register TextItem *textPtr = (TextItem *) itemPtr; ! 205: ! 206: if (argc < 2) { ! 207: Tcl_AppendResult(canvasPtr->interp, "wrong # args: should be \"", ! 208: Tk_PathName(canvasPtr->tkwin), ! 209: "\" create text x y [options]", (char *) NULL); ! 210: return TCL_ERROR; ! 211: } ! 212: ! 213: /* ! 214: * Carry out initialization that is needed in order to clean ! 215: * up after errors during the the remainder of this procedure. ! 216: */ ! 217: ! 218: textPtr->text = NULL; ! 219: textPtr->anchor = TK_ANCHOR_CENTER; ! 220: textPtr->width = 0; ! 221: textPtr->justify = TK_JUSTIFY_LEFT; ! 222: textPtr->fontPtr = NULL; ! 223: textPtr->color = NULL; ! 224: textPtr->stipple = None; ! 225: textPtr->gc = None; ! 226: textPtr->linePtr = NULL; ! 227: textPtr->numLines = 0; ! 228: textPtr->cursorPos = 0; ! 229: textPtr->selTextGC = None; ! 230: ! 231: /* ! 232: * Process the arguments to fill in the item record. ! 233: */ ! 234: ! 235: if ((TkGetCanvasCoord(canvasPtr, argv[0], &textPtr->x) != TCL_OK) ! 236: || (TkGetCanvasCoord(canvasPtr, argv[1], &textPtr->y) != TCL_OK)) { ! 237: return TCL_ERROR; ! 238: } ! 239: ! 240: if (ConfigureText(canvasPtr, itemPtr, argc-2, argv+2, 0) != TCL_OK) { ! 241: DeleteText(itemPtr); ! 242: return TCL_ERROR; ! 243: } ! 244: return TCL_OK; ! 245: } ! 246: ! 247: /* ! 248: *-------------------------------------------------------------- ! 249: * ! 250: * TextCoords -- ! 251: * ! 252: * This procedure is invoked to process the "coords" widget ! 253: * command on text items. See the user documentation for ! 254: * details on what it does. ! 255: * ! 256: * Results: ! 257: * Returns TCL_OK or TCL_ERROR, and sets canvasPtr->interp->result. ! 258: * ! 259: * Side effects: ! 260: * The coordinates for the given item may be changed. ! 261: * ! 262: *-------------------------------------------------------------- ! 263: */ ! 264: ! 265: static int ! 266: TextCoords(canvasPtr, itemPtr, argc, argv) ! 267: register Tk_Canvas *canvasPtr; /* Canvas containing item. */ ! 268: Tk_Item *itemPtr; /* Item whose coordinates are to be ! 269: * read or modified. */ ! 270: int argc; /* Number of coordinates supplied in ! 271: * argv. */ ! 272: char **argv; /* Array of coordinates: x1, y1, ! 273: * x2, y2, ... */ ! 274: { ! 275: register TextItem *textPtr = (TextItem *) itemPtr; ! 276: ! 277: if (argc == 0) { ! 278: sprintf(canvasPtr->interp->result, "%g %g", textPtr->x, textPtr->y); ! 279: } else if (argc == 2) { ! 280: if ((TkGetCanvasCoord(canvasPtr, argv[0], &textPtr->x) != TCL_OK) ! 281: || (TkGetCanvasCoord(canvasPtr, argv[1], ! 282: &textPtr->y) != TCL_OK)) { ! 283: return TCL_ERROR; ! 284: } ! 285: ComputeTextBbox(canvasPtr, textPtr); ! 286: } else { ! 287: sprintf(canvasPtr->interp->result, ! 288: "wrong # coordinates: expected 0 or 2, got %d", ! 289: argc); ! 290: return TCL_ERROR; ! 291: } ! 292: return TCL_OK; ! 293: } ! 294: ! 295: /* ! 296: *-------------------------------------------------------------- ! 297: * ! 298: * ConfigureText -- ! 299: * ! 300: * This procedure is invoked to configure various aspects ! 301: * of a text item, such as its border and background colors. ! 302: * ! 303: * Results: ! 304: * A standard Tcl result code. If an error occurs, then ! 305: * an error message is left in canvasPtr->interp->result. ! 306: * ! 307: * Side effects: ! 308: * Configuration information, such as colors and stipple ! 309: * patterns, may be set for itemPtr. ! 310: * ! 311: *-------------------------------------------------------------- ! 312: */ ! 313: ! 314: static int ! 315: ConfigureText(canvasPtr, itemPtr, argc, argv, flags) ! 316: Tk_Canvas *canvasPtr; /* Canvas containing itemPtr. */ ! 317: Tk_Item *itemPtr; /* Rectangle item to reconfigure. */ ! 318: int argc; /* Number of elements in argv. */ ! 319: char **argv; /* Arguments describing things to configure. */ ! 320: int flags; /* Flags to pass to Tk_ConfigureWidget. */ ! 321: { ! 322: register TextItem *textPtr = (TextItem *) itemPtr; ! 323: XGCValues gcValues; ! 324: GC newGC, newSelGC; ! 325: unsigned long mask; ! 326: ! 327: if (Tk_ConfigureWidget(canvasPtr->interp, canvasPtr->tkwin, ! 328: configSpecs, argc, argv, (char *) textPtr, flags) != TCL_OK) { ! 329: return TCL_ERROR; ! 330: } ! 331: ! 332: /* ! 333: * A few of the options require additional processing, such as ! 334: * graphics contexts. ! 335: */ ! 336: ! 337: textPtr->numChars = strlen(textPtr->text); ! 338: newGC = newSelGC = None; ! 339: if ((textPtr->color != NULL) && (textPtr->fontPtr != NULL)) { ! 340: gcValues.foreground = textPtr->color->pixel; ! 341: gcValues.font = textPtr->fontPtr->fid; ! 342: mask = GCForeground|GCFont; ! 343: if (textPtr->stipple != None) { ! 344: gcValues.stipple = textPtr->stipple; ! 345: gcValues.fill_style = FillStippled; ! 346: mask |= GCForeground|GCStipple|GCFillStyle; ! 347: } ! 348: newGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues); ! 349: gcValues.foreground = canvasPtr->selFgColorPtr->pixel; ! 350: newSelGC = Tk_GetGC(canvasPtr->tkwin, mask, &gcValues); ! 351: } ! 352: if (textPtr->gc != None) { ! 353: Tk_FreeGC(textPtr->gc); ! 354: } ! 355: textPtr->gc = newGC; ! 356: if (textPtr->selTextGC != None) { ! 357: Tk_FreeGC(textPtr->selTextGC); ! 358: } ! 359: textPtr->selTextGC = newSelGC; ! 360: ! 361: /* ! 362: * If the text was changed, move the selection and insertion indices ! 363: * to keep them inside the item. ! 364: */ ! 365: ! 366: if (canvasPtr->selItemPtr == itemPtr) { ! 367: if (canvasPtr->selectFirst >= textPtr->numChars) { ! 368: canvasPtr->selItemPtr = NULL; ! 369: } else { ! 370: if (canvasPtr->selectLast >= textPtr->numChars) { ! 371: canvasPtr->selectLast = textPtr->numChars-1; ! 372: } ! 373: if ((canvasPtr->anchorItemPtr == itemPtr) ! 374: && (canvasPtr->selectAnchor >= textPtr->numChars)) { ! 375: canvasPtr->selectAnchor = textPtr->numChars-1; ! 376: } ! 377: } ! 378: } ! 379: if (textPtr->cursorPos >= textPtr->numChars) { ! 380: textPtr->cursorPos = textPtr->numChars; ! 381: } ! 382: ! 383: ComputeTextBbox(canvasPtr, textPtr); ! 384: return TCL_OK; ! 385: } ! 386: ! 387: /* ! 388: *-------------------------------------------------------------- ! 389: * ! 390: * DeleteText -- ! 391: * ! 392: * This procedure is called to clean up the data structure ! 393: * associated with a text item. ! 394: * ! 395: * Results: ! 396: * None. ! 397: * ! 398: * Side effects: ! 399: * Resources associated with itemPtr are released. ! 400: * ! 401: *-------------------------------------------------------------- ! 402: */ ! 403: ! 404: static void ! 405: DeleteText(itemPtr) ! 406: Tk_Item *itemPtr; /* Item that is being deleted. */ ! 407: { ! 408: register TextItem *textPtr = (TextItem *) itemPtr; ! 409: ! 410: if (textPtr->text != NULL) { ! 411: ckfree(textPtr->text); ! 412: } ! 413: if (textPtr->fontPtr != NULL) { ! 414: Tk_FreeFontStruct(textPtr->fontPtr); ! 415: } ! 416: if (textPtr->color != NULL) { ! 417: Tk_FreeColor(textPtr->color); ! 418: } ! 419: if (textPtr->stipple != None) { ! 420: Tk_FreeBitmap(textPtr->stipple); ! 421: } ! 422: if (textPtr->gc != None) { ! 423: Tk_FreeGC(textPtr->gc); ! 424: } ! 425: if (textPtr->linePtr != NULL) { ! 426: ckfree((char *) textPtr->linePtr); ! 427: } ! 428: if (textPtr->selTextGC != None) { ! 429: Tk_FreeGC(textPtr->selTextGC); ! 430: } ! 431: } ! 432: ! 433: /* ! 434: *-------------------------------------------------------------- ! 435: * ! 436: * ComputeTextBbox -- ! 437: * ! 438: * This procedure is invoked to compute the bounding box of ! 439: * all the pixels that may be drawn as part of a text item. ! 440: * In addition, it recomputes all of the geometry information ! 441: * used to display a text item or check for mouse hits. ! 442: * ! 443: * Results: ! 444: * None. ! 445: * ! 446: * Side effects: ! 447: * The fields x1, y1, x2, and y2 are updated in the header ! 448: * for itemPtr, and the linePtr structure is regenerated ! 449: * for itemPtr. ! 450: * ! 451: *-------------------------------------------------------------- ! 452: */ ! 453: ! 454: static void ! 455: ComputeTextBbox(canvasPtr, textPtr) ! 456: register Tk_Canvas *canvasPtr; /* Canvas that contains item. */ ! 457: register TextItem *textPtr; /* Item whose bbos is to be ! 458: * recomputed. */ ! 459: { ! 460: register TextLine *linePtr; ! 461: #define MAX_LINES 100 ! 462: char *lineStart[MAX_LINES]; ! 463: int lineChars[MAX_LINES]; ! 464: int linePixels[MAX_LINES]; ! 465: int numLines, wrapPixels, maxLinePixels, leftX, topY, y; ! 466: int lineHeight, i, fudge; ! 467: char *p; ! 468: XCharStruct *maxBoundsPtr = &textPtr->fontPtr->max_bounds; ! 469: ! 470: if (textPtr->linePtr != NULL) { ! 471: ckfree((char *) textPtr->linePtr); ! 472: textPtr->linePtr = NULL; ! 473: } ! 474: ! 475: /* ! 476: * Work through the text computing the starting point, number of ! 477: * characters, and number of pixels in each line. ! 478: */ ! 479: ! 480: p = textPtr->text; ! 481: if (textPtr->width > 0) { ! 482: wrapPixels = maxLinePixels = textPtr->width; ! 483: } else { ! 484: wrapPixels = 10000000; ! 485: maxLinePixels = 0; ! 486: } ! 487: for (numLines = 0; (numLines < MAX_LINES) && (*p != 0); ! 488: numLines++) { ! 489: int numChars, numPixels; ! 490: numChars = TkMeasureChars(textPtr->fontPtr, p, ! 491: (textPtr->text + textPtr->numChars) - p, 0, ! 492: wrapPixels, TK_WHOLE_WORDS|TK_AT_LEAST_ONE, &numPixels); ! 493: if (numPixels > maxLinePixels) { ! 494: maxLinePixels = numPixels; ! 495: } ! 496: lineStart[numLines] = p; ! 497: lineChars[numLines] = numChars; ! 498: linePixels[numLines] = numPixels; ! 499: p += numChars; ! 500: ! 501: /* ! 502: * Skip space character that terminates a line, if there is one. ! 503: * In the case of multiple spaces, all but one will be displayed. ! 504: * This is important to make sure the insertion cursor gets ! 505: * displayed when it is in the middle of a multi-space. ! 506: */ ! 507: ! 508: if (isspace(*p)) { ! 509: p++; ! 510: } ! 511: } ! 512: ! 513: /* ! 514: * Use overall geometry information to compute the top-left corner ! 515: * of the bounding box for the text item. ! 516: */ ! 517: ! 518: leftX = textPtr->x + 0.5; ! 519: topY = textPtr->y + 0.5; ! 520: lineHeight = textPtr->fontPtr->ascent + textPtr->fontPtr->descent; ! 521: switch (textPtr->anchor) { ! 522: case TK_ANCHOR_NW: ! 523: case TK_ANCHOR_N: ! 524: case TK_ANCHOR_NE: ! 525: break; ! 526: ! 527: case TK_ANCHOR_W: ! 528: case TK_ANCHOR_CENTER: ! 529: case TK_ANCHOR_E: ! 530: topY -= (lineHeight * numLines)/2; ! 531: break; ! 532: ! 533: case TK_ANCHOR_SW: ! 534: case TK_ANCHOR_S: ! 535: case TK_ANCHOR_SE: ! 536: topY -= lineHeight * numLines; ! 537: break; ! 538: } ! 539: switch (textPtr->anchor) { ! 540: case TK_ANCHOR_NW: ! 541: case TK_ANCHOR_W: ! 542: case TK_ANCHOR_SW: ! 543: break; ! 544: ! 545: case TK_ANCHOR_N: ! 546: case TK_ANCHOR_CENTER: ! 547: case TK_ANCHOR_S: ! 548: leftX -= maxLinePixels/2; ! 549: break; ! 550: ! 551: case TK_ANCHOR_NE: ! 552: case TK_ANCHOR_E: ! 553: case TK_ANCHOR_SE: ! 554: leftX -= maxLinePixels; ! 555: break; ! 556: } ! 557: textPtr->rightEdge = leftX + maxLinePixels; ! 558: ! 559: /* ! 560: * Create the new TextLine array and fill it in using the geometry ! 561: * information gathered already. ! 562: */ ! 563: ! 564: if (numLines > 0) { ! 565: textPtr->linePtr = (TextLine *) ckalloc((unsigned) ! 566: (numLines * sizeof(TextLine))); ! 567: } else { ! 568: textPtr->linePtr = NULL; ! 569: } ! 570: textPtr->numLines = numLines; ! 571: for (i = 0, linePtr = textPtr->linePtr, y = topY; ! 572: i < numLines; i++, linePtr++, y += lineHeight) { ! 573: linePtr->firstChar = lineStart[i]; ! 574: linePtr->numChars = lineChars[i]; ! 575: if (i == (numLines-1)) { ! 576: linePtr->totalChars = linePtr->numChars; ! 577: } else { ! 578: linePtr->totalChars = lineStart[i+1] - lineStart[i]; ! 579: } ! 580: switch (textPtr->justify) { ! 581: case TK_JUSTIFY_LEFT: ! 582: case TK_JUSTIFY_FILL: ! 583: linePtr->x = leftX; ! 584: break; ! 585: case TK_JUSTIFY_CENTER: ! 586: linePtr->x = leftX + maxLinePixels/2 - linePixels[i]/2; ! 587: break; ! 588: case TK_JUSTIFY_RIGHT: ! 589: linePtr->x = leftX + maxLinePixels - linePixels[i]; ! 590: break; ! 591: } ! 592: linePtr->y = y + textPtr->fontPtr->ascent; ! 593: linePtr->x1 = linePtr->x - maxBoundsPtr->lbearing; ! 594: linePtr->y1 = y; ! 595: linePtr->x2 = linePtr->x + linePixels[i] + maxBoundsPtr->rbearing ! 596: - textPtr->fontPtr->min_bounds.rbearing; ! 597: linePtr->y2 = linePtr->y + textPtr->fontPtr->descent - 1; ! 598: } ! 599: ! 600: /* ! 601: * Last of all, update the bounding box for the item. The item's ! 602: * bounding box includes the bounding box of all its lines, plus ! 603: * an extra fudge factor for the cursor border (which could ! 604: * potentially be quite large). ! 605: */ ! 606: ! 607: linePtr = textPtr->linePtr; ! 608: textPtr->header.x1 = textPtr->header.x2 = leftX; ! 609: textPtr->header.y1 = topY; ! 610: textPtr->header.y2 = topY + numLines*lineHeight; ! 611: for (linePtr = textPtr->linePtr, i = textPtr->numLines; i > 0; ! 612: i--, linePtr++) { ! 613: if (linePtr->x1 < textPtr->header.x1) { ! 614: textPtr->header.x1 = linePtr->x1; ! 615: } ! 616: if (linePtr->x2 >= textPtr->header.x2) { ! 617: textPtr->header.x2 = linePtr->x2 + 1; ! 618: } ! 619: } ! 620: ! 621: fudge = canvasPtr->cursorWidth/2; ! 622: if (canvasPtr->selBorderWidth > fudge) { ! 623: fudge = canvasPtr->selBorderWidth; ! 624: } ! 625: textPtr->header.x1 -= fudge; ! 626: textPtr->header.x2 += fudge; ! 627: } ! 628: ! 629: /* ! 630: *-------------------------------------------------------------- ! 631: * ! 632: * DisplayText -- ! 633: * ! 634: * This procedure is invoked to draw a text item in a given ! 635: * drawable. ! 636: * ! 637: * Results: ! 638: * None. ! 639: * ! 640: * Side effects: ! 641: * ItemPtr is drawn in drawable using the transformation ! 642: * information in canvasPtr. ! 643: * ! 644: *-------------------------------------------------------------- ! 645: */ ! 646: ! 647: static void ! 648: DisplayText(canvasPtr, itemPtr, drawable) ! 649: register Tk_Canvas *canvasPtr; /* Canvas that contains item. */ ! 650: Tk_Item *itemPtr; /* Item to be displayed. */ ! 651: Drawable drawable; /* Pixmap or window in which to draw ! 652: * item. */ ! 653: { ! 654: register TextItem *textPtr = (TextItem *) itemPtr; ! 655: Display *display = Tk_Display(canvasPtr->tkwin); ! 656: register TextLine *linePtr; ! 657: int i, focusHere, cursorX, cursorIndex, lineIndex; ! 658: int beforeSelect, inSelect, afterSelect, selStartX, selEndX; ! 659: ! 660: if (textPtr->gc == None) { ! 661: return; ! 662: } ! 663: focusHere = (canvasPtr->focusItemPtr == itemPtr) && ! 664: (canvasPtr->flags & GOT_FOCUS); ! 665: for (linePtr = textPtr->linePtr, i = textPtr->numLines; ! 666: i > 0; linePtr++, i--) { ! 667: ! 668: /* ! 669: * If part or all of this line is selected, then draw a special ! 670: * background under the selected part of the line. ! 671: */ ! 672: ! 673: lineIndex = linePtr->firstChar - textPtr->text; ! 674: if ((canvasPtr->selItemPtr != itemPtr) ! 675: || (canvasPtr->selectLast < lineIndex) ! 676: || (canvasPtr->selectFirst >= (lineIndex ! 677: + linePtr->totalChars))) { ! 678: beforeSelect = linePtr->numChars; ! 679: inSelect = 0; ! 680: } else { ! 681: beforeSelect = canvasPtr->selectFirst - lineIndex; ! 682: if (beforeSelect <= 0) { ! 683: beforeSelect = 0; ! 684: selStartX = linePtr->x; ! 685: } else { ! 686: (void) TkMeasureChars(textPtr->fontPtr, ! 687: linePtr->firstChar, beforeSelect, 0, ! 688: (int) 1000000, TK_PARTIAL_OK, &selStartX); ! 689: selStartX += linePtr->x; ! 690: } ! 691: inSelect = canvasPtr->selectLast + 1 - (lineIndex + beforeSelect); ! 692: ! 693: /* ! 694: * If the selection spans the end of this line, then display ! 695: * selection background all the way to the end of the line. ! 696: * However, for the last line we only want to display up to ! 697: * the last character, not the end of the line, hence the ! 698: * "i != 1" check. ! 699: */ ! 700: ! 701: if (inSelect >= (linePtr->totalChars - beforeSelect)) { ! 702: inSelect = linePtr->numChars - beforeSelect; ! 703: if (i != 1) { ! 704: selEndX = textPtr->rightEdge; ! 705: goto fillSelectBackground; ! 706: } ! 707: } ! 708: (void) TkMeasureChars(textPtr->fontPtr, ! 709: linePtr->firstChar + beforeSelect, inSelect, ! 710: selStartX-linePtr->x, (int) 1000000, TK_PARTIAL_OK, ! 711: &selEndX); ! 712: selEndX += linePtr->x; ! 713: fillSelectBackground: ! 714: Tk_Fill3DRectangle(display, drawable, canvasPtr->selBorder, ! 715: selStartX - canvasPtr->drawableXOrigin ! 716: - canvasPtr->selBorderWidth, ! 717: linePtr->y - canvasPtr->drawableYOrigin ! 718: - textPtr->fontPtr->ascent, ! 719: selEndX - selStartX + 2*canvasPtr->selBorderWidth, ! 720: textPtr->fontPtr->ascent + textPtr->fontPtr->descent, ! 721: canvasPtr->selBorderWidth, TK_RELIEF_RAISED); ! 722: } ! 723: ! 724: /* ! 725: * If the insertion cursor is in this line, then draw a special ! 726: * background for the cursor before drawing the text. Note: ! 727: * if we're the cursor item but the cursor is turned off, then ! 728: * redraw background over the area of the cursor. This guarantees ! 729: * that the selection won't make the cursor invisible on mono ! 730: * displays, where both are drawn in the same color. ! 731: */ ! 732: ! 733: if (focusHere) { ! 734: cursorIndex = textPtr->cursorPos ! 735: - (linePtr->firstChar - textPtr->text); ! 736: if ((cursorIndex >= 0) && (cursorIndex <= linePtr->numChars)) { ! 737: (void) TkMeasureChars(textPtr->fontPtr, linePtr->firstChar, ! 738: cursorIndex, 0, (int) 1000000, TK_PARTIAL_OK, &cursorX); ! 739: if (canvasPtr->flags & CURSOR_ON) { ! 740: Tk_Fill3DRectangle(display, drawable, ! 741: canvasPtr->cursorBorder, ! 742: linePtr->x - canvasPtr->drawableXOrigin ! 743: + cursorX - (canvasPtr->cursorWidth)/2, ! 744: linePtr->y - canvasPtr->drawableYOrigin ! 745: - textPtr->fontPtr->ascent, ! 746: canvasPtr->cursorWidth, ! 747: textPtr->fontPtr->ascent ! 748: + textPtr->fontPtr->descent, ! 749: canvasPtr->cursorBorderWidth, TK_RELIEF_RAISED); ! 750: } else if (Tk_DefaultDepth( ! 751: Tk_Screen(canvasPtr->tkwin)) == 1){ ! 752: Tk_Fill3DRectangle(display, drawable, ! 753: canvasPtr->bgBorder, ! 754: linePtr->x - canvasPtr->drawableXOrigin ! 755: + cursorX - (canvasPtr->cursorWidth)/2, ! 756: linePtr->y - canvasPtr->drawableYOrigin ! 757: - textPtr->fontPtr->ascent, ! 758: canvasPtr->cursorWidth, ! 759: textPtr->fontPtr->ascent ! 760: + textPtr->fontPtr->descent, ! 761: 0, TK_RELIEF_FLAT); ! 762: } ! 763: } ! 764: } ! 765: ! 766: /* ! 767: * Display the text in three pieces: the part before the ! 768: * selection, the selected part (which needs a different graphics ! 769: * context), and the part after the selection. ! 770: */ ! 771: ! 772: if (beforeSelect != 0) { ! 773: TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr, ! 774: linePtr->firstChar, beforeSelect, ! 775: linePtr->x - canvasPtr->drawableXOrigin, ! 776: linePtr->y - canvasPtr->drawableYOrigin, 0); ! 777: } ! 778: if (inSelect != 0) { ! 779: TkDisplayChars(display, drawable, textPtr->selTextGC, ! 780: textPtr->fontPtr, linePtr->firstChar + beforeSelect, ! 781: inSelect, selStartX - canvasPtr->drawableXOrigin, ! 782: linePtr->y - canvasPtr->drawableYOrigin, 0); ! 783: } ! 784: afterSelect = linePtr->numChars - beforeSelect - inSelect; ! 785: if (afterSelect > 0) { ! 786: TkDisplayChars(display, drawable, textPtr->gc, textPtr->fontPtr, ! 787: linePtr->firstChar + beforeSelect + inSelect, ! 788: afterSelect, selEndX - canvasPtr->drawableXOrigin, ! 789: linePtr->y - canvasPtr->drawableYOrigin, 0); ! 790: } ! 791: } ! 792: } ! 793: ! 794: /* ! 795: *-------------------------------------------------------------- ! 796: * ! 797: * TextInsert -- ! 798: * ! 799: * Insert characters into a text item at a given position. ! 800: * ! 801: * Results: ! 802: * Always returns TCL_OK. ! 803: * ! 804: * Side effects: ! 805: * The text in the given item is modified. The cursor and ! 806: * selection positions are also modified to reflect the ! 807: * insertion. ! 808: * ! 809: *-------------------------------------------------------------- ! 810: */ ! 811: ! 812: static int ! 813: TextInsert(canvasPtr, itemPtr, beforeThis, string) ! 814: Tk_Canvas *canvasPtr; /* Canvas containing text item. */ ! 815: Tk_Item *itemPtr; /* Text item to be modified. */ ! 816: int beforeThis; /* Index of character before which text is ! 817: * to be inserted. */ ! 818: char *string; /* New characters to be inserted. */ ! 819: { ! 820: register TextItem *textPtr = (TextItem *) itemPtr; ! 821: int length; ! 822: char *new; ! 823: ! 824: length = strlen(string); ! 825: if (length == 0) { ! 826: return TCL_OK; ! 827: } ! 828: if (beforeThis < 0) { ! 829: beforeThis = 0; ! 830: } ! 831: if (beforeThis > textPtr->numChars) { ! 832: beforeThis = textPtr->numChars; ! 833: } ! 834: ! 835: new = (char *) ckalloc((unsigned) (textPtr->numChars + length + 1)); ! 836: strncpy(new, textPtr->text, beforeThis); ! 837: strcpy(new+beforeThis, string); ! 838: strcpy(new+beforeThis+length, textPtr->text+beforeThis); ! 839: ckfree(textPtr->text); ! 840: textPtr->text = new; ! 841: textPtr->numChars += length; ! 842: ! 843: /* ! 844: * Inserting characters invalidates indices such as those for the ! 845: * selection and cursor. Update the indices appropriately. ! 846: */ ! 847: ! 848: if (canvasPtr->selItemPtr == itemPtr) { ! 849: if (canvasPtr->selectFirst >= beforeThis) { ! 850: canvasPtr->selectFirst += length; ! 851: } ! 852: if (canvasPtr->selectLast >= beforeThis) { ! 853: canvasPtr->selectLast += length; ! 854: } ! 855: if ((canvasPtr->anchorItemPtr == itemPtr) ! 856: && (canvasPtr->selectAnchor >= beforeThis)) { ! 857: canvasPtr->selectAnchor += length; ! 858: } ! 859: } ! 860: if (textPtr->cursorPos >= beforeThis) { ! 861: textPtr->cursorPos += length; ! 862: } ! 863: ComputeTextBbox(canvasPtr, textPtr); ! 864: return TCL_OK; ! 865: } ! 866: ! 867: /* ! 868: *-------------------------------------------------------------- ! 869: * ! 870: * TextDeleteChars -- ! 871: * ! 872: * Delete one or more characters from a text item. ! 873: * ! 874: * Results: ! 875: * Always returns TCL_OK. ! 876: * ! 877: * Side effects: ! 878: * Characters between "first" and "last", inclusive, get ! 879: * deleted from itemPtr, and things like the selection ! 880: * position get updated. ! 881: * ! 882: *-------------------------------------------------------------- ! 883: */ ! 884: ! 885: static int ! 886: TextDeleteChars(canvasPtr, itemPtr, first, last) ! 887: Tk_Canvas *canvasPtr; /* Canvas containing itemPtr. */ ! 888: Tk_Item *itemPtr; /* Item in which to delete characters. */ ! 889: int first; /* Index of first character to delete. */ ! 890: int last; /* Index of last character to delete. */ ! 891: { ! 892: register TextItem *textPtr = (TextItem *) itemPtr; ! 893: int count; ! 894: char *new; ! 895: ! 896: if (first < 0) { ! 897: first = 0; ! 898: } ! 899: if (last >= textPtr->numChars) { ! 900: last = textPtr->numChars-1; ! 901: } ! 902: if (first > last) { ! 903: return TCL_OK; ! 904: } ! 905: count = last + 1 - first; ! 906: ! 907: new = ckalloc((unsigned) (textPtr->numChars + 1 - count)); ! 908: strncpy(new, textPtr->text, first); ! 909: strcpy(new+first, textPtr->text+last+1); ! 910: ckfree(textPtr->text); ! 911: textPtr->text = new; ! 912: textPtr->numChars -= count; ! 913: ! 914: /* ! 915: * Update indexes for the selection and cursor to reflect the ! 916: * renumbering of the remaining characters. ! 917: */ ! 918: ! 919: if (canvasPtr->selItemPtr == itemPtr) { ! 920: if (canvasPtr->selectFirst > first) { ! 921: canvasPtr->selectFirst -= count; ! 922: if (canvasPtr->selectFirst < first) { ! 923: canvasPtr->selectFirst = first; ! 924: } ! 925: } ! 926: if (canvasPtr->selectLast >= first) { ! 927: canvasPtr->selectLast -= count; ! 928: if (canvasPtr->selectLast < (first-1)) { ! 929: canvasPtr->selectLast = (first-1); ! 930: } ! 931: } ! 932: if (canvasPtr->selectFirst > canvasPtr->selectLast) { ! 933: canvasPtr->selItemPtr = NULL; ! 934: } ! 935: if ((canvasPtr->anchorItemPtr == itemPtr) ! 936: && (canvasPtr->selectAnchor > first)) { ! 937: canvasPtr->selectAnchor -= count; ! 938: if (canvasPtr->selectAnchor < first) { ! 939: canvasPtr->selectAnchor = first; ! 940: } ! 941: } ! 942: } ! 943: if (textPtr->cursorPos > first) { ! 944: textPtr->cursorPos -= count; ! 945: if (textPtr->cursorPos < first) { ! 946: textPtr->cursorPos = first; ! 947: } ! 948: } ! 949: ComputeTextBbox(canvasPtr, textPtr); ! 950: return TCL_OK; ! 951: } ! 952: ! 953: /* ! 954: *-------------------------------------------------------------- ! 955: * ! 956: * TextToPoint -- ! 957: * ! 958: * Computes the distance from a given point to a given ! 959: * text item, in canvas units. ! 960: * ! 961: * Results: ! 962: * The return value is 0 if the point whose x and y coordinates ! 963: * are pointPtr[0] and pointPtr[1] is inside the arc. If the ! 964: * point isn't inside the arc then the return value is the ! 965: * distance from the point to the arc. ! 966: * ! 967: * Side effects: ! 968: * None. ! 969: * ! 970: *-------------------------------------------------------------- ! 971: */ ! 972: ! 973: /* ARGSUSED */ ! 974: static double ! 975: TextToPoint(canvasPtr, itemPtr, pointPtr) ! 976: Tk_Canvas *canvasPtr; /* Canvas containing itemPtr. */ ! 977: Tk_Item *itemPtr; /* Item to check against point. */ ! 978: double *pointPtr; /* Pointer to x and y coordinates. */ ! 979: { ! 980: TextItem *textPtr = (TextItem *) itemPtr; ! 981: register TextLine *linePtr; ! 982: int i; ! 983: double xDiff, yDiff, dist, minDist; ! 984: ! 985: /* ! 986: * Treat each line in the text item as a rectangle, compute the ! 987: * distance to that rectangle, and take the minimum of these ! 988: * distances. Perform most of the calculations in integer pixel ! 989: * units, since that's how the dimensions of the text are defined. ! 990: */ ! 991: ! 992: minDist = -1.0; ! 993: for (linePtr = textPtr->linePtr, i = textPtr->numLines; ! 994: i > 0; linePtr++, i--) { ! 995: ! 996: /* ! 997: * If the point is inside the line's rectangle, then can ! 998: * return immediately. ! 999: */ ! 1000: ! 1001: if ((pointPtr[0] >= linePtr->x1) ! 1002: && (pointPtr[0] <= linePtr->x2) ! 1003: && (pointPtr[1] >= linePtr->y1) ! 1004: && (pointPtr[1] <= linePtr->y2)) { ! 1005: return 0.0; ! 1006: } ! 1007: ! 1008: /* ! 1009: * Point is outside line's rectangle; compute distance to nearest ! 1010: * side. ! 1011: */ ! 1012: ! 1013: if (pointPtr[0] < linePtr->x1) { ! 1014: xDiff = linePtr->x1 - pointPtr[0]; ! 1015: } else if (pointPtr[0] > linePtr->x2) { ! 1016: xDiff = pointPtr[0] - linePtr->x2; ! 1017: } else { ! 1018: xDiff = 0; ! 1019: } ! 1020: ! 1021: if (pointPtr[1] < linePtr->y1) { ! 1022: yDiff = linePtr->y1 - pointPtr[1]; ! 1023: } else if (pointPtr[1] > linePtr->y2) { ! 1024: yDiff = pointPtr[1] - linePtr->y2; ! 1025: } else { ! 1026: yDiff = 0; ! 1027: } ! 1028: ! 1029: dist = hypot((float) xDiff, (float) yDiff); ! 1030: if ((dist < minDist) || (minDist < 0.0)) { ! 1031: minDist = dist; ! 1032: } ! 1033: } ! 1034: return minDist; ! 1035: } ! 1036: ! 1037: /* ! 1038: *-------------------------------------------------------------- ! 1039: * ! 1040: * TextToArea -- ! 1041: * ! 1042: * This procedure is called to determine whether an item ! 1043: * lies entirely inside, entirely outside, or overlapping ! 1044: * a given rectangle. ! 1045: * ! 1046: * Results: ! 1047: * -1 is returned if the item is entirely outside the area ! 1048: * given by rectPtr, 0 if it overlaps, and 1 if it is entirely ! 1049: * inside the given area. ! 1050: * ! 1051: * Side effects: ! 1052: * None. ! 1053: * ! 1054: *-------------------------------------------------------------- ! 1055: */ ! 1056: ! 1057: /* ARGSUSED */ ! 1058: static int ! 1059: TextToArea(canvasPtr, itemPtr, rectPtr) ! 1060: Tk_Canvas *canvasPtr; /* Canvas containing itemPtr. */ ! 1061: Tk_Item *itemPtr; /* Item to check against rectangle. */ ! 1062: double *rectPtr; /* Pointer to array of four coordinates ! 1063: * (x1, y1, x2, y2) describing rectangular ! 1064: * area. */ ! 1065: { ! 1066: TextItem *textPtr = (TextItem *) itemPtr; ! 1067: register TextLine *linePtr; ! 1068: int i, result; ! 1069: ! 1070: /* ! 1071: * Scan the lines one at a time, seeing whether each line is ! 1072: * entirely in, entirely out, or overlapping the rectangle. If ! 1073: * an overlap is detected, return immediately; otherwise wait ! 1074: * until all lines have been processed and see if they were all ! 1075: * inside or all outside. ! 1076: */ ! 1077: ! 1078: result = 0; ! 1079: for (linePtr = textPtr->linePtr, i = textPtr->numLines; ! 1080: i > 0; linePtr++, i--) { ! 1081: if ((rectPtr[2] < linePtr->x1) || (rectPtr[0] > linePtr->x2) ! 1082: || (rectPtr[3] < linePtr->y1) || (rectPtr[1] > linePtr->y2)) { ! 1083: if (result == 1) { ! 1084: return 0; ! 1085: } ! 1086: result = -1; ! 1087: continue; ! 1088: } ! 1089: if ((linePtr->x1 < rectPtr[0]) || (linePtr->x2 > rectPtr[2]) ! 1090: || (linePtr->y1 < rectPtr[1]) || (linePtr->y2 > rectPtr[3])) { ! 1091: return 0; ! 1092: } ! 1093: if (result == -1) { ! 1094: return 0; ! 1095: } ! 1096: result = 1; ! 1097: } ! 1098: return result; ! 1099: } ! 1100: ! 1101: /* ! 1102: *-------------------------------------------------------------- ! 1103: * ! 1104: * ScaleText -- ! 1105: * ! 1106: * This procedure is invoked to rescale a text item. ! 1107: * ! 1108: * Results: ! 1109: * None. ! 1110: * ! 1111: * Side effects: ! 1112: * Scales the position of the text, but not the size ! 1113: * of the font for the text. ! 1114: * ! 1115: *-------------------------------------------------------------- ! 1116: */ ! 1117: ! 1118: /* ARGSUSED */ ! 1119: static void ! 1120: ScaleText(canvasPtr, itemPtr, originX, originY, scaleX, scaleY) ! 1121: Tk_Canvas *canvasPtr; /* Canvas containing rectangle. */ ! 1122: Tk_Item *itemPtr; /* Rectangle to be scaled. */ ! 1123: double originX, originY; /* Origin about which to scale rect. */ ! 1124: double scaleX; /* Amount to scale in X direction. */ ! 1125: double scaleY; /* Amount to scale in Y direction. */ ! 1126: { ! 1127: register TextItem *textPtr = (TextItem *) itemPtr; ! 1128: ! 1129: textPtr->x = originX + scaleX*(textPtr->x - originX); ! 1130: textPtr->y = originY + scaleY*(textPtr->y - originY); ! 1131: ComputeTextBbox(canvasPtr, textPtr); ! 1132: return; ! 1133: } ! 1134: ! 1135: /* ! 1136: *-------------------------------------------------------------- ! 1137: * ! 1138: * TranslateText -- ! 1139: * ! 1140: * This procedure is called to move a text item by a ! 1141: * given amount. ! 1142: * ! 1143: * Results: ! 1144: * None. ! 1145: * ! 1146: * Side effects: ! 1147: * The position of the text item is offset by (xDelta, yDelta), ! 1148: * and the bounding box is updated in the generic part of the ! 1149: * item structure. ! 1150: * ! 1151: *-------------------------------------------------------------- ! 1152: */ ! 1153: ! 1154: static void ! 1155: TranslateText(canvasPtr, itemPtr, deltaX, deltaY) ! 1156: Tk_Canvas *canvasPtr; /* Canvas containing item. */ ! 1157: Tk_Item *itemPtr; /* Item that is being moved. */ ! 1158: double deltaX, deltaY; /* Amount by which item is to be ! 1159: * moved. */ ! 1160: { ! 1161: register TextItem *textPtr = (TextItem *) itemPtr; ! 1162: ! 1163: textPtr->x += deltaX; ! 1164: textPtr->y += deltaY; ! 1165: ComputeTextBbox(canvasPtr, textPtr); ! 1166: } ! 1167: ! 1168: /* ! 1169: *-------------------------------------------------------------- ! 1170: * ! 1171: * GetTextIndex -- ! 1172: * ! 1173: * Parse an index into a text item and return either its value ! 1174: * or an error. ! 1175: * ! 1176: * Results: ! 1177: * A standard Tcl result. If all went well, then *indexPtr is ! 1178: * filled in with the index (into itemPtr) corresponding to ! 1179: * string. Otherwise an error message is left in ! 1180: * canvasPtr->interp->result. ! 1181: * ! 1182: * Side effects: ! 1183: * None. ! 1184: * ! 1185: *-------------------------------------------------------------- ! 1186: */ ! 1187: ! 1188: static int ! 1189: GetTextIndex(canvasPtr, itemPtr, string, indexPtr) ! 1190: Tk_Canvas *canvasPtr; /* Canvas containing item. */ ! 1191: Tk_Item *itemPtr; /* Item for which the index is being ! 1192: * specified. */ ! 1193: char *string; /* Specification of a particular character ! 1194: * in itemPtr's text. */ ! 1195: int *indexPtr; /* Where to store converted index. */ ! 1196: { ! 1197: register TextItem *textPtr = (TextItem *) itemPtr; ! 1198: int length; ! 1199: ! 1200: length = strlen(string); ! 1201: ! 1202: if (string[0] == 'e') { ! 1203: if (strncmp(string, "end", length) == 0) { ! 1204: *indexPtr = textPtr->numChars; ! 1205: } else { ! 1206: badIndex: ! 1207: ! 1208: /* ! 1209: * Some of the paths here leave messages in ! 1210: * canvasPtr->interp->result, so we have to clear it out ! 1211: * before storing our own message. ! 1212: */ ! 1213: ! 1214: Tcl_SetResult(canvasPtr->interp, (char *) NULL, TCL_STATIC); ! 1215: Tcl_AppendResult(canvasPtr->interp, "bad index \"", string, ! 1216: "\"", (char *) NULL); ! 1217: return TCL_ERROR; ! 1218: } ! 1219: } else if (string[0] == 'c') { ! 1220: if (strncmp(string, "cursor", length) == 0) { ! 1221: *indexPtr = textPtr->cursorPos; ! 1222: } else { ! 1223: goto badIndex; ! 1224: } ! 1225: } else if (string[0] == 's') { ! 1226: if (canvasPtr->selItemPtr != itemPtr) { ! 1227: canvasPtr->interp->result = "selection isn't in item"; ! 1228: return TCL_ERROR; ! 1229: } ! 1230: if (length < 5) { ! 1231: goto badIndex; ! 1232: } ! 1233: if (strncmp(string, "sel.first", length) == 0) { ! 1234: *indexPtr = canvasPtr->selectFirst; ! 1235: } else if (strncmp(string, "sel.last", length) == 0) { ! 1236: *indexPtr = canvasPtr->selectLast; ! 1237: } else { ! 1238: goto badIndex; ! 1239: } ! 1240: } else if (string[0] == '@') { ! 1241: int x, y, dummy, i; ! 1242: char *end, *p; ! 1243: register TextLine *linePtr; ! 1244: ! 1245: p = string+1; ! 1246: x = strtol(p, &end, 0); ! 1247: if ((end == p) || (*end != ',')) { ! 1248: goto badIndex; ! 1249: } ! 1250: p = end+1; ! 1251: y = strtol(p, &end, 0); ! 1252: if ((end == p) || (*end != 0)) { ! 1253: goto badIndex; ! 1254: } ! 1255: if ((textPtr->numChars == 0) || (y < textPtr->linePtr[0].y1)) { ! 1256: *indexPtr = 0; ! 1257: return TCL_OK; ! 1258: } ! 1259: for (i = 0, linePtr = textPtr->linePtr; ; i++, linePtr++) { ! 1260: if (i >= textPtr->numLines) { ! 1261: *indexPtr = textPtr->numChars; ! 1262: return TCL_OK; ! 1263: } ! 1264: if (y <= linePtr->y2) { ! 1265: break; ! 1266: } ! 1267: } ! 1268: *indexPtr = TkMeasureChars(textPtr->fontPtr, linePtr->firstChar, ! 1269: linePtr->numChars, linePtr->x, x, 0, &dummy); ! 1270: *indexPtr += linePtr->firstChar - textPtr->text; ! 1271: } else { ! 1272: if (Tcl_GetInt(canvasPtr->interp, string, indexPtr) != TCL_OK) { ! 1273: goto badIndex; ! 1274: } ! 1275: if (*indexPtr < 0){ ! 1276: *indexPtr = 0; ! 1277: } else if (*indexPtr > textPtr->numChars) { ! 1278: *indexPtr = textPtr->numChars; ! 1279: } ! 1280: } ! 1281: return TCL_OK; ! 1282: } ! 1283: ! 1284: /* ! 1285: *-------------------------------------------------------------- ! 1286: * ! 1287: * SetTextCursor -- ! 1288: * ! 1289: * Set the position of the insertion cursor in this item. ! 1290: * ! 1291: * Results: ! 1292: * None. ! 1293: * ! 1294: * Side effects: ! 1295: * The cursor position will change. ! 1296: * ! 1297: *-------------------------------------------------------------- ! 1298: */ ! 1299: ! 1300: /* ARGSUSED */ ! 1301: static void ! 1302: SetTextCursor(canvasPtr, itemPtr, index) ! 1303: Tk_Canvas *canvasPtr; /* Record describing canvas widget. */ ! 1304: Tk_Item *itemPtr; /* Text item in which cursor position ! 1305: * is to be set. */ ! 1306: int index; /* Index of character just before which ! 1307: * cursor is to be positioned. */ ! 1308: { ! 1309: register TextItem *textPtr = (TextItem *) itemPtr; ! 1310: ! 1311: if (index < 0) { ! 1312: textPtr->cursorPos = 0; ! 1313: } else if (index > textPtr->numChars) { ! 1314: textPtr->cursorPos = textPtr->numChars; ! 1315: } else { ! 1316: textPtr->cursorPos = index; ! 1317: } ! 1318: } ! 1319: ! 1320: /* ! 1321: *-------------------------------------------------------------- ! 1322: * ! 1323: * GetSelText -- ! 1324: * ! 1325: * This procedure is invoked to return the selected portion ! 1326: * of a text item. It is only called when this item has ! 1327: * the selection. ! 1328: * ! 1329: * Results: ! 1330: * The return value is the number of non-NULL bytes stored ! 1331: * at buffer. Buffer is filled (or partially filled) with a ! 1332: * NULL-terminated string containing part or all of the selection, ! 1333: * as given by offset and maxBytes. ! 1334: * ! 1335: * Side effects: ! 1336: * None. ! 1337: * ! 1338: *-------------------------------------------------------------- ! 1339: */ ! 1340: ! 1341: static int ! 1342: GetSelText(canvasPtr, itemPtr, offset, buffer, maxBytes) ! 1343: Tk_Canvas *canvasPtr; /* Canvas containing selection. */ ! 1344: Tk_Item *itemPtr; /* Text item containing selection. */ ! 1345: int offset; /* Offset within selection of first ! 1346: * character to be returned. */ ! 1347: char *buffer; /* Location in which to place ! 1348: * selection. */ ! 1349: int maxBytes; /* Maximum number of bytes to place ! 1350: * at buffer, not including terminating ! 1351: * NULL character. */ ! 1352: { ! 1353: TextItem *textPtr = (TextItem *) itemPtr; ! 1354: int count; ! 1355: ! 1356: count = canvasPtr->selectLast + 1 - canvasPtr->selectFirst - offset; ! 1357: if (canvasPtr->selectLast == textPtr->numChars) { ! 1358: count -= 1; ! 1359: } ! 1360: if (count > maxBytes) { ! 1361: count = maxBytes; ! 1362: } ! 1363: if (count <= 0) { ! 1364: return 0; ! 1365: } ! 1366: strncpy(buffer, textPtr->text + canvasPtr->selectFirst + offset, count); ! 1367: buffer[count] = '\0'; ! 1368: return count; ! 1369: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.