DIRECTORY Ascii, Basics, Buttons, Commander USING [CommandProc, Register], Containers USING [ChildXBound, ChildYBound, Container, Create], FS, IO, MessageWindow, RefText, Rope, Rules USING [Create, Rule], ASortDirDefs, TypeScript, VFonts, ViewerClasses USING [Viewer, ViewerClassRec], ViewerIO USING [CreateViewerStreams], ViewerOps USING [PaintViewer], ViewerTools USING [GetContents, MakeNewTextViewer, SetSelection]; ASortDir: CEDAR PROGRAM IMPORTS Buttons, Commander, Containers, FS, IO, MessageWindow, RefText, Rope, Rules, TypeScript, VFonts, ViewerIO, ViewerOps, ViewerTools = BEGIN OPEN ASortDirDefs; entryHeight: CARDINAL = 15; -- how tall to make each line of items entryVSpace: CARDINAL = 8; -- vertical leading space between lines entryHSpace: CARDINAL = 10; -- horizontal space between items in a line ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; dash: CHAR = Ascii.ControlV; PromptRec: TYPE = RECORD [ handle: Handle, viewer: ViewerClasses.Viewer _ NIL]; PromptHandle: TYPE = REF PromptRec; MakeTool: Commander.CommandProc = BEGIN rule: Rules.Rule; my: Handle _ NEW[MyRec]; my.outer _ Containers.Create[[-- construct the outer container name: "Directory Sorter", -- name displayed in the caption iconic: TRUE, -- so tool will be iconic (small) when first created column: left, -- initially in the left column scrollable: FALSE ]]; -- inhibit user from scrolling contents MakeCommands[my]; -- build each (sub)viewer in turn rule _ Rules.Create [[parent: my.outer, wy: my.height, ww: my.outer.cw, wh: 2]]; Containers.ChildXBound[my.outer, rule]; my.height _ my.height + entryHeight + 2; -- interline spacing MakeTypescript[my]; my.dinnerOrder['d] _ 1; my.dinnerOrder['h] _ 2; my.dinnerOrder['i] _ 3; my.dinnerOrder['o] _ 4; my.dinnerOrder['-] _ 5; my.dinnerOrder[' ] _ 6; my.activityOrder['a] _ 1; my.activityOrder['b] _ 2; my.activityOrder['c] _ 3; my.activityOrder['-] _ 4; my.activityOrder[' ] _ 5; my.ageOrder['k] _ 1; my.ageOrder['y] _ 2; my.ageOrder['a] _ 3; my.ageOrder['s] _ 4; my.ageOrder['-] _ 5; my.ageOrder[' ] _ 6; my.levelOrder['9] _ 1; my.levelOrder['8] _ 2; my.levelOrder['7] _ 3; my.levelOrder['6] _ 4; my.levelOrder['5] _ 5; my.levelOrder['4] _ 6; my.levelOrder['3] _ 7; my.levelOrder['2] _ 8; my.levelOrder['1] _ 9; my.levelOrder['c] _ 10; my.levelOrder['0] _ 11; my.levelOrder['-] _ 12; my.levelOrder[' ] _ 13; ViewerOps.PaintViewer[my.outer, all]; -- reflect above change END; MakeTypescript: PROC [handle: Handle] = BEGIN handle.height _ handle.height + entryVSpace; -- space down from the top of the viewer handle.ts _ TypeScript.Create[ info: [name: "SortDir.ts", wy: handle.height, parent: handle.outer, border: FALSE ]]; [handle.tsIn, handle.tsOut] _ ViewerIO.CreateViewerStreams [ name: "SortDir.ts", viewer: handle.ts, backingFile: "SortDir.ts", editedStream: FALSE]; Containers.ChildXBound[handle.outer, handle.ts]; Containers.ChildYBound[handle.outer, handle.ts]; END; MakeCommands: PROC [handle: Handle] = BEGIN initialData: Rope.ROPE = NIL; wx: INT _ 0; NewLine: PROC = {handle.height _ handle.height + entryHeight + entryVSpace; wx _ 0}; LabeledItem: PROC [label: ROPE, width: INT, data: ROPE _ NIL] RETURNS [v: ViewerClasses.Viewer] = { ph: PromptHandle _ NEW [PromptRec _ [handle: handle]]; t: Buttons.Button _ Buttons.Create[ info: [ name: Rope.Concat[label, ":"], wy: handle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform wx: wx, parent: handle.outer, border: FALSE ], proc: Prompt, clientData: ph]; -- this will be passed to our button proc wx _ wx + t.ww + entryHSpace; v _ ViewerTools.MakeNewTextViewer[ [ parent: handle.outer, wx: wx, wy: handle.height, ww: width*VFonts.CharWidth['0], wh: entryHeight, data: data, scrollable: FALSE, border: FALSE]]; ph.viewer _ v; wx _ wx + v.ww + entryHSpace}; Cmd: PROC [label: ROPE, proc: Buttons.ButtonProc] = { t: Buttons.Button _ Buttons.Create[ info: [ name: label, wx: wx, wy: handle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: TRUE ], proc: proc, clientData: handle]; -- this will be passed to our button proc wx _ wx + t.ww + entryHSpace}; Bool: PROC [label: ROPE, initial: BOOL] RETURNS [flag: REF BOOL] = { t: Buttons.Button; flag _ NEW[BOOL _ initial]; t _ Buttons.Create[ info: [ name: label, wx: wx, wy: handle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: TRUE ], proc: ToggleBool, clientData: flag]; -- this will be passed to our button proc Buttons.SetDisplayStyle[ button: t, style: IF initial THEN $WhiteOnBlack ELSE $BlackOnWhite, paint: FALSE]; wx _ wx + t.ww + entryHSpace}; NewLine[]; Cmd["Sort", DoIt]; Cmd["Merge", MergeFiles]; NewLine[]; handle.recruiterNumber _ Bool["recruiterNumber", FALSE]; handle.callerNumber _ Bool["callerNumber", FALSE]; handle.dinner _ Bool["dinner", FALSE]; handle.level _ Bool["level", FALSE]; handle.age _ Bool["age", FALSE]; handle.activity _ Bool["activity", FALSE]; NewLine[]; handle.address _ Bool["address", FALSE]; handle.zip _ Bool["zip", FALSE]; handle.recruiter _ Bool["recruiter", FALSE]; handle.caller _ Bool["caller", FALSE]; handle.phone _ Bool["phone", FALSE]; handle.member _ Bool["member", FALSE]; NewLine[]; handle.cmd.inputFile _ LabeledItem["input", 50, "///FUMC/"]; NewLine[]; handle.cmd.outputFile _ LabeledItem["output", 50, "///FUMC/"]; NewLine[]; handle.cmd.secondary _ LabeledItem["secondary input", 50, "///FUMC/"]; NewLine[]; handle.cmd.callers _ LabeledItem["callers", 50, "///FUMC/CallerNames.txt"]; NewLine[]; END; Prompt: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = BEGIN ph: PromptHandle _ NARROW[clientData]; ViewerTools.SetSelection[ph.viewer]; -- force the selection END; ToggleBool: Buttons.ButtonProc = { switch: REF BOOL _ NARROW [clientData]; switch^ _ ~switch^; Buttons.SetDisplayStyle[ button: NARROW[parent], style: IF switch^ THEN $WhiteOnBlack ELSE $BlackOnWhite]; }; DoIt: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = BEGIN handle: Handle _ NARROW[clientData]; -- get our data BEGIN ENABLE { UNWIND => {IF handle.in # NIL THEN handle.in.Close[]; handle.in _ NIL}; Problem, ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}}; iName: ROPE = ViewerTools.GetContents[handle.cmd.inputFile]; oName: ROPE = ViewerTools.GetContents[handle.cmd.outputFile]; handle.item _ NEW[EntrySeqBody[1000]]; IF iName = NIL OR oName = NIL THEN { handle.tsOut.Put[[rope["specify file names"]], [character['\n]]]; RETURN}; handle.in _ OpenFile[iName]; IF handle.in # NIL THEN handle.eof _ FALSE ELSE handle.eof _ TRUE; handle.tsOut.PutText["Reading:"]; WHILE ~handle.eof DO EnterItem[handle, FALSE]; IF handle.item.count MOD 50 = 0 THEN handle.tsOut.PutChar['.]; ENDLOOP; handle.tsOut.PutText["Sorting:"]; SortEntries[handle, FALSE]; handle.tsOut.PutText["Writing:"]; handle.out _ FS.StreamOpen[fileName: oName, accessOptions: $create]; FOR i: CARDINAL IN [0..handle.item.count) DO WriteEntry[handle, handle.item[i]]; handle.out.PutChar['\n]; handle.item[i] _ NIL; ENDLOOP; handle.item _ NIL; EXITS done => NULL; END; -- of Enable IF handle.in # NIL THEN {handle.in.Close[]; handle.in _ NIL}; IF handle.out # NIL THEN {handle.out.Close[]; handle.out _ NIL}; handle.tsOut.Put[[character['\n]], [rope["done"]], [character['\n]]]; END; MergeFiles: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = BEGIN handle: Handle _ NARROW[clientData]; -- get our data BEGIN ENABLE { UNWIND => {IF handle.in # NIL THEN handle.in.Close[]; handle.in _ NIL}; Problem, ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}}; iName: ROPE = ViewerTools.GetContents[handle.cmd.inputFile]; oName: ROPE = ViewerTools.GetContents[handle.cmd.outputFile]; sName: ROPE = ViewerTools.GetContents[handle.cmd.secondary]; i1, i2: CARDINAL; handle.item _ NEW[EntrySeqBody[1000]]; handle.secondary _ NEW[EntrySeqBody[300]]; IF iName = NIL OR oName = NIL THEN { handle.tsOut.Put[[rope["specify file names"]], [character['\n]]]; RETURN}; handle.in _ OpenFile[iName]; IF handle.in # NIL THEN handle.eof _ FALSE ELSE Quit[handle, "No input file"]; handle.tsOut.PutText["Reading primary:"]; WHILE ~handle.eof DO EnterItem[handle, FALSE]; IF handle.item.count MOD 50 = 0 THEN handle.tsOut.PutChar['.]; ENDLOOP; handle.tsOut.PutText["Sorting:"]; SortEntries[handle, FALSE]; handle.in2 _ OpenFile[sName]; IF handle.in2 # NIL THEN handle.eof _ FALSE ELSE Quit[handle, "No secondary file"]; handle.tsOut.PutText["Reading secondary:"]; WHILE ~handle.eof DO EnterItem[handle, TRUE]; IF handle.secondary.count MOD 50 = 0 THEN handle.tsOut.PutChar['.]; ENDLOOP; handle.tsOut.PutText["Sorting:"]; SortEntries[handle, TRUE]; handle.tsOut.PutText["Merging:"]; handle.out _ FS.StreamOpen[fileName: oName, accessOptions: $create]; i1 _ i2 _ 0; DO r1, r2: REF Entry; comp: Basics.Comparison; IF i1 < handle.item.count THEN r1 _ handle.item[i1] ELSE r1 _ NIL; IF i2 < handle.secondary.count THEN r2 _ handle.secondary[i2] ELSE r2 _ NIL; IF r1 = NIL AND r2 = NIL THEN EXIT; comp _ CompareProc[handle, r1, r2]; SELECT comp FROM less => { WriteEntry[handle, r1]; handle.out.PutChar['\n]; i1 _ i1 + 1}; greater => { WriteEntry[handle, r2]; handle.out.PutChar['\n]; i2 _ i2 + 1}; ENDCASE => { IF r1 # NIL THEN i1 _ i1 + 1; WriteEntry[handle, r2]; handle.out.PutChar['\n]; i2 _ i2 + 1}; ENDLOOP; handle.item _ NIL; handle.secondary _ NIL; EXITS done => NULL; END; -- of Enable IF handle.in # NIL THEN {handle.in.Close[]; handle.in _ NIL}; IF handle.in2 # NIL THEN {handle.in2.Close[]; handle.in2 _ NIL}; IF handle.out # NIL THEN {handle.out.Close[]; handle.out _ NIL}; handle.tsOut.Put[[character['\n]], [rope["done"]], [character['\n]]]; END; UC: PROC [c: CHAR] RETURNS [CHAR] = { RETURN[IF c IN ['a..'z] THEN VAL[c.ORD - ORD['a] + ORD['A]] ELSE c]}; CFName: PROC [name: ROPE] RETURNS [cf: ROPE] = { cName: REF TEXT _ NEW[TEXT[Rope.Length[name]]]; -- plenty long FOR i: INT IN [0..Rope.Length[name]) DO c: CHAR _ UC[Rope.Fetch[name, i]]; SELECT c FROM '*, '+ => LOOP; ENDCASE => { cName[cName.length] _ c; cName.length _ cName.length + 1}; ENDLOOP; RETURN[Rope.FromRefText[cName]]}; Problem: ERROR = CODE; MyBreak: IO.BreakProc -- [char: CHAR] RETURNS [IO.CharClass] -- = { RETURN [SELECT char FROM '\\, '|, '}, '\n => break, '\t, ', ' => sepr, ENDCASE => other]; }; OpenFile: PROC [name: ROPE] RETURNS [st: STREAM] = { st _ FS.StreamOpen[name, $read ! FS.Error => IF error.group # bug THEN CONTINUE]}; EnterItem: PROC [handle: Handle, secondary: BOOL] = { e: REF Entry; st1, st2: CARDINAL; ch: CHAR; num: INT _ 0; st: STREAM = IF secondary THEN handle.in2 ELSE handle.in; IF handle.eof THEN RETURN; IF handle.in = NIL THEN { MessageWindow.Append[ message: "Please open a file first", clearFirst: TRUE]; MessageWindow.Blink[ ]; ERROR ABORTED}; [] _ st.SkipWhitespace[]; IF st.EndOf[] THEN {handle.eof _ TRUE; GO TO done}; e _ ReadEntry[handle, st]; e.cname _ CFName[e.name[0]]; st1 _ 0; st2 _ IF e.addr[0] = NIL THEN 0 ELSE CARDINAL[Rope.Length[e.addr[0]]]; WHILE st1 < st2 AND (ch _ Rope.Fetch[e.addr[0], st1]) IN ['0..'9] DO num _ 10*num + ch.ORD - '0.ORD; st1 _ st1 + 1; ENDLOOP; e.number _ num; WHILE st1 < st2 AND Rope.Fetch[e.addr[0], st1] = ' DO st1 _ st1 + 1 ENDLOOP; FOR k: CARDINAL IN [st1..st2) DO IF Rope.Fetch[e.addr[0], k] = ', THEN {st2 _ k; EXIT}; ENDLOOP; e.street _ Rope.Substr[base: e.addr[0], start: st1, len: st2-st1]; IF e.town # NIL THEN e.ctown _ e.town ELSE IF e.addr[0] = NIL THEN e.ctown _ "" ELSE { FOR k: CARDINAL IN [0..4) DO IF e.addr[k] = NIL THEN {e.ctown _ e.addr[k-1]; EXIT}; REPEAT FINISHED => e.ctown _ e.addr[3]; ENDLOOP}; IF secondary THEN { IF handle.secondary.count = handle.secondary.max THEN GrowSecondaryRec[handle]; handle.secondary[handle.secondary.count] _ e; handle.secondary.count _ handle.secondary.count + 1} ELSE { IF handle.item.count = handle.item.max THEN GrowItemRec[handle]; handle.item[handle.item.count] _ e; handle.item.count _ handle.item.count + 1}; EXITS done => NULL; }; GrowItemRec: PROC [h: Handle] = { n: CARDINAL; new: EntrySeq; IF h.item = NIL THEN n _ 1000 ELSE n _ h.item.max + 100; new _ NEW[EntrySeqBody[n]]; IF h.item # NIL THEN { FOR i: CARDINAL IN [0..h.item.count) DO new[i] _ h.item[i]; ENDLOOP; new.count _ h.item.count}; h.item _ new}; GrowSecondaryRec: PROC [h: Handle] = { n: CARDINAL; new: EntrySeq; IF h.secondary = NIL THEN n _ 500 ELSE n _ h.secondary.max + 100; new _ NEW[EntrySeqBody[n]]; IF h.secondary # NIL THEN { FOR i: CARDINAL IN [0..h.secondary.count) DO new[i] _ h.secondary[i]; ENDLOOP; new.count _ h.secondary.count}; h.secondary _ new}; ReadEntry: PROC [handle: Handle, st: STREAM] RETURNS [e: REF Entry] = { ENABLE IO.EndOfStream => {handle.eof _ TRUE; Quit2[handle, st, "Syntax error "]}; ch: CHAR; i: CARDINAL; e _ NEW [Entry]; IF (ch _ st.GetChar[]) # '{ THEN Quit2[handle, st, "Syntax error "]; i _ 0; DO e.phone[i] _ GetTokenRope[st, MyBreak].token; SELECT (ch _ st.GetChar[]) FROM '\\ => IF i = 3 THEN Quit2[handle, st, "Syntax error "]; '| => EXIT; '} => RETURN; ENDCASE => Quit2[handle, st, "Syntax error "]; i _ i + 1; ENDLOOP; i _ 0; DO e.name[i] _ GetTokenRope[st, MyBreak].token; SELECT (ch _ st.GetChar[]) FROM '\\ => IF i = 3 THEN Quit2[handle, st, "Syntax error "]; '| => EXIT; '} => RETURN; ENDCASE => Quit2[handle, st, "Syntax error "]; i _ i + 1; ENDLOOP; i _ 0; DO e.addr[i] _ GetTokenRope[st, MyBreak].token; SELECT (ch _ st.GetChar[]) FROM '\\ => IF i = 3 THEN Quit2[handle, st, "Syntax error "]; '| => EXIT; '} => RETURN; ENDCASE => GO TO badsyntax; i _ i + 1; ENDLOOP; e.town _ GetTokenRope[st, MyBreak].token; IF st.GetChar[] = '} THEN RETURN; e.zip _ GetTokenRope[st, MyBreak].token; IF st.GetChar[] # '} THEN Quit2[handle, st, "Syntax error "]; EXITS badsyntax => Quit[handle, "Syntax error "]; }; WriteEntry: PROC [handle: Handle, e: REF Entry] = { st: STREAM _ handle.out; i: CARDINAL; st.PutChar['{]; FOR i IN [0..4) WHILE e.phone[i] # NIL DO IF i # 0 THEN st.PutChar['\\]; st.PutRope[e.phone[i]]; ENDLOOP; st.PutRope["|\t"]; IF e.phone[0] = NIL THEN st.PutRope["\t\t\t\t"]; FOR i IN [0..4) WHILE e.name[i] # NIL DO IF i # 0 THEN st.PutChar['\\]; st.PutRope[e.name[i]]; ENDLOOP; IF e.addr[0] = NIL AND e.town = NIL AND e.zip = NIL THEN { st.PutChar['}]; RETURN}; st.PutRope["|\t"]; FOR i IN [0..4) WHILE e.addr[i] # NIL DO IF i # 0 THEN st.PutChar['\\]; st.PutRope[e.addr[i]]; ENDLOOP; IF e.town = NIL AND e.zip = NIL THEN {st.PutChar['}]; RETURN}; st.PutRope["|\t"]; IF e.town # NIL THEN st.PutRope[e.town]; IF e.zip = NIL THEN {st.PutChar['}]; RETURN}; st.PutRope["|\t"]; st.PutRope[e.zip]; st.PutChar['}]; }; Quit: PROC [handle: Handle, reason: ROPE _ NIL] = { loc: INT = handle.in.GetIndex[]; handle.in.Close[]; handle.in _ NIL; handle.eof _ TRUE; handle.tsOut.Put[[rope[reason]], [integer[loc]], [character['\n]]]; ERROR Problem}; Quit2: PROC [handle: Handle, st: STREAM, reason: ROPE _ NIL] = { loc: INT = st.GetIndex[]; st.Close[]; handle.tsOut.Put[[rope[reason]], [integer[loc]], [character['\n]]]; ERROR Problem}; GetToken: PROC [stream: STREAM, breakProc: IO.BreakProc, buffer: REF TEXT] RETURNS[token: REF TEXT, charsSkipped: INT] = { quit, include: BOOL _ FALSE; anySeen: BOOL _ FALSE; charsSkipped _ 0; buffer.length _ 0; DO char: CHAR _ stream.GetChar[ ! IO.EndOfStream => IF buffer.length > 0 THEN EXIT ELSE REJECT]; SELECT breakProc[char] FROM break => {include _ FALSE; quit _ TRUE}; sepr => {include _ FALSE; quit _ anySeen }; other => {include _ TRUE; quit _ FALSE; anySeen _ TRUE}; ENDCASE => ERROR; IF include THEN buffer _ RefText.InlineAppendChar[buffer, char] ELSE IF quit THEN stream.Backup[char] ELSE charsSkipped _ charsSkipped + 1; IF quit THEN EXIT; ENDLOOP; RETURN[buffer, charsSkipped]; }; GetTokenRope: PUBLIC PROC [stream: STREAM, breakProc: IO.BreakProc] RETURNS [token: ROPE, charsSkipped: INT] = { buffer: REF TEXT = RefText.ObtainScratch[100]; { ENABLE UNWIND => RefText.ReleaseScratch[buffer]; tokenText: REF TEXT; [tokenText, charsSkipped] _ GetToken[stream, breakProc, buffer]; token _ IF tokenText.length = 0 THEN NIL ELSE Rope.FromRefText[tokenText]; }; RefText.ReleaseScratch[buffer]; RETURN [token, charsSkipped]; }; CompareProc: PROC [h: Handle, r1, r2: REF Entry] RETURNS [Basics.Comparison] = { IF r1 = NIL THEN -- to simplify merge, make NIL be greater than anything IF r2 = NIL THEN RETURN [equal] ELSE RETURN[greater] ELSE IF r2 = NIL THEN RETURN[less]; IF h.address^ THEN { SELECT Rope.Compare[r1.ctown, r2.ctown] FROM greater => RETURN[greater]; less => RETURN[less]; ENDCASE; SELECT Rope.Compare[r1.street, r2.street] FROM greater => RETURN[greater]; less => RETURN[less]; ENDCASE; SELECT r1.number FROM > r2.number => RETURN[greater]; < r2.number => RETURN[less]; ENDCASE; }; IF h.phone^ THEN SELECT Rope.Compare[r1.phone[0], r2.phone[0]] FROM greater => RETURN[greater]; less => RETURN[less]; ENDCASE; SELECT Rope.Compare[r1.cname, r2.cname] FROM greater => RETURN[greater]; less => RETURN[less]; ENDCASE; RETURN[equal]}; SortEntries: PROC [h: Handle, secondary: BOOL] = { Greater: PROC [r1, r2: REF Entry] RETURNS [BOOL] = { RETURN[CompareProc[h, r1, r2] = greater]}; IF secondary THEN Sort[h.secondary, h.secondary.count, Greater] ELSE Sort[h.item, h.item.count, Greater]; }; Sort: PROC [ a: EntrySeq, n: CARDINAL, greater: PROC [r1, r2: REF Entry] RETURNS [BOOL]] = { i: CARDINAL; temp: REF Entry; SiftUp: PROC [l, u: CARDINAL] = { s: CARDINAL; key: REF Entry _ a[l-1]; DO s _ l*2; IF s > u THEN EXIT; IF s < u AND greater[a[s+1-1], a[s-1]] THEN s _ s+1; IF greater[key, a[s-1]] THEN EXIT; a[l-1] _ a[s-1]; l _ s; ENDLOOP; a[l-1] _ key}; FOR i DECREASING IN [2..n/2] DO SiftUp[i, n]; ENDLOOP; FOR i DECREASING IN [2..n] DO SiftUp[1, i]; temp _ a[1-1]; a[1-1] _ a[i-1]; a[i-1] _ temp; ENDLOOP}; Commander.Register[key: "SortDir", proc: MakeTool, doc: "Sort a church directory" ]; [ ] _ MakeTool[NIL]; -- and create an instance END. ”ASortDir.mesa; Last Edited by: Sweet, December 2, 1985 11:45:27 pm PST The Containers interface is used to create an outer envelope or "container" for the different sections below. For uniformity, we define some standard distances between entries in the tool. set order on interesting values (bogus ones come out first) default the width so that it will be computed for us -- default the width so that it will be computed for us -- default the width so that it will be computed for us -- force the selection into the user input field force the selection into the user input field various initializations do the work force the selection into the user input field various initializations do the work extract interesting data enter into list to be sorted one should SkipWhitespace before calling (and check for eof) copied from IOSearchImpl because it didn't handle empty tokens properly Sort by various keys Κœ– "Cedar" style˜Iproc– "Cedar" stylešœ™J™7unitšΟk ˜ Lšœ˜J˜Jšœ˜Jšœ œ˜(Jšœ œ/˜?Jšœ˜Jšœ˜J˜J˜Jšœ˜Jšœœ˜J˜ J˜ J˜Jšœœ˜-Jšœ œ˜%Jšœ œ˜Jšœ œ0˜A—šœ œœ˜Jšœ%œ]˜‹—Lš œ˜Jšœ½™½Jšœ œΟc&˜BJšœ œž'˜CJšœ œž+˜HJšœœœ˜Jšœœœœ˜Jšœœ˜J˜˜J˜—šœ œœ˜Jšœ/œ˜4—Jšœœœ ˜#šœ"˜'Jšœ˜Jšœ œ˜emphasisšœžœ˜>Jšœž ˜:Jšœœž4˜DJšœž˜0Jšœ œœž'˜>—Jšœž!˜4JšœP˜PJšœ'˜'Jšœ)ž˜=Jšœ˜šœ;™;J˜GJ˜GJ˜MJ˜3J˜>J˜>J˜DJ˜DJ˜DJ˜GJ˜—Jšœ)ž˜@Jšœ˜—J˜šΟnœœœ˜.Jšœ-ž(˜Ušœ˜JšœLœ˜U—šœ<˜Jšœ%˜%Jšœœ ˜6šœ#˜#šœ˜Jšœ˜Jšœ˜Jšœ7™7Jšœž4˜EJšœ˜Jšœ˜Jšœœ˜—Jšœ ˜ Jšœž)˜:—Jšœ˜šœ$˜$Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ œ˜Jšœœ˜—J˜Jšœ˜—šŸœœ œ˜5šœ#˜#šœ˜Jšœ ˜ J˜Jšœ˜Jšœ7™7Jšœž4˜EJšœ˜Jšœœ˜—Jšœ ˜ Jšœž(œ˜>—Jšœ˜—šŸœœ œ œœœœ˜DJ˜Jšœœœ ˜šœ˜šœ˜Jšœ ˜ J˜Jšœ˜Jšœ7™7Jšœž4˜EJšœ˜Jšœœ˜—Jšœ˜Jšœž(œ˜<—J•StartOfExpansion[]š œ,œ œœœ˜lJšœ˜—J˜Jšœ ˜ J˜J˜Jšœ ˜ J˜Jšœ1œ˜8Jšœ+œ˜2Jšœœ˜&Jšœœ˜$Jšœœ˜ Jšœ#œ˜*Jšœ ˜ J˜Jšœ!œ˜(Jšœœ˜ Jšœ%œ˜,Jšœœ˜&Jšœœ˜$Jšœœ˜&Jšœ ˜ J˜Jšœ<˜˜>Jšœ ˜ J˜JšœF˜FJšœ ˜ J˜JšœK˜KJšœ ˜ Jšœ˜—–† -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- šœΠck…œ˜©Jšœ-™-Jšœœ ˜&Jšœ&ž˜Jšœ˜—J˜!Jšœœ˜J˜!JšœD˜Dšœœœ˜,Jšœ#˜#J˜Jšœœ˜Jšœ˜—Jšœœ˜š˜Jšœœ˜ —Jšœž ˜Jšœ œœ!œ˜=Jšœœœ#œ˜@J˜EJšœ˜J˜—–† -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- šœ …œ˜­Jšœ-™-Jšœœž˜4Jš˜šœ˜Jš œœ œœ œ˜GJšœ œ'œœ˜D—Jšœœ1˜Jšœ˜—J˜!Jšœœ˜J˜Jšœœœ˜+Jšœ#˜'J˜+šœ ˜Jšœœ˜Jšœœœ˜CJšœ˜—J˜!Jšœœ˜J˜!JšœD˜DJ˜ š˜Jšœœ˜J˜Jšœœœœ˜BJšœœœœ˜LJš œœœœœœ˜#J˜#šœ˜˜ Jšœ˜J˜Jšœ ˜ —˜ Jšœ˜J˜Jšœ ˜ —šœ˜ Jšœœœ ˜Jšœ˜J˜Jšœ ˜ ——Jšœ˜—Jšœœœ˜*š˜Jšœœ˜ —Jšœž ˜Jšœ œœ!œ˜=Jšœœœ#œ˜@Jšœœœ#œ˜@J˜EJšœ˜J˜—š œœœœœ˜%Jšœœœ œœœœœœ˜EJ˜—š Ÿœœœœœ˜0Jš œœœœœž˜>šœœœ˜'Jšœœœ˜"šœ˜ Jšœ œ˜šœ˜ Jšœ:˜:——Jšœ˜—Jšœ˜!J˜—Jšœ œœ˜J˜–+ -- [char: CHAR] RETURNS [IO.CharClass] -- šœ œ  *œ˜Dšœœ˜Jšœ˜J˜Jšœ ˜—J˜J˜—š Ÿœœœœœ˜4šœœ˜Jš œœ œœœ˜3—J˜—šŸ œœœ˜5Jšœœ˜ Jšœ œ˜Jšœœ˜ Jšœœ˜ Jš œœœ œ œ ˜9Jšœ œœ˜šœ œœ˜šœ˜Jšœ$˜$Jšœ œ˜—Jšœ˜Jš œ˜—Jšœ˜Jš œ œœœœ˜3Jšœ˜Jšœ™J˜Jš œœ œœœœ˜Ošœ œ#œ ˜DJšœœœ˜J˜Jšœ˜—J˜Jšœ œ!œœ˜Mšœœœ ˜ Jšœœ œ˜6Jšœ˜—J˜BJšœ œœ˜%Jšœœ œœ ˜)šœ˜šœœœ˜Jšœ œœœ˜6š˜Jšœ˜ —Jšœ˜ ——Jšœ™šœ œ˜Jšœ/œ˜OJšœ.˜.Jšœ4˜4—šœ˜Jšœ%œ˜@J˜O—š˜Jšœœ˜ —J˜—J˜šŸ œœ˜!Jšœœ˜ J˜Jšœ œœ œ˜8Jšœœ˜šœ œœ˜šœœœ˜'J˜Jšœ˜—J˜—J˜J˜—šŸœœ˜&Jšœœ˜ J˜Jšœœœ œ˜AJšœœ˜šœœœ˜šœœœ˜,J˜Jšœ˜—J˜—J˜J˜—š Ÿ œœœœœ ˜GJšœ<™J˜Jšœ œœ˜(Jšœ œœœ˜-J˜Jšœ˜Jšœ˜J˜J˜—šŸœœœœ˜3Jšœœ˜ Jšœ6˜6JšœC˜CJšœ ˜J˜—š Ÿœœœ œœ˜@Jšœœ˜Jšœ ˜ JšœC˜CJšœ ˜J˜—JšœG™Gš Ÿœœ œ œœœ˜JJšœ œœ˜/Jšœœœ˜Jšœ œœ˜Jšœ˜J˜š˜šœœ˜ Jš œœœœœ˜P—šœ˜Jšœœ œ˜(Jšœœ˜+Jšœœ œ œ˜8Jšœœ˜—Jšœ œ0˜?š˜Jšœœœ!˜F—Jšœœœ˜Jšœ˜—Jšœ˜Jšœ˜—š Ÿ œœœ œ œ ˜CJšœœœ˜,Jšœœœ˜.šœœœ#˜2Jšœ œ˜Jšœ@˜@JšœJ˜JJ˜—Jšœ˜Jšœ˜J˜J˜—Jšœ™J™šŸ œœœœ˜Pšœœœž7˜HJšœœœœ˜Jšœœ ˜—Jš œœœœœ˜#šœ œ˜šœ"˜,Jšœ œ ˜Jšœœ˜Jšœ˜—šœ$˜.Jšœ œ ˜Jšœœ˜Jšœ˜—šœ ˜Jšœœ ˜Jšœœ˜Jšœ˜—J˜—šœ œœ(˜CJšœ œ ˜Jšœœ˜Jšœ˜—šœ"˜,Jšœ œ ˜Jšœœ˜Jšœ˜—Jšœ ˜J˜—šŸ œœœ˜2š Ÿœœ œœœ˜4Jšœ$˜*—Jšœ œ.˜?Jšœ%˜)J˜J˜—šŸœœ˜ J˜ Jšœœ˜ Jš œ œ œœœ˜5Jšœœ˜ Jšœœ˜J˜šŸœœœ˜!Jšœœ˜ Jšœœ˜š˜J˜Jšœœœ˜Jšœœœ ˜4Jšœœœ˜"J˜J˜Jšœ˜—J˜J˜—Jš œ œœ œœ˜6šœ œœ˜J˜ J˜J˜J˜Jšœ˜ J˜——J˜šœ2˜2Jšœ!˜!—Mšœœž˜.I modheaderšœ˜Ic˜N˜J˜—…—GdL