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 ], 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 [GetContents, MakeNewTextViewer, SetContents, SetSelection ] ; FinchToolImpl: CEDAR PROGRAM IMPORTS BasicTime, Booting, Buttons, Commander, Containers, FinchSmarts, FinchTool, IO, Labels, MBQueue, Menus, Names, Rope, Rules, Log, 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, "Disconnect", Hangup, finchToolHandle ] ]; Menus.AppendMenuEntry[ menu: finchMenu, entry: MBQueue.CreateMenuEntry[finchQueue, "Directory", MakeDirectory, 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[]; }; 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. }; 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 = { directoryName: Rope.ROPE; defaultName: Rope.ROPE="User.TDir"; IF finchToolHandle.directory#NIL THEN ViewerOps.DestroyViewer[finchToolHandle.directory]; directoryName _ UserProfile.Token[key: "FinchTelephoneDirectory", default: defaultName]; finchToolHandle.directory _ FinchTool.BuildDirectoryDisplayer[ directoryName: directoryName, viewerName: IF directoryName=defaultName THEN "Personal" ELSE directoryName ]; FinchTool.DisplayDirectoryInViewer[ directoryName: directoryName, 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, November 27, 1983 3:28 pm 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 Finch Viewer Conversations Viewer Menu Procs 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. << Be prepared to catch a "doesn't exist" in the above, and to ignore it. >> Executive Commands ENABLE ANY => GOTO Quit; << When you know what comes up, deal directly with it. >> 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 Κ5– "Cedar" style˜J– "Cedar" stylešœ™J™4J˜šΟk ˜ J˜%J˜>Jšœœ1˜>Jšœ œ!˜0Jšœ œ1˜Ašœ œ˜JšœK˜K—Jšœ ˜ Jšœœ˜J˜Jšœœ˜Jšœœ2˜?Jšœœ0˜;Jšœœ˜Jšœœœ ˜6Jšœœ ˜Jšœœ.˜7Jšœœe˜qJšœ œ ˜Jšœ œ ˜J˜#Jšœœ'˜:Jšœ œ"˜4Jšœ œ˜'Jšœ œ˜)Jšœ œg˜vJ˜BJšœ œ=˜NJšœ˜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šœT˜TJšœ˜—šœ˜Jšœ˜JšœX˜XJšœ˜——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™™ J˜šžœ*˜:J˜—šžœ)˜8J™—šžœ˜ Jšœ œ˜Jšœ œ˜JšœB˜BJš œœ œœœ˜=J˜#Jšœ*˜*JšœH ˜[J˜J˜—JšžœSœ˜mJ™—™J™šžœ˜,Jšœœ ˜ J˜'Jšœ œ8˜GJšœœœ9˜UJšœ˜—J™——™ J˜šžœœœ˜4Jšœ1™1Jšœ[™[—J˜šž œœœ˜Jšœ˜Jšœ œœ œ˜KJ˜—šœ$˜$JšœD˜D—Jšœ˜J˜——™J˜JšΟbœ/˜?J˜Jšžœ,˜4J˜Jšž œ+˜5J˜šžœ˜$Jšœœœ@™RJšœ œE˜VJšœ œ˜J˜š œœ œœœ˜MJ˜#J˜AJšœ*˜*JšœH ˜[J˜—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˜—š ž œœœœœ˜