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