EditNodeImpl.mesa; written by Bill Paxton, June 1981
edited by Bill Paxton, December 23, 1982 2:47 pm
DIRECTORY
EditSpan,
EditSpanSupport,
EditGroup,
EditNotify,
NodeProps,
Rope,
UndoEvent,
TextEdit,
TreeSlice,
TiogaLooks,
TiogaNode,
TiogaNodeOps,
TiogaTreeOps;
EditNodeImpl: CEDAR PROGRAM
IMPORTS NodeProps, TextEdit, EditGroup, TiogaNodeOps, TiogaTreeOps, UndoEvent, EditSpan, EditNotify
EXPORTS EditSpan
SHARES TiogaNode =
BEGIN
OPEN EditSpan, EditSpanSupport, TreeSlice, EditNotify;
Slice: TYPE = TreeSlice.Slice;
nullSpan: TreeSpan = TiogaTreeOps.nullSpan;
nullBranchSpan: BranchSpan = TiogaTreeOps.nullBranchSpan;
***** New nodes
Insert: PUBLIC PROC [root: RefBranchNode, old: Ref, where: Place ← after, event: Event ← NIL]
RETURNS [new: Ref] = TRUSTED {
empty copy of old node is inserted in tree in position determined by "where"
IF old=NIL THEN RETURN [NIL];
new ← WITH old SELECT FROM
br: RefBranchNode => TiogaNodeOps.NewBranchNode[],
tx: RefTextNode => TiogaNodeOps.NewTextNode[tx.class],
bx: TiogaNode.RefBoxNode => TiogaNodeOps.NewBoxNode[bx.class],
ls: TiogaNode.RefListNode => TiogaNodeOps.NewListNode[ls.class],
bs: TiogaNode.RefBasicNode => TiogaNodeOps.NewBasicNode[bs.class],
ENDCASE => ERROR;
Inherit[old, new];
DoInsertNode[root, old, new, where, event] };
InsertTextNode: PUBLIC PROC
[root: RefBranchNode, old: Ref, class: TiogaNode.ItemClassID,
where: Place ← after, inherit: BOOLFALSE, event: Event ← NIL]
RETURNS [new: RefTextNode] = BEGIN
IF old=NIL THEN RETURN [NIL];
new ← TiogaNodeOps.NewTextNode[class];
IF inherit THEN Inherit[old, new];
DoInsertNode[root, old, new, where, event];
END;
InsertBranchNode: PUBLIC PROC [
root: RefBranchNode, old: Ref, where: Place ← after, inherit: BOOLFALSE, event: Event ← NIL]
RETURNS [new: TiogaNode.RefBranchNode] = BEGIN
IF old=NIL THEN RETURN [NIL];
new ← TiogaNodeOps.NewBranchNode[];
IF inherit THEN Inherit[old, new];
DoInsertNode[root, old, new, where, event];
END;
InsertBoxNode: PUBLIC PROC [
root: RefBranchNode, old: Ref, class: TiogaNode.ItemClassID,
where: Place ← after, inherit: BOOLFALSE, event: Event ← NIL]
RETURNS [new: TiogaNode.RefBoxNode] = BEGIN
IF old=NIL THEN RETURN [NIL];
new ← TiogaNodeOps.NewBoxNode[class];
IF inherit THEN Inherit[old, new];
DoInsertNode[root, old, new, where, event];
END;
InsertListNode: PUBLIC PROC [
root: RefBranchNode, old: Ref, class: TiogaNode.ItemClassID,
where: Place ← after, inherit: BOOLFALSE, event: Event ← NIL]
RETURNS [new: TiogaNode.RefListNode] = BEGIN
IF old=NIL THEN RETURN [NIL];
new ← TiogaNodeOps.NewListNode[class];
IF inherit THEN Inherit[old, new];
DoInsertNode[root, old, new, where, event];
END;
InsertBasicNode: PUBLIC PROC [
root: RefBranchNode, old: Ref, class: TiogaNode.BasicClassID,
where: Place ← after, inherit: BOOLFALSE, event: Event ← NIL]
RETURNS [new: TiogaNode.RefBasicNode] = BEGIN
IF old=NIL THEN RETURN [NIL];
new ← TiogaNodeOps.NewBasicNode[class];
IF inherit THEN Inherit[old, new];
DoInsertNode[root, old, new, where, event];
END;
Inherit: PUBLIC PROC [old, new: Ref, allprops: BOOLFALSE] = {
newitm, olditm: TiogaNode.RefItemNode;
newbs, oldbs: TiogaNode.RefBasicNode;
CopyProp: PROC [name: ATOM, value: REF] RETURNS [BOOL] = {
newvalue: REF;
IF ~allprops THEN SELECT name FROM
$Prefix, $Postfix, $StyleDef => NULL; -- these control formatting
ENDCASE => RETURN [FALSE]; -- don't copy anything else
IF (newvalue ← NodeProps.CopyInfo[name: name, value: value, from: old, to: new]) # NIL
THEN NodeProps.PutProp[new, name, newvalue];
RETURN [FALSE] };
new.format ← old.format;
new.comment ← old.comment;
IF old.props # NIL AND (allprops OR old.hasprefix OR old.haspostfix OR old.hasstyledef) THEN
[] ← NodeProps.MapProps[old, CopyProp, FALSE, FALSE] };
DoInsertNode: PROC [root: RefBranchNode, old, new: Ref, where: Place, event: Event] = {
notify: REF InsertingNode Change;
oldBr, newBr: RefBranchNode;
Link: PROC [node, sib: Ref] = INLINE {
sib.next ← node.next; sib.last ← node.last; node.last ← FALSE; node.next ← sib };
NotifyBefore: PROC = INLINE {
new.new ← TRUE;
notify ← NEW[InsertingNode Change ← [InsertingNode[root, new, old, where]]];
Notify[notify, before] };
IF new = NIL OR old = NIL THEN RETURN;
IF where = before THEN { -- convert to other kind of insert
prev: Ref;
IF TiogaNodeOps.IsBranch[old] # TiogaNodeOps.IsBranch[new] THEN ERROR CannotDoEdit;
IF (prev ← TiogaTreeOps.Previous[old]) # NIL THEN { where ← sibling; old ← prev }
ELSE { where ← child; old ← TiogaTreeOps.Parent[old] }};
SELECT where FROM
sibling => {
IF TiogaNodeOps.IsBranch[old] # TiogaNodeOps.IsBranch[new] THEN ERROR CannotDoEdit;
NotifyBefore[]; Link[old, new] };
after => {
oldBr ← TiogaNodeOps.NarrowToBranchNode[old];
newBr ← TiogaNodeOps.NarrowToBranchNode[new];
IF (oldBr=NIL) # (newBr=NIL) THEN ERROR CannotDoEdit;
NotifyBefore[]; Link[old, new];
IF oldBr # NIL AND oldBr.child # NIL THEN {-- adopt oldBr's children
newBr.child ← oldBr.child;
TiogaTreeOps.LastSibling[newBr.child].next ← newBr;
oldBr.child ← NIL }};
child => WITH old SELECT FROM
br: RefBranchNode => {
NotifyBefore[];
newBr ← TiogaNodeOps.NarrowToBranchNode[new];
IF newBr # NIL THEN { -- insert new branch as child of old branch
IF br.child # NIL THEN { new.next ← br.child; new.last ← FALSE }
ELSE { new.next ← br; new.last ← TRUE };
br.child ← newBr }
ELSE { -- insert new as contents of old
IF br.contents # NIL THEN { new.next ← br.contents; new.last ← FALSE }
ELSE { new.next ← br; new.last ← TRUE };
br.contents ← TiogaNodeOps.NarrowToItemNode[new] }};
bx: TiogaNode.RefBoxNode => {
NotifyBefore[];
IF bx.contents # NIL THEN { new.next ← bx.contents; new.last ← FALSE }
ELSE { new.next ← bx; new.last ← TRUE };
bx.contents ← new };
ls: TiogaNode.RefListNode => {
NotifyBefore[];
IF ls.contents # NIL THEN { new.next ← ls.contents; new.last ← FALSE }
ELSE { new.next ← ls; new.last ← TRUE }};
ENDCASE => ERROR CannotDoEdit;
ENDCASE => ERROR;
Notify[notify, after];
UndoEvent.Note[event, UndoInsertNode, notify] };
UndoInsertNode: PROC [undoRef: REF, currentEvent: Event] = TRUSTED {
saved: REF Change ← NARROW[undoRef];
WITH x:saved SELECT FROM
InsertingNode => { [] ← EditGroup.DeleteGroup[x.root, x.new, x.new, FALSE, currentEvent] };
ENDCASE => ERROR };
***** split & merge
JoinStatements: PUBLIC PROC [root, statement: RefBranchNode, event: Event ← NIL]
RETURNS [loc: TreeLoc] = {
moves contents of statement to end of previous one and then deletes statement
pred: RefBranchNode ← TiogaTreeOps.Backward[statement].back;
dest, source: Ref;
IF pred = NIL OR TiogaTreeOps.Parent[pred] = NIL THEN ERROR CannotDoEdit;
dest ← TiogaTreeOps.Contents[pred];
IF (source ← TiogaTreeOps.Contents[statement])=NIL THEN
IF dest=NIL THEN loc ← [pred, NodeItself]
ELSE {
dest ← TiogaTreeOps.LastSibling[dest];
loc ← [dest, IF ~TiogaNodeOps.IsText[dest] THEN NodeItself
ELSE TiogaNodeOps.Size[TiogaNodeOps.NarrowToTextNode[dest]]] }
ELSE -- move source contents to end of pred contents
IF dest=NIL THEN { -- pred doesn't have contents yet
loc ← [source, IF TiogaNodeOps.IsText[source] THEN 0 ELSE NodeItself];
EditGroup.MoveGroup[
destRoot: root, sourceRoot: root, dest: pred, contents: TRUE,
sourceStart: source, sourceEnd: TiogaTreeOps.LastSibling[source], event: event] }
ELSE { -- move to end of contents of pred
destText, sourceText: RefTextNode;
dest ← TiogaTreeOps.LastSibling[dest];
EditGroup.MoveGroup[
destRoot: root, sourceRoot: root, dest: dest, contents: FALSE,
sourceStart: source, sourceEnd: TiogaTreeOps.LastSibling[source], event: event];
IF (destText ← TiogaNodeOps.NarrowToTextNode[dest]) # NIL AND
(sourceText ← TiogaNodeOps.NarrowToTextNode[source]) # NIL THEN {
loc.node ← dest;
loc.where ← TextEdit.CopyText[root, root, destText, MaxLen, sourceText, 0, MaxLen, event].resultStart;
[] ← EditGroup.DeleteGroup[root, sourceText, sourceText, FALSE, event] }
ELSE loc ← [source, NodeItself] };
[] ← EditGroup.DeleteGroup[root, statement, statement, FALSE, event] };
BreakTextNode: PUBLIC PROC [root: RefBranchNode, loc: TreeLoc, event: Event ← NIL] RETURNS [new: Ref] = {
old: Ref ← loc.node;
oldTxt, newTxt: RefTextNode;
IF (oldTxt ← TiogaNodeOps.NarrowToTextNode[old]) = NIL THEN RETURN [NIL];
new ← Insert[root, old, after, event];
IF loc.where = NodeItself THEN RETURN;
newTxt ← TiogaNodeOps.NarrowToTextNode[new];
[] ← TextEdit.MoveText[destRoot: root, sourceRoot: root, dest: newTxt, destLoc: 0,
source: oldTxt, start: loc.where, event: event] };
BreakParent: PUBLIC PROC [root: RefBranchNode, node: Ref, before: BOOLTRUE, event: Event ← NIL]
RETURNS [parent: Ref] = {
Inserts copy of parent as next sibling of parent.
If before is true, moves children starting with node to be contents of new.
Otherwise, moves children starting with next sibling of node.
Returns the parent of node after the break.
move, new: Ref;
IF node=NIL THEN RETURN [NIL];
IF (parent ← TiogaTreeOps.Parent[node])=NIL THEN ERROR CannotDoEdit;
IF TiogaNodeOps.IsBranch[node] AND TiogaNodeOps.IsBranch[parent] THEN ERROR CannotDoEdit;
new ← Insert[root, parent, after, event];
IF before THEN { move ← node; parent ← new }
ELSE move ← TiogaTreeOps.Next[node];
IF move#NIL THEN EditGroup.MoveGroup[
destRoot: root, sourceRoot: root, dest: new, contents: TRUE,
sourceStart: move, sourceEnd: TiogaTreeOps.LastSibling[move], event: event] };
BreakStatement: PUBLIC PROC [root: RefBranchNode, node: Ref, before: BOOLTRUE, event: Event ← NIL]
RETURNS [RefBranchNode] = {
loc points somewhere in the contents of a statement. This works its way up breaking nodes until contents from node on have been moved to a new statement. If before is true, node moves to the new statement; otherwise, it stays in the old one.
Returns the statement which contains node after doing the break.
IF node=NIL THEN RETURN [NIL];
UNTIL TiogaNodeOps.IsBranch[node] DO node ← BreakParent[root, node, before, event]; ENDLOOP;
RETURN [TiogaNodeOps.NarrowToBranchNode[node]] };
END....