1859 lines
56 KiB
C
1859 lines
56 KiB
C
/*
|
||
* 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);
|
||
XFillRectangle(display, textInfo->scrollBar,
|
||
DEFAULT_GC, top, BARSIZE - (2*BARBORDER) - 2,
|
||
bottom - top);
|
||
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 */
|
||
XCopyArea(display, textInfo->arrowMap, textInfo->mainWindow,
|
||
textInfo->CursorGC,
|
||
0, 0, arrow_width, arrow_height,
|
||
x, y + h - arrow_height, 1);
|
||
|
||
/* 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 */
|
||
XClearArea(display, textInfo->mainWindow,
|
||
0, textInfo->h - textInfo->bottomSpace,
|
||
textInfo->w, textInfo->bottomSpace);
|
||
|
||
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;
|
||
}
|