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