-- 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]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]; }. ..