<<>> <> <> <> <> <> DIRECTORY MessageWindow, Rope, TEditDocument, TEditInput, TEditInputExtras, TEditInputOps, TEditOps, TEditRefresh, TEditSelection, TextEdit, TextNode, Tioga, TiogaFind, ViewerClasses, ViewerOps; KeyboardScan: CEDAR PROGRAM IMPORTS MessageWindow, Rope, TEditInput, TEditInputExtras, TEditOps, TEditRefresh, TEditSelection, TextEdit, TextNode, TiogaFind, ViewerOps ={ ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; Location: TYPE = TextNode.Location; Selection: TYPE = TEditDocument.Selection; SelectionId: TYPE ~ TEditDocument.SelectionId; record: BOOL ¬ TRUE; ScanNull: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = {RETURN}; ScanStart: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = { NoteStart: PROC [tdd: TEditDocument.TEditDocumentData, tSel: Selection] = { TEditSelection.Copy[source: tSel, dest: startSel]; RETURN}; TEditSelection.CallWithSelAndDocAndTddLocks[viewer, primary, NoteStart]; target ¬ NIL; failTailLen ¬ 0; findWhere ¬ forwards; caseMatters ¬ FALSE; RETURN}; ScanUpper: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = {case ¬ Upper}; ScanLower: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = {case ¬ Lower}; ScanForward: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = {findWhere ¬ forwards}; ScanBackward: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = {findWhere ¬ backwards}; ScanReverse: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = {SELECT findWhere FROM forwards => {findWhere ¬ backwards; MessageWindow.Append["Leaping backwards", TRUE]}; backwards => {findWhere ¬ forwards; MessageWindow.Append["Leaping forwards", TRUE]}; ENDCASE => ERROR}; ScanToggleCase: PROC [viewer: Viewer ¬ NIL] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] --TEditInput.CommandProc-- = { caseMatters ¬ NOT caseMatters; MessageWindow.Append[IF caseMatters THEN "Case matters" ELSE "Case doesn't matter", TRUE]}; Case: TYPE = {Upper, Lower}; case: Case; startSel: TEditDocument.Selection ~ TEditSelection.Create[]; findWhere: TEditSelection.FindWhere; caseMatters: BOOL; target, lastTarget: ROPE ¬ NIL; failTailLen: NAT ¬ 0; ScanTo: PROC [data: REF ANY, viewer: Viewer, param: REF ANY] RETURNS [recordAtom: BOOL, quit: BOOL] --TEditInputExtras.CommandClosureProc-- ~ { parmRope: ROPE ~ NARROW[param]; raw: CHAR ~ IF parmRope.Length = 1 THEN parmRope.Fetch[0] ELSE ERROR; goal: CHAR ~ IF raw NOT IN ['A .. 'Z] THEN raw ELSE SELECT case FROM Upper => raw, Lower => raw - 'A + 'a, ENDCASE => ERROR; newTarget: ROPE ~ target.Concat[Rope.FromChar[goal]]; target ¬ lastTarget ¬ newTarget; IF failTailLen>0 THEN {failTailLen ¬ failTailLen+1; Bitch[target]} ELSE IF NOT FindRope[viewer, newTarget, FALSE, findWhere, FALSE, FALSE, primary, caseMatters] THEN failTailLen ¬ 1; RETURN [TRUE, TRUE]}; ScanRepeat: PROC [viewer: Viewer] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] = { target ¬ lastTarget; IF failTailLen>0 THEN Bitch[target] ELSE IF NOT FindRope[viewer, target, TRUE, findWhere, FALSE, FALSE, primary, caseMatters] THEN failTailLen ¬ 1; RETURN}; ScanShorten: PROC [viewer: Viewer] RETURNS [recordAtom: BOOL ¬ record, quit: BOOL ¬ TRUE] = { oldLen: INT ~ target.Length; IF oldLen>0 THEN target ¬ lastTarget ¬ target.Substr[len: oldLen-1]; IF oldLen<=1 THEN { MessageWindow.Append["Empty search.", TRUE]; ViewerOps.BlinkDisplay[]; RETURN}; IF failTailLen>0 THEN failTailLen ¬ failTailLen-1; IF failTailLen>0 THEN Bitch[target] ELSE IF NOT FindRope[viewer, target, FALSE, findWhere, FALSE, FALSE, primary, caseMatters] THEN failTailLen ¬ 1; RETURN}; FindRope: PUBLIC PROC [viewer: Viewer, rope: ROPE, moveOn: BOOL, findWhere: TEditSelection.FindWhere ¬ anywhere, def, word: BOOL ¬ FALSE, id: SelectionId ¬ primary, case: BOOL ¬ TRUE -- case => case of characters is significant --] RETURNS [found: BOOL] = { IF (found ¬ DoFind[viewer, rope, moveOn, findWhere, def, word, id, case]) THEN MessageWindow.Clear[] ELSE Bitch[rope]; RETURN}; Bitch: PROC [target: ROPE] ~ { IF Rope.Size[target]>50 THEN target ¬ Rope.Concat[Rope.Substr[target, 0, 47], "..."]; MessageWindow.Append[Rope.Concat[target, " not found."], TRUE]; ViewerOps.BlinkDisplay[]; RETURN}; NormalizeLoc: PROC [loc: Location, dw: INT] RETURNS [Location] ~ { IF loc.where=TextNode.NodeItself THEN loc.where ¬ 0; loc.where ¬ MIN[loc.node.rope.Length, MAX[INT[0], loc.where + dw]]; RETURN [loc]}; DoFind: PUBLIC PROC [viewer: Viewer, rope: ROPE, moveOn: BOOL, findWhere: TEditSelection.FindWhere ¬ anywhere, def, word: BOOL ¬ FALSE, id: SelectionId ¬ primary, case: BOOL ¬ TRUE -- case => case of characters is significant --] RETURNS [found: BOOL] = { where: TextNode.RefTextNode ¬ NIL; first, last: Location; at, atEnd: TextNode.Offset ¬ 0; mv: INT ~ IF moveOn THEN 1 ELSE 0; targLen: INT ~ rope.Length; inViewer: BOOL ¬ FALSE; targetNode: Tioga.Node; DoFindIt: PROC [tdd: TEditDocument.TEditDocumentData, tSel: Selection] = { interrupt: REF BOOL ~ TEditInput.interrupt; FirstLoc: PROC [root: Tioga.Node] RETURNS [Location] ~ { RETURN[[TextNode.FirstChild[root], 0]] }; LastLoc: PROC [root: Tioga.Node] RETURNS [Location] ~ { RETURN[TextNode.LastLocWithin[root]] }; Forward: PROC = { first ¬ NormalizeLoc[tSel.start.pos, mv]; [where, at, atEnd] ¬ TiogaFind.LiteralSearch[direction: forward, target: targetNode, case: FALSE, loc1: first, loc2: LastLoc[tdd.text], interrupt: interrupt]; }; Backward: PROC = { first ¬ NormalizeLoc[tSel.start.pos, targLen-mv]; [where, at, atEnd] ¬ TiogaFind.LiteralSearch[direction: backward, target: targetNode, case: FALSE, loc1: FirstLoc[tdd.text], loc2: first, interrupt: interrupt] }; FromStart: PROC = { last ¬ NormalizeLoc[tSel.start.pos, targLen+mv]; [where, at, atEnd] ¬ TiogaFind.LiteralSearch[direction: forward, target: targetNode, case: FALSE, loc1: FirstLoc[tdd.text], loc2: LastLoc[tdd.text], interrupt: interrupt] }; FromEnd: PROC = { first ¬ [TextNode.LastWithin[tdd.text], 0]; first.where ¬ first.node.rope.Length; last ¬ NormalizeLoc[tSel.start.pos, -mv]; [where, at, atEnd] ¬ TiogaFind.LiteralSearch[direction: backward, target: targetNode, case: FALSE, loc1: FirstLoc[tdd.text], loc2: LastLoc[tdd.text], interrupt: interrupt] }; interrupt­ ¬ FALSE; IF NOT (inViewer ¬ tSel.viewer=viewer) THEN RETURN; SELECT findWhere FROM backwards => { Backward[]; IF where = NIL THEN FromEnd[] }; forwards => { Forward[]; IF where = NIL THEN FromStart[] }; ENDCASE => ERROR; IF where#NIL THEN { IF def THEN atEnd ¬ atEnd-1; -- skip the trailing : tSel.start.pos ¬ [where,at]; tSel.end.pos ¬ [where,MAX[0,atEnd-1]]; tSel.granularity ¬ IF word THEN word ELSE char; tSel.insertion ¬ before; tSel.pendingDelete ¬ FALSE; tSel.viewer ¬ viewer; tSel.data ¬ tdd; } ELSE { TEditSelection.Copy[source: startSel, dest: tSel]; }; TEditOps.RememberCurrentPosition[viewer]; TEditSelection.SetSelLooks[tSel]; TEditSelection.MakeSelection[new: tSel, selection: id]; IF closeOnLeap THEN TEditInput.CloseEvent[]; TEditRefresh.ScrollToEndOfSel[viewer, FALSE, id]; }; found ¬ FALSE; IF Rope.Size[rope] = 0 THEN RETURN; IF def THEN rope ¬ Rope.Concat[rope,":"]; targetNode ¬ TextEdit.FromRope[rope]; TEditSelection.CallWithSelAndDocAndTddLocks[viewer, id, DoFindIt]; found ¬ where#NIL; IF NOT inViewer THEN { MessageWindow.Append["This can't happen!", TRUE]; MessageWindow.Blink[]; }; }; closeOnLeap: BOOL ¬ FALSE; Start: PROC = { TEditInput.Register[$ScanNull, ScanNull]; TEditInput.Register[$ScanStart, ScanStart]; TEditInput.Register[$ScanRepeat, ScanRepeat]; TEditInput.Register[$ScanShorten, ScanShorten]; TEditInput.Register[$ScanUpper, ScanUpper]; TEditInput.Register[$ScanLower, ScanLower]; TEditInput.Register[$ScanForward, ScanForward]; TEditInput.Register[$ScanBackward, ScanBackward]; TEditInput.Register[$ScanReverse, ScanReverse]; TEditInput.Register[$ScanToggleCase, ScanToggleCase]; TEditInputExtras.RegisterClosure[[$ScanTo, ScanTo, NIL]]; }; Start[]; }.