EditToolSubsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, March 28, 1985 9:12:52 am PST
Doug Wyatt, April 6, 1985 2:16:32 pm PST
Russ Atkinson (RRA) June 18, 1985 5:07:32 pm PDT
DIRECTORY
Atom USING [GetPName],
Buttons USING [ButtonProc],
Convert USING [RopeFromInt],
EditSpanSupport USING [CopySpan],
EditToolBuilder USING [BuildButton, BuildDataFieldPair, BuildPair, BuildTriple, DataFieldButton, GetDataLooks, GetDataNode],
EditToolPrivate USING [afterSel, anywhere, ChangeState, CheckPSel, CycleTriple, DoButton, DoOpsCom, entireDoc, Extend, FixPSel, GetOps, GetPatternNode, Info, mainToolInfo, nodes, Register, ReportPatternError, SavePSel, Search, SubsInfo, SubsInfoRec, withinSel, words],
Labels USING [Set],
Menus USING [MenuProc],
MessageWindow USING [Append, Blink],
NodeAddrs USING [GetTextAddr, PutTextAddr, RemTextAddr],
NodeProps USING [false, true],
NodeStyleOps USING [StyleNameForNode],
Rope USING [Concat, ROPE, Substr],
RopeReader USING [Create, Get, GetIndex, ReadOffEnd, Ref, SetPosition],
TEditDocument USING [BeforeAfter, Selection, SelectionGrain, SelectionRec, TEditDocumentData],
TEditInput USING [CommandProc, CurrentEvent, Interpret],
TEditInputOps USING [CheckReadonly],
TEditLocks USING [Lock],
TEditOps USING [GetSelData],
TEditRefresh USING [ScrollToEndOfSel],
TEditSelection USING [Deselect, LockSel, MakeSelection, pSel, SelectionRoot, UnlockDocAndPSel, UnlockSel],
TextEdit USING [ChangeCaps, ChangeLooks, ChangeStyle, ChangeFormat, DeleteText, FetchChar, FetchLooks, FromRope, PutProp, ReplaceText, SetLooks, Size],
TextFind USING [MalformedPattern, NameLoc, NameLooks],
TextLooks USING [FetchLooks, Looks, noLooks],
TextNode USING [MakeNodeSpan, NodeItself, Offset, Ref, RefTextNode, Root],
TreeFind USING [Apply, CommentControl, Create],
UndoEvent USING [Ref],
ViewerClasses USING [Viewer];
EditToolSubsImpl: CEDAR PROGRAM
IMPORTS Atom, Convert, EditSpanSupport, EditToolBuilder, EditToolPrivate, Labels, MessageWindow, NodeAddrs, NodeProps, NodeStyleOps, Rope, RopeReader, TEditInput, TEditInputOps, TEditLocks, TEditOps, TEditRefresh, TEditSelection, TextEdit, TextFind, TextLooks, TextNode, TreeFind
EXPORTS EditToolPrivate
= BEGIN
Offset: TYPE = TextNode.Offset;
Viewer: TYPE = ViewerClasses.Viewer;
sourceArgAtom: LIST OF REF = EditToolPrivate.Register[$ReplaceBy,SourceArgOp];
clearSourceArgAtom: LIST OF REF = EditToolPrivate.Register[$ClearReplaceBy,ClearSourceArgOp];
SourceButton: Buttons.ButtonProc = {
EditToolPrivate.DoButton[sourceArgAtom, clearSourceArgAtom, mouseButton=red]
};
SourceArgOp: TEditInput.CommandProc = {
SourceArg[EditToolPrivate.mainToolInfo];
};
SourceArg: PROC [info: EditToolPrivate.Info] = {
EditToolPrivate.SavePSel; EditToolBuilder.DataFieldButton[info.sourceArg,FALSE];
};
ClearSourceArgOp: TEditInput.CommandProc = {
ClearSourceArg[EditToolPrivate.mainToolInfo];
};
ClearSourceArg: PROC [info: EditToolPrivate.Info] = { EditToolPrivate.SavePSel; EditToolBuilder.DataFieldButton[info.sourceArg,TRUE] };
BuildSourceEntry: PUBLIC PROC [info: EditToolPrivate.Info] = {
[,info.sourceArg] ← EditToolBuilder.BuildDataFieldPair[info.layout, "Replacement:", SourceButton, info, 2];
};
replaceRope: Rope.ROPE = "Replace";
subsRope: Rope.ROPE = "Substitute";
BuildDoItEntries: PUBLIC PROC [info: EditToolPrivate.Info] = {
info.substituteButton ← EditToolBuilder.BuildButton[info.layout, "Substitute", DoSubstitute, info, TRUE];
[] ← EditToolBuilder.BuildButton[info.layout, "Yes", DoYes, info];
[] ← EditToolBuilder.BuildButton[info.layout, "No", DoNo, info];
info.doitButton ← EditToolBuilder.BuildButton[info.layout, "Replace", DoIt, info];
[] ← EditToolBuilder.BuildButton[info.layout, "Count", DoCount, info];
};
DoSubstituteMenuButton: PUBLIC Menus.MenuProc = {
DoSubstitute[NARROW[parent], clientData, mouseButton]
};
DoYesMenuButton: PUBLIC Menus.MenuProc = {
DoYes[NARROW[parent], clientData, mouseButton]
};
DoNoMenuButton: PUBLIC Menus.MenuProc = {
DoNo[NARROW[parent], clientData, mouseButton]
};
DoItMenuButton: PUBLIC Menus.MenuProc = {
DoIt[NARROW[parent], clientData, mouseButton]
};
DoCountMenuButton: PUBLIC Menus.MenuProc = {
DoCount[NARROW[parent], clientData, mouseButton]
};
replaceOperation: Rope.ROPE = "Do Replace";
specifiedOperation: Rope.ROPE = "Do Operations";
doOperationsAtom: LIST OF REF = EditToolPrivate.Register[$DoOperations,DoOperationsOp];
doReplaceAtom: LIST OF REF = EditToolPrivate.Register[$DoReplace,DoReplaceOp];
OperationButton: Buttons.ButtonProc = {
EditToolPrivate.ChangeState[EditToolPrivate.mainToolInfo.doReplace,doReplaceAtom,doOperationsAtom]
};
DoOperationsOp: TEditInput.CommandProc = { DoOperations[EditToolPrivate.mainToolInfo] };
DoOperations: PROC [info: EditToolPrivate.Info] = {
info.doReplace ← FALSE;
Labels.Set[info.operationLabel, specifiedOperation]
};
DoReplaceOp: TEditInput.CommandProc = { DoRep[EditToolPrivate.mainToolInfo] };
DoRep: PROC [info: EditToolPrivate.Info] = {
info.doReplace ← TRUE;
Labels.Set[info.operationLabel, replaceOperation]
};
BuildOperationEntry: PUBLIC PROC [info: EditToolPrivate.Info] = {
info.doReplace ← TRUE;
[info.operationLabel,] ← EditToolBuilder.BuildPair[info.layout,OperationButton,
info.doReplace,replaceOperation,specifiedOperation,info]
};
forcingInitCap: Rope.ROPE = "First cap like replaced";
ignoringInitCap: Rope.ROPE = "Don't change caps";
forceInitCapAtom: LIST OF REF = EditToolPrivate.Register[$ChangeInitCap,ChangeInitCapOp];
ignoreInitCapAtom: LIST OF REF = EditToolPrivate.Register[$LeaveInitCap,LeaveInitCapOp];
InitCapButton: Buttons.ButtonProc = {
EditToolPrivate.ChangeState[EditToolPrivate.mainToolInfo.forceInitCap,
forceInitCapAtom,ignoreInitCapAtom]
};
ChangeInitCapOp: TEditInput.CommandProc = { ChangeInitCap[EditToolPrivate.mainToolInfo] };
ChangeInitCap: PROC [info: EditToolPrivate.Info] = {
info.forceInitCap ← TRUE;
Labels.Set[info.initCapLabel,forcingInitCap]
};
LeaveInitCapOp: TEditInput.CommandProc = { LeaveInitCap[EditToolPrivate.mainToolInfo] };
LeaveInitCap: PROC [info: EditToolPrivate.Info] = {
info.forceInitCap ← FALSE;
Labels.Set[info.initCapLabel,ignoringInitCap]
};
BuildInitCapEntry: PUBLIC PROC [info: EditToolPrivate.Info] = {
info.forceInitCap ← TRUE;
[info.initCapLabel,] ← EditToolBuilder.BuildPair[info.layout,InitCapButton,
info.forceInitCap,forcingInitCap,ignoringInitCap,info]
};
doSubsAtom: LIST OF REF = EditToolPrivate.Register[$DoSubstitute,DoSubstituteOp];
DoSubstitute: Buttons.ButtonProc = {
EditToolPrivate.DoButton[doSubsAtom]
};
DoSubstituteOp: TEditInput.CommandProc = { DoSubstituteCom[EditToolPrivate.mainToolInfo] };
DoSubstituteCom: PROC [info: EditToolPrivate.Info, countOnly: BOOLFALSE] = {
subsinfo: EditToolPrivate.SubsInfo ← NEW[EditToolPrivate.SubsInfoRec];
root: TextNode.Ref;
{
first, selStart, selEnd: TextNode.Ref;
firstText, lastText: TextNode.RefTextNode;
lastWhere: TextNode.RefTextNode;
pattern: TextNode.RefTextNode;
commentControl: TreeFind.CommentControl;
format: ATOM;
style: ATOM;
start: Offset;
count: LONG INTEGER;
pSel: TEditDocument.Selection;
vwr: ViewerClasses.Viewer;
data: TEditDocument.TEditDocumentData;
insertion: TEditDocument.BeforeAfter;
granularity: TEditDocument.SelectionGrain;
lit: BOOL;
source: TextNode.RefTextNode ← EditToolBuilder.GetDataNode[info.sourceArg];
size: Offset ← TextEdit.Size[source];
params: LIST OF REF ANY;
MakeSel: PROC [where: TextNode.RefTextNode, at, atEnd:Offset] = {
tSel.start.pos ← [where,at];
tSel.end.pos ← [where,MAX[0,atEnd-1]];
tSel.granularity ← char;
tSel.viewer ← vwr;
tSel.data ← data;
tSel.insertion ← after;
TEditSelection.MakeSelection[new: tSel];
};
CountOnly: PROC [where: TextNode.RefTextNode, at, atEnd, before, after: Offset] RETURNS [continue, bumpCount: BOOL, from, delta: Offset] = {
IF info.interrupt^ THEN { continue ← FALSE; RETURN };
continue ← bumpCount ← TRUE;
from ← atEnd; delta ← 0
};
DoSubs: PROC [where: TextNode.RefTextNode, at, atEnd, before, after: Offset] RETURNS [continue, bumpCount: BOOL, from, delta: Offset] = {
IF info.varNode # NIL THEN info.varNode.next ← NIL;
Break circularity with root node, assuming this is just one node.
info.varNode ← IF info.literal THEN NIL ELSE EditSpanSupport.CopySpan[TextNode.MakeNodeSpan[where, where]].start.node;
I'm not sure it's very wonderful to do this copy here, but better to be safe . . . mfp
[continue, bumpCount, from, delta] ← DoOneSubs[info, root, where, at, atEnd, subsinfo, vwr];
};
DoOps: PROC [where: TextNode.RefTextNode, at, atEnd, before, after: Offset] RETURNS [continue, bumpCount: BOOL, from, delta: Offset] = {
len: Offset ← atEnd-at;
size: Offset ← TextEdit.Size[where];
IF info.interrupt^ THEN { continue ← FALSE; RETURN };
MakeSel[where,at,atEnd];
IF where # lastWhere AND lastWhere # NIL THEN
NodeAddrs.RemTextAddr[lastWhere,$After];
lastWhere ← where;
NodeAddrs.PutTextAddr[where,$After,atEnd];
TEditInput.Interpret[vwr, params];
delta ← NodeAddrs.GetTextAddr[where,$After].location-atEnd;
from ← atEnd+delta; continue ← bumpCount ← TRUE;
};
subsinfo.event ← TEditInput.CurrentEvent[];
subsinfo.substitute ← TRUE;
TEditSelection.LockSel[primary, "DoSubstituteCom"];
EditToolPrivate.FixPSel[];
pSel ← TEditOps.GetSelData[];
IF (NOT countOnly AND NOT TEditInputOps.CheckReadonly[pSel])
OR (root ← TEditSelection.SelectionRoot[pSel])=NIL
OR CheckForSubs[info,pSel]=FALSE OR (pattern ← EditToolPrivate.GetPatternNode[info])=NIL
THEN { TEditSelection.UnlockSel[primary]; RETURN };
vwr ← pSel.viewer;
[pattern,lit,subsinfo.searchLooks,format,style,commentControl] ←
GetLooksAndPatternInfo[pattern,info];
SELECT info.subsRange FROM
EditToolPrivate.withinSel => {
first ← pSel.start.pos.node;
subsinfo.last ← pSel.end.pos.node;
start ← pSel.start.pos.where;
subsinfo.lastLen ← pSel.end.pos.where+1;
};
EditToolPrivate.afterSel => {
first ← pSel.end.pos.node;
subsinfo.last ← NIL;
start ← pSel.end.pos.where+1;
subsinfo.lastLen ← 0;
};
EditToolPrivate.entireDoc => {
first ← TextNode.Root[pSel.start.pos.node];
subsinfo.last ← NIL;
start ← 0;
subsinfo.lastLen ← 0;
};
ENDCASE => ERROR;
data ← pSel.data;
insertion ← pSel.insertion;
granularity ← pSel.granularity;
selStart ← pSel.start.pos.node;
selEnd ← pSel.end.pos.node;
tSel^ ← pSel^;
[] ← TEditLocks.Lock[root, "DoSubstituteCom"];
IF (firstText ← selStart) # NIL THEN
NodeAddrs.PutTextAddr[firstText,$Start,tSel.start.pos.where];
IF (lastText ← selEnd) # NIL THEN
NodeAddrs.PutTextAddr[lastText,$End,tSel.end.pos.where+1];
info.interrupt^ ← FALSE;
IF source # NIL AND NOT countOnly AND info.doReplace THEN {
subsinfo.sourceFormat ← source.formatName;
IF NOT info.ignoreStyle THEN subsinfo.sourceStyle ← NodeStyleOps.StyleNameForNode[source];
subsinfo.sourceComment ← source.comment;
subsinfo.sourceNode ← source;
};
subsinfo.sourceLen ← size;
IF NOT info.ignoreLooks OR NOT info.ignoreText THEN {
create a description of the pattern
info.finder ← TreeFind.Create [
pattern: pattern,
literal: lit,
word: info.searchWhere=EditToolPrivate.words,
ignoreLooks: info.ignoreLooks,
ignoreCase: info.ignoreCase,
addBounds: info.searchWhere=EditToolPrivate.nodes !
TextFind.MalformedPattern => { EditToolPrivate.ReportPatternError[ec]; GOTO Quit }
];
};
IF NOT countOnly AND NOT info.doReplace THEN { -- doing specified operations
IF (params ← EditToolPrivate.GetOps[info])=NIL THEN {
MessageWindow.Append["Specify operations to be performed.", TRUE];
MessageWindow.Blink[]; TEditSelection.UnlockDocAndPSel[root]; RETURN
}
}
ELSE IF info.doReplace AND info.ignoreText AND NOT info.ignoreLooks THEN {
subsinfo.targetLooks ← EditToolBuilder.GetDataLooks[info.targetArg,"\"Search for\" field"];
IF NOT countOnly THEN subsinfo.sourceLooks ← EditToolBuilder.GetDataLooks[info.sourceArg,"\"Replace by\" field"]
};
IF NOT countOnly THEN {
TEditSelection.Deselect[]; -- clear selection
IF info.doReplace AND NOT info.literal THEN subsinfo.rdr ← RopeReader.Create[]
};
IF NOT countOnly AND NOT info.doReplace THEN TEditSelection.LockSel[primary , "DoSubstituteCom"];
count ← TreeFind.Apply[
finder: info.finder,
first: first,
proc: IF countOnly THEN CountOnly ELSE IF info.doReplace THEN DoSubs ELSE DoOps,
start: start,
last: subsinfo.last,
lastLen: subsinfo.lastLen,
looksExact: info.looksExact,
commentControl: commentControl,
checkFormat: NOT info.ignoreFormat,
format: format,
checkStyle: NOT info.ignoreStyle,
style: style,
styleProc: NodeStyleOps.StyleNameForNode
];
IF NOT countOnly AND NOT info.doReplace THEN TEditSelection.UnlockSel[primary];
update the selection
tSel.start.pos ← [selStart,
IF firstText=NIL THEN TextNode.NodeItself
ELSE NodeAddrs.GetTextAddr[firstText,$Start].location];
tSel.end.pos ← [selEnd,
IF lastText=NIL THEN TextNode.NodeItself
ELSE MAX[NodeAddrs.GetTextAddr[lastText,$End].location,1]-1];
IF selStart=selEnd THEN tSel.end.pos.where ← MAX[tSel.start.pos.where, tSel.end.pos.where];
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 ← vwr;
tSel.data ← data;
tSel.insertion ← insertion;
IF NOT countOnly THEN TEditSelection.MakeSelection[new: tSel]; -- restore selection
TEditSelection.UnlockDocAndPSel[root];
display the number of substitutions made
MessageWindow.Append[
Rope.Concat[Convert.RopeFromInt[count],
IF countOnly THEN IF count # 1 THEN " matches." ELSE " match."
ELSE IF count # 1 THEN " substitutions." ELSE " substitution."], TRUE];
EXITS Quit => { TEditSelection.UnlockDocAndPSel[root]; RETURN };
}
};
DoOneSubs: PROC [info: EditToolPrivate.Info, root: TextNode.Ref, where: TextNode.RefTextNode, at, atEnd: Offset, subsinfo: EditToolPrivate.SubsInfo, viewer: ViewerClasses.Viewer] RETURNS [continue, bumpCount: BOOL, from, delta: Offset] = {
nameRope: Rope.ROPENIL;
len: Offset ← atEnd-at;
size: Offset ← TextEdit.Size[where];
initLooks: TextLooks.Looks;
DoFormat: PROC = {
IF NOT info.ignoreFormat AND where.formatName # subsinfo.sourceFormat THEN
TextEdit.ChangeFormat[where,subsinfo.sourceFormat,subsinfo.event,root];
};
DoStyle: PROC = {
IF NOT info.ignoreStyle AND NodeStyleOps.StyleNameForNode[where] # subsinfo.sourceStyle THEN {
subsinfo.styleRope ← IF subsinfo.sourceStyle=NIL THEN NIL ELSE Atom.GetPName[subsinfo.sourceStyle];
TextEdit.ChangeStyle[where,subsinfo.styleRope,subsinfo.event,root]
};
};
DoComment: PROC = {
IF NOT info.ignoreComment AND where.comment # subsinfo.sourceComment THEN
TextEdit.PutProp[where, $Comment,
IF subsinfo.sourceComment THEN NodeProps.true ELSE NodeProps.false, subsinfo.event, root];
};
initCap, initLower: BOOLFALSE;
IF info.forceInitCap AND at < size THEN {
s: NAT; c: CHAR;
[s,c] ← TextEdit.FetchChar[where, at];
initCap ← s=0 AND c IN ['A..'Z];
initLower ← s=0 AND c IN ['a..'z];
};
IF info.interrupt^ THEN { continue ← FALSE; RETURN };
continue ← bumpCount ← TRUE; from ← atEnd; delta ← 0;
SELECT TRUE FROM
info.ignoreText AND NOT info.ignoreLooks => { -- looks only
IF info.searchWhere=EditToolPrivate.anywhere AND subsinfo.substitute THEN { -- need to extend to include entire run
[at,atEnd] ← EditToolPrivate.Extend[info,TRUE,subsinfo.searchLooks,where,at,atEnd,subsinfo.last,subsinfo.lastLen];
len ← atEnd-at
};
TextEdit.ChangeLooks[root,where,subsinfo.targetLooks,subsinfo.sourceLooks,at,len,subsinfo.event];
from ← at+len;
DoFormat; DoStyle; DoComment;
RETURN
};
info.ignoreLooks AND NOT info.ignoreText => initLooks ← IF at < size THEN
TextEdit.FetchLooks[where, at] ELSE TextLooks.noLooks;
ENDCASE;
IF NOT info.ignoreText OR NOT info.ignoreLooks THEN {
sLen: Offset ← subsinfo.sourceLen;
IF NOT info.literal THEN { -- treat source as pattern
end, dest: Offset ← at+len; -- insert after the text to be deleted
litstart: Offset ← 0;
InsertLit: PROC [after: Offset] = {
litlen: Offset ← after-litstart;
IF litlen <= 0 THEN RETURN;
[] ← TextEdit.ReplaceText[
destRoot: root,
sourceRoot: TextNode.Root[subsinfo.sourceNode],
dest: where,
destStart: dest,
destLen: 0,
source: subsinfo.sourceNode,
sourceStart: litstart,
sourceLen: litlen,
event: subsinfo.event
];
dest ← dest+litlen;
};
RopeReader.SetPosition[subsinfo.rdr, subsinfo.sourceNode.rope, 0];
DO -- read the source
char: CHAR ← RopeReader.Get[subsinfo.rdr ! RopeReader.ReadOffEnd => EXIT];
WasCharSetZero: PROC RETURNS [BOOLEAN] ~ {
IF subsinfo.sourceNode.hascharsets THEN RETURN [TextEdit.FetchChar[subsinfo.sourceNode, RopeReader.GetIndex[subsinfo.rdr]-1].charSet=0]
ELSE RETURN [TRUE]
};
SELECT char FROM
'' => IF WasCharSetZero[] THEN { -- treat next as literal
loc: Offset ← RopeReader.GetIndex[subsinfo.rdr]; -- after the quote
InsertLit[loc-1]; -- to take care of stuff up to the quote
litstart ← loc;
[] ← RopeReader.Get[subsinfo.rdr ! RopeReader.ReadOffEnd => EXIT]
};
'< => IF WasCharSetZero[] THEN { -- read name, insert value
loc: Offset ← RopeReader.GetIndex[subsinfo.rdr]; -- after the <
at, atEnd, after: Offset;
addLooks: TextLooks.Looks;
IF NOT info.ignoreLooks THEN -- read looks from the < at the start of the name
addLooks ← TextLooks.FetchLooks[subsinfo.sourceNode.runs, RopeReader.GetIndex[subsinfo.rdr]-1];
InsertLit[loc-1]; -- to take care of stuff up to the <
DO -- read to >
IF RopeReader.Get[subsinfo.rdr ! RopeReader.ReadOffEnd => EXIT]
= '> THEN EXIT;
ENDLOOP;
after ← RopeReader.GetIndex[subsinfo.rdr]; -- after the >
nameRope ← Rope.Substr[subsinfo.sourceNode.rope, loc, after-loc-1];
[at,atEnd] ← TextFind.NameLoc[info.finder,nameRope];
atEnd ← MIN[atEnd, TextEdit.Size[info.varNode]];
IF at < atEnd THEN {
len: Offset ← atEnd-at;
[] ← TextEdit.ReplaceText[
destRoot: root,
sourceRoot: TextNode.Root[info.varNode],
dest: where,
destStart: dest,
destLen: 0,
source: info.varNode,
sourceStart: at,
sourceLen: len,
event: subsinfo.event
];
IF NOT info.ignoreLooks THEN { -- change the looks for the subpattern
remLooks: TextLooks.Looks = TextFind.NameLooks[info.finder, nameRope];
TextEdit.ChangeLooks[root, where, remLooks, addLooks, dest, len, subsinfo.event]
};
dest ← dest+len;
};
litstart ← after;
LOOP
};
ENDCASE;
ENDLOOP;
InsertLit[subsinfo.sourceLen];
sLen ← dest-end; -- the length of the replacement text
TextEdit.DeleteText[root, where, at, len, subsinfo.event]
delete last to keep selection addrs correct --
}
ELSE {
[] ← TextEdit.ReplaceText[destRoot: root,
sourceRoot: TextNode.Root[subsinfo.sourceNode],
dest: where,
destStart: at,
destLen: len,
source: subsinfo.sourceNode,
sourceStart: 0,
sourceLen: subsinfo.sourceLen,
event: subsinfo.event];
};
IF initCap AND subsinfo.sourceLen > 0 THEN {
s: NAT; c: CHAR; [s,c] ← TextEdit.FetchChar[where, at];
IF s=0 AND c IN ['a..'z] THEN {
TextEdit.ChangeCaps[root,where,at,1,allCaps,subsinfo.event]
};
}
ELSE IF initLower AND subsinfo.sourceLen > 0 THEN {
s: NAT; c: CHAR; [s,c] ← TextEdit.FetchChar[where, at];
IF s=0 AND c IN ['A..'Z] THEN {
TextEdit.ChangeCaps[root,where,at,1,allLower,subsinfo.event];
};
};
IF info.ignoreLooks THEN -- just changing the text, so now restore the looks
TextEdit.SetLooks[root,where,initLooks,at,sLen,subsinfo.event];
from ← at+sLen; delta ← sLen-len;
};
DoFormat[]; DoStyle[]; DoComment[];
};
doItAtom: LIST OF REF = EditToolPrivate.Register[$DoIt, DoItOp];
DoIt: Buttons.ButtonProc = {
EditToolPrivate.DoButton[doItAtom]
};
DoItOp: TEditInput.CommandProc = { DoItCom[EditToolPrivate.mainToolInfo] };
DoItCom: PROC [info: EditToolPrivate.Info] = {
EditToolPrivate.FixPSel[];
IF info.doReplace THEN DoReplaceCom[info] ELSE EditToolPrivate.DoOpsCom[info]
};
tSel: PUBLIC TEditDocument.Selection ← NEW[TEditDocument.SelectionRec];
CheckForSubs: PROC [info: EditToolPrivate.Info, pSel: TEditDocument.Selection] RETURNS [ok: BOOL] = {
IF info.ignoreText AND info.ignoreLooks AND info.ignoreFormat
AND info.ignoreStyle AND info.ignoreComment THEN {
MessageWindow.Append["Pick one or more of text/looks/format/style/comment to replace.", TRUE];
MessageWindow.Blink[]; RETURN [FALSE]
};
IF NOT EditToolPrivate.CheckPSel[pSel] THEN RETURN [FALSE];
RETURN [TRUE]
};
DoReplaceCom: PROC [info: EditToolPrivate.Info] = {
subsinfo: EditToolPrivate.SubsInfo;
pSel: TEditDocument.Selection;
source: TextNode.RefTextNode ← EditToolBuilder.GetDataNode[info.sourceArg];
where: TextNode.RefTextNode;
root: TextNode.Ref;
at, atEnd, delta: TextNode.Offset;
TEditSelection.LockSel[primary, "DoReplaceCom"];
EditToolPrivate.FixPSel[];
pSel ← TEditOps.GetSelData[];
IF NOT TEditInputOps.CheckReadonly[pSel]
OR (root ← TEditSelection.SelectionRoot[pSel])=NIL
OR CheckForSubs[info,pSel]=FALSE
THEN { TEditSelection.UnlockSel[primary]; RETURN };
IF (where ← pSel.start.pos.node) # pSel.end.pos.node THEN {
MessageWindow.Append["Selection to replace must be within a single node.", TRUE];
MessageWindow.Blink[]; TEditSelection.UnlockSel[primary]; RETURN
};
at ← pSel.start.pos.where;
atEnd ← pSel.end.pos.where+1;
subsinfo ← NEW[EditToolPrivate.SubsInfoRec];
subsinfo.event ← TEditInput.CurrentEvent[];
subsinfo.substitute ← FALSE;
subsinfo.sourceLen ← TextEdit.Size[source];
IF source # NIL THEN {
subsinfo.sourceFormat ← source.formatName;
IF NOT info.ignoreStyle THEN subsinfo.sourceStyle ← NodeStyleOps.StyleNameForNode[source];
subsinfo.sourceComment ← source.comment;
subsinfo.sourceNode ← source;
};
subsinfo.rdr ← RopeReader.Create[];
IF info.ignoreText AND NOT info.ignoreLooks THEN { -- looks only
subsinfo.targetLooks ← EditToolBuilder.GetDataLooks[info.targetArg,"\"Search for\" field"];
subsinfo.sourceLooks ← EditToolBuilder.GetDataLooks[info.sourceArg,"\"Replace by\" field"];
};
tSel^ ← pSel^;
[] ← TEditLocks.Lock[root, "DoReplaceCom"];
TEditSelection.Deselect[]; -- remove the selection
delta ← DoOneSubs[info, root, where, at, atEnd, subsinfo, tSel.viewer].delta;
tSel.end.pos.where ← tSel.end.pos.where + delta;
IF tSel.start.pos.node = tSel.end.pos.node AND tSel.start.pos.where > tSel.end.pos.where THEN {
tSel.end.pos.where ← tSel.start.pos.where;
tSel.granularity ← point;
tSel.insertion ← before
};
TEditSelection.MakeSelection[new: tSel];
TEditRefresh.ScrollToEndOfSel[tSel.viewer, FALSE];
TEditSelection.UnlockDocAndPSel[root];
};
GetSelInitLooks: PROC [pSel: TEditDocument.Selection] RETURNS [TextLooks.Looks] = {
node: TextNode.RefTextNode ← pSel.start.pos.node;
loc: 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 [BOOL] = {
node: TextNode.RefTextNode ← pSel.start.pos.node;
loc: Offset ← pSel.start.pos.where;
s: NAT; c: CHAR;
IF node=NIL OR loc=TextNode.NodeItself OR loc=TextEdit.Size[node]
THEN RETURN [FALSE];
[s,c] ← TextEdit.FetchChar[node,loc];
RETURN [s=0 AND c IN ['A..'Z]]
};
IsSelInitLower: PROC [pSel: TEditDocument.Selection] RETURNS [BOOL] = {
node: TextNode.RefTextNode ← pSel.start.pos.node;
loc: Offset ← pSel.start.pos.where;
s: NAT; c: CHAR;
IF node=NIL OR loc=TextNode.NodeItself OR loc=TextEdit.Size[node]
THEN RETURN [FALSE];
[s,c] ← TextEdit.FetchChar[node,loc];
RETURN [s=0 AND c IN ['a..'z]]
};
doYesAtom: LIST OF REF = EditToolPrivate.Register[$DoYes,DoYesOp];
doYesBackAtom: LIST OF REF = EditToolPrivate.Register[$DoYesBack,DoYesBackOp];
DoYes: Buttons.ButtonProc = {
EditToolPrivate.DoButton[doYesAtom, doYesBackAtom, mouseButton=red]
};
DoYesOp: TEditInput.CommandProc = { DoYesCom[EditToolPrivate.mainToolInfo] };
DoYesCom: PROC [info: EditToolPrivate.Info] = {
DoItCom[info];
EditToolPrivate.Search[forwards,info];
};
DoYesBackOp: TEditInput.CommandProc = { DoYesBackCom[EditToolPrivate.mainToolInfo] };
DoYesBackCom: PROC [info: EditToolPrivate.Info] = {
DoItCom[info];
EditToolPrivate.Search[backwards,info];
};
doNoAtom: LIST OF REF = EditToolPrivate.Register[$DoNo,DoNoOp];
doNoBackAtom: LIST OF REF = EditToolPrivate.Register[$DoNoBack,DoNoBackOp];
DoNo: Buttons.ButtonProc = {
EditToolPrivate.DoButton[doNoAtom, doNoBackAtom, mouseButton=red]
};
DoNoOp: TEditInput.CommandProc = { DoNoCom[EditToolPrivate.mainToolInfo] };
DoNoCom: PROC [info: EditToolPrivate.Info] = {
EditToolPrivate.FixPSel[];
EditToolPrivate.Search[forwards,info];
};
DoNoBackOp: TEditInput.CommandProc = {
DoNoBackCom[EditToolPrivate.mainToolInfo];
};
DoNoBackCom: PROC [info: EditToolPrivate.Info] = {
EditToolPrivate.FixPSel[];
EditToolPrivate.Search[backwards,info];
};
doCountAtom: LIST OF REF = EditToolPrivate.Register[$DoCount,DoCountOp];
DoCount: Buttons.ButtonProc = {
EditToolPrivate.DoButton[doCountAtom]
};
DoCountOp: TEditInput.CommandProc = { DoCountCom[EditToolPrivate.mainToolInfo] };
DoCountCom: PROC [info: EditToolPrivate.Info] = { DoSubstituteCom[info,TRUE] };
withinSelRope: Rope.ROPE = "Within Selection Only";
afterSelRope: Rope.ROPE = "After Selection Only";
entireDocRope: Rope.ROPE = "In Entire Document";
withinSelAtom: LIST OF REF = EditToolPrivate.Register[$SubstituteInSel,SubsWithinOp];
afterSelAtom: LIST OF REF = EditToolPrivate.Register[$SubstituteAfterSel,SubsAfterOp];
entireDocAtom: LIST OF REF = EditToolPrivate.Register[$SubstituteInEntireDoc,SubsEntireDocOp];
BuildSubstituteEntry: PUBLIC PROC [info: EditToolPrivate.Info] = {
info.subsRange ← EditToolPrivate.entireDoc;
[info.subsRangeLabel,] ← EditToolBuilder.BuildTriple[info.layout, SubsRangeButton, info.subsRange, withinSelRope, afterSelRope, entireDocRope, info];
};
SubsRangeButton: Buttons.ButtonProc = {
EditToolPrivate.CycleTriple[EditToolPrivate.mainToolInfo.subsRange, withinSelAtom, afterSelAtom, entireDocAtom]
};
SubsWithinOp: TEditInput.CommandProc = { SubsWithin[EditToolPrivate.mainToolInfo] };
SubsWithin: PROC [info: EditToolPrivate.Info] = {
info.subsRange ← EditToolPrivate.withinSel;
Labels.Set[info.subsRangeLabel,withinSelRope]
};
SubsAfterOp: TEditInput.CommandProc = { SubsAfter[EditToolPrivate.mainToolInfo] };
SubsAfter: PROC [info: EditToolPrivate.Info] = {
info.subsRange ← EditToolPrivate.afterSel;
Labels.Set[info.subsRangeLabel,afterSelRope]
};
SubsEntireDocOp: TEditInput.CommandProc = { SubsEntireDoc[EditToolPrivate.mainToolInfo] };
SubsEntireDoc: PROC [info: EditToolPrivate.Info] = {
info.subsRange ← EditToolPrivate.entireDoc;
Labels.Set[info.subsRangeLabel,entireDocRope]
};
GetLooksAndPatternInfo: PUBLIC PROC [pattern: TextNode.RefTextNode, info: EditToolPrivate.Info] RETURNS [pat: TextNode.RefTextNode, lit: BOOL, searchLooks: TextLooks.Looks, format: ATOM, style: ATOM, commentControl: TreeFind.CommentControl] = {
pat ← pattern; lit ← info.literal; searchLooks ← TextLooks.noLooks;
IF info.ignoreText AND NOT info.ignoreLooks THEN { -- make a phony search pattern and get the looks
size: Offset = TextEdit.Size[pattern];
searchLooks ← IF size=0 THEN TextLooks.noLooks
ELSE TextEdit.FetchLooks[pattern,0];
FOR i: Offset IN [1..size) DO
IF TextEdit.FetchLooks[pattern,i]#searchLooks THEN {
MessageWindow.Append["Search pattern does not have uniform looks.",TRUE];
MessageWindow.Append[" Using looks from first char."];
MessageWindow.Blink[]; EXIT
};
ENDLOOP;
lit ← FALSE;
pat ← TextEdit.FromRope["#*"];
TextEdit.SetLooks[NIL,pat,searchLooks]
};
format ← IF pattern # NIL THEN pat.formatName ELSE NIL;
style ← IF info.ignoreStyle THEN NIL ELSE NodeStyleOps.StyleNameForNode[pattern];
commentControl ← IF info.ignoreComment THEN includeComments
ELSE IF pattern # NIL AND pattern.comment THEN commentsOnly
ELSE excludeComments;
};
END.