-- EditToolSortImpl.mesa
-- Edited by Paxton on February 8, 1982 9:49 am
DIRECTORY
EditToolPrivate,
Buttons,
Convert,
EditNotify,
EditSpan,
Inline,
IOStream,
UndoEvent,
Labels,
List,
Menus,
MessageWindow,
NodeAddrs,
Rope,
RopeEdit,
RopeReader,
RunReader,
Runtime,
TextEdit,
TextFind,
TextLooks,
TextLooksSupport,
TextNode,
TEditDocument,
TEditInputOps,
TEditOps,
TreeFind,
UserTerminal,
ViewerOps,
ViewerClasses,
ViewerMenus,
ViewerSpecs;
EditToolSortImpl: PROGRAM
IMPORTS EditToolPrivate, Buttons, EditSpan, Labels, MessageWindow,
Rope, RopeEdit, RopeReader, TEditOps, TextEdit, TextNode,
UserTerminal, ViewerOps, ViewerSpecs
EXPORTS EditToolPrivate =
{ OPEN EditToolPrivate, ViewerSpecs;
sortButton: Buttons.Button;
BuildSortButtons: PUBLIC PROC = {
startHeight: CARDINAL ← heightSoFar;
initLeft: CARDINAL = entryLeft;
BuildSortOrderEntry[];
entryLeft ← (openRightWidth-scrollBarW-40)/2;
heightSoFar ← startHeight;
BuildSortKindEntry[];
entryLeft ← initLeft;
sortButton ← Buttons.Create["Sort!", DoSort,
entryLeft, heightSoFar, 0, 0,
NIL, TRUE, container, FALSE];
sortButton.border ← FALSE;
heightSoFar ← heightSoFar + entryHeight + entryVSpace };
----------------------------
----------------------------
sortOrderButton: Buttons.Button;
sortOrderLabel: Labels.Label;
sortIncreasing: PUBLIC BOOLEAN ← TRUE;
sortIncRope: Rope.Ref = "Sort increasing";
sortDecRope: Rope.Ref = "Sort decreasing";
sortIncAtom: ATOM = $EditToolSortIncreasing;
sortDecAtom: ATOM = $EditToolSortDecreasing;
SortOrderButton: Buttons.ButtonProc = {
IF mainEditTool THEN ChangeState[sortOrderLabel,sortIncreasing,sortIncAtom,sortDecAtom]
ELSE IF sortIncreasing THEN [] ← SortDecOp[] ELSE [] ← SortIncOp[] };
SortIncOp: TEditOps.CommandProc = {
sortIncreasing ← TRUE;
Labels.Set[sortOrderLabel,sortIncRope] };
SortDecOp: TEditOps.CommandProc = {
sortIncreasing ← FALSE;
Labels.Set[sortOrderLabel,sortDecRope] };
BuildSortOrderEntry: PROC = {
[sortOrderLabel,sortOrderButton] ←
BuildPair[SortOrderButton,sortIncreasing,sortIncRope,sortDecRope] };
----------------------------
----------------------------
sortKindButton: Buttons.Button;
sortKindLabel: Labels.Label;
sortKind: PUBLIC [0..2] ← sortText;
sortTextRope: Rope.Ref = "Sort Text (blanks delimit)";
sortLinesRope: Rope.Ref = "Sort Lines (CRs delimit)";
sortBranchesRope: Rope.Ref = "Sort Branches";
sortTextAtom: ATOM = $EditToolSortText;
sortLinesAtom: ATOM = $EditToolSortLines;
sortBranchesAtom: ATOM = $EditToolSortBranches;
SortKindButton: Buttons.ButtonProc = {
IF mainEditTool THEN CycleTriple[sortKindLabel,sortKind,
sortTextAtom, sortLinesAtom, sortBranchesAtom]
ELSE SELECT sortKind FROM
sortText => [] ← SortLinesOp[];
sortLines => [] ← SortBranchesOp[];
sortBranches => [] ← SortTextOp[];
ENDCASE => ERROR };
SortTextOp: TEditOps.CommandProc = {
sortKind ← sortText;
Labels.Set[sortKindLabel,sortTextRope] };
SortLinesOp: TEditOps.CommandProc = {
sortKind ← sortLines;
Labels.Set[sortKindLabel,sortLinesRope] };
SortBranchesOp: TEditOps.CommandProc = {
sortKind ← sortBranches;
Labels.Set[sortKindLabel,sortBranchesRope] };
BuildSortKindEntry: PROC = {
[sortKindLabel,sortKindButton] ←
BuildTriple[SortKindButton,sortKind,
sortTextRope, sortLinesRope, sortBranchesRope] };
----------------------------
----------------------------
doSortAtom: ATOM = $EditToolDoSort;
DoSort: Buttons.ButtonProc = {
IF mainEditTool THEN TEditOps.InterpretAtom[button,doSortAtom]
ELSE [] ← DoSortOp[] };
DoSortOp: TEditOps.CommandProc = {
sel: TEditDocument.Selection = TEditOps.GetSelData[];
r1, r2: RopeReader.Ref;
span: TextNode.Span;
moved: BOOLEAN ← FALSE;
increasing: BOOLEAN ← sortIncreasing;
kind: [0..2] ← sortKind;
GetRopeReaders: PROC = {
r1 ← RopeReader.Create[]; r2 ← RopeReader.Create[] };
event: UndoEvent.Ref ← TEditOps.CurrentEvent[];
tSel↑ ← sel↑;
IF tSel=NIL OR tSel.viewer=NIL OR tSel.viewer.class.flavor#$Text THEN {
UserTerminal.BlinkDisplay[]; RETURN };
span ← [tSel.start.pos, tSel.end.pos];
IF kind=sortBranches THEN {
start, end, last, loc: TextNode.Ref;
Next: PROC [n: TextNode.Ref] RETURNS [TextNode.Ref] = INLINE {
RETURN [TextNode.Next[n]] };
GoesBefore: PROC [a, b: TextNode.RefTextNode] RETURNS [BOOLEAN] = {
-- returns true if node a goes before node b
-- should look at children if a and b are equal *** FIX THIS ***
compare: INTEGER ← RopeReader.Compare[r1: a.rope, r2: b.rope, rdr1:r1, rdr2:r2, case: FALSE];
RETURN [IF increasing THEN compare < 0 ELSE compare > 0] };
IsSibling: PROC [first, last: TextNode.Ref] RETURNS [BOOLEAN] = {
FOR n: TextNode.Ref ← first, Next[n] DO
SELECT n FROM
NIL => RETURN [FALSE];
last => EXIT;
ENDCASE;
ENDLOOP;
RETURN [TRUE] };
start ← span.start.node;
FOR n: TextNode.Ref ← span.end.node, TextNode.Parent[n] DO
IF n = NIL THEN { OPEN MessageWindow;
Append["Selection for branch sort must end inside a sibling of start node.",TRUE];
Blink[];
RETURN };
IF IsSibling[start,n] THEN { last ← n; EXIT };
ENDLOOP;
interrupt ← FALSE;
TEditOps.OpenRepeatSequence[];
GetRopeReaders[];
loc ← Next[start];
end ← Next[last];
UNTIL loc=end OR interrupt DO -- insertion sort
next: TextNode.Ref ← Next[loc];
l: TextNode.Ref ← start;
UNTIL l=loc DO -- find dest for [loc,next]
nl: TextNode.Ref ← Next[l];
txtLoc, txtL: TextNode.RefTextNode;
IF (txtLoc ← TextNode.NarrowToTextNode[loc]) # NIL AND
(txtL ← TextNode.NarrowToTextNode[l]) # NIL AND
GoesBefore[txtLoc,txtL] THEN { -- move it
IF loc=last THEN last ← TextNode.Previous[last];
IF l=start THEN start ← loc;
[] ← EditSpan.Move[dest: TextNode.MakeNodeLoc[l],
source: TextNode.MakeNodeSpan[loc,TextNode.LastWithin[loc]],
where: before, event: event];
moved ← TRUE;
EXIT };
l ← nl;
ENDLOOP;
loc ← next;
ENDLOOP;
tSel.start.pos ← [start, 0];
tSel.end.pos ← [last, MAX[TextEdit.Size[TextNode.NarrowToTextNode[last]],1]-1];
SELECT tSel.granularity FROM
node, branch => NULL;
ENDCASE => tSel.granularity ← node;
TEditOps.SetSelData[data: tSel, finalise: FALSE];
}
ELSE { -- sorting text/lines within a single node
node: TextNode.RefTextNode;
rope: Rope.ROPE;
start, loc, end: TextNode.Offset;
Next: PROC [at: TextNode.Offset] RETURNS [next: TextNode.Offset] = { -- scan to break
RopeReader.SetPosition[r1,rope,at];
next ← at;
IF kind=sortText THEN { -- scan to blanks
UNTIL next=end OR RopeEdit.BlankChar[RopeReader.Get[r1]] DO next ← next+1; ENDLOOP;
IF next=end THEN RETURN; next ← next+1;
UNTIL next=end OR ~RopeEdit.BlankChar[RopeReader.Get[r1]] DO next ← next+1; ENDLOOP }
ELSE { -- scan to CRs
UNTIL next=end OR RopeReader.Get[r1] = '\n DO next ← next+1; ENDLOOP;
IF next=end THEN RETURN; next ← next+1;
UNTIL next=end OR RopeReader.Get[r1] # '\n DO next ← next+1; ENDLOOP }};
GoesBefore: PROC [a1, a2, b1, b2: TextNode.Offset] RETURNS [BOOLEAN] = {
-- returns true if text [a1..a2] goes before [b1..b2]
compare: INTEGER ← RopeReader.CompareSubstrs[r1:rope, r2:rope, rdr1:r1, rdr2:r2,
start1: a1, start2: b1, len1: a2-a1, len2: b2-b1, case: FALSE];
RETURN [IF increasing THEN compare < 0 ELSE compare > 0] };
IF span.start.node # span.end.node THEN { OPEN MessageWindow;
Append["Selection for text/line sort must be in a single node.",TRUE];
Blink[]; RETURN };
node ← TextNode.NarrowToTextNode[span.start.node];
IF node=NIL THEN RETURN;
rope ← node.rope;
IF (start ← span.start.where) = TextNode.NodeItself THEN start ← 0;
IF (end ← span.end.where) = TextNode.NodeItself THEN end ← Rope.Size[rope];
IF end <= start THEN RETURN;
end ← end+1; -- location after the things to be sorted
TEditOps.OpenRepeatSequence[];
GetRopeReaders[];
loc ← Next[start];
UNTIL loc=end DO -- insertion sort
next: TextNode.Offset ← Next[loc];
l: TextNode.Offset ← start;
UNTIL l=loc DO -- find dest for [loc,next]
nl: TextNode.Offset ← Next[l];
IF GoesBefore[loc,next,l,nl] THEN { -- move it
addedChar: BOOLEAN ← FALSE;
IF next=end THEN { -- moving the last one
lastChar: CHAR ← TextEdit.FetchChar[node,end-1];
IF kind=sortText THEN {
IF ~RopeEdit.BlankChar[lastChar] THEN { -- add blank
[] ← TextEdit.InsertChar[dest: node, destLoc: end,
char: ' , event: event];
next ← end ← end+1; addedChar ← TRUE }}
ELSE IF lastChar # '\n THEN { -- add CR
[] ← TextEdit.InsertChar[dest: node, destLoc: end,
char: '\n , event: event];
next ← end ← end+1; addedChar ← TRUE }};
[] ← TextEdit.MoveText[dest:node, destLoc:l,
source:node, start:loc, len:next-loc, event:event];
moved ← TRUE;
IF addedChar THEN -- delete trailing delimiter
TextEdit.DeleteText[text: node, start: end-1, len: 1, event: event];
rope ← node.rope;
EXIT };
l ← nl;
ENDLOOP;
loc ← next;
ENDLOOP;
}; -- end of text/line case
IF moved THEN ViewerOps.SetNewVersion[tSel.viewer];
TEditOps.SetSelData[data: tSel, primary: TRUE, finalise: FALSE, setlooks: FALSE];
TEditOps.PaintEdits[tSel]; -- repaint the selection
};
----------------------------
RegisterSort: PUBLIC PROC = {
TEditOps.Register[doSortAtom, DoSortOp];
TEditOps.Register[sortIncAtom, SortIncOp];
TEditOps.Register[sortDecAtom, SortDecOp];
TEditOps.Register[sortTextAtom, SortTextOp];
TEditOps.Register[sortLinesAtom, SortLinesOp];
TEditOps.Register[sortBranchesAtom, SortBranchesOp];
};
UnRegisterSort: PUBLIC PROC = {
TEditOps.UnRegister[doSortAtom];
TEditOps.UnRegister[sortIncAtom];
TEditOps.UnRegister[sortDecAtom];
TEditOps.UnRegister[sortTextAtom];
TEditOps.UnRegister[sortLinesAtom];
TEditOps.UnRegister[sortBranchesAtom];
};
}.
..