home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacFormat 1995 January
/
macformat-020.iso
/
Shareware City
/
Developers
/
CWASTE folder
/
WASTE3.c
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Text File
|
1994-07-20
|
33.6 KB
|
1,355 lines
|
[
TEXT/MPCC
]
// unit WASTE3;
// { WASTE PROJECT }
// { Unit Three: Selecting; Highlighting; Scrolling &c. }
// { Copyright © 1993-1994 Marco Piovanelli }
// { All Rights Reserved }
// conversion to C by Dan Crevier
#include "WASTEIntf.h"
#include <QDOffscreen.h>
// { values for _WEArrowOffset action parameter: }
// { plain arrow keys }
#define kGoLeft 0
#define kGoRight 1
#define kGoUp 2
#define kGoDown 3
// { modifiers }
#define kOption 4
#define kCommand 8
// { option + arrow combos }
#define kGoWordStart kGoLeft + kOption
#define kGoWordEnd kGoRight + kOption
#define kGoTextStart kGoUp + kOption
#define kGoTextEnd kGoDown + kOption
// { command + arrow combos }
#define kGoLineStart kGoLeft + kCommand
#define kGoLineEnd kGoRight + kCommand
#define kGoPageStart kGoUp + kCommand
#define kGoPageEnd kGoDown + kCommand
void ClearHiliteBit(void)
{
LMSetHiliteMode(LMGetHiliteMode() & 0x7f);
}
Boolean SLPixelToChar(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
void *callbackData);
Boolean SLPixelToChar(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
void *callbackData)
{
struct SLPixelToCharData *p = (struct SLPixelToCharData *) callbackData;
WEPtr pWE = p->pWE;
Fixed slop;
Fixed width;
// long adjustment;
// { if this is the first style run on the line, subtract pen indent from pixelWidth }
if (styleRunPosition <= smLeftStyleRun)
{
// adjustment = _WECalcPenIndent(pLine->lineSlop, pWE->alignment);
// adjustment = BSL(adjustment, 16);
p->pixelWidth = p->pixelWidth - BSL(_WECalcPenIndent(pLine->lineSlop, pWE->alignment), 16);
}
// { strip trailing spaces if this is the last segment on the line }
if (!(styleRunPosition & 1))
{
segmentLength = VisibleLength(pSegment, segmentLength);
}
// { calculate slop for this text segment (justified text only) }
if (pWE->alignment == weJustify)
{
slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
*(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling)),
pLine->lineJustAmount);
}
else
{
slop = 0;
}
// { call PixelToChar for this segment }
p->offset = segmentStart + NPixel2Char(pSegment, segmentLength, slop, p->pixelWidth,
(Boolean *)p->edge, &width, styleRunPosition, *(Point *)(&kOneToOneScaling),
*(Point *)(&kOneToOneScaling));
// { update pixelWidth for next iteration }
p->pixelWidth = width;
// { if pixelWidth has gone negative, we're finished; otherwise go to next run }
return (p->pixelWidth < 0);
}
pascal long WEGetOffset(LongPt *thePoint, char *edge, WEHandle hWE)
{
// { given a long point in local coordinates, }
// { find the text offset corresponding to the nearest glyph }
WEPtr pWE;
long lineIndex;
Fixed pixelWidth;
Boolean saveWELock;
long retval;
struct SLPixelToCharData callbackData;
LongPt tempPoint = *thePoint; // so we don't change original point
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { offset thePoint so that it is relative to the top left corner of the destination rectangle }
tempPoint.v = tempPoint.v - pWE->destRect.top;
tempPoint.h = tempPoint.h - pWE->destRect.left;
// { if the point is above the destination rect, return zero }
if (tempPoint.v < 0)
{
retval = 0;
*edge = kTrailingEdge;
}
else
{
// { if the point is below the last line, return last char offset }
if (tempPoint.v >= WEGetHeight(0, LONG_MAX, hWE))
{
retval = pWE->textLength;
*edge = kLeadingEdge;
}
else
{
// { find the line index corresponding to the vertical pixel offset }
lineIndex = _WEPixelToLine(tempPoint.v, hWE);
// { express the horizontal pixel offset as a Fixed value }
pixelWidth = BSL(tempPoint.h, 16);
// { walk through the segments on this line calling PixelToChar }
callbackData.pWE = pWE;
callbackData.pixelWidth = pixelWidth;
callbackData.edge = edge;
callbackData.offset = 0;
_WESegmentLoop(lineIndex, lineIndex, SLPixelToChar, (void *)&callbackData, hWE);
retval = callbackData.offset;
}
}
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
return retval;
}
Boolean SLCharToPixel(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
void *callbackData);
Boolean SLCharToPixel(LinePtr pLine, WERunAttributesPtr pAttrs, Ptr pSegment,
long segmentStart, long segmentLength, JustStyleCode styleRunPosition,
void *callbackData)
{
struct SLCharToPixelData *p = (struct SLCharToPixelData *) callbackData;
WEPtr pWE = p->pWE;
Fixed slop;
Boolean leadingEdge;
// { if this is the first style run on the line, add pen indent to thePoint.h }
if (styleRunPosition <= smLeftStyleRun)
{
p->thePoint->h = p->thePoint->h + _WECalcPenIndent(pLine->lineSlop, pWE->alignment);
}
// { calculate slop for this text segment (justified text only) }
if (pWE->alignment == weJustify)
{
slop = FixMul(PortionLine(pSegment, segmentLength, styleRunPosition,
*(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling)),
pLine->lineJustAmount);
}
else
{
slop = 0;
}
// { call CharToPixel to get width of segment up to specified offset }
leadingEdge = smHilite;
p->thePoint->h = p->thePoint->h + NChar2Pixel(pSegment, segmentLength, slop,
p->offset - segmentStart, smHilite, styleRunPosition,
*(Point *)(&kOneToOneScaling), *(Point *)(&kOneToOneScaling));
// { drop out of loop when we reach offset }
return (p->offset < segmentStart + segmentLength);
}
pascal void WEGetPoint(long offset, LongPt *thePoint, short *lineHeight, WEHandle hWE)
{
// { given a byte offset into the text, find the corresponding glyph position }
// { this routine is useful for highlighting the text and for positioning the caret }
WEPtr pWE;
LinePeek pLine;
long lineIndex;
Boolean saveWELock;
struct SLCharToPixelData callbackData;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle) hWE, true);
pWE = *hWE;
// { the base point is the top left corner of the destination rectangle }
*thePoint = *(LongPt *)&pWE->destRect.top;
// { first of all find the line on which the glyph lies }
lineIndex = _WEOffsetToLine(offset, hWE);
// { calculate the vertical coordinate and the line height }
pLine = (LinePeek)&((*pWE->hLines)[lineIndex]);
thePoint->v = thePoint->v + pLine->first.lineOrigin;
*lineHeight = pLine->second.lineOrigin - pLine->first.lineOrigin;
if ((offset == pWE->textLength) && (WEGetChar(offset - 1, hWE) == '\r'))
{
// { SPECIAL CASE: if offset is past the last character and }
// { the last character is a carriage return, return a point below the last line }
thePoint->v = thePoint->v + *lineHeight;
thePoint->h = thePoint->h + _WECalcPenIndent(pWE->destRect.right - pWE->destRect.left, pWE->alignment);
}
else
{
callbackData.pWE = pWE;
callbackData.offset = offset;
callbackData.thePoint = thePoint;
// { to get the horizontal coordinate, walk through the style runs on this line }
_WESegmentLoop(lineIndex, lineIndex, SLCharToPixel, (void *)&callbackData, hWE);
}
// { pin the horizontal coordinate to the destination rectangle }
thePoint->h = _WEPinInRange(thePoint->h, pWE->destRect.left, pWE->destRect.right);
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
pascal void WEFindLine(long offset, char edge, long *lineStart, long *lineEnd, WEHandle hWE)
{
WEPtr pWE;
LineArrayPtr pLine;
pWE = *hWE;
pLine = &(*pWE->hLines)[_WEOffsetToLine(offset, hWE)];
*lineStart = pLine[0].lineStart;
*lineEnd = pLine[1].lineStart;
}
pascal short WEFindScriptRun(long offset, char edge, long *scriptRunStart, long *scriptRunEnd,
WEHandle hWE)
{
long index, saveIndex, saveRunEnd;
WERunInfo runInfo;
ScriptCode script1, script2;
short retval;
if (BTST((*hWE)->flags, weFNonRoman))
{
// { if more than one script is installed, limit the search of script run boundaries }
// { to a single line, for speed's sake }
WEFindLine(offset, edge, scriptRunStart, scriptRunEnd, hWE);
// { find the style run the specified offset is in }
index = _WEOffsetToRun(offset, hWE);
_WEGetIndStyle(index, &runInfo, hWE);
// { return the style run font as function result }
retval = runInfo.runAttrs.runStyle.tsFont;
// { find the script code associated with this style run }
script1 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
// { save index and runInfo.runEnd for the second while loop }
saveIndex = index;
saveRunEnd = runInfo.runEnd;
// { walk backwards across style runs preceding offset, looking for a script run boundary }
while (runInfo.runStart > *scriptRunStart)
{
index = index - 1;
_WEGetIndStyle(index, &runInfo, hWE);
script2 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
if (script1 != script2)
{
*scriptRunStart = runInfo.runEnd;
break;
}
}
// { restore index and runInfo.runEnd }
index = saveIndex;
runInfo.runEnd = saveRunEnd;
// { walk forward across style runs following offset, looking for a script run boundary }
while (runInfo.runEnd < *scriptRunEnd)
{
index = index + 1;
_WEGetIndStyle(index, &runInfo, hWE);
script2 = Font2Script(runInfo.runAttrs.runStyle.tsFont);
if (script1 != script2)
{
*scriptRunEnd = runInfo.runStart;
break;
}
}
}
else
{
// { only the Roman script is enabled: the whole text constitutes one script run }
retval = systemFont;
*scriptRunStart = 0;
*scriptRunEnd = (*hWE)->textLength;
}
return retval;
}
pascal void WEFindWord(long offset, char edge, long *wordStart, long *wordEnd, WEHandle hWE)
{
WEPtr pWE;
GrafPtr port, savePort;
Handle hText;
long runStart, runEnd;
OffsetTable wordBreaks;
short saveFont;
Boolean saveTextLock;
pWE = *hWE;
port = pWE->port;
hText = pWE->hText;
// { set up the port }
GetPort(&savePort);
SetPort(port);
// { find the script run the specified offset is in (words cannot straddle script boundaries) }
// { and set the port font to the specified script }
saveFont = port->txFont;
TextFont(WEFindScriptRun(offset, edge, &runStart, &runEnd, hWE));
// { make sure we pass FindWord short values }
runStart = _WEPinInRange(runStart, offset - (SHRT_MAX / 2), offset);
runEnd = _WEPinInRange(runEnd, offset, offset + (SHRT_MAX / 2));
// { lock the text }
saveTextLock = _WESetHandleLock(hText, true);
// { call FindWord using the whole script run as a context }
FindWord(*hText + runStart, runEnd - runStart, offset - runStart, (Boolean)edge,
nil, wordBreaks);
// { unlock the text }
_WESetHandleLock(hText, saveTextLock);
// { restore font and port }
TextFont(saveFont);
SetPort(savePort);
// { calculate wordStart and wordEnd relative to the beginning of the text }
*wordStart = runStart + wordBreaks[0].offFirst;
*wordEnd = runStart + wordBreaks[0].offSecond;
}
void _WEDrawCaret(WEHandle hWE)
{
WEPtr pWE;
LongPt thePoint;
Rect caretRect;
short caretHeight;
GrafPtr savePort;
RgnHandle saveClip;
// { the WE record must be already locked }
pWE = *hWE;
// { do nothing if we're not active }
if (!BTST(pWE->flags, weFActive))
{
return;
}
// { find the caret position using WEGetPoint }
WEGetPoint(pWE->selStart, &thePoint, &caretHeight, hWE);
WELongPointToPoint(&thePoint, (Point *)&caretRect.top);
if (caretRect.left > pWE->destRect.left)
{
caretRect.left = caretRect.left - 1;
}
// { calculate caret rectangle }
caretRect.bottom = caretRect.top + caretHeight;
caretRect.right = caretRect.left + 1;
// { set up the port }
GetPort(&savePort);
SetPort(pWE->port);
// { clip to the view region }
saveClip = NewRgn();
GetClip(saveClip);
SetClip(pWE->viewRgn);
// { draw the caret }
InvertRect(&caretRect);
// { invert caretVisible }
pWE->flags = pWE->flags ^ (BSL(1, weFCaretVisible));
// { update caretTime }
pWE->caretTime = TickCount();
// { restore the clip region }
SetClip(saveClip);
DisposeRgn(saveClip);
// { restore the port }
SetPort(savePort);
}
pascal RgnHandle WEGetHiliteRgn(long rangeStart, long rangeEnd, WEHandle hWE)
{
// { returns the hilite region corresponding to the specified range }
// { the caller is responsible for disposing of the returned region }
// { when it's finished with it }
WEPtr pWE;
RgnHandle hiliteRgn;
LongRect selRect;
LongPt firstPoint, lastPoint;
short firstLineHeight, lastLineHeight;
Rect r;
GrafPtr savePort;
Boolean saveWELock;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { set up the port }
GetPort(&savePort);
SetPort(pWE->port);
// { make sure rangeStart comes before rangeEnd }
_WEReorder(&rangeStart, &rangeEnd);
// { calculate pixel location corresponding to rangeStart }
WEGetPoint(rangeStart, &firstPoint, &firstLineHeight, hWE);
// { calculate pixel location corresponding to rangeEnd }
WEGetPoint(rangeEnd, &lastPoint, &lastLineHeight, hWE);
// { open a region: rects to be hilited will be accumulated in this }
OpenRgn();
if (firstPoint.v == lastPoint.v)
{
// { selection range encompasses only one line }
WESetLongRect(&selRect, firstPoint.h, firstPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
WELongRectToRect(&selRect, &r);
FrameRect(&r);
}
else
{
// { selection range encompasses more than one line }
// { hilite the first line }
WESetLongRect(&selRect, firstPoint.h, firstPoint.v, pWE->destRect.right, firstPoint.v + firstLineHeight);
WELongRectToRect(&selRect, &r);
FrameRect(&r);
// { any lines between the first and the last one? }
if (firstPoint.v + firstLineHeight < lastPoint.v)
{
// { hilite all the lines in-between }
WESetLongRect(&selRect, pWE->destRect.left, firstPoint.v + firstLineHeight, pWE->destRect.right, lastPoint.v);
WELongRectToRect(&selRect, &r);
FrameRect(&r);
}
// { hilite the last line }
WESetLongRect(&selRect, pWE->destRect.left, lastPoint.v, lastPoint.h, lastPoint.v + lastLineHeight);
WELongRectToRect(&selRect, &r);
FrameRect(&r);
}
// { copy the accumulated region into a new region }
hiliteRgn = NewRgn();
CloseRgn(hiliteRgn);
// { restrict this region to the view region }
SectRgn(hiliteRgn, pWE->viewRgn, hiliteRgn);
// { restore the port }
SetPort(savePort);
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
// { return the hilite region }
return hiliteRgn;
}
void _WEHiliteRange(long rangeStart, long rangeEnd, WEHandle hWE)
{
WEPtr pWE;
RgnHandle saveClip, auxRgn, hiliteRgn;
PenState savePen;
GrafPtr savePort;
// { the WE record must be already locked }
pWE = *hWE;
// { do nothing if the specified range is empty }
if (rangeStart == rangeEnd)
{
return;
}
// { set up the port }
GetPort(&savePort);
SetPort(pWE->port);
// { create auxiliary regions }
saveClip = NewRgn();
auxRgn = NewRgn();
// { restrict the clip region to the view rectangle }
GetClip(saveClip);
SectRgn(saveClip, pWE->viewRgn, auxRgn);
SetClip(auxRgn);
// { get the hilite region corresponding to the specified range }
hiliteRgn = WEGetHiliteRgn(rangeStart, rangeEnd, hWE);
// { hilite the region or frame it, depending on the setting of the active flag }
if (BTST(pWE->flags, weFActive))
{
ClearHiliteBit();
InvertRgn(hiliteRgn);
}
else if (BTST(pWE->flags, weFOutlineHilite))
{
GetPenState(&savePen);
PenNormal();
PenMode(patXor);
ClearHiliteBit();
FrameRgn(hiliteRgn);
SetPenState(&savePen);
}
// { restore the clip region }
SetClip(saveClip);
// { dispose of all regions }
DisposeRgn(saveClip);
DisposeRgn(auxRgn);
DisposeRgn(hiliteRgn);
// { restore the port }
SetPort(savePort);
}
pascal void WESetSelection(long selStart, long selEnd, WEHandle hWE)
{
WEPtr pWE;
long oldSelStart, oldSelEnd;
Boolean saveWELock;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { invalid the null style }
BCLR(pWE->flags, weFUseNullStyle);
// { hide the caret if it's showing }
if (BTST(pWE->flags, weFCaretVisible))
{
_WEDrawCaret(hWE);
}
// { range-check parameters }
selStart = _WEPinInRange(selStart, 0, pWE->textLength);
selEnd = _WEPinInRange(selEnd, 0, pWE->textLength);
// { set the weFAnchorIsEnd bit if selStart > selEnd, reorder the endpoints }
if (selStart > selEnd)
{
BSET(pWE->flags, weFAnchorIsEnd);
}
else
{
BCLR(pWE->flags, weFAnchorIsEnd);
}
_WEReorder(&selStart, &selEnd);
// { get old selection range }
oldSelStart = pWE->selStart;
oldSelEnd = pWE->selEnd;
// { set new selection range }
pWE->selStart = selStart;
pWE->selEnd = selEnd;
// { if we're active, invert the exclusive-OR between the old range and the new range. }
// { if we're inactive, this optimization can't be used because of outline highlighting. }
if (BTST(pWE->flags, weFActive))
{
_WEReorder(&oldSelStart, &selStart);
_WEReorder(&oldSelEnd, &selEnd);
_WEReorder(&oldSelEnd, &selStart);
}
_WEHiliteRange(oldSelStart, oldSelEnd, hWE);
_WEHiliteRange(selStart, selEnd, hWE);
if (!BTST(pWE->flags, weFMouseTracking))
{
// { redraw the caret immediately, if the selection range is empty }
if (pWE->selStart == pWE->selEnd)
{
_WEDrawCaret(hWE);
}
// { clear clickCount, unless we're tracking the mouse }
pWE->clickCount = 0;
// { scroll the selection into view, unless we're tracking the mouse }
WESelView(hWE);
}
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
pascal void WEClick(Point mouseLoc, short modifiers, long clickTime, WEHandle hWE)
{
WEPtr pWE;
LongPt thePoint;
long offset, anchor;
long rangeStart, rangeEnd;
char edge;
Boolean isMultipleClick;
Boolean saveWELock;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { hide the caret if it's showing }
if (BTST(pWE->flags, weFCaretVisible))
{
_WEDrawCaret(hWE);
}
// { find click offset }
WEPointToLongPoint(mouseLoc, &thePoint);
offset = WEGetOffset(&thePoint, &edge, hWE);
// { determine whether this click is part of a sequence }
isMultipleClick = ((clickTime < pWE->clickTime + GetDblTime()) &&
(offset == pWE->clickLoc));
// { remember click time, click offset and edge value }
pWE->clickTime = clickTime;
pWE->clickLoc = offset;
pWE->clickEdge = edge;
if ((modifiers & shiftKey) == 0)
{
// { is this click part of a sequence or is it an isolate click? }
if (isMultipleClick)
{
pWE->clickCount = pWE->clickCount + 1;
// { a double (triple) click creates an anchor-word (anchor-line) }
if (pWE->clickCount > 1)
{
WEFindLine(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
}
else
{
WEFindWord(offset, edge, &pWE->anchorStart, &pWE->anchorEnd, hWE);
}
offset = pWE->anchorStart;
}
else
{
pWE->clickCount = 0;
anchor = offset;
}
}
else
{
// { if the shift key was down, use the old anchor offset found with the previous click }
if (BTST(pWE->flags, weFAnchorIsEnd))
{
anchor = pWE->selEnd;
}
else
{
anchor = pWE->selStart;
}
}
// { set the weFMouseTracking bit while we track the mouse }
BSET(pWE->flags, weFMouseTracking);
// { MOUSE TRACKING LOOP }
do
{
// { get text offset corresponding to mouse position }
WEPointToLongPoint(mouseLoc, &thePoint);
offset = WEGetOffset(&thePoint, &edge, hWE);
// { if we're selecting words or lines, pin offset to a word or line boundary }
if (pWE->clickCount > 0)
{
if (pWE->clickCount > 1)
{
WEFindLine(offset, edge, &rangeStart, &rangeEnd, hWE);
}
else
{
WEFindWord(offset, edge, &rangeStart, &rangeEnd, hWE);
}
// { choose the word/line boundary and the anchor that are farthest away from each other }
if (offset > pWE->anchorStart)
{
anchor = pWE->anchorStart;
offset = rangeEnd;
}
else
{
offset = rangeStart;
anchor = pWE->anchorEnd;
}
}
// { set the selection range from anchor point to current offset }
WESetSelection(anchor, offset, hWE);
// { call the click loop callback, if any }
if (pWE->clickLoop != nil)
{
if (((WEClickLoopProcPtr)pWE->clickLoop)(hWE) == false)
{
break;
}
}
// { update mouse position }
GetMouse(&mouseLoc);
} while (WaitMouseUp());
// { clear the weFMouseTracking bit }
BCLR(pWE->flags, weFMouseTracking);
// { redraw the caret immediately if the selection range is empty }
if (anchor == offset)
{
_WEDrawCaret(hWE);
}
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
pascal void WESetAlignment(char alignment, WEHandle hWE)
{
if ((alignment >= weFlushLeft) && (alignment <= weJustify))
{
if (alignment != (*hWE)->alignment)
{
(*hWE)->alignment = alignment;
WEUpdate(nil, hWE);
}
}
}
long _WEArrowOffset(short action, long offset, WEHandle hWE)
{
// { given an action code (corresponding to a modifiers + arrow key combo) }
// { and an offset into the text, find the offset of the new caret position }
LongPt thePoint;
long textLength, rangeStart, rangeEnd;
short lineHeight;
char edge;
textLength = (*hWE)->textLength;
switch (action)
{
case kGoLeft:
if (offset > 0)
{
offset = offset - 1;
if (WECharByte(offset, hWE) != smSingleByte)
{
offset = offset - 1;
}
}
break;
case kGoRight:
if (offset < textLength)
{
offset = offset + 1;
if (WECharByte(offset, hWE) != smSingleByte)
{
offset = offset + 1;
}
}
break;
case kGoUp:
WEGetPoint(offset, &thePoint, &lineHeight, hWE);
thePoint.v = thePoint.v - 1;
offset = WEGetOffset(&thePoint, &edge, hWE);
break;
case kGoDown:
WEGetPoint(offset, &thePoint, &lineHeight, hWE);
thePoint.v = thePoint.v + lineHeight;
offset = WEGetOffset(&thePoint, &edge, hWE);
break;
case kGoWordStart:
WEFindWord(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
offset = rangeStart;
break;
case kGoWordEnd:
WEFindWord(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
offset = rangeEnd;
break;
case kGoTextStart:
offset = 0;
break;
case kGoTextEnd:
offset = textLength;
break;
case kGoLineStart:
WEFindLine(offset, kLeadingEdge, &rangeStart, &rangeEnd, hWE);
offset = rangeStart;
break;
case kGoLineEnd:
WEFindLine(offset, kTrailingEdge, &rangeStart, &rangeEnd, hWE);
offset = rangeEnd;
if (offset < textLength)
{
offset = offset - 1;
if (WECharByte(offset, hWE) != smSingleByte)
{
offset = offset - 1;
}
}
break;
default:
break;
}
return offset;
}
void _WEDoArrowKey (short arrow, short modifiers, WEHandle hWE)
{
// { this routine is called by WEKey to handle arrow keys }
// { the WE record is guaranteed to be already locked }
WEPtr pWE;
short action;
long selStart, selEnd;
long caretLoc, anchor;
pWE = *hWE;
// { calculate the "action" parameter for _WEArrowOffset from arrow and modifiers }
action = arrow - kArrowLeft; // { possible range: 0..3 }
if (modifiers & optionKey)
{
action = action + kOption;
}
if (modifiers & cmdKey)
{
action = action + kCommand;
}
// { get selection range }
selStart = pWE->selStart;
selEnd = pWE->selEnd;
if ((modifiers & shiftKey) == 0)
{
// { if selection range isn't empty, collapse it to one of the endpoints }
if (selStart < selEnd)
{
if ((arrow == kArrowLeft) || (arrow == kArrowUp))
{
caretLoc = selStart;
}
else
{
caretLoc = selEnd;
}
}
else
{
// { otherwise move the insertion point }
caretLoc = _WEArrowOffset(action, selStart, hWE);
}
// { set anchor to caretLoc, so new selection will be empty }
anchor = caretLoc;
}
else
{
// { shift key was held down: extend the selection rather than replacing it }
// { find out which selection boundary is the anchor and which is the free endpoint }
if (BTST(pWE->flags, weFAnchorIsEnd))
{
anchor = selEnd;
caretLoc = selStart;
}
else
{
anchor = selStart;
caretLoc = selEnd;
}
// { move the free endpoint }
caretLoc = _WEArrowOffset(action, caretLoc, hWE);
}
// { select the new selection }
WESetSelection(anchor, caretLoc, hWE);
}
pascal Boolean WEAdjustCursor(Point mouseLoc, RgnHandle mouseRgn, WEHandle hWE)
{
// { Call WEAdjustCursor to set the cursor shape when the mouse is in the view rectangle. }
// { MouseRgn should be either a valid region handle or NIL. }
// { If mouseRgn is supplied (i.e., if it's not NIL), it is intersected with a region }
// { in global coordinates within which the cursor is to retain its shape. }
// { WEAdjustCursor returns TRUE if the cursor has been set. }
// { Your application should set the cursor only if WEAdjustCursor returns FALSE. }
WEPtr pWE;
RgnHandle auxRgn;
Point portDelta;
GrafPtr savePort;
Boolean saveWELock;
Boolean adjustCursor;
Point zeroPoint = {0, 0};
adjustCursor = false;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { set up the port }
GetPort(&savePort);
SetPort(pWE->port);
// { calculate delta between the local coordinate system and the global one }
portDelta = zeroPoint;
LocalToGlobal(&portDelta);
// { calculate the visible portion of the view rectangle, in global coordinates }
auxRgn = NewRgn();
CopyRgn(pWE->viewRgn, auxRgn);
SectRgn(auxRgn, pWE->port->visRgn, auxRgn);
OffsetRgn(auxRgn, portDelta.h, portDelta.v);
if (PtInRgn(mouseLoc, auxRgn))
{
// { mouse is within view rectangle: it's up to us to set the cursor }
adjustCursor = true;
// { set the cursor to an I-beam }
SetCursor(*GetCursor(iBeamCursor));
// { set mouseRgn, if provided }
if (mouseRgn != nil)
{
SectRgn(mouseRgn, auxRgn, mouseRgn);
}
}
else
{
// { mouse is outside view rectangle: don't set the cursor; subtract viewRgn from mouseRgn }
if (mouseRgn != nil)
{
DiffRgn(mouseRgn, auxRgn, mouseRgn);
}
}
// { dispose of the temporary region }
DisposeRgn(auxRgn);
// { restore the port }
SetPort(savePort);
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
return adjustCursor;
}
pascal void WEIdle(long *maxSleep, WEHandle hWE)
{
WEPtr pWE;
long caretInterval, sleepTime;
Boolean saveWELock;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { the caret blinks only if we're active and the selection point is empty }
if (BTST(pWE->flags, weFActive) && (pWE->selStart == pWE->selEnd))
{
// { the low-memory global variable CaretTime contains the preferred interval }
// { between successive inversions of the caret }
caretInterval = GetCaretTime();
// { calculate how many ticks we can sleep before we need to invert the caret }
// { the caretTime field of the WE record contains the time of the last inversion }
sleepTime = caretInterval - (TickCount() - pWE->caretTime);
// { if sleepTime has gone negative, invert the caret }
if (sleepTime <= 0)
{
_WEDrawCaret(hWE);
sleepTime = caretInterval;
}
}
else
{
// { if we don't need to blink the caret, we can sleep forever }
sleepTime = LONG_MAX;
}
// { return sleepTime to the caller if maxSleep isn't NIL }
if (maxSleep != nil)
{
*maxSleep = sleepTime;
}
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
pascal void WEUpdate(RgnHandle updateRgn, WEHandle hWE)
{
WEPtr pWE;
long firstLine, lastLine;
Rect auxRect;
RgnHandle saveClip, auxRgn;
GrafPtr savePort;
Boolean saveWELock;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { set up the port }
GetPort(&savePort);
SetPort(pWE->port);
// { save the clip region }
saveClip = NewRgn();
GetClip(saveClip);
// { clip to the insersection between updateRgn and the view rectangle }
// { (updateRgn may be NIL; in this case, just clip to the view rectangle) }
auxRgn = NewRgn();
if (updateRgn != nil)
{
SectRgn(updateRgn, pWE->viewRgn, auxRgn);
}
else
{
CopyRgn(pWE->viewRgn, auxRgn);
}
SetClip(auxRgn);
if (EmptyRgn(auxRgn) == false)
{
// { set auxRect to the bounding box of the update region (clipped to the view rectangle) }
auxRect = (*auxRgn)->rgnBBox;
// { find out which lines need to be redrawn }
firstLine = _WEPixelToLine(auxRect.top - pWE->destRect.top, hWE);
lastLine = _WEPixelToLine((auxRect.bottom - 1) - pWE->destRect.top, hWE);
// { draw them (if updateRgn is NIL, erase each line rectangle before redrawing) }
_WEDrawLines(firstLine, lastLine, (updateRgn == nil), hWE);
// { hilite the selection range or draw the caret (only if active) }
if (pWE->selStart < pWE->selEnd)
_WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
else if (BTST(pWE->flags, weFCaretVisible))
{
_WEDrawCaret(hWE);
BSET(pWE->flags, weFCaretVisible);
}
}
DisposeRgn(auxRgn);
// { restore the clip region }
SetClip(saveClip);
DisposeRgn(saveClip);
// { restore the port }
SetPort(savePort);
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
pascal void WEDeactivate(WEHandle hWE)
{
WEPtr pWE;
Boolean saveWELock;
if (!WEIsActive(hWE)) return;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { hide the selection range or the caret }
_WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
if (BTST(pWE->flags, weFCaretVisible))
{
_WEDrawCaret(hWE);
}
// { clear the active flag }
BCLR(pWE->flags, weFActive);
// { frame the selection }
_WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
// { dispose of the offscreen graphics world, if any }
if (pWE->offscreenPort != nil)
{
DisposeGWorld((GWorldPtr)(pWE->offscreenPort));
pWE->offscreenPort = nil;
}
// { notify Text Services }
if (pWE->tsmReference != nil)
{
DeactivateTSMDocument(pWE->tsmReference);
}
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
pascal void WEActivate(WEHandle hWE)
{
WEPtr pWE;
Boolean saveWELock;
if (WEIsActive(hWE)) return;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { remove the selection frame }
_WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
// { set the active flag }
BSET(pWE->flags, weFActive);
// { show the selection range }
_WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
// { notify Text Services }
if (pWE->tsmReference != nil)
{
ActivateTSMDocument(pWE->tsmReference);
}
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
pascal Boolean WEIsActive(WEHandle hWE)
{
// { return TRUE iff the specified WE instance is currently active }
return BTST((*hWE)->flags, weFActive);
}
pascal void WEScroll(long hOffset, long vOffset, WEHandle hWE)
{
WEPtr pWE;
Rect viewRect;
RgnHandle updateRgn;
GrafPtr savePort;
Boolean saveWELock;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { hide the caret if it's showing }
if (BTST(pWE->flags, weFCaretVisible))
{
_WEDrawCaret(hWE);
}
// { offset the destination rectangle by the specified amount }
WEOffsetLongRect(&pWE->destRect, hOffset, vOffset);
// { set up the port }
GetPort(&savePort);
SetPort(pWE->port);
viewRect = (*pWE->viewRgn)->rgnBBox;
updateRgn = NewRgn();
// { scroll the view rectangle }
ScrollRect(&viewRect, hOffset, vOffset, updateRgn);
// { redraw the exposed region }
WEUpdate(updateRgn, hWE);
DisposeRgn(updateRgn);
// { restore the port }
SetPort(savePort);
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}
Boolean _WEScrollIntoView (long offset, WEHandle hWE)
{
WEPtr pWE;
LongPt thePoint;
short lineHeight;
long hScroll, vScroll, temp;
Boolean retval;
pWE = *hWE;
// { do nothing if automatic scrolling is disabled }
if (!BTST(pWE->flags, weFAutoScroll))
{
return false;
}
// { find the selection point }
WEGetPoint(offset, &thePoint, &lineHeight, hWE);
// { assume no scrolling is needed }
retval = false;
vScroll = 0;
hScroll = 0;
// { determine if we need to scroll vertically }
if ((thePoint.v < pWE->viewRect.top) ||
(thePoint.v + lineHeight >= pWE->viewRect.bottom))
{
// { calculate the amount of vertical scrolling needed to center the selection into view }
vScroll = BSR(pWE->viewRect.top + pWE->viewRect.bottom, 1) - thePoint.v;
// { we'd like to superimpose the bottom margins of the dest/view rects, if possible }
temp = pWE->viewRect.bottom - pWE->destRect.bottom;
if (temp > vScroll)
{
vScroll = temp;
}
// { but we also have to make sure the dest top isn't scrolled below the view top }
temp = pWE->viewRect.top - pWE->destRect.top;
if (temp < vScroll)
{
vScroll = temp;
}
}
// { determine if we need to scroll horizontally }
if ((thePoint.h - 1 < pWE->viewRect.left) || (thePoint.h >= pWE->viewRect.right))
{
// { calculate the amount of horizontal scrolling needed to center the selection into view }
hScroll = BSR(pWE->viewRect.left + pWE->viewRect.right, 1) - thePoint.h;
// { we'd like to superimpose the right margins of the dest/view rects, if possible }
temp = pWE->viewRect.right - pWE->destRect.right;
if (temp > hScroll)
{
hScroll = temp;
}
// { but we also have to make sure the dest left isn't scrolled to the right of the view left }
temp = pWE->viewRect.left - pWE->destRect.left;
if (temp < hScroll)
{
hScroll = temp;
}
}
// { scroll the text if necessary }
if ((vScroll != 0) || (hScroll != 0))
{
retval = true;
WEScroll(hScroll, vScroll, hWE);
}
// { call the scroll callback, if any }
if (pWE->scrollProc != nil)
{
((WEScrollProcPtr)pWE->scrollProc)(hWE);
}
return retval;
}
pascal void WESelView(WEHandle hWE)
{
WEPtr pWE;
long offset;
Boolean saveWELock;
// { lock the WE record }
saveWELock = _WESetHandleLock((Handle)hWE, true);
pWE = *hWE;
// { scroll the free endpoint of the selection into view }
if (BTST(pWE->flags, weFAnchorIsEnd))
{
offset = pWE->selStart;
}
else
{
offset = pWE->selEnd;
}
_WEScrollIntoView(offset, hWE);
// { unlock the WE record }
_WESetHandleLock((Handle)hWE, saveWELock);
}