DIRECTORY Atom USING [GetPName], Commander USING [CommandProc, Handle, Register], CommandTool USING [DoCommand, GetProp], Convert USING [RopeFromInt], EditedStream USING [GetEcho, Rubout, SetEcho, SetMode, UnAppendBufferChars], FileNames USING [GetShortName], IO USING [BreakProc, Close, EndOf, EndOfStream, Error, GetRefAny, GetToken, IDProc, PeekChar, RIS, SkipWhitespace, STREAM, TokenProc], MBQueue USING [CreateMenuEntry], Menus USING [AppendMenuEntry, ClickProc, CopyEntry, CreateMenu, FindEntry, Menu, MenuEntry, ReplaceMenuEntry], ReadEvalPrint USING [Handle], Rope USING [Cat, Concat, Equal, Find, FromChar, FromRefText, Length, Match, Replace, ROPE, SkipTo, Substr], RopeList USING [Reverse], TiogaOps USING [GetSelection, Location, LocOffset, Ref, Root], UserCredentials USING [Get, Login], UserProfile USING [CallWhenProfileChanges, Line, ProfileChangedProc], ViewerClasses USING [Viewer], ViewerIO USING [GetBuffer, TypeChars], ViewerOps USING [EnumerateViewers, EnumProc, FetchProp, PaintViewer], ViewerTools USING [GetSelectionContents, SetSelection]; CommandsCImpl: CEDAR PROGRAM IMPORTS Atom, Commander, CommandTool, Convert, EditedStream, FileNames, IO, MBQueue, Menus, Rope, RopeList, TiogaOps, UserCredentials, UserProfile, ViewerIO, ViewerOps, ViewerTools = BEGIN AliasCellObject: TYPE = RECORD [ args: LIST OF ROPE _ NIL, def: ROPE ]; AliasCell: TYPE = REF AliasCellObject; ROPE: TYPE = Rope.ROPE; lastUser: ROPE _ NIL; asterisky: BOOLEAN _ FALSE; Alias: Commander.CommandProc = TRUSTED { commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name, def: ROPE; token: REF TEXT _ NEW[TEXT[30]]; args: LIST OF ROPE _ NIL; aliasCell: AliasCell; { ENABLE { IO.EndOfStream => GOTO Die; IO.Error => GOTO Die; }; token.length _ 0; token _ commandLineStream.GetToken[IO.TokenProc, token].token; name _ Rope.FromRefText[token]; [] _ commandLineStream.SkipWhitespace[FALSE]; IF NOT commandLineStream.EndOf[] AND commandLineStream.PeekChar[] = '( THEN { FOR l: LIST OF REF ANY _ NARROW[commandLineStream.GetRefAny[]], l.rest UNTIL l = NIL DO WITH l.first SELECT FROM r: ROPE => args _ CONS[r, args]; a: ATOM => args _ CONS[Atom.GetPName[a], args]; ENDCASE => ERROR; ENDLOOP; TRUSTED {args _ RopeList.Reverse[args]; }; }; token.length _ 0; token _ commandLineStream.GetToken[CRBreak, token].token; def _ Rope.FromRefText[token]; aliasCell _ NEW[AliasCellObject _ [args, def]]; Commander.Register[ key: name, proc: AliasImplProc, doc: Rope.Concat["Alias ", cmd.commandLine], clientData: aliasCell]; commandLineStream.Close[]; EXITS Die => NULL; }; }; RopeSubst: PROC [old, new, base: ROPE, case: BOOL _ FALSE, allOccurrences: BOOL _ TRUE] RETURNS[ROPE] = { lenOld: INT = old.Length[]; lenNew: INT = new.Length[]; i: INT _ 0; WHILE (i _ Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1 DO base _ Rope.Replace[base: base, start: i, len: lenOld, with: new]; IF ~allOccurrences THEN EXIT; i _ i + lenNew; ENDLOOP; RETURN[base]; }; RopeMemb: PROC [rope: ROPE, lst: LIST OF ROPE] RETURNS[BOOL] = { FOR l: LIST OF ROPE _ lst, l.rest UNTIL l = NIL DO IF Rope.Equal[rope, l.first, FALSE] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE] }; gennum: LONG CARDINAL _ 10000; UniqueRope: PROC [c: CHAR _ 'A] RETURNS[ROPE] = { gennum _ gennum + 1; RETURN[Rope.Concat[Rope.FromChar[c], Convert.RopeFromInt[gennum, 10, FALSE]]]; }; AliasImplProc: Commander.CommandProc = TRUSTED { aliasCell: AliasCell _ NARROW[cmd.procData.clientData]; newCommandLine: ROPE _ aliasCell.def; token: REF TEXT _ NEW[TEXT[40]]; new: ROPE; restOfStream: ROPE; commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; synonyms: LIST OF REF SynonymRecord; SynonymRecord: TYPE = RECORD[key, val: ROPE]; FOR l: LIST OF ROPE _ aliasCell.args, l.rest UNTIL l = NIL DO token.length _ 0; token _ commandLineStream.GetToken[IO.IDProc, token ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; new _ Rope.FromRefText[token]; IF RopeMemb[new, l.rest] THEN { dummy: ROPE = UniqueRope[]; synonyms _ CONS[NEW[SynonymRecord _ [new, dummy]], synonyms]; new _ dummy; }; newCommandLine _ RopeSubst[old: l.first, new: new, base: newCommandLine, case: FALSE]; ENDLOOP; FOR l: LIST OF REF SynonymRecord _ synonyms, l.rest UNTIL l = NIL DO newCommandLine _ RopeSubst[old: l.first.val, new: l.first.key, base: newCommandLine, case: FALSE]; ENDLOOP; token.length _ 0; token _ commandLineStream.GetToken[CRBreak, token ! IO.EndOfStream => CONTINUE; IO.Error => GO TO Nasty ].token; restOfStream _ Rope.FromRefText[token]; newCommandLine _ Rope.Cat[newCommandLine, restOfStream, "\n"]; commandLineStream.Close[]; result _ CommandTool.DoCommand[commandLine: newCommandLine, parent: cmd]; EXITS Nasty => {msg _ "IO.Error while parsing arguments"; result _ $Failed}; }; CRBreak: IO.BreakProc = { IF char = '\n THEN RETURN[break]; RETURN[other]; }; CreateButton: Commander.CommandProc = TRUSTED { commandLineStream: IO.STREAM = IO.RIS[cmd.commandLine]; name: ROPE; def: ROPE; token: REF TEXT _ NEW[TEXT[30]]; br: ButtonImplRef _ NEW[ButtonImplObject]; cth: Commander.Handle _ cmd; reph: ReadEvalPrint.Handle _ br.rep _ GetReadEvalPrint[cmd]; { ENABLE { IO.EndOfStream => GOTO Die; IO.Error => GOTO Die; }; token.length _ 0; token _ commandLineStream.GetToken[IO.TokenProc, token].token; name _ Rope.FromRefText[token]; [] _ commandLineStream.SkipWhitespace[FALSE]; token.length _ 0; token _ commandLineStream.GetToken[CRBreak, token ! IO.EndOfStream => CONTINUE].token; EXITS Die => NULL; }; IO.Close[commandLineStream]; def _ Rope.FromRefText[token]; SELECT TRUE FROM Rope.Length[def] = 0 => def _ NIL; Rope.Match["*\n", def] => {}; ENDCASE => def _ Rope.Concat[def, "\n"]; br.def _ def; IF reph = NIL THEN RETURN [$Failure, "Can't find $ReadEvalPrintHandle"] ELSE { viewer: ViewerClasses.Viewer _ reph.viewer; IF viewer # NIL THEN { menu: Menus.Menu _ reph.viewer.menu; IF menu # NIL THEN { new: Menus.MenuEntry _ IF def = NIL THEN NIL ELSE MBQueue.CreateMenuEntry[q: reph.menuHitQueue, name: name, proc: ButtonImplButtonProc, clientData: br]; old: Menus.MenuEntry _ Menus.FindEntry[menu, name]; SELECT TRUE FROM old # NIL => Menus.ReplaceMenuEntry[menu, old, new]; new # NIL => Menus.AppendMenuEntry[menu, new, 0]; ENDCASE; ViewerOps.PaintViewer[viewer: viewer, hint: menu, clearClient: FALSE]; }; }; }; }; ClearMenu: Commander.CommandProc = TRUSTED { reph: ReadEvalPrint.Handle _ GetReadEvalPrint[cmd]; IF reph # NIL THEN { viewer: ViewerClasses.Viewer _ reph.viewer; IF viewer # NIL THEN { oldMenu: Menus.Menu _ viewer.menu; IF oldMenu # NIL THEN { newMenu: Menus.Menu _ Menus.CreateMenu[1]; CopyNamedEntry["STOP!", oldMenu, newMenu]; CopyNamedEntry["Find", oldMenu, newMenu]; CopyNamedEntry["Split", oldMenu, newMenu]; reph.viewer.menu _ newMenu; ViewerOps.PaintViewer[viewer: viewer, hint: menu, clearClient: FALSE]; }; RETURN; }; }; }; CopyNamedEntry: PROC [name: ROPE, oldMenu,newMenu: Menus.Menu] = { old: Menus.MenuEntry _ Menus.FindEntry[oldMenu, name]; IF old # NIL THEN Menus.AppendMenuEntry[newMenu, Menus.CopyEntry[old], 0]; }; GetReadEvalPrint: PROC [cmd: Commander.Handle] RETURNS [ReadEvalPrint.Handle] = { DO WITH CommandTool.GetProp[cmd, $ReadEvalPrintHandle] SELECT FROM reph: ReadEvalPrint.Handle => IF reph # NIL AND reph.viewer # NIL THEN RETURN [reph]; ENDCASE; WITH CommandTool.GetProp[cmd, $ParentCommander] SELECT FROM next: Commander.Handle => {cmd _ next; LOOP;}; ENDCASE; RETURN [NIL]; ENDLOOP; }; StuffCommand: PROC [viewer: ViewerClasses.Viewer, commands: ROPE] = { WITH ViewerOps.FetchProp[viewer, $ReadEvalPrint] SELECT FROM reph: ReadEvalPrint.Handle => { data: ButtonImplRef _ NEW[ButtonImplObject _ [rep: reph, def: commands]]; ButtonImplButtonProc[viewer, data]; }; ENDCASE; }; ButtonImplButtonProc: Menus.ClickProc = { br: ButtonImplRef _ NARROW[clientData]; def: ROPE; curSel: ROPE; viewer: ViewerClasses.Viewer _ NIL; start: TiogaOps.Location; viewerName: ROPE _ NIL; fileName: ROPE _ NIL; shortFileName: ROPE _ NIL; index: INT _ -1; pos: INT; controlRope: ROPE _ IF control THEN "control" ELSE "noControl"; shiftRope: ROPE _ IF shift THEN "shift" ELSE "noShift"; buttonRope: ROPE; SELECT mouseButton FROM red => buttonRope _ "left"; yellow => buttonRope _ "middle"; blue => buttonRope _ "right"; ENDCASE => ERROR; IF br = NIL THEN RETURN; def _ br.def; [viewer: viewer, start: start] _ TiogaOps.GetSelection[primary]; curSel _ ViewerTools.GetSelectionContents[]; IF viewer # NIL AND NOT viewer.destroyed AND NOT viewer.newFile THEN { root: TiogaOps.Ref _ TiogaOps.Root[start.node]; offset: INT _ TiogaOps.LocOffset[loc1: [root, 0], loc2: start, skipCommentNodes: TRUE]; index _ offset; viewerName _ viewer.file; IF viewerName = NIL THEN viewerName _ viewer.name; fileName _ viewer.file; fileName _ Rope.Substr[fileName, 0, Rope.SkipTo[fileName, 0, "!"]]; }; pos _ curSel.Find["\n"]; IF pos # -1 THEN curSel _ Rope.Substr[base: curSel, start: 0, len: pos]; fileName _ IF (Rope.SkipTo[s: curSel, pos: 0, skip: " \t"] = curSel.Length[]) AND (curSel.Length[] > 1) THEN curSel ELSE fileName; shortFileName _ FileNames.GetShortName[path: fileName, stripOffVersionNumber: TRUE]; IF Rope.SkipTo[def, 0, "$"] < Rope.Length[def] THEN { def _ RopeSubst[old: "$CurrentSelection$", new: curSel, base: def, case: TRUE]; def _ RopeSubst[old: "$FileNameSelection$", new: fileName, base: def, case: TRUE]; def _ RopeSubst[old: "$ShortFileNameSelection$", new: shortFileName, base: def, case: TRUE]; def _ RopeSubst[old: "$SelectedViewerName$", new: viewerName, base: def, case: TRUE]; def _ RopeSubst[old: "$ViewerPosition$", new: Convert.RopeFromInt[index, 10, FALSE], base: def, case: TRUE]; def _ RopeSubst[old: "$MouseButton$", new: buttonRope, base: def, case: TRUE]; def _ RopeSubst[old: "$ControlKey$", new: controlRope, base: def, case: TRUE]; def _ RopeSubst[old: "$ShiftKey$", new: shiftRope, base: def, case: TRUE]; }; { bufferContents: REF TEXT _ ViewerIO.GetBuffer[br.rep.in]; IF bufferContents # NIL AND bufferContents.length > 0 AND bufferContents[bufferContents.length - 1] # '\n THEN { FOR n: NAT DECREASING IN [0..bufferContents.length) DO IF bufferContents[n] = '\n THEN { EditedStream.UnAppendBufferChars[ stream: br.rep.in, nChars: bufferContents.length - n - 1]; EXIT; } REPEAT FINISHED => EditedStream.UnAppendBufferChars[stream: br.rep.in, nChars: LAST[NAT]]; ENDLOOP; }; }; IF viewer = br.rep.viewer THEN ViewerTools.SetSelection[viewer: viewer, selection: NIL]; ViewerIO.TypeChars[editedViewerStream: br.rep.in, chars: def]; }; ButtonImplRef: TYPE = REF ButtonImplObject; ButtonImplObject: TYPE = RECORD [ rep: ReadEvalPrint.Handle _ NIL, def: ROPE _ NIL ]; WhenProfileChanges: UserProfile.ProfileChangedProc = { IF reason = rollBack THEN { user: ROPE _ UserCredentials.Get[].name; IF NOT Rope.Equal[user, lastUser, FALSE] THEN { firstTime: BOOL _ TRUE; eachTool: ViewerOps.EnumProc = { IF Rope.Match["CommandTool: *", v.name] AND v.class.flavor = $Typescript AND NOT v.destroyed THEN { IF firstTime THEN { firstTime _ FALSE; StuffCommand[v, "///Commands/NoteNewUser\n"]; }; StuffCommand[v, "///Commands/NotePerLogin\n"]; }; }; ViewerOps.EnumerateViewers[eachTool]; }; }; }; NotePerLogin: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.PerLogin", NIL]; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NoteNewUser: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.NewUser", NIL]; lastUser _ UserCredentials.Get[].name; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NotePerCommandTool: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.PerCommandTool", NIL]; IF line = NIL THEN line _ UserProfile.Line["CommandTool.EachCommandToolCommands", NIL]; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; NoteBootCommands: Commander.CommandProc = { line: ROPE _ UserProfile.Line["CommandTool.BootCommands", NIL]; IF line # NIL THEN [] _ CommandTool.DoCommand[line, cmd]; }; Login: Commander.CommandProc = { oldEcho: IO.STREAM; StartInteraction: PROC RETURNS [in, out: IO.STREAM] = { IF asterisky THEN EditedStream.SetMode[stream: cmd.in, echoAsterisks: TRUE] ELSE { oldEcho _ EditedStream.GetEcho[cmd.in]; EditedStream.SetEcho[cmd.in, NIL]; }; in _ cmd.in; out _ cmd.out; }; EndInteraction: PROC [in, out: IO.STREAM] = { IF asterisky THEN EditedStream.SetMode[stream: cmd.in, echoAsterisks: FALSE] ELSE EditedStream.SetEcho[cmd.in, oldEcho]; }; UserCredentials.Login[ startInteraction: StartInteraction, endInteraction: EndInteraction, options: [alwaysInteract: TRUE] ! EditedStream.Rubout => {result _ $Failed; msg _ " -- Aborted.\n"; CONTINUE} ]; }; Init: PROC = { Commander.Register[key: "///Commands/Alias", proc: Alias, doc: "Create an alias for a command", interpreted: FALSE]; Commander.Register[key: "///Commands/CreateButton", proc: CreateButton, doc: "Create a CommandTool herald button", interpreted: FALSE]; Commander.Register[key: "///Commands/ClearMenu", proc: ClearMenu, doc: "Reset the CommandTool menu"]; Commander.Register[key: "///Commands/Login", proc: Login, doc: "Login a new user"]; Commander.Register[key: "///Commands/NoteBootCommands", proc: NoteBootCommands, doc: "Perform commands for the first commander after a full boot"]; Commander.Register[key: "///Commands/NoteNewUser", proc: NoteNewUser, doc: "Perform commands for a new user"]; Commander.Register[key: "///Commands/NotePerCommandTool", proc: NotePerCommandTool, doc: "Perform commands for a login (or rollback)"]; Commander.Register[key: "///Commands/NotePerLogin", proc: NotePerLogin, doc: "Perform commands for a login (or rollback)"]; Commander.Register[key: "///Commands/RemoveButton", proc: CreateButton, doc: "Remove a CommandTool herald button"]; UserProfile.CallWhenProfileChanges[WhenProfileChanges]; }; Init[]; END. ΐCommandsCImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. L. Stewart, January 17, 1984 11:08 am Russ Atkinson, April 11, 1985 12:25:22 pm PST Russ Atkinson (RRA) August 19, 1985 6:56:37 pm PDT The "last" known user (initially unknown) set by NoteNewUser TRUE if the typescript supports *'s during login if old is not found in base, then value = base. if allOccurrences THEN substitute for each occurrence of old, otherwise only for first. e.g. args are (x y) and substituting y gorp. The first token willl be the name of the button. The rest of the commandLine will be the thing to stuff. A special character sequence, $$, will stand for the current selection. Get the rest of it! Various cases: (0) no old entry & no new definition => ignore (1) no old entry & a new definition => create a new entry (2) old entry & no new definition => remove the old one (3) old entry & a new definition => replace the old entry This command will clean the current menu back to its ground state (STOP! Find Split). There is an old menu, so we make a clean new one. Returns the enclosing ReadEvalPrint.Handle (NIL if no such handle). [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] There are specialized tokens: $CurrentSelection$ => replaced by the current selection up to but not including the first carriage return $FileNameSelection$ => replaced by the current selection if it appears to be a file name, otherwise replaced by the name of the selected viewer $ShortFileNameSelection$ => same as $FileNameSelection$ except that version number and directory are omitted $SelectedViewerName$ => replaced by the name of the selected viewer $ViewerPosition$ => replaced by the position of the current selection in a viewer $MouseButton$ => "left", "middle", or "right" $ShiftKey$ => "shift", "noShift" $ControlKey$ "control", "noControl" Get prefix of current selection before the first CR The curSel is the fileName if curSel is longer than one character and contains no whitespace. It is likely that we have substitutions to do If the selected viewer is the commandtool, then set the caret to the end, or the ViewerIO.TypeChars won't work. We get this whenever we rollback, or the credentials change. There is a new user, so we should try to customize the various command tools that exist. [v: ViewerClasses.Viewer] RETURNS [BOOL _ TRUE] RRA: EachCommandToolCommands is only for compatibility ΚΆ˜codešœ™Kšœ Οmœ7™BK™&K™-K™2—K˜šΟk ˜ Kšœžœ ˜Kšœ žœ!˜0Kšœ žœ˜'Kšœžœ˜Kšœ žœ:˜LKšœ žœ˜KšžœžœVžœžœ ˜†Kšœžœ˜ Kšœžœc˜nKšœžœ ˜KšœžœKžœ˜kKšœ žœ ˜Kšœ žœ0˜>Kšœžœ˜#Kšœ žœ4˜EKšœžœ ˜Kšœ žœ˜&Kšœ žœ6˜EKšœ žœ&˜7—K˜šœžœž˜KšžœAžœj˜΄Kšœž˜K˜šœžœžœ˜ Kš œžœžœžœžœ˜Kšœž˜ K˜—K˜Kšœ žœžœ˜&K˜Kšžœžœžœ˜šœ žœžœ˜Kšœ<™<—šœ žœžœ˜Kšœ0™0—K˜—šœžœ˜(Kš œžœžœžœžœ˜7Kšœ žœ˜Kš œžœžœžœžœ˜ Kš œžœžœžœžœ˜K˜˜šžœ˜Kšžœžœ˜Kšžœ žœ˜K˜—K˜Kšœ#žœ˜>Kšœ˜Kšœ&žœ˜-šžœžœžœ#žœ˜Mšžœžœžœžœžœžœ(žœžœž˜Xšžœ žœž˜Kšœžœ žœ ˜ Kšœžœ žœ˜/Kšžœžœ˜—Kšžœ˜—Kšžœ#˜*K˜—K˜Kšœ9˜9Kšœ˜Kšœ žœ ˜/šœ˜Kšœ ˜ Kšœ˜Kšœ-˜-Kšœ˜—Kšœ˜šž˜Kšœžœ˜ —K˜—K˜—K˜šΟn œžœžœžœžœžœžœžœžœ˜išœ/™/KšœW™W—Kšœžœ˜Kšœžœ˜Kšœžœ˜ šžœ>ž˜EKšœB˜BKšžœžœžœ˜K˜Kšžœ˜—Kšžœ˜ K˜—K˜šŸœžœžœžœžœžœžœžœ˜@š žœžœžœžœžœžœž˜2Kš žœžœžœžœžœ˜6Kšžœ˜—Kšžœžœ˜ K˜—K˜Kšœ žœžœ ˜K˜š Ÿ œžœžœžœžœ˜1K˜Kšžœ?žœ˜NKšžœ˜—K™šœ'žœ˜0Kšœžœ˜7Kšœžœ˜%Kš œžœžœžœžœ˜ Kšœžœ˜ Kšœžœ˜Kš œžœžœžœžœ˜7Kšœ žœžœžœ˜$Kšœžœžœ žœ˜-K˜š žœžœžœžœžœžœž˜=K˜šœ#žœ˜3Kš œžœžœžœ žœžœ˜5Kšœ˜—Kšœ˜šžœžœ˜Kšœ,™,Kšœžœ˜Kšœ žœžœ*˜=K˜ K˜—KšœOžœ˜WKšžœ˜—š žœžœžœžœ"žœžœž˜DKšœ[žœ˜cKšžœ˜—K˜šœ1˜1Kš œžœžœžœ žœžœ˜5Kšœ˜—Kšœ'˜'Kšœ>˜>Kšœ˜KšœI˜Išž˜K˜F—K˜—K˜šœ žœ˜Kšžœ žœžœ˜!Kšžœ˜K˜—K˜šœ&žœ˜/K™²Kš œžœžœžœžœ˜7Kšœžœ˜ Kšœžœ˜ Kš œžœžœžœžœ˜ Kšœžœ˜*K˜Kšœ<˜<˜šžœ˜Kšžœžœ˜Kšžœ žœ˜K˜—K˜Kšœ#žœ˜>Kšœ˜Kšœ&žœ˜-K™K˜Kšœ4žœžœ˜Všž˜Kšœžœ˜ —K˜—šžœ˜K˜—Kšœ˜šžœžœž˜Kšœžœ˜"Kšœ˜Kšžœ!˜(—K˜ K˜šžœž˜ Kšžœžœ.˜9Kšžœ˜šœ™Kšœ.™.Kšœ9™9Kšœ7™7Kšœ9™9—Kšœ+˜+šžœ žœžœ˜Kšœ$˜$šžœžœžœ˜Kš œžœžœžœžœžœg˜˜Kšœ3˜3šžœžœž˜Kšœžœ+˜4Kšœžœ(˜1Kšžœ˜—Kšœ?žœ˜FK˜—K˜—K˜—K˜K˜—šœ#žœ˜,K™UKšœ3˜3šžœžœžœ˜Kšœ+˜+šžœ žœžœ˜Kšœ"˜"šžœ žœžœ˜Kšœ1™1Kšœ*˜*Kšœ*˜*Kšœ)˜)Kšœ*˜*Kšœ˜Kšœ?žœ˜FK˜—Kšžœ˜K˜—K˜—K˜K˜—šŸœžœžœ"˜BKšœ6˜6Kšžœžœžœ9˜JK˜K˜—šŸœžœžœ˜QKšœ,žœ™Cšž˜šžœ0žœž˜?šœ˜Kš žœžœžœžœžœžœ˜7—Kšžœ˜—šžœ,žœž˜;Kšœ'žœ˜.Kšžœ˜—Kšžœžœ˜ Kšžœ˜—K˜K˜—šŸ œžœ*žœ˜Ešžœ-žœž˜<šœ˜Kšœžœ0˜IKšœ#˜#K˜—Kšžœ˜—K˜K˜—•StartOfExpansion‚ -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]šœ)˜)KšΠck~™~™K™jK™K™lK™CK™QK™-K™ K™#—Kšœžœ ˜'Kšœžœ˜ Kšœžœ˜ Kšœžœ˜#Kšœ˜Kšœ žœžœ˜Kšœ žœžœ˜Kšœžœžœ˜Kšœžœ˜Kšœžœ˜ Kš œ žœžœ žœ žœ ˜?Kš œ žœžœžœ žœ ˜7Kšœ žœ˜K˜šžœ ž˜Kšœ˜Kšœ ˜ Kšœ˜Kšžœžœ˜—K˜Kšžœžœžœžœ˜K˜ Kšœ@˜@Kšœ,˜,šžœ žœžœžœžœžœžœ˜FKšœ/˜/KšœžœFžœ˜WK˜Kšœ˜Kšžœžœžœ˜2Kšœ˜K–)[s: ROPE, pos: INT _ 0, skip: ROPE]šœC˜CK˜—K˜K™3Kšœ˜Kšžœ žœ8˜HK™]Kš œ žœAžœžœžœ ˜‚KšœNžœ˜TK˜šžœ-žœ˜5Kšœ-™-KšœIžœ˜OKšœLžœ˜RKšœVžœ˜\KšœOžœ˜UKšœMžœžœ˜lKšœHžœ˜NKšœHžœ˜NKšœDžœ˜JKšœ˜—K˜˜Kšœžœžœ!˜9š žœžœžœžœ1žœ˜pš žœžœž œžœž˜6šžœžœ˜!šœ!˜!Kšœ:˜:—Kšžœ˜K˜—Kšž˜Kšžœ@žœžœ˜SKšžœ˜—K˜—K˜—Kšœo™oKšžœžœ5žœ˜XKšœ>˜>K˜—K˜Kšœžœžœ˜+K˜šœžœžœ˜!Kšœžœ˜ Kšœžœž˜K˜K˜—šœ6˜6šžœžœ˜Kšœ<™šžœ˜Kšœ'˜'Kšœžœ˜"Kšœ˜——K˜ K˜Kšœ˜—šŸœžœ žœžœ˜-šžœ ˜ Kšžœ5žœ˜?Kšžœ'˜+—K˜—˜Kšœ#˜#Kšœ˜Kšœžœ˜KšœDžœ˜MKšœ˜—K˜K˜—šŸœžœ˜Kšœmžœ˜tKšœ€žœ˜‡Kšœe˜eKšœS˜SKšœ“˜“Kšœn˜nKšœ‡˜‡Kšœ{˜{Kšœs˜sKšœ7˜7K˜—K˜K˜K˜Kšžœ˜K˜—…—5ΈQ.