BSD 4_3_Net_2 release
[unix-history] / usr / src / games / chess / Xchess / scrollText / scrollText.c
CommitLineData
cb290b32
C
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
20extern char *malloc();
21extern 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
26extern XAssocTable *XCreateAssocTable();
27extern caddr_t XLookUpAssoc();
28
29static 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
35static int ScrollOption = NOOPTION;
36
37typedef 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
63static 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
85typedef 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
102typedef 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
124typedef 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\f
168
169
170static int InitLine(newLine)
171struct 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\f
186
187
188int TxtGrab(display, txtWin, program, mainFont, bg, fg, cur)
189Display *display; /* display window is on */
190Window txtWin; /* Window to take over as scrollable text */
191char *program; /* Program name for Xdefaults */
192XFontStruct *mainFont; /* Primary text font */
193int 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\f
331
332int TxtRelease(display, w)
333Display *display;
334Window 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\f
371
372static int RecompBuffer(textInfo)
373struct 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\f
485
486int TxtAddFont(display, textWin, fontNumber, newFont, newColor)
487Display *display;
488Window textWin; /* Scrollable text window */
489int fontNumber; /* Place to add font (0-7) */
490XFontStruct *newFont; /* Font to add */
491int 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\f
555
556int TxtWinP(display, w)
557Display *display;
558Window 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\f
570
571static int FindEndLine(textInfo, botSpace)
572struct txtWin *textInfo;
573int *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\f
599
600static int UpdateScroll(display, textInfo)
601Display *display;
602struct 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\f
639
640int TxtClear(display, w)
641Display *display;
642Window 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\f
692
693static int WarpToBottom(display, textInfo)
694Display *display;
695struct 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\f
724
725static int UpdateExposures(display, textInfo)
726Display *display;
727struct 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\f
756
757static int ScrollDown(display,textInfo)
758Display *display;
759struct 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\f
816
817static int ExpandLines(textInfo)
818struct 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
842static int ExpandBuffer(textInfo)
843struct 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\f
859
860static int HandleNewLine(display, textInfo, flagWord)
861Display *display;
862struct txtWin *textInfo; /* Text Information */
863int 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\f
924
925static int CharSize(textInfo, lineNum, charNum)
926struct txtWin *textInfo; /* Current Text Information */
927int lineNum; /* Line in buffer */
928int 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\f
953
954
955static int HandleBackspace(display, textInfo, flagWord)
956Display *display;
957struct txtWin *textInfo; /* Text Information */
958int 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\f
1034
1035static int DrawLineWrap(display, win, x, y, h, col)
1036Display *display;
1037Window win; /* What window to draw it in */
1038int x, y; /* Position of upper left corner */
1039int h; /* Height of indicator */
1040int 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\f
1068
1069static int DrawLine(display, textInfo, lineIndex, ypos)
1070Display *display;
1071struct txtWin *textInfo; /* Text window information */
1072int lineIndex; /* Index of line to draw */
1073int 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\f
1134
1135static int HandleNewFont(display, fontNum, textInfo, flagWord)
1136Display *display;
1137int fontNum; /* Font number */
1138struct txtWin *textInfo; /* Text information */
1139int 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\f
1218
1219int TxtWriteStr(display, w, str)
1220Display *display;
1221Window w; /* Text window */
1222register 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\f
1355
1356int TxtJamStr(display, w, str)
1357Display *display;
1358Window w; /* Text window */
1359register 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\f
1439
1440int TxtRepaint(display,w)
1441Display *display;
1442Window 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\f
1484
1485static int InsertIndex(textInfo, thisIndex, ypos)
1486struct txtWin *textInfo; /* Text Window Information */
1487int thisIndex; /* Line index of exposed line */
1488int 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\f
1539static int ScrollUp(display, textInfo)
1540Display *display;
1541struct 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\f
1580
1581static int ScrollToSpot(display, textInfo, ySpot)
1582Display *display;
1583struct txtWin *textInfo; /* Text window information */
1584int 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\f
1615
1616static int LineToTop(display, textInfo, pos)
1617Display *display;
1618struct txtWin *textInfo; /* Text window information */
1619int 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\f
1664
1665static int TopToHere(display, textInfo, pos)
1666Display *display;
1667struct txtWin *textInfo; /* Text window information */
1668int 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\f
1715
1716int TxtFilter(display, evt)
1717Display *display;
1718XEvent *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}