diff options
Diffstat (limited to 'gnu/games/chess/Xchess/scrollText.c')
| -rw-r--r-- | gnu/games/chess/Xchess/scrollText.c | 1877 |
1 files changed, 1877 insertions, 0 deletions
diff --git a/gnu/games/chess/Xchess/scrollText.c b/gnu/games/chess/Xchess/scrollText.c new file mode 100644 index 000000000000..847b41c622af --- /dev/null +++ b/gnu/games/chess/Xchess/scrollText.c @@ -0,0 +1,1877 @@ +/* + * A Scrollable Text Output Window + * + * David Harrison + * University of California, Berkeley + * 1986 + * + * The following is an implementation for a scrollable text output + * system. It handles exposure events only (other interactions are + * under user control). For scrolling, a always present scroll bar + * is implemented. It detects size changes and compensates accordingly. + */ + +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/X10.h> +#include <sys/types.h> +#include "scrollText.h" + +extern char *malloc(); +extern char *realloc(); +#define alloc(type) (type *) malloc(sizeof(type)) +#define numalloc(type, num) (type *) malloc((unsigned) (num * sizeof(type))) +#define MAXINT 2147483647 + +extern XAssocTable *XCreateAssocTable(); +extern caddr_t XLookUpAssoc(); + +static XAssocTable *textWindows = (XAssocTable *) 0; + +#define NOOPTION -1 /* Option hasn't been set yet */ +#define NORMSCROLL 0 /* Smooth scroll on LineToTop and TopToHere */ +#define JUMPSCROLL 1 /* Jump scrolling on LineToTop and TopToHere */ + +static int ScrollOption = NOOPTION; + +typedef char *Generic; + +#define DEFAULT_GC textInfo->fontGC[textInfo->curFont] + +#define BARSIZE 15 +#define BARBORDER 1 +#define MAXFONTS 8 +#define INITBUFSIZE 1024 +#define INITLINES 50 +#define INITEXPARY 50 +#define XPADDING 2 +#define YPADDING 2 +#define INTERLINE 5 +#define INTERSPACE 1 +#define CURSORWIDTH 2 +#define EXPANDPERCENT 40 +#define BUFSIZE 1024 +#define CUROFFSET 1 +#define MAXFOREIGN 250 +#define NOINDEX -1 + +/* The wrap line indicator */ +#define WRAPINDSIZE 7 +#define STEMOFFSET 5 +#define arrow_width 7 +#define arrow_height 5 +static char arrow_bits[] = { + 0x24, 0x26, 0x3f, 0x06, 0x04}; + +#define NEWLINE '\n' +#define BACKSPACE '\010' +#define NEWFONT '\006' +#define LOWCHAR '\040' +#define HIGHCHAR '\176' + +#define CHARMASK 0x00ff /* Character mask */ +#define FONTMASK 0x0700 /* Character font */ +#define FONTSHIFT 8 /* Shift amount */ + +#define WRAPFLAG 0x01 /* Line wrap flag */ + +/* + * Lines are represented by a pointer into the overall array of + * 16-bit characters. The lower eight bits is used to indicate the character + * (in ASCII), and the next two bits are used to indicate the font + * the character should be drawn in. + */ + +typedef struct txtLine { + int lineLength; /* Current line length */ + int lineHeight; /* Full height of line in pixels */ + int lineBaseLine; /* Current baseline of the line */ + int lineWidth; /* Drawing position at end of line */ + int lineText; /* Offset into master buffer */ + int lineFlags; /* Line wrap flag is here */ +}; + + +/* + * For ExposeCopy events, we queue up the redraw requests collapsing + * them into line redraw requests until the CopyExpose event arrives. + * The queue is represented as a dynamic array of the following + * structure: + */ + +typedef struct expEvent { + int lineIndex; /* Index of line to redraw */ + int ypos; /* Drawing position of line */ +}; + + +/* + * The text buffer is represented using a dynamic counted array + * of 16-bit quantities. This array expands as needed. + * For the screen representation, a dynamic counted array + * of line structures is used. This array points into the + * text buffer to denote the start of each line and its parameters. + * The windows are configured as one overall window which contains + * the scroll bar as a sub-window along its right edge. Thus, + * the text drawing space is actually w-BARSIZE. + */ + +#define NOTATBOTTOM 0x01 /* Need to scroll to bottom before appending */ +#define FONTNUMWAIT 0x02 /* Waiting for font number */ +#define COPYEXPOSE 0x04 /* Need to process a copy expose event */ +#define SCREENWRONG 0x08 /* TxtJamStr has invalidated screen contents */ + +typedef struct txtWin { + /* Basic text buffer */ + int bufAlloc; /* Allocated size of buffer */ + int bufSpot; /* Current writing position in buffer */ + short *mainBuffer; /* Main buffer of text */ + + /* Line information */ + int numLines; /* Number of display lines in buffer */ + int allocLines; /* Number of lines allocated */ + struct txtLine **txtBuffer; /* Dynamic array of lines */ + + /* Current Window display information */ + Window mainWindow; /* Text display window */ + Window scrollBar; /* Subwindow for scroll bar */ + Pixmap arrowMap; /* line wrap indicator */ + int bgPix, fgPix; /* Background and cursor */ + GC CursorGC; /* gc for the cursor */ + GC bgGC; /* gc for erasing things */ + GC fontGC[MAXFONTS]; /* gc for doing fonts */ + XFontStruct theFonts[MAXFONTS];/* Display fonts */ + int theColors[MAXFONTS]; /* foregrounds of the fonts */ + int curFont; /* current font for tracking */ + int w, h; /* Current size */ + int startLine; /* Top line in display */ + int endLine; /* Bottom line in display */ + int bottomSpace; /* Space at bottom of screen */ + int flagWord; /* If non-zero, not at end */ + + /* For handling ExposeCopy events */ + int exposeSize; /* Current size of array */ + int exposeAlloc; /* Allocated size */ + struct expEvent **exposeAry;/* Array of line indices */ + + /* Drawing position information */ + int curLine; /* Current line in buffer */ + int curX; /* Current horizontal positi */ + int curY; /* Current vertical drawing */ +}; + +/* Flags for the various basic character handling functions */ + +#define DODISP 0x01 /* Update the display */ +#define NONEWLINE 0x02 /* Dont append newline */ + + + +static int InitLine(newLine) +struct txtLine *newLine; /* Newly created line structure */ +/* + * This routine initializes a newly created line structure. + */ +{ + newLine->lineLength = 0; + newLine->lineHeight = 0; + newLine->lineBaseLine = 0; + newLine->lineWidth = XPADDING; + newLine->lineText = NOINDEX; + newLine->lineFlags = 0; + return 1; +} + + + + +int TxtGrab(display, txtWin, program, mainFont, bg, fg, cur) +Display *display; /* display window is on */ +Window txtWin; /* Window to take over as scrollable text */ +char *program; /* Program name for Xdefaults */ +XFontStruct *mainFont; /* Primary text font */ +int bg, fg, cur; /* Background, foreground, and cursor colors */ +/* + * This routine takes control of 'txtWin' and makes it into a scrollable + * text output window. It will create a sub-window for the scroll bar + * with a background of 'bg' and an bar with color 'fg'. Both fixed width + * and variable width fonts are supported. Additional fonts can be loaded + * using 'TxtAddFont'. Returns 0 if there were problems, non-zero if + * everything went ok. + */ +{ + struct txtWin *newWin; /* Text package specific information */ + XWindowAttributes winInfo; /* Window information */ + int index; + XGCValues gc_val; + + if (textWindows == (XAssocTable *) 0) { + textWindows = XCreateAssocTable(32); + if (textWindows == (XAssocTable *) 0) return(0); + } + if (XGetWindowAttributes(display, txtWin, &winInfo) == 0) return 0; + + if (ScrollOption == NOOPTION) { + /* Read to see if the user wants jump scrolling or not */ + if (XGetDefault(display, program, "JumpScroll")) { + ScrollOption = JUMPSCROLL; + } else { + ScrollOption = NORMSCROLL; + } + } + + /* Initialize local structure */ + newWin = alloc(struct txtWin); + + /* Initialize arrow pixmap */ + newWin->arrowMap = XCreatePixmapFromBitmapData(display, txtWin, + arrow_bits, + arrow_width, arrow_height, + cur, bg, + DisplayPlanes(display, 0)); + + newWin->bufAlloc = INITBUFSIZE; + newWin->bufSpot = 0; + newWin->mainBuffer = numalloc(short, INITBUFSIZE); + + newWin->numLines = 1; + newWin->allocLines = INITLINES; + newWin->txtBuffer = numalloc(struct txtLine *, INITLINES); + for (index = 0; index < INITLINES; index++) { + newWin->txtBuffer[index] = alloc(struct txtLine); + InitLine(newWin->txtBuffer[index]); + } + + /* Window display information */ + newWin->mainWindow = txtWin; + newWin->w = winInfo.width; + newWin->h = winInfo.height; + newWin->startLine = 0; + newWin->endLine = 0; + newWin->bottomSpace = winInfo.height + - YPADDING - mainFont->ascent - mainFont->descent - INTERLINE; + newWin->flagWord = 0; + newWin->bgPix = bg; + newWin->fgPix = fg; + + /* Scroll Bar Creation */ + newWin->scrollBar = XCreateSimpleWindow(display, txtWin, + winInfo.width - BARSIZE, + 0, BARSIZE - (2*BARBORDER), + winInfo.height - (2*BARBORDER), + BARBORDER, + fg, bg); + XSelectInput(display, newWin->scrollBar, ExposureMask|ButtonReleaseMask); + XMapRaised(display, newWin->scrollBar); + + /* Font and Color Initialization */ + newWin->theFonts[0] = *mainFont; + newWin->theColors[0] = fg; + gc_val.function = GXcopy; + gc_val.plane_mask = AllPlanes; + gc_val.foreground = fg; + gc_val.background = bg; + gc_val.graphics_exposures = 1; + gc_val.font = mainFont->fid; + gc_val.line_width = 1; + gc_val.line_style = LineSolid; + + newWin->fontGC[0] = XCreateGC(display, txtWin, + GCFunction | GCPlaneMask | + GCForeground | GCBackground | + GCGraphicsExposures | GCFont, + &gc_val); + + gc_val.foreground = cur; + newWin->CursorGC = XCreateGC(display, txtWin, + GCFunction | GCPlaneMask | + GCForeground | GCBackground | + GCLineStyle | GCLineWidth, + &gc_val); + + gc_val.foreground = bg; + newWin->bgGC = XCreateGC(display, txtWin, + GCFunction | GCPlaneMask | + GCForeground | GCBackground | + GCGraphicsExposures | GCFont, + &gc_val); + + + for (index = 1; index < MAXFONTS; index++) { + newWin->theFonts[index].fid = 0; + newWin->fontGC[index] = 0; + } + + + /* Initialize size of first line */ + newWin->txtBuffer[0]->lineHeight = newWin->theFonts[0].ascent + + newWin->theFonts[0].descent; + newWin->txtBuffer[0]->lineText = 0; + + /* ExposeCopy array initialization */ + newWin->exposeSize = 0; + newWin->exposeAlloc = INITEXPARY; + newWin->exposeAry = numalloc(struct expEvent *, INITEXPARY); + for (index = 0; index < newWin->exposeAlloc; index++) + newWin->exposeAry[index] = alloc(struct expEvent); + /* Put plus infinity in last slot for sorting purposes */ + newWin->exposeAry[0]->lineIndex = MAXINT; + + /* Drawing Position Information */ + newWin->curLine = 0; + newWin->curX = 0; + newWin->curY = YPADDING + mainFont->ascent + mainFont->descent; + + /* Attach it to both windows */ + XMakeAssoc(display, textWindows, (XID) txtWin, (caddr_t) newWin); + XMakeAssoc(display, textWindows, (XID) newWin->scrollBar, (caddr_t) newWin); + return 1; +} + + +int TxtRelease(display, w) +Display *display; +Window w; /* Window to release */ +/* + * This routine releases all resources associated with the + * specified window which are consumed by the text + * window package. This includes the entire text buffer, line start + * array, and the scroll bar window. However, the window + * itself is NOT destroyed. The routine will return zero if + * the window is not owned by the text window package. + */ +{ + struct txtWin *textInfo; + int index; + + if ((textInfo = (struct txtWin *) XLookUpAssoc(display, + textWindows, (XID) w)) == 0) + return 0; + + for (index = 0; index < MAXFONTS; index++) + if (textInfo->fontGC[index] != 0) + XFreeGC(display, textInfo->fontGC[index]); + + free((Generic) textInfo->mainBuffer); + for (index = 0; index < textInfo->numLines; index++) { + free((Generic) textInfo->txtBuffer[index]); + } + free((Generic) textInfo->txtBuffer); + XDestroyWindow(display, textInfo->scrollBar); + for (index = 0; index < textInfo->exposeSize; index++) { + free((Generic) textInfo->exposeAry[index]); + } + free((Generic) textInfo->exposeAry); + XDeleteAssoc(display, textWindows, (XID) w); + free((Generic) textInfo); + return 1; +} + + + +static int RecompBuffer(textInfo) +struct txtWin *textInfo; /* Text window information */ +/* + * This routine recomputes all line breaks in a buffer after + * a change in window size or font. This is done by throwing + * away the old line start array and recomputing it. Although + * a lot of this work is also done elsewhere, it has been included + * inline here for efficiency. + */ +{ + int startPos, endSize, linenum; + register int index, chsize, curfont; + register short *bufptr; + register XFontStruct *fontptr; + register struct txtLine *lineptr; + char theChar; + + /* Record the old position so we can come back to it */ + for (startPos = textInfo->txtBuffer[textInfo->startLine]->lineText; + (startPos > 0) && (textInfo->mainBuffer[startPos] != '\n'); + startPos--) + /* null loop body */; + + /* Clear out the old line start array */ + for (index = 0; index < textInfo->numLines; index++) { + InitLine(textInfo->txtBuffer[index]); + } + + /* Initialize first line */ + textInfo->txtBuffer[0]->lineHeight = + textInfo->theFonts[0].ascent + textInfo->theFonts[0].descent; + textInfo->txtBuffer[0]->lineText = 0; + + /* Process the text back into lines */ + endSize = textInfo->w - BARSIZE - WRAPINDSIZE; + bufptr = textInfo->mainBuffer; + lineptr = textInfo->txtBuffer[0]; + linenum = 0; + fontptr = &(textInfo->theFonts[0]); + curfont = 0; + for (index = 0; index < textInfo->bufSpot; index++) { + theChar = bufptr[index] & CHARMASK; + + if ((bufptr[index] & FONTMASK) != curfont) { + int newFontNum, heightDiff; + + /* Switch fonts */ + newFontNum = (bufptr[index] & FONTMASK) >> FONTSHIFT; + if (textInfo->theFonts[newFontNum].fid != 0) { + /* Valid font */ + curfont = bufptr[index] & FONTMASK; + fontptr = &(textInfo->theFonts[newFontNum]); + heightDiff = (fontptr->ascent + fontptr->descent) - + lineptr->lineHeight; + if (heightDiff < 0) heightDiff = 0; + lineptr->lineHeight += heightDiff; + } + } + if (theChar == '\n') { + /* Handle new line */ + if (linenum >= textInfo->allocLines-1) + /* Expand number of lines */ + ExpandLines(textInfo); + linenum++; + lineptr = textInfo->txtBuffer[linenum]; + /* Initialize next line */ + lineptr->lineHeight = fontptr->ascent + fontptr->descent; + lineptr->lineText = index+1; + /* Check to see if its the starting line */ + if (index == startPos) textInfo->startLine = linenum; + } else { + /* Handle normal character */ + chsize = CharSize(textInfo, linenum, index); + if (lineptr->lineWidth + chsize > endSize) { + /* Handle line wrap */ + lineptr->lineFlags |= WRAPFLAG; + if (linenum >= textInfo->allocLines-1) + /* Expand number of lines */ + ExpandLines(textInfo); + linenum++; + lineptr = textInfo->txtBuffer[linenum]; + /* Initialize next line */ + lineptr->lineHeight = fontptr->ascent + fontptr->descent; + lineptr->lineText = index; + lineptr->lineLength = 1; + lineptr->lineWidth += chsize; + } else { + /* Handle normal addition of character */ + lineptr->lineLength += 1; + lineptr->lineWidth += chsize; + } + } + } + /* We now have a valid line array. Let's clean up some other fields. */ + textInfo->numLines = linenum+1; + if (startPos == 0) { + textInfo->startLine = 0; + } + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + textInfo->curLine = linenum; + /* Check to see if we are at the bottom */ + if (textInfo->endLine >= textInfo->numLines-1) { + textInfo->curY = textInfo->h - textInfo->bottomSpace - + lineptr->lineHeight; + textInfo->flagWord &= (~NOTATBOTTOM); + } else { + textInfo->flagWord |= NOTATBOTTOM; + } + return 1; +} + + + + +int TxtAddFont(display, textWin, fontNumber, newFont, newColor) +Display *display; +Window textWin; /* Scrollable text window */ +int fontNumber; /* Place to add font (0-7) */ +XFontStruct *newFont; /* Font to add */ +int newColor; /* Color of font */ +/* + * This routine loads a new font so that it can be used in a previously + * created text window. There are eight font slots numbered 0 through 7. + * If there is already a font in the specified slot, it will be replaced + * and an automatic redraw of the window will take place. See TxtWriteStr + * for details on using alternate fonts. The color specifies the foreground + * color of the text. The default foreground color is used if this + * parameter is TXT_NO_COLOR. Returns a non-zero value if + * everything went well. + */ +{ + struct txtWin *textInfo; + int redrawFlag; + XGCValues gc_val; + + if ((fontNumber < 0) || (fontNumber >= MAXFONTS)) return 0; + if ((textInfo = (struct txtWin *) + XLookUpAssoc(display, textWindows, (XID) textWin)) == 0) + return 0; + if (newColor == TXT_NO_COLOR) { + newColor = textInfo->fgPix; + } + + gc_val.font = newFont->fid; + gc_val.foreground = newColor; + gc_val.background = textInfo->bgPix; + gc_val.plane_mask = AllPlanes; + gc_val.graphics_exposures = 1; + gc_val.function = GXcopy; + + if (textInfo->fontGC[fontNumber] != 0) + { + XChangeGC(display, textInfo->fontGC[fontNumber], + GCFont | GCForeground, &gc_val); + } + else + textInfo->fontGC[fontNumber] = XCreateGC(display, textWin, + GCFont | + GCForeground | + GCBackground | + GCFunction | + GCPlaneMask | + GCGraphicsExposures, + &gc_val); + + + redrawFlag = (textInfo->theFonts[fontNumber].fid != 0) && + (((newFont) && (newFont->fid != textInfo->theFonts[fontNumber].fid)) || + (newColor != textInfo->theColors[fontNumber])); + if (newFont) { + textInfo->theFonts[fontNumber] = *newFont; + } + textInfo->theColors[fontNumber] = newColor; + + if (redrawFlag) { + RecompBuffer(textInfo); + XClearWindow(display, textWin); + TxtRepaint(display, textWin); + } + return 1; +} + + + +int TxtWinP(display, w) +Display *display; +Window w; +/* + * Returns a non-zero value if the window has been previously grabbed + * using TxtGrab and 0 if it has not. + */ +{ + if (XLookUpAssoc(display, textWindows, (XID) w)) + return(1); + else return(0); +} + + + +static int FindEndLine(textInfo, botSpace) +struct txtWin *textInfo; +int *botSpace; +/* + * Given the starting line in 'textInfo->startLine', this routine + * determines the index of the last line that can be drawn given the + * current size of the screen. If there are not enough lines to + * fill the screen, the index of the last line will be returned. + * The amount of empty bottom space is returned in 'botSpace'. + */ +{ + int index, height, lineHeight; + + height = YPADDING; + index = textInfo->startLine; + while (index < textInfo->numLines) { + lineHeight = textInfo->txtBuffer[index]->lineHeight + INTERLINE; + if (height + lineHeight > textInfo->h) break; + height += lineHeight; + index++; + } + if (botSpace) { + *botSpace = textInfo->h - height; + } + return index - 1; +} + + + +static int UpdateScroll(display, textInfo) +Display *display; +struct txtWin *textInfo; /* Text window information */ +/* + * This routine computes the current extent of the scroll bar + * indicator and repaints the bar with the correct information. + */ +{ + int top, bottom; + + if (textInfo->numLines > 1) { + top = textInfo->startLine * (textInfo->h - 2*BARBORDER) / + (textInfo->numLines - 1); + bottom = textInfo->endLine * (textInfo->h - 2*BARBORDER) / + (textInfo->numLines - 1); + } else { + top = 0; + bottom = textInfo->h - (2*BARBORDER); + } + + /* Draw it - make sure there is a little padding */ + if (top == 0) top++; + if (bottom == textInfo->h-(2*BARBORDER)) bottom--; + + XFillRectangle(display, textInfo->scrollBar, + textInfo->bgGC, + 0, 0, BARSIZE, top-1); +#ifdef __386BSD__ + XFillRectangle(display, textInfo->scrollBar, + DEFAULT_GC, top, BARSIZE - (2*BARBORDER) - 2, + BARSIZE, bottom - top); +#else + XFillRectangle(display, textInfo->scrollBar, + DEFAULT_GC, top, BARSIZE - (2*BARBORDER) - 2, + bottom - top); +#endif + XFillRectangle(display, textInfo->scrollBar, DEFAULT_GC, + 0, bottom+1, BARSIZE, + textInfo->h - (2 * BARBORDER) - bottom); + + return 1; +} + + + + +int TxtClear(display, w) +Display *display; +Window w; +/* + * This routine clears a scrollable text window. It resets the current + * writing position to the upper left hand corner of the screen. + * NOTE: THIS ALSO CLEARS THE CONTENTS OF THE TEXT WINDOW BUFFER AND + * RESETS THE SCROLL BAR. Returns 0 if the window is not a text window. + * This should be used *instead* of XClear. + */ +{ + struct txtWin *textInfo; + int index; + + if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w)) == 0) + return 0; + + /* Zero out the arrays */ + textInfo->bufSpot = 0; + for (index = 0; index < textInfo->numLines; index++) { + InitLine(textInfo->txtBuffer[index]); + } + textInfo->txtBuffer[0]->lineHeight = + textInfo->theFonts[textInfo->curFont].ascent + + textInfo->theFonts[textInfo->curFont].descent; + + textInfo->numLines = 1; + textInfo->startLine = 0; + textInfo->endLine = 0; + textInfo->curLine = 0; + textInfo->curX = 0; + textInfo->curY = YPADDING + textInfo->theFonts[textInfo->curFont].ascent + + textInfo->theFonts[textInfo->curFont].descent; + + textInfo->bottomSpace = textInfo->h - YPADDING - + textInfo->theFonts[textInfo->curFont].ascent - INTERLINE - + textInfo->theFonts[textInfo->curFont].descent; + /* Actually clear the window */ + XClearWindow(display, w); + + /* Draw the current cursor */ + XFillRectangle(display, w, textInfo->CursorGC, + XPADDING + CUROFFSET, textInfo->curY, + CURSORWIDTH, + textInfo->theFonts[textInfo->curFont].ascent + + textInfo->theFonts[textInfo->curFont].descent); + + /* Update the scroll bar */ + UpdateScroll(display, textInfo); + return 1; +} + + +static int WarpToBottom(display, textInfo) +Display *display; +struct txtWin *textInfo; /* Text Information */ +/* + * This routine causes the specified text window to display its + * last screen of information. It updates the scroll bar + * to the appropriate spot. The implementation scans backward + * through the buffer to find an appropriate starting spot for + * the window. + */ +{ + int index, height, lineHeight; + + index = textInfo->numLines-1; + height = 0; + while (index >= 0) { + lineHeight = textInfo->txtBuffer[index]->lineHeight + INTERLINE; + if (height + lineHeight > textInfo->h) break; + height += lineHeight; + index--; + } + textInfo->startLine = index + 1; + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + textInfo->curY = textInfo->h - textInfo->bottomSpace - + textInfo->txtBuffer[textInfo->endLine]->lineHeight; + XClearWindow(display, textInfo->mainWindow); + TxtRepaint(display, textInfo->mainWindow); + return 1; +} + + + +static int UpdateExposures(display, textInfo) +Display *display; +struct txtWin *textInfo; /* Text window information */ +/* + * Before a new scrolling action occurs, the text window package + * must handle all COPYEXPOSE events generated by the last scrolling + * action. This routine is called to do this. Foreign events (those + * not handled by TxtFilter) are queued up and replaced on the queue + * after the processing of the exposure events is complete. + */ +{ +#if 0 + XEvent foreignQueue[MAXFOREIGN]; + int index, lastItem = 0; + + while (textInfo->flagWord & COPYEXPOSE) { + XNextEvent(display, &(foreignQueue[lastItem])); + if (!TxtFilter(display, &(foreignQueue[lastItem]))) + lastItem++; + if (lastItem >= MAXFOREIGN) { + printf("Too many foreign events to queue!\n"); + textInfo->flagWord &= (~COPYEXPOSE); + } + } + for (index = 0; index < lastItem; index++) { + XPutBackEvent(display, &(foreignQueue[index])); + } +#endif + return 1; +} + + +static int ScrollDown(display,textInfo) +Display *display; +struct txtWin *textInfo; /* Text window information */ +/* + * This routine scrolls the indicated text window down by one + * line. The line below the current line must exist. The window + * is scrolled so that the line below the last line is fully + * displayed. This may cause many lines to scroll off the top. + * Scrolling is done using XCopyArea. The exposure events should + * be caught using ExposeCopy. + */ +{ + int lineSum, index, targetSpace, freeSpace, updateFlag; + + lineSum = 0; + if (textInfo->endLine + 1 >= textInfo->numLines) return 0; + targetSpace = textInfo->txtBuffer[textInfo->endLine+1]->lineHeight + + INTERLINE; + if (textInfo->bottomSpace < targetSpace) { + index = textInfo->startLine; + while (index < textInfo->endLine) { + lineSum += (textInfo->txtBuffer[index]->lineHeight + INTERLINE); + if (textInfo->bottomSpace + lineSum >= targetSpace) break; + index++; + } + + /* Must move upward by 'lineSum' pixels */ + XCopyArea(display, textInfo->mainWindow, textInfo->mainWindow, + DEFAULT_GC, 0, lineSum, + textInfo->w - BARSIZE, textInfo->h, + 0, 0); + + textInfo->flagWord |= COPYEXPOSE; + /* Repair the damage to the structures */ + textInfo->startLine = index + 1; + updateFlag = 1; + } else { + updateFlag = 0; + } + /* More lines might be able to fit. Let's check. */ + freeSpace = textInfo->bottomSpace + lineSum - targetSpace; + index = textInfo->endLine + 1; + while (index < textInfo->numLines-1) { + if (freeSpace - textInfo->txtBuffer[index+1]->lineHeight - INTERLINE < 0) + break; + freeSpace -= (textInfo->txtBuffer[index+1]->lineHeight + INTERLINE); + index++; + } + textInfo->endLine = index; + textInfo->bottomSpace = freeSpace; + if (updateFlag) { + UpdateExposures(display, textInfo); + } + UpdateScroll(display, textInfo); + return 1; +} + + + + +static int ExpandLines(textInfo) +struct txtWin *textInfo; /* Text Information */ +/* + * This routine allocates and initializes additional space in + * the line start array (txtBuffer). The new space + * is allocated using realloc. The expansion factor is a percentage + * given by EXPANDPERCENT. + */ +{ + int newSize, index; + + newSize = textInfo->allocLines; + newSize += (newSize * EXPANDPERCENT) / 100; + + textInfo->txtBuffer = (struct txtLine **) + realloc((char *) textInfo->txtBuffer, + (unsigned) (newSize * sizeof(struct txtLine *))); + for (index = textInfo->allocLines; index < newSize; index++) { + textInfo->txtBuffer[index] = alloc(struct txtLine); + InitLine(textInfo->txtBuffer[index]); + } + textInfo->allocLines = newSize; + return 1; +} + +static int ExpandBuffer(textInfo) +struct txtWin *textInfo; /* Text information */ +/* + * Expands the basic character buffer using realloc. The expansion + * factor is a percentage given by EXPANDPERCENT. + */ +{ + int newSize; + + newSize = textInfo->bufAlloc + (textInfo->bufAlloc * EXPANDPERCENT) / 100; + textInfo->mainBuffer = (short *) + realloc((char *) textInfo->mainBuffer, (unsigned) newSize * sizeof(short)); + textInfo->bufAlloc = newSize; + return 1; +} + + + +static int HandleNewLine(display, textInfo, flagWord) +Display *display; +struct txtWin *textInfo; /* Text Information */ +int flagWord; /* DODISP or NONEWLINE or both */ +/* + * This routine initializes the next line for drawing by setting + * its height to the current font height, scrolls the screen down + * one line, and updates the current drawing position to the + * left edge of the newly cleared line. If DODISP is specified, + * the screen will be updated (otherwise not). If NONEWLINE is + * specified, no newline character will be added to the text buffer + * (this is for line wrap). + */ +{ + struct txtLine *curLine, *nextLine; + + /* Check to see if a new line must be allocated */ + if (textInfo->curLine >= textInfo->allocLines-1) + /* Expand the number of lines */ + ExpandLines(textInfo); + textInfo->numLines += 1; + + /* Then we initialize the next line */ + nextLine = textInfo->txtBuffer[textInfo->numLines-1]; + nextLine->lineHeight = + textInfo->theFonts[textInfo->curFont].ascent + + textInfo->theFonts[textInfo->curFont].descent; + + curLine = textInfo->txtBuffer[textInfo->curLine]; + if (flagWord & DODISP) { + /* Scroll down a line if required */ + if ((textInfo->curY + curLine->lineHeight + + nextLine->lineHeight + (INTERLINE * 2)) > textInfo->h) + { + ScrollDown(display, textInfo); + } + else + { + /* Update the bottom space appropriately */ + textInfo->bottomSpace -= (nextLine->lineHeight + INTERLINE); + textInfo->endLine += 1; + } + /* Update drawing position */ + textInfo->curY = textInfo->h - + (textInfo->bottomSpace + nextLine->lineHeight); + } + + /* Move down a line */ + textInfo->curLine += 1; + if (!(flagWord & NONEWLINE)) { + /* Append end-of-line to text buffer */ + if (textInfo->bufSpot >= textInfo->bufAlloc) { + /* Allocate more space in main text buffer */ + ExpandBuffer(textInfo); + } + textInfo->mainBuffer[(textInfo->bufSpot)++] = + (textInfo->curFont << FONTSHIFT) | '\n'; + } + nextLine->lineText = textInfo->bufSpot; + textInfo->curX = 0; + return 1; +} + + + +static int CharSize(textInfo, lineNum, charNum) +struct txtWin *textInfo; /* Current Text Information */ +int lineNum; /* Line in buffer */ +int charNum; /* Character in line */ +/* + * This routine determines the size of the specified character. + * It takes in account the font of the character and whether its + * fixed or variable. The size includes INTERSPACE spacing between + * the characters. + */ +{ + register XFontStruct *charFont; + register short *theLine; + register short theChar; + + theLine = &(textInfo->mainBuffer[textInfo->txtBuffer[lineNum]->lineText]); + theChar = theLine[charNum] & CHARMASK; + charFont = &(textInfo->theFonts[(theChar & FONTMASK) >> FONTSHIFT]); + if (theChar <= charFont->min_char_or_byte2 || + theChar >= charFont->max_char_or_byte2 || + charFont->per_char == 0) + return charFont->max_bounds.width + 1; + else + return charFont->per_char[theChar].width + 1; +} + + + + + +static int HandleBackspace(display, textInfo, flagWord) +Display *display; +struct txtWin *textInfo; /* Text Information */ +int flagWord; /* DODISP or nothing */ +/* + * This routine handles a backspace found in the input stream. The + * character before the current writing position will be erased and + * the drawing position will move back one character. If the writing + * position is at the left margin, the drawing position will move + * up to the previous line. If it is a line that has been wrapped, + * the character at the end of the previous line will be erased. + */ +{ + struct txtLine *thisLine, *prevLine; + int chSize; + + thisLine = textInfo->txtBuffer[textInfo->curLine]; + /* First, determine whether we need to go back a line */ + if (thisLine->lineLength == 0) { + /* Bleep if at top of buffer */ + if (textInfo->curLine == 0) { + XBell(display, 50); + return 0; + } + + /* See if we have to scroll in the other direction */ + if ((flagWord & DODISP) && (textInfo->curY <= YPADDING)) { + /* This will display the last lines of the buffer */ + WarpToBottom(display, textInfo); + } + + /* Set drawing position at end of previous line */ + textInfo->curLine -= 1; + prevLine = textInfo->txtBuffer[textInfo->curLine]; + textInfo->numLines -= 1; + if (flagWord & DODISP) { + textInfo->curY -= (prevLine->lineHeight + INTERLINE); + textInfo->bottomSpace += (thisLine->lineHeight + INTERLINE); + textInfo->endLine -= 1; + } + + /* We are unlinewrapping if the previous line has flag set */ + if (prevLine->lineFlags & WRAPFLAG) { + /* Get rid of line wrap indicator */ + if (flagWord & DODISP) { + XFillRectangle(display, textInfo->mainWindow, + textInfo->bgGC, + textInfo->w - BARSIZE - WRAPINDSIZE, + textInfo->curY, WRAPINDSIZE, + prevLine->lineHeight); + } + prevLine->lineFlags &= (~WRAPFLAG); + /* Call recursively to wipe out the ending character */ + HandleBackspace(display, textInfo, flagWord); + } else { + /* Delete the end-of-line in the primary buffer */ + textInfo->bufSpot -= 1; + } + } else { + /* Normal deletion of character */ + chSize = + CharSize(textInfo, textInfo->curLine, + textInfo->txtBuffer[textInfo->curLine]->lineLength - 1); + /* Move back appropriate amount and wipe it out */ + thisLine->lineWidth -= chSize; + if (flagWord & DODISP) { + XFillRectangle(display, textInfo->mainWindow, + textInfo->bgGC, + thisLine->lineWidth, textInfo->curY, + chSize, thisLine->lineHeight); + } + /* Delete from buffer */ + textInfo->txtBuffer[textInfo->curLine]->lineLength -= 1; + textInfo->bufSpot -= 1; + } + return 1; +} + + + +static int DrawLineWrap(display, win, x, y, h, col) +Display *display; +Window win; /* What window to draw it in */ +int x, y; /* Position of upper left corner */ +int h; /* Height of indicator */ +int col; /* Color of indicator */ +/* + * This routine draws a line wrap indicator at the end of a line. + * Visually, it is an arrow of the specified height directly against + * the scroll bar border. The bitmap used for the arrow is stored + * in 'arrowMap' with size 'arrow_width' and 'arrow_height'. + */ +{ + struct txtWin *textInfo; + + textInfo = (struct txtWin *)XLookUpAssoc(display, textWindows, + (XID) win); + + /* First, draw the arrow */ +#ifdef __386BSD__ + XCopyArea(display, textInfo->arrowMap, textInfo->mainWindow, + textInfo->CursorGC, + 0, 0, arrow_width, arrow_height, + x, y + h - arrow_height); +#else + XCopyArea(display, textInfo->arrowMap, textInfo->mainWindow, + textInfo->CursorGC, + 0, 0, arrow_width, arrow_height, + x, y + h - arrow_height, 1); +#endif + + /* Then draw the stem */ + XDrawLine(display, textInfo->mainWindow, textInfo->CursorGC, + x + STEMOFFSET, y, + x + STEMOFFSET, y + h - arrow_height); + return 1; +} + + + + +static int DrawLine(display, textInfo, lineIndex, ypos) +Display *display; +struct txtWin *textInfo; /* Text window information */ +int lineIndex; /* Index of line to draw */ +int ypos; /* Y position for line */ +/* + * This routine destructively draws the indicated line in the + * indicated window at the indicated position. It does not + * clear to end of line however. It draws a line wrap indicator + * if needed but does not draw a cursor. + */ +{ + int index, startPos, curFont, theColor, curX, saveX, fontIndex; + struct txtLine *someLine; + char lineBuffer[BUFSIZE], *glyph; + short *linePointer; + XFontStruct *theFont; + XGCValues gc; + + /* First, we draw the text */ + index = 0; + curX = XPADDING; + someLine = textInfo->txtBuffer[lineIndex]; + linePointer = &(textInfo->mainBuffer[someLine->lineText]); + while (index < someLine->lineLength) { + startPos = index; + saveX = curX; + curFont = linePointer[index] & FONTMASK; + fontIndex = curFont >> FONTSHIFT; + theFont = &(textInfo->theFonts[fontIndex]); + theColor = textInfo->theColors[fontIndex]; + glyph = &(lineBuffer[0]); + while ((index < someLine->lineLength) && + ((linePointer[index] & FONTMASK) == curFont)) + { + *glyph = linePointer[index] & CHARMASK; + index++; + curX += CharSize(textInfo, lineIndex, index); + glyph++; + } + + /* Flush out the glyphs */ + XFillRectangle(display, textInfo->mainWindow, + textInfo->bgGC, + saveX, ypos, + textInfo->w - BARSIZE, + someLine->lineHeight + YPADDING + INTERLINE); + + XDrawString(display, textInfo->mainWindow, + textInfo->fontGC[fontIndex], + saveX, ypos, + lineBuffer, someLine->lineLength); + } + /* Then the line wrap indicator (if needed) */ + if (someLine->lineFlags & WRAPFLAG) { + DrawLineWrap(display, textInfo->mainWindow, + textInfo->w - BARSIZE - WRAPINDSIZE, + ypos, someLine->lineHeight, + textInfo->fgPix); + } + return 1; +} + + + + +static int HandleNewFont(display, fontNum, textInfo, flagWord) +Display *display; +int fontNum; /* Font number */ +struct txtWin *textInfo; /* Text information */ +int flagWord; /* DODISP or nothing */ +/* + * This routine handles a new font request. These requests take + * the form "^F<digit>". The parsing is done in TxtWriteStr. + * This routine is called only if the form is valid. It may return + * a failure (0 status) if the requested font is not loaded. + * If the new font is larger than any of the current + * fonts on the line, it will change the line height and redisplay + * the line. + */ +{ + struct txtLine *thisLine; + int heightDiff, baseDiff, redrawFlag; + + if (textInfo->theFonts[fontNum].fid == 0) { + return 0; + } else { + thisLine = textInfo->txtBuffer[textInfo->curLine]; + textInfo->curFont = fontNum; + redrawFlag = 0; + heightDiff = textInfo->theFonts[fontNum].ascent + + textInfo->theFonts[fontNum].descent - + thisLine->lineHeight; + + if (heightDiff > 0) { + redrawFlag = 1; + } else { + heightDiff = 0; + } + + if (redrawFlag) { + if (flagWord & DODISP) { + /* Clear current line */ + XFillRectangle(display, textInfo->mainWindow, + textInfo->bgGC, + 0, textInfo->curY, textInfo->w, + thisLine->lineHeight); + + /* Check to see if it requires scrolling */ + if ((textInfo->curY + thisLine->lineHeight + heightDiff + + INTERLINE) > textInfo->h) + { + /* + * General approach: "unscroll" the last line up + * and then call ScrollDown to do the right thing. + */ + textInfo->endLine -= 1; + textInfo->bottomSpace += thisLine->lineHeight + + INTERLINE; + + XFillRectangle(display, textInfo->mainWindow, + textInfo->bgGC, + 0, textInfo->h - textInfo->bottomSpace, + textInfo->w, textInfo->bottomSpace); + + thisLine->lineHeight += heightDiff; + ScrollDown(display, textInfo); + textInfo->curY = textInfo->h - + (textInfo->bottomSpace + INTERLINE + + thisLine->lineHeight); + } + else + { + /* Just update bottom space */ + textInfo->bottomSpace -= heightDiff; + thisLine->lineHeight += heightDiff; + } + /* Redraw the current line */ + DrawLine(display, textInfo, textInfo->curLine, textInfo->curY); + } else { + /* Just update line height */ + thisLine->lineHeight += heightDiff; + } + } + return 1; + } +} + + + +int TxtWriteStr(display, w, str) +Display *display; +Window w; /* Text window */ +register char *str; /* 0 terminated string */ +/* + * This routine writes a string to the specified text window. + * The following notes apply: + * - Text is always appended to the end of the text buffer. + * - If the scroll bar is positioned such that the end of the + * text is not visible, an automatic scroll to the bottom + * will be done before the appending of text. + * - Non-printable ASCII characters are not displayed. + * - The '\n' character causes the current text position to + * advance one line and start at the left. + * - Tabs are not supported. + * - Lines too long for the screen will be wrapped and a line wrap + * indication will be drawn. + * - Backspace clears the previous character. It will do the right + * thing if asked to backspace past a wrapped line. + * - A new font can be chosen using the sequence '^F<digit>' where + * <digit> is 0-7. The directive will be ignored if + * there is no font in the specified slot. + * Returns 0 if something went wrong. + */ +{ + register int fontIndex; + register struct txtWin *textInfo; + register struct txtLine *thisLine; + + if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w)) == 0) + return 0; + + /* See if screen needs to be updated */ + if (textInfo->flagWord & SCREENWRONG) { + TxtRepaint(display, textInfo->mainWindow); + } + + /* See if we have to scroll down to the bottom */ + if (textInfo->flagWord & NOTATBOTTOM) { + WarpToBottom(display, textInfo); + textInfo->flagWord &= (~NOTATBOTTOM); + } + + /* Undraw the current cursor */ + thisLine = textInfo->txtBuffer[textInfo->curLine]; + + XFillRectangle(display, w, textInfo->bgGC, + thisLine->lineWidth + CUROFFSET, + textInfo->curY, + CURSORWIDTH, + thisLine->lineHeight); + + for ( /* str is ok */ ; (*str != 0) ; str++) { + /* Check to see if we are waiting on a font */ + if (textInfo->flagWord & FONTNUMWAIT) { + textInfo->flagWord &= (~FONTNUMWAIT); + fontIndex = *str - '0'; + if ((fontIndex >= 0) && (fontIndex < MAXFONTS)) { + /* Handle font -- go get next character */ + if (HandleNewFont(display, fontIndex, textInfo, DODISP)) + continue; + } + } + + /* Inline code for handling normal character case */ + if ((*str >= LOWCHAR) && (*str <= HIGHCHAR)) { + register XFontStruct *thisFont; + register struct txtLine *thisLine; + register int charWidth; + int thisColor; + + /* Determine size of character */ + thisFont = &(textInfo->theFonts[textInfo->curFont]); + thisColor = textInfo->theColors[textInfo->curFont]; + if (*str <= thisFont->min_char_or_byte2 || + *str >= thisFont->max_char_or_byte2 || + thisFont->per_char == 0) + charWidth = thisFont->max_bounds.width + 1; + else + charWidth = thisFont->per_char[*str].width + 1; + + /* Check to see if line wrap is required */ + thisLine = textInfo->txtBuffer[textInfo->curLine]; + if (thisLine->lineWidth + charWidth > + (textInfo->w-BARSIZE-WRAPINDSIZE)) + { + DrawLineWrap(display, textInfo->mainWindow, + textInfo->w-BARSIZE-WRAPINDSIZE, + textInfo->curY, thisLine->lineHeight, + textInfo->fgPix); + thisLine->lineFlags |= WRAPFLAG; + /* Handle the spacing problem the same way as a newline */ + HandleNewLine(display, textInfo, DODISP | NONEWLINE); + thisLine = textInfo->txtBuffer[textInfo->curLine]; + } + + /* Ready to draw character */ + XDrawString(display, textInfo->mainWindow, + DEFAULT_GC, + textInfo->curX += charWidth, + textInfo->curY + thisLine->lineHeight, + str, 1); + + /* Append character onto main buffer */ + if (textInfo->bufSpot >= textInfo->bufAlloc) + /* Make room for more characters */ + ExpandBuffer(textInfo); + textInfo->mainBuffer[(textInfo->bufSpot)++] = + (textInfo->curFont << FONTSHIFT) | (*str); + + /* Update the line start array */ + thisLine->lineLength += 1; + thisLine->lineWidth += charWidth; + } else if (*str == NEWLINE) { + HandleNewLine(display, textInfo, DODISP); + } else if (*str == NEWFONT) { + /* Go into waiting for font number mode */ + textInfo->flagWord |= FONTNUMWAIT; + } else if (*str == BACKSPACE) { + HandleBackspace(display, textInfo, DODISP); + } else { + /* Ignore all others */ + } + } + /* Draw the cursor in its new position */ + thisLine = textInfo->txtBuffer[textInfo->curLine]; + + XFillRectangle(display, w, textInfo->CursorGC, + thisLine->lineWidth + CUROFFSET, + textInfo->curY /* + thisLine->lineHeight */, + CURSORWIDTH, thisLine->lineHeight); + + return 1; +} + + + +int TxtJamStr(display, w, str) +Display *display; +Window w; /* Text window */ +register char *str; /* NULL terminated string */ +/* + * This is the same as TxtWriteStr except the screen is NOT updated. + * After a call to this routine, TxtRepaint should be called to + * update the screen. This routine is meant to be used to load + * a text buffer with information and then allow the user to + * scroll through it at will. + */ +{ + register int fontIndex; + register struct txtWin *textInfo; + + if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w) + ) == 0) + return 0; + + for ( /* str is ok */ ; (*str != 0) ; str++) { + /* Check to see if we are waiting on a font */ + if (textInfo->flagWord & FONTNUMWAIT) { + textInfo->flagWord &= (~FONTNUMWAIT); + fontIndex = *str - '0'; + if ((fontIndex >= 0) && (fontIndex < MAXFONTS)) { + if (HandleNewFont(display, fontIndex, textInfo, 0)) { + /* Handled font -- go get next character */ + continue; + } + } + } + /* Inline code for handling normal character case */ + if ((*str >= LOWCHAR) && (*str <= HIGHCHAR)) { + register XFontStruct *thisFont; + register struct txtLine *thisLine; + register int charWidth; + + /* Determine size of character */ + thisFont = &(textInfo->theFonts[textInfo->curFont]); + + if (*str <= thisFont->min_char_or_byte2 || + *str >= thisFont->max_char_or_byte2 || + thisFont->per_char == 0) + charWidth = thisFont->max_bounds.width + 1; + else + charWidth = thisFont->per_char[*str].width + 1; + + /* Check to see if line wrap is required */ + thisLine = textInfo->txtBuffer[textInfo->curLine]; + if (thisLine->lineWidth + charWidth > + (textInfo->w-BARSIZE-WRAPINDSIZE)) + { + thisLine->lineFlags |= WRAPFLAG; + /* Handle the spacing problem the same way as a newline */ + HandleNewLine(display, textInfo, NONEWLINE); + thisLine = textInfo->txtBuffer[textInfo->curLine]; + } + /* Append character onto main buffer */ + if (textInfo->bufSpot >= textInfo->bufAlloc) + /* Make room for more characters */ + ExpandBuffer(textInfo); + textInfo->mainBuffer[(textInfo->bufSpot)++] = + (textInfo->curFont << FONTSHIFT) | (*str); + + /* Update the line start array */ + thisLine->lineLength += 1; + thisLine->lineWidth += charWidth; + } else if (*str == NEWLINE) { + HandleNewLine(display, textInfo, 0); + } else if (*str == NEWFONT) { + /* Go into waiting for font number mode */ + textInfo->flagWord |= FONTNUMWAIT; + } else if (*str == BACKSPACE) { + HandleBackspace(display, textInfo, 0); + } else { + /* Ignore all others */ + } + } + textInfo->flagWord |= SCREENWRONG; + return 1; +} + + + +int TxtRepaint(display,w) +Display *display; +Window w; +/* + * Repaints the given scrollable text window. The routine repaints + * the entire window. For handling exposure events, the TxtFilter + * routine should be used. + */ +{ + struct txtWin *textInfo; + int index, ypos; + + if ((textInfo = (struct txtWin *) XLookUpAssoc(display, textWindows, (XID) w) + ) == 0) + return 0; + + /* Check to see if the screen is up to date */ + if (textInfo->flagWord & SCREENWRONG) { + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + textInfo->flagWord &= (~SCREENWRONG); + } + + ypos = YPADDING; + index = textInfo->startLine; + for (;;) { + DrawLine(display, textInfo, index, ypos); + if (index >= textInfo->endLine) break; + ypos += (textInfo->txtBuffer[index]->lineHeight + INTERLINE); + index++; + } + /* Draw the cursor (if on screen) */ + if (textInfo->endLine == textInfo->curLine) { + XFillRectangle(display, w, textInfo->CursorGC, + textInfo->txtBuffer[index]->lineWidth + CUROFFSET, + ypos /* + textInfo->txtBuffer[index]->lineHeight */, + CURSORWIDTH, textInfo->txtBuffer[index]->lineHeight); + + } + /* Update the scroll bar */ + UpdateScroll(display, textInfo); + return 1; +} + + + +static int InsertIndex(textInfo, thisIndex, ypos) +struct txtWin *textInfo; /* Text Window Information */ +int thisIndex; /* Line index of exposed line */ +int ypos; /* Drawing position of line */ +/* + * This routine inserts the supplied line index into the copy + * exposure array for 'textInfo'. The array is kept sorted + * from lowest to highest using insertion sort. The array + * is dynamically expanded if needed. + */ +{ + struct expEvent *newItem; + int newSize, index, downIndex; + + /* Check to see if we need to expand it */ + if ((textInfo->exposeSize + 3) >= textInfo->exposeAlloc) { + newSize = textInfo->exposeAlloc + + (textInfo->exposeAlloc * EXPANDPERCENT / 100); + textInfo->exposeAry = (struct expEvent **) + realloc((char *) textInfo->exposeAry, + (unsigned) (newSize * sizeof(struct expEvent *))); + for (index = textInfo->exposeAlloc; index < newSize; index++) + textInfo->exposeAry[index] = alloc(struct expEvent); + textInfo->exposeAlloc = newSize; + } + /* Find spot for insertion. NOTE: last spot has big number */ + for (index = 0; index <= textInfo->exposeSize; index++) { + if (textInfo->exposeAry[index]->lineIndex >= thisIndex) { + if (textInfo->exposeAry[index]->lineIndex > thisIndex) { + /* Insert before this entry */ + newItem = textInfo->exposeAry[textInfo->exposeSize+1]; + for (downIndex = textInfo->exposeSize; + downIndex >= index; + downIndex--) + { + textInfo->exposeAry[downIndex+1] = + textInfo->exposeAry[downIndex]; + } + /* Put a free structure at this spot */ + textInfo->exposeAry[index] = newItem; + /* Fill it in */ + textInfo->exposeAry[index]->lineIndex = thisIndex; + textInfo->exposeAry[index]->ypos = ypos; + /* Break out of loop */ + textInfo->exposeSize += 1; + } + break; + } + } + return 1; +} + + + +static int ScrollUp(display, textInfo) +Display *display; +struct txtWin *textInfo; /* Text window information */ +/* + * This routine scrolls the indicated text window up by one + * line. The line above the current line must exist. The + * window is scrolled so that the line above the start line + * is displayed at the top of the screen. This may cause + * many lines to scroll off the bottom. The scrolling is + * done using XCopyArea. The exposure events should be caught + * by ExposeCopy. + */ +{ + int targetSpace; + + /* Make sure all exposures have been handled by now */ + if (textInfo->startLine == 0) return 0; + targetSpace = textInfo->txtBuffer[textInfo->startLine-1]->lineHeight + + INTERLINE; + /* Move the area downward by the target amount */ + XCopyArea(display, textInfo->mainWindow, textInfo->mainWindow, + DEFAULT_GC, + 0, YPADDING, textInfo->w - BARSIZE, + textInfo->h, 0, targetSpace); + + textInfo->flagWord |= COPYEXPOSE; + /* Update the text window parameters */ + textInfo->startLine -= 1; + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + + /* Clear out bottom space region */ +#ifdef __386BSD__ + XClearArea(display, textInfo->mainWindow, + 0, textInfo->h - textInfo->bottomSpace, + textInfo->w, textInfo->bottomSpace, 1); +#else + XClearArea(display, textInfo->mainWindow, + 0, textInfo->h - textInfo->bottomSpace, + textInfo->w, textInfo->bottomSpace); +#endif + + UpdateExposures(display, textInfo); + UpdateScroll(display, textInfo); + + return 1; +} + + +static int ScrollToSpot(display, textInfo, ySpot) +Display *display; +struct txtWin *textInfo; /* Text window information */ +int ySpot; /* Button position in scroll window */ +/* + * This routine scrolls the specified text window relative to the + * position of the mouse in the scroll bar. The center of the screen + * will be positioned to correspond to the mouse position. + */ +{ + int targetLine, aboveLines; + + targetLine = textInfo->numLines * ySpot / textInfo->h; + textInfo->startLine = targetLine; + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + aboveLines = 0; + /* Make the target line the *center* of the window */ + while ((textInfo->startLine > 0) && + (aboveLines < textInfo->endLine - targetLine)) + { + textInfo->startLine -= 1; + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + aboveLines++; + } + if (textInfo->endLine == textInfo->numLines-1) { + WarpToBottom(display, textInfo); + } else { + XClearWindow(display, textInfo->mainWindow); + TxtRepaint(display, textInfo->mainWindow); + } + return 1; +} + + + +static int LineToTop(display, textInfo, pos) +Display *display; +struct txtWin *textInfo; /* Text window information */ +int pos; /* Y position of mouse */ +/* + * This routine scrolls the screen down until the line at the + * mouse position is at the top of the screen. It stops + * if it can't scroll the buffer down that far. If the + * global 'ScrollOption' is NORMSCROLL, a smooth scroll + * is used. Otherwise, it jumps to the right position + * and repaints the screen. + */ +{ + int index, sum; + + /* First, we find the current line */ + sum = 0; + for (index = textInfo->startLine; index <= textInfo->endLine; index++) { + if (sum + textInfo->txtBuffer[index]->lineHeight + INTERLINE> pos) break; + sum += textInfo->txtBuffer[index]->lineHeight + INTERLINE; + } + /* We always want to scroll down at least one line */ + if (index == textInfo->startLine) index++; + if (ScrollOption == NORMSCROLL) { + /* Scroll down until 'index' is the starting line */ + while ((textInfo->startLine < index) && ScrollDown(display, textInfo)) + { + /* Empty Loop Body */ + } + } else { + /* Immediately jump to correct spot */ + textInfo->startLine = index; + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + if (textInfo->endLine == textInfo->numLines-1) { + WarpToBottom(display, textInfo); + } else { + XClearWindow(display, textInfo->mainWindow); + TxtRepaint(display, textInfo->mainWindow); + } + } + /* Check to see if at end of buffer */ + if (textInfo->endLine >= textInfo->numLines-1) { + textInfo->flagWord &= (~NOTATBOTTOM); + } + return 1; +} + + + +static int TopToHere(display, textInfo, pos) +Display *display; +struct txtWin *textInfo; /* Text window information */ +int pos; /* Y position of mouse */ +/* + * This routine scrolls the screen up until the top line of + * the screen is at the current Y position of the mouse. Again, + * it will stop if it can't scroll that far. If the global + * 'ScrollOption' is NORMSCROLL, a smooth scroll is used. + * If it's not, it will simply redraw the screen at the + * correct spot. + */ +{ + int sum, target, linesup, index; + + target = pos - textInfo->txtBuffer[textInfo->startLine]->lineHeight; + /* We always want to scroll up at least one line */ + if (target <= 0) target = 1; + sum = 0; + linesup = 0; + /* Check to see if we are at the top anyway */ + if (textInfo->startLine == 0) return 0; + if (ScrollOption == NORMSCROLL) { + /* Scroll up until sum of new top lines greater than target */ + while ((sum < target) && ScrollUp(display, textInfo)) { + sum += textInfo->txtBuffer[textInfo->startLine]->lineHeight; + linesup++; + } + } else { + /* Search backward to find index */ + index = textInfo->startLine - 1; + while ((index > 0) && (sum < target)) { + sum += textInfo->txtBuffer[index]->lineHeight; + linesup++; + index--; + } + /* Go directly to the index */ + textInfo->startLine = index; + textInfo->endLine = FindEndLine(textInfo, &(textInfo->bottomSpace)); + XClearWindow(display, textInfo->mainWindow); + TxtRepaint(display, textInfo->mainWindow); + } + /* If we scrolled, assert we are not at bottom of buffer */ + if (linesup > 0) { + textInfo->flagWord |= NOTATBOTTOM; + } + return 1; +} + + + +int TxtFilter(display, evt) +Display *display; +XEvent *evt; +/* + * This routine handles events associated with scrollable text windows. + * It will handle all exposure events and any button released events + * in the scroll bar of a text window. It does NOT handle any other + * events. If it cannot handle the event, it will return 0. + */ +{ + XExposeEvent *expose = &evt->xexpose; + XButtonEvent *btEvt = &evt->xbutton; + XGraphicsExposeEvent *gexpose = &evt->xgraphicsexpose; + XNoExposeEvent *noexpose = &evt->xnoexpose; + struct txtWin *textInfo; + int index, ypos; + Window w, sw; + + if (textWindows == (XAssocTable *) 0) { + textWindows = XCreateAssocTable(32); + if (textWindows == (XAssocTable *) 0) return(0); + } + if (evt->type == Expose) { + w = expose->window; + sw = 0; + } + else if (evt->type == GraphicsExpose) { + w = gexpose->drawable; + sw = 0; + } + else if (evt->type == NoExpose) { + w = noexpose->drawable; + sw = 0; + } + else if (evt->type == ButtonRelease) { + w = btEvt->window; + sw = btEvt->subwindow; + } + else + return 0; + + if ((textInfo = (struct txtWin *) + XLookUpAssoc(display, textWindows, (XID) w)) == 0) + return 0; + + /* Determine whether it's main window or not */ + if ((w == textInfo->mainWindow) && (sw == 0)) { + /* Main Window - handle exposures */ + switch (evt->type) { + case Expose: + ypos = 0 /*YPADDING*/; + for (index = textInfo->startLine; + index <= textInfo->endLine; + index++) + { + int lh = textInfo->txtBuffer[index]->lineHeight; + + if (((ypos + lh) >= expose->y) && + (ypos <= (expose->y + expose->height))) + { + /* Intersection region */ + /* Draw line immediately */ + DrawLine(display, textInfo, index, ypos); + /* And possibly draw cursor */ + if (textInfo->curLine == index) { + XFillRectangle(display, w, textInfo->CursorGC, + textInfo->txtBuffer[index]->lineWidth + + CUROFFSET, + ypos, + CURSORWIDTH, + lh); + } + } + ypos += lh + INTERLINE; + } + break; + case GraphicsExpose: + ypos = 0 /*YPADDING*/; + for (index = textInfo->startLine; + index <= textInfo->endLine; + index++) + { + int lh = textInfo->txtBuffer[index]->lineHeight; + + if (((ypos + lh) >= gexpose->y) && + (ypos <= (gexpose->y + gexpose->height))) + { + /* Intersection region */ + /* Draw line immediately */ + DrawLine(display, textInfo, index, ypos); + /* And possibly draw cursor */ + if (textInfo->curLine == index) { + XFillRectangle(display, w, textInfo->CursorGC, + textInfo->txtBuffer[index]->lineWidth + + CUROFFSET, + ypos, + CURSORWIDTH, + lh); + } + } + ypos += lh + INTERLINE; + } + break; + case NoExpose: + break; + default: + /* Not one of our events */ + return 0; + } + } else { + switch (evt->type) { + case Expose: + UpdateScroll(display, textInfo); + break; + case ButtonRelease: + /* Find out which button */ + switch (btEvt->button) { + case Button1: + /* Scroll up until top line is at mouse position */ + TopToHere(display, textInfo, btEvt->y); + break; + case Button2: + /* Scroll to spot relative to position */ + ScrollToSpot(display, textInfo, btEvt->y); + if (textInfo->endLine >= textInfo->numLines-1) { + textInfo->flagWord &= (~NOTATBOTTOM); + } else { + textInfo->flagWord |= NOTATBOTTOM; + } + break; + case Button3: + /* Scroll down until pointed line is at top */ + LineToTop(display, textInfo, btEvt->y); + break; + } + break; + default: + /* Not one of our events */ + return 0; + } + } + return 1; +} |
