DIRECTORY Ascii USING [Upper], EditSpan USING [ChangeLooks, ChangeNesting, CannotDoEdit, Copy, Delete, InsertTextNode, Merge, Move, MoveOnto, Place, Replace, SaveForPaste, SavedForPaste, Split, Transpose], EditSpanSupport USING [Apply], MessageWindow USING [Append, Blink], Rope USING [ROPE, Substr, Concat], RopeEdit USING [AlphaNumericChar], TEditDisplay USING [EstablishLine], TEditDocument USING [BeforeAfter, Selection, SelectionId, SelectionGrain, SelectionPoint, SelectionRec, TEditDocumentData], TEditImpl USING [CaretAtEnd], TEditInput USING [currentEvent], TEditInputOps USING [GoToPreviousNode, ModifyOp], TEditLocks USING [Access, Lock, LockBoth, LockRef, Unlock], TEditOps USING [], TEditProfile USING [editTypeScripts], TEditRefresh USING [ScrollToEndOfSel], TEditSelection USING [Alloc, CaretVisible, Copy, Free, Deselect, GetSelectionGrain, InsertionPoint, LockBothSelections, LockSel, MakeSelection, MakePointSelection, nilSel, oldSel, pSel, SelectionRoot, sSel, UnlockBothSelections, UnlockDocAndPSel, UnlockSel], TextEdit USING [DeleteText, FetchChar, FetchLooks, FromRope, Size], TextLooks USING [Look, Looks, allLooks, noLooks], TextLooksSupport USING [ModifyLooks], TextNode USING [EndPos, FirstChild, LastLocWithin, Level, Location, MakeNodeLoc, MakeNodeSpan, NarrowToTextNode, NodeItself, nullSpan, Parent, Ref, RefTextNode, Root, Span, StepForward, StepBackward], TypeScript USING [ChangeLooks], ViewerClasses USING [Viewer]; TEditInputOpsImpl: CEDAR PROGRAM IMPORTS Ascii, EditSpan, MessageWindow, EditSpanSupport, Rope, RopeEdit, TextEdit, TextLooksSupport, TextNode, TEditDisplay, TEditImpl, TEditInput, TEditInputOps, TEditLocks, TEditProfile, TEditRefresh, TEditSelection, TypeScript EXPORTS TEditInputOps, TEditOps = BEGIN OPEN TEditDocument, TEditSelection; RefTextNode: TYPE ~ TextNode.RefTextNode; CallWithLocks: PUBLIC PROC [proc: PROC [root: RefTextNode, tSel: Selection], access: TEditLocks.Access _ write] = { root: RefTextNode; tSel: Selection _ NIL; lockRef: TEditLocks.LockRef _ NIL; { ENABLE UNWIND => { UnlockSel[primary]; IF lockRef # NIL THEN TEditLocks.Unlock[root]; IF tSel # NIL THEN Free[tSel]; }; LockSel[primary, "CallWithLocks"]; IF (access=write AND ~CheckReadonly[pSel]) OR (root _ SelectionRoot[pSel])=NIL THEN { UnlockSel[primary]; RETURN }; lockRef _ TEditLocks.Lock[root, "CallWithLocks", access]; tSel _ Alloc[]; TEditSelection.Copy[source: pSel, dest: tSel]; proc[root, tSel]; Free[tSel]; tSel _ NIL; UnlockDocAndPSel[root]; }; }; CallWithBothLocked: PUBLIC PROC [ proc: PROC [sourceRoot, destRoot: RefTextNode, tSel, srcSel, targetSel: Selection], targetSel, srcSel: Selection, sourceAccess: TEditLocks.Access] = { sourceRoot, destRoot: RefTextNode; tSel, tSel1, tSel2: Selection _ NIL; lockRef: TEditLocks.LockRef _ NIL; Cleanup: PROC = { IF lockRef # NIL THEN { TEditLocks.Unlock[sourceRoot]; TEditLocks.Unlock[destRoot] }; IF tSel # NIL THEN Free[tSel]; IF tSel1 # NIL THEN Free[tSel1]; IF tSel2 # NIL THEN Free[tSel2]; UnlockBothSelections[] }; { ENABLE UNWIND => Cleanup[]; LockBothSelections["CallWithBothLocked"]; IF srcSel.viewer=NIL OR targetSel.viewer=NIL THEN { UnlockBothSelections[]; RETURN }; IF ~CheckReadonly[targetSel] OR -- don't edit a readonly document (sourceAccess=write AND ~CheckReadonly[srcSel]) THEN { Deselect[$secondary]; UnlockBothSelections[]; RETURN }; tSel1 _ Alloc[]; tSel2 _ Alloc[]; tSel _ Alloc[]; TEditSelection.Copy[source: srcSel, dest: tSel1]; TEditSelection.Copy[source: targetSel, dest: tSel2]; TEditSelection.Copy[source: targetSel, dest: tSel]; sourceRoot _ SelectionRoot[srcSel]; destRoot _ SelectionRoot[targetSel]; [lockRef, ----] _ TEditLocks.LockBoth[ sourceRoot, destRoot, "CallWithBothLocked", sourceAccess, write]; Deselect[$secondary]; Deselect[$primary]; proc[sourceRoot: sourceRoot, destRoot: destRoot, tSel: tSel, srcSel: tSel1, targetSel: tSel2]; }; Cleanup[]; }; pdelNode: RefTextNode = TextEdit.FromRope["!"]; DoPendingDelete: PUBLIC PROC = { PendingDelete: PROC [root: RefTextNode, tSel: Selection] = { pSel.pendingDelete _ TRUE; tSel^ _ nilSel^; tSel.granularity _ char; tSel.looks _ pSel.looks; tSel.start.pos _ tSel.end.pos _ [pdelNode,0]; tSel.pendingDelete _ FALSE; CopyToDoc[targetSel: pSel, srcSel: tSel, target: primary, lock: FALSE]; Delete[saveForPaste: FALSE]; }; CallWithLocks[PendingDelete] }; EditFailed: PUBLIC PROC [msg: Rope.ROPE _ NIL] = { MessageWindow.Append[IF msg=NIL THEN "Can't do it." ELSE msg, TRUE]; MessageWindow.Blink[]; }; CaretLoc: PUBLIC PROC [s: Selection _ NIL] RETURNS [TextNode.Location] = { IF s=NIL THEN s _ pSel; IF GetSelectionGrain[s] = node THEN SELECT s.insertion FROM before => RETURN[[s.start.pos.node, TextNode.NodeItself]]; after => RETURN[[s.end.pos.node, TextNode.NodeItself]]; ENDCASE => ERROR ELSE SELECT s.insertion FROM before => RETURN[s.start.pos]; after => RETURN[[s.end.pos.node, s.end.pos.where+1]]; ENDCASE => ERROR; }; NodeSpanFromSelection: PROC[sel: Selection] RETURNS[span: TextNode.Span] = { RETURN[TextNode.MakeNodeSpan[first: sel.start.pos.node, last: sel.end.pos.node]] }; SelectionSpan: PROC[sel: Selection] RETURNS[span: TextNode.Span] = { IF GetSelectionGrain[sel]=node THEN RETURN[NodeSpanFromSelection[sel]] ELSE RETURN[[start: sel.start.pos, end: sel.end.pos]]; }; Delete: PUBLIC PROC [saveForPaste: BOOL _ TRUE] = { DoDelete: PROC [root: RefTextNode, tSel: Selection] = { Deselect[$primary]; tSel.pendingDelete _ FALSE; SELECT GetSelectionGrain[tSel] FROM point => NULL; node => { -- this is complex because must worry about deleting all of the document newSelNode: RefTextNode _ TextNode.StepForward[tSel.end.pos.node]; newSelText: RefTextNode _ TextNode.NarrowToTextNode[newSelNode]; newSelInsertion: BeforeAfter; IF newSelNode=NIL THEN { -- deleting the last node of the doc newSelNode _ TextNode.StepBackward[tSel.start.pos.node]; IF newSelNode=NIL OR newSelNode=root THEN { -- deleting all the nodes child: RefTextNode _ TextNode.FirstChild[root]; tSel.start.pos.node _ child; newSelNode _ EditSpan.InsertTextNode[root: root, old: child, where: before, inherit: FALSE, event: TEditInput.currentEvent]; }; newSelText _ TextNode.NarrowToTextNode[newSelNode]; newSelInsertion _ IF newSelText#NIL AND TextEdit.Size[newSelText] > 0 THEN after ELSE before } ELSE { newSelInsertion _ before }; EditSpan.Delete[ root: SelectionRoot[tSel], del: NodeSpanFromSelection[tSel], event: TEditInput.currentEvent, saveForPaste: saveForPaste ! EditSpan.CannotDoEdit => GOTO Bad]; IF newSelText # NIL THEN { tSel.start.pos.node _ tSel.end.pos.node _ newSelText; tSel.start.pos.where _ tSel.end.pos.where _ IF newSelInsertion=before THEN 0 ELSE TextEdit.Size[newSelText] -- -1? --; tSel.granularity _ point } ELSE { -- the new selection node is not a text node tSel.start.pos _ tSel.end.pos _ TextNode.MakeNodeLoc[newSelNode]; tSel.granularity _ node }; tSel.insertion _ newSelInsertion; }; ENDCASE => { EditSpan.Delete[ root: SelectionRoot[tSel], del: [tSel.start.pos, tSel.end.pos], event: TEditInput.currentEvent, saveForPaste: saveForPaste ! EditSpan.CannotDoEdit => GOTO Bad]; tSel.end.pos _ tSel.start.pos; tSel.granularity _ point; tSel.insertion _ before; }; MakeSelection[selection: primary, new: tSel]; EXITS Bad => { MakeSelection[selection: primary, new: tSel]; EditFailed[] } }; CallWithLocks[DoDelete]; }; BackSpace: PUBLIC PROC [count: INT _ 1] = { DoBackSpace: PROC [root: RefTextNode, tSel: Selection] = { node: RefTextNode; flush: TextNode.Location _ InsertionPoint[tSel]; IF flush.where=TextNode.NodeItself THEN GOTO Bad; IF (node _ TextNode.NarrowToTextNode[flush.node])=NIL THEN GOTO Bad; IF flush.where=0 THEN { Join[]; RETURN }; -- already at start of node; do a Join IF (count _ MIN[count,flush.where]) <= 0 THEN GOTO Bad; Deselect[$primary]; flush.where _ flush.where-count; TextEdit.DeleteText[root, node, flush.where, count, TEditInput.currentEvent]; MakePointSelection[tSel, flush]; EXITS Bad => EditFailed[] }; IF count > 0 THEN CallWithLocks[DoBackSpace]; }; FindPrevWord: PUBLIC PROC [node: RefTextNode, offset: INT] RETURNS [nChars: CARDINAL] = { Alpha: PROC [index: INT] RETURNS [BOOL] ~ { set: NAT; char: CHAR; [set, char] _ TextEdit.FetchChar[node, index]; RETURN [set=0 AND RopeEdit.AlphaNumericChar[char]]; }; nChars _ 0; WHILE offset>0 AND NOT Alpha[offset-1] DO offset _ offset - 1; nChars _ nChars + 1; ENDLOOP; WHILE offset>0 AND Alpha[offset-1] DO offset _ offset - 1; nChars _ nChars + 1; ENDLOOP; }; BackWord: PUBLIC PROC [count: INT _ 1] = { DoBackWord: PROC [root: RefTextNode, tSel: Selection] = { node: RefTextNode; nChars: CARDINAL _ 0; pos: TextNode.Location _ InsertionPoint[tSel]; IF (node _ TextNode.NarrowToTextNode[pos.node])=NIL THEN GOTO Bad; IF pos.where = TextNode.NodeItself THEN GOTO Bad; Deselect[$primary]; FOR garbage:INT IN [0..count) DO wChars: CARDINAL _ FindPrevWord[node,pos.where]; pos.where _ pos.where - wChars; nChars _ nChars + wChars; ENDLOOP; TextEdit.DeleteText[root, node, pos.where, nChars, TEditInput.currentEvent]; MakePointSelection[tSel, pos]; EXITS Bad => EditFailed[]; }; IF count > 0 THEN CallWithLocks[DoBackWord]; }; GoToPreviousWord: PUBLIC PROC [count: INT _ 1] = { DoGoToPreviousWord: PROC [root: RefTextNode, tSel: Selection] = { pos: TextNode.Location; node: RefTextNode; nChars: CARDINAL; pos _ InsertionPoint[tSel]; FOR garbage:INT IN [0..count) DO IF (node _ TextNode.NarrowToTextNode[pos.node])=NIL OR pos.where=TextNode.NodeItself OR pos.where=0 THEN { TEditInputOps.GoToPreviousNode; RETURN }; nChars _ FindPrevWord[node,pos.where]; pos.where _ pos.where - nChars; ENDLOOP; MakePointSelection[tSel,pos]; }; IF count > 0 THEN CallWithLocks[DoGoToPreviousWord, read]; }; CopyToTypeScript: PROC [targetSel: Selection, source: TextNode.Span] = { TSCopy: PROC [node: RefTextNode, start, len: INT] RETURNS [stop: BOOL] = { rope: Rope.ROPE _ Rope.Substr[node.rope, start, len]; IF node # source.end.node THEN rope _ Rope.Concat[rope, "\n"]; -- add CR's between nodes targetSel.viewer.class.notify[targetSel.viewer, LIST[rope]]; RETURN [FALSE]; }; EditSpanSupport.Apply[source,TSCopy]; }; UnConvertNodeSelects: PROC [sel: Selection] = { SELECT GetSelectionGrain[sel] FROM char => { IF sel.end.pos.node=sel.start.pos.node AND sel.end.pos.where { sel.start.pos.where _ 0; sel.end.pos.where _ TextNode.EndPos[sel.end.pos.node]; }; ENDCASE; }; CopyToDoc: PROC [targetSel, srcSel: Selection, target: SelectionId, lock: BOOL _ TRUE] = { DoCopyToDoc: PROC [sourceRoot, destRoot: RefTextNode, tSel, srcSel, targetSel: Selection] = { IF tSel.pendingDelete THEN { -- replace target by source [start: tSel.start.pos, end: tSel.end.pos] _ EditSpan.Replace[ destRoot: destRoot, sourceRoot: sourceRoot, dest: SelectionSpan[tSel], source: SelectionSpan[srcSel], saveForPaste: TRUE, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad].result; tSel.pendingDelete _ FALSE; } ELSE { -- source goes to target caret loc: TextNode.Location _ InsertionPoint[tSel]; unnest: INTEGER _ 0; -- amount to unnest by if copying nodes after where: EditSpan.Place _ IF tSel.insertion = before THEN before ELSE after; IF GetSelectionGrain[srcSel]=node THEN { -- don't copy nodes into target node IF where=before AND loc.where>0 AND loc.where=TextEdit.Size[TextNode.NarrowToTextNode[loc.node]] THEN where _ after; -- caret at end of node, so copy after loc.where _ TextNode.NodeItself; IF where=after AND tSel.start.pos.node#tSel.end.pos.node THEN unnest _ TextNode.Level[tSel.end.pos.node]-TextNode.Level[tSel.start.pos.node]; }; [start: tSel.start.pos, end: tSel.end.pos] _ EditSpan.Copy[ destRoot: destRoot, sourceRoot: sourceRoot, dest: loc, source: SelectionSpan[srcSel], where: where, nesting: 0, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad].result; IF unnest > 0 THEN -- unnest so that copied span starts at same level as start of dest span [start: tSel.start.pos, end: tSel.end.pos] _ EditSpan.ChangeNesting[ root: destRoot, span: [tSel.start.pos, tSel.end.pos], change: -unnest, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => CONTINUE].new; }; tSel.granularity _ srcSel.granularity; UnConvertNodeSelects[tSel]; tSel.insertion _ after; MakeSelection[selection: primary, new: tSel]; TEditSelection.Copy[source: tSel, dest: oldSel]; -- save the copied selection for Repeat's EXITS Bad => { MakeSelection[selection: primary, new: IF target=primary THEN targetSel ELSE srcSel]; EditFailed[] } }; IF lock THEN CallWithBothLocked[DoCopyToDoc, targetSel, srcSel, read] ELSE { -- special hack for DoPendingDelete. tSel: Selection _ Alloc[]; TEditSelection.Copy[source: targetSel, dest: tSel]; DoCopyToDoc[SelectionRoot[srcSel], SelectionRoot[targetSel], tSel, srcSel, targetSel]; Free[tSel]; }; }; Copy: PUBLIC PROC [target: SelectionId _ primary] = { ENABLE UNWIND => UnlockBothSelections[]; targetSel: Selection _ IF target=primary THEN pSel ELSE sSel; srcSel: Selection _ IF target=primary THEN sSel ELSE pSel; LockBothSelections["Copy"]; IF srcSel.viewer#NIL AND targetSel.viewer#NIL THEN DoCopy[target, targetSel, srcSel]; UnlockBothSelections[]; }; CheckReadonly: PUBLIC PROC [targetSel: Selection] RETURNS [BOOL] = { IF targetSel.viewer=NIL OR NOT targetSel.data.readOnly THEN RETURN [TRUE]; EditFailed["Cannot modify read only document."]; RETURN [FALSE]; }; DoCopy: PROC [target: SelectionId, targetSel, srcSel: Selection] = { IF ~CheckReadonly[targetSel] THEN { Deselect[$secondary]; RETURN }; IF targetSel.data.tsInfo#NIL AND (~TEditProfile.editTypeScripts OR TEditImpl.CaretAtEnd[targetSel]) THEN { tSel: Selection _ Alloc[]; tSel1: Selection _ Alloc[]; span: TextNode.Span _ [srcSel.start.pos, srcSel.end.pos]; TEditSelection.Copy[source: sSel, dest: oldSel]; -- save the secondary selection for Repeat's TEditSelection.Copy[source: targetSel, dest: tSel]; tSel.insertion _ before; tSel.granularity _ point; tSel.start.pos _ tSel.end.pos _ TextNode.LastLocWithin[tSel.data.text]; TEditSelection.Copy[source: srcSel, dest: tSel1]; srcSel _ tSel1; Deselect[$primary]; Deselect[$secondary]; CopyToTypeScript[tSel, span]; IF target=primary THEN { -- leave primary as caret at end of typescript tSel.start.pos _ tSel.end.pos _ TextNode.LastLocWithin[tSel.data.text]; MakeSelection[selection: primary, new: tSel]; } ELSE MakeSelection[selection: primary, new: srcSel]; Free[tSel]; Free[tSel1]; } ELSE IF srcSel.pendingDelete THEN Move[target] ELSE CopyToDoc[targetSel, srcSel, target]; }; Paste: PUBLIC PROC = { DoPaste: PROC [root: RefTextNode, tSel: Selection] = { source: TextNode.Span _ EditSpan.SavedForPaste[]; tdd: TEditDocument.TEditDocumentData _ tSel.data; IF source = TextNode.nullSpan OR tSel.viewer=NIL THEN GOTO Bad; IF tdd.tsInfo=NIL AND tSel.pendingDelete THEN { DoPendingDelete[]; TEditSelection.Copy[source: pSel, dest: tSel] }; Deselect[$secondary]; tSel.start.pos _ source.start; tSel.end.pos _ source.end; IF source.start.where=TextNode.NodeItself OR source.end.where=TextNode.NodeItself THEN { tSel.granularity _ node; UnConvertNodeSelects[tSel] } ELSE tSel.granularity _ char; tSel.viewer _ pSel.viewer; -- else Copy will think there isn't a secondary selection tSel.data _ NIL; tSel.pendingDelete _ FALSE; DoCopy[primary, pSel, tSel]; EXITS Bad => EditFailed[]; }; CallWithLocks[DoPaste]; }; Move: PUBLIC PROC [target: SelectionId _ primary] = { targetSel: Selection _ IF target=primary THEN pSel ELSE sSel; srcSel: Selection _ IF target=primary THEN sSel ELSE pSel; DoMove: PROC [sourceRoot, destRoot: RefTextNode, tSel, srcSel, targetSel: Selection] = { pDel: BOOL; srcGrain: TEditDocument.SelectionGrain ~ GetSelectionGrain[srcSel]; IF srcGrain=node THEN { -- see if moving entire contents newSelNode: RefTextNode _ TextNode.StepForward[srcSel.end.pos.node]; IF newSelNode=NIL THEN { -- moving the last node of the doc newSelNode _ TextNode.StepBackward[srcSel.start.pos.node]; IF (newSelNode=NIL OR newSelNode=sourceRoot) AND destRoot # sourceRoot THEN { child: RefTextNode _ TextNode.FirstChild[sourceRoot]; srcSel.start.pos.node _ child; -- make sure doesn't include the root [] _ EditSpan.InsertTextNode[ root: sourceRoot, old: child, where: before, inherit: FALSE, event: TEditInput.currentEvent]; }; }; }; IF (pDel _ tSel.pendingDelete) THEN { -- move source onto target IF srcGrain=point THEN { EditSpan.Delete[ root: destRoot, del: SelectionSpan[tSel], event: TEditInput.currentEvent, saveForPaste: TRUE ! EditSpan.CannotDoEdit => GOTO Bad]; tSel.end.pos _ tSel.start.pos; tSel.granularity _ point; } ELSE { [start: tSel.start.pos, end: tSel.end.pos] _ EditSpan.MoveOnto[ destRoot: destRoot, sourceRoot: sourceRoot, dest: SelectionSpan[tSel], source: SelectionSpan[srcSel], saveForPaste: TRUE, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad].result; }; tSel.pendingDelete _ FALSE; } ELSE { -- move source to target caret loc: TextNode.Location _ InsertionPoint[tSel]; where: EditSpan.Place _ (IF tSel.insertion = before THEN before ELSE after); unnest: INTEGER _ 0; -- amount to unnest by if moving nodes after IF srcGrain=node THEN { -- don't move nodes into target node IF where = before AND loc.where > 0 AND loc.where = TextEdit.Size[TextNode.NarrowToTextNode[loc.node]] THEN where _ after; -- caret at end of node, so move after loc.where _ TextNode.NodeItself; IF where=after AND tSel.start.pos.node#tSel.end.pos.node THEN unnest _ TextNode.Level[tSel.end.pos.node]-TextNode.Level[tSel.start.pos.node]; }; IF srcGrain=point THEN { tSel.end.pos _ tSel.start.pos _ loc; tSel.granularity _ point; } ELSE { [start: tSel.start.pos, end: tSel.end.pos] _ EditSpan.Move[ destRoot: destRoot, sourceRoot: sourceRoot, dest: loc, source: SelectionSpan[srcSel], where: where, nesting: 0, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad].result; }; IF unnest > 0 THEN -- unnest so that moved span starts at same level as start of dest span [start: tSel.start.pos, end: tSel.end.pos] _ EditSpan.ChangeNesting[ root: destRoot, span: [tSel.start.pos, tSel.end.pos], change: -unnest, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => CONTINUE].new; }; tSel.granularity _ srcSel.granularity; UnConvertNodeSelects[tSel]; tSel.insertion _ after; TEditSelection.Copy[source: tSel, dest: oldSel]; -- save for Repeat's oldSel.pendingDelete _ (target=primary) OR (target=secondary AND pDel); tSel.pendingDelete _ FALSE; MakeSelection[selection: primary, new: tSel]; EXITS Bad => { MakeSelection[selection: primary, new: tSel]; EditFailed[] } }; CallWithBothLocked[DoMove, targetSel, srcSel, write]; }; Transpose: PUBLIC PROC [target: SelectionId _ primary] = { targetSel: Selection _ IF target=primary THEN pSel ELSE sSel; srcSel: Selection _ IF target=primary THEN sSel ELSE pSel; DoTranspose: PROC [sourceRoot, destRoot: RefTextNode, tSel, srcSel, targetSel: Selection] = { [start: srcSel.start.pos, end: srcSel.end.pos] _ EditSpan.Transpose[ alphaRoot: destRoot, betaRoot: sourceRoot, alpha: SelectionSpan[targetSel], beta: SelectionSpan[srcSel], event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad].newAlpha; UnConvertNodeSelects[srcSel]; srcSel.pendingDelete _ FALSE; MakeSelection[selection: primary, new: srcSel]; TEditSelection.Copy[source: srcSel, dest: oldSel]; -- save for Repeat's EXITS Bad => { MakeSelection[selection: primary, new: IF target=primary THEN targetSel ELSE srcSel]; EditFailed[] } }; CallWithBothLocked[DoTranspose, targetSel, srcSel, write]; }; Break: PUBLIC PROC = { DoBreak: PROC [root: RefTextNode, tSel: Selection] = { null: BOOL _ FALSE; caret: TextNode.Location; newNode: RefTextNode; IF tSel.pendingDelete THEN { DoPendingDelete[]; TEditSelection.Copy[source: pSel, dest: tSel] }; caret _ InsertionPoint[pSel]; Deselect[$primary]; newNode _ TextNode.NarrowToTextNode[EditSpan.Split[root, caret, TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad]]; IF newNode # NIL AND TextEdit.Size[TextNode.NarrowToTextNode[caret.node]]=0 AND TextEdit.Size[newNode] > 0 THEN null _ TRUE -- caret was at front of nonempty node ELSE tSel.start.pos.node _ newNode; -- move caret to start of new node tSel.start.pos.where _ 0; tSel.end.pos _ tSel.start.pos; tSel.granularity _ point; tSel.insertion _ before; tSel.pendingDelete _ FALSE; tSel.looks _ TextLooks.noLooks; MakeSelection[selection: primary, new: tSel]; CheckStartLine[viewer: tSel.viewer, old: caret, new: [newNode,0], null: null]; EXITS Bad => { MakeSelection[selection: primary, new: tSel]; EditFailed[] } }; CallWithLocks[DoBreak]; }; Join: PUBLIC PROC = { DoJoin: PROC [root: RefTextNode, tSel: Selection] = { loc: TextNode.Location; pred: RefTextNode; node: RefTextNode _ TextNode.NarrowToTextNode[InsertionPoint[].node]; IF node=NIL OR (pred _ TextNode.StepBackward[node])=NIL OR TextNode.Parent[pred]=NIL --i.e., pred is root-- THEN { EditFailed[]; RETURN }; Deselect[$primary]; loc _ EditSpan.Merge[root, node, TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad]; MakePointSelection[tSel, loc]; CheckStartLine[viewer: tSel.viewer, old: [node,0], new: loc]; EXITS Bad => { MakeSelection[selection: primary, new: tSel]; EditFailed[] } }; CallWithLocks[DoJoin]; }; CheckStartLine: PROC [viewer: ViewerClasses.Viewer, old, new: TextNode.Location, null: BOOL _ FALSE] = { FOR v: ViewerClasses.Viewer _ viewer, v.link UNTIL v=NIL DO WITH v.data SELECT FROM tdd: TEditDocument.TEditDocumentData => { vloc: TextNode.Location _ tdd.lineTable.lines[0].pos; IF vloc.node = old.node AND vloc.where >= old.where THEN { vnew: TextNode.Location _ [new.node, new.where+vloc.where-old.where]; IF null AND vnew.where = 0 THEN vnew.node _ old.node; TEditDisplay.EstablishLine[tdd, vnew]; }; }; ENDCASE; IF v.link = viewer THEN EXIT; ENDLOOP; }; SaveForPaste: PUBLIC PROC = { DoSaveForPaste: PROC [root: RefTextNode, tSel: Selection] = { EditSpan.SaveForPaste[SelectionSpan[tSel], TEditInput.currentEvent]; }; CallWithLocks[DoSaveForPaste, read]; }; SaveSpanForPaste: PUBLIC PROC [ startLoc, endLoc: TextNode.Location, grain: TEditDocument.SelectionGrain] = { root: RefTextNode = TextNode.Root[startLoc.node]; lockRef: TEditLocks.LockRef _ NIL; { ENABLE UNWIND => { IF lockRef # NIL THEN TEditLocks.Unlock[root] }; lockRef _ TEditLocks.Lock[root, "SaveSpanForPaste", read]; IF grain=node OR grain=branch THEN { startLoc.where _ TextNode.NodeItself; endLoc.where _ TextNode.NodeItself }; EditSpan.SaveForPaste[[startLoc, endLoc], TEditInput.currentEvent]; TEditLocks.Unlock[root]; }; }; Nest: PUBLIC PROC = { ChangeNesting[1] }; UnNest: PUBLIC PROC = { ChangeNesting[-1] }; ChangeNesting: PROC [change: INTEGER] = { DoChangeNesting: PROC [root: RefTextNode, tSel: Selection] = { Deselect[$primary]; [] _ EditSpan.ChangeNesting[root: root, span: TextNode.MakeNodeSpan[tSel.start.pos.node, tSel.end.pos.node], change: change, event: TEditInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad]; tSel.pendingDelete _ FALSE; MakeSelection[selection: primary, new: tSel]; IF CaretVisible[] THEN TEditRefresh.ScrollToEndOfSel[tSel.viewer, TRUE]; EXITS Bad => { MakeSelection[selection: primary, new: tSel]; EditFailed[] } }; CallWithLocks[DoChangeNesting]; }; ModifyLook: PUBLIC PROC [look: TextLooks.Look, op: TEditInputOps.ModifyOp] = { DoModifyLook: PROC [root: RefTextNode, tSel: Selection] = { remLooks, addLooks: TextLooks.Looks _ TextLooks.noLooks; IF tSel.granularity#point THEN { Deselect[$primary]; IF op=add THEN addLooks[look] _ TRUE ELSE remLooks[look] _ TRUE; EditSpan.ChangeLooks[root: root, span: [tSel.start.pos, tSel.end.pos], remove: remLooks, add: addLooks, event: TEditInput.currentEvent]; tSel.pendingDelete _ FALSE; MakeSelection[selection: primary, new: tSel]; }; ModifyCaretLook[look, op]; }; CallWithLocks[DoModifyLook]; }; ModifyCaretLook: PUBLIC PROC [look: TextLooks.Look, op: TEditInputOps.ModifyOp] = { LockSel[primary, "ModifyCaretLook"]; pSel.looks[look] _ (op=add); IF SelAtEndOfTypeScript[pSel] THEN SELECT op FROM add => TypeScript.ChangeLooks[pSel.viewer, look]; remove => TypeScript.ChangeLooks[pSel.viewer, Ascii.Upper[look]]; ENDCASE => ERROR; UnlockSel[primary]; }; GetSelLooks: PROC [sel: Selection] RETURNS [looks: TextLooks.Looks] = { first: BOOL _ TRUE; GetSelLooks: PROC [node: RefTextNode, start, len: INT] RETURNS [stop: BOOL] = { end: INT _ MIN[TextEdit.Size[node],start+len]; FOR i: INT IN [start..end) DO lks: TextLooks.Looks _ TextEdit.FetchLooks[node,i]; IF first THEN { first _ FALSE; looks _ lks } ELSE IF lks#looks THEN { OPEN MessageWindow; Append["Selection does not have uniform looks.",TRUE]; Append[" Using looks from first char."]; Blink[]; RETURN [TRUE] }; ENDLOOP; RETURN [FALSE]; }; EditSpanSupport.Apply[span: [sel.start.pos, sel.end.pos], proc: GetSelLooks]; IF first THEN looks _ sel.looks; -- null selection, use caret }; ChangeLooks: PUBLIC PROC [add, remove: TextLooks.Looks] = { DoChangeLooks: PROC [root: RefTextNode, tSel: Selection] = { Deselect[$primary]; ChangeSelLooks[add, remove, tSel]; MakeSelection[tSel, primary]; }; CallWithLocks[DoChangeLooks]; }; CopyLooks: PUBLIC PROC [target: SelectionId _ primary] = { targetSel: Selection _ IF target=primary THEN pSel ELSE sSel; srcSel: Selection _ IF target=primary THEN sSel ELSE pSel; DoCopyLooks: PROC [sourceRoot, destRoot: RefTextNode, tSel, srcSel, targetSel: Selection] = { TEditSelection.Copy[source: srcSel, dest: oldSel]; -- save the secondary selection for Repeat's ChangeSelLooks[add: GetSelLooks[srcSel], remove: TextLooks.allLooks, targetSel: targetSel]; MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary]; }; CallWithBothLocked[DoCopyLooks, targetSel, srcSel, read]; }; TransposeLooks: PUBLIC PROC [target: SelectionId _ primary] = { targetSel: Selection _ IF target=primary THEN pSel ELSE sSel; srcSel: Selection _ IF target=primary THEN sSel ELSE pSel; DoTransLooks: PROC [sourceRoot, destRoot: RefTextNode, tSel, srcSel, targetSel: Selection] = { srcLooks, targetLooks: TextLooks.Looks; srcLooks _ GetSelLooks[srcSel]; targetLooks _ GetSelLooks[targetSel]; TEditSelection.Copy[source: srcSel, dest: oldSel]; -- save for Repeat's Deselect[$primary]; Deselect[$secondary]; ChangeSelLooks[add: srcLooks, remove: targetLooks, targetSel: targetSel]; ChangeSelLooks[add: targetLooks, remove: srcLooks, targetSel: srcSel]; MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary]; }; CallWithBothLocked[DoTransLooks, targetSel, srcSel, write]; }; ChangeSelLooks: PROC [add, remove: TextLooks.Looks, targetSel: Selection] = { IF targetSel.granularity # point THEN { EditSpan.ChangeLooks[ root: SelectionRoot[targetSel], span: [targetSel.start.pos, targetSel.end.pos], remove: remove, add: add, event: TEditInput.currentEvent]; targetSel.pendingDelete _ FALSE; }; targetSel.looks _ TextLooksSupport.ModifyLooks[targetSel.looks, remove, add]; AdjustTypeScriptLooks[targetSel, add, remove]; }; ChangeCaretLooks: PUBLIC PROC [add, remove: TextLooks.Looks] = { LockSel[primary, "ChangeCaretLooks"]; pSel.looks _ TextLooksSupport.ModifyLooks[pSel.looks, remove, add]; AdjustTypeScriptLooks[pSel, add, remove]; UnlockSel[primary]; }; SelAtEndOfTypeScript: PROC [targetSel: Selection] RETURNS [BOOL] = { tdd: TEditDocumentData; IF targetSel.viewer=NIL THEN RETURN [FALSE]; tdd _ NARROW[targetSel.viewer.data]; IF tdd = NIL OR tdd.tsInfo=NIL THEN RETURN [FALSE]; -- not a typescript IF ~TEditImpl.CaretAtEnd[targetSel] THEN RETURN [FALSE]; RETURN [TRUE]; }; AdjustTypeScriptLooks: PROC [targetSel: Selection, add, remove: TextLooks.Looks] = { IF ~SelAtEndOfTypeScript[targetSel] THEN RETURN; FOR c: CHAR IN TextLooks.Look DO IF remove[c] THEN TypeScript.ChangeLooks[targetSel.viewer, Ascii.Upper[c]]; IF add[c] THEN TypeScript.ChangeLooks[targetSel.viewer, c]; ENDLOOP; }; END. úTEditInputOpsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Edited by Paxton on February 8, 1983 9:34 am Edited by McGregor on July 30, 1982 3:15 pm Last Edited by: Maxwell, January 14, 1983 1:58 pm Russ Atkinson (RRA) June 17, 1985 11:47:36 pm PDT Michael Plass, March 29, 1985 3:44:32 pm PST Doug Wyatt, March 3, 1985 6:40:57 pm PST Calls a procedure that operates on the primary selection. root is the root for the primary selection tSel is a copy of the primary selection Calls a procedure that operates on the primary and secondary selections. sourceRoot is the root for the source selection destRoot is the root for the target selection tSel is a copy of the target selection srcSel is a copy of the source selection targetSel is another copy of the target selection [DKW: Note the calls on LockBothSelections and Deselect; they suggest that trouble will ensue unless (targetSel=pSel AND srcSel=sSel) OR (targetSel=sSel AND srcSel=pSel).] like a replace of pSel by a single character, then delete that character. same as calling Delete for pSel a text selection, but NOT for pSel a node/branch selection. -- shove it in as though typed... DKW: some EditSpan operations (such as a Move that moves zero characters) can return a reversed span. This crock tries to fix up the reversed selection so produced. special copy to typescript unless we are editing typescripts like regular Tioga documents and the target caret is at the end -- force selection to end of typescript -- now create a phony secondary selection for Copy -- moving all the nodes to different tree -- move the selection to a deeper nesting level in the tree -- move the selection to a shallower nesting level in the tree -- Transpose the looks of the primary and secondary selections ʘcodešœ™Kšœ Ïmœ1™˜>Kšœ+˜+Kšœ9˜9Kšœžœ ˜2Kšœžœ ˜,—Kšœžœ˜Kšœ˜—šžœ¡˜%K˜.Kšœžœ¡-˜BKšœžœžœžœ˜Jšžœ žœ¡$˜MKšžœžœ ˜šžœ>ž˜EKšœ¡&˜5—K˜ šžœ žœ'ž˜=KšœO˜O—K˜—˜;Kšœ+˜+Kšœ)˜)Kšœ8˜8Kšœžœ ˜,—šžœ žœ¡H˜[šœD˜DKšœ5˜5Kšœ/˜/Kšœžœ˜)——K˜—K˜&K˜K˜K˜-Kšœ1¡)˜Zšžœ ˜Kšœ'žœžœ žœ ˜UK˜—K˜—Kšžœžœ9˜Ešžœ¡$˜+Kšœ˜K˜3KšœV˜VKšœ ˜ Kšœ˜—Kšœ˜K˜—š œžœžœ$˜5Kšžœžœ˜(Kšœžœžœžœ˜=Kšœžœžœžœ˜:Kšœ˜Kš žœžœžœžœžœ#˜UKšœ˜Kšœ˜K˜—š   œžœžœžœžœ˜DKšžœžœžœžœžœžœžœ˜JK˜0Kšžœžœ˜Kšœ˜K˜—š œžœ9˜EKšžœžœžœ˜CKšžœž˜šžœ žœ"žœ˜MKšœ|™|Kšœ˜Kšœ˜K˜9Kšœ1¡,˜]Kšœ'™'Kšœ3˜3K˜K˜K˜GKšœA˜AK˜K˜K˜šžœžœ¡.˜GK˜GK˜-K˜—Kšžœ0˜4K˜Kšœ˜—Kšžœžœžœ ˜.Kšžœ&˜*K˜K˜—š œžœžœ˜š œžœ)˜6K˜1Kšœ1˜1Kš žœžœ žœžœžœ˜?šžœ žœžœžœ˜/Kšœ˜K˜0—K˜Kšœ2™2K˜K˜šžœ(žœ%˜QKšžœ8˜™>K˜—š  œžœ žœ˜)š œžœ)˜>K˜˜'K˜DKšœ.˜.Kšœžœ˜%—Kšœžœ˜K˜-Kšžœžœ,žœ˜HKšžœF˜KKšœ˜—Kšœ˜Kšœ˜K˜—š  œžœžœ7˜Nš  œžœ)˜;K˜8šžœžœ˜ K˜Kš žœžœžœžœžœ˜@˜ K˜%K˜ K˜ —Kšœžœ˜K˜-K˜—K˜K˜—Kšœ˜Kšœ˜K˜—š œžœžœ7˜SKšœ$˜$K˜šžœžœžœž˜1Kšœ1˜1KšœA˜AKšžœžœ˜—Kšœ˜Kšœ˜K˜—š  œžœžœ˜GKšœžœžœ˜š  œžœ!žœ˜6Kšžœžœ˜Kšœžœžœ ˜.šžœžœžœž˜K˜3Kšžœžœ žœ˜,šžœžœ žœ˜Kšžœ˜Kšœ0žœ˜6K˜)Kšœ žœžœ˜—Kšžœ˜—Kšžœžœ˜Kšœ˜—K˜MKšžœžœ¡˜=K˜K˜—š  œžœžœ#˜;š  œžœ)˜™>Kšœžœžœžœ˜=Kšœžœžœžœ˜:š  œžœL˜^K˜'K˜K˜%Kšœ4¡˜HK˜)K˜IK˜FKšœžœžœ žœ˜EKšœ˜—Kšœ;˜;Kšœ˜K˜—š œžœ9˜Mšžœžœ˜'šœ˜KšœO˜OKšœ:˜:—Kšœžœ˜ Kšœ˜—K˜MKšœ.˜.Kšœ˜K˜—š œžœžœ#˜@Kšœ%˜%KšœC˜CKšœ)˜)Kšœ˜Kšœ˜K˜—š œžœžœžœ˜DKšœ˜Kš žœžœžœžœžœ˜,Kšœžœ˜$Kšžœžœžœ žœžœžœžœ¡˜GKšžœ"žœžœžœ˜8Kšžœžœ˜Kšœ˜K˜—š œžœ9˜TKšžœ"žœžœ˜0šžœžœžœž˜ Kšžœ žœ:˜KKšžœžœ-˜;Kšžœ˜—Kšœ˜K˜—Kšžœ˜—…—l`Žx