-- TiogaInputOpsImpl.mesa; Edited by Paxton on June 14, 1983 10:21 am
-- Edited by McGregor on July 30, 1982 3:15 pm
Last Edited by: Maxwell, January 14, 1983 1:58 pm
DIRECTORY
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],
TextEdit USING [DeleteText, FetchChar, FetchLooks, FromRope, Size],
TiogaLooks USING [Look, Looks, allLooks, noLooks],
TiogaLooksSupport USING [ModifyLooks],
TiogaNode USING [Location, NodeItself, nullSpan, Offset, Ref, RefBranchNode, RefTextNode, Span],
TiogaNodeOps USING [BranchChild, LastLocWithin, Level, MakeNodeLoc, MakeNodeSpan, NarrowToTextNode, Parent, Root, StepForwardNode, StepBackwardNode],
TiogaDisplay USING [EstablishLine],
TiogaDocument USING [BeforeAfter, Selection, SelectionId, SelectionGrain, SelectionPoint, SelectionRec, TiogaDocumentData],
TiogaImpl USING [CaretAtEnd],
TiogaInput USING [currentEvent],
TiogaInputOps,
TiogaLocks USING [Access, Lock, LockBoth, LockRef, Unlock],
TiogaOps,
TiogaProfile USING [editTypeScripts],
TiogaRefresh USING [ScrollToEndOfSel],
TiogaSelection USING [Alloc, CaretVisible, Copy, Free, Deselect, GetSelectionGrain, InsertionPoint, LockBothSelections, LockSel, MakeSelection, MakePointSelection, nilSel, oldSel, pSel, SelectionRoot, sSel, UnlockBothSelections, UnlockDocAndPSel, UnlockSel],
TypeScript USING [ChangeLooks],
ViewerClasses USING [Viewer];
TiogaInputOpsImpl: CEDAR PROGRAM
IMPORTS EditSpan, MessageWindow, EditSpanSupport, Rope, RopeEdit, TextEdit, TiogaLooksSupport, TiogaNodeOps, TiogaDisplay, TiogaImpl, TiogaInput, TiogaInputOps, TiogaLocks, TiogaProfile, TiogaRefresh, TiogaSelection, TypeScript
EXPORTS TiogaInputOps, TiogaOps = BEGIN OPEN TiogaDocument, TiogaSelection;
pdelNode: TiogaNode.RefTextNode = TextEdit.FromRope["!"];
DoPendingDelete: PUBLIC PROC = {
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.
PendingDelete: PROC [root: TiogaNode.RefBranchNode, 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] };
NodeSel: PROC [sel: Selection] RETURNS [BOOLEAN] = INLINE {
RETURN [GetSelectionGrain[sel]=node OR sel.start.pos.node # sel.end.pos.node] };
Delete: PUBLIC PROC [saveForPaste: BOOLEANTRUE] = {
DoDelete: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
Deselect[];
tSel.pendingDelete ← FALSE;
SELECT GetSelectionGrain[tSel] FROM
point => NULL;
node => BEGIN -- this is complex because must worry about deleting all of the document
newSelNode: TiogaNode.Ref ← TiogaNodeOps.StepForwardNode[tSel.end.pos.node];
newSelText: TiogaNode.RefTextNode ← TiogaNodeOps.NarrowToTextNode[newSelNode];
newSelInsertion: BeforeAfter;
IF newSelNode=NIL THEN { -- deleting the last node of the doc
newSelNode ← TiogaNodeOps.StepBackwardNode[tSel.start.pos.node].back;
IF newSelNode=NIL OR newSelNode=root THEN { -- deleting all the nodes
child: TiogaNode.Ref ← TiogaNodeOps.BranchChild[root];
tSel.start.pos.node ← child;
newSelNode ← EditSpan.InsertTextNode[root, child,
before, FALSE, TiogaInput.currentEvent] };
newSelText ← TiogaNodeOps.NarrowToTextNode[newSelNode];
newSelInsertion ← IF newSelText#NIL AND TextEdit.Size[newSelText] > 0 THEN after ELSE before }
ELSE { newSelInsertion ← before };
EditSpan.Delete[
SelectionRoot[tSel],
TiogaNodeOps.MakeNodeSpan[tSel.start.pos.node,tSel.end.pos.node],
TiogaInput.currentEvent,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 ← TiogaNodeOps.MakeNodeLoc[newSelNode];
tSel.granularity ← node };
tSel.insertion ← newSelInsertion;
END;
ENDCASE => BEGIN
EditSpan.Delete[
SelectionRoot[tSel],
[tSel.start.pos, tSel.end.pos],
TiogaInput.currentEvent,saveForPaste !
EditSpan.CannotDoEdit => GOTO Bad];
tSel.end.pos ← tSel.start.pos;
tSel.granularity ← point;
tSel.insertion ← before;
END;
MakeSelection[selection: primary, new: tSel];
EXITS Bad => { MakeSelection[selection: primary, new: tSel]; EditFailed[] }};
CallWithLocks[DoDelete] };
EditFailed: PUBLIC PROC [msg: Rope.ROPENIL] = { OPEN MessageWindow;
Append[IF msg=NIL THEN "Can't do it." ELSE msg, TRUE]; Blink[] };
CaretLoc: PUBLIC PROC [s: Selection ← NIL] RETURNS [TiogaNode.Location] = {
IF s=NIL THEN s ← pSel;
IF GetSelectionGrain[s] = node THEN RETURN [
[IF s.insertion = before THEN s.start.pos.node ELSE s.end.pos.node, TiogaNode.NodeItself]];
IF s.insertion = before THEN RETURN [s.start.pos];
RETURN [[s.end.pos.node, s.end.pos.where+1]] };
CallWithLocks: PUBLIC PROC [
proc: PROC [root: TiogaNode.RefBranchNode, tSel: Selection],
access: TiogaLocks.Access ← write] = BEGIN
root: TiogaNode.RefBranchNode;
tSel: Selection;
lockRef: TiogaLocks.LockRef;
BEGIN
ENABLE UNWIND => {
UnlockSel[primary];
IF lockRef # NIL THEN TiogaLocks.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 ← TiogaLocks.Lock[root, "CallWithLocks", access];
tSel ← Alloc[];
TiogaSelection.Copy[source: pSel, dest: tSel];
proc[root, tSel];
Free[tSel]; tSel ← NIL;
UnlockDocAndPSel[root];
END END;
BackSpace: PUBLIC PROC [count: INT ← 1] = {
DoBackSpace: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
node: TiogaNode.RefTextNode;
flush: TiogaNode.Location ← InsertionPoint[tSel];
IF flush.where=TiogaNode.NodeItself THEN GOTO Bad;
IF (node ← TiogaNodeOps.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, TiogaInput.currentEvent];
MakePointSelection[tSel, flush];
EXITS Bad => EditFailed[] };
IF count > 0 THEN CallWithLocks[DoBackSpace];
};
FindPrevWord: PUBLIC PROC [node: TiogaNode.RefTextNode, offset: TiogaNode.Offset]
RETURNS [nChars: CARDINAL] = {
nChars ← 0;
WHILE offset>0 AND ~RopeEdit.AlphaNumericChar[TextEdit.FetchChar[node, offset-1]] DO
offset ← offset - 1; nChars ← nChars + 1; ENDLOOP;
WHILE offset>0 AND RopeEdit.AlphaNumericChar[TextEdit.FetchChar[node, offset-1]] DO
offset ← offset - 1; nChars ← nChars + 1; ENDLOOP;
};
BackWord: PUBLIC PROC [count: INT ← 1] = BEGIN
DoBackWord: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
node: TiogaNode.RefTextNode;
nChars: CARDINAL ← 0;
pos: TiogaNode.Location ← InsertionPoint[tSel];
IF (node ← TiogaNodeOps.NarrowToTextNode[pos.node])=NIL THEN GOTO Bad;
IF pos.where = TiogaNode.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, TiogaInput.currentEvent];
MakePointSelection[tSel, pos];
EXITS Bad => EditFailed[] };
IF count > 0 THEN CallWithLocks[DoBackWord];
END;
GoToPreviousWord: PUBLIC PROC [count: INT ← 1] = BEGIN
DoGoToPreviousWord: PROC [root: TiogaNode.Ref, tSel: Selection] = {
pos: TiogaNode.Location;
node: TiogaNode.RefTextNode;
nChars: CARDINAL;
pos ← InsertionPoint[tSel];
FOR garbage:INT IN [0..count) DO
IF (node ← TiogaNodeOps.NarrowToTextNode[pos.node])=NIL OR
pos.where=TiogaNode.NodeItself OR pos.where=0 THEN {
TiogaInputOps.GoToPreviousNode; RETURN };
nChars ← FindPrevWord[node,pos.where];
pos.where ← pos.where - nChars;
ENDLOOP;
MakePointSelection[tSel,pos] };
IF count > 0 THEN CallWithLocks[DoGoToPreviousWord, read];
END;
CopyToTypeScript: PROC [targetSel: Selection, source: TiogaNode.Span] = {
TSCopy: PROC [node: TiogaNode.RefTextNode, start, len: TiogaNode.Offset]
RETURNS [stop: BOOLEAN] = {
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
-- shove it in as though typed...
targetSel.viewer.class.notify[targetSel.viewer, LIST[rope]];
RETURN [FALSE] };
EditSpanSupport.Apply[source,TSCopy] };
ConvertNodeSelects: PROC [sel: Selection] RETURNS [BOOLEAN] = {
IF GetSelectionGrain[sel] # node THEN RETURN [FALSE];
sel.start.pos.where ← sel.end.pos.where ← TiogaNode.NodeItself;
RETURN [TRUE] };
ExpandToNodeSelection: PROC [sel: Selection] = {
sel.start.pos.where ← 0;
sel.end.pos.where ←
MAX[TextEdit.Size[TiogaNodeOps.NarrowToTextNode[sel.end.pos.node]],1]-1;
sel.granularity ← node;
};
UnConvertNodeSelects: PROC [sel: Selection] = {
IF GetSelectionGrain[sel] # node THEN RETURN;
sel.start.pos.where ← 0;
sel.end.pos.where ← MAX[TextEdit.Size[TiogaNodeOps.NarrowToTextNode[sel.end.pos.node]]-1,0] };
CallWithBothLocked: PUBLIC PROC [
proc: PROC [sourceRoot, destRoot: TiogaNode.RefBranchNode, tSel, srcSel, targetSel: Selection],
targetSel, srcSel: Selection, sourceAccess: TiogaLocks.Access] = BEGIN
sourceRoot, destRoot: TiogaNode.RefBranchNode;
tSel, tSel1, tSel2: Selection;
lockRef: TiogaLocks.LockRef;
Cleanup: PROC = {
IF lockRef # NIL THEN { TiogaLocks.Unlock[sourceRoot]; TiogaLocks.Unlock[destRoot] };
IF tSel # NIL THEN Free[tSel];
IF tSel1 # NIL THEN Free[tSel1];
IF tSel2 # NIL THEN Free[tSel2];
UnlockBothSelections[] };
BEGIN
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[selection: secondary]; UnlockBothSelections[]; RETURN };
tSel1 ← Alloc[]; tSel2 ← Alloc[]; tSel ← Alloc[];
TiogaSelection.Copy[source: srcSel, dest: tSel1];
TiogaSelection.Copy[source: targetSel, dest: tSel2];
TiogaSelection.Copy[source: targetSel, dest: tSel];
sourceRoot ← SelectionRoot[srcSel];
destRoot ← SelectionRoot[targetSel];
[lockRef, ----] ← TiogaLocks.LockBoth[
sourceRoot, destRoot, "CallWithBothLocked", sourceAccess, write];
Deselect[selection: secondary];
Deselect[selection: primary];
proc[sourceRoot, destRoot, tSel, tSel1, tSel2];
Cleanup[];
END END;
CopyToDoc: PROC [targetSel, srcSel: Selection, target: SelectionId, lock: BOOLTRUE] = {
DoCopyToDoc: PROC [
sourceRoot, destRoot: TiogaNode.RefBranchNode, tSel, srcSel, targetSel: Selection] = {
wordMode: BOOLEAN = FALSE; -- (srcSel.granularity = word);
nodeCopy: BOOLEAN ← ConvertNodeSelects[srcSel];
pDel: BOOLEAN ← tSel.pendingDelete;
IF pDel THEN { -- replace target by source
[] ← ConvertNodeSelects[tSel];
[[tSel.start.pos, tSel.end.pos]] ←
EditSpan.Replace[destRoot, sourceRoot,
[tSel.start.pos, tSel.end.pos],
[srcSel.start.pos, srcSel.end.pos], wordMode, TRUE,
TiogaInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad];
tSel.pendingDelete ← FALSE }
ELSE { -- source goes to target caret
loc: TiogaNode.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 nodeCopy THEN { -- don't copy nodes into target node
IF where = before AND loc.where > 0 AND
loc.where = TextEdit.Size[TiogaNodeOps.NarrowToTextNode[loc.node]] THEN
where ← after; -- caret at end of node, so copy after
loc.where ← TiogaNode.NodeItself };
IF nodeCopy AND where=after AND tSel.start.pos.node#tSel.end.pos.node THEN
unnest ← TiogaNodeOps.Level[tSel.end.pos.node]-TiogaNodeOps.Level[tSel.start.pos.node];
[[tSel.start.pos, tSel.end.pos]] ←
EditSpan.Copy[destRoot, sourceRoot, loc,
[srcSel.start.pos, srcSel.end.pos], wordMode, where, 0,
TiogaInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad];
IF unnest > 0 THEN -- unnest so that copied span starts at same level as start of dest span
[[tSel.start.pos, tSel.end.pos]] ←
EditSpan.ChangeNesting[root: destRoot, span: [tSel.start.pos, tSel.end.pos],
change: -unnest, event: TiogaInput.currentEvent !
EditSpan.CannotDoEdit => CONTINUE];
};
tSel.granularity ← srcSel.granularity;
UnConvertNodeSelects[tSel];
tSel.insertion ← after;
MakeSelection[selection: primary, new: tSel];
TiogaSelection.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[];
TiogaSelection.Copy[source: targetSel, dest: tSel];
DoCopyToDoc[SelectionRoot[srcSel], SelectionRoot[targetSel], tSel, srcSel, targetSel];
Free[tSel] }};
Copy: PUBLIC PROCEDURE [target: SelectionId ← primary] = BEGIN
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[];
END;
CheckReadonly: PUBLIC PROC [targetSel: Selection] RETURNS [BOOL] = {
IF targetSel.viewer=NIL OR ~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[selection: secondary]; RETURN };
IF targetSel.data.tsInfo#NIL AND
(~TiogaProfile.editTypeScripts OR TiogaImpl.CaretAtEnd[targetSel]) THEN BEGIN
special copy to typescript unless we are editing typescripts like regular Tioga documents and the target caret is at the end
tSel: Selection ← Alloc[];
tSel1: Selection ← Alloc[];
span: TiogaNode.Span ← [srcSel.start.pos, srcSel.end.pos];
TiogaSelection.Copy[source: sSel, dest: oldSel]; -- save the secondary selection for Repeat's
-- force selection to end of typescript
TiogaSelection.Copy[source: targetSel, dest: tSel];
tSel.insertion ← before;
tSel.granularity ← point;
tSel.start.pos ← tSel.end.pos ← TiogaNodeOps.LastLocWithin[tSel.data.text];
TiogaSelection.Copy[source: srcSel, dest: tSel1]; srcSel ← tSel1;
Deselect[selection: primary];
Deselect[selection: secondary];
CopyToTypeScript[tSel, span];
IF target=primary THEN { -- leave primary as caret at end of typescript
tSel.start.pos ← tSel.end.pos ← TiogaNodeOps.LastLocWithin[tSel.data.text];
MakeSelection[selection: primary, new: tSel] }
ELSE MakeSelection[selection: primary, new: srcSel];
Free[tSel]; Free[tSel1];
END
ELSE IF srcSel.pendingDelete THEN Move[target]
ELSE CopyToDoc[targetSel, srcSel, target];
};
Paste: PUBLIC PROC = {
DoPaste: PROC [root: TiogaNode.Ref, tSel: Selection] = {
source: TiogaNode.Span ← EditSpan.SavedForPaste[];
tdd: TiogaDocument.TiogaDocumentData ← tSel.data;
IF source = TiogaNode.nullSpan OR tSel.viewer=NIL THEN GOTO Bad;
IF tdd.tsInfo=NIL AND tSel.pendingDelete THEN {
DoPendingDelete[];
TiogaSelection.Copy[source: pSel, dest: tSel] };
Deselect[selection: secondary];
-- now create a phony secondary selection for Copy
tSel.start.pos ← source.start;
tSel.end.pos ← source.end;
IF source.start.where=TiogaNode.NodeItself OR source.end.where=TiogaNode.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 PROCEDURE [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: TiogaNode.RefBranchNode, tSel, srcSel, targetSel: Selection] = {
nodeMove: BOOLEAN;
pDel: BOOLEAN;
wordMode: BOOLEAN = FALSE; -- (srcSel.granularity = word);
IF GetSelectionGrain[srcSel] = node THEN { -- see if moving entire contents
newSelNode: TiogaNode.Ref ← TiogaNodeOps.StepForwardNode[srcSel.end.pos.node];
IF newSelNode=NIL THEN { -- moving the last node of the doc
newSelNode ← TiogaNodeOps.StepBackwardNode[srcSel.start.pos.node].back;
IF (newSelNode=NIL OR newSelNode=sourceRoot) AND destRoot # sourceRoot THEN {
-- moving all the nodes to different tree
child: TiogaNode.Ref ← TiogaNodeOps.BranchChild[sourceRoot];
srcSel.start.pos.node ← child; -- make sure doesn't include the root
[] ← EditSpan.InsertTextNode[sourceRoot, child, before, FALSE, TiogaInput.currentEvent] }}};
nodeMove ← ConvertNodeSelects[srcSel];
IF (pDel ← tSel.pendingDelete) THEN { -- move source onto target
[] ← ConvertNodeSelects[tSel];
[[tSel.start.pos, tSel.end.pos]] ← EditSpan.MoveOnto[
destRoot, sourceRoot, [tSel.start.pos, tSel.end.pos],
[srcSel.start.pos, srcSel.end.pos], wordMode, TRUE, TiogaInput.currentEvent !
EditSpan.CannotDoEdit => GOTO Bad];
tSel.pendingDelete ← FALSE }
ELSE { -- move source to target caret
loc: TiogaNode.Location ← InsertionPoint[tSel];
unnest: INTEGER ← 0; -- amount to unnest by if moving nodes after
where: EditSpan.Place ← IF tSel.insertion = before THEN before ELSE after;
IF nodeMove THEN { -- don't move nodes into target node
IF where = before AND loc.where > 0 AND
loc.where = TextEdit.Size[TiogaNodeOps.NarrowToTextNode[loc.node]] THEN
where ← after; -- caret at end of node, so move after
loc.where ← TiogaNode.NodeItself };
IF nodeMove AND where=after AND tSel.start.pos.node#tSel.end.pos.node THEN
unnest ← TiogaNodeOps.Level[tSel.end.pos.node]-TiogaNodeOps.Level[tSel.start.pos.node];
[[tSel.start.pos, tSel.end.pos]] ← EditSpan.Move[
destRoot, sourceRoot, loc,
[srcSel.start.pos, srcSel.end.pos], wordMode,
where, 0, TiogaInput.currentEvent !
EditSpan.CannotDoEdit => GOTO Bad];
IF unnest > 0 THEN -- unnest so that moved span starts at same level as start of dest span
[[tSel.start.pos, tSel.end.pos]] ←
EditSpan.ChangeNesting[root: destRoot, span: [tSel.start.pos, tSel.end.pos],
change: -unnest, event: TiogaInput.currentEvent !
EditSpan.CannotDoEdit => CONTINUE];
};
tSel.granularity ← srcSel.granularity;
UnConvertNodeSelects[tSel];
tSel.insertion ← after;
TiogaSelection.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 PROCEDURE [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: TiogaNode.RefBranchNode, tSel, srcSel, targetSel: Selection] = {
wordMode: BOOLEAN = FALSE; -- targetSel.granularity=word AND srcSel.granularity=word;
tempT: TiogaNode.Span;
[] ← ConvertNodeSelects[srcSel];
[] ← ConvertNodeSelects[targetSel];
[tempT, ----] ← EditSpan.Transpose[destRoot, sourceRoot,
[targetSel.start.pos, targetSel.end.pos],
[srcSel.start.pos, srcSel.end.pos],
wordMode, TiogaInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad];
[srcSel.start.pos, srcSel.end.pos] ← tempT;
UnConvertNodeSelects[srcSel];
srcSel.pendingDelete ← FALSE;
MakeSelection[selection: primary, new: srcSel];
TiogaSelection.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: TiogaNode.RefBranchNode, tSel: Selection] = {
null: BOOLFALSE;
caret: TiogaNode.Location;
newNode: TiogaNode.RefTextNode;
IF tSel.pendingDelete THEN {
DoPendingDelete[];
TiogaSelection.Copy[source: pSel, dest: tSel] };
caret ← InsertionPoint[pSel];
Deselect[primary];
newNode ← TiogaNodeOps.NarrowToTextNode[EditSpan.Split[root,
caret, TiogaInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad]];
IF newNode # NIL AND TextEdit.Size[TiogaNodeOps.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 ← TiogaLooks.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 PROCEDURE = {
DoJoin: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
loc: TiogaNode.Location;
pred: TiogaNode.Ref;
node: TiogaNode.RefTextNode ← TiogaNodeOps.NarrowToTextNode[InsertionPoint[].node];
IF node=NIL OR (pred ← TiogaNodeOps.StepBackwardNode[node].back)=NIL OR
TiogaNodeOps.Parent[pred]=NIL --i.e., pred is root-- THEN { EditFailed[]; RETURN };
Deselect[primary];
loc ← EditSpan.Merge[root, node, TiogaInput.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: TiogaNode.Location, null: BOOLFALSE] = {
first: BOOLEANTRUE;
FOR v: ViewerClasses.Viewer ← viewer, v.link UNTIL v=NIL OR (v=viewer AND ~first) DO
tdd: TiogaDocument.TiogaDocumentData ← NARROW[v.data];
IF tdd # NIL THEN {
vloc: TiogaNode.Location ← tdd.lineTable.lines[0].pos;
first ← FALSE;
IF vloc.node = old.node AND vloc.where >= old.where THEN {
vnew: TiogaNode.Location ← [new.node, new.where+vloc.where-old.where];
IF null AND vnew.where = 0 THEN vnew.node ← old.node;
TiogaDisplay.EstablishLine[tdd, vnew] };
};
ENDLOOP };
SaveForPaste: PUBLIC PROC = {
DoSaveForPaste: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
[] ← ConvertNodeSelects[tSel];
EditSpan.SaveForPaste[[tSel.start.pos, tSel.end.pos], TiogaInput.currentEvent] };
CallWithLocks[DoSaveForPaste, read] };
SaveSpanForPaste: PUBLIC PROC [
startLoc, endLoc: TiogaNode.Location, grain: TiogaDocument.SelectionGrain] = {
root: TiogaNode.RefBranchNode = TiogaNodeOps.Root[startLoc.node];
lockRef: TiogaLocks.LockRef;
{ENABLE UNWIND => { IF lockRef # NIL THEN TiogaLocks.Unlock[root] };
lockRef ← TiogaLocks.Lock[root, "SaveSpanForPaste", read];
IF grain=node OR grain=branch THEN {
startLoc.where ← TiogaNode.NodeItself; endLoc.where ← TiogaNode.NodeItself };
EditSpan.SaveForPaste[[startLoc, endLoc], TiogaInput.currentEvent];
TiogaLocks.Unlock[root] }};
Nest: PUBLIC PROC = { ChangeNesting[1] };
-- move the selection to a deeper nesting level in the tree
UnNest: PUBLIC PROC = { ChangeNesting[-1] };
-- move the selection to a shallower nesting level in the tree
ChangeNesting: PROC [change: INTEGER] = {
DoChangeNesting: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
Deselect[primary];
[] ← EditSpan.ChangeNesting[root,
TiogaNodeOps.MakeNodeSpan[tSel.start.pos.node, tSel.end.pos.node],
change, TiogaInput.currentEvent ! EditSpan.CannotDoEdit => GOTO Bad];
tSel.pendingDelete ← FALSE;
MakeSelection[selection: primary, new: tSel];
IF CaretVisible[] THEN TiogaRefresh.ScrollToEndOfSel[tSel.viewer, TRUE];
EXITS Bad => { MakeSelection[selection: primary, new: tSel]; EditFailed[] }};
CallWithLocks[DoChangeNesting] };
ModifyLook: PUBLIC PROC [look: TiogaLooks.Look, op: TiogaInputOps.ModifyOp] = {
DoModifyLook: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
remLooks, addLooks: TiogaLooks.Looks ← TiogaLooks.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: TiogaInput.currentEvent];
tSel.pendingDelete ← FALSE;
MakeSelection[selection: primary, new: tSel] };
ModifyCaretLook[look, op] };
CallWithLocks[DoModifyLook] };
ModifyCaretLook: PUBLIC PROC [look: TiogaLooks.Look, op: TiogaInputOps.ModifyOp] = BEGIN
LockSel[primary, "ModifyCaretLook"];
pSel.looks[look] ← (op=add);
IF SelAtEndOfTypeScript[pSel] THEN
IF (op=add) THEN TypeScript.ChangeLooks[pSel.viewer, look]
ELSE TypeScript.ChangeLooks[pSel.viewer, Capital[look]];
UnlockSel[primary];
END;
GetSelLooks: PROC [sel: Selection] RETURNS [looks: TiogaLooks.Looks] = {
first: BOOLEANTRUE;
GetSelLooks: PROC [node: TiogaNode.RefTextNode, start, len: TiogaNode.Offset]
RETURNS [stop: BOOLEAN] = {
end: TiogaNode.Offset ← MIN[TextEdit.Size[node],start+len];
FOR i: TiogaNode.Offset IN [start..end) DO
lks: TiogaLooks.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[[sel.start.pos, sel.end.pos],GetSelLooks];
IF first THEN looks ← sel.looks -- null selection, use caret
};
ChangeLooks: PUBLIC PROC [add, remove: TiogaLooks.Looks] = {
DoChangeLooks: PROC [root: TiogaNode.RefBranchNode, tSel: Selection] = {
Deselect[primary];
ChangeSelLooks[add,remove,tSel];
MakeSelection[tSel,primary] };
CallWithLocks[DoChangeLooks] };
CopyLooks: PUBLIC PROCEDURE [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: TiogaNode.RefBranchNode, tSel, srcSel, targetSel: Selection] = {
TiogaSelection.Copy[source: srcSel, dest: oldSel]; -- save the secondary selection for Repeat's
ChangeSelLooks[add: GetSelLooks[srcSel], remove: TiogaLooks.allLooks, targetSel: targetSel];
MakeSelection[IF target=primary THEN targetSel ELSE srcSel, primary] };
CallWithBothLocked[DoCopyLooks, targetSel, srcSel, read] };
TransposeLooks: PUBLIC PROC [target: SelectionId ← primary] = {
-- Transpose the looks of the primary and secondary selections
targetSel: Selection ← IF target=primary THEN pSel ELSE sSel;
srcSel: Selection ← IF target=primary THEN sSel ELSE pSel;
DoTransLooks: PROC [
sourceRoot, destRoot: TiogaNode.RefBranchNode, tSel, srcSel, targetSel: Selection] = {
srcLooks, targetLooks: TiogaLooks.Looks;
srcLooks ← GetSelLooks[srcSel];
targetLooks ← GetSelLooks[targetSel];
TiogaSelection.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: TiogaLooks.Looks, targetSel: Selection] = BEGIN
IF targetSel.granularity # point THEN {
EditSpan.ChangeLooks[SelectionRoot[targetSel],
[targetSel.start.pos, targetSel.end.pos], remove, add,
TiogaInput.currentEvent];
targetSel.pendingDelete ← FALSE };
targetSel.looks ← TiogaLooksSupport.ModifyLooks[targetSel.looks, remove, add];
AdjustTypeScriptLooks[targetSel, add, remove];
END;
ChangeCaretLooks: PUBLIC PROC [add, remove: TiogaLooks.Looks] = BEGIN
LockSel[primary, "ChangeCaretLooks"];
pSel.looks ← TiogaLooksSupport.ModifyLooks[pSel.looks, remove, add];
AdjustTypeScriptLooks[pSel, add, remove];
UnlockSel[primary];
END;
SelAtEndOfTypeScript: PROC [targetSel: Selection] RETURNS [BOOL] = {
tdd: TiogaDocumentData;
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 ~TiogaImpl.CaretAtEnd[targetSel] THEN RETURN [FALSE];
RETURN [TRUE] };
Capital: PROC [c: CHAR] RETURNS[CHAR] = INLINE {
RETURN[IF c IN ['a..'z] THEN LOOPHOLE[c - 'a + 'A] ELSE c]};
AdjustTypeScriptLooks: PROC [targetSel: Selection, add, remove: TiogaLooks.Looks] = BEGIN
IF ~SelAtEndOfTypeScript[targetSel] THEN RETURN;
FOR c: CHAR IN TiogaLooks.Look DO
IF remove[c] THEN TypeScript.ChangeLooks[targetSel.viewer, Capital[c]];
IF add[c] THEN TypeScript.ChangeLooks[targetSel.viewer, c];
ENDLOOP;
END;
END.