TEditMiscOpsImpl.mesa
Copyright Ó 1985, 1987, 1988, 1991, 1992 by Xerox Corporation. All rights reserved.
Paxton on June 6, 1983 4:09 pm
Maxwell, January 6, 1983 11:33 am
Russ Atkinson, September 28, 1983 2:57 pm
Michael Plass, March 23, 1989 2:13:01 pm PST
Russ Atkinson (RRA) January 23, 1986 0:10:22 am PST
Willie-s, February 13, 1991 3:43 pm PST
Doug Wyatt, March 13, 1992 2:13 pm PST
DIRECTORY
AbbrevExpand USING [Expand, Load],
Ascii USING [CR, LF, Lower],
Atom USING [GetPName, MakeAtom],
BasicTime USING [Now],
Char,
CharOps,
EditSpan USING [ChangeCaps],
MessageWindow USING [Append, Blink],
NodeStyleOps USING [ReloadStyle, StyleNameForNode],
NodeStyleWorks USING [ForEachAttachedStyle],
PFS USING [Error],
Rope USING [Concat, Fetch, FromChar, FromProc, IsEmpty, ROPE, Size, Substr],
RopeReader USING [FreeRopeReader, Get, GetIndex, GetRope, GetRopeReader, Ref, SetPosition],
TBase,
TEditDocument USING [Selection, SelectionId],
TEditInput USING [CloseEvent, currentEvent, Interpret],
TEditInputOps USING [CallWithBothLocked, CallWithLocks, CheckReadonly, Delete, DoPendingDelete, EditFailed, FindPlaceholders, InsertRope],
TEditLocks USING [Lock, Unlock],
TEditOps USING [RememberCurrentPosition],
TEditRefresh USING [ScrollToEndOfSel],
TEditScrolling USING [AutoScroll],
TEditSelection USING [Alloc, CaretAfterSelection, CaretVisible, Copy, Deselect, Free, InsertionPoint, LockSel, MakePointSelection, MakeSelection, oldSel, pSel, SelectionRoot, SetSelLooks, sSel, UnlockSel],
TEditTouchup USING [fullUpdate],
TextEdit USING [ChangeStyle, DeleteText, FetchChar, FetchLooks, GetFormat, GetNewlineDelimiter, PutFormat, ReplaceByRope, Size],
TextEditBogus USING [GetRope],
TextLooks USING [Looks],
TextNode USING [FirstChild, LastLocWithin, Parent, Root, StepBackward, StepForward],
Tioga,
ViewerClasses USING [Viewer],
ViewerOps USING [PaintViewer],
ViewerTools USING [SetSelection];
=
BEGIN
ROPE: TYPE ~ Rope.ROPE;
Node: TYPE ~ Tioga.Node;
Location: TYPE ~ Tioga.Location;
NodeItself: INT ~ Tioga.NodeItself;
Looks: TYPE ~ Tioga.Looks;
noLooks: Looks ~ Tioga.noLooks;
Selection:
TYPE ~ TEditDocument.Selection;
Capitalise:
PUBLIC
PROC [flavor: Tioga.CapChange] = {
DoCapitalise:
PROC [root: Node, tSel: Selection] = {
TEditSelection.Deselect[];
EditSpan.ChangeCaps[root, [tSel.start.pos, tSel.end.pos], flavor, TEditInput.currentEvent];
tSel.pendingDelete ¬ FALSE;
TEditSelection.MakeSelection[tSel, primary]
};
TEditInputOps.CallWithLocks[DoCapitalise]
};
MungeChar:
PROC [proc: TBase.CaretProc] ~ {
DoMungeChar:
PROC [root: Node, tSel: Selection] = {
caret: Location ~ TEditSelection.InsertionPoint[tSel];
ok: BOOL ¬ FALSE;
IF caret.node#
NIL
AND caret.where#NodeItself
THEN {
resultStart, resultLen: INT ¬ 0;
TEditSelection.Deselect[];
[ok, resultStart, resultLen] ¬ proc[node: caret.node, caret: caret.where,
event: TEditInput.currentEvent];
IF ok THEN TEditSelection.MakePointSelection[tSel, [caret.node, resultStart+resultLen]]
ELSE TEditSelection.MakeSelection[tSel];
};
IF NOT ok THEN TEditInputOps.EditFailed[];
};
TEditInputOps.CallWithLocks[DoMungeChar];
};
MakeControlCharacter:
PUBLIC
PROC ~ {
MungeChar[TBase.MakeControlChar];
};
UnMakeControlCharacter:
PUBLIC
PROC ~ {
MungeChar[TBase.UnMakeControlChar];
};
MakeOctalCharacter:
PUBLIC
PROC ~ {
MungeChar[TBase.MakeOctalChar];
};
UnMakeOctalCharacter:
PUBLIC
PROC ~ {
MungeChar[TBase.UnMakeOctalChar];
};
GetWord:
PROC
RETURNS [word:
ROPE] = {
tSel: Selection;
start: INT;
pos: Location;
node: Node;
nChars: CARDINAL ¬ 0;
TEditSelection.CaretAfterSelection;
pos ¬ TEditSelection.InsertionPoint[];
IF (node ¬ pos.node)=NIL THEN RETURN [NIL];
IF pos.where = NodeItself THEN RETURN [NIL];
start ¬ pos.where-1;
WHILE start>=0
DO
c: Char.XCHAR ~ TextEdit.FetchChar[node, start];
IF NOT CharOps.XAlphaNumeric[c] THEN EXIT;
start ¬ start - 1; nChars ¬ nChars + 1;
ENDLOOP;
IF nChars = 0 THEN RETURN [NIL];
start ¬ pos.where-nChars;
word ¬ Rope.Substr[TextEditBogus.GetRope[node], start, nChars];
tSel ¬ TEditSelection.Alloc[];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel];
tSel.start.pos ¬ [node, start];
tSel.end.pos ¬ [node, start+nChars-1];
tSel.granularity ¬ char;
TEditSelection.MakeSelection[tSel, primary];
TEditSelection.Free[tSel];
TEditInputOps.Delete[];
};
SetStyle:
PUBLIC
PROC = {
DoSetStyle:
PROC [root: Node, tSel: Selection] = {
name: ROPE ¬ GetWord[];
IF name=NIL THEN { TEditInputOps.EditFailed[]; RETURN };
SetStyleName[name]
};
TEditInputOps.CallWithLocks[DoSetStyle]
};
ForceLower:
PROC [r:
ROPE]
RETURNS [
ROPE] = {
Force: PROC RETURNS [c: CHAR] = { c ¬ Ascii.Lower[Rope.Fetch[r, i]]; i ¬ i+1 };
i: INT ¬ 0;
RETURN [Rope.FromProc[Rope.Size[r], Force]]
};
SetStyleName:
PUBLIC
PROC [name:
ROPE, node: Node ¬
NIL] = {
root: Node;
IF node=NIL THEN
node ¬ IF TEditSelection.pSel.insertion=before THEN TEditSelection.pSel.start.pos.node ELSE TEditSelection.pSel.end.pos.node;
root ¬ TextNode.Root[node];
[] ¬ TEditLocks.Lock[root, "SetStyleName"];
TextEdit.ChangeStyle[node, ForceLower[name], TEditInput.currentEvent !
UNWIND => TEditLocks.Unlock[root]];
TEditLocks.Unlock[root];
};
ReloadStyle:
PUBLIC
PROC = {
styleName: ATOM ¬ NodeStyleOps.StyleNameForNode[TEditSelection.InsertionPoint[].node];
IF NOT NodeStyleOps.ReloadStyle[styleName] THEN CannotReload[styleName]
ELSE ViewerOps.PaintViewer[TEditSelection.pSel.viewer, client, FALSE, TEditTouchup.fullUpdate];
};
ReloadStyleName:
PUBLIC
PROC [name:
ROPE] = {
styleName: ATOM;
IF name.IsEmpty THEN RETURN;
styleName ¬ Atom.MakeAtom[name];
IF NOT NodeStyleOps.ReloadStyle[styleName] THEN CannotReload[styleName];
};
CannotReload:
PROC [styleName:
ATOM] = {
MessageWindow.Append["Failed in attempt to load style named ", TRUE];
MessageWindow.Append[Atom.GetPName[styleName]];
MessageWindow.Blink[]
};
SetFormat:
PUBLIC
PROC = {
DoSetFormat:
PROC [root: Node, tSel: Selection] = {
name: ROPE ¬ GetWord[];
IF name=NIL THEN { TEditInputOps.EditFailed[]; RETURN };
SetFormatName[name]
};
TEditInputOps.CallWithLocks[DoSetFormat]
};
GetFormat:
PUBLIC
PROC = {
DoGetFormat:
PROC [root: Node, tSel: Selection] = {
name: ROPE;
format: ATOM ¬ TextEdit.GetFormat[TEditSelection.InsertionPoint[].node];
name ¬ IF format=NIL THEN "default" ELSE Atom.GetPName[format];
TEditInputOps.InsertRope[name]
};
TEditInputOps.CallWithLocks[DoGetFormat]
};
TransposeFormat:
PUBLIC
PROC [target: TEditDocument.SelectionId ¬ primary] = {
Transpose the formats of the primary and secondary selections
targetSel: Selection ¬ IF target=primary THEN TEditSelection.pSel ELSE TEditSelection.sSel;
srcSel: Selection ¬ IF target=primary THEN TEditSelection.sSel ELSE TEditSelection.pSel;
DoTransFormat:
PROC [sourceRoot, destRoot: Node, tSel, srcSel, targetSel: Selection] = {
targetFormat, srcFormat: ATOM;
targetNode, srcNode: Node;
srcNode ¬ IF srcSel.insertion=before THEN srcSel.start.pos.node ELSE srcSel.end.pos.node;
srcFormat ¬ TextEdit.GetFormat[srcNode];
targetNode ¬ IF targetSel.insertion=before THEN targetSel.start.pos.node
ELSE targetSel.end.pos.node;
targetFormat ¬ TextEdit.GetFormat[targetNode];
TEditSelection.Copy[source: targetSel, dest: TEditSelection.oldSel]; -- save for Repeat's
TBase.SetFormat[node1: targetSel.start.pos.node, node2: targetSel.end.pos.node,
format: srcFormat, event: TEditInput.currentEvent];
TBase.SetFormat[node1: srcSel.start.pos.node, node2: srcSel.end.pos.node,
format: targetFormat, event: TEditInput.currentEvent];
TEditSelection.MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary]
};
TEditInputOps.CallWithBothLocked[DoTransFormat, targetSel, srcSel, write]
};
CopyFormat:
PUBLIC
PROC [target: TEditDocument.SelectionId ¬ primary] = {
targetSel: Selection ¬ IF target=primary THEN TEditSelection.pSel ELSE TEditSelection.sSel;
srcSel: Selection ¬ IF target=primary THEN TEditSelection.sSel ELSE TEditSelection.pSel;
DoCopyFormat:
PROC [sourceRoot, destRoot: Node, tSel, srcSel, targetSel: Selection] = {
srcNode: Node ¬
IF srcSel.insertion=before THEN srcSel.start.pos.node ELSE srcSel.end.pos.node;
format: ATOM ¬ TextEdit.GetFormat[srcNode];
TEditSelection.Copy[source: srcSel, dest: TEditSelection.oldSel]; -- save for Repeat's
TBase.SetFormat[node1: targetSel.start.pos.node, node2: targetSel.end.pos.node,
format: format, event: TEditInput.currentEvent];
TEditSelection.MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary]
};
TEditInputOps.CallWithBothLocked[DoCopyFormat, targetSel, srcSel, read]
};
SetFormatName:
PUBLIC
PROC [name:
ROPE, node: Node ¬
NIL] = {
root: Node;
lockSel: BOOL = (node=NIL);
format: ATOM ¬ IF name.IsEmpty THEN NIL ELSE Atom.MakeAtom[ForceLower[name]];
IF lockSel
THEN {
TEditSelection.LockSel[primary, "SetFormatName"];
IF
NOT TEditInputOps.CheckReadonly[TEditSelection.pSel]
OR (root ¬ TEditSelection.SelectionRoot[TEditSelection.pSel])=
NIL
THEN {
TEditSelection.UnlockSel[primary]; RETURN
};
node ¬ TEditSelection.InsertionPoint[].node
}
ELSE root ¬ TextNode.Root[node];
{
ENABLE
UNWIND =>
IF lockSel
THEN TEditSelection.UnlockSel[primary];
[] ¬ TEditLocks.Lock[root, "SetFormatName"];
TextEdit.PutFormat[node, format, TEditInput.currentEvent];
TEditLocks.Unlock[root];
IF lockSel THEN TEditSelection.UnlockSel[primary]
}
};
SetCommentProp:
PUBLIC
PROC [flag:
BOOLEAN] = {
DoSetCommentProp:
PROC [root: Node, tSel: Selection] = {
TBase.SetComment[node1: tSel.start.pos.node, node2: tSel.end.pos.node,
comment: flag, event: TEditInput.currentEvent];
};
TEditInputOps.CallWithLocks[DoSetCommentProp]
};
abbrevFailedProc: PROC RETURNS [BOOL] ¬ NIL;
RegisterAbbrevFailedProc:
PUBLIC
PROC [proc:
PROC
RETURNS [
BOOL]] = {
abbrevFailedProc ¬ proc
};
ExpandAbbreviation:
PUBLIC
PROC = {
DoExpand:
PROC [root: Node, tSel: Selection] = {
pos: Location;
node: Node;
offset: INT;
done, keyDeterminesDict: BOOLEAN ¬ FALSE;
clearedMessageWindow: BOOLEAN ¬ FALSE;
keyStart, keyLen, resultLen: INT ¬ 0;
rdr: RopeReader.Ref;
commands: LIST OF REF ANY;
styleName: ATOM;
Try:
PROC [name:
ATOM]
RETURNS [stop:
BOOLEAN] = {
dict: ROPE = Atom.GetPName[name];
[done, keyDeterminesDict, keyStart, keyLen, resultLen, commands] ¬
AbbrevExpand.Expand[node, offset, dict, TEditInput.currentEvent];
IF NOT done AND NOT keyDeterminesDict THEN
NodeStyleWorks.ForEachAttachedStyle[name, Try];
RETURN [done]
};
TEditSelection.CaretAfterSelection;
pos ¬ TEditSelection.InsertionPoint[];
node ¬ pos.node;
offset ¬ pos.where;
styleName ¬ NodeStyleOps.StyleNameForNode[node];
TEditSelection.Deselect[];
IF
NOT Try[styleName]
THEN {
TEditSelection.MakeSelection[tSel, primary];
IF abbrevFailedProc # NIL AND abbrevFailedProc[] THEN RETURN;
MessageWindow.Append[Rope.Substr[TextEditBogus.GetRope[node], keyStart, keyLen], NOT clearedMessageWindow];
MessageWindow.Append["? Unknown abbreviation."];
RETURN
};
tSel.end.pos.node ¬ tSel.start.pos.node ¬ node;
tSel.start.pos.where ¬ keyStart;
tSel.pendingDelete ¬ FALSE;
IF resultLen = 0
THEN {
-- make a caret
tSel.end.pos.where ¬ keyStart;
tSel.insertion ¬ before;
tSel.granularity ¬ point
}
ELSE {
tSel.end.pos.where ¬ keyStart+resultLen-1;
tSel.insertion ¬ after;
tSel.granularity ¬ char
};
TEditSelection.MakeSelection[tSel, primary];
rdr ¬ RopeReader.GetRopeReader[];
RopeReader.SetPosition[rdr, TextEditBogus.GetRope[node], keyStart];
FOR i:
INT
IN [0..resultLen)
DO
-- check for placeholder
IF RopeReader.Get[rdr] = 1C
THEN {
-- found one
TEditSelection.MakePointSelection[tSel, [node, keyStart+i]];
TEditInputOps.FindPlaceholders[TRUE];
EXIT
};
ENDLOOP;
RopeReader.FreeRopeReader[rdr];
IF commands # NIL THEN TEditInput.Interpret[TEditSelection.pSel.viewer, commands]
};
TEditInputOps.CallWithLocks[DoExpand]
};
LoadAbbreviations:
PUBLIC
PROC [dictName:
ROPE] = {
count: LONG INTEGER ¬ 0;
fileName: ROPE;
IF Rope.Size[dictName]=0 THEN RETURN;
fileName ¬ Rope.Concat[dictName, ".abbreviations"];
count ¬ AbbrevExpand.Load[dictName ! PFS.Error => {CONTINUE}];
IF count = 0
THEN {
-- something went wrong
MessageWindow.Append["The file named <", TRUE];
MessageWindow.Append[fileName];
MessageWindow.Append["> was not found or was not an abbreviation dictionary"]
}
};
InsertChar:
PUBLIC
PROC [char:
CHAR] = {
InsertRope[Rope.FromChar[char]];
DoInsertChar: PROC [root: Node, tSel: Selection] = {
pos: Location;
looks: TextLooks.Looks ¬ tSel.looks;
IF tSel.pendingDelete THEN {
TEditInputOps.DoPendingDelete[];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]
};
pos ¬ TEditSelection.InsertionPoint[TEditSelection.pSel]; -- need to get insertion point after pending delete
TEditSelection.Deselect[primary];
[] ¬ TextEdit.ReplaceByRope[root: root,
dest: pos.node, start: pos.where, len: 0,
rope: Rope.FromChar[char], looks: looks,
event: TEditInput.currentEvent];
pos.where ¬ pos.where+1;
TEditSelection.MakePointSelection[tSel, pos];
IF TEditSelection.CaretVisible[] THEN TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]
};
TEditInputOps.CallWithLocks[DoInsertChar];
};
InsertRope:
PUBLIC
PROC [rope:
ROPE] = {
DoInsertRope:
PROC [root: Node, tSel: Selection] = {
pos: Location;
looks: TextLooks.Looks;
IF tSel.pendingDelete
THEN {
TEditInputOps.DoPendingDelete[];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]
};
pos ¬ TEditSelection.InsertionPoint[TEditSelection.pSel]; -- need to get insertion point after pending delete
looks ¬ tSel.looks;
TEditSelection.Deselect[primary];
pos.where ¬ pos.where + TextEdit.ReplaceByRope[root: root,
dest: pos.node, start: pos.where, len: 0,
rope: rope, looks: looks, charSet: 0, charProps: NIL,
event: TEditInput.currentEvent].resultLen;
TEditSelection.MakePointSelection[tSel, pos];
IF TEditSelection.CaretVisible[] THEN TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]
};
TEditInputOps.CallWithLocks[DoInsertRope];
};
InsertLineBreak:
PUBLIC
PROC = {
copy leading blanks from previous line
DoInsertLineBreak:
PROC [root: Node, tSel: Selection] = {
pos: Location;
node: Node;
start, end, lim: INT;
rope: ROPE;
IF tSel.pendingDelete
THEN {
TEditInputOps.DoPendingDelete[];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]
};
pos ¬ TEditSelection.InsertionPoint[TEditSelection.pSel]; -- need to get insertion point after pending delete
IF (node ¬ pos.node) = NIL THEN { TEditInputOps.EditFailed[]; RETURN };
rope ¬ TextEditBogus.GetRope[node];
start ¬ lim ¬ MAX[0, MIN[pos.where, Rope.Size[rope]]];
WHILE start > 0 AND NOT (SELECT Rope.Fetch[rope, start-1] FROM Ascii.CR, Ascii.LF => TRUE, ENDCASE => FALSE) DO start ¬ start-1; ENDLOOP;
end ¬ start;
WHILE end < lim
AND CharOps.Blank[Rope.Fetch[rope, end]]
DO
end ¬ end+1;
ENDLOOP;
TEditInputOps.InsertRope[Rope.Concat[TextEdit.GetNewlineDelimiter[root], Rope.Substr[rope, start, end-start]]];
};
TEditInputOps.CallWithLocks[DoInsertLineBreak];
};
DeleteNextChar:
PUBLIC
PROC [count:
INT ¬ 1] = {
DoDeleteNextChar:
PROC [root: Node, tSel: Selection] = {
flush: Location ¬ TEditSelection.InsertionPoint[tSel];
node: Node ¬ flush.node;
IF node = NIL THEN GOTO Bad;
IF flush.where=NodeItself THEN GOTO Bad;
IF (count ¬ MIN[count, TextEdit.Size[node]-flush.where]) <= 0 THEN GOTO Bad;
TEditSelection.Deselect[primary];
TextEdit.DeleteText[root, node, flush.where, count, TEditInput.currentEvent];
TEditSelection.MakePointSelection[tSel, flush];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
EXITS Bad => TEditInputOps.EditFailed[]
};
IF count > 0 THEN TEditInputOps.CallWithLocks[DoDeleteNextChar];
};
GoToNextChar:
PUBLIC
PROC [count:
INT ¬ 1] = {
DoGoToNextChar:
PROC [root: Node, tSel: Selection] = {
loc: Location ¬ TEditSelection.InsertionPoint[tSel];
node: Node ¬ loc.node;
IF node = NIL OR
loc.where=NodeItself OR
(count ¬
MIN[count, TextEdit.Size[node]-loc.where]) <= 0
THEN {
-- try next node
GoToNextNode; RETURN
};
TEditSelection.MakePointSelection[tSel, [node, loc.where+count]];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
};
IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToNextChar, read];
};
GoToPreviousChar:
PUBLIC
PROC [count:
INT ¬ 1] = {
DoGoToPreviousChar:
PROC [root: Node, tSel: Selection] = {
loc: Location ¬ TEditSelection.InsertionPoint[tSel];
node: Node ¬ loc.node;
IF node = NIL OR
loc.where=NodeItself OR loc.where < count
THEN { GoToPreviousNode; RETURN }; -- try previous node
TEditSelection.MakePointSelection[tSel, [node, loc.where-count]];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
};
IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToPreviousChar, read];
};
FindNextWord:
PUBLIC
PROC [node: Node, start:
INT]
RETURNS [nChars:
CARDINAL] = {
offset: INT ¬ start;
size: INT ¬ TextEdit.Size[node];
Alpha:
PROC [index:
INT]
RETURNS [
BOOL] = {
RETURN[CharOps.XAlphaNumeric[TextEdit.FetchChar[node, index]]];
};
nChars ¬ 1;
WHILE offset<size
AND
NOT Alpha[offset]
DO
offset ¬ offset + 1;
nChars ¬ nChars + 1;
ENDLOOP;
WHILE offset<size
AND Alpha[offset]
DO
offset ¬ offset + 1;
nChars ¬ nChars + 1;
ENDLOOP;
nChars ¬ nChars - 1; -- correction: loops overshoot by 1
};
DeleteNextWord:
PUBLIC
PROC [count:
INT ¬ 1] = {
DoDeleteNextWord:
PROC [root: Node, tSel: Selection] = {
pos: Location ¬ TEditSelection.InsertionPoint[tSel];
node: Node ¬ pos.node;
nChars, start, next: INT;
IF (start ¬ pos.where) = NodeItself THEN GOTO Bad;
IF node=NIL THEN GOTO Bad;
next ¬ start;
THROUGH [0..count) DO next ¬ next+FindNextWord[node, next] ENDLOOP;
nChars ¬ next-start;
TEditSelection.Deselect[primary];
TextEdit.DeleteText[root, node, start, nChars, TEditInput.currentEvent];
TEditSelection.MakePointSelection[tSel, pos];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
EXITS Bad => TEditInputOps.EditFailed[];
};
IF count > 0 THEN TEditInputOps.CallWithLocks[DoDeleteNextWord];
};
GoToNextWord:
PUBLIC
PROC [count:
INT ¬ 1] = {
DoGoToNextWord:
PROC [root: Node, tSel: Selection] = {
pos: Location ¬ TEditSelection.InsertionPoint[tSel];
node: Node ¬ pos.node;
size, next: INT;
IF node=NIL OR
(next ¬ pos.where)=NodeItself
THEN {
-- try next node
GoToNextNode; RETURN
};
size ¬ TextEdit.Size[node];
THROUGH [0..count)
DO
IF next >= size THEN { GoToNextNode[]; RETURN };
next ¬ next+FindNextWord[node, next];
ENDLOOP;
TEditSelection.MakePointSelection[tSel, [node, next]];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
};
IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToNextWord, read];
};
GoToNextNode:
PUBLIC
PROC [count:
INT ¬ 1] = {
DoGoToNextNode:
PROC [root: Node, tSel: Selection] = {
pos: Location ¬ TEditSelection.InsertionPoint[tSel];
node: Node ¬ pos.node;
THROUGH [0..count)
DO
IF (node ¬ TextNode.StepForward[node])=NIL THEN { TEditInputOps.EditFailed[]; RETURN };
ENDLOOP;
TEditSelection.MakePointSelection[tSel, [node, 0]];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
};
IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToNextNode, read];
};
GoToPreviousNode:
PUBLIC
PROC [count:
INT ¬ 1] = {
DoGoToPreviousNode:
PROC [root: Node, tSel: Selection] = {
pos: Location ¬ TEditSelection.InsertionPoint[tSel];
node: Node ¬ pos.node;
THROUGH [0..count)
DO
IF (node ¬ TextNode.StepBackward[node])=NIL OR
TextNode.Parent[node]=NIL THEN GOTO Bad;
ENDLOOP;
IF node=NIL THEN GOTO Bad;
TEditSelection.MakePointSelection[tSel, [node, TextEdit.Size[node]]];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
EXITS Bad => TEditInputOps.EditFailed[];
};
IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToPreviousNode, read];
};
InsertTime:
PUBLIC
PROC = {
DoInsertTime:
PROC [root: Node, tSel: Selection] = {
resultStart, resultLen, dateLen: INT;
caret: Location;
looks: TextLooks.Looks ¬ TEditSelection.pSel.looks;
IF TEditSelection.pSel.pendingDelete THEN TEditInputOps.DoPendingDelete[];
caret ¬ TEditSelection.InsertionPoint[TEditSelection.pSel];
TEditSelection.Deselect[primary];
[resultStart: resultStart, resultLen: resultLen, dateLen: dateLen] ¬ TBase.InsertTime[
node: caret.node, index: caret.where, gmt: BasicTime.Now[],
looks: looks, event: TEditInput.currentEvent];
CharSelectionFromLocations[tSel,
[caret.node, resultStart+dateLen], [caret.node, resultStart+resultLen]];
tSel.insertion ¬ after;
tSel.pendingDelete ¬ FALSE;
TEditSelection.MakeSelection[selection: primary, new: tSel];
};
TEditInputOps.CallWithLocks[DoInsertTime];
};
LocationsFromSelection:
PROC [tSel: Selection]
RETURNS [loc1, loc2: Location] ~ {
loc1 ¬ tSel.start.pos; loc2 ¬ tSel.end.pos;
IF loc1.node=NIL OR loc2.node=NIL THEN RETURN[[NIL, 0], [NIL, 0]];
IF loc1.where=NodeItself THEN loc1.where ¬ 0;
IF tSel.granularity=point THEN loc2 ¬ loc1
ELSE IF loc2.where=NodeItself THEN loc2.where ¬ TextEdit.Size[loc2.node]
ELSE loc2.where ¬ loc2.where+1;
};
CharSelectionFromLocations:
PROC [tSel: Selection, loc1, loc2: Location] ~ {
IF loc1.node=loc2.node
AND loc1.where=loc2.where
THEN PointSelectionFromLocation[tSel, loc1]
ELSE {
tSel.start.pos ¬ loc1;
tSel.end.pos ¬ [loc2.node, loc2.where-1];
tSel.granularity ¬ char;
};
};
PointSelectionFromLocation:
PROC [tSel: Selection, loc: Location] ~ {
tSel.start.pos ¬ tSel.end.pos ¬ loc;
tSel.granularity ¬ point;
};
InsertBrackets:
PUBLIC
PROC [left, right:
CHAR] = {
InsertBracketRopes[Rope.FromChar[left], Rope.FromChar[right]];
};
InsertBracketRopes:
PROC [rope1, rope2:
ROPE, includeInSelection:
BOOL ¬
FALSE] ~ {
insert rope1 before selection, rope2 after selection
DoInsertBracketRopes:
PROC [root: Node, tSel: Selection] = {
loc1, loc2: Location;
[loc1, loc2] ¬ LocationsFromSelection[tSel];
IF loc1.node=NIL THEN GOTO Bad;
TEditSelection.Deselect[primary];
[result1: loc1, result2: loc2] ¬ TBase.InsertBrackets[
loc1: loc1, loc2: loc2, rope1: rope1, rope2: rope2,
looks: tSel.looks, props: NIL, includeInResult: includeInSelection,
event: TEditInput.currentEvent];
CharSelectionFromLocations[tSel, loc1, loc2];
tSel.pendingDelete ¬ FALSE;
TEditSelection.MakeSelection[selection: primary, new: tSel];
EXITS Bad => TEditInputOps.EditFailed[];
};
TEditInputOps.CallWithLocks[DoInsertBracketRopes];
};
SelectMatchingBrackets:
PUBLIC
PROC [left, right:
CHAR] = {
IF NOT DoSelectMatchingBrackets[left, right] THEN TEditInputOps.EditFailed["No match."];
};
DoSelectMatchingBrackets:
PUBLIC
PROC [left, right:
CHAR]
RETURNS [found:
BOOL ¬
FALSE] ~ {
extend selection until includes matching left and right brackets
DoSelect:
PROC [root: Node, tSel: Selection] ~ {
loc1, loc2: Location;
[loc1, loc2] ¬ LocationsFromSelection[tSel];
[found, loc1, loc2] ¬ TBase.ExpandToBrackets[
char1: Char.Widen[left], char2: Char.Widen[right],
loc1: loc1, loc2: loc2];
IF found
THEN {
CharSelectionFromLocations[tSel, loc1, loc2];
TEditSelection.SetSelLooks[tSel];
TEditSelection.MakeSelection[tSel];
};
};
TEditInputOps.CallWithLocks[DoSelect, read];
};
CallWithPrimarySelectionLocked:
PROC [who:
ROPE, action:
PROC [Selection]] = {
TEditSelection.LockSel[primary, who];
action[TEditSelection.pSel ! UNWIND => TEditSelection.UnlockSel[primary]];
TEditSelection.UnlockSel[primary];
};
NextViewer:
PUBLIC
PROC [forward:
BOOL] = {
IF NOT DoNextViewer[forward] THEN TEditInputOps.EditFailed["No next viewer."];
};
DoNextViewer:
PUBLIC
PROC [forward:
BOOL]
RETURNS [found:
BOOL ¬
FALSE] = {
Viewer: TYPE ~ ViewerClasses.Viewer;
DoNextViewerAction:
PROC [PSel: Selection] = {
thisViewer: Viewer ~ TEditSelection.pSel.viewer;
nextViewer: Viewer ¬ NIL;
Enumerate:
PROC [enum:
PROC [Viewer]] = {
FOR v: Viewer ¬ thisViewer.parent.child, v.sibling
UNTIL v=
NIL
DO
enum[v];
ENDLOOP;
};
ConvergeForward:
PROC [v: Viewer] = {
IF v.class.flavor=$Text
AND (v.wy > thisViewer.wy
OR (v.wy = thisViewer.wy AND v.wx > thisViewer.wx)) THEN {
IF nextViewer=NIL OR v.wy < nextViewer.wy THEN {nextViewer ¬ v; RETURN};
IF v.wy > nextViewer.wy THEN RETURN;
IF (v.wy > thisViewer.wy OR v.wx > thisViewer.wx) AND v.wx < nextViewer.wx THEN
nextViewer ¬ v;
};
};
ConvergeBackward:
PROC [v: Viewer] = {
IF v.class.flavor=$Text
AND (v.wy < thisViewer.wy
OR (v.wy = thisViewer.wy AND v.wx < thisViewer.wx)) THEN {
IF nextViewer=NIL OR v.wy > nextViewer.wy THEN {nextViewer ¬ v; RETURN};
IF v.wy < nextViewer.wy THEN RETURN;
IF (v.wy < thisViewer.wy OR v.wx < thisViewer.wx) AND v.wx > nextViewer.wx THEN
nextViewer ¬ v;
};
};
IF thisViewer=NIL OR thisViewer.parent=NIL THEN RETURN;
Enumerate[IF forward THEN ConvergeForward ELSE ConvergeBackward];
IF nextViewer # NIL THEN { ViewerTools.SetSelection[nextViewer, NIL]; found ¬ TRUE };
};
CallWithPrimarySelectionLocked["DoNextViewer", DoNextViewerAction];
};
FindPlaceholders:
PUBLIC
PROC [next:
BOOL] = {
found, wentToEnd: BOOL;
[found, wentToEnd] ¬ DoFindPlaceholders[next, TRUE];
IF NOT found AND NOT wentToEnd THEN NextViewer[next];
};
TryToGetLooks:
PROC [node: Node, index:
INT, default: Looks]
RETURNS [Looks] ~ {
RETURN[IF index IN[0..TextEdit.Size[node])
THEN TextEdit.FetchLooks[node, index] ELSE default];
};
DoFindPlaceholders:
PUBLIC
PROC [next, gotoend:
BOOL,
startBoundaryNode, endBoundaryNode: Node ¬
NIL,
startBoundaryOffset:
INT ¬ 0, endBoundaryOffset:
INT ¬
LAST[
INT]]
RETURNS [found, wenttoend:
BOOL ¬
FALSE] = {
DoFind:
PROC [root: Node, tSel: Selection] = {
direction: TBase.Direction ~ (IF next THEN forward ELSE backward);
from: Location ¬ TEditSelection.InsertionPoint[tSel];
start: INT ¬ from.where;
result1, result2: Location;
SELECT direction
FROM
forward => IF tSel.insertion=before AND tSel.granularity#point
AND start<TextEdit.Size[from.node] THEN start ¬ start+1;
backward => IF tSel.insertion=after AND start>0 THEN start ¬ start-1;
ENDCASE;
[found, result1, result2] ¬ TBase.FindBrackets[direction: direction,
char1: Char.Widen['], char2: Char.Widen['], loc: [from.node, start],
bound1: [startBoundaryNode, startBoundaryOffset],
bound2: [endBoundaryNode, endBoundaryOffset],
includeInResult: TRUE];
IF found
THEN {
TEditInput.CloseEvent[];
CharSelectionFromLocations[tSel, result1, result2];
tSel.insertion ¬ before;
tSel.pendingDelete ¬ TRUE;
tSel.looks ¬ TryToGetLooks[result1.node, result1.where+1, noLooks]; -- char after
}
ELSE
IF gotoend
THEN {
end: Location ~ SELECT direction FROM
forward => TextNode.LastLocWithin[root],
backward => [TextNode.FirstChild[root], 0],
ENDCASE => ERROR;
IF from#end
OR tSel.granularity#point
THEN {
PointSelectionFromLocation[tSel, end];
wenttoend ¬ TRUE;
};
};
IF found
OR wenttoend
THEN {
TEditOps.RememberCurrentPosition[tSel.viewer];
TEditSelection.MakeSelection[tSel, primary];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, FALSE];
};
};
TEditInputOps.CallWithLocks[DoFind, read];
};