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
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,
the following hold all the data pertinent to selections for each fingered object
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];
The list of the user properties currently managed by the FingerTool
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;
For uniformity, some standard distances between entries in the tool are defined.
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: BOOLFALSE, height: CARDINAL ← entryHeight, edit: BOOLFALSE] =
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];
-- 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];
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];
host part of the screen
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: REALMIN[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];
};
see if host exists
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]]];
look over the properties
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];
now set editable bit (for now, let anybody edit everything!)
fingerHandle.editable ← Rope.Equal[user, self, FALSE];
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 the entry is real old, don't bother displaying it
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];
look over the properties
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 => oneBPPInverted,
1 => oneBPPPositive,
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.ROPENIL;
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: BOOLTRUE;
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: PROC [fileStem, fmt: Rope.ROPE] RETURNS [fileName: Rope.ROPE] = {
PerCandidate: PROC [rope: Rope.ROPE] RETURNS [tryNext: BOOLTRUE] = {
exists: BOOLTRUE;
[] ← 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]]];
};
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;
-- 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;
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 = {
force the selection into the user input field
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];
} };
Register commands with the Exec to perform finger and create an instance of the Finger Tool.
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.