-- EditTool.mesa
-- Edited by Paxton on 11-Dec-81 13:04:10
DIRECTORY
Buttons,
Convert,
EditNotify,
Inline,
IOStream,
UndoEvent,
Labels,
List,
Menus,
MessageWindow,
NodeAddrs,
Rope,
RunReader,
Runtime,
TextEdit,
TextFind,
TextLooks,
TextLooksSupport,
TextNode,
TEditDocument,
TEditInputOps,
TEditOps,
TreeFind,
UserTerminal,
ViewerOps,
ViewerClasses,
ViewerMenus;
EditTool: PROGRAM
IMPORTS Buttons, Convert, Inline, IOStream, Labels, List, Menus, MessageWindow, NodeAddrs, Rope, RunReader,
Runtime, TEditInputOps, TEditOps, TextEdit, TextFind, TextLooksSupport, TextNode,
TreeFind, UserTerminal, ViewerClasses, ViewerMenus, ViewerOps =
{ OPEN ViewerClasses;
Event: TYPE = UndoEvent.Ref;
entryHeight: CARDINAL = 15;
entryVSpace: CARDINAL = 5;
entryLeft: CARDINAL ← 5;
gapSize: CARDINAL = 5;
heightSoFar: CARDINAL ← entryVSpace*2;
container: ViewerClasses.Viewer;
containerName: Rope.Ref =" Edit Tool ";
BuildContainer: PROC = {
container ← ViewerOps.CreateViewer[flavor: $Container, name: containerName,
iconic: TRUE, attributes: right, paint: TRUE];
container.menu ← Menus.CreateMenu[];
Menus.InsertMenuEntry[container.menu, "Destroy", ViewerMenus.Destroy];
Menus.InsertMenuEntry[container.menu, "<-->", ViewerMenus.Move];
Menus.InsertMenuEntry[container.menu, "Grow", ViewerMenus.Grow];
Menus.InsertMenuEntry[container.menu, "Close", ViewerMenus.Close];
};
----------------------------
forwardButton: Buttons.Button; -- the "Search Forward!" button
backwardsButton: Buttons.Button; -- the "Search Backwards!" button
SearchForward: Buttons.ButtonProc = { Search[TRUE] };
SearchBackwards: Buttons.ButtonProc = { Search[FALSE] };
tSel: TEditDocument.Selection = NEW[TEditDocument.SelectionRec];
CheckPSel: PROC [pSel: TEditDocument.Selection] RETURNS [ok: BOOLEAN] = {
IF pSel=NIL OR pSel.viewer=NIL OR pSel.viewer.class.flavor#$Text THEN { OPEN MessageWindow;
Append["Please make a text selection.",TRUE];
Blink[]; RETURN [FALSE] };
RETURN [TRUE] };
GetPatternNode: PROC RETURNS [pattern: TextNode.RefTextNode] = {
IF (pattern ← GetDataNode[targetArg])=NIL OR
TextEdit.Size[pattern]=0 THEN { OPEN MessageWindow;
Append["Please enter a search pattern in the Target field.",TRUE];
Blink[]; RETURN [NIL] }};
Search: PROC [forward: BOOLEAN] = { OPEN TreeFind;
finder: Finder;
found: BOOLEAN;
where: TextNode.RefTextNode;
pattern: TextNode.RefTextNode;
at, atEnd: TextNode.Offset;
pSel: TEditDocument.Selection = TEditOps.GetSelData[];
ignoreLooks, lit: BOOLEAN;
searchLooks: TextLooks.Looks;
IF CheckPSel[pSel]=FALSE OR (pattern ← GetPatternNode[])=NIL THEN RETURN;
[pattern,ignoreLooks,lit,searchLooks] ← GetLooksAndPatternInfo[pattern];
{ ENABLE TextFind.MalformedPattern => { OPEN MessageWindow;
Append[SELECT ec FROM
toobig => "Search pattern is too big",
endquote => "Search pattern incorrectly ends with quote",
boundary => "Search pattern incorrectly has | inside rather than at beginning or end",
ENDCASE => "Error in search pattern", TRUE];
Blink[];
GOTO Quit };
finder ← TreeFind.Create[pattern,lit,word,ignoreLooks,ignoreCase];
IF forward THEN
[found,where,at,atEnd,,] ←
Try[finder: finder, first: pSel.end.pos.node, start: pSel.end.pos.where+1,
looksExact:looksExact]
ELSE [found,where,at,atEnd,,] ←
TryBackwards[finder: finder, first: pSel.start.pos.node, len: pSel.start.pos.where,
looksExact: looksExact];
};
IF ~found OR where=NIL THEN { UserTerminal.BlinkDisplay[]; RETURN };
IF looksChoice=looksOnly AND ~word THEN
[at,atEnd] ← Extend[forward,searchLooks,where,at,atEnd];
tSel.start.pos ← [where,at];
tSel.end.pos ← tSel.clickPoint ← [where,MAX[0,atEnd-1]];
tSel.granularity ← char;
tSel.viewer ← pSel.viewer;
tSel.data ← pSel.data;
tSel.insertion ← after;
TEditOps.SetSelData[tSel];
TEditOps.CloseRepeatSequence[];
TEditOps.AutoScroll[FALSE];
EXITS Quit => RETURN;
};
Extend: PROC
[forward: BOOLEAN, searchLooks: TextLooks.Looks,
where: TextNode.RefTextNode, at, atEnd: TextNode.Offset,
last: TextNode.Ref ← NIL, lastLen: TextNode.Offset ← TextNode.MaxLen]
RETURNS [newAt, newAtEnd: TextNode.Offset] = {
runrdr: RunReader.Ref ← RunReader.GetRunReader[];
looks: TextLooks.Looks;
runLen: TextNode.Offset;
runs: TextLooks.Runs ← where.runs;
IF forward THEN { -- extend toward end of node
size: TextNode.Offset ← TextEdit.Size[where];
IF atEnd=size OR size=0 THEN RETURN [at,atEnd];
lastLen ← IF where=last THEN MIN[size,lastLen] ELSE size;
RunReader.SetPosition[runrdr,runs,atEnd];
WHILE atEnd < lastLen DO
IF runs=NIL THEN { runLen ← size-atEnd; looks ← TextLooks.noLooks }
ELSE [runLen,looks] ← RunReader.Get[runrdr];
IF ~looksExact THEN looks ← TextLooksSupport.LooksAND[looks,searchLooks];
IF searchLooks # looks THEN EXIT;
atEnd ← atEnd+runLen;
ENDLOOP;
RunReader.FreeRunReader[runrdr];
RETURN [at,MIN[atEnd,lastLen]] };
IF at=0 THEN RETURN [at,atEnd];
RunReader.SetPosition[runrdr,runs,at];
WHILE at > 0 DO
IF runs=NIL THEN { runLen ← at; looks ← TextLooks.noLooks }
ELSE [runLen,looks] ← RunReader.Backwards[runrdr];
IF ~looksExact THEN looks ← TextLooksSupport.LooksAND[looks,searchLooks];
IF searchLooks # looks THEN EXIT;
at ← at-runLen;
ENDLOOP;
RunReader.FreeRunReader[runrdr];
RETURN [at,atEnd] };
BuildSearchEntries: PROC = {
forwardButton ← Buttons.Create["Search Forward!", SearchForward,
entryLeft, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
forwardButton.border ← FALSE;
backwardsButton ← Buttons.Create["Search Backwards!", SearchBackwards,
entryLeft+forwardButton.ww+gapSize*2, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
backwardsButton.border ← FALSE;
heightSoFar ← heightSoFar + entryHeight + entryVSpace;
};
----------------------------
FlipState: PROC [label: Labels.Label, flag: BOOLEAN, l1, l2: Rope.Ref]
RETURNS [new: BOOLEAN] = {
new ← ~flag;
Labels.Set[label, IF new THEN l1 ELSE l2] };
BuildPair: PROC [proc: Buttons.ButtonProc, flag: BOOLEAN, l1, l2: Rope.Ref]
RETURNS [label: Labels.Label, button: Buttons.Button] = {
fudge: INTEGER = 1;
xTemp: INTEGER;
w: INTEGER = MAX[
Menus.ComputeStringInfo[Rope.ToString[l1]].width,
Menus.ComputeStringInfo[Rope.ToString[l2]].width];
button ← Buttons.Create[">> ", proc,
entryLeft, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
xTemp ← entryLeft+button.ww+gapSize;
label ← Labels.Create[IF flag THEN l1 ELSE l2, container,
xTemp, heightSoFar+fudge, w+15, entryHeight, FALSE];
label.border ← FALSE;
heightSoFar ← heightSoFar + entryHeight + entryVSpace;
};
----------------------------
CycleTriple: PROC [label: Labels.Label, state: [0..2], l0, l1, l2: Rope.Ref]
RETURNS [new: [0..2]] = {
nextState: ARRAY [0..2] OF [0..2] = [1,2,0];
nextRope: ARRAY [0..2] OF Rope.Ref = [l0,l1,l2];
new ← nextState[state];
Labels.Set[label,nextRope[new]] };
BuildTriple: PROC [proc: Buttons.ButtonProc, state: [0..2], l0, l1, l2: Rope.Ref]
RETURNS [label: Labels.Label, button: Buttons.Button] = {
fudge: INTEGER = 1;
w: INTEGER = MAX[
Menus.ComputeStringInfo[Rope.ToString[l0]].width,
Menus.ComputeStringInfo[Rope.ToString[l1]].width,
Menus.ComputeStringInfo[Rope.ToString[l2]].width];
labelRopes: ARRAY [0..2] OF Rope.Ref = [l0,l1,l2];
xTemp: INTEGER;
button ← Buttons.Create[">> ", proc,
entryLeft, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
xTemp ← entryLeft+button.ww+gapSize;
label ← Labels.Create[labelRopes[state], container,
xTemp, heightSoFar+fudge, w+15, entryHeight, FALSE];
label.border ← FALSE;
heightSoFar ← heightSoFar + entryHeight + entryVSpace;
};
----------------------------
BuildSearchButtons: PROC = {
startHeight: CARDINAL ← heightSoFar;
initLeft: CARDINAL = entryLeft;
BuildCaseEntry[]; BuildWordEntry[];
entryLeft ← (openRightWidth-scrollBarW-40)/2;
heightSoFar ← startHeight;
BuildLooksEntry[]; BuildLooksMatchEntry[];
entryLeft ← initLeft;
BuildLitOrPatternEntry[] };
----------------------------
----------------------------
looksMatchButton: Buttons.Button;
looksMatchLabel: Labels.Label;
looksExact: BOOLEAN ← FALSE;
equalLooks: Rope.Ref = "Equal as Looks Test";
subsetLooks: Rope.Ref = "Subset as Looks Test";
LooksMatchButton: Buttons.ButtonProc = {
looksExact ← FlipState[looksMatchLabel,looksExact,equalLooks,subsetLooks] };
BuildLooksMatchEntry: PROC = {
[looksMatchLabel,looksMatchButton] ←
BuildPair[LooksMatchButton,looksExact,equalLooks,subsetLooks] };
----------------------------
----------------------------
looksButton: Buttons.Button;
looksLabel: Labels.Label;
looksChoice: [0..2] ← textAndLooks;
looksOnly: [0..2] = 0;
textOnly: [0..2] = 1;
textAndLooks: [0..2] = 2;
textOnlyRope: Rope.Ref = "Text Only";
looksOnlyRope: Rope.Ref = "Looks Only";
textAndLooksRope: Rope.Ref = "Text & Looks";
LooksButton: Buttons.ButtonProc = {
looksChoice ← CycleTriple[looksLabel,looksChoice,
looksOnlyRope, textOnlyRope, textAndLooksRope] };
BuildLooksEntry: PROC = {
[looksLabel,looksButton] ←
BuildTriple[LooksButton,looksChoice,
looksOnlyRope, textOnlyRope, textAndLooksRope] };
----------------------------
----------------------------
literalButton: Buttons.Button;
literalLabel: Labels.Label;
literal: BOOLEAN ← TRUE;
matchingLiterally: Rope.Ref = "Match Literally";
matchingAsPattern: Rope.Ref = "Match as Pattern";
LiteralButton: Buttons.ButtonProc = {
literal ← FlipState[literalLabel,literal,matchingLiterally,matchingAsPattern] };
BuildLitOrPatternEntry: PROC = {
[literalLabel,literalButton] ←
BuildPair[LiteralButton,literal,matchingLiterally,matchingAsPattern] };
----------------------------
patternDoc1: Rope.Ref = "# = any char; * = any string; 'x = use x literally;";
patternDoc2: Rope.Ref = "& = any alpha string; ~ = any non-alpha string;";
patternDoc3: Rope.Ref = "@ = any alpha char; ! = any non-alpha char;";
patternDoc4: Rope.Ref = "| = node boundary; { } bounds resulting selection";
BuildPatternDocEntry: PROC = {
Place: PROC [line: Rope.Ref] = {
label: Labels.Label ← Labels.Create[line, container,
entryLeft+gapSize*3, heightSoFar,
openRightWidth-scrollBarW-5, entryHeight,
FALSE];
label.border ← FALSE; heightSoFar ← heightSoFar + entryHeight };
Place[patternDoc1]; Place[patternDoc2]; Place[patternDoc3]; Place[patternDoc4];
heightSoFar ← heightSoFar + entryVSpace };
----------------------------
caseButton: Buttons.Button;
caseLabel: Labels.Label;
ignoreCase: BOOLEAN ← FALSE;
ignoringCase: Rope.Ref = "Ignore Case";
matchingCase: Rope.Ref = "Match Case";
CaseButton: Buttons.ButtonProc = {
ignoreCase ← FlipState[caseLabel,ignoreCase,ignoringCase,matchingCase] };
BuildCaseEntry: PROC = {
[caseLabel,caseButton] ←
BuildPair[CaseButton,ignoreCase,ignoringCase,matchingCase] };
----------------------------
wordButton: Buttons.Button;
wordLabel: Labels.Label;
word: BOOLEAN ← FALSE;
matchingWords: Rope.Ref = "Match Words Only";
matchingAnywhere: Rope.Ref = "Match Anywhere";
WordButton: Buttons.ButtonProc = {
word ← FlipState[wordLabel,word,matchingWords,matchingAnywhere] };
BuildWordEntry: PROC = {
[wordLabel,wordButton] ←
BuildPair[WordButton,word,matchingWords,matchingAnywhere] };
----------------------------
DataFieldButton: PROC [arg: ViewerClasses.Viewer] = {
tdd: TEditDocument.TEditDocumentData = NARROW[arg.data];
TEditOps.SetTextContents[arg, NIL]; -- clear the field
tSel.start.pos ← tSel.end.pos ← tSel.clickPoint ← [GetDataNode[arg],0];
tSel.granularity ← char;
tSel.viewer ← arg;
tSel.data ← tdd;
tSel.insertion ← before;
TEditOps.SetSelData[tSel]; -- place caret in field
};
BuildDataFieldPair: PROC [buttonRope: Rope.Ref, buttonProc: Buttons.ButtonProc, lines: CARDINAL ← 2]
RETURNS [button: Buttons.Button, arg: ViewerClasses.Viewer] = {
xTemp: CARDINAL;
fudge: CARDINAL = 2;
button ← Buttons.Create[buttonRope, buttonProc,
entryLeft, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
button.border ← FALSE;
xTemp ← entryLeft+scrollBarW+button.ww;
arg ← ViewerOps.CreateChild[flavor: $Text, parent: container,
x: xTemp, y: heightSoFar+fudge,
w: openRightWidth-xTemp-scrollBarW-5,
h: entryHeight*lines, paint: FALSE];
arg.border ← FALSE; arg.scrollable ← TRUE;
heightSoFar ← heightSoFar + entryHeight*lines + entryVSpace;
TEditOps.SetTextContents[arg, NIL]; -- clear the field
};
GetDataNode: PROC [arg: ViewerClasses.Viewer] RETURNS [TextNode.RefTextNode] = {
tdd: TEditDocument.TEditDocumentData = NARROW[arg.data];
RETURN [TextNode.NarrowToTextNode[TextNode.FirstChild[tdd.text]]] };
GetDataLooks: PROC [arg: ViewerClasses.Viewer, name: Rope.Ref]
RETURNS [looks: TextLooks.Looks] = {
node: TextNode.RefTextNode ← GetDataNode[arg];
size: TextNode.Offset = TextEdit.Size[node];
IF size=0 THEN RETURN [TextLooks.noLooks];
looks ← TextEdit.FetchLooks[node,0];
FOR i: TextNode.Offset IN [1..size) DO
IF TextEdit.FetchLooks[node,i]#looks THEN { OPEN MessageWindow;
Append[name,TRUE];
Append[" does not have uniform looks. Using looks from first char."];
Blink[]; EXIT };
ENDLOOP };
----------------------------
targetButton: Buttons.Button;
targetArg: ViewerClasses.Viewer; -- the viewer holding the target pattern
TargetButton: Buttons.ButtonProc = { DataFieldButton[targetArg] };
BuildTargetEntry: PROC = {
[targetButton,targetArg] ← BuildDataFieldPair["Search for:", TargetButton] };
----------------------------
sourceButton: Buttons.Button;
sourceArg: ViewerClasses.Viewer; -- the viewer holding the source text
SourceButton: Buttons.ButtonProc = { DataFieldButton[sourceArg] };
BuildSourceEntry: PROC = {
[sourceButton,sourceArg] ← BuildDataFieldPair["Replace by:", SourceButton] };
----------------------------
doitButton: Buttons.Button;
yesButton: Buttons.Button;
noButton: Buttons.Button;
substituteButton: Buttons.Button;
helpButton: Buttons.Button;
replaceRope: Rope.Ref = "Replace!";
doitRope: Rope.Ref = "DoOne!";
subsRope: Rope.Ref = "Substitute!";
doallRope: Rope.Ref = "DoAll!";
BuildDoItEntries: PROC = {
yesButton ← Buttons.Create["Yes!", DoYes,
entryLeft, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
yesButton.border ← FALSE;
noButton ← Buttons.Create["No!", DoNo,
entryLeft+gapSize*2+yesButton.ww, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
noButton.border ← FALSE;
doitButton ← Buttons.Create[replaceRope, DoIt,
entryLeft+gapSize*4+yesButton.ww+noButton.ww, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
doitButton.border ← FALSE;
substituteButton ← Buttons.Create[subsRope, DoSubstitute,
entryLeft+gapSize*6+yesButton.ww+noButton.ww+doitButton.ww,
heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
substituteButton.border ← FALSE;
helpButton ← Buttons.Create["Help!", Help,
entryLeft+gapSize*8+yesButton.ww+noButton.ww+
doitButton.ww+substituteButton.ww,
heightSoFar, 0, 0, NIL, FALSE, container, FALSE];
helpButton.border ← FALSE;
heightSoFar ← heightSoFar + entryHeight + entryVSpace;
};
Help: Buttons.ButtonProc = { OPEN MessageWindow;
Append["See [Indigo]<Tioga>FindTool.Doc for help.",TRUE];
Blink[] };
----------------------------
operationButton: Buttons.Button;
operationLabel: Labels.Label;
doReplace: BOOLEAN ← TRUE;
replaceOperation: Rope.Ref = "Do Replace";
specifiedOperation: Rope.Ref = "Do Operations Specified Below";
OperationButton: Buttons.ButtonProc = {
doReplace ← FlipState[operationLabel,doReplace,replaceOperation,specifiedOperation];
Buttons.ReLabel[doitButton, IF doReplace THEN replaceRope ELSE doitRope];
Buttons.ReLabel[substituteButton, IF doReplace THEN subsRope ELSE doallRope] };
BuildOperationEntry: PROC = {
[operationLabel,operationButton] ←
BuildPair[OperationButton,doReplace,replaceOperation,specifiedOperation] };
----------------------------
----------------------------
initCapButton: Buttons.Button;
initCapLabel: Labels.Label;
forceInitCap: BOOLEAN ← TRUE;
forcingInitCap: Rope.Ref = "Capitalize like first replaced char";
ignoringInitCap: Rope.Ref = "Don't change replacement capitalization";
InitCapButton: Buttons.ButtonProc = {
forceInitCap ← FlipState[initCapLabel,forceInitCap,forcingInitCap,ignoringInitCap] };
BuildInitCapEntry: PROC = {
[initCapLabel,initCapButton] ←
BuildPair[InitCapButton,forceInitCap,forcingInitCap,ignoringInitCap] };
----------------------------
opsButton: Buttons.Button;
opsArg: ViewerClasses.Viewer;
OpsButton: Buttons.ButtonProc = { DataFieldButton[opsArg] };
BuildOperationField: PROC = {
[opsButton,opsArg] ← BuildDataFieldPair["Operations:", OpsButton, 4] };
GetOps: PROC RETURNS [list: LIST OF REF ANY] = {
GetIOStream[];
{ OPEN IOStream;
rope: Rope.Ref ← TextEdit.GetRope[GetDataNode[opsArg]];
h: Handle ← CreateInputStreamFromRope[rope];
item: REF ANY;
WHILE (item ← GetRefAny[h ! EndOfStream => { item ← NIL; CONTINUE}]) # NIL DO
WITH item SELECT FROM
x: ROPE => item ← LOOPHOLE[Rope.ToString[x], REF TEXT]; -- for now must convert ROPE to REF TEXT
ENDCASE;
list ← List.Nconc1[list, item];
ENDLOOP;
--h.Close[];
}};
loadedIOStream: BOOLEAN ← FALSE;
GetIOStream: PROC = {
IF ~Runtime.IsBound[IOStream.GetRefAny] THEN { OPEN MessageWindow;
Append["Run IOStreamPackage", TRUE]; Blink };
};
----------------------------
----------------------------
getopsButton: Buttons.Button;
setopButton: Buttons.Button;
getopButton: Buttons.Button;
BuildGetAndSetOpsEntries: PROC = {
getopsButton ← Buttons.Create["GetLastOps!", DoGetOps,
entryLeft, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
getopsButton.border ← FALSE;
setopButton ← Buttons.Create["SetCom!", DoSetCom,
entryLeft+gapSize*2+getopsButton.ww, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
setopButton.border ← FALSE;
getopButton ← Buttons.Create["GetCom!", DoGetCom,
entryLeft+gapSize*4+getopsButton.ww+setopButton.ww, heightSoFar, 0, 0,
NIL, FALSE, container, FALSE];
getopButton.border ← FALSE;
heightSoFar ← heightSoFar + entryHeight + entryVSpace;
};
DoGetOps: Buttons.ButtonProc = { ShowOps[TEditOps.GetRepeatSequence[]] };
ShowOps: PROC [list: LIST OF REF ANY] = {
GetIOStream[];
{ OPEN IOStream;
h: IOStream.Handle ← CreateOutputStreamToRope[];
doingChars: BOOLEAN ← FALSE;
nospace: BOOLEAN ← TRUE;
Space: PROC = {
IF doingChars THEN { -- end of string
PutChar[h, '"]; doingChars ← FALSE };
IF nospace THEN nospace ← FALSE ELSE PutChar[h,' ] };
AddChar: PROC [c: CHARACTER] = {
IF ~doingChars THEN { -- start of string
Space[]; PutChar[h, '"]; doingChars ← TRUE };
PutChar[h, c] };
{ ENABLE UNWIND => h.Close[];
FOR l: LIST OF REF ANY ← list, l.rest UNTIL l=NIL DO
WITH l.first SELECT FROM
x: ATOM => { Space[]; Put[h,atom[x]] };
x: REF INT => { Space[]; Put[h,int[x↑]] };
x: REF CHARACTER => AddChar[x↑];
x: ROPE => {
Space[]; PutChar[h,'"]; Put[h,rope[x]]; PutChar[h,'"] };
x: REF TEXT => {
Space[]; PutChar[h,'"]; Put[h,text[x]]; PutChar[h,'"] };
ENDCASE;
ENDLOOP;
nospace ← TRUE; Space[];
TEditOps.SetTextContents[opsArg, GetOutputStreamRope[h]];
}}};
debugCom: BOOLEAN ← FALSE;
SetCom: PROC [num: [0..9]] = {
list: LIST OF REF ANY ← GetOps[];
IF debugCom THEN ShowOps[list];
TEditOps.SetCommand[num,list];
};
DoSetCom: Buttons.ButtonProc = { OPEN IOStream;
rope: Rope.Ref ← TextEdit.GetRope[GetDataNode[comArg]];
h: Handle ← CreateInputStreamFromRope[rope];
num: INT ← GetInt[h ! Error => GOTO BadNum];
IF num ~IN [0..9] THEN GOTO BadNum;
SetCom[Inline.LowHalf[num]];
EXITS BadNum => { OPEN MessageWindow;
Append["Enter number from 0 to 9 in Command Number field", TRUE];
Blink[] } };
DoGetCom: Buttons.ButtonProc = { OPEN IOStream;
rope: Rope.Ref ← TextEdit.GetRope[GetDataNode[comArg]];
h: Handle ← CreateInputStreamFromRope[rope];
num: INT ← GetInt[h ! Error => GOTO BadNum];
IF num ~IN [0..9] THEN GOTO BadNum;
ShowOps[TEditOps.GetCommand[Inline.LowHalf[num]]];
EXITS BadNum => { OPEN MessageWindow;
Append["Enter number from 0 to 9 in Command Number field", TRUE];
Blink[] } };
----------------------------
comButton: Buttons.Button;
comArg: ViewerClasses.Viewer;
ComNumButton: Buttons.ButtonProc = { DataFieldButton[comArg] };
BuildComNumField: PROC = {
[comButton,comArg] ← BuildDataFieldPair["Command number [0..9]:", ComNumButton, 1];
TEditOps.SetTextContents[comArg, "1 "]; };
----------------------------
----------------------------
DoIt: Buttons.ButtonProc = {
pSel: TEditDocument.Selection;
params: LIST OF REF ANY;
IF doReplace THEN { DoReplace[]; RETURN };
IF (pSel ← TEditOps.GetSelData[])=NIL OR pSel.viewer=NIL OR pSel.viewer.class.flavor#$Text OR
(params ← GetOps[])=NIL THEN { UserTerminal.BlinkDisplay[]; RETURN };
TEditOps.InterpretInput[pSel.viewer, params] };
DoReplace: PROC = {
-- for now do it as a delete followed by a copy
-- to do the copy, must make source the secondary selection
pSel: TEditDocument.Selection = TEditOps.GetSelData[];
tdd: TEditDocument.TEditDocumentData = NARROW[sourceArg.data];
node: TextNode.RefTextNode ← GetDataNode[sourceArg];
size: TextNode.Offset;
runs: TextLooks.Runs;
initCap: BOOLEAN;
IF pSel=NIL OR pSel.viewer=NIL OR pSel.viewer.class.flavor#$Text THEN {
UserTerminal.BlinkDisplay[]; RETURN };
SELECT looksChoice FROM
looksOnly => {
targetLooks: TextLooks.Looks ← GetDataLooks[targetArg,"Target"];
sourceLooks: TextLooks.Looks ← GetDataLooks[sourceArg,"Source"];
TEditInputOps.ChangeLooks[sourceLooks,targetLooks];
RETURN };
textOnly => IF node#NIL THEN {
runs ← node.runs; -- save source looks
TextEdit.SetLooks[node,GetSelInitLooks[pSel]] };
textAndLooks => NULL;
ENDCASE => ERROR;
initCap ← forceInitCap AND IsSelInitCap[pSel];
TEditOps.OpenRepeatSequence[];
TEditInputOps.Delete[];
IF (size ← TextEdit.Size[node]) = 0 THEN RETURN; -- no source
tSel.start.pos ← [node,0];
tSel.end.pos ← tSel.clickPoint ← [node,size];
tSel.granularity ← char;
tSel.viewer ← sourceArg;
tSel.data ← tdd;
tSel.insertion ← before;
TEditOps.SetSelData[tSel,FALSE];
TEditInputOps.Copy[];
IF looksChoice=textOnly AND node#NIL THEN node.runs ← runs; -- restore source
IF initCap AND IsSelInitLower[pSel] THEN TEditInputOps.Capitalise[firstCap];
};
GetSelInitLooks: PROC [pSel: TEditDocument.Selection] RETURNS [TextLooks.Looks] = {
node: TextNode.RefTextNode ← TextNode.NarrowToTextNode[pSel.start.pos.node];
loc: TextNode.Offset ← pSel.start.pos.where;
IF node=NIL OR loc=TextNode.NodeItself OR loc=TextEdit.Size[node]
THEN RETURN [TextLooks.noLooks];
RETURN [TextEdit.FetchLooks[node,loc]] };
IsSelInitCap: PROC [pSel: TEditDocument.Selection] RETURNS [BOOLEAN] = {
node: TextNode.RefTextNode ← TextNode.NarrowToTextNode[pSel.start.pos.node];
loc: TextNode.Offset ← pSel.start.pos.where;
IF node=NIL OR loc=TextNode.NodeItself OR loc=TextEdit.Size[node]
THEN RETURN [FALSE];
RETURN [TextEdit.FetchChar[node,loc] IN ['A..'Z]] };
IsSelInitLower: PROC [pSel: TEditDocument.Selection] RETURNS [BOOLEAN] = {
node: TextNode.RefTextNode ← TextNode.NarrowToTextNode[pSel.start.pos.node];
loc: TextNode.Offset ← pSel.start.pos.where;
IF node=NIL OR loc=TextNode.NodeItself OR loc=TextEdit.Size[node]
THEN RETURN [FALSE];
RETURN [TextEdit.FetchChar[node,loc] IN ['a..'z]] };
DoYes: Buttons.ButtonProc = { DoIt[NIL, NIL]; SearchForward[NIL, NIL] };
DoNo: Buttons.ButtonProc = { SearchForward[NIL, NIL] };
----------------------------
subsRangeButton: Buttons.Button;
subsRangeLabel: Labels.Label;
subsRange: [0..2] ← withinSel;
withinSel: [0..2] = 0;
afterSel: [0..2] = 1;
entireDoc: [0..2] = 2;
withinSelRope: Rope.Ref = "Within Selection Only";
afterSelRope: Rope.Ref = "After Selection Only";
entireDocRope: Rope.Ref = "In Entire Document";
BuildSubstituteEntry: PROC = {
[subsRangeLabel,subsRangeButton] ← BuildTriple[SubsRangeButton, withinSel,
withinSelRope, afterSelRope, entireDocRope] };
SubsRangeButton: Buttons.ButtonProc = {
subsRange ← CycleTriple[subsRangeLabel, subsRange,
withinSelRope, afterSelRope, entireDocRope] };
GetLooksAndPatternInfo: PROC [pattern: TextNode.RefTextNode]
RETURNS [pat: TextNode.RefTextNode,
ignoreLooks, lit: BOOLEAN,
searchLooks: TextLooks.Looks] = {
pat ← pattern; lit ← literal; searchLooks ← TextLooks.noLooks;
SELECT looksChoice FROM
looksOnly => {
size: TextNode.Offset = TextEdit.Size[pattern];
searchLooks ← IF size=0 THEN TextLooks.noLooks
ELSE TextEdit.FetchLooks[pattern,0];
FOR i: TextNode.Offset IN [1..size) DO
IF TextEdit.FetchLooks[pattern,i]#searchLooks THEN {
OPEN MessageWindow;
Append["Pattern does not have uniform looks.",TRUE];
Append[" Using looks from first char."];
Blink[]; EXIT };
ENDLOOP;
ignoreLooks ← lit ← FALSE;
pat ← TextEdit.FromRope["#*"];
TextEdit.SetLooks[pat,searchLooks] };
textOnly => ignoreLooks ← TRUE;
textAndLooks => ignoreLooks ← FALSE;
ENDCASE => ERROR };
DoSubstitute: Buttons.ButtonProc = {
finder: TreeFind.Finder;
first, last, selStart, selEnd: TextNode.Ref;
firstText, lastText: TextNode.RefTextNode;
lastWhere: TextNode.RefTextNode;
pattern: TextNode.RefTextNode;
start, lastLen: TextNode.Offset;
count: LONG INTEGER;
pSel: TEditDocument.Selection = TEditOps.GetSelData[];
viewer: ViewerClasses.Viewer;
data: TEditDocument.TEditDocumentData;
insertion: TEditDocument.BeforeAfter;
granularity: TEditDocument.SelectionGrain;
ignoreLooks, lit: BOOLEAN;
searchLooks: TextLooks.Looks;
IF CheckPSel[pSel]=FALSE OR (pattern ← GetPatternNode[])=NIL THEN RETURN;
viewer ← pSel.viewer;
[pattern,ignoreLooks,lit,searchLooks] ← GetLooksAndPatternInfo[pattern];
SELECT subsRange FROM
withinSel => {
first ← pSel.start.pos.node;
last ← pSel.end.pos.node;
start ← pSel.start.pos.where;
lastLen ← pSel.end.pos.where };
afterSel => {
first ← pSel.end.pos.node;
last ← NIL;
start ← pSel.end.pos.where+1;
lastLen ← 0 };
entireDoc => {
first ← TextNode.Root[pSel.start.pos.node];
last ← NIL;
start ← 0;
lastLen ← 0 };
ENDCASE => ERROR;
data ← pSel.data;
insertion ← pSel.insertion;
granularity ← pSel.granularity;
selStart ← pSel.start.pos.node;
selEnd ← pSel.end.pos.node;
IF (firstText ← TextNode.NarrowToTextNode[selStart]) # NIL THEN
NodeAddrs.PutTextAddr[firstText,$Start,pSel.start.pos.where];
IF (lastText ← TextNode.NarrowToTextNode[selEnd]) # NIL THEN
NodeAddrs.PutTextAddr[lastText,$End,pSel.end.pos.where];
{ ENABLE TextFind.MalformedPattern => { OPEN MessageWindow;
Append[SELECT ec FROM
toobig => "Search pattern is too big",
endquote => "Search pattern incorrectly ends with quote",
boundary => "Search pattern incorrectly has | inside rather than at beginning or end",
ENDCASE => "Error in search pattern", TRUE];
Blink[];
GOTO Quit };
MakeSel: PROC [where: TextNode.RefTextNode, at, atEnd:TextNode.Offset] = {
tSel.start.pos ← [where,at];
tSel.end.pos ← [where,MAX[0,atEnd-1]];
tSel.granularity ← char;
tSel.viewer ← pSel.viewer;
tSel.data ← pSel.data;
tSel.insertion ← after;
TEditOps.SetSelData[tSel];
TEditOps.AutoScroll[];
};
DoSubs: PROC [where: TextNode.RefTextNode, at, atEnd, before, after: TextNode.Offset]
RETURNS [continue, bumpCount: BOOLEAN, from, delta: TextNode.Offset] = {
len: TextNode.Offset ← atEnd-at;
size: TextNode.Offset ← TextEdit.Size[where];
initCap: BOOLEAN ← forceInitCap AND
at < size AND TextEdit.FetchChar[where,at] IN ['A..'Z];
initLooks: TextLooks.Looks;
continue ← bumpCount ← TRUE;
SELECT looksChoice FROM
looksOnly => {
IF looksChoice=looksOnly AND ~word THEN {
[at,atEnd] ← Extend[TRUE,searchLooks,where,at,atEnd,last,lastLen];
len ← atEnd-at };
TextEdit.ChangeLooks[where,targetLooks,sourceLooks,
at,len,event];
from ← at+len; delta ← 0; RETURN };
textOnly => initLooks ← IF at < size THEN
TextEdit.FetchLooks[where,at] ELSE TextLooks.noLooks;
textAndLooks => NULL;
ENDCASE => ERROR;
MakeSel[where,at,atEnd];
[] ← TextEdit.ReplaceByText[where,at,len,
sourceRope,sourceRuns,sourceStart,sourceLen,event];
IF initCap AND sourceLen > 0 AND TextEdit.FetchChar[where,at] IN ['a..'z] THEN
TextEdit.ChangeCaps[where,at,1,firstCap,event];
IF looksChoice=textOnly THEN
TextEdit.SetLooks[where,initLooks,at,sourceLen,event];
TEditOps.PaintEdits[pSel];
from ← at+sourceLen; delta ← sourceLen-len };
DoOps: PROC [where: TextNode.RefTextNode, at, atEnd, before, after: TextNode.Offset]
RETURNS [continue, bumpCount: BOOLEAN, from, delta: TextNode.Offset] = {
len: TextNode.Offset ← atEnd-at;
size: TextNode.Offset ← TextEdit.Size[where];
MakeSel[where,at,atEnd];
IF where # lastWhere AND lastWhere # NIL THEN
NodeAddrs.RemTextAddr[lastWhere,$After];
lastWhere ← where;
NodeAddrs.PutTextAddr[where,$After,atEnd];
TEditOps.InterpretInput[pSel.viewer, params];
delta ← NodeAddrs.GetTextAddr[where,$After]-atEnd;
from ← atEnd+delta; continue ← bumpCount ← TRUE };
source: TextNode.RefTextNode ← GetDataNode[sourceArg];
sourceStart, sourceLen, sourceEnd: TextNode.Offset;
sourceRope: Rope.Ref;
sourceRuns: TextLooks.Runs;
targetLooks, sourceLooks: TextLooks.Looks;
size: TextNode.Offset ← TextEdit.Size[source];
event: UndoEvent.Ref ← TEditOps.CurrentEvent[];
params: LIST OF REF ANY;
IF source # NIL THEN { sourceRope ← source.rope; sourceRuns ← source.runs };
sourceStart ← 0; sourceLen ← size; sourceEnd ← sourceStart+sourceLen;
finder ← TreeFind.Create[pattern,lit,word,ignoreLooks,ignoreCase];
IF ~doReplace THEN {
IF (params ← GetOps[])=NIL THEN {
UserTerminal.BlinkDisplay[]; RETURN }}
ELSE IF looksChoice=looksOnly THEN {
targetLooks ← GetDataLooks[targetArg,"Target"];
sourceLooks ← GetDataLooks[sourceArg,"Source"] };
TEditOps.OpenRepeatSequence;
count ← TreeFind.Apply[finder,first,
IF doReplace THEN DoSubs ELSE DoOps,
start,last,lastLen,looksExact];
};
-- update the selection
tSel.start.pos ← [selStart,
IF firstText=NIL THEN TextNode.NodeItself
ELSE NodeAddrs.GetTextAddr[firstText,$Start]];
tSel.end.pos ← tSel.clickPoint ← [selEnd,
IF lastText=NIL THEN TextNode.NodeItself
ELSE NodeAddrs.GetTextAddr[lastText,$End]];
IF firstText#NIL THEN NodeAddrs.RemTextAddr[firstText,$Start];
IF lastText#NIL THEN NodeAddrs.RemTextAddr[lastText,$End];
IF lastWhere#NIL THEN NodeAddrs.RemTextAddr[lastWhere,$After];
tSel.granularity ← granularity;
tSel.viewer ← viewer;
tSel.data ← data;
tSel.insertion ← insertion;
-- repaint
IF subsRange=withinSel THEN {
TEditOps.SetSelData[tSel,TRUE,FALSE];
TEditOps.PaintEdits[pSel] }
ELSE { TEditOps.SetSelData[NIL]; -- clear it
ViewerOps.PaintViewer[viewer];
TEditOps.SetSelData[tSel] };
-- display the number of substitutions made
MessageWindow.Append[
Rope.Concat[Convert.ValueToRope[[signed[count,10]]],
IF count # 1 THEN " substitutions." ELSE " substitution."], TRUE];
EXITS Quit => RETURN;
};
----------------------------
BuildContainer[]; -- build enclosing viewer
BuildTargetEntry[];
heightSoFar ← heightSoFar + entryVSpace;
BuildSearchEntries[];
heightSoFar ← heightSoFar + entryVSpace;
BuildSearchButtons[];
heightSoFar ← heightSoFar + entryVSpace;
BuildSourceEntry[];
heightSoFar ← heightSoFar + entryVSpace;
BuildDoItEntries[];
heightSoFar ← heightSoFar + entryVSpace;
BuildSubstituteEntry[];
BuildInitCapEntry[];
BuildOperationEntry[];
heightSoFar ← heightSoFar + entryVSpace*2;
BuildOperationField[];
heightSoFar ← heightSoFar + entryVSpace*2;
BuildGetAndSetOpsEntries[];
BuildComNumField[];
heightSoFar ← heightSoFar + entryVSpace*2;
BuildPatternDocEntry[];
heightSoFar ← heightSoFar + entryVSpace;
ViewerOps.SetOpenHeight[container, heightSoFar];
}.
..