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