|
|
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.