DIRECTORY AIS, Atom USING [GetPName], BasicTime USING [nullGMT, GMT, Now, Period], Buttons USING [Button, ButtonProc, Create, SetDisplayStyle], Commander USING [CommandProc, Handle, Register], Containers USING [Container, Create], Convert USING [RopeFromTime], FingerOps, FS USING [Error, ExpandName, FileInfo], Icons USING [IconFlavor, NewIconFromFile], Imager, ImagerBackdoor, ImagerColorOperator, ImagerTransformation, ImagerPixelArray, ImagerPixelArrayDefs, ImagerFont, IO, MBQueue, MessageWindow, Process USING[Detach], ThisMachine, Rope, UserCredentials USING [Get], UserProfile, VFonts, ViewerClasses, ViewerEvents, ViewerOps, ViewerTools, WalnutRegistry; FingerTool: CEDAR PROGRAM IMPORTS AIS, Atom, BasicTime, Buttons, Commander, Containers, Convert, FingerOps, FS, Icons, Imager, ImagerBackdoor, ImagerColorOperator, ImagerPixelArray, ImagerTransformation, IO, MBQueue, MessageWindow, Process, ThisMachine, Rope, UserCredentials, UserProfile, ViewerEvents, VFonts, ViewerOps, ViewerTools, WalnutRegistry = BEGIN FingerHandle: TYPE = REF FingerToolRec; -- a REF to the data for a particular instance of the finger tool; multiple instances can be created. FingerToolRec: TYPE = RECORD -- the data for a particular tool instance [outer: Containers.Container _ NIL, -- handle for the enclosing container height: CARDINAL _ 0, -- height measured from the top of the container performFingerButton: Buttons.Button, -- button clicked to perform finger operation storeChangesButton: Buttons.Button, -- button clicked to make changes to finger data talkButton: Buttons.Button, -- button to connect to talk program performHostButton, storeHostButton: Buttons.Button, name, pictureFileName, plan, actions, nickName, mailRead, host, owner, location, network, gateway, lastchange: SelectionData _ NEW[SelectionRecord], pictureViewer: ViewerClasses.Viewer]; SelectionData: TYPE = REF SelectionRecord; SelectionRecord: TYPE = RECORD [parentHandle: FingerHandle, selectable: BOOL, button: Buttons.Button, result: ViewerClasses.Viewer, registration: ViewerEvents.EventRegistration _ NIL]; PictureState: TYPE = REF PictureStateRecord; PictureStateRecord: TYPE = RECORD [rectangle: ImagerTransformation.Rectangle, pa: ImagerPixelArrayDefs.PixelArray, colorOperator: Imager.ColorOperator]; fingerIcon: Icons.IconFlavor = Icons.NewIconFromFile["Finger.icon", 0]; fingerQueue: MBQueue.Queue = MBQueue.Create[]; mailProp: FingerOps.PropPair _ NEW[FingerOps.PropPairObject _ [prop:$MailRead, val: NIL]]; fingerToolProps: LIST OF ATOM = LIST[$MailRead, $NickName, $Plan, $AISFile]; RecordMailRead: WalnutRegistry.EventProc = { IF event = mailRead THEN { mailProp.val _ TimeRope[BasicTime.Now[]]; FingerOps.SetUserProps[user: UserCredentials.Get[].name, props: LIST[ mailProp] ! FingerOps.FingerError => CONTINUE ] } }; MakeFingerTool: Commander.CommandProc = BEGIN fingerHandle: FingerHandle = NEW[FingerToolRec]; fingerHandle.outer _ Containers.Create -- construct the outer container [[name: "Finger Tool", -- name displayed in the caption icon: fingerIcon, iconic: TRUE, -- so tool will be iconic when first created column: left, -- initially in the left column scrollable: FALSE]]; -- don't allow user to scroll contents fingerHandle.height _ 0; SetupViewer[fingerHandle]; ViewerOps.SetOpenHeight[fingerHandle.outer, fingerHandle.height]; -- hint our desired height ViewerOps.PaintViewer[fingerHandle.outer, all]; -- reflect above change END; pictureColumnWidth: CARDINAL = 192; pictureHeight: CARDINAL = 192; entryVSpace: CARDINAL = 8; -- vertical leading space between sections entryHeight: CARDINAL = 15; -- how tall to make each line of items horizSpace: CARDINAL = 10; -- space between items on the same line firstColumnIndent: CARDINAL = 1; -- measured from left margin firstColumnWidth: CARDINAL = 110; secondColumnWidth: CARDINAL = 240; pictureFont: ImagerFont.Font = VFonts.EstablishFont["Helvetica", 12, TRUE]; SetupViewer: PROC [fingerHandle: FingerHandle] = BEGIN SetupButtons: PROC [name: Rope.ROPE, which: SelectionData, scroll: BOOL _ FALSE, height: CARDINAL _ entryHeight, edit: BOOL _ FALSE] = BEGIN which.button _ Buttons.Create [info: [name: name, wx: firstColumnIndent, -- keep the buttons aligned wy: fingerHandle.height, ww: firstColumnWidth, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: fingerHandle.outer, border: FALSE ], proc: SelectionButtonClicked, clientData: which]; -- this will be passed to our button proc which.parentHandle _ fingerHandle; which.selectable _ edit; Buttons.SetDisplayStyle[which.button, $WhiteOnBlack]; which.result _ ViewerOps.CreateViewer [flavor: $Text, info: [wx: firstColumnIndent + firstColumnWidth + horizSpace, -- keep the buttons aligned wy: fingerHandle.height, ww: secondColumnWidth, wh: height, scrollable: scroll, parent: fingerHandle.outer, border: FALSE]]; IF NOT edit THEN ViewerTools.InhibitUserEdits[which.result]; fingerHandle.height _ fingerHandle.height + height; -- maintain total height END; fingerHandle.height _ fingerHandle.height + entryVSpace; -- space down from the top of the viewer fingerHandle.performFingerButton _ Buttons.Create -- button for performing finger [info: [name: "perform finger", wx: 0, wy: fingerHandle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: fingerHandle.outer, border: TRUE], clientData: fingerHandle, -- this will be passed to our button proc proc: PerformFingerAtTool]; fingerHandle.storeChangesButton _ Buttons.Create -- button for making changes to finger data [info: [name: "store changes", wx: fingerHandle.performFingerButton.wx + fingerHandle.performFingerButton.ww + horizSpace, wy: fingerHandle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: fingerHandle.outer, border: TRUE], clientData: fingerHandle, -- this will be passed to our button proc proc: StoreChanges]; fingerHandle.height _ fingerHandle.height + entryHeight + entryVSpace; -- space down from the action buttons SetupButtons[name: "User Name:", which: fingerHandle.name, edit: TRUE]; fingerHandle.name.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.name.result]; SetupButtons[name: "Nick Name:", which: fingerHandle.nickName, edit: TRUE]; fingerHandle.nickName.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.nickName.result]; SetupButtons[name: "Actions:", which: fingerHandle.actions, scroll: TRUE, height: 3*entryHeight, edit: FALSE]; SetupButtons[name: "Mail Read:", which: fingerHandle.mailRead, scroll: FALSE, height: entryHeight, edit: FALSE]; SetupButtons[name: "Picture Filename:", which: fingerHandle.pictureFileName, edit: TRUE]; fingerHandle.pictureFileName.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.pictureFileName.result]; SetupButtons[name: "Plan:", which: fingerHandle.plan, scroll: TRUE, height: 3*entryHeight, edit: TRUE]; fingerHandle.plan.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.plan.result]; fingerHandle.pictureViewer _ ViewerOps.CreateViewer[flavor: $FingerPicture, info: [wx: firstColumnIndent + firstColumnWidth + horizSpace + secondColumnWidth + horizSpace, wy: fingerHandle.name.result.wy, ww: pictureColumnWidth, wh: pictureHeight, parent: fingerHandle.outer, border: FALSE, scrollable: FALSE], paint: TRUE]; fingerHandle.height _ fingerHandle.height + entryVSpace; -- space down a line fingerHandle.performHostButton _ Buttons.Create [info: [name: "Get Host Information", wx: 0, wy: fingerHandle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: fingerHandle.outer, border: TRUE], clientData: fingerHandle, -- this will be passed to our button proc proc: PerformHost]; fingerHandle.storeHostButton _ Buttons.Create -- button for making changes to finger data [info: [name: "Store Host Data", wx: fingerHandle.performFingerButton.wx + fingerHandle.performHostButton.ww + horizSpace, wy: fingerHandle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: fingerHandle.outer, border: TRUE], clientData: fingerHandle, -- this will be passed to our button proc proc: StoreHost]; fingerHandle.height _ fingerHandle.height + entryHeight + entryVSpace; -- space down from the action buttons SetupButtons[name: "Host:", which: fingerHandle.host, edit: TRUE]; fingerHandle.host.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.host.result]; SetupButtons[name: "Owner:", which: fingerHandle.owner, edit: TRUE]; fingerHandle.owner.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.owner.result]; SetupButtons[name: "Location:", which: fingerHandle.location, edit: TRUE]; fingerHandle.location.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.location.result]; SetupButtons[name: "Gateway:", which: fingerHandle.gateway, edit: TRUE]; fingerHandle.gateway.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.gateway.result]; SetupButtons[name: "Network:", which: fingerHandle.network, edit: TRUE]; fingerHandle.network.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.network.result]; SetupButtons[name: "Last User:", which: fingerHandle.lastchange, scroll: TRUE, height: entryHeight, edit: FALSE]; fingerHandle.height _ fingerHandle.height + entryVSpace; -- space at bottom of finger viewer END; SetNew: ViewerEvents.EventProc = { WHILE viewer.parent # NIL DO viewer _ viewer.parent ENDLOOP; viewer.newVersion _ TRUE; TRUSTED { Process.Detach[FORK ViewerOps.PaintViewer[viewer: viewer, hint: caption]] } }; PaintPicture: ViewerClasses.PaintProc = { state: PictureState = NARROW[self.data]; rect: Imager.Rectangle = ImagerBackdoor.GetBounds[context]; IF state = NIL THEN { Imager.SetColor[context, Imager.white]; Imager.MaskRectangle[context, rect]; Imager.SetColor[context, Imager.black]; Imager.SetXY[context, [3.0, pictureHeight*0.5]]; Imager.SetFont[context, pictureFont]; Imager.ShowRope[context, "No Picture"]; RETURN }; BEGIN scaleX: REAL _ self.ww / state.rectangle.w; scaleY: REAL _ self.wh / state.rectangle.h; scale: REAL _ MIN[scaleX, scaleY]; Imager.ScaleT[context, scale]; Imager.SetSampledColor[context: context, pa: state.pa, m: NIL, colorOperator: state.colorOperator]; Imager.MaskRectangle[context, state.rectangle]; END }; Message: PROC [r: Rope.ROPE] = { MessageWindow.Clear[]; MessageWindow.Append[r]; MessageWindow.Blink[]; }; fingerErrorCode: FingerOps.Reason; PerformHost: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle _ NARROW[clientData]; machine: Rope.ROPE _ ViewerTools.GetContents[fingerHandle.host.result]; lastChange: FingerOps.StateChange; time: BasicTime.GMT; lastUser: Rope.ROPE; IF Rope.Length[machine]=0 THEN { machine _ ThisMachine.Name[$Pup]; ViewerTools.SetContents[fingerHandle.host.result, machine]; }; IF NOT FingerOps.MachineExists[machine] THEN { Message[Rope.Cat[machine," is not a valid host!"]]; ViewerTools.SetContents[fingerHandle.lastchange.result, NIL]; ViewerTools.SetContents[fingerHandle.network.result, NIL]; ViewerTools.SetContents[fingerHandle.owner.result, NIL]; ViewerTools.SetContents[fingerHandle.gateway.result, NIL]; ViewerTools.SetContents[fingerHandle.location.result, NIL]; RETURN; }; [lastChange, time, lastUser] _ FingerOps.GetMachineData[machine]; IF NOT Rope.Equal[lastUser, ""] THEN ViewerTools.SetContents[fingerHandle.lastchange.result, Rope.Cat[IF lastChange = FingerOps.StateChange[login] THEN "Login " ELSE "Logout ", lastUser, " at ",TimeRope[time]]]; FOR p: LIST OF FingerOps.PropPair _ FingerOps.GetMachineProps[machine], p.rest UNTIL p=NIL DO IF p.first.prop=$Network THEN ViewerTools.SetContents[fingerHandle.network.result, p.first.val]; IF p.first.prop=$Owner THEN ViewerTools.SetContents[fingerHandle.owner.result, p.first.val]; IF p.first.prop=$Gateway THEN ViewerTools.SetContents[fingerHandle.gateway.result, p.first.val]; IF p.first.prop=$Location THEN ViewerTools.SetContents[fingerHandle.location.result, p.first.val]; ENDLOOP; fingerHandle.outer.newVersion _ FALSE; ViewerOps.PaintViewer[fingerHandle.outer, caption]; EXITS FingerProblem => { MessageWindow.Append[ message: SELECT fingerErrorCode FROM Aborted => "\n... Transaction Aborted; Retry Finger Operation", Error => "\n... Internal Finger Error; Contact Implementor", Failure => "\n... Connection with server broken; try again later", ENDCASE => NIL, clearFirst: TRUE ]; MessageWindow.Blink[] }; END; StoreHost: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle = NARROW[clientData]; -- get finger viewer data p: LIST OF FingerOps.PropPair _ LIST[ NEW[FingerOps.PropPairObject _ [$Gateway, ViewerTools.GetContents[fingerHandle.gateway.result]]], NEW[FingerOps.PropPairObject _ [$Owner, ViewerTools.GetContents[fingerHandle.owner.result]]], NEW[FingerOps.PropPairObject _ [$Location, ViewerTools.GetContents[fingerHandle.location.result]]], NEW[FingerOps.PropPairObject _ [$Network, ViewerTools.GetContents[fingerHandle.network.result]]]]; FingerOps.SetMachineProps[ViewerTools.GetContents[fingerHandle.host.result], p]; fingerHandle.outer.newVersion _ FALSE; ViewerOps.PaintViewer[fingerHandle.outer, caption]; EXITS FingerProblem => { MessageWindow.Append[ message: SELECT fingerErrorCode FROM Aborted => "\n... Transaction Aborted; Retry Finger Operation", Error => "\n... Internal Finger Error; Contact Implementor", Failure => "\n... Connection with server broken; try again later", ENDCASE => NIL, clearFirst: TRUE ]; MessageWindow.Blink[] }; END; PerformFingerAtTool: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle _ NARROW[clientData]; user: Rope.ROPE _ ViewerTools.GetContents[fingerHandle.name.result]; lastChange: FingerOps.StateChange; time: BasicTime.GMT; now: BasicTime.GMT = BasicTime.Now[]; twoDaysInSeconds: INT = LONG[48] * 60 * 60; muser: Rope.ROPE; action: Rope.ROPE _ " "; IF Rope.Length[user] = 0 THEN { user _ UserCredentials.Get[].name; ViewerTools.SetContents[fingerHandle.name.result, user]; }; IF NOT FingerOps.PersonExists[user] THEN { Message[Rope.Cat[user, " not found in Finger database!"]]; ViewerTools.SetContents[fingerHandle.actions.result, NIL]; ViewerTools.SetContents[fingerHandle.pictureFileName.result, NIL]; SetPicture[fingerHandle.pictureViewer, NIL]; ViewerTools.SetContents[fingerHandle.plan.result, NIL]; ViewerTools.SetContents[fingerHandle.nickName.result, NIL]; ViewerTools.SetContents[fingerHandle.mailRead.result, NIL]; RETURN; }; FOR m: LIST OF Rope.ROPE _ FingerOps.GetUserData[user], m.rest UNTIL m=NIL DO [lastChange, time, muser] _ FingerOps.GetMachineData[m.first]; IF BasicTime.Period[from: time, to: now] > twoDaysInSeconds THEN LOOP; action _ Rope.Cat[action, muser, Rope.Cat[IF lastChange=FingerOps.StateChange[login] THEN " logged in " ELSE " logged out ", m.first], Rope.Cat[" at\n ",TimeRope[time],"\n"]]; ENDLOOP; ViewerTools.SetContents[fingerHandle.actions.result, action]; FOR p: LIST OF FingerOps.PropPair _ FingerOps.GetUserProps[user], p.rest UNTIL p=NIL DO IF p.first.prop=$AISFile THEN { SetPicture[fingerHandle.pictureViewer, IF mouseButton = blue THEN p.first.val ELSE NIL]; ViewerTools.SetContents[fingerHandle.pictureFileName.result, p.first.val]; }; IF p.first.prop=$Plan THEN ViewerTools.SetContents[fingerHandle.plan.result, p.first.val]; IF p.first.prop=$NickName THEN ViewerTools.SetContents[fingerHandle.nickName.result, p.first.val]; IF p.first.prop=$MailRead THEN ViewerTools.SetContents[fingerHandle.mailRead.result, p.first.val]; ENDLOOP; fingerHandle.outer.newVersion _ FALSE; ViewerOps.PaintViewer[fingerHandle.outer, caption]; EXITS FingerProblem => { MessageWindow.Append[ message: SELECT fingerErrorCode FROM Aborted => "\n... Transaction Aborted; Retry Finger Operation", Error => "\n... Internal Finger Error; Contact Implementor", Failure => "\n... Connection with server broken; try again later", ENDCASE => NIL, clearFirst: TRUE ]; MessageWindow.Blink[] }; END; TimeRope: PROC[time: BasicTime.GMT] RETURNS[rope: Rope.ROPE] = { rope _ IF time = BasicTime.nullGMT THEN "unknown" ELSE Convert.RopeFromTime[time] }; SetPicture: PROC[v: ViewerClasses.Viewer, filename: Rope.ROPE] ~ { v.data _ NIL; BEGIN ENABLE FS.Error => CONTINUE; IF Rope.Length[filename] = 0 THEN NULL ELSE IF FS.ExpandName[filename].cp.ext.length # 0 THEN {--suppose it is a single black-and-white AIS file name pa: ImagerPixelArrayDefs.PixelArray _ ImagerPixelArray.FromAIS[filename]; maxSample: ImagerPixelArray.Sample _ ImagerPixelArray.MaxSampleValue[pa, 0]; r: ImagerTransformation.Rectangle _ ImagerTransformation.TransformRectangle[pa.m, [0, 0, pa.sSize, pa.fSize]]; aisFile: AIS.FRef _ AIS.OpenFile[filename]; colorOperator: Imager.ColorOperator _ SELECT aisFile.raster.bitsPerPixel FROM 0 => ImagerColorOperator.BlackColorModel[clear: FALSE], ENDCASE => ImagerColorOperator.GrayLinearColorModel[maxSample, 0, maxSample, NIL]; AIS.CloseFile[aisFile]; v.data _ NEW[PictureStateRecord _ [rectangle: r, pa: pa, colorOperator: colorOperator]]; } ELSE {--suppose it is the stem of the file names for the RGB seperations of a color picture redName: Rope.ROPE _ GetSep[filename, "AISSeparationKeys.red", LIST["-red", "-r"]]; greenName: Rope.ROPE _ GetSep[filename, "AISSeparationKeys.green", LIST["-grn", "-green", "-g"]]; blueName: Rope.ROPE _ GetSep[filename, "AISSeparationKeys.blue", LIST["-blu", "-blue", "-b"]]; pa: ImagerPixelArrayDefs.PixelArray _ ImagerPixelArray.Join3AIS[redName, greenName, blueName]; maxSample: ImagerPixelArray.Sample _ ImagerPixelArray.MaxSampleValue[pa, 0]; r: ImagerTransformation.Rectangle _ ImagerTransformation.TransformRectangle[pa.m, [0, 0, pa.sSize, pa.fSize]]; colorOperator: Imager.ColorOperator _ ImagerColorOperator.RGBLinearColorModel[maxSample]; v.data _ NEW[PictureStateRecord _ [rectangle: r, pa: pa, colorOperator: colorOperator]]; }; END; ViewerOps.PaintViewer[v, all]; }; RopeList: TYPE = LIST OF Rope.ROPE; GetSep: PROC [fileStem, sepKey: Rope.ROPE, sepDefault: RopeList] RETURNS [fileName: Rope.ROPE] = { seps: RopeList _ UserProfile.ListOfTokens[sepKey, sepDefault]; exts: RopeList _ UserProfile.ListOfTokens["AISExtensions", LIST["AIS"]]; sl, el: Rope.ROPE _ NIL; FOR ss: RopeList _ seps, ss.rest WHILE ss # NIL DO IF sl = NIL THEN sl _ ss.first ELSE sl _ sl.Cat[", ", ss.first]; FOR es: RopeList _ exts, es.rest WHILE es # NIL DO exists: BOOL _ TRUE; fileName _ Rope.Cat[fileStem, ss.first, ".", es.first]; [] _ FS.FileInfo[fileName !FS.Error => {exists _ FALSE; CONTINUE}]; IF exists THEN RETURN; ENDLOOP; ENDLOOP; FOR es: RopeList _ exts, es.rest WHILE es # NIL DO IF el = NIL THEN el _ es.first ELSE el _ el.Cat[", ", es.first]; ENDLOOP; FS.Error[[user, $NoSuchFile, IO.PutFR[ "%g{%g}.{%g}", [rope[fileStem]], [rope[sl]], [rope[el]] ]]]; }; oneBPPPositive: Imager.ColorOperator _ ImagerColorOperator.MapColorModel[1, MapPositive]; oneBPPInverted: Imager.ColorOperator _ ImagerColorOperator.MapColorModel[1, MapInverted]; MapPositive: PROC [s: ImagerPixelArray.Sample] RETURNS [Imager.ConstantColor] = { RETURN [SELECT s FROM 0 => Imager.black, 1 => Imager.white, ENDCASE => ERROR]; }; MapInverted: PROC [s: ImagerPixelArray.Sample] RETURNS [Imager.ConstantColor] = { RETURN [SELECT s FROM 0 => Imager.white, 1 => Imager.black, ENDCASE => ERROR]; }; StoreChanges: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle = NARROW[clientData]; -- get finger viewer data aisFile: Rope.ROPE _ ViewerTools.GetContents[fingerHandle.pictureFileName.result]; p: LIST OF FingerOps.PropPair _ LIST[ NEW[FingerOps.PropPairObject _ [$Plan, ViewerTools.GetContents[fingerHandle.plan.result]]], NEW[FingerOps.PropPairObject _ [$AISFile, aisFile]], NEW[FingerOps.PropPairObject _ [$NickName, ViewerTools.GetContents[fingerHandle.nickName.result]]]]; FingerOps.SetUserProps[ViewerTools.GetContents[fingerHandle.name.result], p]; SetPicture[fingerHandle.pictureViewer, aisFile]; fingerHandle.outer.newVersion _ FALSE; ViewerOps.PaintViewer[fingerHandle.outer, caption]; EXITS FingerProblem => { MessageWindow.Append[ message: SELECT fingerErrorCode FROM Aborted => "\n... Transaction Aborted; Retry Finger Operation", Error => "\n... Internal Finger Error; Contact Implementor", Failure => "\n... Connection with server broken; try again later", ENDCASE => NIL, clearFirst: TRUE ]; MessageWindow.Blink[] }; END; SetUserProperties: Commander.CommandProc = { ENABLE FingerOps.FingerError => { fingerErrorCode _ reason; GOTO FingerProblem }; FOR propList: LIST OF ATOM _ fingerToolProps, propList.rest UNTIL propList = NIL DO FingerOps.AddUserProp[Atom.GetPName[propList.first]]; ENDLOOP; EXITS FingerProblem => { IO.PutRope[cmd.out, SELECT fingerErrorCode FROM Aborted => "\n... Transaction Aborted; Retry Finger Operation", Error => "\n... Internal Finger Error; Contact Implementor", Failure => "\n... Connection with server broken; try again later", ENDCASE => NIL ] }; }; SelectionButtonClicked: Buttons.ButtonProc = { whichButton: SelectionData _ NARROW[clientData]; -- get our data fingerHandle: FingerHandle _ whichButton.parentHandle; -- get other data relating to finger tool IF whichButton.selectable THEN { ViewerTools.EnableUserEdits[whichButton.result]; ViewerTools.SetSelection[whichButton.result]; } }; ViewerOps.RegisterViewerClass[flavor: $FingerPicture, class: NEW[ViewerClasses.ViewerClassRec _ [flavor: $FingerPicture, paint: PaintPicture]]]; Commander.Register[key: "FingerTool", proc: MakeFingerTool, doc: "Create a finger tool." ]; Commander.Register[key: "SetUserProperties", proc: SetUserProperties, doc: "Initialize all of the necessary user properties for the FingerTool"]; [] _ WalnutRegistry.Register[procSet: WalnutRegistry.ProcSet[eventProc: RecordMailRead], queue: fingerQueue]; END. €FingerTool.mesa Last edited by: Donahue, February 5, 1986 5:25:02 pm PST Last edited by: Gifford, July 26, 1985 4:25:45 pm PDT Spreitzer, December 2, 1985 2:18:12 pm PST the following hold all the data pertinent to selections for each fingered object The list of the user properties currently managed by the FingerTool For uniformity, some standard distances between entries in the tool are defined. -- Can't use talker yet, because it hasn't been carried over fingerHandle.talkButton _ Buttons.Create -- button to connect to talk program [info: [name: "talk to", wx: fingerHandle.storeChangesButton.wx + fingerHandle.storeChangesButton.ww + horizSpace, wy: fingerHandle.height, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: fingerHandle.outer, border: TRUE], clientData: fingerHandle, -- this will be passed to our button proc proc: TalkTo]; host part of the screen see if host exists look over the properties now set editable bit (for now, let anybody edit everything!) fingerHandle.editable _ Rope.Equal[user, self, FALSE]; If the entry is real old, don't bother displaying it look over the properties 0 => oneBPPInverted, 1 => oneBPPPositive, GetSep: PROC [fileStem, fmt: Rope.ROPE] RETURNS [fileName: Rope.ROPE] = { PerCandidate: PROC [rope: Rope.ROPE] RETURNS [tryNext: BOOL _ TRUE] = { exists: BOOL _ TRUE; [] _ FS.FileInfo[rope !FS.Error => {exists _ FALSE; CONTINUE}]; IF exists THEN fileName _ rope; tryNext _ NOT exists; }; fileName _ NIL; UserProfileOps.Enumerate[PerCandidate, fmt, IO.rope[fileStem]]; IF fileName = NIL THEN FS.Error[[user, $NoSuchFile, fmt.Cat[" of ", fileStem]]]; }; -- Can't use talker yet, when it gets converted, we'll use it TalkTo: Buttons.ButtonProc = BEGIN fingerHandle: FingerHandle = NARROW[clientData]; -- get finger viewer data userNamePattern: Rope.ROPE = ViewerTools.GetContents[fingerHandle.name.result]; cmd: Commander.Handle _ NEW[Commander.CommandObject _ []]; cmd.out _ IO.noWhereStream; cmd.err _ IO.noWhereStream; IF Rope.Length[userNamePattern] # 0 THEN [] _ CommandTool.DoCommandRope[commandLine: Rope.Cat["///Commands/Talk ", userNamePattern, " &"], parent: cmd] ELSE BEGIN MessageWindow.Append[message: "Enter username for talk connection.", clearFirst: TRUE]; MessageWindow.Blink[]; END; END; force the selection into the user input field Register commands with the Exec to perform finger and create an instance of the Finger Tool. Κ΄˜šœ™Jšœ8™8Jšœ5™5Icode™*J™—šΟk ˜ Jšœ˜Jšœœ ˜Jšœ œ œ˜,Jšœœ/˜—J˜"J˜J˜5˜%J˜˜Jšœ8ž˜SJ˜J˜J˜ J˜J˜Jšœœ˜——Jšœœœ,˜œœ˜gJ˜h˜KJšœΟœœ˜ιJšœœ˜ J˜J˜—Jšœ™Jšœ:ž˜N˜/˜J˜J˜J˜Jšœž4˜FJ˜Jšœœ˜—Jšœž)˜DJ˜—šœ.ž+˜Y˜J˜J˜YJ˜Jšœž4˜FJ˜Jšœœ˜—Jšœž)˜DJ˜—JšœHž%˜mJšœ<œ˜BJ˜hJšœ>œ˜DJ˜jJšœDœ˜JJ˜pJšœBœ˜HJ˜nJšœBœ˜HJ˜nJšœIœœ˜qJšœ:ž#˜]—Jšœ˜—J˜˜"Jšœœœœ˜Jšœœ˜J˜>Jšœ4™4Jšœ:œœ˜Fšœ*œ)œœ˜ˆJ˜(—Jšœ˜—J˜=J˜Jšœ™šœœœ:˜HJšœœ˜šœœ˜Jš œ'œœ œœ˜XJ˜JJ˜—šœ˜J˜?—šœ˜J˜C—šœ˜J˜C—Jšœ˜—J˜Jšœ œ˜&Jšœ3˜3J˜Jšœ˜˜Jšœ œ˜$J˜?J˜˜>Kšœ;œ ˜HKšœ œœ˜šœœœ˜2Kšœœœœ˜@šœœœ˜2Jšœœœ˜K˜7Jš œœœœœ˜CJšœœœ˜Jšœ˜—Jšœ˜—šœœœ˜2Kšœœœœ˜@Jšœ˜—šœœ˜&Kšœ˜Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ˜—K˜—J˜š Ÿœœœœœ™Iš Ÿ œœ œœ œœ™GJšœœœ™Jš œœœœœ™?Jšœœ™Jšœ œ™J™—Jšœ œ™Jšœ,œ™?Jšœ œœœ7™PJ™—J˜JšœY˜YJšœY˜YJ˜šŸ œœœ˜Qšœœ˜J˜J˜Jšœœ˜—J˜—J˜šŸ œœœ˜Qšœœ˜J˜J˜Jšœœ˜—J˜—J˜˜"šœ˜ Jšœ4œ˜I—Jšœœž˜JJšœœ@˜Ršœœœ˜šœ˜JšœX˜[Jšœ1˜4šœ'˜*J˜9———J˜MJ˜0Jšœ œ˜&Jšœ3˜3šœ˜˜Jšœ œ˜$J˜?J˜