DIRECTORY AbbrevExpand USING [Expand, Load], Ascii USING [Lower], Atom USING [GetPName, MakeAtom, PropList], Basics USING [BITAND], BasicTime USING [Now], EditSpan USING [ChangeCaps], FS USING [Error], IO USING [PutR], MessageWindow USING [Append, Blink], NodeProps USING [false, true], NodeStyleOps USING [ReloadStyle, StyleNameForNode], NodeStyleWorks USING [ForEachAttachedStyle], Rope USING [Concat, Fetch, FromProc, Index, IsEmpty, ROPE, Size, Substr], RopeEdit USING [AlphaNumericChar, BlankChar, Concat, Substr], RopeReader USING [Backwards, Create, FreeRopeReader, Get, GetIndex, GetRope, GetRopeReader, ReadOffEnd, Ref, SetPosition], TEditDocument USING [Selection, SelectionId], TEditInput USING [CloseEvent, currentEvent, Interpret], TEditInputOps USING [BackSpace, CallWithBothLocked, CallWithLocks, CheckReadonly, Delete, DoPendingDelete, EditFailed, FindPlaceholders, InsertChar, InsertRope], TEditLocks USING [Lock, Unlock], TEditOps USING [RememberCurrentPosition], TEditRefresh USING [ScrollToEndOfSel], TEditScrolling USING [AutoScroll], TEditSelection USING [Alloc, CaretAfterSelection, CaretVisible, Copy, Deselect, Free, InsertionPoint, LockSel, MakePointSelection, MakeSelection, oldSel, pSel, SelectionRoot, SetSelLooks, sSel, UnlockSel], TEditTouchup USING [fullUpdate], TextEdit USING [CapChange, ChangeFormat, ChangeStyle, DeleteText, FetchChar, FetchCharPropList, FetchLooks, InsertChar, InsertRope, PutCharPropList, PutProp, ReplaceByChar, Size, XChar], TextLooks USING [Looks], TextNode USING [Backward, FirstChild, LastLocWithin, Location, Node, NodeItself, Parent, Root, StepBackward, StepForward], TreeFind USING [CreateFromRope, Finder, Try, TryBackwards], UndoEvent USING [], ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer], ViewerTools USING [SetSelection]; TEditMiscOpsImpl: CEDAR PROGRAM IMPORTS AbbrevExpand, Ascii, Atom, Basics, BasicTime, EditSpan, FS, IO, MessageWindow, NodeProps, NodeStyleOps, NodeStyleWorks, Rope, RopeEdit, RopeReader, TEditInput, TEditInputOps, TEditLocks, TEditOps, TEditRefresh, TEditScrolling, TEditSelection, TEditTouchup, TextEdit, TextNode, TreeFind, ViewerOps, ViewerTools EXPORTS TEditInputOps, TEditOps = BEGIN Node: TYPE ~ TextNode.Node; ROPE: TYPE ~ Rope.ROPE; XChar: TYPE ~ TextEdit.XChar; Capitalise: PUBLIC PROC [flavor: TextEdit.CapChange] = { DoCapitalise: PROC [root: Node, tSel: TEditDocument.Selection] = { TEditSelection.Deselect[]; EditSpan.ChangeCaps[root, [tSel.start.pos, tSel.end.pos], flavor, TEditInput.currentEvent]; tSel.pendingDelete _ FALSE; TEditSelection.MakeSelection[tSel, primary] }; TEditInputOps.CallWithLocks[DoCapitalise] }; ControlChar: PROC [make: BOOLEAN] = { DoControlChar: PROC [root: Node, tSel: TEditDocument.Selection] = { caret: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node; where: INT; char: XChar; IF caret.where=TextNode.NodeItself OR caret.where=0 THEN GOTO Bad; IF (node _ caret.node) = NIL THEN GOTO Bad; where _ caret.where-1; char _ TextEdit.FetchChar[node, where]; IF char.set#0 THEN GOTO Bad; IF make THEN char.code _ Basics.BITAND[char.code, 37B] ELSE IF char.code<40B THEN char.code _ ORD['@]+char.code ELSE GOTO Bad; TEditSelection.Deselect[]; [] _ TextEdit.ReplaceByChar[root: root, dest: node, char: VAL[char.code], start: where, len: 1, inherit: FALSE, looks: TextEdit.FetchLooks[node, where], charSet: char.set, event: TEditInput.currentEvent]; TEditSelection.MakePointSelection[tSel, caret]; EXITS Bad => TEditInputOps.EditFailed[] }; TEditInputOps.CallWithLocks[DoControlChar] }; MakeControlCharacter: PUBLIC PROC = { ControlChar[TRUE] }; UnMakeControlCharacter: PUBLIC PROC = { ControlChar[FALSE] }; UnMakeOctalCharacter: PUBLIC PROC = { DoUnMakeOctalCharacter: PROC [root: Node, tSel: TEditDocument.Selection] = { InsertOctal: PROC [c: CARDINAL] = { TEditInputOps.InsertChar[c/64 + '0]; TEditInputOps.InsertChar[(c/8) MOD 8 + '0]; TEditInputOps.InsertChar[c MOD 8 + '0]; }; caret: TextNode.Location ~ TEditSelection.InsertionPoint[tSel]; node: Node ~ caret.node; char: XChar; charProps: Atom.PropList; IF caret.where=TextNode.NodeItself OR caret.where=0 THEN GOTO Bad; IF node = NIL THEN GOTO Bad; char _ TextEdit.FetchChar[node, caret.where-1]; charProps _ TextEdit.FetchCharPropList[node, caret.where-1]; TEditInputOps.BackSpace[1]; IF char.set = 0 THEN InsertOctal[char.code] ELSE { TEditInputOps.InsertChar['(]; InsertOctal[char.set]; TEditInputOps.InsertChar['|]; InsertOctal[char.code]; TEditInputOps.InsertChar[')]; }; IF charProps # NIL THEN { TextEdit.PutCharPropList[node: node, index: caret.where-1, nChars: IF char.set = 0 THEN 3 ELSE 9, propList: charProps, event: TEditInput.currentEvent] }; EXITS Bad => TEditInputOps.EditFailed[] }; TEditInputOps.CallWithLocks[DoUnMakeOctalCharacter] }; MakeOctalCharacter: PUBLIC PROC = { DoMakeOctalCharacter: PROC [root: Node, tSel: TEditDocument.Selection] = { caret: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node ~ caret.node; GetOctal: PROC [offset: INT] RETURNS [d: CARDINAL _ 0] = { char: XChar; FOR i: INT IN [0..3) DO char _ TextEdit.FetchChar[node, offset+i]; IF char.set#0 OR char.code NOT IN [ORD['0]..ORD['7]] THEN RETURN [NAT.LAST]; d _ d*8 + (char.code-ORD['0]); ENDLOOP; }; len: INT _ 0; char: XChar _ [0, 0]; IF caret.where=TextNode.NodeItself OR caret.where<3 THEN GOTO Bad; IF node = NIL THEN GOTO Bad; IF TextEdit.FetchChar[node, caret.where-1] = [0, ORD[')]] THEN { IF caret.where<9 THEN GOTO Bad; IF TextEdit.FetchChar[node, caret.where-5] # [0, ORD['|]] THEN GOTO Bad; IF TextEdit.FetchChar[node, caret.where-9] # [0, ORD['(]] THEN GOTO Bad; char.code _ GetOctal[caret.where-4]; char.set _ GetOctal[caret.where-8]; len _ 9; } ELSE { char.code _ GetOctal[caret.where-3]; len _ 3; }; IF char.code > 255 OR char.set >= 255 THEN GOTO Bad; TEditSelection.Deselect[]; { charProps: Atom.PropList ~ TextEdit.FetchCharPropList[node, caret.where-len]; [] _ TextEdit.ReplaceByChar[ root: root, dest: node, char: VAL[char.code], start: caret.where-len, len: len, inherit: FALSE, looks: TextEdit.FetchLooks[node, caret.where-len], charSet: char.set, event: TEditInput.currentEvent]; IF charProps # NIL THEN TextEdit.PutCharPropList[node: node, index: caret.where-len, nChars: 1, propList: charProps, event: TEditInput.currentEvent] }; caret.where _ caret.where-len+1; TEditSelection.MakePointSelection[tSel, caret]; EXITS Bad => TEditInputOps.EditFailed[] }; TEditInputOps.CallWithLocks[DoMakeOctalCharacter] }; GetWord: PROC RETURNS [word: ROPE] = { tSel: TEditDocument.Selection; start: INT; pos: TextNode.Location; node: Node; nChars: CARDINAL _ 0; TEditSelection.CaretAfterSelection; pos _ TEditSelection.InsertionPoint[]; IF (node _ pos.node)=NIL THEN RETURN [NIL]; IF pos.where = TextNode.NodeItself THEN RETURN [NIL]; start _ pos.where-1; WHILE start>=0 DO char: XChar ~ TextEdit.FetchChar[node, start]; IF char.set#0 OR NOT RopeEdit.AlphaNumericChar[VAL[char.code]] THEN EXIT; start _ start - 1; nChars _ nChars + 1; ENDLOOP; IF nChars = 0 THEN RETURN [NIL]; start _ pos.where-nChars; word _ RopeEdit.Substr[node.rope, start, nChars]; tSel _ TEditSelection.Alloc[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]; tSel.start.pos _ [node, start]; tSel.end.pos _ [node, start+nChars-1]; tSel.granularity _ char; TEditSelection.MakeSelection[tSel, primary]; TEditSelection.Free[tSel]; TEditInputOps.Delete[]; }; SetStyle: PUBLIC PROC = { DoSetStyle: PROC [root: Node, tSel: TEditDocument.Selection] = { name: ROPE _ GetWord[]; IF name=NIL THEN { TEditInputOps.EditFailed[]; RETURN }; SetStyleName[name] }; TEditInputOps.CallWithLocks[DoSetStyle] }; ForceLower: PROC [r: ROPE] RETURNS [ROPE] = { Force: PROC RETURNS [c: CHAR] = { c _ Ascii.Lower[Rope.Fetch[r, i]]; i _ i+1 }; i: INT _ 0; RETURN [Rope.FromProc[Rope.Size[r], Force]] }; SetStyleName: PUBLIC PROC [name: ROPE, node: Node _ NIL] = { root: Node; IF node=NIL THEN node _ IF TEditSelection.pSel.insertion=before THEN TEditSelection.pSel.start.pos.node ELSE TEditSelection.pSel.end.pos.node; root _ TextNode.Root[node]; [] _ TEditLocks.Lock[root, "SetStyleName"]; TextEdit.ChangeStyle[node, ForceLower[name], TEditInput.currentEvent, root ! UNWIND => TEditLocks.Unlock[root]]; TEditLocks.Unlock[root]; }; ReloadStyle: PUBLIC PROC = { styleName: ATOM _ NodeStyleOps.StyleNameForNode[TEditSelection.InsertionPoint[].node]; IF NOT NodeStyleOps.ReloadStyle[styleName] THEN CannotReload[styleName] ELSE ViewerOps.PaintViewer[TEditSelection.pSel.viewer, client, FALSE, TEditTouchup.fullUpdate]; }; ReloadStyleName: PUBLIC PROC [name: ROPE] = { styleName: ATOM; IF name.IsEmpty THEN RETURN; styleName _ Atom.MakeAtom[name]; IF NOT NodeStyleOps.ReloadStyle[styleName] THEN CannotReload[styleName]; }; CannotReload: PROC [styleName: ATOM] = { MessageWindow.Append["Failed in attempt to load style named ", TRUE]; MessageWindow.Append[Atom.GetPName[styleName]]; MessageWindow.Blink[] }; SetFormat: PUBLIC PROC = { DoSetFormat: PROC [root: Node, tSel: TEditDocument.Selection] = { name: ROPE _ GetWord[]; IF name=NIL THEN { TEditInputOps.EditFailed[]; RETURN }; SetFormatName[name] }; TEditInputOps.CallWithLocks[DoSetFormat] }; GetFormat: PUBLIC PROC = { DoGetFormat: PROC [root: Node, tSel: TEditDocument.Selection] = { name: ROPE; format: ATOM _ TEditSelection.InsertionPoint[].node.formatName; name _ IF format=NIL THEN "default" ELSE Atom.GetPName[format]; TEditInputOps.InsertRope[name] }; TEditInputOps.CallWithLocks[DoGetFormat] }; TransposeFormat: PUBLIC PROC [target: TEditDocument.SelectionId _ primary] = { targetSel: TEditDocument.Selection _ IF target=primary THEN TEditSelection.pSel ELSE TEditSelection.sSel; srcSel: TEditDocument.Selection _ IF target=primary THEN TEditSelection.sSel ELSE TEditSelection.pSel; DoTransFormat: PROC [sourceRoot, destRoot: Node, tSel, srcSel, targetSel: TEditDocument.Selection] = { targetFormat, srcFormat: ATOM; targetNode, srcNode: Node; srcNode _ IF srcSel.insertion=before THEN srcSel.start.pos.node ELSE srcSel.end.pos.node; srcFormat _ srcNode.formatName; targetNode _ IF targetSel.insertion=before THEN targetSel.start.pos.node ELSE targetSel.end.pos.node; targetFormat _ targetNode.formatName; TEditSelection.Copy[source: targetSel, dest: TEditSelection.oldSel]; -- save for Repeat's FOR node: Node _ targetSel.start.pos.node, TextNode.StepForward[node] DO TextEdit.ChangeFormat[node, srcFormat, TEditInput.currentEvent, destRoot]; IF node = targetSel.end.pos.node THEN EXIT; ENDLOOP; FOR node: Node _ srcSel.start.pos.node, TextNode.StepForward[node] DO TextEdit.ChangeFormat[node, targetFormat, TEditInput.currentEvent, sourceRoot]; IF node = srcSel.end.pos.node THEN EXIT; ENDLOOP; TEditSelection.MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary] }; TEditInputOps.CallWithBothLocked[DoTransFormat, targetSel, srcSel, write] }; CopyFormat: PUBLIC PROC [target: TEditDocument.SelectionId _ primary] = { targetSel: TEditDocument.Selection _ IF target=primary THEN TEditSelection.pSel ELSE TEditSelection.sSel; srcSel: TEditDocument.Selection _ IF target=primary THEN TEditSelection.sSel ELSE TEditSelection.pSel; DoCopyFormat: PROC [sourceRoot, destRoot: Node, tSel, srcSel, targetSel: TEditDocument.Selection] = { srcNode: Node _ IF srcSel.insertion=before THEN srcSel.start.pos.node ELSE srcSel.end.pos.node; format: ATOM _ srcNode.formatName; TEditSelection.Copy[source: srcSel, dest: TEditSelection.oldSel]; -- save for Repeat's FOR node: Node _ targetSel.start.pos.node, TextNode.StepForward[node] DO TextEdit.ChangeFormat[node, format, TEditInput.currentEvent, destRoot]; IF node = targetSel.end.pos.node THEN EXIT; ENDLOOP; TEditSelection.MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary] }; TEditInputOps.CallWithBothLocked[DoCopyFormat, targetSel, srcSel, read] }; SetFormatName: PUBLIC PROC [name: ROPE, node: Node _ NIL] = { root: Node; lockSel: BOOL = (node=NIL); format: ATOM _ IF name.IsEmpty THEN NIL ELSE Atom.MakeAtom[ForceLower[name]]; IF lockSel THEN { TEditSelection.LockSel[primary, "SetFormatName"]; IF NOT TEditInputOps.CheckReadonly[TEditSelection.pSel] OR (root _ TEditSelection.SelectionRoot[TEditSelection.pSel])=NIL THEN { TEditSelection.UnlockSel[primary]; RETURN }; node _ TEditSelection.InsertionPoint[].node } ELSE root _ TextNode.Root[node]; { ENABLE UNWIND => IF lockSel THEN TEditSelection.UnlockSel[primary]; [] _ TEditLocks.Lock[root, "SetFormatName"]; TextEdit.ChangeFormat[node, format, TEditInput.currentEvent, root]; TEditLocks.Unlock[root]; IF lockSel THEN TEditSelection.UnlockSel[primary] } }; SetSelFormatName: PROC [name: ROPE, sel: TEditDocument.Selection] = { DoSetSelFormatName: PROC [root: Node, tSel: TEditDocument.Selection] = { format: ATOM _ IF name.IsEmpty THEN NIL ELSE Atom.MakeAtom[ForceLower[name]]; FOR node: Node _ tSel.start.pos.node, TextNode.StepForward[node] DO TextEdit.ChangeFormat[node, format, TEditInput.currentEvent, root]; IF node = tSel.end.pos.node THEN EXIT; ENDLOOP }; TEditInputOps.CallWithLocks[DoSetSelFormatName] }; SetCommentProp: PUBLIC PROC [flag: BOOLEAN] = { DoSetCommentProp: PROC [root: Node, tSel: TEditDocument.Selection] = { FOR node: Node _ tSel.start.pos.node, TextNode.StepForward[node] DO n: Node _ node; IF n # NIL THEN TextEdit.PutProp[n, $Comment, IF flag THEN NodeProps.true ELSE NodeProps.false, TEditInput.currentEvent]; IF node = tSel.end.pos.node THEN EXIT; ENDLOOP }; TEditInputOps.CallWithLocks[DoSetCommentProp] }; abbrevFailedProc: PROC RETURNS [BOOL] _ NIL; RegisterAbbrevFailedProc: PUBLIC PROC [proc: PROC RETURNS [BOOL]] = { abbrevFailedProc _ proc }; ExpandAbbreviation: PUBLIC PROC = { DoExpand: PROC [root: Node, tSel: TEditDocument.Selection] = { pos: TextNode.Location; node: Node; offset: INT; done, keyDeterminesDict: BOOLEAN _ FALSE; clearedMessageWindow: BOOLEAN _ FALSE; keyStart, keyLen, resultLen: INT; rdr: RopeReader.Ref; commands: LIST OF REF ANY; styleName: ATOM; Try: PROC [name: ATOM] RETURNS [stop: BOOLEAN] = { dict: ROPE = Atom.GetPName[name]; [done, keyDeterminesDict, keyStart, keyLen, resultLen, commands] _ AbbrevExpand.Expand[node, offset, dict, TEditInput.currentEvent]; IF NOT done AND NOT keyDeterminesDict THEN NodeStyleWorks.ForEachAttachedStyle[name, Try]; RETURN [done] }; TEditSelection.CaretAfterSelection; pos _ TEditSelection.InsertionPoint[]; node _ pos.node; offset _ pos.where; styleName _ NodeStyleOps.StyleNameForNode[node]; TEditSelection.Deselect[]; IF NOT Try[styleName] THEN { TEditSelection.MakeSelection[tSel, primary]; IF abbrevFailedProc # NIL AND abbrevFailedProc[] THEN RETURN; MessageWindow.Append[Rope.Substr[node.rope, keyStart, keyLen], NOT clearedMessageWindow]; MessageWindow.Append["? Unknown abbreviation."]; RETURN }; tSel.end.pos.node _ tSel.start.pos.node _ node; tSel.start.pos.where _ keyStart; tSel.pendingDelete _ FALSE; IF resultLen = 0 THEN { -- make a caret tSel.end.pos.where _ keyStart; tSel.insertion _ before; tSel.granularity _ point } ELSE { tSel.end.pos.where _ keyStart+resultLen-1; tSel.insertion _ after; tSel.granularity _ char }; TEditSelection.MakeSelection[tSel, primary]; rdr _ RopeReader.GetRopeReader[]; RopeReader.SetPosition[rdr, node.rope, keyStart]; FOR i: INT IN [0..resultLen) DO -- check for placeholder IF RopeReader.Get[rdr] = 1C THEN { -- found one TEditSelection.MakePointSelection[tSel, [node, keyStart+i]]; TEditInputOps.FindPlaceholders[TRUE]; EXIT }; ENDLOOP; RopeReader.FreeRopeReader[rdr]; IF commands # NIL THEN TEditInput.Interpret[TEditSelection.pSel.viewer, commands] }; TEditInputOps.CallWithLocks[DoExpand] }; LoadAbbreviations: PUBLIC PROC [dictName: ROPE] = { count: LONG INTEGER _ 0; fileName: ROPE; IF Rope.Size[dictName]=0 THEN RETURN; fileName _ Rope.Concat[dictName, ".Abbreviations"]; count _ AbbrevExpand.Load[fileName, dictName ! FS.Error => {CONTINUE}]; IF count = 0 THEN { -- something went wrong MessageWindow.Append["The file named <", TRUE]; MessageWindow.Append[fileName]; MessageWindow.Append["> was not found or was not an abbreviation dictionary"] } }; InsertChar: PUBLIC PROC [char: CHAR] = { DoInsertChar: PROC [root: Node, tSel: TEditDocument.Selection] = { pos: TextNode.Location; looks: TextLooks.Looks _ tSel.looks; IF tSel.pendingDelete THEN { TEditInputOps.DoPendingDelete[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel] }; pos _ TEditSelection.InsertionPoint[TEditSelection.pSel]; -- need to get insertion point after pending delete TEditSelection.Deselect[primary]; [] _ TextEdit.InsertChar[root: root, dest: pos.node, char: char, destLoc: pos.where, inherit: FALSE, looks: looks, event: TEditInput.currentEvent]; pos.where _ pos.where+1; TEditSelection.MakePointSelection[tSel, pos]; IF TEditSelection.CaretVisible[] THEN TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE] }; TEditInputOps.CallWithLocks[DoInsertChar]; }; InsertRope: PUBLIC PROC [rope: ROPE] = { DoInsertRope: PROC [root: Node, tSel: TEditDocument.Selection] = { pos: TextNode.Location; len: INT _ Rope.Size[rope]; looks: TextLooks.Looks; IF tSel.pendingDelete THEN { TEditInputOps.DoPendingDelete[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel] }; pos _ TEditSelection.InsertionPoint[TEditSelection.pSel]; -- need to get insertion point after pending delete looks _ tSel.looks; TEditSelection.Deselect[primary]; [] _ TextEdit.InsertRope[root: root, dest: pos.node, rope: rope, destLoc: pos.where, inherit: FALSE, looks: looks, event: TEditInput.currentEvent]; pos.where _ pos.where+len; TEditSelection.MakePointSelection[tSel, pos]; IF TEditSelection.CaretVisible[] THEN TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE] }; TEditInputOps.CallWithLocks[DoInsertRope]; }; InsertLineBreak: PUBLIC PROC = { DoInsertLineBreak: PROC [root: Node, tSel: TEditDocument.Selection] = { pos: TextNode.Location; node: Node; start, end, lim: INT; rope: ROPE; IF tSel.pendingDelete THEN { TEditInputOps.DoPendingDelete[]; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel] }; pos _ TEditSelection.InsertionPoint[TEditSelection.pSel]; -- need to get insertion point after pending delete IF (node _ pos.node) = NIL THEN { TEditInputOps.EditFailed[]; RETURN }; rope _ node.rope; start _ lim _ MAX[0, MIN[pos.where, Rope.Size[rope]]]; WHILE start > 0 AND Rope.Fetch[rope, start-1] # 15C DO start _ start-1; ENDLOOP; end _ start; WHILE end < lim AND RopeEdit.BlankChar[Rope.Fetch[rope, end]] DO end _ end+1; ENDLOOP; TEditInputOps.InsertRope[RopeEdit.Concat["\n", RopeEdit.Substr[rope, start, end-start]]]; }; TEditInputOps.CallWithLocks[DoInsertLineBreak]; }; DeleteNextChar: PUBLIC PROC [count: INT _ 1] = { DoDeleteNextChar: PROC [root: Node, tSel: TEditDocument.Selection] = { flush: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node _ flush.node; IF node = NIL THEN GOTO Bad; IF flush.where=TextNode.NodeItself THEN GOTO Bad; IF (count _ MIN[count, TextEdit.Size[node]-flush.where]) <= 0 THEN GOTO Bad; TEditSelection.Deselect[primary]; TextEdit.DeleteText[root, node, flush.where, count, TEditInput.currentEvent]; TEditSelection.MakePointSelection[tSel, flush]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; EXITS Bad => TEditInputOps.EditFailed[] }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoDeleteNextChar]; }; GoToNextChar: PUBLIC PROC [count: INT _ 1] = { DoGoToNextChar: PROC [root: Node, tSel: TEditDocument.Selection] = { loc: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node _ loc.node; IF node = NIL OR loc.where=TextNode.NodeItself OR (count _ MIN[count, TextEdit.Size[node]-loc.where]) <= 0 THEN { -- try next node GoToNextNode; RETURN }; TEditSelection.MakePointSelection[tSel, [node, loc.where+count]]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToNextChar, read]; }; GoToPreviousChar: PUBLIC PROC [count: INT _ 1] = { DoGoToPreviousChar: PROC [root: Node, tSel: TEditDocument.Selection] = { loc: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node _ loc.node; IF node = NIL OR loc.where=TextNode.NodeItself OR loc.where < count THEN { GoToPreviousNode; RETURN }; -- try previous node TEditSelection.MakePointSelection[tSel, [node, loc.where-count]]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToPreviousChar, read]; }; FindNextWord: PUBLIC PROC [node: Node, start: INT] RETURNS [nChars: CARDINAL] = { offset: INT _ start; size: INT _ TextEdit.Size[node]; Alpha: PROC [index: INT] RETURNS [BOOL] = { char: XChar ~ TextEdit.FetchChar[node, index]; RETURN [char.set=0 AND RopeEdit.AlphaNumericChar[VAL[char.code]]]; }; nChars _ 1; WHILE offset TEditInputOps.EditFailed[]; }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoDeleteNextWord]; }; GoToNextWord: PUBLIC PROC [count: INT _ 1] = { DoGoToNextWord: PROC [root: Node, tSel: TEditDocument.Selection] = { pos: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node _ pos.node; size, next: INT; IF node=NIL OR (next _ pos.where)=TextNode.NodeItself THEN { -- try next node GoToNextNode; RETURN }; size _ TextEdit.Size[node]; FOR garbage: INT IN [0..count) DO IF next >= size THEN { GoToNextNode; RETURN }; next _ next+FindNextWord[node, next]; ENDLOOP; TEditSelection.MakePointSelection[tSel, [node, next]]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToNextWord, read]; }; GoToNextNode: PUBLIC PROC [count: INT _ 1] = { DoGoToNextNode: PROC [root: Node, tSel: TEditDocument.Selection] = { pos: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node _ pos.node; FOR garbage: INT IN [0..count) DO IF (node _ TextNode.StepForward[node])=NIL THEN { TEditInputOps.EditFailed[]; RETURN }; ENDLOOP; TEditSelection.MakePointSelection[tSel, [node, 0]]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToNextNode, read]; }; GoToPreviousNode: PUBLIC PROC [count: INT _ 1] = { DoGoToPreviousNode: PROC [root: Node, tSel: TEditDocument.Selection] = { pos: TextNode.Location _ TEditSelection.InsertionPoint[tSel]; node: Node _ pos.node; FOR garbage: INT IN [0..count) DO IF (node _ TextNode.StepBackward[node])=NIL OR TextNode.Parent[node]=NIL THEN GOTO Bad; ENDLOOP; IF node=NIL THEN GOTO Bad; TEditSelection.MakePointSelection[tSel, [node, TextEdit.Size[node]]]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; EXITS Bad => TEditInputOps.EditFailed[]; }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoGoToPreviousNode, read]; }; AppendDecimal: PROC [text: REF TEXT, number: NAT] = { pos: NAT _ text.length; skipZeros: BOOL _ TRUE; div: NAT _ 1000; DO c: CHAR _ '0 + (number / div); number _ number MOD div; IF c # '0 THEN skipZeros _ FALSE; IF NOT skipZeros THEN {text[pos] _ c; pos _ pos + 1}; IF div = 10 THEN EXIT; div _ div / 10; ENDLOOP; text[pos] _ '0 + (number / div); text.length _ pos + 1; }; InsertTime: PUBLIC PROC = { DoInsertTime: PROC [root: Node, tSel: TEditDocument.Selection] = { resLen: INT; caret: TextNode.Location; dest: Node; looks: TextLooks.Looks _ TEditSelection.pSel.looks; date: ROPE _ IO.PutR[[time[BasicTime.Now[]]]]; dateLen: INT _ Rope.Index[date, 0, ", "]; IF dateLen < date.Size[] THEN dateLen _ Rope.Index[date, dateLen+2, " "]; IF TEditSelection.pSel.pendingDelete THEN TEditInputOps.DoPendingDelete[]; caret _ TEditSelection.InsertionPoint[TEditSelection.pSel]; IF (dest _ caret.node)=NIL THEN GOTO Bad; TEditSelection.Deselect[primary]; [----, resLen] _ TextEdit.InsertRope[ root: root, dest: dest, rope: date, destLoc: caret.where, inherit: FALSE, looks: looks, event: TEditInput.currentEvent]; tSel.end.pos.node _ tSel.start.pos.node _ caret.node; tSel.end.pos.where _ caret.where+resLen-1; tSel.start.pos.where _ caret.where+dateLen; tSel.insertion _ after; tSel.granularity _ char; tSel.pendingDelete _ FALSE; TEditSelection.MakeSelection[selection: primary, new: tSel]; EXITS Bad => TEditInputOps.EditFailed[]; }; TEditInputOps.CallWithLocks[DoInsertTime]; }; MakeLook: PROC [c: CHAR] RETURNS [looks: TextLooks.Looks] ~ INLINE { looks _ ALL[FALSE]; looks[c] _ TRUE; }; InsertBrackets: PUBLIC PROC [left, right: CHAR] = { DoInsertBrackets: PROC [root: Node, tSel: TEditDocument.Selection] = { leftEnd, rightEnd: TextNode.Location; leftNode, rightNode: Node; l, r: INT; bracketLooks: TextLooks.Looks ~ IF left = 1C THEN MakeLook['t] ELSE tSel.looks; leftEnd _ tSel.start.pos; rightEnd _ tSel.end.pos; IF (leftNode _ leftEnd.node)=NIL THEN GOTO Bad; IF (rightNode _ rightEnd.node)=NIL THEN GOTO Bad; IF (r _ rightEnd.where) = TextNode.NodeItself THEN r _ TextEdit.Size[rightNode] ELSE IF tSel.granularity # point THEN r _ r+1; IF (l _ leftEnd.where) = TextNode.NodeItself THEN l _ 0; TEditSelection.Deselect[primary]; [----, ----] _ TextEdit.InsertChar[ root: root, dest: rightNode, char: right, destLoc: r, inherit: FALSE, looks: bracketLooks, event: TEditInput.currentEvent]; [----, ----] _ TextEdit.InsertChar[ root: root, dest: leftNode, char: left, destLoc: l, inherit: FALSE, looks: bracketLooks, event: TEditInput.currentEvent]; tSel.start.pos.where _ l+1; IF tSel.granularity = point THEN tSel.end.pos.where _ l+1 ELSE { tSel.granularity _ char; IF leftNode=rightNode THEN tSel.end.pos.where _ r }; tSel.pendingDelete _ FALSE; TEditSelection.MakeSelection[selection: primary, new: tSel]; EXITS Bad => TEditInputOps.EditFailed[]; }; TEditInputOps.CallWithLocks[DoInsertBrackets]; }; End: ERROR = CODE; -- private; for use in SelectMatchingBrackets SelectMatchingBrackets: PUBLIC PROC [left, right: CHAR] = { IF NOT DoSelectMatchingBrackets[left, right] THEN TEditInputOps.EditFailed["No match."]; }; DoSelectMatchingBrackets: PUBLIC PROC [left, right: CHAR] RETURNS [found: BOOL] = { rdr: RopeReader.Ref _ RopeReader.Create[]; ref, parent: Node; node: Node; GetPreviousNode: PROC RETURNS [n: Node] = { DO -- search for previous text node [back: ref, backparent: parent] _ TextNode.Backward[ref, parent]; IF ref=NIL THEN ERROR End; IF ref # NIL THEN RETURN [ref]; ENDLOOP }; GetLeftChar: PROC RETURNS [CHAR] = { c: CHAR; c _ RopeReader.Backwards[rdr ! RopeReader.ReadOffEnd => { node _ GetPreviousNode[]; RopeReader.SetPosition[rdr, node.rope, Rope.Size[node.rope]]; RETRY } ]; RETURN [c]; }; GetNextNode: PROC RETURNS [n: Node] = { DO -- search for next text node IF (ref _ TextNode.StepForward[ref])=NIL THEN ERROR End; IF ref # NIL THEN RETURN [ref]; ENDLOOP; }; GetRightChar: PROC RETURNS [CHAR] = { c: CHAR; c _ RopeReader.Get[rdr ! RopeReader.ReadOffEnd => { node _ GetNextNode[]; RopeReader.SetPosition[rdr, node.rope]; RETRY } ]; RETURN [c] }; DoSelect: PROC [root: Node, tSel: TEditDocument.Selection] = { leftEnd, rightEnd: TextNode.Location; nest: CARDINAL _ 0; loc: INT; leftEnd _ tSel.start.pos; rightEnd _ tSel.end.pos; ref _ leftEnd.node; IF (node _ ref)=NIL THEN { IF (node _ GetPreviousNode[])=NIL THEN GOTO NoMatch; loc _ Rope.Size[node.rope] } ELSE IF (loc _ leftEnd.where) = TextNode.NodeItself THEN loc _ 0; RopeReader.SetPosition[rdr, node.rope, loc]; DO -- left end SELECT GetLeftChar[ ! End => GOTO NoMatch] FROM left => IF nest=0 THEN EXIT ELSE nest _ nest-1; right => nest _ nest+1; ENDCASE; ENDLOOP; leftEnd.node _ node; leftEnd.where _ RopeReader.GetIndex[rdr]; ref _ rightEnd.node; IF (node _ ref)=NIL THEN { IF (node _ GetNextNode[])=NIL THEN GOTO NoMatch; loc _ 0 } ELSE IF (loc _ rightEnd.where) = TextNode.NodeItself THEN loc _ Rope.Size[node.rope] ELSE IF TEditSelection.pSel.granularity # point THEN loc _ loc+1; RopeReader.SetPosition[rdr, node.rope, loc]; DO -- right end SELECT GetRightChar[ ! End => GOTO NoMatch] FROM right => IF nest=0 THEN EXIT ELSE nest _ nest-1; left => nest _ nest+1; ENDCASE; ENDLOOP; rightEnd.node _ node; rightEnd.where _ RopeReader.GetIndex[rdr]-1; tSel.start.pos _ leftEnd; tSel.end.pos _ rightEnd; tSel.granularity _ char; TEditSelection.SetSelLooks[tSel]; TEditSelection.MakeSelection[selection: primary, new: tSel]; found _ TRUE; EXITS NoMatch => found _ FALSE; }; TEditInputOps.CallWithLocks[DoSelect, read]; }; CallWithPrimarySelectionLocked: PROC [who: ROPE, action: PROC [TEditDocument.Selection]] = { TEditSelection.LockSel[primary, who]; action[TEditSelection.pSel ! UNWIND => TEditSelection.UnlockSel[primary]]; TEditSelection.UnlockSel[primary]; }; NextViewer: PUBLIC PROC [forward: BOOL] = { IF NOT DoNextViewer[forward] THEN TEditInputOps.EditFailed["No next viewer."]; }; DoNextViewer: PUBLIC PROC [forward: BOOL] RETURNS [found: BOOL _ FALSE] = { Viewer: TYPE ~ ViewerClasses.Viewer; DoNextViewerAction: PROC [PSel: TEditDocument.Selection] = { thisViewer: Viewer ~ TEditSelection.pSel.viewer; nextViewer: Viewer _ NIL; Enumerate: PROC [enum: PROC [Viewer]] = { FOR v: Viewer _ thisViewer.parent.child, v.sibling UNTIL v=NIL DO enum[v]; ENDLOOP; }; ConvergeForward: PROC [v: Viewer] = { IF v.class.flavor=$Text AND (v.wy > thisViewer.wy OR (v.wy = thisViewer.wy AND v.wx > thisViewer.wx)) THEN { IF nextViewer=NIL OR v.wy < nextViewer.wy THEN {nextViewer _ v; RETURN}; IF v.wy > nextViewer.wy THEN RETURN; IF (v.wy > thisViewer.wy OR v.wx > thisViewer.wx) AND v.wx < nextViewer.wx THEN nextViewer _ v; }; }; ConvergeBackward: PROC [v: Viewer] = { IF v.class.flavor=$Text AND (v.wy < thisViewer.wy OR (v.wy = thisViewer.wy AND v.wx < thisViewer.wx)) THEN { IF nextViewer=NIL OR v.wy > nextViewer.wy THEN {nextViewer _ v; RETURN}; IF v.wy < nextViewer.wy THEN RETURN; IF (v.wy < thisViewer.wy OR v.wx < thisViewer.wx) AND v.wx > nextViewer.wx THEN nextViewer _ v; }; }; IF thisViewer=NIL OR thisViewer.parent=NIL THEN RETURN; Enumerate[IF forward THEN ConvergeForward ELSE ConvergeBackward]; IF nextViewer # NIL THEN { ViewerTools.SetSelection[nextViewer, NIL]; found _ TRUE }; }; CallWithPrimarySelectionLocked["DoNextViewer", DoNextViewerAction]; }; FindPlaceholders: PUBLIC PROC [next: BOOL] = { found, wentToEnd: BOOL; [found, wentToEnd] _ DoFindPlaceholders[next, TRUE]; IF NOT found AND NOT wentToEnd THEN NextViewer[next]; }; DoFindPlaceholders: PUBLIC PROC [next, gotoend: BOOL, startBoundaryNode, endBoundaryNode: Node _ NIL, startBoundaryOffset: INT _ 0, endBoundaryOffset: INT _ LAST[INT]] RETURNS [found, wenttoend: BOOL] = { DoFind: PROC [root: Node, tSel: TEditDocument.Selection] = { finder: TreeFind.Finder _ TreeFind.CreateFromRope[IF next THEN "" ELSE ""]; from: TextNode.Location; where: Node; at, atEnd, start: INT; Failed: PROC = { IF gotoend THEN { loc: TextNode.Location = IF next THEN TextNode.LastLocWithin[root] ELSE [TextNode.FirstChild[root], 0]; IF loc # from OR tSel.granularity # point THEN { wenttoend _ TRUE; TEditOps.RememberCurrentPosition[tSel.viewer]; TEditSelection.MakePointSelection[tSel, loc]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, FALSE] } }; }; from _ TEditSelection.InsertionPoint[tSel]; start _ from.where; IF next AND tSel.insertion=before AND tSel.granularity#point THEN start _ start+1 ELSE IF NOT next AND tSel.insertion=after THEN start _ MAX[start-1, 0]; [found, where, at, atEnd, , ] _ IF next THEN TreeFind.Try[finder: finder, first: from.node, start: start, last: endBoundaryNode, lastLen: endBoundaryOffset] ELSE TreeFind.TryBackwards[finder: finder, first: from.node, len: start, last: startBoundaryNode, lastStart: startBoundaryOffset]; wenttoend _ FALSE; IF NOT found THEN { Failed[]; RETURN }; TEditSelection.MakePointSelection[tSel, [where, IF next THEN at+1 ELSE MAX[at, 0]]]; IF NOT DoSelectMatchingBrackets[', '] THEN { Failed[]; RETURN }; TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel]; tSel.insertion _ before; tSel.pendingDelete _ TRUE; TEditOps.RememberCurrentPosition[tSel.viewer]; tSel.looks _ TextEdit.FetchLooks[ -- take looks from first char after the  tSel.start.pos.node, tSel.start.pos.where+1]; TEditSelection.MakeSelection[tSel, primary]; TEditInput.CloseEvent[]; TEditRefresh.ScrollToEndOfSel[tSel.viewer, FALSE]; }; TEditInputOps.CallWithLocks[DoFind, read]; }; GetSelContents: PUBLIC PROC RETURNS [contents: ROPE] = { RETURN[IF TEditSelection.pSel.viewer=NIL THEN NIL ELSE NARROW[TEditSelection.pSel.viewer.class.get[TEditSelection.pSel.viewer, $SelChars]]] } ; GetSelData: PUBLIC PROC [primary: BOOLEAN _ TRUE] RETURNS [data: TEditDocument.Selection] = { RETURN [IF primary THEN TEditSelection.pSel ELSE TEditSelection.sSel] }; AutoScroll: PUBLIC PROC [tryToGlitch: BOOLEAN _ TRUE] = { TEditScrolling.AutoScroll[tryToGlitch: tryToGlitch] -- scroll to selection }; GetTextContents: PUBLIC PROC [viewer: ViewerClasses.Viewer] RETURNS [contents: ROPE] = { RETURN[NARROW[viewer.class.get[viewer]]] }; SetTextContents: PUBLIC PROC [viewer: ViewerClasses.Viewer, contents: ROPE] = { viewer.class.set[viewer, contents] }; END. xTEditMiscOpsImpl.mesa Copyright Σ 1985, 1987, 1988 by Xerox Corporation. All rights reserved. Paxton on June 6, 1983 4: 09 pm Maxwell, January 6, 1983 11: 33 am Russ Atkinson, September 28, 1983 2: 57 pm Michael Plass, October 19, 1987 11:08:22 am PDT Russ Atkinson (RRA) January 23, 1986 0: 10: 22 am PST Doug Wyatt, February 17, 1988 6:21:49 pm PST Transpose the formats of the primary and secondary selections copy leading blanks from previous line RRA wrote this insert left char at start of selection, right char after selection extend selection until includes matching left and right brackets TEditOps Implementation Κ"˜codešœ™KšœH™HKšœ™Kšœ"™"Kšœ*™*Kšœ/™/Kšœ5™5Kšœ,™,K™—šΟk ˜ Kšœ œ˜"Kšœœ ˜Kšœœ ˜*Kšœœœ˜Kšœ œ˜Kšœ œ˜Kšœœ ˜Kšœœ˜Kšœœ˜$Kšœ œ˜Kšœ œ!˜3Kšœœ˜,Kšœœ+œ˜IKšœ œ/˜=Kšœ œj˜zKšœœ˜-Kšœ œ'˜7KšœœŽ˜‘Kšœ œ˜ Kšœ œ˜)Kšœ œ˜&Kšœœ˜"KšœœΉ˜ΝKšœ œ˜ Kšœ œ¬˜ΊKšœ œ ˜Kšœ œl˜zKšœ œ-˜;Kšœ œ˜Kšœœ ˜Kšœ œ˜Kšœ œ˜!K˜—KšΠlnœœ˜Kšœ9œœχ˜½Kšœ˜šœ˜K˜Kšœœ˜Kšœœœ˜šœœ˜K˜—šΟn œœœ!˜8šŸ œœ0˜BK˜K˜[Kšœœ˜K˜,K˜—K˜*K˜—K˜šŸ œœœ˜%šŸ œœ0˜CK˜?Kšœ ˜ Kšœœ˜ Kšœ ˜ Kšœ!œœœ˜BKšœœœœ˜+K˜K˜'Kšœ œœ˜šœ˜Kšœœ˜.Kš œœœ œœœ˜G—K˜Kšœ:œ,œ^˜ΜK˜/Kšœ#˜(K˜—K˜+K˜—K˜KšŸœœœœ˜:KšŸœœœœ˜=šŸœœœ˜%šŸœœ0˜LšŸ œœœ˜#K˜$Kšœœ ˜+Kšœœ ˜'K˜—K˜?Kšœ˜Kšœ ˜ K˜Kšœ!œœœ˜BKšœœœœ˜K˜/K˜˜CK˜CKšœœœ˜&Kšœ˜—K˜—K˜0K˜—K˜šŸœœœœ˜/šŸœœ0˜Fšœ>˜CKšœ˜Kšœœ˜˜Kšœœœ˜2K˜—Kšœœœ˜&Kšœ˜—K˜—K˜.K˜—K˜Kš œœœœœ˜,š Ÿœœœœœœ˜E˜K˜——K˜šŸœœœ˜#šŸœœ0˜>K˜Kšœ ˜ Kšœœ˜ Kšœœœ˜)Kšœœœ˜&Kšœœ˜!K˜Kš œ œœœœ˜Kšœ œ˜K˜š Ÿœœœœœ˜2Kšœœ˜!˜BK˜A—Kš œœœœ˜*K˜/Kšœ˜K˜—K˜#K˜&K˜K˜K˜0K˜K˜šœœœ˜K˜,Kš œœœœœ˜=Kšœ?œ˜YK˜1Kšœ˜K˜—K˜K˜/K˜ Kšœœ˜šœœ ˜'K˜K˜˜K˜——šœ˜K˜*K˜˜K˜——K˜,K˜K˜!K˜1š œœœœ ˜8šœœ  ˜/K˜Kšœœ˜K˜—K˜šœ œœ ˜!Kšœœœ˜.K˜%Kšœ˜—K˜6Kšœ+œ˜1K˜—Kšœ œ3˜DK˜—K˜šŸ œœœ œ ˜.šŸœœ0˜DK˜=K˜šœ œœ ˜!Kšœ%œœœ˜WKšœ˜—K˜3Kšœ+œ˜1K˜—Kšœ œ3˜DK˜—K˜šŸœœœ œ ˜2šŸœœ0˜HK˜=K˜šœ œœ ˜!Kšœ&œ˜.Kšœœœœ˜(Kšœ˜—Kšœœœœ˜K˜EKšœ+œ˜1Kšœ#˜(K˜—Kšœ œ7˜HK˜—K˜š Ÿ œœœœ œ˜5Kšœ™Kšœœ˜Kšœ œœ˜Kšœœ˜š˜Kšœœ˜Kšœœ˜Kšœœ œ˜!Kšœœ œ ˜5Kšœ œœ˜K˜Kšœ˜—K˜ K˜K˜—K˜šŸ œœœ˜šŸ œœ0˜BKšœœ˜ K˜Kšœ ˜ K˜3Kšœœœ˜.Kšœ œ˜)Kšœœ,˜IKšœ#œ!˜JK˜;Kšœœœœ˜)K˜!šœ œ ˜%K˜$Kšœœ0˜T—K˜5K˜*K˜+K˜K˜Kšœœ˜K˜K˜%Kšœœ˜Kšœœ˜ K˜K˜K˜šœœœ˜Kšœœœœ ˜4K˜K˜—Kšœœ-œ ˜AK˜,šœ  ˜šœœ ˜/Kš œœœœœ˜/K˜Kšœ˜—Kšœ˜—K˜K˜)K˜šœœœ˜Kšœœœœ ˜0K˜K˜—šœœ.œ˜TKšœœ)œ ˜A—K˜,šœ  ˜šœœ ˜0Kš œ œœœœ˜0K˜Kšœ˜—Kšœ˜—K˜K˜,K˜K˜K˜K˜K˜!K˜