DIRECTORY BasicTime USING [ GMT, Now, Period ], Booting USING [ CheckpointProc, RollbackProc, RegisterProcs ], Buttons USING [ButtonProc, Create, ReLabel, SetDisplayStyle ], Commander USING [CommandProc, Handle, Register], Containers USING [ ChildXBound, ChildYBound, Container, Create ], FinchSmarts USING [ AnswerCall, ConvDesc, DisconnectCall, InitFinchSmarts, UninitFinchSmarts ], FinchTool, Icons USING [ IconFlavor ], IO, Labels USING [Create, Label], MBQueue USING [ Create, CreateButton, CreateMenuEntry, Queue ], Menus USING [AppendMenuEntry, CreateMenu, Menu, MenuProc ], Names USING [ CurrentRName ], Rope USING [ Cat, Equal, Find, Length, ROPE, Substr ], Rules USING [ Create ], Log USING [ Report, RegisterWhereToReport, WhereProc ], Process USING [ Pause, SecondsToTicks], Thrush USING [ ConversationHandle, IntervalSpec, NB, nullConvHandle, nullHandle, Reason, StateInConv, ThHandle ], TypeScript USING [ Create ], UserProfile USING [ Token ], VFonts USING [ Font, defaultFont ], ViewerClasses USING [ Viewer, ViewerClassRec, ViewerRec ], ViewerEvents USING [ EventProc, RegisterEventProc ], ViewerIO USING [ CreateViewerStreams ], ViewerLocks USING [ CallUnderWriteLock ], ViewerOps USING [AddProp, ComputeColumn, CreateViewer, DestroyViewer, FetchProp, PaintViewer, SetMenu, SetOpenHeight], ViewerSpecs USING [ openLeftTopY, openLeftWidth, openRightWidth ], ViewerTools USING [GetSelectionContents, GetContents, MakeNewTextViewer, SetContents, SetSelection ] ; FinchToolImpl: CEDAR PROGRAM IMPORTS BasicTime, Booting, Buttons, Commander, Containers, FinchSmarts, FinchTool, IO, Labels, MBQueue, Menus, Names, Rope, Rules, Log, Process, TypeScript, UserProfile, VFonts, ViewerEvents, ViewerIO, ViewerLocks, ViewerOps, ViewerSpecs, ViewerTools EXPORTS FinchTool = { OPEN IO, FinchTool; ConversationHandle: TYPE = Thrush.ConversationHandle; nullConvHandle: ConversationHandle = Thrush.nullConvHandle; ConvDesc: TYPE = FinchSmarts.ConvDesc; NB: TYPE = Thrush.NB; Reason: TYPE = Thrush.Reason; ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; nullHandle: Thrush.ThHandle = Thrush.nullHandle; finchToolHandle: PUBLIC Handle; cmdHandle: Commander.Handle_NIL; printLabel: ARRAY Thrush.StateInConv OF Rope.ROPE_ALL["does not make sense"]; finchQueue: PUBLIC MBQueue.Queue _ MBQueue.Create[]; finchIcon: Icons.IconFlavor _ tool; finchMenu: Menus.Menu; finchStartMenu: Menus.Menu; finchConvMenu: Menus.Menu; xFudge: INTEGER = 4; entryHeight: INTEGER = 14; MakeFinchTool: PROC = BEGIN finchWidth: INTEGER; dif, wH: INTEGER; bt: Viewer; h: Handle; v: Viewer; IF finchToolHandle#NIL THEN { ViewerOps.PaintViewer[finchToolHandle.outer, all]; RETURN; }; finchToolHandle _ NEW[FinchToolRec]; h _ finchToolHandle; MakeMenus[]; finchToolHandle.outer _ Containers.Create[[-- outer container name: "Finch", -- name displayed in the caption iconic: FALSE, -- so tool will not be iconic (small) when first created column: left, -- initially in the left column menu: finchStartMenu,-- displaying our menu command openHeight: ViewerSpecs.openLeftTopY/4, -- See Walnut scrollable: FALSE ]]; -- inhibit user from scrolling contentsLabel v _ h.outer; finchWidth _ IF v.column=right THEN ViewerSpecs.openRightWidth ELSE ViewerSpecs.openLeftWidth; []_ViewerEvents.RegisterEventProc[ proc: UnfinchOnDestroy, event: destroy, filter: v, before: TRUE]; h.status _ FirstLabel[ name: " ", parent: v]; bt _ QueuedButton[name: "Phone", proc: CallProc, border: TRUE, sib: finchToolHandle.status, newLine: TRUE]; bt _ ImmediateButton[name: "Party:", proc: SetCalledPartyProc, border: FALSE, sib: bt]; h.calledPartyText _ NextRightTextViewer[sib: bt, w: finchWidth]; Containers.ChildXBound[h.outer, h.calledPartyText]; bt _ MakeRuler[sib: bt, h: 2]; wH _ v.ch; IF (dif _ (wH-bt.cy)) < 64 THEN { SetOpenHeight[v, wH + (64-dif)]; IF ~v.iconic THEN ViewerOps.ComputeColumn[v.column]; }; h.typescript _ MakeTypescript[sib: bt]; [h.tsIn, h.tsOut] _ ViewerIO.CreateViewerStreams[NIL, h.typescript]; ViewerOps.PaintViewer[v, all]; -- reflect above change END; MakeMenus: PROC = TRUSTED { finchStartMenu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[ menu: finchStartMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Participate", ButtonStartFinch, finchToolHandle] ]; Menus.AppendMenuEntry[ menu: finchStartMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Directory", MakeDirectory, finchToolHandle] ]; finchMenu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Drop Out", ButtonStopFinch, finchToolHandle] ]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Answer", Answer, finchToolHandle] ]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Directory", MakeDirectory, finchToolHandle ] ]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Phone", CallProc, finchToolHandle ] ]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "PhoneSelection", CallSelectedProc, finchToolHandle ] ]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Redial", ReDial, finchToolHandle ] ]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Disconnect", Hangup, finchToolHandle ] ]; finchConvMenu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[ menu: finchConvMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Answer", Answer, finchToolHandle] ]; Menus.AppendMenuEntry[ menu: finchConvMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Disconnect", Hangup, finchToolHandle] ]; }; BuildConversationsWindow: PROC = { IF finchToolHandle.conversations#NIL AND ~finchToolHandle.conversations.destroyed THEN RETURN; finchToolHandle.conversations _ ViewerOps.CreateViewer[ flavor: $Container, paint: TRUE, info: [name: "Conversations", column: left, menu: finchConvMenu, openHeight: ViewerSpecs.openLeftTopY/8, iconic: FALSE, icon: finchIcon, inhibitDestroy: TRUE]]; ViewerOps.PaintViewer[finchToolHandle.outer, all]; ViewerOps.PaintViewer[finchToolHandle.conversations, all]; }; AddConvDesc: PROC[cViewer: Viewer, cDesc: ConvDesc] RETURNS [newV: Viewer] = { lastButton: Viewer; prevDesc: ConvDesc_NIL; isIconic: BOOL; BuildLine: PROC = { IF lastButton.parent = NIL THEN newV _ FirstButton[ q: finchQueue, name: NIL, proc: ConversationMgmtProc, width: 1024, parent: lastButton] ELSE newV _ AnotherButton[ q: finchQueue, name: NIL, proc: ConversationMgmtProc, width: 1024, sib: lastButton, newLine: TRUE]; }; lastButton _ NARROW[ViewerOps.FetchProp[cViewer, $lastButton]]; IF lastButton#NIL THEN prevDesc _ NARROW[ViewerOps.FetchProp[lastButton, $convDesc]] ELSE lastButton_ cViewer; isIconic _ IF lastButton.parent = NIL THEN lastButton.iconic ELSE lastButton.parent.iconic; IF isIconic THEN BuildLine[] ELSE ViewerLocks.CallUnderWriteLock[BuildLine,lastButton]; cDesc.clientData _ newV; ViewerOps.AddProp[newV, $convDesc, cDesc]; lastButton _ newV; ViewerOps.AddProp[cViewer, $lastButton, lastButton]; }; SelectEntryInConversations: PROC[entryButton: Viewer, reselect: BOOL_TRUE] RETURNS[sameAsBefore: BOOL] = { prevSelected: Viewer = NARROW[ViewerOps.FetchProp[entryButton.parent, $selectedEntry]]; IF prevSelected = entryButton AND reselect THEN RETURN[TRUE]; IF prevSelected # NIL AND ~prevSelected.destroyed THEN { Buttons.SetDisplayStyle[prevSelected, $BlackOnWhite]; ViewerOps.PaintViewer[prevSelected, all]; }; IF entryButton.destroyed THEN Report["Conversation is no longer available."] ELSE IF ~reselect THEN ViewerOps.AddProp[entryButton.parent, $selectedEntry, NIL] ELSE { Buttons.SetDisplayStyle[entryButton, $BlackOnGrey]; -- show it is selected ViewerOps.PaintViewer[entryButton, all]; ViewerOps.AddProp[entryButton.parent, $selectedEntry, entryButton]; }; RETURN[FALSE]; }; ReportSystemState: PROC[on: BOOL] = TRUSTED { IF finchToolHandle=NIL THEN RETURN; finchToolHandle.finchActive _ on; IF finchToolHandle.finchWasActive=finchToolHandle.finchActive THEN RETURN; finchToolHandle.finchWasActive_finchToolHandle.finchActive; ViewerOps.SetMenu[finchToolHandle.outer, IF finchToolHandle.finchActive THEN finchMenu ELSE finchStartMenu]; ViewerOps.PaintViewer[finchToolHandle.outer, menu]; IF finchToolHandle.finchActive THEN BuildConversationsWindow[] ELSE { IF finchToolHandle.conversations#NIL THEN { finchToolHandle.conversations.inhibitDestroy _ FALSE; ViewerOps.DestroyViewer[finchToolHandle.conversations]; }; finchToolHandle.conversations _ NIL; }; }; StartFinch: PUBLIC PROC[] = TRUSTED { handle: Handle _ finchToolHandle; IF handle=NIL THEN { MakeFinchTool[]; handle _ finchToolHandle; IF handle=NIL THEN RETURN; -- complain?-- }; Report["Connecting . . ."]; FinchSmarts.InitFinchSmarts[ ReportSystemState, ReportConversationState]; Report[IF handle.finchActive THEN "Finch is connected" ELSE "Could not connect", " to telephone server"]; }; StopFinch: PUBLIC PROC[] = TRUSTED { handle: Handle = finchToolHandle; IF handle=NIL THEN RETURN; Report["Disconnecting . . ."]; FinchSmarts.UninitFinchSmarts[]; Report[IF handle.finchActive THEN "Could not disconnect" ELSE "Finch is disconnected", " from telephone server"]; }; CheckActive: PUBLIC PROC[handle: Handle] RETURNS[active: BOOL_FALSE] = TRUSTED { IF handle#NIL AND handle.finchActive THEN RETURN[TRUE]; Status["Finch not connected to telephone system"]; }; ReportConversationState: PROC[ nb: NB, cDesc: ConvDesc, remark: Rope.ROPE ] = TRUSTED { s: IO.STREAM; difficulty: BOOL_FALSE; button: Viewer; state: Thrush.StateInConv; intervalSpec: Thrush.IntervalSpec; SELECT nb FROM -- should possibly interpret more success => NULL; ENDCASE => { Status[remark]; RETURN; }; IF finchToolHandle.conversations=NIL OR finchToolHandle.conversations.destroyed THEN BuildConversationsWindow[]; IF cDesc = NIL THEN RETURN; -- not much more can be done. button _ NARROW[cDesc.clientData]; IF button = NIL THEN button _ AddConvDesc[finchToolHandle.conversations, cDesc]; s _ IO.ROS[]; state_cDesc.cState.state; SELECT state FROM active => { cDesc.completed _ TRUE; cDesc.failed_FALSE; }; reserved, parsing => { cDesc.originator _ us; cDesc.completed _ FALSE; cDesc.conference _ FALSE; SELECT cDesc.cState.reason FROM busy, error, noCircuits => difficulty_TRUE; ENDCASE; }; ENDCASE => cDesc.failed_FALSE; IF (state#reserved AND state#parsing) OR difficulty THEN s.PutF["Call %s %s at %t", rope[SELECT cDesc.originator FROM us => "to", them=>"from", ENDCASE=>"to/from"], rope[cDesc.otherPartyDesc], time[cDesc.startTime] ]; IF difficulty OR state=idle THEN { s.PutF["%s%s, duration = %r", rope[IF difficulty THEN " was not successful" ELSE IF cDesc.completed THEN printLabel[state] ELSE " was abandoned"], rope[IF ~difficulty THEN "" ELSE SELECT cDesc.cState.reason FROM busy => " -- busy", error => " -- connection failed", noCircuits => " -- no circuits", ENDCASE => " for some reason" ], int[BasicTime.Period[cDesc.startTime, BasicTime.Now[]]] ]; } ELSE s.PutRope[printLabel[state]]; IF cDesc.cState.comment#NIL THEN s.PutF[" (%s)", rope[cDesc.cState.comment]]; IF remark#NIL THEN s.PutF[" [%s]", rope[remark]]; IF ~cDesc.failed THEN Buttons.ReLabel[ button: button, paint: TRUE, newName: s.RopeFromROS[] ]; IF difficulty THEN cDesc.failed_TRUE; [] _ SelectEntryInConversations[button, state#idle]; FOR intervals: LIST OF Thrush.IntervalSpec _ cDesc.newIntervals, intervals.rest WHILE intervals#NIL DO intervalSpec _ intervals.first; IF intervalSpec.type=request THEN LOOP; SELECT intervalSpec.direction FROM record => SELECT intervalSpec.type FROM started => Status["Please begin speaking"]; finished => Status["Thank you"]; ENDCASE => Status[NIL]; play => Report[ SELECT intervalSpec.type FROM started => "Playing message", finished => "End of message", ENDCASE => NIL]; ENDCASE; ENDLOOP; }; ParseCallee: PROC[fullCallee: ROPE] RETURNS [callee: ROPE, residence: BOOL_TRUE] = { endCallee: INT; callee_fullCallee; IF fullCallee.Equal["home", FALSE] THEN callee_Names.CurrentRName[] ELSE IF (endCallee_fullCallee.Find[" at home", 0, FALSE]) >= 0 THEN callee_fullCallee.Substr[len: endCallee] ELSE residence_FALSE; }; HangItUp: PROC[complain: BOOL] = TRUSTED { cDesc: ConvDesc = GetSelectedDesc[]; IF cDesc=NIL OR cDesc.cState.state=idle THEN {Report[" No conversation to leave"]; RETURN}; FinchSmarts.DisconnectCall[convID: cDesc.cState.credentials.convID]; }; GetSelectedDesc: PROC RETURNS [cDesc: ConvDesc_NIL] = { viewer: Viewer = finchToolHandle.conversations; selected: Viewer _ IF viewer=NIL THEN NIL ELSE NARROW[ViewerOps.FetchProp[viewer, $selectedEntry]]; IF ~CheckActive[finchToolHandle] OR selected = NIL THEN RETURN; cDesc _ NARROW[ViewerOps.FetchProp[selected, $convDesc]]; }; ButtonStartFinch: Buttons.ButtonProc = { StartFinch[]; }; ButtonStopFinch: Buttons.ButtonProc = { StopFinch[]; }; ReDial: Buttons.ButtonProc = { HangItUp[TRUE]; Process.Pause[Process.SecondsToTicks[4]]; --maybe a status check wait loop here ?? CallProc[parent: parent, clientData: clientData, mouseButton: mouseButton, shift: shift, control: control]; }; CallProc: Buttons.ButtonProc = { callee: Rope.ROPE; residence: BOOL; callee _ ViewerTools.GetContents[finchToolHandle.calledPartyText]; IF ~CheckActive[finchToolHandle] OR callee = NIL THEN RETURN; Status["Placing call to ", callee]; [callee, residence] _ ParseCallee[callee]; FinchTool.CallByDescription[description: callee, residence: residence]; -- Launch the call. }; CallSelectedProc: Buttons.ButtonProc = { callee: Rope.ROPE; residence: BOOL; callee _ ViewerTools.GetSelectionContents[]; ViewerTools.SetContents[finchToolHandle.calledPartyText, callee]; IF ~CheckActive[finchToolHandle] OR callee = NIL THEN RETURN; Status["Placing call to ", callee]; [callee, residence] _ ParseCallee[callee]; FinchTool.CallByDescription[description: callee, residence: residence]; -- Launch the call. }; SetCalledPartyProc: Buttons.ButtonProc = { ViewerTools.SetSelection[finchToolHandle.calledPartyText, NIL]; }; ConversationMgmtProc: Buttons.ButtonProc = { viewer: Viewer = NARROW[parent]; []_ SelectEntryInConversations[viewer]; IF control THEN Hangup[parent: viewer.parent, mouseButton: mouseButton] ELSE IF mouseButton#red THEN Answer[parent: viewer.parent, mouseButton: mouseButton]; }; Hangup: PUBLIC Menus.MenuProc = { HangItUp[TRUE]; }; HangupQuietly: PUBLIC Menus.MenuProc = { HangItUp[FALSE]; }; Answer: Menus.MenuProc = TRUSTED { viewer: Viewer = finchToolHandle.conversations; selected: Viewer _ IF viewer=NIL THEN NIL ELSE NARROW[ViewerOps.FetchProp[viewer, $selectedEntry]]; cDesc: ConvDesc = GetSelectedDesc[]; IF cDesc = NIL OR (SELECT cDesc.cState.state FROM pending, ringing =>FALSE, ENDCASE=>TRUE) THEN { Report[" No conversation to join"]; RETURN; }; FinchSmarts.AnswerCall[convID: cDesc.cState.credentials.convID]; }; MakeDirectory: Menus.MenuProc = { directoryFiles: Rope.ROPE; directoryFilesList: LIST OF Rope.ROPE; defaultFile: Rope.ROPE="User.TDir"; IF finchToolHandle.directory#NIL THEN ViewerOps.DestroyViewer[finchToolHandle.directory!ANY=>CONTINUE]; directoryFiles _ UserProfile.Token[key: "FinchTelephoneDirectory", default: defaultFile]; { nextDirectoryFile: Rope.ROPE; stream: IO.STREAM = IO.RIS[directoryFiles]; UNTIL IO.EndOf[stream] DO nextDirectoryFile _ IO.GetLineRope[stream]; IF NOT Rope.Equal[nextDirectoryFile,""] THEN directoryFilesList _ CONS[nextDirectoryFile, directoryFilesList]; ENDLOOP; }; finchToolHandle.directory _ FinchTool.BuildDirectoryDisplayer[ directoryFiles: directoryFilesList, viewerName: IF directoryFilesList.first=defaultFile THEN "Personal" ELSE directoryFilesList.first ]; FinchTool.DisplayDirectoryInViewer[ directoryFiles: directoryFilesList, msViewer: finchToolHandle.directory ]; }; MakeFinchToolCmd: Commander.CommandProc = { MakeFinchTool[]; }; FinchCmd: Commander.CommandProc = { StartFinch[]; }; UnfinchCmd: Commander.CommandProc = { StopFinch[]; }; CallCmd: Commander.CommandProc = { callee: Rope.ROPE _ cmd.commandLine.Substr[start: 1, len: cmd.commandLine.Length[]-2]; residence: BOOL; cmdHandle _ cmd; IF CheckActive[finchToolHandle] AND callee # NIL AND callee.Length[]#0 THEN { Status["Placing call to ", callee]; ViewerTools.SetContents[finchToolHandle.calledPartyText, callee]; [callee, residence] _ ParseCallee[callee]; FinchTool.CallByDescription[description: callee, residence: residence]; -- Launch the call. }; cmdHandle _ NIL; }; CallHomeCmd: Commander.CommandProc = { cmd.commandLine _ " home\n"; [result, msg] _ CallCmd[cmd]; }; UnfinchOnDestroy: ViewerEvents.EventProc = { IF finchToolHandle=NIL THEN RETURN; StopFinch[]; IF finchToolHandle.directory#NIL THEN { ViewerOps.DestroyViewer[finchToolHandle.directory]; finchToolHandle.directory_NIL; }; IF finchToolHandle.tsIn#NIL THEN finchToolHandle.tsIn.Close[]; IF finchToolHandle.tsOut#NIL THEN finchToolHandle.tsOut.Close[]; finchToolHandle _ NIL; }; UnFinchOnCheckpoint: Booting.CheckpointProc = { IF finchToolHandle=NIL THEN RETURN; finchToolHandle.finchActiveAtCheckpoint _ finchToolHandle.finchActive; IF finchToolHandle.finchActive THEN StopFinch[]; }; ReFinchOnRollback: Booting.RollbackProc = { IF finchToolHandle=NIL OR ~finchToolHandle.finchActiveAtCheckpoint THEN RETURN; StartFinch[]; }; SetOpenHeight: PROC[viewer: ViewerClasses.Viewer, clientHeight: INTEGER] = { LockedSetHeight: PROC = {ViewerOps.SetOpenHeight[viewer, clientHeight]}; ViewerLocks.CallUnderWriteLock[LockedSetHeight, viewer]; }; FirstLabel: PROC[name: ROPE, parent: Viewer] RETURNS [nV: Viewer] = { IF ~FinchTool.CheckAborted[parent] THEN RETURN; nV_ Labels.Create[ info: [name: name, parent: parent, wh: entryHeight, wy: 1, wx: IF parent.scrollable THEN 0 ELSE xFudge, border: FALSE]]; }; ImmediateButton: PUBLIC PROC[name: ROPE, proc: Buttons.ButtonProc, border: BOOL, sib: Viewer, fork: BOOL_ TRUE, guarded: BOOL_ FALSE, newLine: BOOL_ FALSE] RETURNS[Viewer] = { info: ViewerClasses.ViewerRec_ [name: name, wy: sib.wy, wh: entryHeight, parent: sib.parent, border: border]; IF newLine THEN -- first button on new line { info.wy_ sib.wy + sib.wh + (IF border THEN 1 ELSE 0); -- extra bit info.wx_ IF sib.parent.scrollable THEN 0 ELSE xFudge; } ELSE -- next button right on same line as previous info.wx_ sib.wx+sib.ww+xFudge; RETURN[Buttons.Create[info: info, proc: proc, fork: fork, guarded: guarded]]; }; QueuedButton: PUBLIC PROC[name: ROPE, proc: Buttons.ButtonProc, border: BOOL, sib: Viewer, guarded: BOOL_ FALSE, newLine: BOOL_ FALSE] RETURNS[Viewer] = { info: ViewerClasses.ViewerRec_ [name: name, wy: sib.wy, wh: entryHeight, parent: sib.parent, border: border]; IF newLine THEN -- first button on new line { info.wy_ sib.wy + sib.wh + (IF border THEN 1 ELSE 0); -- extra bit info.wx_ IF sib.parent.scrollable THEN 0 ELSE xFudge; } ELSE -- next button right on same line as previous info.wx_ sib.wx+sib.ww+xFudge; RETURN[MBQueue.CreateButton[q: finchQueue, info: info, proc: proc, guarded: guarded]]; }; -- sib is a viewer to the left or above the button to be made AnotherButton: PUBLIC PROC[ q: MBQueue.Queue, name: ROPE, proc: Buttons.ButtonProc, sib: Viewer, data: REF ANY_ NIL, border: BOOL_ FALSE, width: INTEGER_ 0, guarded: BOOL_ FALSE, font: VFonts.Font _ VFonts.defaultFont, newLine: BOOL_ FALSE] RETURNS [nV: Viewer] = { info: ViewerClasses.ViewerRec_ [name: name, wy: sib.wy, ww: width, wh: entryHeight, parent: sib.parent, border: border]; IF ~CheckAborted[sib] THEN RETURN; IF newLine THEN { -- first button on new line info.wy_ sib.wy + sib.wh + (IF border THEN 1 ELSE 0); -- extra bit info.wx_ IF sib.parent.scrollable THEN 0 ELSE xFudge; } ELSE -- next button right on same line as previous info.wx_ sib.wx+sib.ww+xFudge; RETURN[MBQueue.CreateButton[ q: q, info: info, proc: proc, clientData: data, font: font, guarded: guarded]] }; FirstButton: PUBLIC PROC[ q: MBQueue.Queue, name: ROPE, proc: Buttons.ButtonProc, parent: Viewer, data: REF ANY_ NIL, border: BOOL_ FALSE, width: INTEGER_ 0, guarded: BOOL_ FALSE, font: VFonts.Font _ VFonts.defaultFont] RETURNS [nV: Viewer] = { info: ViewerClasses.ViewerRec_ [name: name, parent: parent, wh: entryHeight, wy: 1, ww: width, wx: IF parent.scrollable THEN 0 ELSE xFudge, border: border]; IF ~CheckAborted[parent] THEN RETURN; nV_ MBQueue.CreateButton[ q: q, info: info, proc: proc, clientData: data, font: font, guarded: guarded]; }; CheckAborted: PUBLIC PROC[sib: Viewer] RETURNS[ok: BOOL] = { IF sib = NIL THEN RETURN[TRUE]; IF sib.destroyed THEN RETURN[FALSE]; RETURN[TRUE]; }; NextRightTextViewer: PUBLIC PROC[sib: Viewer, w: INTEGER] RETURNS [nV: Viewer] = { IF ~FinchTool.CheckAborted[sib] THEN RETURN; nV_ ViewerTools.MakeNewTextViewer[ info: [parent: sib.parent, wx: sib.wx+sib.ww+xFudge, wy: sib.wy, ww: w, wh: entryHeight, border: FALSE]]; }; MakeRuler: PROC[sib: Viewer, h: INTEGER_ 1] RETURNS [r: Viewer] = { IF ~FinchTool.CheckAborted[sib] THEN RETURN; r_ Rules.Create[ info: [parent: sib.parent, wy: sib.wy+sib.wh+1, ww: sib.parent.ww, wh: h]]; Containers.ChildXBound[sib.parent, r]; }; MakeTypescript: PROC[sib: Viewer] RETURNS [ts: Viewer] = { y: INTEGER_ sib.wy+sib.wh+xFudge; IF ~CheckAborted[sib] THEN RETURN; ts_ TypeScript.Create[ info: [parent: sib.parent, ww: sib.cw, wy: y, wh: sib.parent.ch - y, border: FALSE] ]; Containers.ChildYBound[sib.parent, ts]; Containers.ChildXBound[sib.parent, ts]; }; Report: PUBLIC PROC[msg1, msg2, msg3, msg4: ROPE_NIL] = { Log.Report[where: $Finch, remark: IO.PutFR["%s%s%s%s", rope[msg1], rope[msg2], rope[msg3], rope[msg4]]]; }; ReportRope: PUBLIC PROC[msg1: ROPE] = { IF msg1#NIL THEN Log.Report[where: $Finch, remark: msg1]; }; Status: PUBLIC PROC[msg1, msg2, msg3, msg4: ROPE_NIL] = { what: ROPE = Rope.Cat[msg1, msg2, msg3, msg4]; ReportRope[what]; }; FinchWhereProc: Log.WhereProc = { RETURN[IF cmdHandle#NIL THEN cmdHandle.out ELSE IF finchToolHandle=NIL THEN NIL ELSE finchToolHandle.tsOut]; }; FinchWhereCmdProc: Log.WhereProc = { RETURN[NIL]; }; Commander.Register[key: "FinchTool", proc: MakeFinchToolCmd, doc: "Create a Finch viewers tool" ]; Commander.Register["Finch", FinchCmd, "Start Finch (connect to server)"]; Commander.Register["Unfinch", UnfinchCmd, "Stop Finch (disconnect server)"]; -- initialize text for print form of connect state Commander.Register["Phone", CallCmd, "Place telephone call to specified party"]; Commander.Register["ET", CallHomeCmd, "Phonnne hommmmmmme"]; Log.RegisterWhereToReport[proc: FinchWhereProc, where: $Finch]; Log.RegisterWhereToReport[proc: FinchWhereCmdProc, where: $FinchCmd]; printLabel[active] _ " is in progress"; printLabel[idle] _ " is completed"; printLabel[ringing] _ " is ringing"; printLabel[pending] _ NIL; printLabel[initiating] _ NIL; printLabel[maybe] _ " is ringing"; printLabel[reserved] _ " Telephone set is off hook"; printLabel[parsing] _ " Call is being dialed"; TRUSTED { Booting.RegisterProcs[c: UnFinchOnCheckpoint, r: ReFinchOnRollback]; }; }. ͺFinchToolImpl.mesa Last Edited by: Swinehart, May 1, 1984 10:47:37 am PDT Last Edited by: Pier, April 17, 1984 3:51:54 pm PST List USING [Reverse], Types and Typeoids Viewer Construction and Operation Finch Viewer Check how much space is left (if any) in the control window for a typescript. Before connecting to server After connecting to server Conversation window Conversations Viewer Starting and Stopping Conversation Management Don't use conversation buttons for failure reports If idle, elaborate. Now report on any interval changes. <> Button Procs ButtonProc: TYPE = PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] Finch Viewer ButtonProc: TYPE = PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] Conversations Viewer Menu Procs MenuProc: TYPE = PROC [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: MouseButton _ red, shift, control: BOOL _ FALSE] Called whenever the user clicks the hangup button Refers to entries in the viewer finchToolHandle.conversations, not necessarily current one. Called to disconnect a call in progress without logging a message. Called whenever the user clicks the answer button Refers to entries in the viewer finchToolHandle.conversations, not necessarily current one. directoryFilesList _ List.Reverse[directoryFilesList]; --convenience; forget for now Executive Commands Event-Triggered Activities Utilities Viewers and Buttons Creates a text viewer, next right on the same line as sib sib must be a Viewer, not NIL Make an h-bit wide line after sib Sib is sibling to create TS after Reporting and Logging IF finchToolHandle#NIL AND finchToolHandle.finchActive THEN Labels.Set[finchToolHandle.status, what]; Registration, Initialization Register a command with the UserExec that will create an instance of this tool Κά– "Cedar" style˜J– "Cedar" stylešœ™J™6J™3J˜šΟk ˜ J˜%J˜>Jšœœ1˜>Jšœ œ!˜0Jšœ œ1˜Ašœ œ˜JšœK˜K—Jšœ ˜ Jšœœ˜J˜Jšœœ˜Jšœœ ™Jšœœ2˜?Jšœœ0˜;Jšœœ˜Jšœœœ ˜6Jšœœ ˜Jšœœ.˜7Jšœœ˜'Jšœœe˜qJšœ œ ˜Jšœ œ ˜J˜#Jšœœ'˜:Jšœ œ"˜4Jšœ œ˜'Jšœ œ˜)Jšœ œg˜vJ˜BJšœ œS˜dJšœ˜J˜—šœ˜(Jšœτ˜ϋJšœ˜Jšœ˜J˜—™J™˜5J˜;—Jšœ œ˜&Jšœœ œ˜Jšœœ˜Jšœœœ˜˜$J˜0—J˜Jšœœ˜Jšœœ˜ J˜Jš œ œœœœ˜MJšœ œ"˜4J˜#J˜J˜J˜J˜J˜Jšœœ˜Jšœ œ˜J˜—™!J™™ J˜šΟn œœ˜Jšœ œ˜Jšœ œ˜J˜ J˜ J˜ šœœœ˜Jšœ3œ˜=—Jšœœ˜$J˜J˜ šœ+Πac˜=JšœΟc ˜/Jšœœ 8˜IJšœ ˜0Jšœ ˜3Jšœ(  ˜5Jšœ œœ 'œ˜C—J˜ Jšœ œœœ˜^šœ"˜"Jšœ;œ˜A—˜J˜hJ˜ —˜0Jšœœ(œ˜:—J˜WJ˜@J˜3J˜JšœM™MJ˜ šœœ˜!J˜ Jšœ œ#˜4J˜—J˜'Jšœ1œ˜DJšœ" ˜9Jšœ˜—J˜šž œœœ˜™J˜$J˜šœ˜Jšœ˜Jšœ\˜\Jšœ˜—šœ˜Jšœ˜JšœW˜WJšœ˜—J˜—™Jšœ˜šœ˜Jšœ˜JšœX˜XJšœ˜—šœ˜Jšœ˜JšœM˜MJšœ˜—šœ˜Jšœ˜JšœZ˜ZJšœ˜—šœ˜Jšœ˜JšœO˜OJšœ˜—šœ˜Jšœ˜Jšœ`˜`Jšœ˜—šœ˜Jšœ˜JšœN˜NJšœ˜—šœ˜Jšœ˜JšœR˜RJšœ˜——J™Jšœ#˜#šœ˜Jšœ˜JšœM˜MJšœ˜—šœ˜Jšœ˜JšœQ˜QJšœ˜—Jšœ˜——J˜™J™šžœœ˜"šœœœ)˜QJšœ˜ —šœ7˜7Jšœœ˜ Jšœ@˜@Jšœ'˜'Jšœœ#œ˜7—Jšœ2˜2Jšœ:˜:Jšœ˜J˜—šž œœ#œ˜NJšœ˜Jšœœ˜Jšœ œ˜J˜šž œœ˜šœœ˜šœ˜Jšœœ>˜V——šœ˜JšœœEœ˜c—J˜J˜—Jšœ œ,˜?šœ œ˜Jšœ œ,˜=Jšœ˜—Jš œ œœœœ˜[Jšœ œ œ6˜WJšœ˜Jšœ*˜*Jšœ˜Jšœ4˜4J˜J˜—š žœœ œœœœ˜jJšœœ:˜WJš œœ œœœ˜=šœœœœ˜8J˜5J˜)J˜—Jšœœ/˜LJšœœ œ7œ˜Qšœ˜Jšœ4 ˜JJšœ(˜(JšœC˜CJ˜—Jšœœ˜J˜J˜———™J™šžœœœœ˜-Jšœœœœ˜#J˜!Jšœ<œœ˜JJšœ;˜;šœ(˜(Jšœœ œ˜C—Jšœ3˜3Jšœœ˜>šœ˜šœœœ˜+Jšœ/œ˜5Jšœ7˜7J˜—Jšœ œ˜$J˜—J˜—J˜šž œ œœ˜%J˜!šœœœ˜J˜J˜Jš œœœœ ˜)Jšœ˜—Jšœ˜šœ˜Jšœ,˜,—šœœœœ˜PJšœ˜—Jšœ˜—J˜šž œ œœ˜$J˜!Jšœœœœ˜Jšœ˜Jšœ ˜ šœœœœ˜VJšœ˜—J˜J˜—š ž œ œœ œœœ˜PJš œœœœœœ˜7J˜2J˜—J˜—™J™š žœœœ œœ˜WJšœœœ˜ Jšœ œœ˜J˜J˜J˜"šœœ !˜0Jšœ œ˜šœœ˜'Jšœ2™2——šœœœ(˜OJšœ˜ —Jš œ œœœ ˜9Jšœœ˜"Jšœ œœ<˜PJšœœœ˜ J˜šœ˜Jšœœœ˜:šœ˜Jšœ˜Jšœœ˜Jšœœ˜Jšœœ'œœ˜TJ˜—Jšœœ˜—šœœœ œ˜SJšœœœœ ˜PJšœ˜Jšœ˜J˜—J™šœ œ œ˜"˜šœœ œ˜-Jšœœœœ˜F—šœœ œ˜šœœ˜$J˜J˜!J˜ Jšœ˜Jšœ˜——J˜7J˜—J˜—Jšœ˜"J˜MJ˜1šœ>œ˜CJšœ˜—Jšœ œœ˜%Jšœ4˜4J™Ušœ œœ9˜Ošœ œ˜J˜Jšœœœ˜'šœ˜"˜ šœ˜Jšœ+˜+Jšœ ˜ Jšœ œ˜——˜šœ˜J˜J˜Jšœœ˜——Jšœ˜——Jšœ˜—J˜J˜—šž œœ œœ œ œœ˜TJšœ œ˜J˜Jšœœœ˜Cšœœ+œ˜CJšœ(˜(—Jšœ œ˜J˜J˜—šžœœ œœ˜*J˜$Jš œœœœ'œ˜[JšœD˜DJšœ˜J˜—šžœœœœ˜7Jšœ/˜/šœœœœ˜)Jšœœ.˜9—Jš œœ œœœ˜?Jšœœ+˜9Jšœ˜—J˜—J™ šœ œœ œœœœœ™DJšœ0œœ™=J™™ J˜šžœ*˜:J˜—šžœ)˜8J™—šžœ˜šœ œœ œœœœœ™DJšœ0œœ™=—Jšœ œ˜Jšœ* (˜RJ•StartOfExpansionˆ[parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE]˜kJšœ˜—J˜šžœ˜ Jšœ œ˜Jšœ œ˜JšœB˜BJš œœ œœœ˜=J˜#Jšœ*˜*JšœH ˜[J˜J˜—šžœ˜(Jšœ œ˜Jšœ œ˜Jšœ,˜,JšœA˜AJš œœ œœœ˜=J˜#Jšœ*˜*JšœH ˜[J˜J˜—JšžœSœ˜mJ™—™J™šžœ˜,Jšœœ ˜ J˜'Jšœ œ8˜GJšœœœ9˜UJšœ˜—J™——J™ šœ œœ œœœœœ™BJšœ0œœ™=J˜šžœœœ˜4Jšœ1™1Jšœ[™[—J˜šž œœœ˜Jšœ#˜#Jšœ œ&œ œ˜aJ˜—šœ$˜$JšœJ˜J—Jšœ˜J˜——™J˜Jš‘œ/˜?J˜Jšžœ,˜4J˜Jšž œ+˜5J˜šžœ˜"Jšœ œE˜VJšœ œ˜J˜š œœ œœœ˜MJ˜#J˜AJšœ*˜*JšœH ˜[J˜—Jšœ œ˜J˜J˜—šž œ˜&Jšœ˜Jšœ˜J˜—J˜—™J˜šžœ˜,Jšœœœœ˜#Jšœ ˜ šœœœ˜'Jšœ3˜3Jšœœ˜J˜—Jšœœœ˜>Jšœœœ˜@Jšœœ˜Jšœ˜—šžœ˜/Jšœœœ˜#JšœF˜FJšœœ ˜0J˜—šžœ˜+Jš œœœ*œœ˜OJšœ ˜ J˜—J˜—™ ™J™šž œœ-œ˜LJšžœœ3˜HJšœ8˜8Jšœ˜—J˜šž œœœœ˜EJšœ!œœ˜/šœ˜Jšœ:˜:Jš œœœœœ˜=—J˜—J˜š žœœœœ$œ˜PJš œœœ œœ œœ˜Jšœ ˜JšœH˜HJšœ$˜$Jšœ œ ˜-Jšœœœœ  ˜DJšœ œœœ˜5Jšœ˜—šœ .˜3Jšœ˜—JšœG˜MJšœ˜—J˜š ž œœœœ$œ˜MJš œœœ œœ˜8šœ ˜JšœH˜HJšœ$˜$Jšœ œ ˜-Jšœœœœ  ˜DJšœ œœœ˜5Jšœ˜—šœ .˜3Jšœ˜—JšœP˜VJšœ˜—J˜Jš >˜>šž œ œ˜Jšœœ(˜DJš œœœœ œœ œ˜;Jš œ œœ3œœ˜SJšœ˜—JšœU˜Ušœ&˜&Jšœœœ˜"šœ œ ˜-Jšœœœœ  ˜BJšœ œœœ˜5Jšœ˜—šœ -˜2Jšœ˜—šœ˜JšœN˜N—Jšœ˜—J˜šž œ œ˜Jšœœ˜7Jš œœœœ œœ œ˜KJšœ œœ)˜=Jšœ˜šœ˜Jšœ?˜?Jšœœœœ˜>—Jšœœœ˜%šœ˜JšœN˜N—Jšœ˜J˜—š ž œœœœœ˜