<> <> <> <> <> <> <> <<>> DIRECTORY AIS, Atom USING [GetPName, MakeAtom], BasicTime USING [nullGMT, GMT, Now, Period], Buttons USING [Button, ButtonProc, Create, SetDisplayStyle, Destroy], Commander USING [CommandProc, Handle, Register], Containers USING [Container, Create], Convert USING [RopeFromTime], FingerOps, FS USING [Error, ExpandName, FileInfo], Icons USING [IconFlavor, NewIconFromFile], Imager, ImagerBackdoor, ImagerBox, ImagerColor, ImagerTransformation, ImagerPixelArray, ImagerFont, IO, List USING [Sort, Comparison, CompareProc], MBQueue, Menus USING [MouseButton], 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, ImagerColor, ImagerPixelArray, ImagerTransformation, IO, List, MBQueue, MessageWindow, Process, ThisMachine, Rope, UserCredentials, UserProfile, ViewerEvents, VFonts, ViewerOps, ViewerTools, WalnutRegistry = BEGIN FingerImpl: ERROR ~ CODE; -- BugCatchers! <> requestMachineList, requestUserList: requestList; Directions: TYPE ~ {forwards, backwards}; requestList: TYPE ~ RECORD [ list: LIST OF Rope.ROPE _ NIL, presentValue: LIST OF Rope.ROPE _ NIL ]; 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 nextUserButton: Buttons.Button, -- transient button for multiple matches nextMachineButton: Buttons.Button, -- transient button for multiple matches performHostButton, storeHostButton: Buttons.Button, <> <> name, host, lastchange, actions: SelectionData _ NEW[SelectionRecord], <<...while these items come directly from the database>> fingerInfo: LIST OF SelectionData, pictureViewer: ViewerClasses.Viewer]; SelectionData: TYPE = REF SelectionRecord; SelectionRecord: TYPE = RECORD [parentHandle: FingerHandle, prop: ATOM _ NIL, selectable: BOOL, button: Buttons.Button, result: ViewerClasses.Viewer, registration: ViewerEvents.EventRegistration _ NIL]; PictureState: TYPE = REF PictureStateRecord; PictureStateRecord: TYPE = RECORD [rectangle: ImagerBox.Rectangle, pa: ImagerPixelArray.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]]; IsPattern: PROC [value: Rope.ROPE] RETURNS [ItsAPattern:BOOL] ~ { <<... returns TRUE iff value is has a "*" in it.>> RETURN [Rope.Find[s1: value, s2: "*", pos1: 0, case: FALSE] # -1] }; -- Append: PROC [list1, list2: LIST OF ATOM] RETURNS [LIST OF ATOM] ~ { <> newList: LIST OF ATOM _ list1; FOR list: LIST OF ATOM _ list2, list.rest UNTIL list = NIL DO newList _ CONS[list.first, newList] ENDLOOP; RETURN[newList] }; InAtomList: PROC [atom: ATOM, atomList: LIST OF ATOM] RETURNS [inList: BOOLEAN] ~ { <<... returns TRUE iff atom is in atomList>> FOR list: LIST OF ATOM _ atomList, list.rest UNTIL list = NIL DO IF atom = list.first THEN RETURN[inList _ TRUE] ENDLOOP; RETURN[inList _ FALSE] }; -- InAtomList RecordMailRead: WalnutRegistry.EventProc ~ { IF event = mailRead THEN { mailProp.val _ TimeRope[BasicTime.Now[]]; FingerOps.SetUserProps[user: UserCredentials.Get[].name, props: LIST[ mailProp] ! FingerOps.FingerError => CONTINUE ] } }; InitFingerInfo: PROC [fingerHandle: FingerHandle] <> ~ { FOR info: LIST OF ATOM _ Append[FingerOps.ListMachineProps[], FingerOps.ListUserProps[]], info.rest UNTIL info = NIL DO fingerHandle.fingerInfo _ CONS[NEW[SelectionRecord], fingerHandle.fingerInfo]; fingerHandle.fingerInfo.first.prop _ info.first ENDLOOP; }; -- InitFingerInfo FindDataForButton: PROC [button: ATOM, fingerInfo: LIST OF SelectionData] RETURNS [SelectionData _ NIL] ~ { <> FOR infoList: LIST OF SelectionData _ fingerInfo, infoList.rest UNTIL infoList = NIL DO IF button = infoList.first.prop THEN RETURN[infoList.first] ENDLOOP; ERROR FingerImpl; -- should never get to here }; -- FindDataForButton ProduceButtonName: PROC [buttonAtom: ATOM] RETURNS [buttonName: Rope.ROPE] <> ~ { buttonName _ Atom.GetPName[atom: buttonAtom]; }; -- ProduceButtonName InvalidateInfo: PROC [buttonSet: LIST OF ATOM, fingerHandle: FingerHandle] ~ { <> FOR button: LIST OF ATOM _ buttonSet, button.rest UNTIL button = NIL DO ViewerTools.SetContents[FindDataForButton[button: button.first, fingerInfo: fingerHandle.fingerInfo].result, NIL]; ENDLOOP; }; Alphabetisize: PROC [atomSet: LIST OF ATOM] RETURNS [LIST OF ATOM] <<...returns atomSet ordered alphabetically by PName.>> ~ { Compare: List.CompareProc = { c:List.Comparison; WITH ref1 SELECT FROM rope: Rope.ROPE => c _ Rope.Compare[s1: rope, s2: NARROW[ref2], case: TRUE]; ENDCASE => IF ref1 = NIL THEN c _ Rope.Compare[s1: NARROW[ref1], s2: NARROW[ref2], case: TRUE] ELSE ERROR; RETURN [IF c=less THEN greater ELSE IF c=greater THEN less ELSE equal] }; pNameSet: LIST OF REF ANY _ NIL; alphaSet: LIST OF ATOM _ NIL; FOR atom: LIST OF ATOM _ atomSet, atom.rest UNTIL atom = NIL DO pNameSet _ CONS[Atom.GetPName[atom: atom.first], pNameSet] ENDLOOP; FOR pName: LIST OF REF ANY _ List.Sort[list: pNameSet, compareProc: Compare], pName.rest UNTIL pName = NIL DO alphaSet _ CONS[Atom.MakeAtom[pName: NARROW[pName.first, Rope.ROPE]], alphaSet] ENDLOOP; RETURN[alphaSet] }; MakeFingerTool: Commander.CommandProc = BEGIN fingerHandle: FingerHandle = NEW[FingerToolRec]; InitFingerInfo[fingerHandle: fingerHandle]; 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 <> DisplayMachineInfo[fingerHandle: fingerHandle, machine: ThisMachine.Name[$Pup]]; DisplayUserInfo[fingerHandle: fingerHandle, user: UserCredentials.Get[].name, mouseButton: red] 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; hostButtonsLevel: CARDINAL _ 0; -- set to the vertical level of the host buttons. userButtonsLevel: CARDINAL _ 0; -- set to the vertical level of the user buttons. 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, style: ATOM _ $WhiteOnBlack] = 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, style]; 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; -- SetupButtons SetupButtonSet: PROC [buttonSet: LIST OF ATOM, fingerInfo: LIST OF SelectionData] <> ~ { data: SelectionData; FOR button: LIST OF ATOM _ Alphabetisize[buttonSet], button.rest UNTIL button = NIL DO data _ FindDataForButton[button: button.first, fingerInfo: fingerInfo]; SetupButtons[ name: ProduceButtonName[button.first], which: data, scroll: TRUE, edit: TRUE]; data.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, data.result] ENDLOOP; }; -- SetupButtonSet fingerHandle.height _ fingerHandle.height + entryVSpace; -- space down from the top of the viewer userButtonsLevel _ fingerHandle.height; 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]; <<>> <<-- Can't use talker yet, because it hasn't been carried over>> <> <<[info:>> <<[name: "talk to",>> <> <> <> <> <> <> <> fingerHandle.height _ fingerHandle.height + entryHeight + entryVSpace; -- space down from the action buttons SetupButtons[name: "User Name", which: fingerHandle.name, scroll: TRUE, edit: TRUE]; fingerHandle.name.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.name.result]; SetupButtonSet[buttonSet: FingerOps.ListUserProps[], fingerInfo: fingerHandle.fingerInfo]; SetupButtons[name: "Actions", which: fingerHandle.actions, scroll: TRUE, height: 3*entryHeight, edit: FALSE, style: $BlackOnGrey]; <<>> 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 hostButtonsLevel _ fingerHandle.height; 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, scroll: TRUE, edit: TRUE]; fingerHandle.host.registration _ ViewerEvents.RegisterEventProc[SetNew, edit, fingerHandle.host.result]; SetupButtonSet[buttonSet: FingerOps.ListMachineProps[], fingerInfo: fingerHandle.fingerInfo]; SetupButtons[name: "Last User", which: fingerHandle.lastchange, scroll: TRUE, height: entryHeight, edit: FALSE, style: $BlackOnGrey]; <<>> fingerHandle.height _ fingerHandle.height + entryVSpace; -- space at bottom of finger viewer END; SetNew: ViewerEvents.EventProc = { WHILE viewer.parent # NIL DO viewer _ viewer.parent ENDLOOP; 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]; IF state.colorOperator # NIL THEN Imager.SetSampledColor[context: context, pa: state.pa, m: NIL, colorOperator: state.colorOperator] ELSE Imager.SetSampledBlack[context: context, pa: state.pa, m: NIL, clear: FALSE]; Imager.MaskRectangle[context, state.rectangle]; END }; Message: PROC [r: Rope.ROPE] = { MessageWindow.Clear[]; MessageWindow.Append[r]; MessageWindow.Blink[]; }; DetermineRequest: PROC [fingerHandle: FingerHandle, relation: ATOM] RETURNS [requestedProp: ATOM, requestedValue: Rope.ROPE] <<...looks in fingerHandle and makes a wild guess as to which field the query is being done on. relation specifies which relation to look in ($User for the user relation and $Machine for the machine relation). It also acts as the default. If more than one field has something in it, default. If the defalut field is empty then use the local machine/logged user as the default.>> ~ { requestedProp _ relation; requestedValue _ NIL; -- since the default case pays no attention to this value IF Rope.Length[ViewerTools.GetContents[IF relation = $User THEN fingerHandle.name.result ELSE fingerHandle.host.result]] # 0 THEN { <> RETURN } ELSE { full: BOOLEAN _ FALSE; FOR prop: LIST OF ATOM _ (IF relation = $User THEN FingerOps.ListUserProps[] ELSE FingerOps.ListMachineProps[]), prop.rest UNTIL prop = NIL DO IF Rope.Length[ViewerTools.GetContents[FindDataForButton[button: prop.first, fingerInfo: fingerHandle.fingerInfo].result]] # 0 THEN IF full THEN { requestedProp _ relation; requestedValue _ NIL -- since the default case pays no attention to this value } ELSE { full _ TRUE; requestedProp _ prop.first; requestedValue _ ViewerTools.GetContents[FindDataForButton[button: prop.first, fingerInfo: fingerHandle.fingerInfo].result] } ENDLOOP } }; -- DetermineRequest NextUser: PROC [fingerHandle: FingerHandle, direction: Directions, picture: BOOL _ FALSE] ~ { <<... displays the next user on the requestUserList list (going in the right direction).>> user: Rope.ROPE; IF direction = forwards THEN { IF requestUserList.presentValue = NIL THEN requestUserList.presentValue _ requestUserList.list ELSE IF (requestUserList.presentValue _ requestUserList.presentValue.rest) = NIL THEN requestUserList.presentValue _ requestUserList.list; user _ requestUserList.presentValue.first; DisplayUserProps[user: user, fingerHandle: fingerHandle, picture: picture]; } ELSE { tempList: LIST OF Rope.ROPE _ requestUserList.list; <> IF requestUserList.presentValue = requestUserList.list THEN WHILE tempList.rest # NIL DO tempList _ tempList.rest ENDLOOP ELSE WHILE tempList.rest # requestUserList.presentValue DO tempList _ tempList.rest ENDLOOP; requestUserList.presentValue _ tempList; user _ requestUserList.presentValue.first; DisplayUserProps[user: user, fingerHandle: fingerHandle, picture: picture]; } }; DisplayUserProps: PROC [user: Rope.ROPE, fingerHandle: FingerHandle, picture: BOOL _ FALSE] <<...displays all the information about user in the fingertool.>> ~ { lastChange: FingerOps.StateChange; time: BasicTime.GMT; now: BasicTime.GMT = BasicTime.Now[]; twoDaysInSeconds: INT = LONG[48] * 60 * 60; muser: Rope.ROPE; action: Rope.ROPE _ " "; <> 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]; ViewerTools.SetContents[fingerHandle.name.result, user]; -- do it again if necessary FOR p: LIST OF FingerOps.PropPair _ FingerOps.GetUserProps[user], p.rest UNTIL p=NIL DO IF p.first.prop=Atom.MakeAtom["Picture File"] THEN { SetPicture[fingerHandle.pictureViewer, IF picture THEN p.first.val ELSE NIL]; }; ViewerTools.SetContents[FindDataForButton[button: p.first.prop, fingerInfo: fingerHandle.fingerInfo].result, p.first.val]; ENDLOOP; }; -- NextUser NextMachine: PROC [fingerHandle: FingerHandle, direction: Directions] ~ { <<... displays the next machine on the requestMachineList list (going in the right direction).>> machine: Rope.ROPE; IF direction = forwards THEN { IF requestMachineList.presentValue = NIL THEN requestMachineList.presentValue _ requestMachineList.list ELSE IF (requestMachineList.presentValue _ requestMachineList.presentValue.rest) = NIL THEN requestMachineList.presentValue _ requestMachineList.list; machine _ requestMachineList.presentValue.first; DisplayMachineProps[machine: machine, fingerHandle: fingerHandle]; } ELSE { tempList: LIST OF Rope.ROPE _ requestMachineList.list; <> IF requestMachineList.presentValue = requestMachineList.list THEN WHILE tempList.rest # NIL DO tempList _ tempList.rest ENDLOOP ELSE WHILE tempList.rest # requestMachineList.presentValue DO tempList _ tempList.rest ENDLOOP; requestMachineList.presentValue _ tempList; machine _ requestMachineList.presentValue.first; DisplayMachineProps[machine: machine, fingerHandle: fingerHandle]; } }; -- NextMachine DisplayMachineProps: PROC [machine: Rope.ROPE, fingerHandle: FingerHandle] <<...displays all the information about machine in the fingertool.>> ~ { lastChange: FingerOps.StateChange; time: BasicTime.GMT; lastUser: Rope.ROPE _ " "; <> [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]]] ELSE ViewerTools.SetContents[fingerHandle.lastchange.result, NIL]; ViewerTools.SetContents[fingerHandle.host.result, machine]; -- do it again if necessary FOR p: LIST OF FingerOps.PropPair _ FingerOps.GetMachineProps[machine], p.rest UNTIL p=NIL DO ViewerTools.SetContents[FindDataForButton[button: p.first.prop, fingerInfo: fingerHandle.fingerInfo].result, p.first.val]; ENDLOOP; }; -- DisplayMachineProps LookUpMachineProp: PROC [fingerHandle: FingerHandle, requestedProp: ATOM, requestedValue: Rope.ROPE] ~ { <<...looks up the machine relation for the relshipSet that matches requestedValue on the requestedProp attribute.>> IF Rope.Length[base: requestedValue] = 0 THEN RETURN; requestMachineList.list _ NIL; -- make sure there is nothing left over fro last time. requestMachineList.list _ FingerOps.MatchMachineProperty[propVal: NEW[FingerOps.PropPairObject _ [prop: requestedProp, val: requestedValue]]]; requestMachineList.presentValue _ NIL; IF requestMachineList.list = NIL THEN { Message["Not valid!"]; InvalidateInfo[buttonSet: FingerOps.ListMachineProps[], fingerHandle: fingerHandle]; ViewerTools.SetContents[fingerHandle.lastchange.result, NIL]; RETURN; }; NextMachine[fingerHandle: fingerHandle, direction: forwards]; <> IF requestMachineList.list.rest # NIL THEN fingerHandle.nextMachineButton _ Buttons.Create [info: [name: "Next", wx: fingerHandle.storeHostButton.wx + fingerHandle.storeHostButton.ww + horizSpace, wy: hostButtonsLevel, 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: PerformNextMachine]; }; -- LookUpMachineProp LookUpUserProp: PROC [fingerHandle: FingerHandle, requestedProp: ATOM, requestedValue: Rope.ROPE] ~ { <<...looks up the user relation for the relshipSet that matches requestedValue on the requestedProp attribute.>> IF Rope.Length[base: requestedValue] = 0 THEN RETURN; requestUserList.list _ NIL; requestUserList.list _ FingerOps.MatchUserProperty[propVal: NEW[FingerOps.PropPairObject _ [prop: requestedProp, val: requestedValue]]]; requestUserList.presentValue _ NIL; IF requestUserList.list = NIL THEN { Message["Not a valid user!"]; SetPicture[fingerHandle.pictureViewer, NIL]; InvalidateInfo[buttonSet: FingerOps.ListUserProps[], fingerHandle: fingerHandle]; ViewerTools.SetContents[fingerHandle.actions.result, NIL]; RETURN; }; NextUser[fingerHandle: fingerHandle, direction: forwards]; <> IF requestUserList.list.rest # NIL THEN fingerHandle.nextUserButton _ Buttons.Create [info: [name: "Next", wx: fingerHandle.storeChangesButton.wx + fingerHandle.storeChangesButton.ww + horizSpace, wy: userButtonsLevel, 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: PerformNextUser] }; -- LookUpUserProp DisplayUserInfo: PROC [fingerHandle: FingerHandle, user: Rope.ROPE, mouseButton: Menus.MouseButton _ red] ~ { <<... displays all the info for user.>> IF Rope.Length[user] = 0 THEN { user _ UserCredentials.Get[].name; ViewerTools.SetContents[fingerHandle.name.result, user]; }; requestUserList.list _ NIL; requestUserList.presentValue _ NIL; <> requestUserList.list _ FingerOps.GetMatchingPersons[pattern: IF NOT IsPattern[user] THEN Rope.Concat[base: user, rest: "*"] ELSE user]; IF (requestUserList.list = NIL) THEN { IF IsPattern[user] THEN Message["No Match"] ELSE Message[Rope.Cat[user, " not found in Finger database!"]]; SetPicture[fingerHandle.pictureViewer, NIL]; InvalidateInfo[buttonSet: FingerOps.ListUserProps[], fingerHandle: fingerHandle]; ViewerTools.SetContents[fingerHandle.actions.result, NIL]; RETURN; }; <> NextUser[fingerHandle: fingerHandle, direction: forwards, picture: mouseButton = blue]; <<>> <> IF requestUserList.list.rest # NIL THEN fingerHandle.nextUserButton _ Buttons.Create [info: [name: "Next", wx: fingerHandle.storeChangesButton.wx + fingerHandle.storeChangesButton.ww + horizSpace, wy: userButtonsLevel, 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: PerformNextUser] }; -- DisplayUserInfo DisplayMachineInfo: PROC [fingerHandle: FingerHandle, machine: Rope.ROPE] ~ { <<... displays all the info for machine.>> IF Rope.Length[machine]=0 THEN { machine _ ThisMachine.Name[$Pup]; ViewerTools.SetContents[fingerHandle.host.result, machine]; }; <> IF NOT FingerOps.MachineExists[machine] AND NOT IsPattern[machine] THEN { Message[Rope.Cat[machine," is not a valid host!"]]; InvalidateInfo[buttonSet: FingerOps.ListMachineProps[], fingerHandle: fingerHandle]; ViewerTools.SetContents[fingerHandle.lastchange.result, NIL]; RETURN; }; <<>> requestMachineList.list _ NIL; -- make sure there is nothing left over fro last time. requestMachineList.presentValue _ NIL; <> IF IsPattern[machine] THEN requestMachineList.list _ FingerOps.GetMatchingMachines[pattern: machine] ELSE requestMachineList.list _ LIST[machine]; <<>> IF (requestMachineList.list = NIL) AND IsPattern[machine] THEN { Message["No Match"]; InvalidateInfo[buttonSet: FingerOps.ListMachineProps[], fingerHandle: fingerHandle]; ViewerTools.SetContents[fingerHandle.lastchange.result, NIL]; RETURN; }; <> NextMachine[fingerHandle: fingerHandle, direction: forwards] ; <> IF requestMachineList.list.rest # NIL THEN fingerHandle.nextMachineButton _ Buttons.Create [info: [name: "Next", wx: fingerHandle.storeHostButton.wx + fingerHandle.storeHostButton.ww + horizSpace, wy: hostButtonsLevel, 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: PerformNextMachine]; }; -- DisplayMachineInfo fingerErrorCode: FingerOps.Reason; PerformNextUser: Buttons.ButtonProc ~ { <<...displays the information about the first user in requestUserList in fingertool and the removes that user from the list except from a button request.>> <> ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle _ NARROW[clientData]; IF mouseButton = yellow THEN RETURN; IF mouseButton = blue THEN NextUser[fingerHandle: fingerHandle, direction: backwards] ELSE NextUser[fingerHandle: fingerHandle, direction: forwards]; 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[] }; }; -- PerformNextUser PerformNextMachine: Buttons.ButtonProc <<...displays the information about the first machine in requestMachineList in fingertool and the removes that machine from the list except from a button request.>> <> ~ { ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle _ NARROW[clientData]; IF mouseButton = yellow THEN RETURN; NextMachine[fingerHandle: fingerHandle, direction: IF mouseButton = blue THEN backwards ELSE forwards] 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[] }; }; -- PerformNextMachine PerformHost: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle _ NARROW[clientData]; machine: Rope.ROPE _ ViewerTools.GetContents[fingerHandle.host.result]; requestedProp: ATOM; requestedValue: Rope.ROPE; <> Buttons.Destroy[fingerHandle.nextMachineButton]; <> [requestedProp, requestedValue] _ DetermineRequest[fingerHandle, $Machine]; IF requestedProp = $Machine -- ie default -- THEN DisplayMachineInfo[fingerHandle: fingerHandle, machine: machine] ELSE LookUpMachineProp[fingerHandle: fingerHandle, requestedProp: requestedProp, requestedValue: requestedValue]; 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; -- PerformHost StoreHost: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle = NARROW[clientData]; -- get finger viewer data newInfo: LIST OF FingerOps.PropPair _ NIL; IF Rope.Length[ViewerTools.GetContents[fingerHandle.host.result]] = 0 THEN RETURN; FOR prop: LIST OF ATOM _ FingerOps.ListMachineProps[], prop.rest UNTIL prop = NIL DO newInfo _ CONS[NEW[FingerOps.PropPairObject _ [prop.first, ViewerTools.GetContents[FindDataForButton[button: prop.first, fingerInfo: fingerHandle.fingerInfo].result]]], newInfo] ENDLOOP; FingerOps.SetMachineProps[ViewerTools.GetContents[fingerHandle.host.result], newInfo]; <<>> 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 PerformFingerAtTool: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle _ NARROW[clientData]; user: Rope.ROPE _ ViewerTools.GetContents[fingerHandle.name.result]; now: BasicTime.GMT = BasicTime.Now[]; twoDaysInSeconds: INT = LONG[48] * 60 * 60; action: Rope.ROPE _ " "; requestedProp: ATOM; requestedValue: Rope.ROPE; <> Buttons.Destroy[fingerHandle.nextUserButton]; <> [requestedProp, requestedValue] _ DetermineRequest[fingerHandle, $User]; IF requestedProp = $User THEN DisplayUserInfo[fingerHandle: fingerHandle, user: user, mouseButton: mouseButton] ELSE LookUpUserProp[fingerHandle: fingerHandle, requestedProp: requestedProp, requestedValue: requestedValue]; 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 StoreChanges: Buttons.ButtonProc = BEGIN ENABLE FingerOps.FingerError => {fingerErrorCode _ reason; GOTO FingerProblem }; fingerHandle: FingerHandle = NARROW[clientData]; -- get finger viewer data aisFile: Rope.ROPE _ ViewerTools.GetContents[FindDataForButton[button: Atom.MakeAtom["Picture File"], fingerInfo: fingerHandle.fingerInfo].result]; -- needed for SetPicture newInfo: LIST OF FingerOps.PropPair _ NIL; IF Rope.Length[ViewerTools.GetContents[fingerHandle.name.result]] = 0 THEN RETURN; FOR buttonVal: LIST OF SelectionData _ fingerHandle.fingerInfo, buttonVal.rest UNTIL buttonVal = NIL DO IF InAtomList[atom: buttonVal.first.prop, atomList: FingerOps.ListUserProps[]] THEN newInfo _ CONS[NEW[FingerOps.PropPairObject _ [prop: buttonVal.first.prop, val: ViewerTools.GetContents[buttonVal.first.result]]], newInfo]; ENDLOOP; FingerOps.SetUserProps[ViewerTools.GetContents[fingerHandle.name.result], newInfo]; IF mouseButton = blue THEN 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; 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: ImagerPixelArray.PixelArray _ ImagerPixelArray.FromAIS[filename]; maxSample: ImagerPixelArray.Sample _ ImagerPixelArray.MaxSampleValue[pa, 0]; r: ImagerBox.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 => oneBPPInverted,>> <<1 => oneBPPPositive,>> 0 => NIL, ENDCASE => ImagerColor.NewColorOperatorGrayLinear[maxSample, 0, maxSample+1, 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: ImagerPixelArray.PixelArray _ ImagerPixelArray.Join3AIS[redName, greenName, blueName]; maxSample: ImagerPixelArray.Sample _ ImagerPixelArray.MaxSampleValue[pa, 0]; r: ImagerBox.Rectangle _ ImagerTransformation.TransformRectangle[pa.m, [0, 0, pa.sSize, pa.fSize]]; colorOperator: Imager.ColorOperator _ ImagerColor.NewColorOperatorRGB[maxSample]; v.data _ NEW[PictureStateRecord _ [rectangle: r, pa: pa, colorOperator: colorOperator]]; }; END; ViewerOps.PaintViewer[v, all]; }; -- SetPicture 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]] ]]]; }; -- GetSep <> <> <> <<[] _ FS.FileInfo[rope !FS.Error => {exists _ FALSE; CONTINUE}];>> <> <> <<};>> <> <> <> <<};>> oneBPPPositive: Imager.ColorOperator _ ImagerColor.NewColorOperatorMap[1, MapPositive]; oneBPPInverted: Imager.ColorOperator _ ImagerColor.NewColorOperatorMap[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]; }; <<-- Can't use talker yet, when it gets converted, we'll use it>> <> <> <> <> <> <> <> <> <> <<[] _ CommandTool.DoCommandRope[commandLine: Rope.Cat["///Commands/Talk ", userNamePattern, " &"], parent: cmd]>> <> <> <> <> <> <> <<>> SetUserProperties: Commander.CommandProc = { ENABLE FingerOps.FingerError => { fingerErrorCode _ reason; GOTO FingerProblem }; FingerOps.AddUserProp["Plan"]; FingerOps.AddMachineProp["Network"]; 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]; <> IF mouseButton = yellow THEN { IF whichButton = fingerHandle.name THEN { Buttons.Destroy[fingerHandle.nextUserButton]; DisplayUserInfo[fingerHandle: fingerHandle, user: ViewerTools.GetContents[whichButton.result]] } ELSE IF whichButton = fingerHandle.host THEN { Buttons.Destroy[fingerHandle.nextMachineButton]; DisplayMachineInfo[fingerHandle: fingerHandle, machine: ViewerTools.GetContents[whichButton.result]] } ELSE IF InAtomList[atom: whichButton.prop, atomList: FingerOps.ListMachineProps[]] THEN { Buttons.Destroy[fingerHandle.nextMachineButton]; LookUpMachineProp[fingerHandle: fingerHandle, requestedProp: whichButton.prop, requestedValue: ViewerTools.GetContents[whichButton.result]] } ELSE { Buttons.Destroy[fingerHandle.nextUserButton]; LookUpUserProp[fingerHandle: fingerHandle, requestedProp: whichButton.prop, requestedValue: ViewerTools.GetContents[whichButton.result]] } } } }; -- SelectionButtonClicked <> 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. <> <> <> <> <<>> <> <> <<>>