DIRECTORY
Ascii USING [CR, LF],
Char USING [XCHAR],
CharOps USING [AlphaNumeric, Blank, XBlank],
EditSpan USING [CompareNodeOrder, InsertTextNode, NodeOrder, Place],
NodeStyle USING [Ref],
NodeStyleOps USING [Alloc, ApplyAll, Free, OfStyle],
Rope USING [Size],
RopeReader USING [Backwards, Get, GetIndex, GetRopeReader, FreeRopeReader, Ref, SetCharForEndOfRope, SetPosition],
Scaled USING [FromInt],
TextEdit USING [FetchChar, FetchLooks, Size],
TextEditBogus USING [GetRope],
TextLooks USING [Looks, noLooks],
TextNode USING [EndPos, FirstChild, LastWithin, Location, Parent, Ref, RefTextNode, Root],
TEditDocument USING [BeforeAfter, LineTable, PunctuationPosition, SelectionGrain, SelectionPoint, Selection, SelectionId, TEditDocumentData],
TEditFormat USING [Allocate, CharPosition, FormatLine, LineInfo, Resolve],
TEditInput USING [currentEvent],
TEditProfile USING [selectionCaret, wordPunctuation, ySelectFudge],
TEditSelection USING [Alloc, Free, Copy, Deselect, FixupSelection, fSel, LevelChange, LockSel, pSel, sSel, MakeSelection, SelectEverything, UnlockSel],
TEditSelectionPrivate USING [],
TEditSelectionPrivateExtras USING [],
TEditTouchup USING [LockAfterRefresh, UnlockAfterRefresh],
ViewerClasses USING [Viewer],
ViewerGroupLocks USING [CallRootUnderWriteLock],
ViewerOps USING [FetchProp];
---- Mouse hit primitives ------
DoSelect:
PUBLIC
PROC [
proc:
PROC [tSel, refSel: TEditDocument.Selection, rightOfLine:
BOOL],
viewer: ViewerClasses.Viewer,
tdd: TEditDocument.TEditDocumentData,
x, y:
INTEGER, sel: TEditDocument.SelectionId] = {
action:
PROC = {
refSel: TEditDocument.Selection =
SELECT sel
FROM
primary => TEditSelection.pSel,
secondary => TEditSelection.sSel,
feedback => TEditSelection.fSel,
ENDCASE => ERROR;
tSel: TEditDocument.Selection;
rightOfLine: BOOL ¬ FALSE;
tSel ¬ TEditSelection.Alloc[];
rightOfLine ¬ ResolveToChar[tSel, viewer, tdd, x, y];
proc[tSel, refSel, rightOfLine];
TEditSelection.Free[tSel]
};
WithProperLocks[action, sel, viewer, tdd];
};
WithProperLocks:
PROC [action:
PROC, sel: TEditDocument.SelectionId, viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData] =
INLINE
--gfi saver-- {
inner:
PROC = {
WITH viewer.data
SELECT
FROM
vtdd: TEditDocument.TEditDocumentData =>
IF vtdd = tdd
THEN {
IF TEditTouchup.LockAfterRefresh[tdd, "DoSelect"]
THEN {
action[
! UNWIND => TEditTouchup.UnlockAfterRefresh[tdd]];
TEditTouchup.UnlockAfterRefresh[tdd];
};
};
ENDCASE;
};
TEditSelection.LockSel[sel, "DoSelect"];
ViewerGroupLocks.CallRootUnderWriteLock[inner, viewer
! UNWIND => TEditSelection.UnlockSel[sel]];
TEditSelection.UnlockSel[sel] ;
};
SelectPoint:
PUBLIC
PROC [
viewer: ViewerClasses.Viewer,
tdd: TEditDocument.TEditDocumentData,
x,y:
INTEGER, sel: TEditDocument.SelectionId, pDel:
BOOLEAN] = {
DoSelectPoint:
PROC [tSel, refSel: TEditDocument.Selection, rightOfLine:
BOOL] = {
newInsertion: TEditDocument.BeforeAfter;
IF refSel.viewer#viewer OR refSel.granularity#point THEN TEditSelection.Deselect[selection: sel];
tSel.end ¬ tSel.start;
newInsertion ¬ SetInsertion[tSel, x, tSel.start.line, rightOfLine, tdd];
IF refSel.viewer#viewer OR refSel.start#tSel.start OR refSel.granularity#point
OR refSel.insertion#newInsertion
OR refSel.pendingDelete#pDel
THEN {
tSel.granularity ¬ point;
tSel.viewer ¬ viewer;
tSel.data ¬ tdd;
tSel.insertion ¬ newInsertion;
tSel.pendingDelete ¬ pDel;
SetSelLooks[tSel];
TEditSelection.MakeSelection[tSel, sel, TRUE, TRUE, FALSE];
};
};
DoSelect[DoSelectPoint, viewer, tdd, x, y, sel];
};
Tr: PROC [code: ATOM, bool: BOOL] RETURNS [BOOL] ~ {
Trr[code, IF bool THEN $true ELSE $false];
RETURN [bool]
};
Trr: PROC [code: ATOM, ref: REF] ~ {
old: LIST OF REF ~ NARROW[Atom.GetProp[atom: $selectchartrace, prop: $selectchartrace]];
Atom.PutProp[atom: $selectchartrace, prop: $selectchartrace,
val: CONS[LIST[code, ref], old]];
};
SelectChar:
PUBLIC
PROC [
viewer: ViewerClasses.Viewer,
tdd: TEditDocument.TEditDocumentData,
x, y:
INTEGER, sel: TEditDocument.SelectionId, pDel:
BOOLEAN] = {
DoSelectChar:
PROC [tSel, refSel: TEditDocument.Selection, rightOfLine:
BOOL] = {
newInsertion: TEditDocument.BeforeAfter;
newGrain: TEditDocument.SelectionGrain;
startValid, endValid: BOOLEAN ¬ TRUE;
tSel.end ¬ tSel.start;
IF Tr[$rl, rightOfLine] AND Tr[$ts, tdd.tsInfo#NIL] AND Tr[$ll, tSel.end.line=tdd.lineTable.lastLine] AND Tr[$eon, tdd.lineTable.lines[tSel.end.line].end=eon]
IF rightOfLine
AND tdd.tsInfo#
NIL
AND tSel.end.line=tdd.lineTable.lastLine
AND tdd.lineTable.lines[tSel.end.line].end=eon
THEN {
-- make point selection at end
tSel.end.pos.where ¬ tSel.start.pos.where ¬ TextEdit.Size[tSel.start.pos.node];
newGrain ¬ point;
newInsertion ¬ before;
startValid ¬ endValid ¬ FALSE;
}
ELSE {
newInsertion ¬ SetInsertion[tSel, x, tSel.start.line, rightOfLine, tdd];
newGrain ¬ char;
};
IF refSel.viewer#viewer OR refSel.start#tSel.start OR refSel.end#tSel.end OR
refSel.granularity#newGrain OR refSel.pendingDelete#pDel OR
refSel.insertion#newInsertion
THEN {
tSel.granularity ¬ newGrain;
tSel.viewer ¬ viewer;
tSel.data ¬ tdd;
tSel.insertion ¬ newInsertion;
tSel.pendingDelete ¬ pDel;
SetSelLooks[tSel];
TEditSelection.MakeSelection[tSel, sel, startValid, endValid, FALSE];
};
};
DoSelect[DoSelectChar, viewer, tdd, x, y, sel];
};
SelectWord:
PUBLIC
PROC [
viewer: ViewerClasses.Viewer,
tdd: TEditDocument.TEditDocumentData,
x, y:
INTEGER, sel: TEditDocument.SelectionId, pDel:
BOOLEAN] = {
DoSelectWord:
PROC [tSel, refSel: TEditDocument.Selection, rightOfLine:
BOOL] = {
start, end: INT;
punc: TEditDocument.PunctuationPosition;
newInsertion: TEditDocument.BeforeAfter;
newGrain: TEditDocument.SelectionGrain;
startValid, endValid: BOOLEAN ¬ TRUE;
hitLine: INTEGER ¬ tSel.start.line;
[start, end, punc] ¬ ExpandToWord[tSel.start.pos];
tSel.viewer ¬ viewer;
tSel.data ¬ tdd;
tSel.start.pos.where ¬ start;
tSel.end.pos ¬ [tSel.start.pos.node, end];
TEditSelection.FixupSelection[tSel, viewer];
IF rightOfLine
AND tdd.tsInfo#
NIL
AND tSel.end.line=tdd.lineTable.lastLine
AND tdd.lineTable.lines[tSel.end.line].end = eon
THEN {
make point selection at end of typescript
tSel.end.pos.where ¬ tSel.start.pos.where ¬ TextEdit.Size[tSel.start.pos.node];
newGrain ¬ point; newInsertion ¬ before; startValid ¬ endValid ¬ FALSE;
}
ELSE {
newInsertion ¬ SetInsertion[tSel, x, hitLine, rightOfLine, tdd];
newGrain ¬ word;
};
IF refSel.viewer#tSel.viewer
OR refSel.granularity#newGrain
OR tSel.start.pos#refSel.start.pos
OR tSel.end.pos#refSel.end.pos
OR newInsertion#refSel.insertion
OR refSel.pendingDelete#pDel
THEN {
tSel.granularity ¬ newGrain;
tSel.punctuation ¬ punc;
tSel.insertion ¬ newInsertion;
tSel.pendingDelete ¬ pDel;
SetSelLooks[tSel];
TEditSelection.MakeSelection[tSel, sel, startValid, endValid, FALSE];
};
};
DoSelect[DoSelectWord, viewer, tdd, x, y, sel];
};
SelectNode:
PUBLIC
PROC [
viewer: ViewerClasses.Viewer,
tdd: TEditDocument.TEditDocumentData,
x,y:
INTEGER, sel: TEditDocument.SelectionId, pDel:
BOOLEAN] = {
DoSelectNode:
PROC [tSel, refSel: TEditDocument.Selection, rightOfLine:
BOOL] = {
hitLine: INTEGER ¬ tSel.start.line;
newInsertion: TEditDocument.BeforeAfter;
tSel.viewer ¬ viewer;
tSel.data ¬ tdd;
tSel.start.pos ¬ [tSel.start.pos.node, 0];
tSel.end.pos ¬ [tSel.start.pos.node,
MAX[TextEdit.Size[tSel.start.pos.node],1]-1];
TEditSelection.FixupSelection[tSel, viewer];
newInsertion ¬ SetInsertion[tSel, x, hitLine, FALSE, tdd];
IF refSel.viewer#viewer OR refSel.start.pos.node#tSel.start.pos.node OR
refSel.granularity#node OR refSel.pendingDelete#pDel OR
refSel.insertion#newInsertion
THEN {
tSel.granularity ¬ node;
tSel.pendingDelete ¬ pDel;
tSel.insertion ¬ newInsertion;
SetSelLooks[tSel];
TEditSelection.MakeSelection[new: tSel, selection: sel, forkPaint: FALSE];
};
};
DoSelect[DoSelectNode, viewer, tdd, x, y, sel];
};
SelectBranch:
PUBLIC
PROC [
viewer: ViewerClasses.Viewer,
tdd: TEditDocument.TEditDocumentData,
x,y:
INTEGER, sel: TEditDocument.SelectionId, pDel:
BOOLEAN] = {
DoSelectBranch:
PROC [tSel, refSel: TEditDocument.Selection, rightOfLine:
BOOL] = {
hitLine: INTEGER ¬ tSel.start.line;
newInsertion: TEditDocument.BeforeAfter;
tSel.viewer ¬ viewer;
tSel.data ¬ tdd;
tSel.start.pos ¬ [tSel.start.pos.node, 0];
tSel.end.pos.node ¬ TextNode.LastWithin[tSel.start.pos.node];
tSel.end.pos.where ¬ TextNode.EndPos[tSel.end.pos.node];
TEditSelection.FixupSelection[tSel, viewer];
newInsertion ¬ SetInsertion[tSel, x, hitLine, FALSE, tdd];
IF refSel.viewer#viewer OR refSel.start.pos.node#tSel.start.pos.node OR
refSel.granularity#branch OR refSel.pendingDelete#pDel OR
refSel.insertion#newInsertion
THEN {
tSel.granularity ¬ branch;
tSel.pendingDelete ¬ pDel;
tSel.insertion ¬ newInsertion;
SetSelLooks[tSel];
TEditSelection.MakeSelection[new: tSel, selection: sel, forkPaint: FALSE];
};
};
DoSelect[DoSelectBranch, viewer, tdd, x, y, sel];
};
ComputeBeforeAfter: PROC [sel: TEditDocument.Selection, new: TextNode.Location, x, y: INTEGER] RETURNS [TEditDocument.BeforeAfter] = {
OPEN sel;
sOS: BOOLEAN = (start.line IN [0..LAST[INTEGER]));
eOS: BOOLEAN = (end.line IN [0..LAST[INTEGER]));
easy: BOOLEAN = (start.pos.node=end.pos.node)
AND (start.pos.node=new.node);
RETURN[SELECT TRUE FROM
NOT (sOS OR eOS) => SELECT TRUE FROM
end.line<0 => after,
start.line>0 => before,
ENDCASE => IF y>viewer.ch/2 THEN after ELSE before,
NOT sOS => after,
NOT eOS => before,
easy AND new.where<=start.pos.where => before,
easy AND new.where>=end.pos.where => after,
easy => IF Dist[sel, before, x, y] < Dist[sel, after, x, y] THEN before ELSE after,
EditSpan.CompareNodeOrder[new.node, start.pos.node]#after => before,
EditSpan.CompareNodeOrder[new.node, end.pos.node]#before => after,
ENDCASE => IF Dist[sel, before, x, y] < Dist[sel, after, x, y] THEN before ELSE after
];
};
Dist:
PROC [sel: TEditDocument.Selection, dir: TEditDocument.BeforeAfter, x, y:
INTEGER]
RETURNS [
LONG
INTEGER] = {
SQR: PROC [n: LONG INTEGER] RETURNS [LONG INTEGER] = INLINE {RETURN[n*n]};
RETURN[
IF dir=before
THEN
SQR[x-sel.start.x]+SQR[y-sel.start.y]
ELSE SQR[x-sel.end.x]+SQR[y-sel.end.y]];
};
CompareLoc:
PROC [loc1, loc2: TextNode.Location]
RETURNS [order: EditSpan.NodeOrder] = {
IF loc1.node=loc2.node
THEN
RETURN [
SELECT loc1.where
FROM
< loc2.where => before,
= loc2.where => same,
ENDCASE => after];
RETURN [EditSpan.CompareNodeOrder[loc1.node,loc2.node]]
};
initStart: TextNode.Location;
initEnd: TextNode.Location;
initTDD: TEditDocument.TEditDocumentData;
Extend:
PUBLIC
PROC [
viewer: ViewerClasses.Viewer,
tdd: TEditDocument.TEditDocumentData, x, y:
INTEGER,
sel: TEditDocument.SelectionId,
pDel:
BOOLEAN,
changeLevel: TEditSelection.LevelChange,
saveEnds:
BOOLEAN] = {
DoExtend:
PROC [tSel, refSel: TEditDocument.Selection, rightOfLine:
BOOL] = {
end: TEditDocument.BeforeAfter;
ok: BOOLEAN;
sp: TEditDocument.SelectionPoint; -- the place we're extending to
IF refSel.viewer=NIL THEN RETURN; -- no selection to extend
ok ¬ refSel.pendingDelete=pDel;
end ¬ refSel.insertion;
IF refSel.viewer#viewer
THEN {
refTDD: TEditDocument.TEditDocumentData = NARROW[refSel.viewer.data];
IF refTDD =
NIL
OR refTDD.text#tdd.text
THEN
RETURN;
can't extend into another document
ok ¬ FALSE -- extending into a different viewer, so must recalculate both ends
};
IF refSel.granularity=point THEN ok ¬ FALSE;
IF saveEnds
OR tdd#initTDD
THEN {
initTDD ¬ tdd;
initStart ¬ refSel.start.pos;
initEnd ¬ refSel.end.pos
};
sp ¬ tSel.start;
IF end=after
THEN {
changing the end of the selection
IF CompareLoc[tSel.start.pos,refSel.start.pos]=before
THEN {
switch ends
end ¬ before;
SELECT CompareLoc[refSel.start.pos,initEnd]
FROM
same, before => refSel.end.pos ¬ initEnd;
ENDCASE => initEnd ¬ refSel.end.pos; -- didn't get saved
ok ¬ FALSE
}
}
ELSE {
changing the start of the selection
IF CompareLoc[tSel.start.pos,refSel.end.pos]=after
THEN {
switch ends
end ¬ after;
SELECT CompareLoc[initStart,refSel.end.pos]
FROM
same, before => refSel.start.pos ¬ initStart;
ENDCASE => initStart ¬ refSel.start.pos; -- didn't get saved
ok ¬ FALSE
}
};
IF ok
AND changeLevel=same
AND
((end=before
AND sp.pos=refSel.start.pos)
OR (end=after
AND sp.pos=refSel.end.pos))
THEN RETURN; -- no change
TEditSelection.Copy[source: refSel, dest: tSel];
tSel.viewer ¬ viewer; -- in case we're extending into a different one
IF tSel.granularity=point
AND end=before
AND tSel.end.pos.where > 0
AND (sp.pos.node # tSel.end.pos.node
OR sp.pos.where < tSel.end.pos.where)
THEN {
tSel.end.pos.where ¬ tSel.end.pos.where-1; -- so don't include char after caret
ok ¬ FALSE
};
SELECT changeLevel
FROM
same => NULL;
reduce => {
ok ¬ FALSE;
tSel.granularity ¬
SELECT tSel.granularity
FROM
branch => node,
node => word,
word => char,
ENDCASE => char
};
expand => {
ok ¬ FALSE;
tSel.granularity ¬
SELECT tSel.granularity
FROM
point => char,
char => word,
word => node,
ENDCASE => branch
};
ENDCASE => ERROR;
SELECT tSel.granularity
FROM
branch, node => {
IF end=after
THEN {
IF tSel.granularity=branch THEN
sp.pos.node ¬ TextNode.LastWithin[sp.pos.node];
IF ok AND sp.pos.node=tSel.end.pos.node THEN RETURN;
tSel.end.pos ¬ [sp.pos.node,
MAX[TextEdit.Size[sp.pos.node],1]-1];
}
ELSE {
IF ok AND sp.pos.node=tSel.start.pos.node THEN RETURN;
tSel.start.pos ¬ [sp.pos.node, 0];
};
};
word => {
prev, start, endPos: INT;
node: TextNode.Ref = sp.pos.node;
prevNode: TextNode.Ref;
punc: TEditDocument.PunctuationPosition;
[start, endPos, punc] ¬ ExpandToWord[sp.pos, end=before];
IF end=after
THEN {
prevNode ¬ tSel.end.pos.node;
tSel.end.pos.node ¬ node;
IF tSel.punctuation # leading
THEN tSel.punctuation ¬ punc
ELSE IF punc = trailing THEN endPos ¬ endPos-1;
prev ¬ tSel.end.pos.where;
IF (tSel.end.pos.where¬endPos)=prev AND ok AND node=prevNode THEN RETURN;
}
ELSE {
prevNode ¬ tSel.start.pos.node;
tSel.start.pos.node ¬ node;
IF tSel.punctuation # trailing
THEN tSel.punctuation ¬ punc
ELSE IF punc = leading THEN start ¬ start+1;
prev ¬ tSel.start.pos.where;
IF (tSel.start.pos.where¬start)=prev AND ok AND node=prevNode THEN RETURN;
};
};
char, point => {
IF end=after
THEN {
IF tSel.end=sp AND ok AND tSel.granularity=char THEN RETURN;
ELSE {
IF tSel.start=sp AND ok AND tSel.granularity=char THEN RETURN;
tSel.granularity ¬ char;
};
ENDCASE => ERROR; -- no other selection flavors
tSel.insertion ¬ end;
tSel.pendingDelete ¬ pDel;
SetSelLooks[tSel];
TEditSelection.MakeSelection[tSel, sel, end=after AND ok, end=before AND ok, FALSE]
};
DoSelect[DoExtend, viewer, tdd, x, y, sel];
};
SetSelLooks:
PUBLIC
PROC [sel: TEditDocument.Selection] = {
loc: TextNode.Location ~ IF sel.insertion = before THEN sel.start.pos ELSE sel.end.pos;
size: INT ¬ TextEdit.Size[loc.node];
sel.looks ¬ (
IF size <= 0 THEN TextLooks.noLooks
ELSE IF loc.where >= size THEN TextEdit.FetchLooks[loc.node, size-1]
ELSE TextEdit.FetchLooks[loc.node, loc.where]
)
};
Update:
PUBLIC
PROC [
viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData,
x,y:
INTEGER, sel: TEditDocument.SelectionId, pDel:
BOOLEAN] = {
refSel: TEditDocument.Selection = IF sel=primary THEN TEditSelection.pSel ELSE IF sel=secondary THEN TEditSelection.sSel ELSE ERROR;
SELECT refSel.granularity
FROM
point => SelectPoint[viewer, tdd, x, y, changeSel, sel, changePDel, pDel];
word => SelectWord[viewer, tdd, x, y, sel, pDel];
node => SelectNode[viewer, tdd, x, y, sel, pDel];
branch => SelectBranch[viewer, tdd, x, y, sel, pDel];
ENDCASE => SelectChar[viewer, tdd, x, y, sel, pDel];
};
ExpandToWord:
PROC [pos: TextNode.Location, frontOnly:
BOOLEAN ¬
FALSE]
RETURNS [start, end:
INT, punc: TEditDocument.PunctuationPosition ¬ none] = {
refChar, char: CHARACTER;
alpha: BOOLEAN;
node: TextNode.RefTextNode = pos.node;
lastOffset: INT ¬ TextEdit.Size[node]-1;
ropeReader: RopeReader.Ref ¬ RopeReader.GetRopeReader[];
start ¬ end ¬ pos.where;
RopeReader.SetPosition[ropeReader, TextEditBogus.GetRope[node], end];
RopeReader.SetCharForEndOfRope[ropeReader, Ascii.CR]; -- so we get a return at the end
refChar ¬ RopeReader.Get[ropeReader];
SELECT refChar
FROM
Ascii.CR, Ascii.LF => { RopeReader.FreeRopeReader[ropeReader]; RETURN }; -- line break is a word
ENDCASE;
alpha ¬ CharOps.AlphaNumeric[refChar];
char ¬ RopeReader.Get[ropeReader];
WHILE ((alpha
AND CharOps.AlphaNumeric[char])
OR char=refChar)
AND end<lastOffset
DO
char ¬ RopeReader.Get[ropeReader];
end ¬ end+1;
ENDLOOP;
IF TEditProfile.wordPunctuation AND ~frontOnly AND alpha AND char=40C
THEN {
punc ¬ trailing;
end ¬ end+1;
};
RopeReader.SetPosition[ropeReader, TextEditBogus.GetRope[node], start];
char ¬ RopeReader.Backwards[ropeReader];
WHILE ((alpha
AND CharOps.AlphaNumeric[char])
OR char=refChar)
AND start>0
DO
char ¬ RopeReader.Backwards[ropeReader];
start ¬ start-1;
ENDLOOP;
IF TEditProfile.wordPunctuation
AND punc=none
AND alpha
AND char=40C
THEN {
punc ¬ leading; start ¬ start-1;
};
RopeReader.FreeRopeReader[ropeReader];
};
SetInsertion:
PROC [sel: TEditDocument.Selection, x, line:
INTEGER, rightOfLine:
BOOLEAN, tdd: TEditDocument.TEditDocumentData]
RETURNS [TEditDocument.BeforeAfter] = {
node: TextNode.RefTextNode ¬ sel.start.pos.node;
size: INT ¬ TextEdit.Size[node];
IF sel.start.line=sel.end.line AND sel.start.pos.where>=size THEN RETURN [before];
This ensures caret before in empty nodes.
SELECT TEditProfile.selectionCaret
FROM
before =>
RETURN[
caret goes before unless making single character selection to right of last character in node
IF sel.start.pos=sel.end.pos AND sel.granularity=char AND rightOfLine AND tdd.lineTable.lines[line].end=eon THEN after ELSE before];
after =>
RETURN[
caret goes after unless making single character selection to left of middle of first character in node
IF sel.start.pos.where=0 AND sel.start.pos=sel.end.pos AND sel.granularity=char AND x-sel.start.x <= sel.end.x+sel.end.w-x THEN before ELSE after];
ENDCASE;
IF sel.start.line=line
THEN {
IF sel.end.line#line THEN RETURN [before];
IF sel.start.pos.where>=size THEN RETURN [before];
IF rightOfLine
THEN {
RETURN [
IF sel.start.pos=sel.end.pos
-- single char selection
AND sel.start.pos.where+1 < size -- not last char in node
AND CharOps.XBlank[TextEdit.FetchChar[node, sel.start.pos.where]]
THEN before ELSE after];
};
RETURN[IF x-sel.start.x <= sel.end.x+sel.end.w-x THEN before ELSE after];
}
ELSE IF sel.end.line=line THEN RETURN[after];
RETURN[IF line-sel.start.line <= sel.end.line-line THEN before ELSE after];
};
---- Screen to viewbox transforms ------
selectionDescentLimit:
NAT ¬ 7;
N.B. Also in TEditSelectionImpl
ResolveToChar:
PUBLIC
ENTRY
PROC [selection: TEditDocument.Selection, viewer: ViewerClasses.Viewer, tdd: TEditDocument.TEditDocumentData, x, y:
INTEGER]
RETURNS [rightOfLine:
BOOLEAN] =
TRUSTED {
lines: TEditDocument.LineTable;
line: INTEGER ¬ 0;
lastLine:
INTEGER;
info is returned in selection.start
lines ¬ tdd.lineTable;
line ¬ lastLine ¬ lines.lastLine;
y ¬ y-TEditProfile.ySelectFudge; -- for people who like to point below the target
IF y > (lines[lastLine].yOffset+lines[lastLine].descent)
THEN {
-- off end of text
x ¬ viewer.cw+10;
}
ELSE
FOR n:
INTEGER
IN (0..lastLine]
DO
-- horrible linear search!
IF lines[n].yOffset >= y
THEN {
bottomOfUpper: INTEGER ¬ lines[n-1].yOffset+lines[n-1].descent;
topOfLower: INTEGER ¬ lines[n].yOffset-lines[n].ascent;
break: INTEGER ¬ MIN[MAX[(bottomOfUpper+topOfLower)/2, lines[n-1].yOffset], lines[n].yOffset-1];
line ¬ IF y < break THEN n-1 ELSE n;
EXIT;
};
ENDLOOP;
selection.start.y ¬ lines[line].yOffset-lines[line].ascent;
selection.start.h ¬ lines[line].ascent+MIN[lines[line].descent, selectionDescentLimit];
selection.start.line ¬ line;
GetLine[viewer, line];
[selection.start.pos, selection.start.x, selection.start.w, rightOfLine] ¬ TEditFormat.Resolve[lineInfo, x];
Trr[$resolve, LIST[NEW[INT ← selection.start.x], NEW[INT ← selection.start.w], NEW[INT ← x]]];
};
--------------
GrowSelectionToBlanks:
PUBLIC
PROC = {
Blank:
PROC [char:
CHAR]
RETURNS [
BOOLEAN] = {
RETURN [CharOps.Blank[char]]
};
GrowSelectionToSomething[Blank, Blank]
};
GrowSelectionToSomething:
PUBLIC
PROC [left, right:
PROC [
CHAR]
RETURNS [
BOOLEAN]] = {
tSel, cSel: TEditDocument.Selection;
selection: TEditDocument.SelectionId;
start, end: TextNode.RefTextNode;
startPos, endPos, endLen: INT;
ropeReader: RopeReader.Ref;
cSel ¬ TEditSelection.sSel;
selection ¬ secondary;
IF cSel=
NIL
OR TEditSelection.sSel.viewer=
NIL
THEN {
cSel ¬ TEditSelection.pSel; selection ¬ primary;
};
IF cSel=NIL OR cSel.viewer=NIL THEN RETURN;
tSel ¬ TEditSelection.Alloc[];
TEditSelection.Copy[source: cSel, dest: tSel];
start ¬ tSel.start.pos.node;
ropeReader ¬ RopeReader.GetRopeReader[];
RopeReader.SetPosition[ropeReader, TextEditBogus.GetRope[start], tSel.start.pos.where];
DO
-- find first blank to left of start of selection
loc: INT = RopeReader.GetIndex[ropeReader];
IF loc = 0 THEN { startPos ¬ 0; EXIT };
IF left[RopeReader.Backwards[ropeReader]] THEN { startPos ¬ loc; EXIT };
ENDLOOP;
tSel.start.pos.where ¬ startPos;
end ¬ tSel.end.pos.node;
endPos ¬ IF tSel.granularity=point THEN tSel.end.pos.where ELSE tSel.end.pos.where+1;
endLen ¬ Rope.Size[TextEditBogus.GetRope[end]];
RopeReader.SetPosition[ropeReader, TextEditBogus.GetRope[end], endPos];
DO
-- find first blank to right of end of selection
loc: INT = RopeReader.GetIndex[ropeReader];
IF loc = endLen THEN { endPos ¬ endLen; EXIT };
IF right[RopeReader.Get[ropeReader]] THEN { endPos ¬ loc; EXIT };
ENDLOOP;
RopeReader.FreeRopeReader[ropeReader];
tSel.end.pos.where ¬ endPos-1;
IF endPos-startPos > 1 AND tSel.granularity = point THEN tSel.granularity ¬ char;
IF tSel.granularity # point THEN tSel.insertion ¬ after;
TEditSelection.MakeSelection[new: tSel, selection: selection];
TEditSelection.Free[tSel]
};
GrowSelection:
PUBLIC
PROC = {
tSel: TEditDocument.Selection;
IF TEditSelection.pSel=NIL OR TEditSelection.pSel.viewer=NIL THEN RETURN;
tSel ¬ TEditSelection.Alloc[];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel];
SELECT tSel.granularity
FROM
point => {
tSel.granularity ¬ char;
};
char => {
start, end: INT;
tSel.granularity ¬ word;
[start, end, ----] ¬ ExpandToWord[tSel.start.pos];
tSel.start.pos.where ¬ start;
tSel.end.pos ¬ [tSel.start.pos.node, end];
};
word => {
tSel.start.pos.where ¬ 0;
tSel.end.pos.where ¬ TextNode.EndPos[tSel.end.pos.node];
tSel.granularity ¬ IF tSel.start.pos.node=tSel.end.pos.node AND
TextNode.FirstChild[tSel.end.pos.node]=NIL THEN branch ELSE node;
};
node => {
tSel.granularity ¬ branch;
tSel.end.pos.node ¬ TextNode.LastWithin[tSel.end.pos.node];
tSel.end.pos.where ¬ TextNode.EndPos[tSel.end.pos.node];
};
branch => {
parent: TextNode.Ref ¬ TextNode.Parent[tSel.start.pos.node];
IF TextNode.Parent[parent]=
NIL
THEN {
-- at the root
TEditSelection.SelectEverything; RETURN
};
tSel.start.pos ¬ [parent, 0];
tSel.end.pos.node ¬ TextNode.LastWithin[parent];
tSel.end.pos.where ¬ TextNode.EndPos[tSel.end.pos.node];
};
ENDCASE => ERROR;
TEditSelection.MakeSelection[new: tSel];
TEditSelection.Free[tSel]
};
--------------
InsertionPoint:
PUBLIC
PROC [s: TEditDocument.Selection ¬ TEditSelection.pSel]
RETURNS [ip: TextNode.Location] = {
nodeSel: BOOLEAN ~ s.granularity = node OR s.granularity = branch;
viewer: ViewerClasses.Viewer ~ s.viewer;
IF viewer = NIL OR s.data # viewer.data THEN RETURN [[NIL, 0]]; -- in case of vanishing viewers.
IF nodeSel
AND s.start.pos.node=
NIL
THEN {
Must create an insertion point
place: EditSpan.Place ~ IF s.insertion=before THEN before ELSE after;
node: TextNode.Ref ~ IF place = before THEN s.start.pos.node ELSE s.end.pos.node;
ip ¬ [EditSpan.InsertTextNode[root: TextNode.Root[node], old: node, where: place, inherit: TRUE, event: TEditInput.currentEvent], 0];
}
ELSE {
IF s.insertion=before THEN {ip ¬ s.start.pos}
ELSE {
size: INT ~ TextEdit.Size[s.end.pos.node];
ip ¬ [s.end.pos.node, MIN[MAX[s.end.pos.where+1, 0], size]];
};
};
};
GetSelectionGrain:
PUBLIC
PROC [sel: TEditDocument.Selection]
RETURNS [TEditDocument.SelectionGrain] = {
IF sel.granularity = node OR sel.granularity = branch THEN RETURN [node];
IF sel.granularity = point AND sel.start.pos = sel.end.pos THEN RETURN [point];
RETURN [IF sel.granularity=word THEN word ELSE char];
};
---- Misc functions ------
a one line cache for the current line being resolved
lineInfoViewer: ViewerClasses.Viewer ¬ NIL;
lineInfoLine: INTEGER ¬ 0;
lineInfo: TEditFormat.LineInfo ¬ TEditFormat.Allocate[];
InvalidateLineCache: PUBLIC ENTRY PROC = { lineInfoViewer ¬ NIL };
GetLine:
INTERNAL
PROC [viewer: ViewerClasses.Viewer, line:
INTEGER] = {
WITH viewer.data
SELECT
FROM
tdd: TEditDocument.TEditDocumentData => {
lines: TEditDocument.LineTable = tdd.lineTable;
IF viewer#lineInfoViewer
OR line#lineInfoLine
THEN {
style: NodeStyle.Ref ¬ NodeStyleOps.Alloc[];
pos: TextNode.Location = lines[line].pos;
kind: NodeStyleOps.OfStyle ~ IF ViewerOps.FetchProp[viewer, $StyleKind] = $Print THEN print ELSE screen;
NodeStyleOps.ApplyAll[style, lines[line].pos.node, kind];
TEditFormat.FormatLine[
lineInfo: lineInfo,
node: pos.node,
startOffset: pos.where,
nodeStyle: style,
lineWidth: Scaled.FromInt[viewer.cw]
];
lineInfoViewer ¬ viewer;
lineInfoLine ¬ line;
NodeStyleOps.Free[style];
};
};
ENDCASE => NULL;
};
CharPositionInCachedLine:
PUBLIC
ENTRY
PROC [viewer: ViewerClasses.Viewer, line:
INTEGER, pos: TextNode.Location]
RETURNS [x, width:
INTEGER] = {
GetLine[viewer, line];
[x, width] ¬ TEditFormat.CharPosition[lineInfo, pos.where];
};