home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
NOTEPAD2.ZIP
/
MDISP.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-02-08
|
38KB
|
1,206 lines
/*
* mdisp.c -- Display manager
*
* Created by Microsoft Corporation, 1989
*/
#define INCL_WIN
#define INCL_GPI
#include <os2.h>
#include <opendlg.h>
#include "mle.h"
#include "mtypes.h"
#include "mfuncs.h"
/***********************************************************************
* Private forward declarations
***********************************************************************/
private PIX YFromLine(PED ped, LINE ln);
private LINE LineFromY(PED ped, PIX y);
private VOID DestroyCursor(PED ped);
private BOOL LineIntersectsRect(PED ped, LINE ln, RECTL rcl);
/***********************************************************************
* Initialization and gross change
***********************************************************************/
/*
* VOID DisplayInit(PED ped)
*
* Initializes the display manager, creating a PS and such as needed.
*/
public VOID DisplayInit(PED ped)
{
HDC hdc; // device context for PS
SIZEL sizl; // page size for presentation space
PMR pmr;
/* Create and configure the PS, plus a scratch region handle. */
hdc = WinOpenWindowDC(ped->hwnd);
sizl.cx = 0L;
sizl.cy = 0L;
ped->hps = GpiCreatePS((HAB)NULL, hdc, &sizl, PU_PELS
| GPIF_DEFAULT
| GPIT_MICRO
| GPIA_ASSOC);
GpiCreateLogColorTable(ped->hps, 0L, LCOLF_RGB, 0L, 0L, 0L);
// Specify all colors for this hps as 24-bit RGB values
GpiSetBackMix(ped->hps, BM_OVERPAINT);
// New text should wipe out old
ped->hrgnT = GpiCreateRegion (ped->hps, 0L, NULL);
// set the scrolling to the beginning of the document
ped->pixCurMax = 1;
ped->hScroll = 0;
// initialize visible line information
// assumes NULL == 0
pmr = InsEndMarker(ped,ped->pprHead,MARKER_DISPLAY);
LockMarker(pmr,TRUE);
ped->pmrFVL = pmr;
ped->iptFVL = 0;
ped->papmrValid = (PPMR)AllocStore(ped,(sizeof(PMR))*DISPLAY_MAXLINES);
LFillStruct((PCH)(ped->papmrValid),(sizeof(PMR))*DISPLAY_MAXLINES,(BYTE)0);
// compute current slider positions and notify owner
NotifyScroll(ped, EN_HSCROLL, TRUE);
NotifyScroll(ped, EN_VSCROLL, TRUE);
ped->fFocus = FALSE;
ped->fFattrDelta = FALSE;
ped->fHSizeDelta = FALSE;
ped->fVSizeDelta = FALSE;
ped->fScrollDelta = FALSE;
ped->TabSize = DEFAULT_TABSIZE;
ped->pcwt = AllocStore(ped, MAXCHAR*sizeof(LONG));
DispChangeFont(ped,NULL);
ped->pixCurMax = ped->AveCharWidth; // decent initial value
ped->fExistsCursor = FALSE;
ped->fInvert = FALSE;
}
/*
* VOID SetFocus(PED ped, BOOL fReceiveFocus)
*
* Handle setting/losing focus
*/
public VOID SetFocus(PED ped, BOOL fReceiveFocus)
{
if (fReceiveFocus) {
if (!ped->fFocus) { // if we didn't already have focus...
ped->fFocus = TRUE;
NotifyOwner(ped, EN_SETFOCUS, NULL);
}
} else { // losing focus
DestroyCursor(ped);
ped->fFocus = FALSE;
NotifyOwner(ped, EN_KILLFOCUS, NULL);
}
// ped->fSelDelta = TRUE;
// DispRefresh(ped, FALSE, ped->rclWnd);
}
/*
* VOID DispChangeFont(PED ped, PFATTRS pfattrs)
*
* Change the drawing font
*/
public VOID DispChangeFont(PED ped, PFATTRS pfattrs)
{
FONTMETRICS fntmet;
SHORT s;
PIX pix;
if (pfattrs == NULL ) { // Reset to the default (system) font
GpiSetCharSet (ped->hps, LCID_DEFAULT);
if (ped->pfattrs != NULL) {
ped->fFattrDelta = TRUE;
ped->pfattrs = NULL;
}
} else {
/* Use the pfattrs we've been passed to select the font
* If we can create the special font, AND if we can set to it,
* then reset the entry in the edit record to this pfattrs to
* indicate our success.
*/
if (GpiCreateLogFont(ped->hps, (PSTR8)"DUMMY",
DUMMY_FONT_SETID, pfattrs) &&
GpiSetCharSet(ped->hps, DUMMY_FONT_SETID)) {
GpiSetCharSet (ped->hps, LCID_DEFAULT);
GpiDeleteSetId(ped->hps, DUMMY_FONT_SETID);
GpiDeleteSetId(ped->hps, SPECIAL_FONT_SETID);
GpiCreateLogFont(ped->hps, (PSTR8)"DUMMY",
SPECIAL_FONT_SETID, pfattrs);
GpiSetCharSet(ped->hps, SPECIAL_FONT_SETID);
ped->pfattrs = pfattrs;
}
ped->fFattrDelta = TRUE;
}
/* Insure the correct character mode */
GpiSetCharMode (ped->hps, CM_MODE1);
/* Get font size information for the new font */
GpiQueryFontMetrics(ped->hps,(LONG)sizeof(fntmet),&fntmet);
ped->AveCharWidth = (SHORT) fntmet.lAveCharWidth;
ped->yCharPels = (SHORT) fntmet.lMaxBaselineExt;
ped->yDescPels = (SHORT) fntmet.lMaxDescender;
ped->pixMaxChar = (PIX) fntmet.lMaxCharInc;
/* Get width table information for this font. Starting from
MINCHAR, read MAXCHAR widths into the global width table. */
GpiQueryWidthTable (ped->hps, MINCHAR, MAXCHAR, ped->pcwt);
pix = ped->pixMaxChar;
for (s=0; s<(MAXCHAR); s++) {
if (ped->pcwt[s] < pix) {
pix = (PIX)ped->pcwt[s];
}
}
ped->pixMinChar = pix;
/* Set the width for a return character to zero */
ped->pcwt['\n'] = (UCHAR) 0;
}
/*
* VOID DispChangeSize(PED ped,USHORT xWndPels,USHORT yWndPels)
*
* Called at WM_SIZE. Updates all the window's dimensions (in ped);
* requests reformatting later. Note that MLE has the class style
* CS_SIZEREDRAW, so the window will be redrawn automatically.
*
* Assumes font information is correct (DispChangeFont must have been called)
*/
public VOID DispChangeSize (PED ped, USHORT xWndPels, USHORT yWndPels)
{
LINE lyWndSize; // text window height in chars
PIX xTextWidth; // text window width in pixels
PIX dxMargLft, dxMargRgt,
dyMargTop, dyMargBot; // margins in pixels
RECTL rclOld; // old text rectangle
/* Find the rectangle of the window's area, in window coordinates. */
ped->rclWnd.xLeft = 0L;
ped->rclWnd.yBottom = 0L;
ped->rclWnd.xRight = xWndPels;
ped->rclWnd.yTop = yWndPels;
/* Find the size of the margins. */
dxMargLft = (ped->AveCharWidth * MARG_LFTNUM) / MARG_LFTDEN;
dxMargRgt = (ped->AveCharWidth * MARG_RGTNUM) / MARG_RGTDEN;
dyMargTop = (ped->yCharPels * MARG_TOPNUM) / MARG_TOPDEN;
dyMargBot = (ped->yCharPels * MARG_BOTNUM) / MARG_BOTDEN;
/* Find the number of lines we can fit into the window, not counting
* the margins.
*/
lyWndSize = max((LINE)0,
(LINE)((yWndPels-dyMargBot-dyMargTop)/ped->yCharPels));
ped->lyWndSize = lyWndSize;
/* Now that we know how many characters fit into the window horizontally
and vertically, we can determine the minimum rectangle that holds those
characters. The minimum rectangle may actually be shorter than the
space between the top and bottom margins, since we rounded down to an
integral number of lines; we center it in that space.
*/
/* Rectangles:
There are two rectangles associated with this edit control. The
first is the rectangle defining the edit control window, which is
stored in ped->rclWnd. Within this, there's a slightly smaller
text rectangle (ped->rclText) within which all text is displayed,
thus providing a nice margin. The width of the text window, in
pixels, is stored in xTextWidth; its height in lines is stored
in ped->lyWndSize.
*/
rclOld = ped->rclText;
ped->rclText.xLeft = dxMargLft;
ped->rclText.xRight = max(xWndPels-dxMargRgt,dxMargLft);
xTextWidth = (SHORT)ped->rclText.xRight - (SHORT)ped->rclText.xLeft;
ped->rclText.yBottom = (yWndPels+(dyMargBot-dyMargTop-ped->yCharPels*lyWndSize))/2;
ped->rclText.yTop = ped->rclText.yBottom+lyWndSize*ped->yCharPels;
ped->fHSizeDelta = ped->fHSizeDelta ||
(ped->rclText.xLeft != rclOld.xLeft) ||
(ped->rclText.xRight != rclOld.xRight);
ped->fVSizeDelta = (ped->rclText.yTop != rclOld.yTop) ||
ped->fVSizeDelta || (ped->rclText.yBottom != rclOld.yBottom);
}
/***********************************************************************
* Cursor/selection handling
***********************************************************************/
/*
* VOID DestroyCursor(PED ped)
*
* Destroy the cursor.
*/
private VOID DestroyCursor(PED ped)
{
if (ped->fExistsCursor) {
WinDestroyCursor(ped->hwnd);
ped->fExistsCursor = FALSE;
}
}
/*
* VOID PositionCursor(PED ped, PIX pixY, PIX pixX, BOOL fForce)
*
* position the cursor to window x,y. If fForce, force sizing of the
* cursor; otherwise, just move it if possible.
*/
private VOID PositionCursor(PED ped, PIX pixY, PIX pixX, BOOL fForce)
{
if (!(ped->fFocus)) {
DestroyCursor(ped);
} else /* if (!(ped->fMouseDown)) */ {
WinCreateCursor(ped->hwnd,
(SHORT)(pixX+ped->rclText.xLeft-ped->hScroll),
(SHORT)pixY,
(SHORT)0,
(SHORT)(ped->yCharPels),
((ped->fExistsCursor && !fForce)
? (CURSOR_SETPOS)
: (CURSOR_SOLID | CURSOR_FLASH)),
&(ped->rclWnd));
WinShowCursor(ped->hwnd, TRUE);
ped->fExistsCursor = TRUE;
}
}
/*
* VOID HideCursor(PED ped)
*
* Hide the cursor, if any exists.
*/
private VOID HideCursor(PED ped)
{
if (ped->fExistsCursor) {
WinShowCursor(ped->hwnd, FALSE);
}
}
/***********************************************************************
* Text drawing helpers
***********************************************************************/
/*
* VOID SetInvert(PED ped, BOOL fInvert)
*
* Set the drawing colors as either normal or inverted based on the
* fInvert flag.
*/
private VOID SetInvert(PED ped, BOOL fInvert)
{
if (ped->fInvert != fInvert) {
GpiSetBackColor(ped->hps, fInvert ? SYSCLR_WINDOWTEXT : SYSCLR_WINDOW);
GpiSetColor(ped->hps, fInvert ? SYSCLR_WINDOW : SYSCLR_WINDOWTEXT);
ped->fInvert = fInvert;
}
}
/*
* VOID WipePix(PED ped, PIX pix, BOOL fInvert)
*
* From current position, wipe out pix pixels. If pix <= 0, wipe to end of
* line. Sets current position appropriately. Colour is dependent on
* fInvert.
*/
private VOID WipePix(PED ped, PIX pix, BOOL fInvert)
{
RECTL rclWipe;
POINTL ptlEnd;
GpiQueryCurrentPosition(ped->hps, &ptlEnd);
rclWipe.yBottom = ptlEnd.y-ped->yDescPels;
rclWipe.yTop = rclWipe.yBottom + ped->yCharPels;
rclWipe.xLeft = ptlEnd.x;
rclWipe.xRight = (pix<0)
? (ped->rclText.xRight)
: (ptlEnd.x+pix);
WinFillRect(ped->hps, (PRECTL)&rclWipe,
fInvert ? SYSCLR_WINDOWTEXT : SYSCLR_WINDOW);
ptlEnd.x = rclWipe.xRight;
GpiMove(ped->hps, &ptlEnd);
}
#define OBUFFSIZE 100
/*
* VOID OutputBuff(PED ped, PCHAR pch, POFFSET poff)
*
* Outputs the contents of the output buffer.
*/
private VOID OutputBuff(PED ped, PCHAR pch, POFFSET poff)
{
if (*poff > 0) {
GpiCharString(ped->hps, (LONG)*poff, pch);
*poff = 0;
}
}
/*
* VOID AppendBuff(PED ped, PCHAR pch, POFFSET poff, CHAR ch)
*
* Appends ch to the end of the output buffer, flushing if the output
* buffer is full.
*/
private VOID AppendBuff(PED ped, PCHAR pch, POFFSET poff, CHAR ch)
{
*(pch+((*poff)++)) = ch;
if (*poff > OBUFFSIZE)
OutputBuff(ped, pch, poff);
}
/*
* VOID ExtRedrawSel(PED ped, PMR pmr, IPT ipt, PIPT piptMin, PIPT piptMax,
* PBOOL pfMatched)
*
* Given a range of characters that is known to need redrawing, extend
* that range (if needed) to include changes to the selection.
* Changes *piptMin and *piptMax accordingly; if *piptMin is changed,
* *pfMatched is set to FALSE (otherwise left alone). pmr is the
* line break record for the line in question; ipt is the starting ipt
* of that line.
*/
private VOID ExtRedrawSel(PED ped, PMR pmr, IPT ipt, PIPT piptMin,
PIPT piptMax, PBOOL pfMatched)
{
#define exch(a, b, t) {t=a; a=b; b=t;}
IPT a0, a1, a2, a3, t;
// find out what's been drawn, and what should be, and save for next time
a0 = pmr->cchBegin;
a1 = pmr->cchEnd;
pmr->cchBegin = a2 = max((IPT)0,min(pmr->cchLine,TxtQueryMinSel(ped)-ipt));
pmr->cchEnd = a3 = max((IPT)0,min(pmr->cchLine,TxtQueryMaxSel(ped)-ipt));
// sort a0..a3 ascending
if (a1>a2)
exch(a1,a2,t);
if (a0>a1)
exch(a0,a1,t);
if (a2>a3)
exch(a2,a3,t);
if (a1>a2)
exch(a1,a2,t);
#undef exch
// extend the range as needed on the low end
if (a0 != a1) {
if (a0 < *piptMin) {
*pfMatched = FALSE;
*piptMin = a0;
}
if (a1 > *piptMax) {
*piptMax = a1;
}
}
// extend the range as needed on the high end
if (a2 != a3) {
if (a2 < *piptMin) {
*pfMatched = FALSE;
*piptMin = a2;
}
if (a3 > *piptMax) {
*piptMax = a3;
}
}
}
/*
* VOID ExtRedrawRcl(PED ped, RECTL rcl, PPIX ppixMin, PPIX ppixMax,
* PBOOL pfMatched)
*
* Given a range of pixels that is known to need redrawing, extend
* that range (if needed) to include the redraw rectangle.
* Changes *ppixMin and *ppixMax accordingly; if *ppixMin is changed,
* *pfMatched is set to FALSE (otherwise left alone).
* Rectangle is in window coordinates.
*/
private VOID ExtRedrawRcl(PED ped, RECTL rcl, PPIX ppixMin,
PPIX ppixMax, PBOOL pfMatched)
{
PIX xL, xR;
xL = (PIX)rcl.xLeft - (PIX)ped->rclText.xLeft + ped->hScroll;
xR = (PIX)rcl.xRight - (PIX)ped->rclText.xLeft + ped->hScroll;
// extend the range as needed on the low end
if (xL != xR) {
if (xL < *ppixMin) {
*pfMatched = FALSE;
*ppixMin = xL;
}
if (xR > *ppixMax) {
*ppixMax = xR;
}
}
}
/***********************************************************************
* General drawing drivers
***********************************************************************/
/*
* PMR DrawLine(PED ped, PMR pmr, LINE ln, IPT ipt, RECTL rcl, BOOL fForce)
*
* Draw one line, given pointer to line-break record and the screen
* position. Returns pointer to marker record for next line.
* If fForce is FALSE, only the invalid part of the line will be redrawn;
* otherwise, the entire line will be redrawn.
*/
private PMR DrawLine(PED ped,PMR pmr,LINE ln,IPT ipt,RECTL rcl,BOOL fForce)
{
POINTL ptl;
PIX pix;
PPR ppr, ppr2;
OFFSET off;
IPT iptMin, iptMax, iptCur, iptIMin, iptIMax;
PIX pixMin, pixMax;
IPT iptOff;
static CHAR obuff[OBUFFSIZE];
OFFSET obuffptr;
register CHAR ch;
register PIX pixCh;
BOOL fMatched;
BOOL fLastLine;
fLastLine = TxtLength(ped) == ipt + pmr->cchLine;
// keep track of where in line the cursor falls
iptCur = TxtQueryCursor(ped)-ipt;
iptIMin = TxtQueryMinSel(ped);
iptIMax = TxtQueryMaxSel(ped);
// find out where this line should be drawn, vertically
ptl.y = ped->rclText.yTop-(ped->yCharPels*(ln+1))+ped->yDescPels;
// set the range to be redrawn; take redraw rectangle and selection
// into account
fMatched = TRUE;
pixMin = fForce ? (PIX)0 : pmr->pixValid;
pixMax = pmr->pixLine;
iptMin = fForce ? (IPT)0 : pmr->cchValid;
iptMax = pmr->cchLine;
if (!fForce) {
ExtRedrawSel(ped, pmr, ipt, &iptMin, &iptMax, &fMatched);
ExtRedrawRcl(ped, rcl, &pixMin, &pixMax, &fMatched);
}
// get ppr/off to the first text character in the line (if any)
if (pmr->cchLine == 0) {
ppr = pmr->pr.pprTextNext;
} else {
ppr = SkipMarkers((PPR)pmr, MARKER_ANY);
}
off = 0;
// only do the drawing if there's something that needs to be
// redrawn:
if ((iptMin == pmr->cchLine) && (pixMin == pmr->pixLine)
&& (pixMax == pmr->pixLine)) {
ptl.x = ped->rclText.xLeft - ped->hScroll + pmr->pixLine;
GpiMove(ped->hps, &ptl);
} else {
// if we still have a matched pixMin/iptMin, advance ppr/off that far.
if (fMatched) {
pix = pixMin;
ipt += iptMin;
if (iptMin == pmr->cchLine) {
ppr = SkipText(ppr, MARKER_LINE);
off = 0;
} else {
iptOff = iptMin;
ppr = PprOfOffset(ppr, &iptOff);
off = (OFFSET)iptOff;
}
} else {
pix = 0;
}
// Search for the beginning of the text that should be drawn
ch = CharOfPointer(ppr,off);
pixCh = PixOfChar(ped, pix, ch);
while ((!(fIsMarker(ppr) && (((PMR)ppr)->fFlags & MARKER_LINE))) &&
(pix+pixCh < pixMin) && (ipt < iptMin)) {
if (!fIsMarker(ppr)) {
pix += pixCh;
ipt++;
}
ch = AdvancePointer(&ppr,&off);
pixCh = PixOfChar(ped, pix, ch);
}
// set up for drawing loop
ptl.x = ped->rclText.xLeft - ped->hScroll + pix;
GpiMove(ped->hps, &ptl);
obuffptr = 0;
// buffer up and output chunks of text until a line marker
// is hit. Handle tabs specially.
while ((!(fIsMarker(ppr) && (((PMR)ppr)->fFlags & MARKER_LINE))) &&
((pix <= pixMax) || (ipt <= iptMax))) {
if (!fIsMarker(ppr)) {
if (((ipt >= iptIMin) && (ipt < iptIMax)) != ped->fInvert) {
OutputBuff(ped, obuff, &obuffptr);
SetInvert(ped,!ped->fInvert);
}
pixCh = PixOfChar(ped, pix, ch);
if (ch == '\t') {
OutputBuff(ped, obuff, &obuffptr);
WipePix(ped, pixCh, ped->fInvert);
} else if (ch == '\n') {
AppendBuff(ped, obuff, &obuffptr, DISPLAY_NEWLINECHAR);
} else {
AppendBuff(ped, obuff, &obuffptr, ch);
}
pix += pixCh;
ipt++;
} // else skip past marker
ch = AdvancePointer(&ppr, &off);
}
// flush the character buffer and clear to end-of-line
OutputBuff(ped, obuff, &obuffptr);
// always wipe out the whitespace on the right when hit EOL
if (fIsMarker(ppr) && (((PMR)ppr)->fFlags & MARKER_LINE)) {
WipePix(ped, (PIX)(-1), FALSE);
}
}
// validate the line
pmr->sScreenLine = ln;
ped->papmrValid[ln] = pmr;
pmr->cchValid = pmr->cchLine;
pmr->pixValid = pmr->pixLine;
// Set cursor within the line (if applicable)
if (((iptCur>=0) && (iptCur<pmr->cchLine)) ||
(iptCur == pmr->cchLine && fLastLine)) {
ppr2 = (PPR)pmr;
PositionCursor(ped,
(SHORT)ptl.y-ped->yDescPels,
PixFromText(ped,&ppr2,&off,(OFFSET)iptCur,0L),
fForce);
}
// return the next line break record
ppr = SkipText(ppr,MARKER_LINE);
return((PMR)ppr);
}
#undef OBUFFSIZE
/*
* VOID WhiteOutBottom(PED ped, LINE ln)
*
* White out the bottom region of the screen, starting with line ln
* and including all screen area below.
*/
private VOID WhiteOutBottom(PED ped, LINE ln)
{
RECTL rcl;
rcl = ped->rclWnd;
rcl.yTop = ped->rclText.yTop-(ped->yCharPels*ln);
WinFillRect(ped->hps, (PRECTL)&rcl, SYSCLR_WINDOW);
}
/***********************************************************************
* Scroll Handling
***********************************************************************/
/*
* struct _SCROLLCTX -- a scrolling context
*/
struct _SCROLLCTX {
RECTL rcl;
PIX dy;
PMR pmrBegin;
LINE cln;
};
typedef struct _SCROLLCTX SCROLLCTX;
typedef SCROLLCTX FAR *PSCROLLCTX;
/*
* VOID FlushScroll(PED ped, PSCROLLCTX psc)
*
* Given a rectangle that has been determined to require scrolling,
* do so. When done, clear the scrolling information and record where
* lines are now.
*/
private VOID FlushScroll(PED ped, PSCROLLCTX psc)
{
PMR pmr;
RECTL rclUpdate;
SHORT sRet;
LINE ln, lnMax;
if (psc->cln > 0) {
// scroll the text
sRet = WinScrollWindow(ped->hwnd, 0, (SHORT)(psc->dy),
(PRECTL)&(psc->rcl), (PRECTL)&(ped->rclText),
NULL, (PRECTL)&rclUpdate, 0);
// mark lines as valid if they're scrolled
pmr = psc->pmrBegin;
while (psc->cln) {
ped->papmrValid[pmr->sScreenLine]=pmr;
pmr = (PMR)SkipText(pmr->pr.pprTextNext,MARKER_LINE);
psc->cln--;
}
// invalidate lines that need redrawing
lnMax = min(ped->lyWndSize, LineFromY(ped,(PIX)rclUpdate.yBottom));
for (ln = max(0,LineFromY(ped,(PIX)rclUpdate.yTop)); ln<lnMax; ln++) {
if (LineIntersectsRect(ped, ln, rclUpdate)) {
ped->papmrValid[ln] = NULL;
}
}
// clean up the scroll context
psc->dy = 0;
}
}
/*
* VOID ExtendScroll(PED ped, PSCROLLCTX psc)
*
* Given a scrolling rectangle and amount, add one line to the bottom of
* the rectangle.
*/
private VOID ExtendScroll(PED ped, PSCROLLCTX psc)
{
psc->rcl.yBottom -= ped->yCharPels;
psc->cln++;
}
/*
* VOID SetScroll(PED ped, PSCROLLCTX psc, PIX dy, PMR pmr, LINE ln)
*
* Given that the text bits for line ln can be found by scrolling them
* dy pixels, set up the appropriate scrolling context.
*/
private VOID SetScroll(PED ped, PSCROLLCTX psc, PIX dy, PMR pmr, LINE ln)
{
psc->dy = dy;
psc->rcl = ped->rclText;
psc->pmrBegin = pmr;
psc->cln = 1;
psc->rcl.yBottom = YFromLine(ped, ln) - ped->yDescPels - dy;
psc->rcl.yTop = psc->rcl.yBottom + ped->yCharPels;
}
/*
* VOID InitScroll(PED ped, PSCROLLCTX psc)
*
* Initialize a scrolling context.
*/
private VOID InitScroll(PED ped, PSCROLLCTX psc)
{
if (ped) psc->cln = 0;
}
/*
* VOID ScrollMovedLines(PED ped, PMR pmr, IPT ipt)
*
* Scroll lines that are valid (or partly so) but have moved on-screen.
* The first line on the screen is defined by pmr. Ipt is the ipt
* immediately preceding the first character on that line.
*/
private VOID ScrollMovedLines(PED ped, PMR pmr, IPT ipt)
{
LINE ln;
PMR pmr2;
PIX dyCur, dyLine;
SCROLLCTX sc;
ln = 0;
InitScroll(ped,(PSCROLLCTX)&sc);
dyCur = 0;
while ((ln<ped->lyWndSize) && !(pmr->fFlags & MARKER_EOT)) {
pmr2 = (PMR)SkipText(pmr->pr.pprTextNext, MARKER_LINE);
// try to scroll this line if it contains valid text
if ((pmr->cchValid == pmr->cchLine) &&
(pmr->sScreenLine != ln) &&
(pmr->sScreenLine >= 0) &&
(pmr->sScreenLine <= ped->lyWndSize) &&
(pmr == ped->papmrValid[pmr->sScreenLine])) {
dyLine = (pmr->sScreenLine - ln) * ped->yCharPels;
if (dyLine != dyCur) {
FlushScroll(ped, &sc);
SetScroll(ped, &sc, dyLine, pmr, ln);
dyCur = dyLine;
} else {
ExtendScroll(ped, &sc);
}
pmr->sScreenLine = ln;
} else {
dyCur = 0;
FlushScroll(ped, &sc);
}
// set up next line
ln++;
ipt += pmr->cchLine;
pmr = pmr2;
}
FlushScroll(ped, &sc);
}
/***********************************************************************
* General redraw driver functions
***********************************************************************/
/*
* VOID DrawOnlyDirty(PED ped, RECTL rcl)
*
* Draw dirty or mismatched lines onto the screen
*/
private VOID DrawOnlyDirty(PED ped, RECTL rcl)
{
LINE ln;
PMR pmr, pmr2;
IPT ipt;
BOOL fForce;
// get marker for first line on screen
pmr = (PMR)SkipBackText((PPR)(ped->pmrFVL), MARKER_LINE);
ipt = ped->iptFVL;
// Handle scroll possibilities before attempting to draw...
ScrollMovedLines(ped, pmr, ipt);
// possibly draw each line...
ln = 0;
if (ped->fSelDelta)
HideCursor(ped);
while ((ln < ped->lyWndSize) && !(pmr->fFlags & MARKER_EOT)) {
fIsMarker((PPR)pmr);
fForce = ((pmr->sScreenLine!=ln) ||
(ped->papmrValid[ln]!=pmr));
if ((pmr->cchValid != pmr->cchLine) ||
(pmr->cchValid == 0) ||
fForce ||
(ped->fScrollDelta) ||
(ped->fSelDelta)) {
pmr2 = DrawLine(ped, pmr, ln, ipt, rcl, fForce);
} else {
pmr2 = (PMR)SkipText(pmr->pr.pprTextNext, MARKER_LINE);
}
ln++;
ipt += pmr->cchLine;
pmr = pmr2;
}
WhiteOutBottom(ped, ln);
}
/*
* VOID DrawEverything(PED ped)
*
* Draw all lines onto the screen
*/
private VOID DrawEverything(PED ped)
{
LINE ln;
PMR pmr, pmr2;
IPT ipt;
HRGN hrgn, hrgnOld;
HideCursor(ped);
hrgn = GpiCreateRegion(ped->hps, 1L, (PRECTL)&(ped->rclWnd));
GpiSetClipRegion(ped->hps, hrgn, &hrgnOld);
GpiDestroyRegion(ped->hps, hrgnOld);
WinFillRect(ped->hps, (PRECTL)&(ped->rclWnd), SYSCLR_APPWORKSPACE);
hrgn = GpiCreateRegion(ped->hps, 1L, (PRECTL)&(ped->rclText));
GpiSetClipRegion(ped->hps, hrgn, &hrgnOld);
GpiDestroyRegion(ped->hps, hrgnOld);
pmr = (PMR)SkipBackText((PPR)(ped->pmrFVL), MARKER_LINE);
ipt = ped->iptFVL;
ln = 0;
while ((ln<ped->lyWndSize) && !(pmr->fFlags & MARKER_EOT)) {
pmr2 = DrawLine(ped, pmr, ln, ipt, ped->rclWnd, TRUE);
ln++;
ipt += pmr->cchLine;
pmr = pmr2;
}
WhiteOutBottom(ped, ln);
}
/*
* VOID DispRefresh(PED ped, BOOL fForce, RECTL rcl)
*
* Refresh the display. Only the area defined by the rectangle rcl needs
* to be considered. Unless fForce is specified, only the minimum necessary
* redraw, based on change flags in the ped, will be drawn.
*/
public VOID DispRefresh(PED ped, BOOL fForce, RECTL rcl)
{
if (ped->fFattrDelta || ped->fHSizeDelta) {
TxtReformat(ped, ped->pprHead, TRUE);
DrawEverything(ped);
} else if (fForce || ped->fVSizeDelta) {
DrawEverything(ped);
} else {
DrawOnlyDirty(ped,rcl);
}
ped->fFattrDelta = ped->fVSizeDelta = ped->fScrollDelta =
ped->fHSizeDelta = ped->fSelDelta = FALSE;
}
/***********************************************************************
* Miscellaneous queries
***********************************************************************/
/*
* PIX DispWidth(PED ped)
*
* Returns the width, in pixels, of the text-display area
*/
public PIX DispWidth(PED ped)
{
return((SHORT)ped->rclText.xRight - (SHORT)ped->rclText.xLeft);
}
/*
* LINE DispHeight(PED ped)
*
* Returns the height, in lines, of the text-display area
*/
public LINE DispHeight(PED ped)
{
return(ped->lyWndSize);
}
/*
* PIX YFromLine(PED ped, LINE ln)
*
* Returns the y-coordinate corresponding to the baseline of characters
* drawn on line ln. Note that this function works properly even if
* ln does not correspond to a line visible on screen.
*/
private PIX YFromLine(PED ped, LINE ln)
{
return((PIX)(ped->rclText.yTop)-(ped->yCharPels*(ln+1))+ped->yDescPels);
}
/*
* BOOL LineIntersectsRect(PED ped, LINE ln, RECTL rcl)
*
* returns whether the rectangle containing line ln, when intersected
* with the rectangle rcl, gives a non-null intersection. Rcl is in
* window coordinates.
*/
private BOOL LineIntersectsRect(PED ped, LINE ln, RECTL rcl)
{
PIX y;
y = YFromLine(ped, ln);
return((rcl.yBottom <= y-ped->yDescPels+ped->yCharPels) &&
(rcl.yTop >= y-ped->yDescPels));
}
/*
* LINE LineFromY(PED ped, PIX y)
*
* Returns the line containing the vertical coordinate y.
* Note that this function works properly even if
* y does not correspond to a line visible on screen.
*/
private LINE LineFromY(PED ped, PIX y)
{
return((LINE)((ped->rclText.yTop - y) / ped->yCharPels));
}
/***********************************************************************
* Horizontal scroll setting
***********************************************************************/
/*
* VOID DispHScroll(PED ped, PIX dx, BOOL fNotify)
*
* changes the horizontal scrolling by dx (if possible). If fNotify is
* true, a notification is sent to the owner window.
*/
public VOID DispHScroll(PED ped, PIX dx, BOOL fNotify)
{
PIX hScrollOld;
RECTL rclScroll;
RECTL rclUpdate;
SHORT sRet;
// normalise and update horizontal scrolling
hScrollOld = ped->hScroll;
ped->hScroll = max(0,min(ped->pixCurMax-ped->AveCharWidth,
ped->hScroll+dx));
dx = ped->hScroll - hScrollOld;
if (dx != 0) {
// find and scroll source rectangle
ped->fScrollDelta = TRUE;
rclScroll = ped->rclText;
if (dx > 0) { // increase scroll => shift display to left
rclScroll.xLeft -= dx;
} else {
rclScroll.xRight -= dx;
}
sRet = WinScrollWindow(ped->hwnd, (SHORT)(-dx), 0,
(PRECTL)&rclScroll, (PRECTL)&(ped->rclText),
NULL, (PRECTL)&rclUpdate, 0);
// paint exposed portion
DispRefresh(ped, FALSE, rclUpdate);
}
if (fNotify) {
NotifyScroll(ped, EN_HSCROLL, FALSE);
}
}
/*
* VOID DispHScrollTo(PED ped, PIX x, BOOL fNotify)
*
* changes the horizontal scrolling to x (or closest legal value). If
* fNotify is true, a notification is sent to the owner window.
*/
public VOID DispHScrollTo(PED ped, PIX x, BOOL fNotify)
{
if (x < 0)
x = 0;
if (x > ped->pixCurMax - ped->AveCharWidth)
x = ped->pixCurMax - ped->AveCharWidth;
DispHScroll(ped, x-ped->hScroll, fNotify);
}
/***********************************************************************
* Vertical scroll setting
***********************************************************************/
/*
* VOID SetVScroll(PED ped, PMR pmrLine, IPT ipt, BOOL fNotify)
*
* Given a line and its ipt, set the FVL to that line. Notify if needed,
* and prod the display for refresh.
*/
private VOID SetVScroll(PED ped, PMR pmrLine, IPT ipt, BOOL fNotify)
{
PMR pmrFVL;
RECTL rcl;
pmrFVL = ped->pmrFVL;
LockMarker(pmrFVL, FALSE);
DelMarker(ped, pmrFVL);
pmrFVL = InsEndMarker(ped, (PPR)pmrLine, MARKER_DISPLAY);
LockMarker(pmrFVL, TRUE);
ped->pmrFVL = pmrFVL;
ped->iptFVL = ipt;
ZeroRect(rcl);
DispRefresh(ped, FALSE, rcl);
if (fNotify) {
NotifyScroll(ped, EN_VSCROLL, TRUE);
}
}
/*
* VOID DispVScroll(PED ped, LINE dy, BOOL fNotify)
*
* changes the vertical scrolling by dy lines (if possible). If fNotify is
* true, a notification is sent to the owner window.
*/
public VOID DispVScroll(PED ped, LINE dy, BOOL fNotify)
{
LINE cl;
PMR pmrLine, pmrNext;
IPT cchLine, iptLine;
// get to the beginning of the FVL
pmrLine = (PMR)SkipBackText((PPR)(ped->pmrFVL),MARKER_LINE);
cchLine = TxtLengthToBOLPPR(ped,(PPR)(ped->pmrFVL),0);
iptLine = ped->iptFVL - cchLine;
// move dy lines forward or backward
cl = dy;
if (dy < 0) {
while (cl && !(pmrLine->fFlags & MARKER_BOT)) {
pmrLine = (PMR)SkipBackText(pmrLine->pr.pprTextPrev,MARKER_LINE);
iptLine -= TxtLengthToEOLPPR(ped, (PPR)pmrLine, 0, TRUE);
cl++;
}
} else if (dy > 0) {
while (cl) {
cchLine = TxtLengthToEOLPPR(ped, (PPR)pmrLine, 0, TRUE);
pmrNext = (PMR)SkipText(pmrLine->pr.pprTextNext,MARKER_LINE);
if (pmrNext->fFlags & MARKER_EOT)
break;
iptLine += cchLine;
pmrLine = pmrNext;
cl--;
}
}
// go with the line found
SetVScroll(ped, pmrLine, iptLine, fNotify);
}
/*
* VOID DispVScrollToIPT(PED ped, IPT ipt, BOOL fNotify)
*
* changes the vertical scrolling to line l (or closest legal value). If
* fNotify is true, a notification is sent to the owner window.
*/
public VOID DispVScrollToIPT(PED ped, IPT ipt, BOOL fNotify)
{
PPR ppr;
PMR pmrLine;
IPT iptTemp;
if (ipt == ped->iptMac)
ipt--;
iptTemp = ipt;
ppr = TxtPPROfIpt(ped, &iptTemp);
pmrLine = (PMR)SkipBackText(ppr, MARKER_LINE);
ipt -= TxtLengthToBOLPPR(ped, ppr, (OFFSET)iptTemp);
SetVScroll(ped, pmrLine, ipt, fNotify);
}
/***********************************************************************
* Cursor positioning, screen-relative
***********************************************************************/
/*
* VOID DispSetCursorXY(PED ped, PIX x, PIX y, BOOL fExtend, BOOL fWord)
*
* Sets the cursor position to the insertion point nearest the window
* point (x,y). If fExtend is true, only the cursor point is moved;
* otherwise, both cursor and anchor are moved. If fWord is true, the
* anchor point (if any) is set to the beginning of the surrounding token;
* the cursor point is set to the end of the surrounding token. fWord
* and fExtend can be combined.
*/
public VOID DispSetCursorXY(PED ped, PIX x, PIX y, BOOL fExtend, BOOL fWord)
{
PMR pmr;
PPR ppr;
OFFSET off;
IPT ipt, iptA, iptC;
LINE ln;
PIX xUsed;
RECTL rcl;
ln = LineFromY(ped,y);
if (ln < 0) {
DispVScroll(ped, -1, TRUE);
ln = 0;
} else if (ln > ped->lyWndSize) {
DispVScroll(ped, 1, TRUE);
ln = ped->lyWndSize;
}
if ((LONG)x < ped->rclText.xLeft) {
DispHScroll(ped, -(DISPLAY_SCROLLACWS*ped->AveCharWidth), TRUE);
x = (PIX)(ped->rclText.xLeft);
} else if ((LONG)x > ped->rclText.xRight) {
DispHScroll(ped, DISPLAY_SCROLLACWS*ped->AveCharWidth, TRUE);
x = (PIX)(ped->rclText.xRight);
}
ipt = ped->iptFVL;
pmr = (PMR)SkipBackText((PPR)ped->pmrFVL,MARKER_LINE);
off = 0;
while (ln && (!(pmr->fFlags & MARKER_EOT))) {
ipt += pmr->cchLine;
ln--;
pmr = (PMR)SkipText(pmr->pr.pprTextNext,MARKER_LINE);
}
if (pmr->fFlags & MARKER_EOT) {
iptA = iptC = TxtLength(ped);
} else {
ppr = pmr->pr.pprTextNext;
xUsed = max(0,x-(PIX)(ped->rclText.xLeft)+ped->hScroll);
if (xUsed > pmr->pixLine) {
ipt += pmr->cchLine;
if (fIsMarkerType(SkipText(ppr,MARKER_LINE),MARKER_LB)) {
ipt--;
}
} else {
ipt += TextFromPix(ped, &ppr, &off, &xUsed, ROUND_CLOSEST);
}
if (fWord) {
iptA = ipt - TxtCchToBegOfToken(ppr,off);
if (fExtend && (ipt < TxtQueryAnchor(ped))) {
iptC = iptA;
} else {
iptC = ipt + TxtCchToEndOfToken(ppr,off);
}
} else {
iptA = iptC = ipt;
}
}
TxtSetAnchorCursor(ped, fExtend?(IPT)(-1):iptA, iptC);
ZeroRect(rcl);
DispRefresh(ped, FALSE, rcl);
}