<> <> <> <> <> <<>> DIRECTORY Buttons USING [Button, ButtonProc, SetDisplayStyle], EditToolBuilder USING [BuildButton, BuildDataFieldPair, BuildLabel, BuildPair, BuildTriple, DataFieldButton, GetDataNode, HGap, ToMiddle, ToNext], EditToolPrivate USING [anywhere, ChangeState, CycleTriple, DoButton, FixPSel, Info, mainToolInfo, nodes, Register, ReportPatternError, SavePSel, words], EditSpanSupport USING [CopySpan], Labels USING [Set], Menus USING [MenuProc], MessageWindow USING [Append, Blink, Clear], NodeProps USING [DoSpecs], Rope USING [Concat, ROPE], RunReader USING [Backwards, FreeRunReader, Get, GetRunReader, Ref, SetPosition], TEditDocument USING [Selection], TEditInput USING [CloseEvent, CommandProc], TEditOps USING [GetSelData, SetTextContents], TEditSelection USING [LockSel, UnlockSel], TextEdit USING [ChangeFormat, PutProp, Size], TextFind USING [Finder, MalformedPattern], TextLooks USING [Looks, noLooks, Runs], TextLooksSupport USING [LooksAND], TextNode USING [Ref, RefTextNode, Root, MakeNodeSpan], TiogaOps USING [CreateGeneralPattern, Finder, MalformedPattern, Pattern, Ref, SearchDir, SelectionSearch], TreeFind USING [Finder], ViewerClasses USING [Viewer], ViewerOps USING [AddProp, BlinkDisplay, CreateViewer], ViewerSpecs USING [openRightWidth], ViewerTools USING [InhibitUserEdits]; EditToolSearchImpl: CEDAR PROGRAM IMPORTS Buttons, EditSpanSupport, EditToolBuilder, EditToolPrivate, Labels, MessageWindow, NodeProps, Rope, RunReader, TEditOps, TextEdit, TextFind, TextLooksSupport, TextNode, TEditInput, TEditSelection, TiogaOps, ViewerOps, ViewerSpecs, ViewerTools EXPORTS EditToolPrivate = { OPEN EditToolPrivate, EditToolBuilder; searchForwardAtom: LIST OF REF = Register[$SearchForward,SearchForwardOp]; SearchForwardOp: TEditInput.CommandProc = { Search[forwards,mainToolInfo] }; searchAnywhereAtom: LIST OF REF = Register[$SearchAnywhere,SearchAnywhereOp]; SearchAnywhereOp: TEditInput.CommandProc = { Search[anywhere,mainToolInfo] }; searchBackwardsAtom: LIST OF REF = Register[$SearchBackwards,SearchBackwardsOp]; SearchBackwardsOp: TEditInput.CommandProc = { Search[backwards,mainToolInfo] }; DoSearchMenuButton: PUBLIC Menus.MenuProc = { SELECT mouseButton FROM red => DoButton[searchForwardAtom]; yellow => DoButton[searchAnywhereAtom]; blue => DoButton[searchBackwardsAtom]; ENDCASE => ERROR; }; CheckPSel: PUBLIC PROC [pSel: TEditDocument.Selection, typescriptOK: BOOL _ TRUE] RETURNS [ok: BOOL] = { OPEN MessageWindow; IF pSel#NIL AND pSel.viewer#NIL AND (pSel.viewer.class.flavor=$Text OR (typescriptOK AND pSel.viewer.class.flavor=$Typescript)) THEN RETURN [TRUE]; Append["Please make a text selection.",TRUE]; Blink[]; RETURN [FALSE]; }; GetPatternNode: PUBLIC PROC [info: Info] RETURNS [pattern: TextNode.RefTextNode] = { OPEN info; IF ((pattern _ GetDataNode[targetArg])=NIL OR TextEdit.Size[pattern]=0) AND (~ignoreLooks OR ~ignoreText) THEN { OPEN MessageWindow; Append["Please enter a search pattern in the \"target\" field.",TRUE]; Blink[]; RETURN [NIL]; }; }; Search: PUBLIC PROC [whichDir: TiogaOps.SearchDir, info: Info] = TRUSTED { IF ~TrySearch[whichDir, info] THEN { MessageWindow.Append["Not found.", TRUE]; ViewerOps.BlinkDisplay[]; } ELSE MessageWindow.Clear[]; }; TrySearch: PUBLIC PROC [whichDir: TiogaOps.SearchDir, info: Info] RETURNS [found: BOOL] = { OPEN info; pattern: TextNode.RefTextNode; pSel: TEditDocument.Selection; searchPattern: TiogaOps.Pattern; IF ignoreText AND ignoreLooks AND ignoreFormat AND ignoreStyle AND ignoreComment THEN { OPEN MessageWindow; Append["Pick one or more of text/looks/format/style/comment to match.", TRUE]; Blink[]; RETURN; }; TEditSelection.LockSel[primary, "TrySearch"]; { ENABLE UNWIND => TEditSelection.UnlockSel[primary]; FixPSel; pSel _ TEditOps.GetSelData[]; IF ~CheckPSel[pSel] OR (pattern _ GetPatternNode[info])=NIL THEN { TEditSelection.UnlockSel[primary]; RETURN; }; TRUSTED { searchPattern _ TiogaOps.CreateGeneralPattern[LOOPHOLE[pattern], ~ignoreText, ~ignoreLooks, ~ignoreFormat, ~ignoreStyle, ~ignoreComment, ~ignoreCase, literal, searchWhere=words, ~looksExact, searchWhere=nodes ! TextFind.MalformedPattern => { ReportPatternError[ec]; GOTO Quit }; TiogaOps.MalformedPattern => { <> msg: Rope.ROPE = SELECT ec FROM toobig => "Pattern too long", endquote => "Pattern ends with quote (')", endtilda => "Pattern ends with tilde (~)", boundary => "Pattern has | inside rather than at beginning or end", missingNameEnd => "Pattern has < without matching >", unmatchedNameEnd => "Pattern has > without previous <", ENDCASE => "Pattern error (consult CedarSupport^)"; MessageWindow.Append[msg, TRUE]; MessageWindow.Blink[]; GOTO Quit}; ]}; TRUSTED { <> finder _ LOOPHOLE[searchPattern.finder, TreeFind.Finder]; }; found _ TiogaOps.SelectionSearch[searchPattern, whichDir, interrupt]; IF NOT found THEN GOTO Quit; TEditInput.CloseEvent[]; IF info.varNode # NIL THEN info.varNode.next _ NIL; <> info.varNode _ IF info.literal THEN NIL ELSE EditSpanSupport.CopySpan[TextNode.MakeNodeSpan[pSel.start.pos.node, pSel.start.pos.node]].start.node; <> EXITS Quit => {}; }; TEditSelection.UnlockSel[primary]; }; Extend: PUBLIC PROC [info: Info, forward: BOOL, searchLooks: TextLooks.Looks, where: TextNode.RefTextNode, at, atEnd: INT, last: TextNode.Ref _ NIL, lastLen: INT _ INT.LAST] RETURNS [newAt, newAtEnd: INT] = { OPEN info; runrdr: RunReader.Ref _ RunReader.GetRunReader[]; looks: TextLooks.Looks; runLen: INT; runs: TextLooks.Runs _ where.runs; IF forward THEN { -- extend toward end of node size: INT _ 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]; }; BuildSearchButtons: PUBLIC PROC [info: Info] = { BuildSearchChoiceEntries[info]; ToNext[info.layout]; BuildCaseEntry[info]; ToMiddle[info.layout]; BuildWhereEntry[info]; ToNext[info.layout]; BuildLitOrPatternEntry[info]; ToMiddle[info.layout]; BuildLooksMatchEntry[info]; }; equalLooks: Rope.ROPE = "Equal as Looks Test"; subsetLooks: Rope.ROPE = "Subset as Looks Test"; equalLooksAtom: LIST OF REF = Register[$EqualLooksTest,EqualLooksTestOp]; subsetLooksAtom: LIST OF REF = Register[$SubsetLooksTest,SubsetLooksTestOp]; LooksMatchButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.looksExact, equalLooksAtom,subsetLooksAtom]; }; EqualLooksTestOp: TEditInput.CommandProc = { EqualLooksTest[mainToolInfo]; }; EqualLooksTest: PROC [info: Info] = { OPEN info; looksExact _ TRUE; Labels.Set[looksMatchLabel,equalLooks]; }; SubsetLooksTestOp: TEditInput.CommandProc = { SubsetLooksTest[mainToolInfo]; }; SubsetLooksTest: PROC [info: Info] = { OPEN info; looksExact _ FALSE; Labels.Set[looksMatchLabel,subsetLooks]; }; BuildLooksMatchEntry: PROC [info: Info] = { OPEN info; looksExact _ FALSE; [looksMatchLabel,] _ BuildPair[layout,LooksMatchButton,looksExact,equalLooks,subsetLooks,info]; }; <<-------------------------->> BuildSearchChoiceEntries: PROC [info: Info] = { OPEN info; BuildEntry: PROC [name: Rope.ROPE, proc: Buttons.ButtonProc, state: BOOL] RETURNS [new: Buttons.Button] = { new _ BuildButton[layout, name, proc, info, FALSE, FALSE]; ViewerOps.AddProp[new, $DisableMBFeedback, $DisableMBFeedback]; SetButton[new, state, FALSE]; }; [] _ BuildLabel[layout, "Pick one or more: "]; HGap[layout]; textButton _ BuildEntry["Text", MatchTextButton, ignoreText _ FALSE]; looksButton _ BuildEntry["Looks", MatchLooksButton, ignoreLooks _ TRUE]; formatButton _ BuildEntry["Format", MatchFormatButton, ignoreFormat _ TRUE]; styleButton _ BuildEntry["Style", MatchStyleButton, ignoreStyle _ TRUE]; commentButton _ BuildEntry["Comment", MatchCommentButton, ignoreComment _ TRUE]; }; SetButton: PROC [viewer: Buttons.Button, ignore: BOOL, paint: BOOL _ TRUE] = { Buttons.SetDisplayStyle[viewer, IF ignore THEN $BlackOnWhite ELSE $WhiteOnBlack, paint]; }; <<-------------------------->> matchTextAtom: LIST OF REF = Register[$MatchText,MatchTextOp]; ignoreTextAtom: LIST OF REF = Register[$IgnoreText,IgnoreTextOp]; MatchTextButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.ignoreText, ignoreTextAtom, matchTextAtom]; }; MatchTextOp: TEditInput.CommandProc = { MatchText[mainToolInfo] }; MatchText: PROC [info: Info] = { OPEN info; SetButton[textButton, ignoreText _ FALSE]; }; IgnoreTextOp: TEditInput.CommandProc = { IgnoreText[mainToolInfo] }; IgnoreText: PROC [info: Info] = { OPEN info; SetButton[textButton, ignoreText _ TRUE]; }; <<-------------------------->> matchLooksAtom: LIST OF REF = Register[$MatchLooks,MatchLooksOp]; ignoreLooksAtom: LIST OF REF = Register[$IgnoreLooks,IgnoreLooksOp]; MatchLooksButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.ignoreLooks, ignoreLooksAtom, matchLooksAtom]; }; MatchLooksOp: TEditInput.CommandProc = { MatchLooks[mainToolInfo]; }; MatchLooks: PROC [info: Info] = { OPEN info; SetButton[looksButton, ignoreLooks _ FALSE]; }; IgnoreLooksOp: TEditInput.CommandProc = { IgnoreLooks[mainToolInfo]; }; IgnoreLooks: PROC [info: Info] = { OPEN info; SetButton[looksButton, ignoreLooks _ TRUE]; }; <<-------------------------->> matchFormatAtom: LIST OF REF = Register[$MatchFormat,MatchFormatOp]; ignoreFormatAtom: LIST OF REF = Register[$IgnoreFormat,IgnoreFormatOp]; MatchFormatButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.ignoreFormat, ignoreFormatAtom,matchFormatAtom] }; MatchFormatOp: TEditInput.CommandProc = { MatchFormat[mainToolInfo]; }; MatchFormat: PROC [info: Info] = { OPEN info; SetButton[formatButton, ignoreFormat _ FALSE]; }; IgnoreFormatOp: TEditInput.CommandProc = { IgnoreFormat[mainToolInfo]; }; IgnoreFormat: PROC [info: Info] = { OPEN info; SetButton[formatButton, ignoreFormat _ TRUE]; }; matchStyleAtom: LIST OF REF = Register[$MatchStyle,MatchStyleOp]; ignoreStyleAtom: LIST OF REF = Register[$IgnoreStyle,IgnoreStyleOp]; MatchStyleButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.ignoreStyle, ignoreStyleAtom,matchStyleAtom] }; MatchStyleOp: TEditInput.CommandProc = { MatchStyle[mainToolInfo] }; MatchStyle: PROC [info: Info] = { OPEN info; SetButton[styleButton, ignoreStyle _ FALSE] }; IgnoreStyleOp: TEditInput.CommandProc = { IgnoreStyle[mainToolInfo] }; IgnoreStyle: PROC [info: Info] = { OPEN info; SetButton[styleButton, ignoreStyle _ TRUE] }; matchCommentAtom: LIST OF REF = Register[$MatchComment,MatchCommentOp]; ignoreCommentAtom: LIST OF REF = Register[$IgnoreComment,IgnoreCommentOp]; MatchCommentButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.ignoreComment, ignoreCommentAtom,matchCommentAtom] }; MatchCommentOp: TEditInput.CommandProc = { MatchComment[mainToolInfo] }; MatchComment: PROC [info: Info] = { OPEN info; SetButton[commentButton, ignoreComment _ FALSE] }; IgnoreCommentOp: TEditInput.CommandProc = { IgnoreComment[mainToolInfo] }; IgnoreComment: PROC [info: Info] = { OPEN info; SetButton[commentButton, ignoreComment _ TRUE] }; matchingLiterally: Rope.ROPE = "Match Literally"; matchingAsPattern: Rope.ROPE = "Match as Pattern"; matchLitAtom: LIST OF REF = Register[$MatchLiterally,MatchLitOp]; matchPatternAtom: LIST OF REF = Register[$MatchPattern,MatchPatternOp]; LiteralButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.literal, matchLitAtom,matchPatternAtom]; }; MatchLitOp: TEditInput.CommandProc = { MatchLit[mainToolInfo]; }; MatchLit: PROC [info: Info] = { OPEN info; literal _ TRUE; Labels.Set[literalLabel,matchingLiterally]; }; MatchPatternOp: TEditInput.CommandProc = { MatchPattern[mainToolInfo]; }; MatchPattern: PROC [info: Info] = { OPEN info; literal _ FALSE; Labels.Set[literalLabel,matchingAsPattern]; }; BuildLitOrPatternEntry: PROC [info: Info] = { OPEN info; literal _ TRUE; [literalLabel,] _ BuildPair[layout,LiteralButton,literal,matchingLiterally,matchingAsPattern,info]; }; numDocRopes: NAT = 18; docRopes: REF ARRAY [0..numDocRopes) OF Rope.ROPE _ NEW[ARRAY [0..numDocRopes) OF Rope.ROPE]; BuildPatternDocEntry: PUBLIC PROC [info: Info] = { OPEN info.layout; lines: CARDINAL = 25; rope: Rope.ROPE; arg: ViewerClasses.Viewer _ ViewerOps.CreateViewer[flavor: $Text, info: [parent: container, border: FALSE, scrollable: TRUE, wx: entryLeft, wy: heightSoFar, ww: ViewerSpecs.openRightWidth-entryLeft-5, wh: entryHeight*lines], paint: FALSE]; heightSoFar _ heightSoFar + entryHeight*lines; FOR i:NAT IN [0..numDocRopes) DO rope _ Rope.Concat[rope, docRopes[i]]; ENDLOOP; TEditOps.SetTextContents[arg, rope]; }; numMiniDocRopes: NAT = 4; miniDocRopes: REF ARRAY [0..numMiniDocRopes) OF Rope.ROPE _ NEW[ARRAY [0..numMiniDocRopes) OF Rope.ROPE]; miniDocStyle: Rope.ROPE ~ " BeginStyle (mini) \"for small comment\" { \"TimesRoman\" family 8 pt size 9 pt leading 10 pt topIndent 0 pt bottomIndent 0 leftIndent 5 pt rightIndent } StyleRule EndStyle "; BuildMiniPatternDocEntry: PUBLIC PROC [info: Info] = { OPEN info.layout; lines: CARDINAL = 3; rope: Rope.ROPE; where: TextNode.RefTextNode; height: CARDINAL = numMiniDocRopes*10+3; arg: ViewerClasses.Viewer _ ViewerOps.CreateViewer[flavor: $Text, info: [parent: container, wx: entryLeft, wy: heightSoFar, ww: ViewerSpecs.openRightWidth-entryLeft-5, wh: height, border: FALSE, scrollable: FALSE], paint: FALSE]; heightSoFar _ heightSoFar + height; FOR i:NAT IN [0..numMiniDocRopes) DO rope _ Rope.Concat[rope, miniDocRopes[i]]; ENDLOOP; TEditOps.SetTextContents[arg, rope]; where _ GetDataNode[arg]; TextEdit.PutProp[node: TextNode.Root[where], name: $StyleDef, value: NodeProps.DoSpecs[$StyleDef, miniDocStyle]]; TextEdit.ChangeFormat[where, $mini]; ViewerTools.InhibitUserEdits[arg]; }; <<-------------------------->> ignoringCase: Rope.ROPE = "Ignore Case"; matchingCase: Rope.ROPE = "Match Case"; ignoreCaseAtom: LIST OF REF = Register[$IgnoreCase,IgnoreCaseOp]; matchCaseAtom: LIST OF REF = Register[$MatchCase,MatchCaseOp]; CaseButton: Buttons.ButtonProc = { ChangeState[mainToolInfo.ignoreCase, ignoreCaseAtom,matchCaseAtom]; }; IgnoreCaseOp: TEditInput.CommandProc = { IgnoreCase[mainToolInfo]; }; IgnoreCase: PROC [info: Info] = { OPEN info; ignoreCase _ TRUE; Labels.Set[caseLabel,ignoringCase]; }; MatchCaseOp: TEditInput.CommandProc = { MatchCase[mainToolInfo]; }; MatchCase: PROC [info: Info] = { OPEN info; ignoreCase _ FALSE; Labels.Set[caseLabel,matchingCase]; }; BuildCaseEntry: PROC [info: Info] = { OPEN info; ignoreCase _ FALSE; [caseLabel,] _ BuildPair[layout,CaseButton,ignoreCase,ignoringCase,matchingCase,info]; }; matchingWords: Rope.ROPE = "Match Words Only"; matchingAnywhere: Rope.ROPE = "Match Anywhere"; matchingNodes: Rope.ROPE = "Match Entire Nodes Only"; matchWordsAtom: LIST OF REF = Register[$MatchWords,MatchWordsOp]; matchAnywhereAtom: LIST OF REF = Register[$MatchAnywhere,MatchAnywhereOp]; matchNodesAtom: LIST OF REF = Register[$MatchNodes,MatchNodesOp]; WhereButton: Buttons.ButtonProc = { CycleTriple[mainToolInfo.searchWhere, matchAnywhereAtom,matchWordsAtom,matchNodesAtom] }; MatchWordsOp: TEditInput.CommandProc = { MatchWords[mainToolInfo] }; MatchWords: PROC [info: Info] = { OPEN info; searchWhere _ words; Labels.Set[wordLabel,matchingWords]; }; MatchNodesOp: TEditInput.CommandProc = { MatchNodes[mainToolInfo] }; MatchNodes: PROC [info: Info] = { OPEN info; searchWhere _ nodes; Labels.Set[wordLabel,matchingNodes]; }; MatchAnywhereOp: TEditInput.CommandProc = { MatchAnywhere[mainToolInfo]; }; MatchAnywhere: PROC [info: Info] = { OPEN info; searchWhere _ anywhere; Labels.Set[wordLabel,matchingAnywhere]; }; BuildWhereEntry: PROC [info: Info] = { OPEN info; searchWhere _ anywhere; [wordLabel,] _ BuildTriple[layout, WhereButton, searchWhere, matchingAnywhere, matchingWords, matchingNodes, info]; }; <<-------------------------->> targetArgAtom: LIST OF REF = Register[$SearchFor,TargetArgOp]; clearTargetArgAtom: LIST OF REF = Register[$ClearSearchFor,ClearTargetArgOp]; TargetButton: Buttons.ButtonProc = { DoButton[targetArgAtom, clearTargetArgAtom, mouseButton=red]; }; TargetArgOp: TEditInput.CommandProc = { TargetArg[mainToolInfo]; }; TargetArg: PROC [info: Info] = { SavePSel; DataFieldButton[info.targetArg,FALSE]; }; ClearTargetArgOp: TEditInput.CommandProc = { ClearTargetArg[mainToolInfo]; }; ClearTargetArg: PROC [info: Info] = { SavePSel; DataFieldButton[info.targetArg,TRUE]; }; BuildTargetEntry: PUBLIC PROC [info: Info] = { OPEN info; [,targetArg] _ BuildDataFieldPair[layout, "Target:", TargetButton, info, 2]; }; <<-------------------------->> docRopes[0] _ "The following special symbols may be used in search patterns: "; docRopes[1] _ " ' Match the next character in the pattern exactly. "; docRopes[2] _ " ~ Match any character except the next one in the pattern. "; docRopes[3] _ " # Match any single character. "; docRopes[4] _ " * Match any sequence of characters. "; docRopes[5] _ " @ Match any single alphanumeric character (letter or digit). "; docRopes[6] _ " & Match any sequence of alphanumeric characters. "; docRopes[7] _ " ~@ Match any single non-alphanumeric character. "; docRopes[8] _ " ~& Match any sequence of non-alphanumeric characters. "; docRopes[9] _ " % Match any single blank character. "; docRopes[10] _ " $ Match any sequence of blank characters. "; docRopes[11] _ " ~% Match any single non-blank character. "; docRopes[12] _ " ~$ Match any sequence of non-blank characters. "; docRopes[13] _ " | Match start or end of node. "; docRopes[14] _ " { Mark start of resulting selection. "; docRopes[15] _ " } Mark end of resulting selection. "; docRopes[16] _ " < Mark start of named subpattern. "; docRopes[17] _ " > Mark end of named subpattern. "; miniDocRopes[0] _ "single character: # any; @ alpha; ~@ nonalpha; % blank; ~% nonblank; "; miniDocRopes[1] _ "min sequence: * any; & alpha; ~& nonalpha; $ blank; ~$ nonblank; "; miniDocRopes[2] _ "max sequence: ** any; && alpha; ~&~& nonalpha; $$ blank; ~$~$ nonblank; "; miniDocRopes[3] _ "miscellaneous: 'x = use \"x\"; ~x = not x; { } bounds selection; < > named subpattern "; }...