home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / games / chess / Xchess / scrollText.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-10-16  |  56.4 KB  |  1,859 lines

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