-- 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]; }; }. ..