|
|
1.1 root 1: /*
2: * A Scrollable Text Output Window
3: *
4: * David Harrison
5: * University of California, Berkeley
6: * 1986
7: *
8: * The following is an implementation for a scrollable text output
9: * system. It handles exposure events only (other interactions are
10: * under user control). For scrolling, a always present scroll bar
11: * is implemented. It detects size changes and compensates accordingly.
12: */
13:
14: #include <X11/X.h>
15: #include <X11/Xlib.h>
16: #include <X11/X10.h>
17: #include <sys/types.h>
18: #include "scrollText.h"
19:
20: extern char *malloc();
21: extern char *realloc();
22: #define alloc(type) (type *) malloc(sizeof(type))
23: #define numalloc(type, num) (type *) malloc((unsigned) (num * sizeof(type)))
24: #define MAXINT 2147483647
25:
26: extern XAssocTable *XCreateAssocTable();
27: extern caddr_t XLookUpAssoc();
28:
29: static XAssocTable *textWindows = (XAssocTable *) 0;
30:
31: #define NOOPTION -1 /* Option hasn't been set yet */
32: #define NORMSCROLL 0 /* Smooth scroll on LineToTop and TopToHere */
33: #define JUMPSCROLL 1 /* Jump scrolling on LineToTop and TopToHere */
34:
35: static int ScrollOption = NOOPTION;
36:
37: typedef char *Generic;
38:
39: #define DEFAULT_GC textInfo->fontGC[textInfo->curFont]
40:
41: #define BARSIZE 15
42: #define BARBORDER 1
43: #define MAXFONTS 8
44: #define INITBUFSIZE 1024
45: #define INITLINES 50
46: #define INITEXPARY 50
47: #define XPADDING 2
48: #define YPADDING 2
49: #define INTERLINE 5
50: #define INTERSPACE 1
51: #define CURSORWIDTH 2
52: #define EXPANDPERCENT 40
53: #define BUFSIZE 1024
54: #define CUROFFSET 1
55: #define MAXFOREIGN 250
56: #define NOINDEX -1
57:
58: /* The wrap line indicator */
59: #define WRAPINDSIZE 7
60: #define STEMOFFSET 5
61: #define arrow_width 7
62: #define arrow_height 5
63: static char arrow_bits[] = {
64: 0x24, 0x26, 0x3f, 0x06, 0x04};
65:
66: #define NEWLINE '\n'
67: #define BACKSPACE '\010'
68: #define NEWFONT '\006'
69: #define LOWCHAR '\040'
70: #define HIGHCHAR '\176'
71:
72: #define CHARMASK 0x00ff /* Character mask */
73: #define FONTMASK 0x0700 /* Character font */
74: #define FONTSHIFT 8 /* Shift amount */
75:
76: #define WRAPFLAG 0x01 /* Line wrap flag */
77:
78: /*
79: * Lines are represented by a pointer into the overall array of
80: * 16-bit characters. The lower eight bits is used to indicate the character
81: * (in ASCII), and the next two bits are used to indicate the font
82: * the character should be drawn in.
83: */
84:
85: typedef struct txtLine {
86: int lineLength; /* Current line length */
87: int lineHeight; /* Full height of line in pixels */
88: int lineBaseLine; /* Current baseline of the line */
89: int lineWidth; /* Drawing position at end of line */
90: int lineText; /* Offset into master buffer */
91: int lineFlags; /* Line wrap flag is here */
92: };
93:
94:
95: /*
96: * For ExposeCopy events, we queue up the redraw requests collapsing
97: * them into line redraw requests until the CopyExpose event arrives.
98: * The queue is represented as a dynamic array of the following
99: * structure:
100: */
101:
102: typedef struct expEvent {
103: int lineIndex; /* Index of line to redraw */
104: int ypos; /* Drawing position of line */
105: };
106:
107:
108: /*
109: * The text buffer is represented using a dynamic counted array
110: * of 16-bit quantities. This array expands as needed.
111: * For the screen representation, a dynamic counted array
112: * of line structures is used. This array points into the
113: * text buffer to denote the start of each line and its parameters.
114: * The windows are configured as one overall window which contains
115: * the scroll bar as a sub-window along its right edge. Thus,
116: * the text drawing space is actually w-BARSIZE.
117: */
118:
119: #define NOTATBOTTOM 0x01 /* Need to scroll to bottom before appending */
120: #define FONTNUMWAIT 0x02 /* Waiting for font number */
121: #define COPYEXPOSE 0x04 /* Need to process a copy expose event */
122: #define SCREENWRONG 0x08 /* TxtJamStr has invalidated screen contents */
123:
124: typedef struct txtWin {
125: /* Basic text buffer */
126: int bufAlloc; /* Allocated size of buffer */
127: int bufSpot; /* Current writing position in buffer */
128: short *mainBuffer; /* Main buffer of text */
129:
130: /* Line information */
131: int numLines; /* Number of display lines in buffer */
132: int allocLines; /* Number of lines allocated */
133: struct txtLine **txtBuffer; /* Dynamic array of lines */
134:
135: /* Current Window display information */
136: Window mainWindow; /* Text display window */
137: Window scrollBar; /* Subwindow for scroll bar */
138: Pixmap arrowMap; /* line wrap indicator */
139: int bgPix, fgPix; /* Background and cursor */
140: GC CursorGC; /* gc for the cursor */
141: GC bgGC; /* gc for erasing things */
142: GC fontGC[MAXFONTS]; /* gc for doing fonts */
143: XFontStruct theFonts[MAXFONTS];/* Display fonts */
144: int theColors[MAXFONTS]; /* foregrounds of the fonts */
145: int curFont; /* current font for tracking */
146: int w, h; /* Current size */
147: int startLine; /* Top line in display */
148: int endLine; /* Bottom line in display */
149: int bottomSpace; /* Space at bottom of screen */
150: int flagWord; /* If non-zero, not at end */
151:
152: /* For handling ExposeCopy events */
153: int exposeSize; /* Current size of array */
154: int exposeAlloc; /* Allocated size */
155: struct expEvent **exposeAry;/* Array of line indices */
156:
157: /* Drawing position information */
158: int curLine; /* Current line in buffer */
159: int curX; /* Current horizontal positi */
160: int curY; /* Current vertical drawing */
161: };
162:
163: /* Flags for the various basic character handling functions */
164:
165: #define DODISP 0x01 /* Update the display */
166: #define NONEWLINE 0x02 /* Dont append newline */
167:
168:
169:
170: static int InitLine(newLine)
171: struct txtLine *newLine; /* Newly created line structure */
172: /*
173: * This routine initializes a newly created line structure.
174: */
175: {
176: newLine->lineLength = 0;
177: newLine->lineHeight = 0;
178: newLine->lineBaseLine = 0;
179: newLine->lineWidth = XPADDING;
180: newLine->lineText = NOINDEX;
181: newLine->lineFlags = 0;
182: return 1;
183: }
184:
185:
186:
187:
188: int TxtGrab(display, txtWin, program, mainFont, bg, fg, cur)
189: Display *display; /* display window is on */
190: Window txtWin; /* Window to take over as scrollable text */
191: char *program; /* Program name for Xdefaults */
192: XFontStruct *mainFont; /* Primary text font */
193: int bg, fg, cur; /* Background, foreground, and cursor colors */
194: /*
195: * This routine takes control of 'txtWin' and makes it into a scrollable
196: * text output window. It will create a sub-window for the scroll bar
197: * with a background of 'bg' and an bar with color 'fg'. Both fixed width
198: * and variable width fonts are supported. Additional fonts can be loaded
199: * using 'TxtAddFont'. Returns 0 if there were problems, non-zero if
200: * everything went ok.
201: */
202: {
203: struct txtWin *newWin; /* Text package specific information */
204: XWindowAttributes winInfo; /* Window information */
205: int index;
206: XGCValues gc_val;
207:
208: if (textWindows == (XAssocTable *) 0) {
209: textWindows = XCreateAssocTable(32);
210: if (textWindows == (XAssocTable *) 0) return(0);
211: }
212: if (XGetWindowAttributes(display, txtWin, &winInfo) == 0) return 0;
213:
214: if (ScrollOption == NOOPTION) {
215: /* Read to see if the user wants jump scrolling or not */
216: if (XGetDefault(display, program, "JumpScroll")) {
217: ScrollOption = JUMPSCROLL;
218: } else {
219: ScrollOption = NORMSCROLL;
220: }
221: }
222:
223: /* Initialize local structure */
224: newWin = alloc(struct txtWin);
225:
226: /* Initialize arrow pixmap */
227: newWin->arrowMap = XCreatePixmapFromBitmapData(display, txtWin,
228: arrow_bits,
229: arrow_width, arrow_height,
230: cur, bg,
231: DisplayPlanes(display, 0));
232:
233: newWin->bufAlloc = INITBUFSIZE;
234: newWin->bufSpot = 0;
235: newWin->mainBuffer = numalloc(short, INITBUFSIZE);
236:
237: newWin->numLines = 1;
238: newWin->allocLines = INITLINES;
239: newWin->txtBuffer = numalloc(struct txtLine *, INITLINES);
240: for (index = 0; index < INITLINES; index++) {
241: newWin->txtBuffer[index] = alloc(struct txtLine);
242: InitLine(newWin->txtBuffer[index]);
243: }
244:
245: /* Window display information */
246: newWin->mainWindow = txtWin;
247: newWin->w = winInfo.width;
248: newWin->h = winInfo.height;
249: newWin->startLine = 0;
250: newWin->endLine = 0;
251: newWin->bottomSpace = winInfo.height
252: - YPADDING - mainFont->ascent - mainFont->descent - INTERLINE;
253: newWin->flagWord = 0;
254: newWin->bgPix = bg;
255: newWin->fgPix = fg;
256:
257: /* Scroll Bar Creation */
258: newWin->scrollBar = XCreateSimpleWindow(display, txtWin,
259: winInfo.width - BARSIZE,
260: 0, BARSIZE - (2*BARBORDER),
261: winInfo.height - (2*BARBORDER),
262: BARBORDER,
263: fg, bg);
264: XSelectInput(display, newWin->scrollBar, ExposureMask|ButtonReleaseMask);
265: XMapRaised(display, newWin->scrollBar);
266:
267: /* Font and Color Initialization */
268: newWin->theFonts[0] = *mainFont;
269: newWin->theColors[0] = fg;
270: gc_val.function = GXcopy;
271: gc_val.plane_mask = AllPlanes;
272: gc_val.foreground = fg;
273: gc_val.background = bg;
274: gc_val.graphics_exposures = 1;
275: gc_val.font = mainFont->fid;
276: gc_val.line_width = 1;
277: gc_val.line_style = LineSolid;
278:
279: newWin->fontGC[0] = XCreateGC(display, txtWin,
280: GCFunction | GCPlaneMask |
281: GCForeground | GCBackground |
282: GCGraphicsExposures | GCFont,
283: &gc_val);
284:
285: gc_val.foreground = cur;
286: newWin->CursorGC = XCreateGC(display, txtWin,
287: GCFunction | GCPlaneMask |
288: GCForeground | GCBackground |
289: GCLineStyle | GCLineWidth,
290: &gc_val);
291:
292: gc_val.foreground = bg;
293: newWin->bgGC = XCreateGC(display, txtWin,
294: GCFunction | GCPlaneMask |
295: GCForeground | GCBackground |
296: GCGraphicsExposures | GCFont,
297: &gc_val);
298:
299:
300: for (index = 1; index < MAXFONTS; index++) {
301: newWin->theFonts[index].fid = 0;
302: newWin->fontGC[index] = 0;
303: }
304:
305:
306: /* Initialize size of first line */
307: newWin->txtBuffer[0]->lineHeight = newWin->theFonts[0].ascent +
308: newWin->theFonts[0].descent;
309: newWin->txtBuffer[0]->lineText = 0;
310:
311: /* ExposeCopy array initialization */
312: newWin->exposeSize = 0;
313: newWin->exposeAlloc = INITEXPARY;
314: newWin->exposeAry = numalloc(struct expEvent *, INITEXPARY);
315: for (index = 0; index < newWin->exposeAlloc; index++)
316: newWin->exposeAry[index] = alloc(struct expEvent);
317: /* Put plus infinity in last slot for sorting purposes */
318: newWin->exposeAry[0]->lineIndex = MAXINT;
319:
320: /* Drawing Position Information */
321: newWin->curLine = 0;
322: newWin->curX = 0;
323: newWin->curY = YPADDING + mainFont->ascent + mainFont->descent;
324:
325: /* Attach it to both windows */
326: XMakeAssoc(display, textWindows, (XID) txtWin, (caddr_t) newWin);
327: XMakeAssoc(display, textWindows, (XID) newWin->scrollBar, (caddr_t) newWin);
328: return 1;
329: }
330:
331:
332: int TxtRelease(display, w)
333: Display *display;
334: Window w; /* Window to release */
335: /*
336: * This routine releases all resources associated with the
337: * specified window which are consumed by the text
338: * window package. This includes the entire text buffer, line start
339: * array, and the scroll bar window. However, the window
340: * itself is NOT destroyed. The routine will return zero if
341: * the window is not owned by the text window package.
342: */
343: {
344: struct txtWin *textInfo;
345: int index;
346:
347: if ((textInfo = (struct txtWin *) XLookUpAssoc(display,
348: textWindows, (XID) w)) == 0)
349: return 0;
350:
351: for (index = 0; index < MAXFONTS; index++)
352: if (textInfo->fontGC[index] != 0)
353: XFreeGC(display, textInfo->fontGC[index]);
354:
355: free((Generic) textInfo->mainBuffer);
356: for (index = 0; index < textInfo->numLines; index++) {
357: free((Generic) textInfo->txtBuffer[index]);
358: }
359: free((Generic) textInfo->txtBuffer);
360: XDestroyWindow(display, textInfo->scrollBar);
361: for (index = 0; index < textInfo->exposeSize; index++) {
362: free((Generic) textInfo->exposeAry[index]);
363: }
364: free((Generic) textInfo->exposeAry);
365: XDeleteAssoc(display, textWindows, (XID) w);
366: free((Generic) textInfo);
367: return 1;
368: }
369:
370:
371:
372: static int RecompBuffer(textInfo)
373: struct txtWin *textInfo; /* Text window information */
374: /*
375: * This routine recomputes all line breaks in a buffer after
376: * a change in window size or font. This is done by throwing
377: * away the old line start array and recomputing it. Although
378: * a lot of this work is also done elsewhere, it has been included
379: * inline here for efficiency.
380: */
381: {
382: int startPos, endSize, linenum;
383: register int index, chsize, curfont;
384: register short *bufptr;
385: register XFontStruct *fontptr;
386: register struct txtLine *lineptr;
387: char theChar;
388:
389: /* Record the old position so we can come back to it */
390: for (startPos = textInfo->txtBuffer[textInfo->startLine]->lineText;
391: (startPos > 0) && (textInfo->mainBuffer[startPos] != '\n');
392: startPos--)
393: /* null loop body */;
394:
395: /* Clear out the old line start array */
396: for (index = 0; index < textInfo->numLines; index++) {
397: InitLine(textInfo->txtBuffer[index]);
398: }
399:
400: /* Initialize first line */
401: textInfo->txtBuffer[0]->lineHeight =
402: textInfo->theFonts[0].ascent + textInfo->theFonts[0].descent;
403: textInfo->txtBuffer[0]->lineText = 0;
404:
405: /* Process the text back into lines */
406: endSize = textInfo->w - BARSIZE - WRAPINDSIZE;
407: bufptr = textInfo->mainBuffer;
408: lineptr = textInfo->txtBuffer[0];
409: linenum = 0;
410: fontptr = &(textInfo->theFonts[0]);
411: curfont = 0;
412: for (index = 0; index < textInfo->bufSpot; index++) {
413: theChar = bufptr[index] & CHARMASK;
414:
415: if ((bufptr[index] & FONTMASK) != curfont) {
416: int newFontNum, heightDiff;
417:
418: /* Switch fonts */
419: newFontNum = (bufptr[index] & FONTMASK) >> FONTSHIFT;
420: if (textInfo->theFonts[newFontNum].fid != 0) {
421: /* Valid font */
422: curfont = bufptr[index] & FONTMASK;
423: fontptr = &(textInfo->theFonts[newFontNum]);
424: heightDiff = (fontptr->ascent + fontptr->descent) -
425: lineptr->lineHeight;
426: if (heightDiff < 0) heightDiff = 0;
427: lineptr->lineHeight += heightDiff;
428: }
429: }
430: if (theChar == '\n') {
431: /* Handle new line */
432: if (linenum >= textInfo->allocLines-1)
433: /* Expand number of lines */
434: ExpandLines(textInfo);
435: linenum++;
436: lineptr = textInfo->txtBuffer[linenum];
437: /* Initialize next line */
438: lineptr->lineHeight = fontptr->ascent + fontptr->descent;
439: lineptr->lineText = index+1;
440: /* Check to see if its the starting line */
441: if (index == startPos) textInfo->startLine = linenum;
442: } else {
443: /* Handle normal character */
444: chsize = CharSize(textInfo, linenum, index);
445: if (lineptr->lineWidth + chsize > endSize) {
446: /* Handle line wrap */
447: lineptr->lineFlags |= WRAPFLAG;
448: if (linenum >= textInfo->allocLines-1)
449: /* Expand number of lines */
450: ExpandLines(textInfo);
451: linenum++;
452: lineptr = textInfo->txtBuffer[linenum];
453: /* Initialize next line */
454: lineptr->lineHeight = fontptr->ascent + fontptr->descent;
455: lineptr->lineText = index;
456: lineptr->lineLength = 1;
457: lineptr->lineWidth += chsize;
458: } else {
459: /* Handle normal addition of character */
460: lineptr->lineLength += 1;
461: lineptr->lineWidth += chsize;
462: }
463: }
464: }
465: /* We now have a valid line array. Let's clean up some other fields. */
466: textInfo->numLines = linenum+1;
467: if (startPos == 0) {
468: textInfo->startLine = 0;
469: }
470: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
471: textInfo->curLine = linenum;
472: /* Check to see if we are at the bottom */
473: if (textInfo->endLine >= textInfo->numLines-1) {
474: textInfo->curY = textInfo->h - textInfo->bottomSpace -
475: lineptr->lineHeight;
476: textInfo->flagWord &= (~NOTATBOTTOM);
477: } else {
478: textInfo->flagWord |= NOTATBOTTOM;
479: }
480: return 1;
481: }
482:
483:
484:
485:
486: int TxtAddFont(display, textWin, fontNumber, newFont, newColor)
487: Display *display;
488: Window textWin; /* Scrollable text window */
489: int fontNumber; /* Place to add font (0-7) */
490: XFontStruct *newFont; /* Font to add */
491: int newColor; /* Color of font */
492: /*
493: * This routine loads a new font so that it can be used in a previously
494: * created text window. There are eight font slots numbered 0 through 7.
495: * If there is already a font in the specified slot, it will be replaced
496: * and an automatic redraw of the window will take place. See TxtWriteStr
497: * for details on using alternate fonts. The color specifies the foreground
498: * color of the text. The default foreground color is used if this
499: * parameter is TXT_NO_COLOR. Returns a non-zero value if
500: * everything went well.
501: */
502: {
503: struct txtWin *textInfo;
504: int redrawFlag;
505: XGCValues gc_val;
506:
507: if ((fontNumber < 0) || (fontNumber >= MAXFONTS)) return 0;
508: if ((textInfo = (struct txtWin *)
509: XLookUpAssoc(display, textWindows, (XID) textWin)) == 0)
510: return 0;
511: if (newColor == TXT_NO_COLOR) {
512: newColor = textInfo->fgPix;
513: }
514:
515: gc_val.font = newFont->fid;
516: gc_val.foreground = newColor;
517: gc_val.background = textInfo->bgPix;
518: gc_val.plane_mask = AllPlanes;
519: gc_val.graphics_exposures = 1;
520: gc_val.function = GXcopy;
521:
522: if (textInfo->fontGC[fontNumber] != 0)
523: {
524: XChangeGC(display, textInfo->fontGC[fontNumber],
525: GCFont | GCForeground, &gc_val);
526: }
527: else
528: textInfo->fontGC[fontNumber] = XCreateGC(display, textWin,
529: GCFont |
530: GCForeground |
531: GCBackground |
532: GCFunction |
533: GCPlaneMask |
534: GCGraphicsExposures,
535: &gc_val);
536:
537:
538: redrawFlag = (textInfo->theFonts[fontNumber].fid != 0) &&
539: (((newFont) && (newFont->fid != textInfo->theFonts[fontNumber].fid)) ||
540: (newColor != textInfo->theColors[fontNumber]));
541: if (newFont) {
542: textInfo->theFonts[fontNumber] = *newFont;
543: }
544: textInfo->theColors[fontNumber] = newColor;
545:
546: if (redrawFlag) {
547: RecompBuffer(textInfo);
548: XClearWindow(display, textWin);
549: TxtRepaint(display, textWin);
550: }
551: return 1;
552: }
553:
554:
555:
556: int TxtWinP(display, w)
557: Display *display;
558: Window w;
559: /*
560: * Returns a non-zero value if the window has been previously grabbed
561: * using TxtGrab and 0 if it has not.
562: */
563: {
564: if (XLookUpAssoc(display, textWindows, (XID) w))
565: return(1);
566: else return(0);
567: }
568:
569:
570:
571: static int FindEndLine(textInfo, botSpace)
572: struct txtWin *textInfo;
573: int *botSpace;
574: /*
575: * Given the starting line in 'textInfo->startLine', this routine
576: * determines the index of the last line that can be drawn given the
577: * current size of the screen. If there are not enough lines to
578: * fill the screen, the index of the last line will be returned.
579: * The amount of empty bottom space is returned in 'botSpace'.
580: */
581: {
582: int index, height, lineHeight;
583:
584: height = YPADDING;
585: index = textInfo->startLine;
586: while (index < textInfo->numLines) {
587: lineHeight = textInfo->txtBuffer[index]->lineHeight + INTERLINE;
588: if (height + lineHeight > textInfo->h) break;
589: height += lineHeight;
590: index++;
591: }
592: if (botSpace) {
593: *botSpace = textInfo->h - height;
594: }
595: return index - 1;
596: }
597:
598:
599:
600: static int UpdateScroll(display, textInfo)
601: Display *display;
602: struct txtWin *textInfo; /* Text window information */
603: /*
604: * This routine computes the current extent of the scroll bar
605: * indicator and repaints the bar with the correct information.
606: */
607: {
608: int top, bottom;
609:
610: if (textInfo->numLines > 1) {
611: top = textInfo->startLine * (textInfo->h - 2*BARBORDER) /
612: (textInfo->numLines - 1);
613: bottom = textInfo->endLine * (textInfo->h - 2*BARBORDER) /
614: (textInfo->numLines - 1);
615: } else {
616: top = 0;
617: bottom = textInfo->h - (2*BARBORDER);
618: }
619:
620: /* Draw it - make sure there is a little padding */
621: if (top == 0) top++;
622: if (bottom == textInfo->h-(2*BARBORDER)) bottom--;
623:
624: XFillRectangle(display, textInfo->scrollBar,
625: textInfo->bgGC,
626: 0, 0, BARSIZE, top-1);
627: XFillRectangle(display, textInfo->scrollBar,
628: DEFAULT_GC, top, BARSIZE - (2*BARBORDER) - 2,
629: bottom - top);
630: XFillRectangle(display, textInfo->scrollBar, DEFAULT_GC,
631: 0, bottom+1, BARSIZE,
632: textInfo->h - (2 * BARBORDER) - bottom);
633:
634: return 1;
635: }
636:
637:
638:
639:
640: int TxtClear(display, w)
641: Display *display;
642: Window w;
643: /*
644: * This routine clears a scrollable text window. It resets the current
645: * writing position to the upper left hand corner of the screen.
646: * NOTE: THIS ALSO CLEARS THE CONTENTS OF THE TEXT WINDOW BUFFER AND
647: * RESETS THE SCROLL BAR. Returns 0 if the window is not a text window.
648: * This should be used *instead* of XClear.
649: */
650: {
651: struct txtWin *textInfo;
652: int index;
653:
654: if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w)) == 0)
655: return 0;
656:
657: /* Zero out the arrays */
658: textInfo->bufSpot = 0;
659: for (index = 0; index < textInfo->numLines; index++) {
660: InitLine(textInfo->txtBuffer[index]);
661: }
662: textInfo->txtBuffer[0]->lineHeight =
663: textInfo->theFonts[textInfo->curFont].ascent +
664: textInfo->theFonts[textInfo->curFont].descent;
665:
666: textInfo->numLines = 1;
667: textInfo->startLine = 0;
668: textInfo->endLine = 0;
669: textInfo->curLine = 0;
670: textInfo->curX = 0;
671: textInfo->curY = YPADDING + textInfo->theFonts[textInfo->curFont].ascent
672: + textInfo->theFonts[textInfo->curFont].descent;
673:
674: textInfo->bottomSpace = textInfo->h - YPADDING -
675: textInfo->theFonts[textInfo->curFont].ascent - INTERLINE -
676: textInfo->theFonts[textInfo->curFont].descent;
677: /* Actually clear the window */
678: XClearWindow(display, w);
679:
680: /* Draw the current cursor */
681: XFillRectangle(display, w, textInfo->CursorGC,
682: XPADDING + CUROFFSET, textInfo->curY,
683: CURSORWIDTH,
684: textInfo->theFonts[textInfo->curFont].ascent +
685: textInfo->theFonts[textInfo->curFont].descent);
686:
687: /* Update the scroll bar */
688: UpdateScroll(display, textInfo);
689: return 1;
690: }
691:
692:
693: static int WarpToBottom(display, textInfo)
694: Display *display;
695: struct txtWin *textInfo; /* Text Information */
696: /*
697: * This routine causes the specified text window to display its
698: * last screen of information. It updates the scroll bar
699: * to the appropriate spot. The implementation scans backward
700: * through the buffer to find an appropriate starting spot for
701: * the window.
702: */
703: {
704: int index, height, lineHeight;
705:
706: index = textInfo->numLines-1;
707: height = 0;
708: while (index >= 0) {
709: lineHeight = textInfo->txtBuffer[index]->lineHeight + INTERLINE;
710: if (height + lineHeight > textInfo->h) break;
711: height += lineHeight;
712: index--;
713: }
714: textInfo->startLine = index + 1;
715: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
716: textInfo->curY = textInfo->h - textInfo->bottomSpace -
717: textInfo->txtBuffer[textInfo->endLine]->lineHeight;
718: XClearWindow(display, textInfo->mainWindow);
719: TxtRepaint(display, textInfo->mainWindow);
720: return 1;
721: }
722:
723:
724:
725: static int UpdateExposures(display, textInfo)
726: Display *display;
727: struct txtWin *textInfo; /* Text window information */
728: /*
729: * Before a new scrolling action occurs, the text window package
730: * must handle all COPYEXPOSE events generated by the last scrolling
731: * action. This routine is called to do this. Foreign events (those
732: * not handled by TxtFilter) are queued up and replaced on the queue
733: * after the processing of the exposure events is complete.
734: */
735: {
736: #if 0
737: XEvent foreignQueue[MAXFOREIGN];
738: int index, lastItem = 0;
739:
740: while (textInfo->flagWord & COPYEXPOSE) {
741: XNextEvent(display, &(foreignQueue[lastItem]));
742: if (!TxtFilter(display, &(foreignQueue[lastItem])))
743: lastItem++;
744: if (lastItem >= MAXFOREIGN) {
745: printf("Too many foreign events to queue!\n");
746: textInfo->flagWord &= (~COPYEXPOSE);
747: }
748: }
749: for (index = 0; index < lastItem; index++) {
750: XPutBackEvent(display, &(foreignQueue[index]));
751: }
752: #endif
753: return 1;
754: }
755:
756:
757: static int ScrollDown(display,textInfo)
758: Display *display;
759: struct txtWin *textInfo; /* Text window information */
760: /*
761: * This routine scrolls the indicated text window down by one
762: * line. The line below the current line must exist. The window
763: * is scrolled so that the line below the last line is fully
764: * displayed. This may cause many lines to scroll off the top.
765: * Scrolling is done using XCopyArea. The exposure events should
766: * be caught using ExposeCopy.
767: */
768: {
769: int lineSum, index, targetSpace, freeSpace, updateFlag;
770:
771: lineSum = 0;
772: if (textInfo->endLine + 1 >= textInfo->numLines) return 0;
773: targetSpace = textInfo->txtBuffer[textInfo->endLine+1]->lineHeight +
774: INTERLINE;
775: if (textInfo->bottomSpace < targetSpace) {
776: index = textInfo->startLine;
777: while (index < textInfo->endLine) {
778: lineSum += (textInfo->txtBuffer[index]->lineHeight + INTERLINE);
779: if (textInfo->bottomSpace + lineSum >= targetSpace) break;
780: index++;
781: }
782:
783: /* Must move upward by 'lineSum' pixels */
784: XCopyArea(display, textInfo->mainWindow, textInfo->mainWindow,
785: DEFAULT_GC, 0, lineSum,
786: textInfo->w - BARSIZE, textInfo->h,
787: 0, 0);
788:
789: textInfo->flagWord |= COPYEXPOSE;
790: /* Repair the damage to the structures */
791: textInfo->startLine = index + 1;
792: updateFlag = 1;
793: } else {
794: updateFlag = 0;
795: }
796: /* More lines might be able to fit. Let's check. */
797: freeSpace = textInfo->bottomSpace + lineSum - targetSpace;
798: index = textInfo->endLine + 1;
799: while (index < textInfo->numLines-1) {
800: if (freeSpace - textInfo->txtBuffer[index+1]->lineHeight - INTERLINE < 0)
801: break;
802: freeSpace -= (textInfo->txtBuffer[index+1]->lineHeight + INTERLINE);
803: index++;
804: }
805: textInfo->endLine = index;
806: textInfo->bottomSpace = freeSpace;
807: if (updateFlag) {
808: UpdateExposures(display, textInfo);
809: }
810: UpdateScroll(display, textInfo);
811: return 1;
812: }
813:
814:
815:
816:
817: static int ExpandLines(textInfo)
818: struct txtWin *textInfo; /* Text Information */
819: /*
820: * This routine allocates and initializes additional space in
821: * the line start array (txtBuffer). The new space
822: * is allocated using realloc. The expansion factor is a percentage
823: * given by EXPANDPERCENT.
824: */
825: {
826: int newSize, index;
827:
828: newSize = textInfo->allocLines;
829: newSize += (newSize * EXPANDPERCENT) / 100;
830:
831: textInfo->txtBuffer = (struct txtLine **)
832: realloc((char *) textInfo->txtBuffer,
833: (unsigned) (newSize * sizeof(struct txtLine *)));
834: for (index = textInfo->allocLines; index < newSize; index++) {
835: textInfo->txtBuffer[index] = alloc(struct txtLine);
836: InitLine(textInfo->txtBuffer[index]);
837: }
838: textInfo->allocLines = newSize;
839: return 1;
840: }
841:
842: static int ExpandBuffer(textInfo)
843: struct txtWin *textInfo; /* Text information */
844: /*
845: * Expands the basic character buffer using realloc. The expansion
846: * factor is a percentage given by EXPANDPERCENT.
847: */
848: {
849: int newSize;
850:
851: newSize = textInfo->bufAlloc + (textInfo->bufAlloc * EXPANDPERCENT) / 100;
852: textInfo->mainBuffer = (short *)
853: realloc((char *) textInfo->mainBuffer, (unsigned) newSize * sizeof(short));
854: textInfo->bufAlloc = newSize;
855: return 1;
856: }
857:
858:
859:
860: static int HandleNewLine(display, textInfo, flagWord)
861: Display *display;
862: struct txtWin *textInfo; /* Text Information */
863: int flagWord; /* DODISP or NONEWLINE or both */
864: /*
865: * This routine initializes the next line for drawing by setting
866: * its height to the current font height, scrolls the screen down
867: * one line, and updates the current drawing position to the
868: * left edge of the newly cleared line. If DODISP is specified,
869: * the screen will be updated (otherwise not). If NONEWLINE is
870: * specified, no newline character will be added to the text buffer
871: * (this is for line wrap).
872: */
873: {
874: struct txtLine *curLine, *nextLine;
875:
876: /* Check to see if a new line must be allocated */
877: if (textInfo->curLine >= textInfo->allocLines-1)
878: /* Expand the number of lines */
879: ExpandLines(textInfo);
880: textInfo->numLines += 1;
881:
882: /* Then we initialize the next line */
883: nextLine = textInfo->txtBuffer[textInfo->numLines-1];
884: nextLine->lineHeight =
885: textInfo->theFonts[textInfo->curFont].ascent +
886: textInfo->theFonts[textInfo->curFont].descent;
887:
888: curLine = textInfo->txtBuffer[textInfo->curLine];
889: if (flagWord & DODISP) {
890: /* Scroll down a line if required */
891: if ((textInfo->curY + curLine->lineHeight +
892: nextLine->lineHeight + (INTERLINE * 2)) > textInfo->h)
893: {
894: ScrollDown(display, textInfo);
895: }
896: else
897: {
898: /* Update the bottom space appropriately */
899: textInfo->bottomSpace -= (nextLine->lineHeight + INTERLINE);
900: textInfo->endLine += 1;
901: }
902: /* Update drawing position */
903: textInfo->curY = textInfo->h -
904: (textInfo->bottomSpace + nextLine->lineHeight);
905: }
906:
907: /* Move down a line */
908: textInfo->curLine += 1;
909: if (!(flagWord & NONEWLINE)) {
910: /* Append end-of-line to text buffer */
911: if (textInfo->bufSpot >= textInfo->bufAlloc) {
912: /* Allocate more space in main text buffer */
913: ExpandBuffer(textInfo);
914: }
915: textInfo->mainBuffer[(textInfo->bufSpot)++] =
916: (textInfo->curFont << FONTSHIFT) | '\n';
917: }
918: nextLine->lineText = textInfo->bufSpot;
919: textInfo->curX = 0;
920: return 1;
921: }
922:
923:
924:
925: static int CharSize(textInfo, lineNum, charNum)
926: struct txtWin *textInfo; /* Current Text Information */
927: int lineNum; /* Line in buffer */
928: int charNum; /* Character in line */
929: /*
930: * This routine determines the size of the specified character.
931: * It takes in account the font of the character and whether its
932: * fixed or variable. The size includes INTERSPACE spacing between
933: * the characters.
934: */
935: {
936: register XFontStruct *charFont;
937: register short *theLine;
938: register short theChar;
939:
940: theLine = &(textInfo->mainBuffer[textInfo->txtBuffer[lineNum]->lineText]);
941: theChar = theLine[charNum] & CHARMASK;
942: charFont = &(textInfo->theFonts[(theChar & FONTMASK) >> FONTSHIFT]);
943: if (theChar <= charFont->min_char_or_byte2 ||
944: theChar >= charFont->max_char_or_byte2 ||
945: charFont->per_char == 0)
946: return charFont->max_bounds.width + 1;
947: else
948: return charFont->per_char[theChar].width + 1;
949: }
950:
951:
952:
953:
954:
955: static int HandleBackspace(display, textInfo, flagWord)
956: Display *display;
957: struct txtWin *textInfo; /* Text Information */
958: int flagWord; /* DODISP or nothing */
959: /*
960: * This routine handles a backspace found in the input stream. The
961: * character before the current writing position will be erased and
962: * the drawing position will move back one character. If the writing
963: * position is at the left margin, the drawing position will move
964: * up to the previous line. If it is a line that has been wrapped,
965: * the character at the end of the previous line will be erased.
966: */
967: {
968: struct txtLine *thisLine, *prevLine;
969: int chSize;
970:
971: thisLine = textInfo->txtBuffer[textInfo->curLine];
972: /* First, determine whether we need to go back a line */
973: if (thisLine->lineLength == 0) {
974: /* Bleep if at top of buffer */
975: if (textInfo->curLine == 0) {
976: XBell(display, 50);
977: return 0;
978: }
979:
980: /* See if we have to scroll in the other direction */
981: if ((flagWord & DODISP) && (textInfo->curY <= YPADDING)) {
982: /* This will display the last lines of the buffer */
983: WarpToBottom(display, textInfo);
984: }
985:
986: /* Set drawing position at end of previous line */
987: textInfo->curLine -= 1;
988: prevLine = textInfo->txtBuffer[textInfo->curLine];
989: textInfo->numLines -= 1;
990: if (flagWord & DODISP) {
991: textInfo->curY -= (prevLine->lineHeight + INTERLINE);
992: textInfo->bottomSpace += (thisLine->lineHeight + INTERLINE);
993: textInfo->endLine -= 1;
994: }
995:
996: /* We are unlinewrapping if the previous line has flag set */
997: if (prevLine->lineFlags & WRAPFLAG) {
998: /* Get rid of line wrap indicator */
999: if (flagWord & DODISP) {
1000: XFillRectangle(display, textInfo->mainWindow,
1001: textInfo->bgGC,
1002: textInfo->w - BARSIZE - WRAPINDSIZE,
1003: textInfo->curY, WRAPINDSIZE,
1004: prevLine->lineHeight);
1005: }
1006: prevLine->lineFlags &= (~WRAPFLAG);
1007: /* Call recursively to wipe out the ending character */
1008: HandleBackspace(display, textInfo, flagWord);
1009: } else {
1010: /* Delete the end-of-line in the primary buffer */
1011: textInfo->bufSpot -= 1;
1012: }
1013: } else {
1014: /* Normal deletion of character */
1015: chSize =
1016: CharSize(textInfo, textInfo->curLine,
1017: textInfo->txtBuffer[textInfo->curLine]->lineLength - 1);
1018: /* Move back appropriate amount and wipe it out */
1019: thisLine->lineWidth -= chSize;
1020: if (flagWord & DODISP) {
1021: XFillRectangle(display, textInfo->mainWindow,
1022: textInfo->bgGC,
1023: thisLine->lineWidth, textInfo->curY,
1024: chSize, thisLine->lineHeight);
1025: }
1026: /* Delete from buffer */
1027: textInfo->txtBuffer[textInfo->curLine]->lineLength -= 1;
1028: textInfo->bufSpot -= 1;
1029: }
1030: return 1;
1031: }
1032:
1033:
1034:
1035: static int DrawLineWrap(display, win, x, y, h, col)
1036: Display *display;
1037: Window win; /* What window to draw it in */
1038: int x, y; /* Position of upper left corner */
1039: int h; /* Height of indicator */
1040: int col; /* Color of indicator */
1041: /*
1042: * This routine draws a line wrap indicator at the end of a line.
1043: * Visually, it is an arrow of the specified height directly against
1044: * the scroll bar border. The bitmap used for the arrow is stored
1045: * in 'arrowMap' with size 'arrow_width' and 'arrow_height'.
1046: */
1047: {
1048: struct txtWin *textInfo;
1049:
1050: textInfo = (struct txtWin *)XLookUpAssoc(display, textWindows,
1051: (XID) win);
1052:
1053: /* First, draw the arrow */
1054: XCopyArea(display, textInfo->arrowMap, textInfo->mainWindow,
1055: textInfo->CursorGC,
1056: 0, 0, arrow_width, arrow_height,
1057: x, y + h - arrow_height, 1);
1058:
1059: /* Then draw the stem */
1060: XDrawLine(display, textInfo->mainWindow, textInfo->CursorGC,
1061: x + STEMOFFSET, y,
1062: x + STEMOFFSET, y + h - arrow_height);
1063: return 1;
1064: }
1065:
1066:
1067:
1068:
1069: static int DrawLine(display, textInfo, lineIndex, ypos)
1070: Display *display;
1071: struct txtWin *textInfo; /* Text window information */
1072: int lineIndex; /* Index of line to draw */
1073: int ypos; /* Y position for line */
1074: /*
1075: * This routine destructively draws the indicated line in the
1076: * indicated window at the indicated position. It does not
1077: * clear to end of line however. It draws a line wrap indicator
1078: * if needed but does not draw a cursor.
1079: */
1080: {
1081: int index, startPos, curFont, theColor, curX, saveX, fontIndex;
1082: struct txtLine *someLine;
1083: char lineBuffer[BUFSIZE], *glyph;
1084: short *linePointer;
1085: XFontStruct *theFont;
1086: XGCValues gc;
1087:
1088: /* First, we draw the text */
1089: index = 0;
1090: curX = XPADDING;
1091: someLine = textInfo->txtBuffer[lineIndex];
1092: linePointer = &(textInfo->mainBuffer[someLine->lineText]);
1093: while (index < someLine->lineLength) {
1094: startPos = index;
1095: saveX = curX;
1096: curFont = linePointer[index] & FONTMASK;
1097: fontIndex = curFont >> FONTSHIFT;
1098: theFont = &(textInfo->theFonts[fontIndex]);
1099: theColor = textInfo->theColors[fontIndex];
1100: glyph = &(lineBuffer[0]);
1101: while ((index < someLine->lineLength) &&
1102: ((linePointer[index] & FONTMASK) == curFont))
1103: {
1104: *glyph = linePointer[index] & CHARMASK;
1105: index++;
1106: curX += CharSize(textInfo, lineIndex, index);
1107: glyph++;
1108: }
1109:
1110: /* Flush out the glyphs */
1111: XFillRectangle(display, textInfo->mainWindow,
1112: textInfo->bgGC,
1113: saveX, ypos,
1114: textInfo->w - BARSIZE,
1115: someLine->lineHeight + YPADDING + INTERLINE);
1116:
1117: XDrawString(display, textInfo->mainWindow,
1118: textInfo->fontGC[fontIndex],
1119: saveX, ypos,
1120: lineBuffer, someLine->lineLength);
1121: }
1122: /* Then the line wrap indicator (if needed) */
1123: if (someLine->lineFlags & WRAPFLAG) {
1124: DrawLineWrap(display, textInfo->mainWindow,
1125: textInfo->w - BARSIZE - WRAPINDSIZE,
1126: ypos, someLine->lineHeight,
1127: textInfo->fgPix);
1128: }
1129: return 1;
1130: }
1131:
1132:
1133:
1134:
1135: static int HandleNewFont(display, fontNum, textInfo, flagWord)
1136: Display *display;
1137: int fontNum; /* Font number */
1138: struct txtWin *textInfo; /* Text information */
1139: int flagWord; /* DODISP or nothing */
1140: /*
1141: * This routine handles a new font request. These requests take
1142: * the form "^F<digit>". The parsing is done in TxtWriteStr.
1143: * This routine is called only if the form is valid. It may return
1144: * a failure (0 status) if the requested font is not loaded.
1145: * If the new font is larger than any of the current
1146: * fonts on the line, it will change the line height and redisplay
1147: * the line.
1148: */
1149: {
1150: struct txtLine *thisLine;
1151: int heightDiff, baseDiff, redrawFlag;
1152:
1153: if (textInfo->theFonts[fontNum].fid == 0) {
1154: return 0;
1155: } else {
1156: thisLine = textInfo->txtBuffer[textInfo->curLine];
1157: textInfo->curFont = fontNum;
1158: redrawFlag = 0;
1159: heightDiff = textInfo->theFonts[fontNum].ascent +
1160: textInfo->theFonts[fontNum].descent -
1161: thisLine->lineHeight;
1162:
1163: if (heightDiff > 0) {
1164: redrawFlag = 1;
1165: } else {
1166: heightDiff = 0;
1167: }
1168:
1169: if (redrawFlag) {
1170: if (flagWord & DODISP) {
1171: /* Clear current line */
1172: XFillRectangle(display, textInfo->mainWindow,
1173: textInfo->bgGC,
1174: 0, textInfo->curY, textInfo->w,
1175: thisLine->lineHeight);
1176:
1177: /* Check to see if it requires scrolling */
1178: if ((textInfo->curY + thisLine->lineHeight + heightDiff +
1179: INTERLINE) > textInfo->h)
1180: {
1181: /*
1182: * General approach: "unscroll" the last line up
1183: * and then call ScrollDown to do the right thing.
1184: */
1185: textInfo->endLine -= 1;
1186: textInfo->bottomSpace += thisLine->lineHeight +
1187: INTERLINE;
1188:
1189: XFillRectangle(display, textInfo->mainWindow,
1190: textInfo->bgGC,
1191: 0, textInfo->h - textInfo->bottomSpace,
1192: textInfo->w, textInfo->bottomSpace);
1193:
1194: thisLine->lineHeight += heightDiff;
1195: ScrollDown(display, textInfo);
1196: textInfo->curY = textInfo->h -
1197: (textInfo->bottomSpace + INTERLINE +
1198: thisLine->lineHeight);
1199: }
1200: else
1201: {
1202: /* Just update bottom space */
1203: textInfo->bottomSpace -= heightDiff;
1204: thisLine->lineHeight += heightDiff;
1205: }
1206: /* Redraw the current line */
1207: DrawLine(display, textInfo, textInfo->curLine, textInfo->curY);
1208: } else {
1209: /* Just update line height */
1210: thisLine->lineHeight += heightDiff;
1211: }
1212: }
1213: return 1;
1214: }
1215: }
1216:
1217:
1218:
1219: int TxtWriteStr(display, w, str)
1220: Display *display;
1221: Window w; /* Text window */
1222: register char *str; /* 0 terminated string */
1223: /*
1224: * This routine writes a string to the specified text window.
1225: * The following notes apply:
1226: * - Text is always appended to the end of the text buffer.
1227: * - If the scroll bar is positioned such that the end of the
1228: * text is not visible, an automatic scroll to the bottom
1229: * will be done before the appending of text.
1230: * - Non-printable ASCII characters are not displayed.
1231: * - The '\n' character causes the current text position to
1232: * advance one line and start at the left.
1233: * - Tabs are not supported.
1234: * - Lines too long for the screen will be wrapped and a line wrap
1235: * indication will be drawn.
1236: * - Backspace clears the previous character. It will do the right
1237: * thing if asked to backspace past a wrapped line.
1238: * - A new font can be chosen using the sequence '^F<digit>' where
1239: * <digit> is 0-7. The directive will be ignored if
1240: * there is no font in the specified slot.
1241: * Returns 0 if something went wrong.
1242: */
1243: {
1244: register int fontIndex;
1245: register struct txtWin *textInfo;
1246: register struct txtLine *thisLine;
1247:
1248: if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w)) == 0)
1249: return 0;
1250:
1251: /* See if screen needs to be updated */
1252: if (textInfo->flagWord & SCREENWRONG) {
1253: TxtRepaint(display, textInfo->mainWindow);
1254: }
1255:
1256: /* See if we have to scroll down to the bottom */
1257: if (textInfo->flagWord & NOTATBOTTOM) {
1258: WarpToBottom(display, textInfo);
1259: textInfo->flagWord &= (~NOTATBOTTOM);
1260: }
1261:
1262: /* Undraw the current cursor */
1263: thisLine = textInfo->txtBuffer[textInfo->curLine];
1264:
1265: XFillRectangle(display, w, textInfo->bgGC,
1266: thisLine->lineWidth + CUROFFSET,
1267: textInfo->curY,
1268: CURSORWIDTH,
1269: thisLine->lineHeight);
1270:
1271: for ( /* str is ok */ ; (*str != 0) ; str++) {
1272: /* Check to see if we are waiting on a font */
1273: if (textInfo->flagWord & FONTNUMWAIT) {
1274: textInfo->flagWord &= (~FONTNUMWAIT);
1275: fontIndex = *str - '0';
1276: if ((fontIndex >= 0) && (fontIndex < MAXFONTS)) {
1277: /* Handle font -- go get next character */
1278: if (HandleNewFont(display, fontIndex, textInfo, DODISP))
1279: continue;
1280: }
1281: }
1282:
1283: /* Inline code for handling normal character case */
1284: if ((*str >= LOWCHAR) && (*str <= HIGHCHAR)) {
1285: register XFontStruct *thisFont;
1286: register struct txtLine *thisLine;
1287: register int charWidth;
1288: int thisColor;
1289:
1290: /* Determine size of character */
1291: thisFont = &(textInfo->theFonts[textInfo->curFont]);
1292: thisColor = textInfo->theColors[textInfo->curFont];
1293: if (*str <= thisFont->min_char_or_byte2 ||
1294: *str >= thisFont->max_char_or_byte2 ||
1295: thisFont->per_char == 0)
1296: charWidth = thisFont->max_bounds.width + 1;
1297: else
1298: charWidth = thisFont->per_char[*str].width + 1;
1299:
1300: /* Check to see if line wrap is required */
1301: thisLine = textInfo->txtBuffer[textInfo->curLine];
1302: if (thisLine->lineWidth + charWidth >
1303: (textInfo->w-BARSIZE-WRAPINDSIZE))
1304: {
1305: DrawLineWrap(display, textInfo->mainWindow,
1306: textInfo->w-BARSIZE-WRAPINDSIZE,
1307: textInfo->curY, thisLine->lineHeight,
1308: textInfo->fgPix);
1309: thisLine->lineFlags |= WRAPFLAG;
1310: /* Handle the spacing problem the same way as a newline */
1311: HandleNewLine(display, textInfo, DODISP | NONEWLINE);
1312: thisLine = textInfo->txtBuffer[textInfo->curLine];
1313: }
1314:
1315: /* Ready to draw character */
1316: XDrawString(display, textInfo->mainWindow,
1317: DEFAULT_GC,
1318: textInfo->curX += charWidth,
1319: textInfo->curY + thisLine->lineHeight,
1320: str, 1);
1321:
1322: /* Append character onto main buffer */
1323: if (textInfo->bufSpot >= textInfo->bufAlloc)
1324: /* Make room for more characters */
1325: ExpandBuffer(textInfo);
1326: textInfo->mainBuffer[(textInfo->bufSpot)++] =
1327: (textInfo->curFont << FONTSHIFT) | (*str);
1328:
1329: /* Update the line start array */
1330: thisLine->lineLength += 1;
1331: thisLine->lineWidth += charWidth;
1332: } else if (*str == NEWLINE) {
1333: HandleNewLine(display, textInfo, DODISP);
1334: } else if (*str == NEWFONT) {
1335: /* Go into waiting for font number mode */
1336: textInfo->flagWord |= FONTNUMWAIT;
1337: } else if (*str == BACKSPACE) {
1338: HandleBackspace(display, textInfo, DODISP);
1339: } else {
1340: /* Ignore all others */
1341: }
1342: }
1343: /* Draw the cursor in its new position */
1344: thisLine = textInfo->txtBuffer[textInfo->curLine];
1345:
1346: XFillRectangle(display, w, textInfo->CursorGC,
1347: thisLine->lineWidth + CUROFFSET,
1348: textInfo->curY /* + thisLine->lineHeight */,
1349: CURSORWIDTH, thisLine->lineHeight);
1350:
1351: return 1;
1352: }
1353:
1354:
1355:
1356: int TxtJamStr(display, w, str)
1357: Display *display;
1358: Window w; /* Text window */
1359: register char *str; /* NULL terminated string */
1360: /*
1361: * This is the same as TxtWriteStr except the screen is NOT updated.
1362: * After a call to this routine, TxtRepaint should be called to
1363: * update the screen. This routine is meant to be used to load
1364: * a text buffer with information and then allow the user to
1365: * scroll through it at will.
1366: */
1367: {
1368: register int fontIndex;
1369: register struct txtWin *textInfo;
1370:
1371: if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w)
1372: ) == 0)
1373: return 0;
1374:
1375: for ( /* str is ok */ ; (*str != 0) ; str++) {
1376: /* Check to see if we are waiting on a font */
1377: if (textInfo->flagWord & FONTNUMWAIT) {
1378: textInfo->flagWord &= (~FONTNUMWAIT);
1379: fontIndex = *str - '0';
1380: if ((fontIndex >= 0) && (fontIndex < MAXFONTS)) {
1381: if (HandleNewFont(display, fontIndex, textInfo, 0)) {
1382: /* Handled font -- go get next character */
1383: continue;
1384: }
1385: }
1386: }
1387: /* Inline code for handling normal character case */
1388: if ((*str >= LOWCHAR) && (*str <= HIGHCHAR)) {
1389: register XFontStruct *thisFont;
1390: register struct txtLine *thisLine;
1391: register int charWidth;
1392:
1393: /* Determine size of character */
1394: thisFont = &(textInfo->theFonts[textInfo->curFont]);
1395:
1396: if (*str <= thisFont->min_char_or_byte2 ||
1397: *str >= thisFont->max_char_or_byte2 ||
1398: thisFont->per_char == 0)
1399: charWidth = thisFont->max_bounds.width + 1;
1400: else
1401: charWidth = thisFont->per_char[*str].width + 1;
1402:
1403: /* Check to see if line wrap is required */
1404: thisLine = textInfo->txtBuffer[textInfo->curLine];
1405: if (thisLine->lineWidth + charWidth >
1406: (textInfo->w-BARSIZE-WRAPINDSIZE))
1407: {
1408: thisLine->lineFlags |= WRAPFLAG;
1409: /* Handle the spacing problem the same way as a newline */
1410: HandleNewLine(display, textInfo, NONEWLINE);
1411: thisLine = textInfo->txtBuffer[textInfo->curLine];
1412: }
1413: /* Append character onto main buffer */
1414: if (textInfo->bufSpot >= textInfo->bufAlloc)
1415: /* Make room for more characters */
1416: ExpandBuffer(textInfo);
1417: textInfo->mainBuffer[(textInfo->bufSpot)++] =
1418: (textInfo->curFont << FONTSHIFT) | (*str);
1419:
1420: /* Update the line start array */
1421: thisLine->lineLength += 1;
1422: thisLine->lineWidth += charWidth;
1423: } else if (*str == NEWLINE) {
1424: HandleNewLine(display, textInfo, 0);
1425: } else if (*str == NEWFONT) {
1426: /* Go into waiting for font number mode */
1427: textInfo->flagWord |= FONTNUMWAIT;
1428: } else if (*str == BACKSPACE) {
1429: HandleBackspace(display, textInfo, 0);
1430: } else {
1431: /* Ignore all others */
1432: }
1433: }
1434: textInfo->flagWord |= SCREENWRONG;
1435: return 1;
1436: }
1437:
1438:
1439:
1440: int TxtRepaint(display,w)
1441: Display *display;
1442: Window w;
1443: /*
1444: * Repaints the given scrollable text window. The routine repaints
1445: * the entire window. For handling exposure events, the TxtFilter
1446: * routine should be used.
1447: */
1448: {
1449: struct txtWin *textInfo;
1450: int index, ypos;
1451:
1452: if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w)
1453: ) == 0)
1454: return 0;
1455:
1456: /* Check to see if the screen is up to date */
1457: if (textInfo->flagWord & SCREENWRONG) {
1458: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
1459: textInfo->flagWord &= (~SCREENWRONG);
1460: }
1461:
1462: ypos = YPADDING;
1463: index = textInfo->startLine;
1464: for (;;) {
1465: DrawLine(display, textInfo, index, ypos);
1466: if (index >= textInfo->endLine) break;
1467: ypos += (textInfo->txtBuffer[index]->lineHeight + INTERLINE);
1468: index++;
1469: }
1470: /* Draw the cursor (if on screen) */
1471: if (textInfo->endLine == textInfo->curLine) {
1472: XFillRectangle(display, w, textInfo->CursorGC,
1473: textInfo->txtBuffer[index]->lineWidth + CUROFFSET,
1474: ypos /* + textInfo->txtBuffer[index]->lineHeight */,
1475: CURSORWIDTH, textInfo->txtBuffer[index]->lineHeight);
1476:
1477: }
1478: /* Update the scroll bar */
1479: UpdateScroll(display, textInfo);
1480: return 1;
1481: }
1482:
1483:
1484:
1485: static int InsertIndex(textInfo, thisIndex, ypos)
1486: struct txtWin *textInfo; /* Text Window Information */
1487: int thisIndex; /* Line index of exposed line */
1488: int ypos; /* Drawing position of line */
1489: /*
1490: * This routine inserts the supplied line index into the copy
1491: * exposure array for 'textInfo'. The array is kept sorted
1492: * from lowest to highest using insertion sort. The array
1493: * is dynamically expanded if needed.
1494: */
1495: {
1496: struct expEvent *newItem;
1497: int newSize, index, downIndex;
1498:
1499: /* Check to see if we need to expand it */
1500: if ((textInfo->exposeSize + 3) >= textInfo->exposeAlloc) {
1501: newSize = textInfo->exposeAlloc +
1502: (textInfo->exposeAlloc * EXPANDPERCENT / 100);
1503: textInfo->exposeAry = (struct expEvent **)
1504: realloc((char *) textInfo->exposeAry,
1505: (unsigned) (newSize * sizeof(struct expEvent *)));
1506: for (index = textInfo->exposeAlloc; index < newSize; index++)
1507: textInfo->exposeAry[index] = alloc(struct expEvent);
1508: textInfo->exposeAlloc = newSize;
1509: }
1510: /* Find spot for insertion. NOTE: last spot has big number */
1511: for (index = 0; index <= textInfo->exposeSize; index++) {
1512: if (textInfo->exposeAry[index]->lineIndex >= thisIndex) {
1513: if (textInfo->exposeAry[index]->lineIndex > thisIndex) {
1514: /* Insert before this entry */
1515: newItem = textInfo->exposeAry[textInfo->exposeSize+1];
1516: for (downIndex = textInfo->exposeSize;
1517: downIndex >= index;
1518: downIndex--)
1519: {
1520: textInfo->exposeAry[downIndex+1] =
1521: textInfo->exposeAry[downIndex];
1522: }
1523: /* Put a free structure at this spot */
1524: textInfo->exposeAry[index] = newItem;
1525: /* Fill it in */
1526: textInfo->exposeAry[index]->lineIndex = thisIndex;
1527: textInfo->exposeAry[index]->ypos = ypos;
1528: /* Break out of loop */
1529: textInfo->exposeSize += 1;
1530: }
1531: break;
1532: }
1533: }
1534: return 1;
1535: }
1536:
1537:
1538:
1539: static int ScrollUp(display, textInfo)
1540: Display *display;
1541: struct txtWin *textInfo; /* Text window information */
1542: /*
1543: * This routine scrolls the indicated text window up by one
1544: * line. The line above the current line must exist. The
1545: * window is scrolled so that the line above the start line
1546: * is displayed at the top of the screen. This may cause
1547: * many lines to scroll off the bottom. The scrolling is
1548: * done using XCopyArea. The exposure events should be caught
1549: * by ExposeCopy.
1550: */
1551: {
1552: int targetSpace;
1553:
1554: /* Make sure all exposures have been handled by now */
1555: if (textInfo->startLine == 0) return 0;
1556: targetSpace = textInfo->txtBuffer[textInfo->startLine-1]->lineHeight +
1557: INTERLINE;
1558: /* Move the area downward by the target amount */
1559: XCopyArea(display, textInfo->mainWindow, textInfo->mainWindow,
1560: DEFAULT_GC,
1561: 0, YPADDING, textInfo->w - BARSIZE,
1562: textInfo->h, 0, targetSpace);
1563:
1564: textInfo->flagWord |= COPYEXPOSE;
1565: /* Update the text window parameters */
1566: textInfo->startLine -= 1;
1567: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
1568:
1569: /* Clear out bottom space region */
1570: XClearArea(display, textInfo->mainWindow,
1571: 0, textInfo->h - textInfo->bottomSpace,
1572: textInfo->w, textInfo->bottomSpace);
1573:
1574: UpdateExposures(display, textInfo);
1575: UpdateScroll(display, textInfo);
1576:
1577: return 1;
1578: }
1579:
1580:
1581: static int ScrollToSpot(display, textInfo, ySpot)
1582: Display *display;
1583: struct txtWin *textInfo; /* Text window information */
1584: int ySpot; /* Button position in scroll window */
1585: /*
1586: * This routine scrolls the specified text window relative to the
1587: * position of the mouse in the scroll bar. The center of the screen
1588: * will be positioned to correspond to the mouse position.
1589: */
1590: {
1591: int targetLine, aboveLines;
1592:
1593: targetLine = textInfo->numLines * ySpot / textInfo->h;
1594: textInfo->startLine = targetLine;
1595: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
1596: aboveLines = 0;
1597: /* Make the target line the *center* of the window */
1598: while ((textInfo->startLine > 0) &&
1599: (aboveLines < textInfo->endLine - targetLine))
1600: {
1601: textInfo->startLine -= 1;
1602: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
1603: aboveLines++;
1604: }
1605: if (textInfo->endLine == textInfo->numLines-1) {
1606: WarpToBottom(display, textInfo);
1607: } else {
1608: XClearWindow(display, textInfo->mainWindow);
1609: TxtRepaint(display, textInfo->mainWindow);
1610: }
1611: return 1;
1612: }
1613:
1614:
1615:
1616: static int LineToTop(display, textInfo, pos)
1617: Display *display;
1618: struct txtWin *textInfo; /* Text window information */
1619: int pos; /* Y position of mouse */
1620: /*
1621: * This routine scrolls the screen down until the line at the
1622: * mouse position is at the top of the screen. It stops
1623: * if it can't scroll the buffer down that far. If the
1624: * global 'ScrollOption' is NORMSCROLL, a smooth scroll
1625: * is used. Otherwise, it jumps to the right position
1626: * and repaints the screen.
1627: */
1628: {
1629: int index, sum;
1630:
1631: /* First, we find the current line */
1632: sum = 0;
1633: for (index = textInfo->startLine; index <= textInfo->endLine; index++) {
1634: if (sum + textInfo->txtBuffer[index]->lineHeight + INTERLINE> pos) break;
1635: sum += textInfo->txtBuffer[index]->lineHeight + INTERLINE;
1636: }
1637: /* We always want to scroll down at least one line */
1638: if (index == textInfo->startLine) index++;
1639: if (ScrollOption == NORMSCROLL) {
1640: /* Scroll down until 'index' is the starting line */
1641: while ((textInfo->startLine < index) && ScrollDown(display, textInfo))
1642: {
1643: /* Empty Loop Body */
1644: }
1645: } else {
1646: /* Immediately jump to correct spot */
1647: textInfo->startLine = index;
1648: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
1649: if (textInfo->endLine == textInfo->numLines-1) {
1650: WarpToBottom(display, textInfo);
1651: } else {
1652: XClearWindow(display, textInfo->mainWindow);
1653: TxtRepaint(display, textInfo->mainWindow);
1654: }
1655: }
1656: /* Check to see if at end of buffer */
1657: if (textInfo->endLine >= textInfo->numLines-1) {
1658: textInfo->flagWord &= (~NOTATBOTTOM);
1659: }
1660: return 1;
1661: }
1662:
1663:
1664:
1665: static int TopToHere(display, textInfo, pos)
1666: Display *display;
1667: struct txtWin *textInfo; /* Text window information */
1668: int pos; /* Y position of mouse */
1669: /*
1670: * This routine scrolls the screen up until the top line of
1671: * the screen is at the current Y position of the mouse. Again,
1672: * it will stop if it can't scroll that far. If the global
1673: * 'ScrollOption' is NORMSCROLL, a smooth scroll is used.
1674: * If it's not, it will simply redraw the screen at the
1675: * correct spot.
1676: */
1677: {
1678: int sum, target, linesup, index;
1679:
1680: target = pos - textInfo->txtBuffer[textInfo->startLine]->lineHeight;
1681: /* We always want to scroll up at least one line */
1682: if (target <= 0) target = 1;
1683: sum = 0;
1684: linesup = 0;
1685: /* Check to see if we are at the top anyway */
1686: if (textInfo->startLine == 0) return 0;
1687: if (ScrollOption == NORMSCROLL) {
1688: /* Scroll up until sum of new top lines greater than target */
1689: while ((sum < target) && ScrollUp(display, textInfo)) {
1690: sum += textInfo->txtBuffer[textInfo->startLine]->lineHeight;
1691: linesup++;
1692: }
1693: } else {
1694: /* Search backward to find index */
1695: index = textInfo->startLine - 1;
1696: while ((index > 0) && (sum < target)) {
1697: sum += textInfo->txtBuffer[index]->lineHeight;
1698: linesup++;
1699: index--;
1700: }
1701: /* Go directly to the index */
1702: textInfo->startLine = index;
1703: textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace));
1704: XClearWindow(display, textInfo->mainWindow);
1705: TxtRepaint(display, textInfo->mainWindow);
1706: }
1707: /* If we scrolled, assert we are not at bottom of buffer */
1708: if (linesup > 0) {
1709: textInfo->flagWord |= NOTATBOTTOM;
1710: }
1711: return 1;
1712: }
1713:
1714:
1715:
1716: int TxtFilter(display, evt)
1717: Display *display;
1718: XEvent *evt;
1719: /*
1720: * This routine handles events associated with scrollable text windows.
1721: * It will handle all exposure events and any button released events
1722: * in the scroll bar of a text window. It does NOT handle any other
1723: * events. If it cannot handle the event, it will return 0.
1724: */
1725: {
1726: XExposeEvent *expose = &evt->xexpose;
1727: XButtonEvent *btEvt = &evt->xbutton;
1728: XGraphicsExposeEvent *gexpose = &evt->xgraphicsexpose;
1729: XNoExposeEvent *noexpose = &evt->xnoexpose;
1730: struct txtWin *textInfo;
1731: int index, ypos;
1732: Window w, sw;
1733:
1734: if (textWindows == (XAssocTable *) 0) {
1735: textWindows = XCreateAssocTable(32);
1736: if (textWindows == (XAssocTable *) 0) return(0);
1737: }
1738: if (evt->type == Expose) {
1739: w = expose->window;
1740: sw = 0;
1741: }
1742: else if (evt->type == GraphicsExpose) {
1743: w = gexpose->drawable;
1744: sw = 0;
1745: }
1746: else if (evt->type == NoExpose) {
1747: w = noexpose->drawable;
1748: sw = 0;
1749: }
1750: else if (evt->type == ButtonRelease) {
1751: w = btEvt->window;
1752: sw = btEvt->subwindow;
1753: }
1754: else
1755: return 0;
1756:
1757: if ((textInfo = (struct txtWin *)
1758: XLookUpAssoc(display, textWindows, (XID) w)) == 0)
1759: return 0;
1760:
1761: /* Determine whether it's main window or not */
1762: if ((w == textInfo->mainWindow) && (sw == 0)) {
1763: /* Main Window - handle exposures */
1764: switch (evt->type) {
1765: case Expose:
1766: ypos = 0 /*YPADDING*/;
1767: for (index = textInfo->startLine;
1768: index <= textInfo->endLine;
1769: index++)
1770: {
1771: int lh = textInfo->txtBuffer[index]->lineHeight;
1772:
1773: if (((ypos + lh) >= expose->y) &&
1774: (ypos <= (expose->y + expose->height)))
1775: {
1776: /* Intersection region */
1777: /* Draw line immediately */
1778: DrawLine(display, textInfo, index, ypos);
1779: /* And possibly draw cursor */
1780: if (textInfo->curLine == index) {
1781: XFillRectangle(display, w, textInfo->CursorGC,
1782: textInfo->txtBuffer[index]->lineWidth +
1783: CUROFFSET,
1784: ypos,
1785: CURSORWIDTH,
1786: lh);
1787: }
1788: }
1789: ypos += lh + INTERLINE;
1790: }
1791: break;
1792: case GraphicsExpose:
1793: ypos = 0 /*YPADDING*/;
1794: for (index = textInfo->startLine;
1795: index <= textInfo->endLine;
1796: index++)
1797: {
1798: int lh = textInfo->txtBuffer[index]->lineHeight;
1799:
1800: if (((ypos + lh) >= gexpose->y) &&
1801: (ypos <= (gexpose->y + gexpose->height)))
1802: {
1803: /* Intersection region */
1804: /* Draw line immediately */
1805: DrawLine(display, textInfo, index, ypos);
1806: /* And possibly draw cursor */
1807: if (textInfo->curLine == index) {
1808: XFillRectangle(display, w, textInfo->CursorGC,
1809: textInfo->txtBuffer[index]->lineWidth +
1810: CUROFFSET,
1811: ypos,
1812: CURSORWIDTH,
1813: lh);
1814: }
1815: }
1816: ypos += lh + INTERLINE;
1817: }
1818: break;
1819: case NoExpose:
1820: break;
1821: default:
1822: /* Not one of our events */
1823: return 0;
1824: }
1825: } else {
1826: switch (evt->type) {
1827: case Expose:
1828: UpdateScroll(display, textInfo);
1829: break;
1830: case ButtonRelease:
1831: /* Find out which button */
1832: switch (btEvt->button) {
1833: case Button1:
1834: /* Scroll up until top line is at mouse position */
1835: TopToHere(display, textInfo, btEvt->y);
1836: break;
1837: case Button2:
1838: /* Scroll to spot relative to position */
1839: ScrollToSpot(display, textInfo, btEvt->y);
1840: if (textInfo->endLine >= textInfo->numLines-1) {
1841: textInfo->flagWord &= (~NOTATBOTTOM);
1842: } else {
1843: textInfo->flagWord |= NOTATBOTTOM;
1844: }
1845: break;
1846: case Button3:
1847: /* Scroll down until pointed line is at top */
1848: LineToTop(display, textInfo, btEvt->y);
1849: break;
1850: }
1851: break;
1852: default:
1853: /* Not one of our events */
1854: return 0;
1855: }
1856: }
1857: return 1;
1858: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.