DIRECTORY AbbrevExpand USING [Expand, Load], Ascii USING [CR, LF, Lower], Atom USING [GetPName, MakeAtom], BasicTime USING [Now], Char, CharOps, EditSpan USING [ChangeCaps], MessageWindow USING [Append, Blink], NodeStyleOps USING [ReloadStyle, StyleNameForNode], NodeStyleWorks USING [ForEachAttachedStyle], PFS USING [Error], Rope USING [Concat, Fetch, FromChar, FromProc, IsEmpty, ROPE, Size, Substr], RopeReader USING [FreeRopeReader, Get, GetIndex, GetRope, GetRopeReader, Ref, SetPosition], TBase, TEditDocument USING [Selection, SelectionId], TEditInput USING [CloseEvent, currentEvent, Interpret], TEditInputOps USING [CallWithBothLocked, CallWithLocks, CheckReadonly, Delete, DoPendingDelete, EditFailed, FindPlaceholders, 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 [ChangeStyle, DeleteText, FetchChar, FetchLooks, GetFormat, GetNewlineDelimiter, PutFormat, ReplaceByRope, Size], TextEditBogus USING [GetRope], TextLooks USING [Looks], TextNode USING [FirstChild, LastLocWithin, Parent, Root, StepBackward, StepForward], Tioga, ViewerClasses USING [Viewer], ViewerOps USING [PaintViewer], ViewerTools USING [SetSelection]; TEditMiscOpsImpl: CEDAR PROGRAM IMPORTS AbbrevExpand, Ascii, Atom, BasicTime, Char, CharOps, EditSpan, MessageWindow, NodeStyleOps, NodeStyleWorks, PFS, Rope, RopeReader, TBase, TEditInput, TEditInputOps, TEditLocks, TEditOps, TEditRefresh, TEditScrolling, TEditSelection, TEditTouchup, TextEdit, TextEditBogus, TextNode, ViewerOps, ViewerTools EXPORTS TEditInputOps, TEditOps = BEGIN ROPE: TYPE ~ Rope.ROPE; Node: TYPE ~ Tioga.Node; Location: TYPE ~ Tioga.Location; NodeItself: INT ~ Tioga.NodeItself; Looks: TYPE ~ Tioga.Looks; noLooks: Looks ~ Tioga.noLooks; Selection: TYPE ~ TEditDocument.Selection; Capitalise: PUBLIC PROC [flavor: Tioga.CapChange] = { DoCapitalise: PROC [root: Node, tSel: 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] }; MungeChar: PROC [proc: TBase.CaretProc] ~ { DoMungeChar: PROC [root: Node, tSel: Selection] = { caret: Location ~ TEditSelection.InsertionPoint[tSel]; ok: BOOL ¬ FALSE; IF caret.node#NIL AND caret.where#NodeItself THEN { resultStart, resultLen: INT ¬ 0; TEditSelection.Deselect[]; [ok, resultStart, resultLen] ¬ proc[node: caret.node, caret: caret.where, event: TEditInput.currentEvent]; IF ok THEN TEditSelection.MakePointSelection[tSel, [caret.node, resultStart+resultLen]] ELSE TEditSelection.MakeSelection[tSel]; }; IF NOT ok THEN TEditInputOps.EditFailed[]; }; TEditInputOps.CallWithLocks[DoMungeChar]; }; MakeControlCharacter: PUBLIC PROC ~ { MungeChar[TBase.MakeControlChar]; }; UnMakeControlCharacter: PUBLIC PROC ~ { MungeChar[TBase.UnMakeControlChar]; }; MakeOctalCharacter: PUBLIC PROC ~ { MungeChar[TBase.MakeOctalChar]; }; UnMakeOctalCharacter: PUBLIC PROC ~ { MungeChar[TBase.UnMakeOctalChar]; }; GetWord: PROC RETURNS [word: ROPE] = { tSel: Selection; start: INT; pos: Location; node: Node; nChars: CARDINAL ¬ 0; TEditSelection.CaretAfterSelection; pos ¬ TEditSelection.InsertionPoint[]; IF (node ¬ pos.node)=NIL THEN RETURN [NIL]; IF pos.where = NodeItself THEN RETURN [NIL]; start ¬ pos.where-1; WHILE start>=0 DO c: Char.XCHAR ~ TextEdit.FetchChar[node, start]; IF NOT CharOps.XAlphaNumeric[c] THEN EXIT; start ¬ start - 1; nChars ¬ nChars + 1; ENDLOOP; IF nChars = 0 THEN RETURN [NIL]; start ¬ pos.where-nChars; word ¬ Rope.Substr[TextEditBogus.GetRope[node], 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: 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 ! 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: Selection] = { name: ROPE ¬ GetWord[]; IF name=NIL THEN { TEditInputOps.EditFailed[]; RETURN }; SetFormatName[name] }; TEditInputOps.CallWithLocks[DoSetFormat] }; GetFormat: PUBLIC PROC = { DoGetFormat: PROC [root: Node, tSel: Selection] = { name: ROPE; format: ATOM ¬ TextEdit.GetFormat[TEditSelection.InsertionPoint[].node]; name ¬ IF format=NIL THEN "default" ELSE Atom.GetPName[format]; TEditInputOps.InsertRope[name] }; TEditInputOps.CallWithLocks[DoGetFormat] }; TransposeFormat: PUBLIC PROC [target: TEditDocument.SelectionId ¬ primary] = { targetSel: Selection ¬ IF target=primary THEN TEditSelection.pSel ELSE TEditSelection.sSel; srcSel: Selection ¬ IF target=primary THEN TEditSelection.sSel ELSE TEditSelection.pSel; DoTransFormat: PROC [sourceRoot, destRoot: Node, tSel, srcSel, targetSel: Selection] = { targetFormat, srcFormat: ATOM; targetNode, srcNode: Node; srcNode ¬ IF srcSel.insertion=before THEN srcSel.start.pos.node ELSE srcSel.end.pos.node; srcFormat ¬ TextEdit.GetFormat[srcNode]; targetNode ¬ IF targetSel.insertion=before THEN targetSel.start.pos.node ELSE targetSel.end.pos.node; targetFormat ¬ TextEdit.GetFormat[targetNode]; TEditSelection.Copy[source: targetSel, dest: TEditSelection.oldSel]; -- save for Repeat's TBase.SetFormat[node1: targetSel.start.pos.node, node2: targetSel.end.pos.node, format: srcFormat, event: TEditInput.currentEvent]; TBase.SetFormat[node1: srcSel.start.pos.node, node2: srcSel.end.pos.node, format: targetFormat, event: TEditInput.currentEvent]; TEditSelection.MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary] }; TEditInputOps.CallWithBothLocked[DoTransFormat, targetSel, srcSel, write] }; CopyFormat: PUBLIC PROC [target: TEditDocument.SelectionId ¬ primary] = { targetSel: Selection ¬ IF target=primary THEN TEditSelection.pSel ELSE TEditSelection.sSel; srcSel: Selection ¬ IF target=primary THEN TEditSelection.sSel ELSE TEditSelection.pSel; DoCopyFormat: PROC [sourceRoot, destRoot: Node, tSel, srcSel, targetSel: Selection] = { srcNode: Node ¬ IF srcSel.insertion=before THEN srcSel.start.pos.node ELSE srcSel.end.pos.node; format: ATOM ¬ TextEdit.GetFormat[srcNode]; TEditSelection.Copy[source: srcSel, dest: TEditSelection.oldSel]; -- save for Repeat's TBase.SetFormat[node1: targetSel.start.pos.node, node2: targetSel.end.pos.node, format: format, event: TEditInput.currentEvent]; 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.PutFormat[node, format, TEditInput.currentEvent]; TEditLocks.Unlock[root]; IF lockSel THEN TEditSelection.UnlockSel[primary] } }; SetCommentProp: PUBLIC PROC [flag: BOOLEAN] = { DoSetCommentProp: PROC [root: Node, tSel: Selection] = { TBase.SetComment[node1: tSel.start.pos.node, node2: tSel.end.pos.node, comment: flag, event: TEditInput.currentEvent]; }; 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: Selection] = { pos: Location; node: Node; offset: INT; done, keyDeterminesDict: BOOLEAN ¬ FALSE; clearedMessageWindow: BOOLEAN ¬ FALSE; keyStart, keyLen, resultLen: INT ¬ 0; 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[TextEditBogus.GetRope[node], 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, TextEditBogus.GetRope[node], 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[dictName ! PFS.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] = { InsertRope[Rope.FromChar[char]]; }; InsertRope: PUBLIC PROC [rope: ROPE] = { DoInsertRope: PROC [root: Node, tSel: Selection] = { pos: Location; 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]; pos.where ¬ pos.where + TextEdit.ReplaceByRope[root: root, dest: pos.node, start: pos.where, len: 0, rope: rope, looks: looks, charSet: 0, charProps: NIL, event: TEditInput.currentEvent].resultLen; TEditSelection.MakePointSelection[tSel, pos]; IF TEditSelection.CaretVisible[] THEN TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE] }; TEditInputOps.CallWithLocks[DoInsertRope]; }; InsertLineBreak: PUBLIC PROC = { DoInsertLineBreak: PROC [root: Node, tSel: Selection] = { pos: 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 ¬ TextEditBogus.GetRope[node]; start ¬ lim ¬ MAX[0, MIN[pos.where, Rope.Size[rope]]]; WHILE start > 0 AND NOT (SELECT Rope.Fetch[rope, start-1] FROM Ascii.CR, Ascii.LF => TRUE, ENDCASE => FALSE) DO start ¬ start-1; ENDLOOP; end ¬ start; WHILE end < lim AND CharOps.Blank[Rope.Fetch[rope, end]] DO end ¬ end+1; ENDLOOP; TEditInputOps.InsertRope[Rope.Concat[TextEdit.GetNewlineDelimiter[root], Rope.Substr[rope, start, end-start]]]; }; TEditInputOps.CallWithLocks[DoInsertLineBreak]; }; DeleteNextChar: PUBLIC PROC [count: INT ¬ 1] = { DoDeleteNextChar: PROC [root: Node, tSel: Selection] = { flush: Location ¬ TEditSelection.InsertionPoint[tSel]; node: Node ¬ flush.node; IF node = NIL THEN GOTO Bad; IF flush.where=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: Selection] = { loc: Location ¬ TEditSelection.InsertionPoint[tSel]; node: Node ¬ loc.node; IF node = NIL OR loc.where=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: Selection] = { loc: Location ¬ TEditSelection.InsertionPoint[tSel]; node: Node ¬ loc.node; IF node = NIL OR loc.where=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] = { RETURN[CharOps.XAlphaNumeric[TextEdit.FetchChar[node, index]]]; }; nChars ¬ 1; WHILE offset TEditInputOps.EditFailed[]; }; IF count > 0 THEN TEditInputOps.CallWithLocks[DoDeleteNextWord]; }; GoToNextWord: PUBLIC PROC [count: INT ¬ 1] = { DoGoToNextWord: PROC [root: Node, tSel: Selection] = { pos: Location ¬ TEditSelection.InsertionPoint[tSel]; node: Node ¬ pos.node; size, next: INT; IF node=NIL OR (next ¬ pos.where)=NodeItself THEN { -- try next node GoToNextNode; RETURN }; size ¬ TextEdit.Size[node]; THROUGH [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: Selection] = { pos: Location ¬ TEditSelection.InsertionPoint[tSel]; node: Node ¬ pos.node; THROUGH [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: Selection] = { pos: Location ¬ TEditSelection.InsertionPoint[tSel]; node: Node ¬ pos.node; THROUGH [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]; }; InsertTime: PUBLIC PROC = { DoInsertTime: PROC [root: Node, tSel: Selection] = { resultStart, resultLen, dateLen: INT; caret: Location; looks: TextLooks.Looks ¬ TEditSelection.pSel.looks; IF TEditSelection.pSel.pendingDelete THEN TEditInputOps.DoPendingDelete[]; caret ¬ TEditSelection.InsertionPoint[TEditSelection.pSel]; TEditSelection.Deselect[primary]; [resultStart: resultStart, resultLen: resultLen, dateLen: dateLen] ¬ TBase.InsertTime[ node: caret.node, index: caret.where, gmt: BasicTime.Now[], looks: looks, event: TEditInput.currentEvent]; CharSelectionFromLocations[tSel, [caret.node, resultStart+dateLen], [caret.node, resultStart+resultLen]]; tSel.insertion ¬ after; tSel.pendingDelete ¬ FALSE; TEditSelection.MakeSelection[selection: primary, new: tSel]; }; TEditInputOps.CallWithLocks[DoInsertTime]; }; LocationsFromSelection: PROC [tSel: Selection] RETURNS [loc1, loc2: Location] ~ { loc1 ¬ tSel.start.pos; loc2 ¬ tSel.end.pos; IF loc1.node=NIL OR loc2.node=NIL THEN RETURN[[NIL, 0], [NIL, 0]]; IF loc1.where=NodeItself THEN loc1.where ¬ 0; IF tSel.granularity=point THEN loc2 ¬ loc1 ELSE IF loc2.where=NodeItself THEN loc2.where ¬ TextEdit.Size[loc2.node] ELSE loc2.where ¬ loc2.where+1; }; CharSelectionFromLocations: PROC [tSel: Selection, loc1, loc2: Location] ~ { IF loc1.node=loc2.node AND loc1.where=loc2.where THEN PointSelectionFromLocation[tSel, loc1] ELSE { tSel.start.pos ¬ loc1; tSel.end.pos ¬ [loc2.node, loc2.where-1]; tSel.granularity ¬ char; }; }; PointSelectionFromLocation: PROC [tSel: Selection, loc: Location] ~ { tSel.start.pos ¬ tSel.end.pos ¬ loc; tSel.granularity ¬ point; }; InsertBrackets: PUBLIC PROC [left, right: CHAR] = { InsertBracketRopes[Rope.FromChar[left], Rope.FromChar[right]]; }; InsertBracketRopes: PROC [rope1, rope2: ROPE, includeInSelection: BOOL ¬ FALSE] ~ { DoInsertBracketRopes: PROC [root: Node, tSel: Selection] = { loc1, loc2: Location; [loc1, loc2] ¬ LocationsFromSelection[tSel]; IF loc1.node=NIL THEN GOTO Bad; TEditSelection.Deselect[primary]; [result1: loc1, result2: loc2] ¬ TBase.InsertBrackets[ loc1: loc1, loc2: loc2, rope1: rope1, rope2: rope2, looks: tSel.looks, props: NIL, includeInResult: includeInSelection, event: TEditInput.currentEvent]; CharSelectionFromLocations[tSel, loc1, loc2]; tSel.pendingDelete ¬ FALSE; TEditSelection.MakeSelection[selection: primary, new: tSel]; EXITS Bad => TEditInputOps.EditFailed[]; }; TEditInputOps.CallWithLocks[DoInsertBracketRopes]; }; 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 ¬ FALSE] ~ { DoSelect: PROC [root: Node, tSel: Selection] ~ { loc1, loc2: Location; [loc1, loc2] ¬ LocationsFromSelection[tSel]; [found, loc1, loc2] ¬ TBase.ExpandToBrackets[ char1: Char.Widen[left], char2: Char.Widen[right], loc1: loc1, loc2: loc2]; IF found THEN { CharSelectionFromLocations[tSel, loc1, loc2]; TEditSelection.SetSelLooks[tSel]; TEditSelection.MakeSelection[tSel]; }; }; TEditInputOps.CallWithLocks[DoSelect, read]; }; CallWithPrimarySelectionLocked: PROC [who: ROPE, action: PROC [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: 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]; }; TryToGetLooks: PROC [node: Node, index: INT, default: Looks] RETURNS [Looks] ~ { RETURN[IF index IN[0..TextEdit.Size[node]) THEN TextEdit.FetchLooks[node, index] ELSE default]; }; DoFindPlaceholders: PUBLIC PROC [next, gotoend: BOOL, startBoundaryNode, endBoundaryNode: Node ¬ NIL, startBoundaryOffset: INT ¬ 0, endBoundaryOffset: INT ¬ LAST[INT]] RETURNS [found, wenttoend: BOOL ¬ FALSE] = { DoFind: PROC [root: Node, tSel: Selection] = { direction: TBase.Direction ~ (IF next THEN forward ELSE backward); from: Location ¬ TEditSelection.InsertionPoint[tSel]; start: INT ¬ from.where; result1, result2: Location; SELECT direction FROM forward => IF tSel.insertion=before AND tSel.granularity#point AND start IF tSel.insertion=after AND start>0 THEN start ¬ start-1; ENDCASE; [found, result1, result2] ¬ TBase.FindBrackets[direction: direction, char1: Char.Widen['], char2: Char.Widen['], loc: [from.node, start], bound1: [startBoundaryNode, startBoundaryOffset], bound2: [endBoundaryNode, endBoundaryOffset], includeInResult: TRUE]; IF found THEN { TEditInput.CloseEvent[]; CharSelectionFromLocations[tSel, result1, result2]; tSel.insertion ¬ before; tSel.pendingDelete ¬ TRUE; tSel.looks ¬ TryToGetLooks[result1.node, result1.where+1, noLooks]; -- char after  } ELSE IF gotoend THEN { end: Location ~ SELECT direction FROM forward => TextNode.LastLocWithin[root], backward => [TextNode.FirstChild[root], 0], ENDCASE => ERROR; IF from#end OR tSel.granularity#point THEN { PointSelectionFromLocation[tSel, end]; wenttoend ¬ TRUE; }; }; IF found OR wenttoend THEN { TEditOps.RememberCurrentPosition[tSel.viewer]; TEditSelection.MakeSelection[tSel, primary]; 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: 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. l TEditMiscOpsImpl.mesa Copyright Ó 1985, 1987, 1988, 1991, 1992 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, March 23, 1989 2:13:01 pm PST Russ Atkinson (RRA) January 23, 1986 0:10:22 am PST Willie-s, February 13, 1991 3:43 pm PST Doug Wyatt, March 13, 1992 2:13 pm PST Transpose the formats of the primary and secondary selections DoInsertChar: PROC [root: Node, tSel: Selection] = { pos: 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.ReplaceByRope[root: root, dest: pos.node, start: pos.where, len: 0, rope: Rope.FromChar[char], 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]; copy leading blanks from previous line insert rope1 before selection, rope2 after selection extend selection until includes matching left and right brackets TEditOps Implementation Êé–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ ÏeœI™TKšÏy™Kšž!™!Kšœ)™)Kšœ,™,Kšœ3™3K™'K™&K™—šÏk ˜ Kšœ Ÿœ˜"KšœŸœŸœŸœ ˜KšœŸœ˜ Kšœ Ÿœ˜Kšœ˜Kšœ˜Kšœ Ÿœ˜KšœŸœ˜$Kšœ Ÿœ!˜3KšœŸœ˜,KšŸœŸœ ˜KšœŸœ.Ÿœ˜LKšœ ŸœK˜[Kšœ˜KšœŸœ˜-Kšœ Ÿœ'˜7KšœŸœw˜ŠKšœ Ÿœ˜ Kšœ Ÿœ˜)Kšœ Ÿœ˜&KšœŸœ˜"KšœŸœ¹˜ÍKšœ Ÿœ˜ Kšœ Ÿœr˜€KšœŸœ ˜Kšœ Ÿœ ˜Kšœ ŸœF˜TKšœ˜KšœŸœ ˜Kšœ Ÿœ˜Kšœ Ÿœ˜!K˜—KšÏnœŸœŸ˜KšŸœmŸœÁ˜¸KšŸœ˜šœŸ˜K˜KšŸœŸœŸœ˜KšœŸœ˜Kšœ Ÿœ˜ Kšœ Ÿœ˜#KšœŸœ˜Kšœ˜šœ Ÿœ˜*K˜—š  œŸœŸœ˜5š  œŸœ"˜4K˜K˜[KšœŸœ˜K˜,K˜—K˜*K˜—K˜š  œŸœ˜+š  œŸœ"˜3Kšœ6˜6KšœŸœŸœ˜šŸœ ŸœŸœŸœ˜3KšœŸœ˜ K˜Kšœk˜kKšŸœŸœM˜WKšŸœ$˜(K˜—KšŸœŸœŸœ˜*K˜—Kšœ)˜)K˜—K˜š œŸœŸœ˜%Kšœ!˜!Kšœ˜K˜—š œŸœŸœ˜'Kšœ#˜#Kšœ˜K˜—š œŸœŸœ˜#Kšœ˜Kšœ˜K˜—š œŸœŸœ˜%Kšœ!˜!Kšœ˜K˜—K˜š œŸœŸœŸœ˜&Kšœ˜KšœŸœ˜ Kšœ˜Kšœ ˜ KšœŸœ˜K˜#K˜&Kš ŸœŸœŸœŸœŸœ˜+KšŸœŸœŸœŸœ˜,K˜šŸœ Ÿ˜KšœŸœ#˜0KšŸœŸœŸœŸœ˜*K˜'KšŸœ˜—KšŸœ ŸœŸœŸœ˜ K˜K˜?K˜K˜=K˜K˜&K˜K˜,K˜K˜K˜—K˜š œŸœŸœ˜š  œŸœ"˜2KšœŸœ ˜KšŸœŸœŸœŸœ˜8K˜K˜—K˜(K˜—K˜š   œŸœŸœŸœŸœ˜-Kš œŸœŸœŸœ3˜OKšœŸœ˜ KšŸœ&˜,K˜—K˜š   œŸœŸœŸœŸœ˜šŸœ Ÿœ¡˜+Kšœ)Ÿœ˜/K˜K˜NK˜—K˜K™—š  œŸœŸœŸœ˜(Kšœ ˜ š  œŸœ"™4Kšœ™K™$šŸœŸœ™K™ K™=K™—Kšœ:¡3™mK™!™(K™)Kšœ)™)K™ —K™K™-KšŸœŸœ,Ÿœ™WK™—K™*K˜—K˜š  œŸœŸœŸœ˜(š  œŸœ"˜4Kšœ˜K˜šŸœŸœ˜K˜ K˜=K˜—Kšœ:¡3˜mK˜K˜!Kšœ˜Ÿœ-˜ÈK˜-KšŸœŸœ,Ÿœ˜WK˜—K˜*K˜—K˜š œŸœŸœ˜ Kšœ&™&š œŸœ"˜9Kšœ˜Kšœ ˜ KšœŸœ˜KšœŸœ˜ šŸœŸœ˜K˜ K˜=K˜—Kšœ:¡3˜mKšŸœŸœŸœŸœ˜GKšœ#˜#KšœŸœŸœ˜6KšŸœ ŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜‰K˜ šŸœ Ÿœ&Ÿ˜;K˜ KšŸœ˜—Kšœo˜oK˜—K˜/K˜—K˜š œŸœŸœ Ÿœ ˜0š œŸœ"˜8Kšœ6˜6Kšœ˜KšŸœŸœŸœŸœ˜KšŸœŸœŸœ˜(KšŸœ Ÿœ/ŸœŸœ˜LK˜!K˜MK˜/Kšœ+Ÿœ˜1KšŸœ#˜(K˜—KšŸœ Ÿœ/˜@K˜—K˜š  œŸœŸœ Ÿœ ˜.š œŸœ"˜6Kšœ4˜4Kšœ˜KšŸœŸœŸ˜KšœŸ˜šœ Ÿœ-Ÿœ¡˜PKšœŸœ˜K˜—K˜AKšœ+Ÿœ˜1K˜—KšŸœ Ÿœ3˜DK˜—K˜š œŸœŸœ Ÿœ ˜2š œŸœ"˜:Kšœ4˜4Kšœ˜KšŸœŸœŸ˜KšœŸœ˜*KšŸœŸœ¡˜7K˜AKšœ+Ÿœ˜1K˜—KšŸœ Ÿœ7˜HK˜—K˜š   œŸœŸœŸœŸœ Ÿœ˜RKšœŸœ ˜KšœŸœ˜ š  œŸœ ŸœŸœŸœ˜+KšŸœ9˜?K˜—K˜ šŸœ ŸœŸœŸ˜*K˜K˜KšŸœ˜—šŸœ ŸœŸ˜&K˜K˜KšŸœ˜—Kšœ¡#˜8K˜—K˜š œŸœŸœ Ÿœ ˜0š œŸœ"˜8Kšœ4˜4Kšœ˜KšœŸœ˜KšŸœ"ŸœŸœ˜2KšŸœŸœŸœŸœ˜K˜ KšŸœ Ÿœ&Ÿœ˜CK˜K˜!K˜HK˜-Kšœ+Ÿœ˜1KšŸœ#˜(K˜—KšŸœ Ÿœ/˜@K˜—K˜š  œŸœŸœ Ÿœ ˜.š œŸœ"˜6Kšœ4˜4Kšœ˜Kšœ Ÿœ˜KšŸœŸœŸ˜šœŸœ¡˜5KšœŸœ˜K˜—K˜šŸœ Ÿ˜KšŸœŸœŸœ˜0K˜%KšŸœ˜—K˜6Kšœ+Ÿœ˜1K˜—KšŸœ Ÿœ3˜DK˜—K˜š  œŸœŸœ Ÿœ ˜.š œŸœ"˜6Kšœ4˜4Kšœ˜šŸœ Ÿ˜KšŸœ%ŸœŸœŸœ˜WKšŸœ˜—K˜3Kšœ+Ÿœ˜1K˜—KšŸœ Ÿœ3˜DK˜—K˜š œŸœŸœ Ÿœ ˜2š œŸœ"˜:Kšœ4˜4Kšœ˜šŸœ Ÿ˜KšŸœ&ŸœŸ˜.KšœŸœŸœŸœ˜(KšŸœ˜—KšŸœŸœŸœŸœ˜K˜EKšœ+Ÿœ˜1KšŸœ#˜(K˜—KšŸœ Ÿœ7˜HK˜—K˜š  œŸœŸœ˜š  œŸœ"˜4Kšœ!Ÿœ˜%Kšœ˜K˜3KšŸœ#Ÿœ!˜JK˜;K˜!KšœÂ˜ÂKšœj˜jK˜KšœŸœ˜K˜˜>K˜K™—š  œŸœŸœŸœŸœ˜SKšœ4™4š œŸœ"˜