|
|
1.1 ! root 1: /* $Header: Text.c,v 1.1 87/09/11 07:58:30 toddb Exp $ */ ! 2: #ifndef lint ! 3: static char *sccsid = "@(#)Text.c 1.27 2/25/87"; ! 4: #endif lint ! 5: ! 6: /* ! 7: * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. ! 8: * ! 9: * All Rights Reserved ! 10: * ! 11: * Permission to use, copy, modify, and distribute this software and its ! 12: * documentation for any purpose and without fee is hereby granted, ! 13: * provided that the above copyright notice appear in all copies and that ! 14: * both that copyright notice and this permission notice appear in ! 15: * supporting documentation, and that the name of Digital Equipment ! 16: * Corporation not be used in advertising or publicity pertaining to ! 17: * distribution of the software without specific, written prior permission. ! 18: * ! 19: * ! 20: * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ! 21: * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ! 22: * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ! 23: * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ! 24: * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ! 25: * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ! 26: * SOFTWARE. ! 27: */ ! 28: ! 29: ! 30: /* File: Text.c */ ! 31: ! 32: #include "Xlib.h" ! 33: #include "Intrinsic.h" ! 34: #include "Text.h" ! 35: #include "Scroll.h" ! 36: #include "Atoms.h" ! 37: #include "TextDisp.h" /** all text subwindow programs include **/ ! 38: #include <strings.h> ! 39: ! 40: /* ||| Kludge, should be in header file somewhere */ ! 41: extern caddr_t XtSetTextEventBindings(); ! 42: ! 43: extern void bcopy(); ! 44: ! 45: #define BufMax 1000 ! 46: #define abs(x) (((x) < 0) ? (-(x)) : (x)) ! 47: #define min(x,y) ((x) < (y) ? (x) : (y)) ! 48: #define max(x,y) ((x) > (y) ? (x) : (y)) ! 49: #define GETLASTPOS (*(ctx->source->scan)) (ctx->source, 0, XtstFile, XtsdRight, 1, TRUE) ! 50: #define BUTTONMASK 0x143d ! 51: ! 52: #define yMargin 2 ! 53: #define zeroPosition ((XtTextPosition) 0) ! 54: ! 55: ! 56: static XContext textContext; ! 57: ! 58: static TextContext glob, globinit; ! 59: ! 60: static Resource resources[] = { ! 61: {XtNwindow, XtCWindow, XrmRWindow, sizeof (Window), ! 62: (caddr_t) &glob.w, NULL}, ! 63: {XtNwidth, XtCWidth, XrmRInt, sizeof (int), ! 64: (caddr_t) &glob.width, NULL}, ! 65: {XtNheight, XtCHeight, XrmRInt, sizeof (int), ! 66: (caddr_t) &glob.height, NULL}, ! 67: {XtNbackground, XtCColor, XrmRPixel, sizeof (int), ! 68: (caddr_t) &glob.background, (caddr_t) &XtDefaultBGPixel}, ! 69: {XtNborder, XtCColor, XrmRPixmap, sizeof (int), ! 70: (caddr_t) &glob.border, (caddr_t) &XtDefaultFGPixel}, ! 71: {XtNborderWidth, XtCBorderWidth, XrmRInt, sizeof (int), ! 72: (caddr_t) &glob.borderWidth, NULL}, ! 73: {XtNdisplayPosition, XtCTextPosition, XrmRInt, ! 74: sizeof (XtTextPosition), (caddr_t) &glob.lt.top, NULL}, ! 75: {XtNselection, XtCSelection, XrmRPointer, sizeof(caddr_t), ! 76: (caddr_t) &glob.s, NULL}, ! 77: {XtNselectionArray, XtCSelectionArray, XrmRPointer, ! 78: sizeof(SelectionArray), (caddr_t) glob.sarray, NULL}, ! 79: {XtNtextOptions, XtCTextOptions, XrmRInt, sizeof (int), ! 80: (caddr_t) &glob.options, NULL}, ! 81: {XtNleftMargin, XtCMargin, XrmRInt, sizeof (int), ! 82: (caddr_t) &glob.leftmargin, NULL}, ! 83: /* ||| {XtNrightMargin, XtCMargin, XtRint, sizeof (int), ! 84: (caddr_t) &rightMargin, NULL}, */ ! 85: {XtNinsertPosition, XtCTextPosition, XtRTextPosition, ! 86: sizeof(XtTextPosition), (caddr_t) &glob.insertPos, NULL}, ! 87: {XtNtextSink, XtCTextSink, XrmRPointer, sizeof (caddr_t), ! 88: (caddr_t) &glob.sink, NULL}, ! 89: {XtNtextSource, XtCTextSource, XrmRPointer, sizeof (caddr_t), ! 90: (caddr_t) &glob.source, NULL}, ! 91: {XtNeventBindings, XtCEventBindings, XtREventBindings, ! 92: sizeof (caddr_t), (caddr_t) &glob.eventTable, NULL}, ! 93: }; ! 94: ! 95: static Boolean initialized = FALSE; ! 96: ! 97: static void TextInitialize() ! 98: { ! 99: if (initialized) ! 100: return; ! 101: initialized = TRUE; ! 102: ! 103: textContext = XUniqueContext(); ! 104: globinit.source = NULL; ! 105: globinit.sink = NULL; ! 106: globinit.lt.top = 0; ! 107: globinit.lt.lines = 0; ! 108: globinit.lt.info = NULL; ! 109: globinit.insertPos = 0; ! 110: globinit.s.left = globinit.s.right = 0; ! 111: globinit.s.type = XtselectPosition; ! 112: globinit.width = 100; ! 113: globinit.height = 0; ! 114: globinit.leftmargin = 2; ! 115: globinit.options = 0; ! 116: globinit.sbar = globinit.outer = globinit.w = NULL; ! 117: globinit.sarray[0] = XtselectPosition; ! 118: globinit.sarray[1] = XtselectWord; ! 119: globinit.sarray[2] = XtselectLine; ! 120: globinit.sarray[3] = XtselectParagraph; ! 121: globinit.sarray[4] = XtselectAll; ! 122: globinit.sarray[5] = XtselectNull; ! 123: globinit.showposition = TRUE; ! 124: globinit.lastPos = 0; ! 125: globinit.dialog = NULL; ! 126: globinit.borderWidth = 1; ! 127: globinit.eventTable = NULL; /* This is WRONG; should be some nice */ ! 128: /* default table. */ ! 129: } ! 130: ! 131: /* Utility routines for support of Text */ ! 132: ! 133: ! 134: /* ! 135: * Procedure to manage insert cursor visibility for editable text. It uses ! 136: * the value of ctx->insertPos and an implicit argument. In the event that ! 137: * position is immediately preceded by an eol graphic, then the insert cursor ! 138: * is displayed at the beginning of the next line. ! 139: */ ! 140: static void InsertCursor (ctx, state) ! 141: TextContext *ctx; ! 142: InsertState state; ! 143: { ! 144: Position x, y; ! 145: int dy, line, visible; ! 146: XtTextBlock text; ! 147: ! 148: if (ctx->lt.lines < 1) return; ! 149: visible = LineAndXYForPosition(ctx, ctx->insertPos, &line, &x, &y); ! 150: if (line < ctx->lt.lines) ! 151: dy = (ctx->lt.info[line + 1].y - ctx->lt.info[line].y) + 1; ! 152: else ! 153: dy = (ctx->lt.info[line].y - ctx->lt.info[line - 1].y) + 1; ! 154: ! 155: /** If the insert position is just after eol then put it on next line **/ ! 156: if (x > ctx->leftmargin && ! 157: ctx->insertPos > 0 && ! 158: ctx->insertPos >= ctx->lastPos) { ! 159: /* reading the source is bogus and this code should use scan */ ! 160: (*(ctx->source->read)) (ctx->source, ctx->insertPos - 1, &text, 1); ! 161: if (text.ptr[0] == '\n') { ! 162: x = ctx->leftmargin; ! 163: y += dy; ! 164: } ! 165: } ! 166: y += dy; ! 167: if (visible) ! 168: (*(ctx->sink->insertCursor))(ctx->sink, ctx->w, x, y, state); ! 169: } ! 170: ! 171: ! 172: /* ! 173: * Procedure to register a span of text that is no longer valid on the display ! 174: * It is used to avoid a number of small, and potentially overlapping, screen ! 175: * updates. [note: this is really a private procedure but is used in ! 176: * multiple modules]. ! 177: */ ! 178: _XtTextNeedsUpdating(ctx, left, right) ! 179: TextContext *ctx; ! 180: XtTextPosition left, right; ! 181: { ! 182: int i; ! 183: if (left < right) { ! 184: for (i = 0; i < ctx->numranges; i++) { ! 185: if (left <= ctx->updateTo[i] && right >= ctx->updateFrom[i]) { ! 186: ctx->updateFrom[i] = min(left, ctx->updateFrom[i]); ! 187: ctx->updateTo[i] = max(right, ctx->updateTo[i]); ! 188: return; ! 189: } ! 190: } ! 191: ctx->numranges++; ! 192: if (ctx->numranges > ctx->maxranges) { ! 193: ctx->maxranges = ctx->numranges; ! 194: i = ctx->maxranges * sizeof(XtTextPosition); ! 195: ctx->updateFrom = (XtTextPosition *) ! 196: XtRealloc((char *)ctx->updateFrom, (unsigned) i); ! 197: ctx->updateTo = (XtTextPosition *) ! 198: XtRealloc((char *)ctx->updateTo, (unsigned) i); ! 199: } ! 200: ctx->updateFrom[ctx->numranges - 1] = left; ! 201: ctx->updateTo[ctx->numranges - 1] = right; ! 202: } ! 203: } ! 204: ! 205: ! 206: /* ! 207: * Procedure to read a span of text in Ascii form. This is purely a hack and ! 208: * we probably need to add a function to sources to provide this functionality. ! 209: * [note: this is really a private procedure but is used in multiple modules]. ! 210: */ ! 211: char *_XtTextGetText(ctx, left, right) ! 212: TextContext *ctx; ! 213: XtTextPosition left, right; ! 214: { ! 215: char *result, *tempResult; ! 216: int length, resultLength; ! 217: XtTextBlock text; ! 218: XtTextPosition end, nend; ! 219: ! 220: resultLength = right - left + 10; /* Bogus? %%% */ ! 221: result = (char *)XtMalloc((unsigned) resultLength); ! 222: end = (*(ctx->source->read))(ctx->source, left, &text, right - left); ! 223: (void) strncpy(result, text.ptr, text.length); ! 224: length = text.length; ! 225: while (end < right) { ! 226: nend = (*(ctx->source->read))(ctx->source, end, &text, right - end); ! 227: tempResult = result + length; ! 228: (void) strncpy(tempResult, text.ptr, text.length); ! 229: length += text.length; ! 230: end = nend; ! 231: } ! 232: result[length] = 0; ! 233: return result; ! 234: } ! 235: ! 236: ! 237: ! 238: /* ! 239: * This routine maps an x and y position in a window that is displaying text ! 240: * into the corresponding position in the source. ! 241: */ ! 242: static XtTextPosition PositionForXY (ctx, x, y) ! 243: TextContext *ctx; ! 244: Position x,y; ! 245: { ! 246: /* it is illegal to call this routine unless there is a valid line table! */ ! 247: int width, fromx, line; ! 248: XtTextPosition position, resultstart, resultend; ! 249: ! 250: /*** figure out what line it is on ***/ ! 251: for (line = 0; line < ctx->lt.lines - 1; line++) { ! 252: if (y <= ctx->lt.info[line + 1].y) ! 253: break; ! 254: } ! 255: position = ctx->lt.info[line].position; ! 256: if (position >= ctx->lastPos) ! 257: return ctx->lastPos; ! 258: fromx = ctx->lt.info[line].x; /* starting x in line */ ! 259: width = x - fromx; /* num of pix from starting of line */ ! 260: (*(ctx->sink->resolve)) (ctx->sink, ctx->source, position, fromx, width, ! 261: &resultstart, &resultend); ! 262: if (resultstart >= ctx->lt.info[line + 1].position) ! 263: resultstart = (*(ctx->source->scan))(ctx->source, ! 264: ctx->lt.info[line + 1].position, XtstPositions, XtsdLeft, 1, TRUE); ! 265: return resultstart; ! 266: } ! 267: ! 268: /* ! 269: * This routine maps a source position in to the corresponding line number ! 270: * of the text that is displayed in the window. ! 271: */ ! 272: int LineForPosition (ctx, position) ! 273: TextContext *ctx; ! 274: XtTextPosition position; ! 275: /* it is illegal to call this routine unless there is a valid line table!*/ ! 276: { ! 277: int line; ! 278: ! 279: if (position <= ctx->lt.info[0].position) ! 280: return 0; ! 281: for (line = 0; line < ctx->lt.lines; line++) ! 282: if (position < ctx->lt.info[line + 1].position) ! 283: break; ! 284: return line; ! 285: } ! 286: ! 287: /* ! 288: * This routine maps a source position into the corresponding line number ! 289: * and the x, y coordinates of the text that is displayed in the window. ! 290: */ ! 291: static int LineAndXYForPosition (ctx, pos, line, x, y) ! 292: TextContext *ctx; ! 293: XtTextPosition pos; ! 294: int *line; ! 295: Position *x, *y; ! 296: /* it is illegal to call this routine unless there is a valid line table!*/ ! 297: { ! 298: XtTextPosition linePos, endPos; ! 299: int visible, realW, realH; ! 300: ! 301: *line = 0; ! 302: *x = ctx->leftmargin; ! 303: *y = yMargin; ! 304: visible = IsPositionVisible(ctx, pos); ! 305: if (visible) { ! 306: *line = LineForPosition(ctx, pos); ! 307: *y = ctx->lt.info[*line].y; ! 308: *x = ctx->lt.info[*line].x; ! 309: linePos = ctx->lt.info[*line].position; ! 310: (*(ctx->sink->findDistance))(ctx->sink, ctx->source, linePos, ! 311: *x, pos, &realW, &endPos, &realH); ! 312: *x = *x + realW; ! 313: } ! 314: return visible; ! 315: } ! 316: ! 317: /* ! 318: * This routine builds a line table. It does this by starting at the ! 319: * specified position and measuring text to determine the staring position ! 320: * of each line to be displayed. It also determines and saves in the ! 321: * linetable all the required metrics for displaying a given line (e.g. ! 322: * x offset, y offset, line length, etc.). ! 323: */ ! 324: static void BuildLineTable (ctx, position) ! 325: TextContext *ctx; ! 326: XtTextPosition position; ! 327: { ! 328: Position x, y; ! 329: Dimension width, realW, realH; ! 330: int line, lines; ! 331: XtTextPosition startPos, endPos; ! 332: Boolean rebuild; ! 333: ! 334: rebuild = (Boolean) (position != ctx->lt.top); ! 335: lines = (*(ctx->sink->maxLines))(ctx->sink, ctx->height); ! 336: if (ctx->lt.info != NULL && lines != ctx->lt.lines) { ! 337: XtFree((char *) ctx->lt.info); ! 338: ctx->lt.info = NULL; ! 339: } ! 340: if (ctx->lt.info == NULL) { ! 341: ctx->lt.info = (LineTableEntry *) ! 342: XtMalloc((unsigned)sizeof(LineTableEntry) * (lines + 1)); ! 343: for (line = 0; line < lines; line++) { ! 344: ctx->lt.info[line].position = 0; ! 345: ctx->lt.info[line].y = 0; ! 346: } ! 347: rebuild = TRUE; ! 348: } ! 349: else ! 350: lines = ctx->lt.lines; ! 351: if (rebuild) { ! 352: ctx->lt.top = position; ! 353: ctx->lt.lines = lines; ! 354: startPos = position; ! 355: y = yMargin; ! 356: for (line = 0; line <= ctx->lt.lines; line++) { ! 357: x = ctx->leftmargin; ! 358: ctx->lt.info[line].x = x; ! 359: ctx->lt.info[line].y = y; ! 360: ctx->lt.info[line].position = startPos; ! 361: if (startPos <= ctx->lastPos) { ! 362: width = (ctx->options & resizeWidth) ? 9999 : ctx->width - x; ! 363: (*(ctx->sink->findPosition))(ctx->sink, ctx->source, ! 364: startPos, x, ! 365: width, (ctx->options & wordBreak), ! 366: &endPos, &realW, &realH); ! 367: if (!(ctx->options & wordBreak) && endPos < ctx->lastPos) { ! 368: endPos = (*(ctx->source->scan))(ctx->source, startPos, ! 369: XtstEOL, XtsdRight, 1, TRUE); ! 370: if (endPos == startPos) ! 371: endPos = ctx->lastPos + 1; ! 372: } ! 373: ctx->lt.info[line].endX = realW + x; ! 374: startPos = endPos; ! 375: } ! 376: else ctx->lt.info[line].endX = x; ! 377: y = y + realH; ! 378: } ! 379: } ! 380: } ! 381: ! 382: /* ! 383: * This routine is used to re-display the entire window, independent of ! 384: * its current state. ! 385: */ ! 386: void ForceBuildLineTable(ctx) ! 387: TextContext *ctx; ! 388: { ! 389: XtTextPosition position; ! 390: ! 391: position = ctx->lt.top; ! 392: ctx->lt.top++; /* ugly, but it works */ ! 393: BuildLineTable(ctx, position); ! 394: } ! 395: ! 396: /* ! 397: * This routine is used by Text to notify an associated scrollbar of the ! 398: * correct metrics (position and shown fraction) for the text being currently ! 399: * displayed in the window. ! 400: */ ! 401: static void SetScrollBar(ctx) ! 402: TextContext *ctx; ! 403: { ! 404: float first, last; ! 405: if (ctx->sbar) { ! 406: if ((ctx->lastPos > 0) && (ctx->lt.lines > 0)) { ! 407: first = ctx->lt.top; ! 408: first /= ctx->lastPos; ! 409: /* Just an approximation */ ! 410: last = ctx->lt.info[ctx->lt.lines].position; ! 411: last /= ctx->lastPos; ! 412: } ! 413: else { ! 414: first = 0.0; ! 415: last = 1.0; ! 416: } ! 417: XtScrollBarSetThumb(ctx->dpy, ctx->sbar, first, last - first); ! 418: } ! 419: } ! 420: ! 421: ! 422: /* ! 423: * The routine will scroll the displayed text by lines. If the arg is ! 424: * positive, move up; otherwise, move down. [note: this is really a private ! 425: * procedure but is used in multiple modules]. ! 426: */ ! 427: _XtTextScroll(ctx, n) ! 428: TextContext *ctx; ! 429: int n; ! 430: { ! 431: XtTextPosition top, target; ! 432: if (n >= 0) { ! 433: top = min(ctx->lt.info[n].position, ctx->lastPos); ! 434: BuildLineTable(ctx, top); ! 435: if (top >= ctx->lastPos) ! 436: DisplayTextWindow(ctx); ! 437: else { ! 438: XCopyArea(ctx->dpy, ctx->w, ctx->w, ctx->gc, ! 439: 0, ctx->lt.info[n].y, ! 440: 9999, (Dimension)ctx->height - ctx->lt.info[n].y, ! 441: 0, ctx->lt.info[0].y); ! 442: (*(ctx->sink->clearToBackground))(ctx->sink, ctx->w, 0, ! 443: ctx->lt.info[0].y + ctx->height - ctx->lt.info[n].y, ! 444: 9999, 9999); ! 445: if (n < ctx->lt.lines) n++; ! 446: _XtTextNeedsUpdating(ctx, ! 447: ctx->lt.info[ctx->lt.lines - n].position, ctx->lastPos); ! 448: SetScrollBar(ctx); ! 449: } ! 450: } else { ! 451: Dimension tempHeight; ! 452: n = -n; ! 453: target = ctx->lt.top; ! 454: top = (*(ctx->source->scan))(ctx->source, target, XtstEOL, ! 455: XtsdLeft, n+1, FALSE); ! 456: tempHeight = ctx->lt.info[ctx->lt.lines-n].y; ! 457: BuildLineTable(ctx, top); ! 458: if (ctx->lt.info[n].position == target) { ! 459: XCopyArea(ctx->dpy, ctx->w, ctx->w, ctx->gc, ! 460: 0, ctx->lt.info[0].y, 9999, tempHeight, ! 461: 0, ctx->lt.info[n].y); ! 462: _XtTextNeedsUpdating(ctx, ! 463: ctx->lt.info[0].position, ctx->lt.info[n].position); ! 464: SetScrollBar(ctx); ! 465: } else if (ctx->lt.top != target) DisplayTextWindow(ctx); ! 466: } ! 467: } ! 468: ! 469: /* ! 470: * The routine will scroll the displayed text by pixels. If the arg is ! 471: * positive, move up; otherwise, move down. ! 472: */ ! 473: /*ARGSUSED*/ /* keep lint happy */ ! 474: static int ScrollUpDownProc (dpy, sbar, w, pix) ! 475: Display *dpy; ! 476: Window sbar, w; ! 477: int pix; ! 478: { ! 479: TextContextPtr ctx; ! 480: int apix, line; ! 481: if (XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) ! 482: return; ! 483: _XtTextPrepareToUpdate(ctx); ! 484: apix = abs(pix); ! 485: for (line = 1; ! 486: line < ctx->lt.lines && apix > ctx->lt.info[line + 1].y; ! 487: line++); ! 488: if (pix >= 0) ! 489: _XtTextScroll(ctx, line); ! 490: else ! 491: _XtTextScroll(ctx, -line); ! 492: _XtTextExecuteUpdate(ctx); ! 493: } ! 494: ! 495: /* ! 496: * The routine "thumbs" the displayed text. Thumbing means reposition the ! 497: * displayed view of the source to a new position determined by a fraction ! 498: * of the way from beginning to end. Ideally, this should be determined by ! 499: * the number of displayable lines in the source. This routine does it as a ! 500: * fraction of the first position and last position and then normalizes to ! 501: * the start of the line containing the position. ! 502: */ ! 503: /*ARGSUSED*/ /* keep lint happy */ ! 504: static int ThumbProc (dpy, sbar, w, where) ! 505: Display *dpy; ! 506: Window sbar, w; ! 507: float where; ! 508: /* BUG/deficiency: The normalize to line portion of this routine will ! 509: * cause thumbing to always position to the start of the source. ! 510: */ ! 511: { ! 512: ! 513: TextContextPtr ctx; ! 514: XtTextPosition position; ! 515: if (XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) ! 516: return; ! 517: _XtTextPrepareToUpdate(ctx); ! 518: position = where * ctx->lastPos; ! 519: position = (*(ctx->source->scan))(ctx->source, position, XtstEOL, XtsdLeft, ! 520: 1, FALSE); ! 521: BuildLineTable(ctx, position); ! 522: DisplayTextWindow(ctx); ! 523: _XtTextExecuteUpdate(ctx); ! 524: } ! 525: ! 526: ! 527: int _XtTextSetNewSelection(ctx, left, right) ! 528: TextContext *ctx; ! 529: XtTextPosition left, right; ! 530: { ! 531: XtTextPosition pos; ! 532: ! 533: if (left < ctx->s.left) { ! 534: pos = min(right, ctx->s.left); ! 535: _XtTextNeedsUpdating(ctx, left, pos); ! 536: } ! 537: if (left > ctx->s.left) { ! 538: pos = min(left, ctx->s.right); ! 539: _XtTextNeedsUpdating(ctx, ctx->s.left, pos); ! 540: } ! 541: if (right < ctx->s.right) { ! 542: pos = max(right, ctx->s.left); ! 543: _XtTextNeedsUpdating(ctx, pos, ctx->s.right); ! 544: } ! 545: if (right > ctx->s.right) { ! 546: pos = max(left, ctx->s.right); ! 547: _XtTextNeedsUpdating(ctx, pos, right); ! 548: } ! 549: ! 550: ctx->s.left = left; ! 551: ctx->s.right = right; ! 552: } ! 553: ! 554: ! 555: ! 556: /* ! 557: * This internal routine deletes the text from pos1 to pos2 in a source and ! 558: * then inserts, at pos1, the text that was passed. As a side effect it ! 559: * "invalidates" that portion of the displayed text (if any). ! 560: */ ! 561: int ReplaceText (ctx, pos1, pos2, text) ! 562: TextContext *ctx; ! 563: XtTextPosition pos1, pos2; ! 564: XtTextBlock *text; ! 565: ! 566: /* it is illegal to call this routine unless there is a valid line table!*/ ! 567: { ! 568: int i, line1, line2, visible, delta, error; ! 569: Position x, y; ! 570: Dimension realW, realH, width; ! 571: XtTextPosition startPos, endPos, updateFrom; ! 572: ! 573: /* the insertPos may not always be set to the right spot in XttextAppend */ ! 574: if ((pos1 == ctx->insertPos) && ! 575: ((*(ctx->source->editType))(ctx->source) == XttextAppend)) { ! 576: ctx->insertPos = GETLASTPOS; ! 577: pos2 = pos2 - pos1 + ctx->insertPos; ! 578: pos1 = ctx->insertPos; ! 579: } ! 580: updateFrom = (*(ctx->source->scan))(ctx->source, pos1, XtstWhiteSpace, XtsdLeft, ! 581: 1, TRUE); ! 582: updateFrom = (*(ctx->source->scan))(ctx->source, updateFrom, XtstPositions, XtsdLeft, ! 583: 1, TRUE); ! 584: startPos = max(updateFrom, ctx->lt.top); ! 585: visible = LineAndXYForPosition(ctx, startPos, &line1, &x, &y); ! 586: error = (*(ctx->source->replace))(ctx->source, pos1, pos2, text, &delta); ! 587: if (error) return error; ! 588: ctx->lastPos = GETLASTPOS; ! 589: if (ctx->lt.top >= ctx->lastPos) { ! 590: BuildLineTable(ctx, ctx->lastPos); ! 591: ClearWindow(ctx); ! 592: SetScrollBar(ctx); ! 593: return error; ! 594: } ! 595: if (delta < ctx->lastPos) { ! 596: for (i = 0; i < ctx->numranges; i++) { ! 597: if (ctx->updateFrom[i] > pos1) ! 598: ctx->updateFrom[i] += delta; ! 599: if (ctx->updateTo[i] >= pos1) ! 600: ctx->updateTo[i] += delta; ! 601: } ! 602: } ! 603: ! 604: line2 = LineForPosition(ctx, pos1); ! 605: /* ! 606: * fixup all current line table entries to reflect edit. ! 607: * BUG: it is illegal to do arithmetic on positions. This code should ! 608: * either use scan or the source needs to provide a function for doing ! 609: * position arithmetic. ! 610: */ ! 611: for (i = line2 + 1; i <= ctx->lt.lines; i++) ! 612: ctx->lt.info[i].position += delta; ! 613: ! 614: endPos = pos1; ! 615: /* ! 616: * Now process the line table and fixup in case edits caused ! 617: * changes in line breaks. If we are breaking on word boundaries, ! 618: * this code checks for moving words to and from lines. ! 619: */ ! 620: if (visible) { ! 621: for (i = line1; i < ctx->lt.lines; i++) {/* fixup line table */ ! 622: width = (ctx->options & resizeWidth) ? 9999 : ctx->width - x; ! 623: if (startPos <= ctx->lastPos) { ! 624: (*(ctx->sink->findPosition))(ctx->sink, ! 625: ctx->source, startPos, x, ! 626: width, (ctx->options & wordBreak), ! 627: &endPos, &realW, &realH); ! 628: if (!(ctx->options & wordBreak) && endPos < ctx->lastPos) { ! 629: endPos = (*(ctx->source->scan))(ctx->source, startPos, ! 630: XtstEOL, XtsdRight, 1, TRUE); ! 631: if (endPos == startPos) ! 632: endPos = ctx->lastPos + 1; ! 633: } ! 634: ctx->lt.info[i].endX = realW + x; ! 635: ctx->lt.info[i + 1].y = realH + ctx->lt.info[i].y; ! 636: if ((endPos > pos1) && ! 637: (endPos == ctx->lt.info[i + 1].position)) ! 638: break; ! 639: startPos = endPos; ! 640: } ! 641: if (startPos > ctx->lastPos) ! 642: ctx->lt.info[i + 1].endX = ctx->leftmargin; ! 643: ctx->lt.info[i + 1].position = startPos; ! 644: x = ctx->lt.info[i + 1].x; ! 645: } ! 646: } ! 647: if (delta >= ctx->lastPos) ! 648: endPos = ctx->lastPos; ! 649: if (delta >= ctx->lastPos || pos2 >= ctx->lt.top) ! 650: _XtTextNeedsUpdating(ctx, updateFrom, endPos); ! 651: SetScrollBar(ctx); ! 652: return error; ! 653: } ! 654: ! 655: ! 656: /* ! 657: * This routine will display text between two arbitrary source positions. ! 658: * In the event that this span contains highlighted text for the selection, ! 659: * only that portion will be displayed highlighted. ! 660: */ ! 661: static void DisplayText(ctx, pos1, pos2) ! 662: TextContext *ctx; ! 663: XtTextPosition pos1, pos2; ! 664: /* it is illegal to call this routine unless there is a valid line table!*/ ! 665: { ! 666: Position x, y; ! 667: Dimension height; ! 668: int line, i, visible; ! 669: XtTextPosition startPos, endPos; ! 670: ! 671: if (pos1 < ctx->lt.top) ! 672: pos1 = ctx->lt.top; ! 673: if (pos2 > ctx->lastPos) ! 674: pos2 = ctx->lastPos; ! 675: if (pos1 >= pos2) return; ! 676: visible = LineAndXYForPosition(ctx, pos1, &line, &x, &y); ! 677: if (!visible) ! 678: return; ! 679: startPos = pos1; ! 680: height = ctx->lt.info[1].y - ctx->lt.info[0].y; ! 681: for (i = line; i < ctx->lt.lines; i++) { ! 682: endPos = ctx->lt.info[i + 1].position; ! 683: if (endPos > pos2) ! 684: endPos = pos2; ! 685: if (endPos > startPos) { ! 686: if (x == ctx->leftmargin) ! 687: (*(ctx->sink->clearToBackground))(ctx->sink, ctx->w, ! 688: 0, y, ctx->leftmargin, height); ! 689: if (startPos >= ctx->s.right || endPos <= ctx->s.left) { ! 690: (*(ctx->sink->display))(ctx->sink, ctx->source, ctx->w, x, y, ! 691: startPos, endPos, FALSE); ! 692: } else if (startPos >= ctx->s.left && endPos <= ctx->s.right) { ! 693: (*(ctx->sink->display))(ctx->sink, ctx->source, ctx->w, x, y, ! 694: startPos, endPos, TRUE); ! 695: } else { ! 696: DisplayText(ctx, startPos, ctx->s.left); ! 697: DisplayText(ctx, max(startPos, ctx->s.left), ! 698: min(endPos, ctx->s.right)); ! 699: DisplayText(ctx, ctx->s.right, endPos); ! 700: } ! 701: } ! 702: startPos = endPos; ! 703: height = ctx->lt.info[i + 1].y - ctx->lt.info[i].y; ! 704: (*(ctx->sink->clearToBackground))(ctx->sink, ctx->w, ! 705: ctx->lt.info[i].endX, y, 999, height); ! 706: x = ctx->leftmargin; ! 707: y = ctx->lt.info[i + 1].y; ! 708: if ((endPos == pos2) && (endPos != ctx->lastPos)) ! 709: break; ! 710: } ! 711: } ! 712: ! 713: /* ! 714: * This routine implements multi-click selection in a hardwired manner. ! 715: * It supports multi-click entity cycling (char, word, line, file) and mouse ! 716: * motion adjustment of the selected entitie (i.e. select a word then, with ! 717: * button still down, adjust wich word you really meant by moving the mouse). ! 718: * [NOTE: This routine is to be replaced by a set of procedures that ! 719: * will allows clients to implements a wide class of draw through and ! 720: * multi-click selection user interfaces.] ! 721: */ ! 722: static void DoSelection (ctx, position, time, motion) ! 723: TextContext *ctx; ! 724: XtTextPosition position; ! 725: unsigned short time; ! 726: Boolean motion; ! 727: { ! 728: int delta; ! 729: XtTextPosition newLeft, newRight; ! 730: XtSelectType newType; ! 731: XtSelectType *sarray; ! 732: ! 733: delta = (time < ctx->lasttime) ? ! 734: ctx->lasttime - time : time - ctx->lasttime; ! 735: if (motion) ! 736: newType = ctx->s.type; ! 737: else { ! 738: if ((delta < 500) && ((position >= ctx->s.left) ! 739: && (position <= ctx->s.right))) { /* multi-click event */ ! 740: sarray = ctx->sarray; ! 741: for (sarray = ctx->sarray; ! 742: *sarray != XtselectNull && *sarray != ctx->s.type; ! 743: sarray++) ; ! 744: if (*sarray != XtselectNull) sarray++; ! 745: if (*sarray == XtselectNull) sarray = ctx->sarray; ! 746: newType = *sarray; ! 747: } else { /* single-click event */ ! 748: newType = *(ctx->sarray); ! 749: } ! 750: ctx->lasttime = time; ! 751: } ! 752: switch (newType) { ! 753: case XtselectPosition: ! 754: newLeft = newRight = position; ! 755: break; ! 756: case XtselectChar: ! 757: newLeft = position; ! 758: newRight = (*(ctx->source->scan))( ! 759: ctx->source, position, position, XtsdRight, 1, FALSE); ! 760: break; ! 761: case XtselectWord: ! 762: newLeft = (*(ctx->source->scan))( ! 763: ctx->source, position, XtstWhiteSpace, XtsdLeft, 1, FALSE); ! 764: newRight = (*(ctx->source->scan))( ! 765: ctx->source, position, XtstWhiteSpace, XtsdRight, 1, FALSE); ! 766: break; ! 767: case XtselectLine: ! 768: case XtselectParagraph: /* need "para" scan mode to implement pargraph */ ! 769: newLeft = (*(ctx->source->scan))( ! 770: ctx->source, position, XtstEOL, XtsdLeft, 1, FALSE); ! 771: newRight = (*(ctx->source->scan))( ! 772: ctx->source, position, XtstEOL, XtsdRight, 1, FALSE); ! 773: break; ! 774: case XtselectAll: ! 775: newLeft = (*(ctx->source->scan))( ! 776: ctx->source, position, XtstFile, XtsdLeft, 1, FALSE); ! 777: newRight = (*(ctx->source->scan))( ! 778: ctx->source, position, XtstFile, XtsdRight, 1, FALSE); ! 779: break; ! 780: } ! 781: if ((newLeft != ctx->s.left) || (newRight != ctx->s.right) ! 782: || (newType != ctx->s.type)) { ! 783: _XtTextSetNewSelection(ctx, newLeft, newRight); ! 784: ctx->s.type = newType; ! 785: if (position - ctx->s.left < ctx->s.right - position) ! 786: ctx->insertPos = newLeft; ! 787: else ! 788: ctx->insertPos = newRight; ! 789: } ! 790: if (!motion) { /* setup so we can freely mix select extend calls*/ ! 791: ctx->origSel.type = ctx->s.type; ! 792: ctx->origSel.left = ctx->s.left; ! 793: ctx->origSel.right = ctx->s.right; ! 794: if (position >= ctx->s.left + ((ctx->s.right - ctx->s.left) / 2)) ! 795: ctx->extendDir = XtsdRight; ! 796: else ! 797: ctx->extendDir = XtsdLeft; ! 798: } ! 799: } ! 800: ! 801: /* ! 802: * This routine implements extension of the currently selected text in ! 803: * the "current" mode (i.e. char word, line, etc.). It worries about ! 804: * extending from either end of the selection and handles the case when you ! 805: * cross through the "center" of the current selection (e.g. switch which ! 806: * end you are extending!). ! 807: * [NOTE: This routine will be replaced by a set of procedures that ! 808: * will allows clients to implements a wide class of draw through and ! 809: * multi-click selection user interfaces.] ! 810: */ ! 811: static void ExtendSelection (ctx, position, motion) ! 812: TextContext *ctx; ! 813: XtTextPosition position; ! 814: Boolean motion; ! 815: { ! 816: XtTextPosition newLeft, newRight; ! 817: ! 818: ! 819: if (!motion) { /* setup for extending selection */ ! 820: ctx->origSel.type = ctx->s.type; ! 821: ctx->origSel.left = ctx->s.left; ! 822: ctx->origSel.right = ctx->s.right; ! 823: if (position >= ctx->s.left + ((ctx->s.right - ctx->s.left) / 2)) ! 824: ctx->extendDir = XtsdRight; ! 825: else ! 826: ctx->extendDir = XtsdLeft; ! 827: } ! 828: else /* check for change in extend direction */ ! 829: if ((ctx->extendDir == XtsdRight && position < ctx->origSel.left) || ! 830: (ctx->extendDir == XtsdLeft && position > ctx->origSel.right)) { ! 831: ctx->extendDir = (ctx->extendDir == XtsdRight)? XtsdLeft : XtsdRight; ! 832: _XtTextSetNewSelection(ctx, ctx->origSel.left, ctx->origSel.right); ! 833: } ! 834: newLeft = ctx->s.left; ! 835: newRight = ctx->s.right; ! 836: switch (ctx->s.type) { ! 837: case XtselectPosition: ! 838: if (ctx->extendDir == XtsdRight) ! 839: newRight = position; ! 840: else ! 841: newLeft = position; ! 842: break; ! 843: case XtselectWord: ! 844: if (ctx->extendDir == XtsdRight) ! 845: newRight = position = (*(ctx->source->scan))( ! 846: ctx->source, position, XtstWhiteSpace, XtsdRight, 1, FALSE); ! 847: else ! 848: newLeft = position = (*(ctx->source->scan))( ! 849: ctx->source, position, XtstWhiteSpace, XtsdLeft, 1, FALSE); ! 850: break; ! 851: case XtselectLine: ! 852: case XtselectParagraph: /* need "para" scan mode to implement pargraph */ ! 853: if (ctx->extendDir == XtsdRight) ! 854: newRight = position = (*(ctx->source->scan))( ! 855: ctx->source, position, XtstEOL, XtsdRight, 1, TRUE); ! 856: else ! 857: newLeft = position = (*(ctx->source->scan))( ! 858: ctx->source, position, XtstEOL, XtsdLeft, 1, FALSE); ! 859: break; ! 860: case XtselectAll: ! 861: position = ctx->insertPos; ! 862: break; ! 863: } ! 864: _XtTextSetNewSelection(ctx, newLeft, newRight); ! 865: ctx->insertPos = position; ! 866: } ! 867: ! 868: ! 869: /* ! 870: * Clear the window to background color. ! 871: */ ! 872: static ClearWindow (ctx) ! 873: TextContext *ctx; ! 874: { ! 875: (*(ctx->sink->clearToBackground))(ctx->sink, ctx->w, 0, 0, ctx->width, ! 876: ctx->height); ! 877: } ! 878: ! 879: ! 880: /* ! 881: * Internal redisplay entire window. ! 882: */ ! 883: DisplayTextWindow (ctx) ! 884: TextContext *ctx; ! 885: { ! 886: ClearWindow(ctx); ! 887: BuildLineTable(ctx, ctx->lt.top); ! 888: _XtTextNeedsUpdating(ctx, zeroPosition, ctx->lastPos); ! 889: SetScrollBar(ctx); ! 890: } ! 891: ! 892: /* ! 893: * This routine checks to see if the window should be resized (grown or ! 894: * shrunk) or scrolled then text to be painted overflows to the right or ! 895: * the bottom of the window. It is used by the keyboard input routine. ! 896: */ ! 897: CheckResizeOrOverflow(ctx) ! 898: TextContext *ctx; ! 899: { ! 900: XtTextPosition posToCheck; ! 901: int visible, line, width; ! 902: WindowBox rbox, abox; ! 903: if (ctx->options & resizeWidth) { ! 904: width = 0; ! 905: for (line=0 ; line<ctx->lt.lines ; line++) ! 906: if (width < ctx->lt.info[line].endX) ! 907: width = ctx->lt.info[line].endX; ! 908: width += ctx->leftmargin; ! 909: if (width > ctx->width) { ! 910: rbox.x = rbox.y = 0; ! 911: rbox.width = width; ! 912: rbox.height = ctx->height; ! 913: (void) XtMakeGeometryRequest(ctx->dpy, ctx->w, XtgeometryResize, &rbox, &abox); ! 914: } ! 915: } ! 916: if ((ctx->options & resizeHeight) || (ctx->options & scrollOnOverflow)) { ! 917: if (ctx->options & scrollOnOverflow) ! 918: posToCheck = ctx->insertPos; ! 919: else ! 920: posToCheck = ctx->lastPos; ! 921: visible = IsPositionVisible(ctx, posToCheck); ! 922: if (visible) ! 923: line = LineForPosition(ctx, posToCheck); ! 924: else ! 925: line = ctx->lt.lines; ! 926: if ((ctx->options & scrollOnOverflow) && (line + 1 > ctx->lt.lines)) { ! 927: BuildLineTable(ctx, ctx->lt.info[1].position); ! 928: XCopyArea(ctx->dpy, ctx->w, ctx->w, ctx->gc, ! 929: (Position)ctx->leftmargin, ctx->lt.info[1].y, 9999, 9999, ! 930: (Position)ctx->leftmargin, ctx->lt.info[0].y); ! 931: } ! 932: else ! 933: if ((ctx->options & resizeHeight) && (line + 1 != ctx->lt.lines)) { ! 934: rbox.x = 0; ! 935: rbox.y = 0; ! 936: rbox.width = ctx->width; ! 937: rbox.height = (*(ctx->sink->maxHeight)) ! 938: (ctx->sink, line + 1) + (2*yMargin)+2; ! 939: (void) XtMakeGeometryRequest(ctx->dpy, ctx->w, XtgeometryResize, &rbox, &abox); ! 940: } ! 941: } ! 942: } ! 943: ! 944: /* ! 945: * This routine processes all keyboard, button and mouse XEvents. It is ! 946: * responsible for performing any key/button to function mapping (with the ! 947: * help of the translation manager) as well as doing any edits of editable ! 948: * sources. ! 949: */ ! 950: static void ProcessKeysAndButtons (event, ctx) ! 951: XEvent *event; ! 952: TextContext *ctx; ! 953: { ! 954: Boolean more; ! 955: XtTextBlock text; ! 956: XEvent ev; ! 957: XtActionTokenPtr actionList; ! 958: ActionProc proc; ! 959: ! 960: if (event->type == MotionNotify) { ! 961: while (QLength(ctx->dpy)) { ! 962: XPeekEvent(ctx->dpy, &ev); ! 963: if (ev.type == MotionNotify) { ! 964: XNextEvent(ctx->dpy, &ev); ! 965: event = &ev; ! 966: } else break; ! 967: } ! 968: } ! 969: ! 970: ctx->time = event->xbutton.time; ! 971: ctx->x = event->xbutton.x; ! 972: ctx->y = event->xbutton.y; ! 973: do { ! 974: actionList = (XtActionTokenPtr) XtTranslateEvent( ! 975: event, (TranslationPtr) ctx->state); ! 976: if (actionList == NULL) ! 977: return; ! 978: while (actionList != NULL) { ! 979: switch (actionList->type) { ! 980: case XttokenAction: ! 981: proc = (ActionProc) XtInterpretAction(ctx->dpy, ! 982: (TranslationPtr) ctx->state, actionList->value.action); ! 983: (*(proc))(ctx); ! 984: break; ! 985: case XttokenChar: ! 986: case XttokenString: ! 987: if (actionList->type == XttokenString) { ! 988: text.ptr = actionList->value.str; ! 989: text.length = strlen(actionList->value.str); ! 990: } ! 991: else { ! 992: text.ptr = &actionList->value.c; ! 993: text.length = 1; ! 994: } ! 995: text.firstPos = 0; ! 996: if (ReplaceText(ctx, ctx->insertPos, ctx->insertPos, ! 997: &text)) { ! 998: XBell(ctx->dpy, 50); ! 999: break; ! 1000: } ! 1001: ctx->insertPos = ! 1002: (*(ctx->source->scan))(ctx->source, ctx->insertPos, ! 1003: XtstPositions, XtsdRight, text.length, TRUE); ! 1004: _XtTextSetNewSelection(ctx, ! 1005: ctx->insertPos, ctx->insertPos); ! 1006: break; ! 1007: } ! 1008: actionList = actionList->next; ! 1009: } ! 1010: more = FALSE; ! 1011: if (QLength(ctx->dpy)) { ! 1012: XPeekEvent(ctx->dpy, &ev); ! 1013: if (ev.type == KeyPress) { ! 1014: XNextEvent(ctx->dpy, &ev); ! 1015: event = &ev; ! 1016: more = TRUE; ! 1017: } ! 1018: } ! 1019: } while (more); ! 1020: CheckResizeOrOverflow(ctx); ! 1021: } ! 1022: ! 1023: /* ! 1024: * This routine is used to perform various selection functions. The goal is ! 1025: * to be able to specify all the more popular forms of draw-through and ! 1026: * multi-click selection user interfaces from the outside. ! 1027: */ ! 1028: void AlterSelection (ctx, mode, action) ! 1029: TextContext *ctx; ! 1030: SelectionMode mode; /* {XtsmTextSelect, XtsmTextExtend} */ ! 1031: SelectionAction action; /* {XtactionStart, XtactionAdjust, XtactionEnd} */ ! 1032: { ! 1033: XtTextPosition position; ! 1034: char *ptr; ! 1035: ! 1036: position = PositionForXY (ctx, (int) ctx->x, (int) ctx->y); ! 1037: if (action == XtactionStart) { ! 1038: switch (mode) { ! 1039: case XtsmTextSelect: ! 1040: DoSelection (ctx, position, ctx->time, FALSE); ! 1041: break; ! 1042: case XtsmTextExtend: ! 1043: ExtendSelection (ctx, position, FALSE); ! 1044: break; ! 1045: } ! 1046: } ! 1047: else { ! 1048: switch (mode) { ! 1049: case XtsmTextSelect: ! 1050: DoSelection (ctx, position, ctx->time, TRUE); ! 1051: break; ! 1052: case XtsmTextExtend: ! 1053: ExtendSelection (ctx, position, TRUE); ! 1054: break; ! 1055: } ! 1056: } ! 1057: if (action == XtactionEnd && ctx->s.left < ctx->s.right) { ! 1058: ptr = _XtTextGetText (ctx, ctx->s.left, ctx->s.right); ! 1059: XStoreBuffer (ctx->dpy, ptr, min (strlen (ptr), MAXCUT), 0); ! 1060: XtFree (ptr); ! 1061: } ! 1062: } ! 1063: ! 1064: /* ! 1065: * This routine processes all "expose region" XEvents. In general, its job ! 1066: * is to the best job at minimal re-paint of the text, displayed in the ! 1067: * window, that it can. ! 1068: */ ! 1069: static ProcessExposeRegion(event, ctx) ! 1070: XEvent *event; ! 1071: TextContextPtr ctx; ! 1072: { ! 1073: XtTextPosition pos1, pos2, resultend; ! 1074: int line; ! 1075: int x = event->xexpose.x; ! 1076: int y = event->xexpose.y; ! 1077: int width = event->xexpose.width; ! 1078: int height = event->xexpose.height; ! 1079: LineTableEntryPtr info; ! 1080: if (x < ctx->leftmargin) /* stomp on caret tracks */ ! 1081: (*(ctx->sink->clearToBackground))(ctx->sink, ctx->w, x, y, ! 1082: width, height); ! 1083: /* figure out starting line that was exposed */ ! 1084: line = LineForPosition(ctx, PositionForXY(ctx, x, y)); ! 1085: while (line < ctx->lt.lines && ctx->lt.info[line + 1].y < y) ! 1086: line++; ! 1087: while (line < ctx->lt.lines) { ! 1088: info = &(ctx->lt.info[line]); ! 1089: if (info->y >= y + height) ! 1090: break; ! 1091: (*(ctx->sink->resolve))(ctx->sink, ctx->source, ! 1092: info->position, info->x, ! 1093: x - info->x, &pos1, &resultend); ! 1094: (*(ctx->sink->resolve))(ctx->sink, ctx->source, ! 1095: info->position, info->x, ! 1096: x + width - info->x, &pos2, ! 1097: &resultend); ! 1098: pos2 = (*(ctx->source->scan))(ctx->source, pos2, XtstPositions, ! 1099: XtsdRight, 1, TRUE); ! 1100: _XtTextNeedsUpdating(ctx, pos1, pos2); ! 1101: line++; ! 1102: } ! 1103: } ! 1104: ! 1105: ! 1106: static int oldinsert = -1; ! 1107: ! 1108: /* ! 1109: * This routine does all setup required to syncronize batched screen updates ! 1110: */ ! 1111: int _XtTextPrepareToUpdate(ctx) ! 1112: TextContext *ctx; ! 1113: { ! 1114: if (oldinsert < 0) { ! 1115: InsertCursor(ctx, XtisOff); ! 1116: ctx->numranges = 0; ! 1117: ctx->showposition = FALSE; ! 1118: oldinsert = ctx->insertPos; ! 1119: } ! 1120: } ! 1121: ! 1122: ! 1123: /* ! 1124: * This is a private utility routine used by _XtTextExecuteUpdate. It ! 1125: * processes all the outstanding update requests and merges update ! 1126: * ranges where possible. ! 1127: */ ! 1128: static FlushUpdate(ctx) ! 1129: TextContext *ctx; ! 1130: { ! 1131: int i, w; ! 1132: XtTextPosition updateFrom, updateTo; ! 1133: while (ctx->numranges > 0) { ! 1134: updateFrom = ctx->updateFrom[0]; ! 1135: w = 0; ! 1136: for (i=1 ; i<ctx->numranges ; i++) { ! 1137: if (ctx->updateFrom[i] < updateFrom) { ! 1138: updateFrom = ctx->updateFrom[i]; ! 1139: w = i; ! 1140: } ! 1141: } ! 1142: updateTo = ctx->updateTo[w]; ! 1143: ctx->numranges--; ! 1144: ctx->updateFrom[w] = ctx->updateFrom[ctx->numranges]; ! 1145: ctx->updateTo[w] = ctx->updateTo[ctx->numranges]; ! 1146: for (i=ctx->numranges-1 ; i>=0 ; i--) { ! 1147: while (ctx->updateFrom[i] <= updateTo && i < ctx->numranges) { ! 1148: updateTo = ctx->updateTo[i]; ! 1149: ctx->numranges--; ! 1150: ctx->updateFrom[i] = ctx->updateFrom[ctx->numranges]; ! 1151: ctx->updateTo[i] = ctx->updateTo[ctx->numranges]; ! 1152: } ! 1153: } ! 1154: DisplayText(ctx, updateFrom, updateTo); ! 1155: } ! 1156: } ! 1157: ! 1158: ! 1159: /* ! 1160: * This is a private utility routine used by _XtTextExecuteUpdate. This routine ! 1161: * worries about edits causing new data or the insertion point becoming ! 1162: * invisible (off the screen). Currently it always makes it visible by ! 1163: * scrolling. It probably needs generalization to allow more options. ! 1164: */ ! 1165: _XtTextShowPosition(ctx) ! 1166: TextContext *ctx; ! 1167: { ! 1168: XtTextPosition top, first, second; ! 1169: if (ctx->insertPos < ctx->lt.top || ! 1170: ctx->insertPos >= ctx->lt.info[ctx->lt.lines].position) { ! 1171: if (ctx->lt.lines > 0 && (ctx->insertPos < ctx->lt.top || ! 1172: ctx->lt.info[ctx->lt.lines].position <= ctx->lastPos)) { ! 1173: first = ctx->lt.top; ! 1174: second = ctx->lt.info[1].position; ! 1175: if (ctx->insertPos < first) ! 1176: top = (*(ctx->source->scan))(ctx->source, ctx->insertPos, XtstEOL, ! 1177: XtsdLeft, 1, FALSE); ! 1178: else ! 1179: top = (*(ctx->source->scan))(ctx->source, ctx->insertPos, XtstEOL, ! 1180: XtsdLeft, ctx->lt.lines, FALSE); ! 1181: BuildLineTable(ctx, top); ! 1182: while (ctx->insertPos >= ctx->lt.info[ctx->lt.lines].position) { ! 1183: if (ctx->lt.info[ctx->lt.lines].position > ctx->lastPos) ! 1184: break; ! 1185: BuildLineTable(ctx, ctx->lt.info[1].position); ! 1186: } ! 1187: if (ctx->lt.top == second) { ! 1188: BuildLineTable(ctx, first); ! 1189: _XtTextScroll(ctx, 1); ! 1190: } else if (ctx->lt.info[1].position == first) { ! 1191: BuildLineTable(ctx, first); ! 1192: _XtTextScroll(ctx, -1); ! 1193: } else { ! 1194: ctx->numranges = 0; ! 1195: if (ctx->lt.top != first) ! 1196: DisplayTextWindow(ctx); ! 1197: } ! 1198: } ! 1199: } ! 1200: } ! 1201: ! 1202: ! 1203: ! 1204: /* ! 1205: * This routine causes all batched screen updates to be performed ! 1206: */ ! 1207: _XtTextExecuteUpdate(ctx) ! 1208: TextContext *ctx; ! 1209: { ! 1210: if (oldinsert >= 0) { ! 1211: if (oldinsert != ctx->insertPos || ctx->showposition) ! 1212: _XtTextShowPosition(ctx); ! 1213: FlushUpdate(ctx); ! 1214: InsertCursor(ctx, XtisOn); ! 1215: oldinsert = -1; ! 1216: } ! 1217: } ! 1218: ! 1219: ! 1220: static HandleDestroyNotify(ctx) ! 1221: TextContext *ctx; ! 1222: { ! 1223: if (ctx->dialog) ! 1224: (void) XtSendDestroyNotify(ctx->dpy, ctx->dialog); ! 1225: (void) XDeleteContext(ctx->dpy, ctx->w, textContext); ! 1226: if (ctx->outer) ! 1227: (void) XDeleteContext(ctx->dpy, ctx->outer, textContext); ! 1228: XtFree((char *)ctx->updateFrom); ! 1229: XtFree((char *)ctx->updateTo); ! 1230: XtFree((char *)ctx); ! 1231: } ! 1232: ! 1233: ! 1234: /* ! 1235: * This is the main routine for handling all selected XEvents. It is normally ! 1236: * the routine to be registered with the Xtoolkit Event dispatcher. It is ! 1237: * currently private and should probably be made public to allow interposition ! 1238: * programming techniques ! 1239: */ ! 1240: static XtEventReturnCode ProcessTextEvent(event, eventdata) ! 1241: XEvent *event; ! 1242: caddr_t eventdata; ! 1243: { ! 1244: TextContext *ctx = (TextContext *) eventdata; ! 1245: XtEventReturnCode returnCode; ! 1246: ! 1247: if (event->type != DestroyNotify && event->type != FocusIn && ! 1248: event->type != FocusOut && event->type != EnterNotify && ! 1249: event->type != LeaveNotify) ! 1250: _XtTextPrepareToUpdate(ctx); ! 1251: returnCode = XteventHandled; ! 1252: switch (event->type) { ! 1253: case ButtonPress: ! 1254: if (!ctx->hasfocus) ! 1255: XSetInputFocus(ctx->dpy, ctx->w, RevertToPointerRoot, ! 1256: CurrentTime); ! 1257: /* Fall through. */ ! 1258: case ButtonRelease: ! 1259: case MotionNotify: ! 1260: case KeyPress: ! 1261: ProcessKeysAndButtons(event, ctx); ! 1262: break; ! 1263: case ConfigureNotify: ! 1264: ctx->width = event->xconfigure.width; ! 1265: ctx->height = event->xconfigure.height; ! 1266: ForceBuildLineTable(ctx); ! 1267: break; ! 1268: case Expose: ! 1269: case GraphicsExpose: ! 1270: ProcessExposeRegion(event, ctx); ! 1271: break; ! 1272: case DestroyNotify: ! 1273: HandleDestroyNotify(ctx); ! 1274: return XteventHandled; /* Avoid the call to _XtTextExecuteUpdate!*/ ! 1275: case FocusIn: ! 1276: ctx->hasfocus = TRUE; ! 1277: return(returnCode); ! 1278: case FocusOut: ! 1279: ctx->hasfocus = FALSE; ! 1280: return(returnCode); ! 1281: case EnterNotify: ! 1282: case LeaveNotify: ! 1283: ctx->hasfocus = event->xcrossing.focus; ! 1284: return(returnCode); ! 1285: default: ! 1286: returnCode = XteventNotHandled; ! 1287: } ! 1288: _XtTextExecuteUpdate(ctx); ! 1289: return(returnCode); ! 1290: } ! 1291: ! 1292: /* Public routines */ ! 1293: ! 1294: Window XtTextCreate(dpy, parent, args, argCount) ! 1295: Display *dpy; ! 1296: Window parent; ! 1297: ArgList args; ! 1298: int argCount; ! 1299: { ! 1300: TextContext *ctx; ! 1301: XrmNameList names; ! 1302: XrmClassList classes; ! 1303: ! 1304: /* stuff for scroll bars */ ! 1305: static Arg scrollMgrArgs[] = { ! 1306: {XtNwindow, NULL}, ! 1307: }; ! 1308: ! 1309: static Arg scrollBarArgs[] = { ! 1310: {XtNvalue, NULL}, ! 1311: {XtNorientation, (XtArgVal) XtorientVertical}, ! 1312: {XtNscrollUpDownProc, (XtArgVal)ScrollUpDownProc}, ! 1313: {XtNthumbProc, (XtArgVal)ThumbProc}, ! 1314: }; ! 1315: ! 1316: ! 1317: if (!initialized) TextInitialize(); ! 1318: ! 1319: ctx = (TextContext *) XtMalloc(sizeof(TextContext)); ! 1320: glob = globinit; ! 1321: glob.dpy = dpy; ! 1322: XtGetResources(dpy, resources, XtNumber(resources), args, argCount, parent, ! 1323: "text", "Text", &names, &classes); ! 1324: *ctx = glob; ! 1325: ctx->state = XtSetTextEventBindings(ctx->dpy, ctx->eventTable); ! 1326: ctx->lastPos = GETLASTPOS; ! 1327: ctx->updateFrom = (XtTextPosition *) XtMalloc(1); ! 1328: ctx->updateTo = (XtTextPosition *) XtMalloc(1); ! 1329: ctx->numranges = ctx->maxranges = 0; ! 1330: if (ctx->height == 0) ! 1331: ctx->height = (*(ctx->sink->maxHeight))(ctx->sink, 1) + (2*yMargin) +2; ! 1332: if (ctx->w == NULL) ! 1333: ctx->w = XCreateSimpleWindow(ctx->dpy, parent, 0, 0, ! 1334: ctx->width, ctx->height, ! 1335: ctx->borderWidth, ctx->border, ctx->background); ! 1336: /* ! 1337: * Note that if the client passed a window, no checks are made to ! 1338: * ensure the size, border, borderWidth and background are consistent ! 1339: */ ! 1340: XtSetNameAndClass(ctx->dpy, ctx->w, names, classes); ! 1341: XrmFreeNameList(names); ! 1342: XrmFreeClassList(classes); ! 1343: ! 1344: if (ctx->options & scrollVertical) { ! 1345: scrollMgrArgs[0].value = ! 1346: scrollBarArgs[0].value = (caddr_t)(ctx->w); ! 1347: ctx->outer = ! 1348: XtScrollMgrCreate(ctx->dpy, parent, scrollMgrArgs, XtNumber(scrollMgrArgs)); ! 1349: ctx->w = XtScrollMgrGetChild(ctx->dpy, ctx->outer); ! 1350: ctx->sbar = ! 1351: XtScrollMgrAddBar(ctx->dpy, ctx->outer, scrollBarArgs, XtNumber(scrollBarArgs)); ! 1352: XMapSubwindows(ctx->dpy, ctx->outer); ! 1353: (void) XSaveContext(ctx->dpy, ctx->outer, textContext, (caddr_t)ctx); ! 1354: (void) XtSetEventHandler(ctx->dpy, ctx->outer, ProcessTextEvent, ! 1355: StructureNotifyMask, (caddr_t) ctx); ! 1356: } ! 1357: (void) XSaveContext(ctx->dpy, ctx->w, textContext, (caddr_t)ctx); ! 1358: (void) XtSetEventHandler(ctx->dpy, ctx->w, ProcessTextEvent, ! 1359: ButtonPressMask | ButtonReleaseMask | ButtonMotionMask ! 1360: | KeyPressMask | ExposureMask | StructureNotifyMask ! 1361: | FocusChangeMask | EnterWindowMask | LeaveWindowMask, ! 1362: (caddr_t) ctx); ! 1363: ctx->gc = DefaultGC(ctx->dpy, DefaultScreen(ctx->dpy)); ! 1364: BuildLineTable(ctx, ctx->lt.top); ! 1365: if (ctx->outer) return ctx->outer; ! 1366: else return ctx->w; ! 1367: } ! 1368: ! 1369: ! 1370: /* ! 1371: * This routine allow the application program to Get attributes. ! 1372: */ ! 1373: ! 1374: void XtTextGetValues(dpy, window, args, argCount) ! 1375: Display *dpy; ! 1376: Window window; ! 1377: ArgList args; ! 1378: int argCount; ! 1379: { ! 1380: TextContext *ctx; ! 1381: if (!XFindContext(dpy, window, textContext, (caddr_t *)&ctx)) { ! 1382: glob = *ctx; ! 1383: XtGetValues(resources, XtNumber(resources), args, argCount); ! 1384: } ! 1385: } ! 1386: ! 1387: ! 1388: /* ! 1389: * This routine allow the application program to Set attributes. ! 1390: */ ! 1391: ! 1392: void XtTextSetValues(dpy, window, args, argCount) ! 1393: Display *dpy; ! 1394: Window window; ! 1395: ArgList args; ! 1396: int argCount; ! 1397: { ! 1398: TextContext *ctx; ! 1399: Boolean redisplay = FALSE; ! 1400: if (XFindContext(dpy, window, textContext, (caddr_t *)&ctx)) return; ! 1401: ! 1402: _XtTextPrepareToUpdate(ctx); ! 1403: glob = *ctx; ! 1404: XtSetValues(resources, XtNumber(resources), args, argCount); ! 1405: ! 1406: if (ctx->sink != glob.sink) { ! 1407: ctx->sink = glob.sink; ! 1408: redisplay = TRUE; ! 1409: } ! 1410: if (ctx->source != glob.source) { ! 1411: ctx->source = glob.source; ! 1412: ForceBuildLineTable(ctx); ! 1413: redisplay = TRUE; ! 1414: } ! 1415: if (ctx->insertPos != glob.insertPos) { ! 1416: ctx->insertPos = glob.insertPos; ! 1417: ctx->showposition = TRUE; ! 1418: } ! 1419: if (ctx->lt.top != glob.lt.top) { ! 1420: ctx->lt.top = glob.lt.top; ! 1421: redisplay = TRUE; ! 1422: } ! 1423: ctx->s = glob.s; ! 1424: ctx->options = glob.options; ! 1425: if (ctx->leftmargin != glob.leftmargin) { ! 1426: ctx->leftmargin = glob.leftmargin; ! 1427: redisplay = TRUE; ! 1428: } ! 1429: bcopy(glob.sarray, ctx->sarray, sizeof(SelectionArray)); ! 1430: ctx->background = glob.background; ! 1431: ! 1432: if (redisplay) ! 1433: DisplayTextWindow(ctx); ! 1434: _XtTextExecuteUpdate(ctx); ! 1435: } ! 1436: ! 1437: ! 1438: ! 1439: void XtTextDestroy(dpy, w) ! 1440: Display *dpy; ! 1441: Window w; ! 1442: { ! 1443: TextContext *ctx; ! 1444: ! 1445: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1446: HandleDestroyNotify(ctx); ! 1447: XDestroyWindow(ctx->dpy, w); ! 1448: } ! 1449: } ! 1450: ! 1451: void XtTextDisplay (dpy, w) ! 1452: Display *dpy; ! 1453: Window w; ! 1454: { ! 1455: TextContext *ctx; ! 1456: ! 1457: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1458: _XtTextPrepareToUpdate(ctx); ! 1459: DisplayTextWindow(ctx); ! 1460: _XtTextExecuteUpdate(ctx); ! 1461: } ! 1462: } ! 1463: ! 1464: /******************************************************************* ! 1465: The following routines provide procedural interfaces to Text window state ! 1466: setting and getting. They need to be redone so than the args code can use ! 1467: them. I suggest we create a complete set that takes the context as an ! 1468: argument and then have the public version lookup the context and call the ! 1469: internal one. The major value of this set is that they have actual application ! 1470: clients and therefore the functionality provided is required for any future ! 1471: version of Text. ! 1472: ********************************************************************/ ! 1473: ! 1474: void XtTextSetSelectionArray(dpy, w, sarray) ! 1475: Display *dpy; ! 1476: Window w; ! 1477: XtSelectType *sarray; ! 1478: { ! 1479: TextContext *ctx; ! 1480: XtSelectType *s2; ! 1481: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1482: s2 = ctx->sarray; ! 1483: while (XtselectNull != (*s2++ = *sarray++)) ; ! 1484: } ! 1485: } ! 1486: ! 1487: void XtTextSetLastPos (dpy, w, lastPos) ! 1488: Display *dpy; ! 1489: Window w; ! 1490: XtTextPosition lastPos; ! 1491: { ! 1492: TextContext * ctx; ! 1493: ! 1494: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1495: _XtTextPrepareToUpdate(ctx); ! 1496: (*(ctx->source->setLastPos))(ctx->source, lastPos); ! 1497: ctx->lastPos = GETLASTPOS; ! 1498: ForceBuildLineTable(ctx); ! 1499: DisplayTextWindow(ctx); ! 1500: _XtTextExecuteUpdate(ctx); ! 1501: } ! 1502: } ! 1503: ! 1504: ! 1505: void XtTextGetSelectionPos(dpy, w, left, right) ! 1506: Display *dpy; ! 1507: Window w; ! 1508: XtTextPosition *left, *right; ! 1509: { ! 1510: TextContext *ctx; ! 1511: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)){ ! 1512: *left = ctx->s.left; ! 1513: *right = ctx->s.right; ! 1514: } ! 1515: } ! 1516: ! 1517: ! 1518: void XtTextNewSource(dpy, w, source, startPos) ! 1519: Display *dpy; ! 1520: Window w; ! 1521: XtTextSource *source; ! 1522: XtTextPosition startPos; ! 1523: { ! 1524: TextContext *ctx; ! 1525: ! 1526: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1527: ctx->source = source; ! 1528: ctx->lt.top = startPos; ! 1529: ctx->s.left = ctx->s.right = 0; ! 1530: ctx->insertPos = startPos; ! 1531: ctx->lastPos = GETLASTPOS; ! 1532: ForceBuildLineTable(ctx); ! 1533: _XtTextPrepareToUpdate(ctx); ! 1534: DisplayTextWindow(ctx); ! 1535: _XtTextExecuteUpdate(ctx); ! 1536: } ! 1537: } ! 1538: ! 1539: /* ! 1540: * This public routine deletes the text from startPos to endPos in a source and ! 1541: * then inserts, at startPos, the text that was passed. As a side effect it ! 1542: * "invalidates" that portion of the displayed text (if any), so that things ! 1543: * will be repainted properly. ! 1544: */ ! 1545: int XtTextReplace(dpy, w, startPos, endPos, text) ! 1546: Display *dpy; ! 1547: Window w; ! 1548: XtTextPosition startPos, endPos; ! 1549: XtTextBlock *text; ! 1550: { ! 1551: TextContext *ctx; ! 1552: int result; ! 1553: result = EDITERROR; ! 1554: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1555: _XtTextPrepareToUpdate(ctx); ! 1556: result = ReplaceText(ctx, startPos, endPos, text); ! 1557: _XtTextExecuteUpdate(ctx); ! 1558: } ! 1559: return result; ! 1560: } ! 1561: ! 1562: ! 1563: XtTextPosition XtTextTopPosition(dpy, w) ! 1564: Display *dpy; ! 1565: Window w; ! 1566: { ! 1567: TextContext *ctx; ! 1568: ! 1569: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) ! 1570: return ctx->lt.top; ! 1571: return 0; ! 1572: } ! 1573: ! 1574: ! 1575: void XtTextSetInsertionPoint(dpy, w, position) ! 1576: Display *dpy; ! 1577: Window w; ! 1578: XtTextPosition position; ! 1579: { ! 1580: TextContext *ctx; ! 1581: ! 1582: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1583: _XtTextPrepareToUpdate(ctx); ! 1584: ctx->insertPos = position; ! 1585: ctx->showposition = TRUE; ! 1586: _XtTextExecuteUpdate(ctx); ! 1587: } ! 1588: } ! 1589: ! 1590: ! 1591: XtTextPosition XtTextGetInsertionPoint(dpy, w) ! 1592: Display *dpy; ! 1593: Window w; ! 1594: { ! 1595: XtTextPosition position; ! 1596: TextContext *ctx; ! 1597: ! 1598: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1599: position = ctx->insertPos; ! 1600: } else { ! 1601: position = 0; ! 1602: } ! 1603: return(position); ! 1604: } ! 1605: ! 1606: ! 1607: void XtTextUnsetSelection(dpy, w) ! 1608: Display *dpy; ! 1609: Window w; ! 1610: { ! 1611: TextContext *ctx; ! 1612: ! 1613: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1614: _XtTextPrepareToUpdate(ctx); ! 1615: _XtTextSetNewSelection(ctx, zeroPosition, zeroPosition); ! 1616: _XtTextExecuteUpdate(ctx); ! 1617: } ! 1618: } ! 1619: ! 1620: ! 1621: void XtTextChangeOptions(dpy, w, options) ! 1622: Display *dpy; ! 1623: Window w; ! 1624: int options; ! 1625: { ! 1626: TextContext *ctx; ! 1627: ! 1628: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) ! 1629: ctx->options = options; ! 1630: } ! 1631: ! 1632: ! 1633: int XtTextGetOptions(dpy, w) ! 1634: Display *dpy; ! 1635: Window w; ! 1636: { ! 1637: TextContext *ctx; ! 1638: ! 1639: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) ! 1640: return ctx->options; ! 1641: else return 0; ! 1642: } ! 1643: ! 1644: void XtTextSetNewSelection (dpy, w, left, right) ! 1645: Display *dpy; ! 1646: Window w; ! 1647: XtTextPosition left, right; ! 1648: { ! 1649: TextContextPtr ctx; ! 1650: ! 1651: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)){ ! 1652: _XtTextPrepareToUpdate(ctx); ! 1653: _XtTextSetNewSelection(ctx, left, right); ! 1654: _XtTextExecuteUpdate(ctx); ! 1655: } ! 1656: } ! 1657: ! 1658: void XtTextInvalidate(dpy, w, from, to) ! 1659: Display *dpy; ! 1660: Window w; ! 1661: XtTextPosition from,to; ! 1662: { ! 1663: TextContextPtr ctx; ! 1664: ! 1665: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1666: ctx->lastPos = (*(ctx->source->getLastPos))(ctx->source); ! 1667: _XtTextPrepareToUpdate(ctx); ! 1668: _XtTextNeedsUpdating(ctx, from, to); ! 1669: ForceBuildLineTable(ctx); ! 1670: _XtTextExecuteUpdate(ctx); ! 1671: } ! 1672: } ! 1673: ! 1674: ! 1675: /* Returns the window actually containing the text (which is not the same ! 1676: as the given window if the text window has scrollbars.) */ ! 1677: ! 1678: Window XtTextGetInnerWindow(dpy, w) ! 1679: Display *dpy; ! 1680: Window w; ! 1681: { ! 1682: TextContextPtr ctx; ! 1683: if (!XFindContext(dpy, w, textContext, (caddr_t *)&ctx)) { ! 1684: return ctx->w; ! 1685: } ! 1686: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.