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;
|
|||
|
}
|