<> <> <> <> <> <<>> 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: BOOL _ FALSE] = { 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; <> info.varNode _ IF info.literal THEN NIL ELSE EditSpanSupport.CopySpan[TextNode.MakeNodeSpan[where, where]].start.node; <> [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 { <> 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]; <> 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]; <> 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.ROPE _ NIL; 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: BOOL _ FALSE; 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] <> } 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.