aboutsummaryrefslogtreecommitdiff
path: root/gnu/games/chess/Xchess/scrollText.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/games/chess/Xchess/scrollText.c')
-rw-r--r--gnu/games/chess/Xchess/scrollText.c1877
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;
+}