-- WFileInterface.mesa
-- last edit: Maxwell, July 14, 1982 10:35 am (search for "JTM")
-- last edit: Stewart, March 28, 1983 5:33 pm
-- last edit: McGregor, 20-Apr-82 14:45:11
-- last edit: MBrown, 29-Mar-82 15:10:31
-- last edit: Paul Rovner, October 28, 1982 5:51 pm
-- last edit: Russ Atkinson, April 7, 1983 12:03 am

DIRECTORY
Buttons USING [Button, ButtonProc, Create, SetDisplayStyle],
Commander USING [CommandProc, Register],
Containers USING [ChildXBound, ChildYBound],
Graphics USING[SetCP],
GraphicsOps USING[BitmapRep, DrawBitmap],
IO USING[Close, CreateOutputStreamToRope, Flush, GetOutputStreamRope, Handle, PutF, PutFR, rope, SetEcho, time],
Labels USING[Create, Set],
-- List USING [Remove],
Process USING [Detach, Pause, SecondsToTicks],
Rope USING [Cat, Equal, Fetch, Find, Index, Length, ROPE, SkipTo, Substr, Translate, TranslatorType],
Rules USING [Create],
Runtime USING [GetBcdTime],
STP USING [Destroy],
TypeScript USING [-- CharProc, -- Create -- GetLine, PutChar --],
VFTOps USING [BuildOuter, CloseCommand, ConfirmAbort, FreeTask, FTPCommand, Functions, GetTheFile, GiveCommand, GiveConfirm, Handle, LeftSmallMap, RGDestroy, RightSmallMap, SmallMap, StopFTP, VFTObject],
ViewerClasses USING [DestroyProc, PaintProc, Viewer, ViewerClass, ViewerClassRec],
ViewerOps USING [AddProp, -- CloseViewer, -- CreateViewer, FetchProp, FetchViewerClass, PaintViewer, RegisterViewerClass],
ViewerIO USING [CreateViewerStreams],
ViewerTools USING [GetContents, SetContents, SetSelection];

WFileInterface: MONITOR LOCKS self.LOCK USING self: VFTOps.Handle
IMPORTS Buttons, Commander, Containers, Graphics,
GraphicsOps, IO, Labels,
Process, Rope, Rules, Runtime,
STP, TypeScript,
VFTOps, ViewerOps, ViewerIO, ViewerTools
EXPORTS VFTOps =
BEGIN

-- CommentRope: Rope.ROPE ← "FileTool Notes:\n Control-DEL doesn't work, use 'Stop!' instead.\n User name and password acquired from UserExec.\n Entry of connect password must end with CR, DEL or Space.\n 'Directory:' will accept either\n [Indigo]<Cedar>Top>\n /Indigo/Cedar/Top/\n 'Directory:' must start with a '/ or '[.\n Except for Delete operations, the file name specification, e.g. *.mesa, may be in either of the fields 'FileName(s):,' or 'Local:.'\n DF File operations always use 'FileName(s):' for individual file names. Last component of df file name must be in 'DF File:' field. Use Control-Del in UserExec vuewer to abort DF operations. ExportsOnly is like bringover /p switch.\n Remote-Delete requires a name in 'FileName(s):.'\n Local-Delete requires a name in 'Local:.'\n Verify requests user approval of each file operation. DF operations are like Bringover /a if Verify is off.\n Update > transfers if the source file is newer than the destination file.\n Update a> transfers if the source file is newer or if the destination file does not exist.\n";

-- vftlist: LIST OF REF ANY ← NIL;

fileToolClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec ← ViewerOps.FetchViewerClass[$Container]^];
 -- deltas from Container class stored in Init Proc elsewhere in this module

bitmapClass: ViewerClasses.ViewerClass ← NEW[ViewerClasses.ViewerClassRec ← [
paint: BitmapPaint
]];

BaselineFix: INTEGER = 3;
TWidth: INTEGER = 500;
THT: INTEGER = 300;
MsgHT: INTEGER = 16;
SWHeight: INTEGER = 40;
CHT: INTEGER = 16;
PCol0: INTEGER = 0;
PCol1: INTEGER = 230;
PCol2: INTEGER = 400;
CCol0: INTEGER = 0;
CCol1: INTEGER = 78;
CCol2: INTEGER = 182;
CCol3: INTEGER = 290;
CCol4: INTEGER = 370;

-- Parameter support procedures

UpdateButtonProc: Buttons.ButtonProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[clientData];
viewer: Buttons.Button ← NARROW[parent];
SELECT viewer FROM
vft.updateButton => vft.update ← BoolButton[b: viewer, p: vft.update];
vft.updateAlwaysButton => vft.updateAlways ← BoolButton[b: viewer, p: vft.updateAlways];
vft.verifyButton => vft.verify ← BoolButton[b: viewer, p: vft.verify];
vft.exportsOnlyButton => vft.publiconly ← BoolButton[b: viewer, p: vft.publiconly];
ENDCASE => ERROR;
};

OptionsButtonProc: Buttons.ButtonProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[clientData];
viewer: Buttons.Button ← NARROW[parent];
SELECT viewer FROM
vft.typeOption => vft.newOptions[type] ← BoolButton[viewer, vft.newOptions[type]];
vft.bytesOption => vft.newOptions[bytes] ← BoolButton[viewer, vft.newOptions[bytes]];
vft.pagesOption => vft.newOptions[pages] ← BoolButton[viewer, vft.newOptions[pages]];
vft.authorOption => vft.newOptions[author] ← BoolButton[viewer, vft.newOptions[author]];
vft.createOption => vft.newOptions[create] ← BoolButton[viewer, vft.newOptions[create]];
vft.writeOption => vft.newOptions[write] ← BoolButton[viewer, vft.newOptions[write]];
vft.readOption => vft.newOptions[read] ← BoolButton[viewer, vft.newOptions[read]];
vft.abortOption => {
vft.newOptions ← vft.options;
DelayChangeCommandSubwindow[vft, commands];
};
vft.applyOption => {
vft.options ← vft.newOptions;
DelayChangeCommandSubwindow[vft, commands];
};
ENDCASE => ERROR;
};

BoolButton: PROC [b: Buttons.Button, p: BOOL] RETURNS [BOOL] = {
p ← NOT p;
SetBoolButton[b, p];
RETURN[p];
};

SetBoolButton: PROC [b: Buttons.Button, p: BOOL] = {
IF p THEN Buttons.SetDisplayStyle[b, $WhiteOnBlack]
ELSE Buttons.SetDisplayStyle[b, $BlackOnWhite];
};

CommandButtonProc: Buttons.ButtonProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[clientData];
viewer: Buttons.Button ← NARROW[parent];
SELECT viewer FROM
vft.retrieveButton => Command[vft, retrieve, TRUE];
vft.storeButton => Command[vft, store, TRUE];
vft.localListButton => Command[vft, localList, FALSE];
vft.remoteListButton => Command[vft, remoteList, TRUE];
vft.listOptionsButton => {
DelayChangeCommandSubwindow[vft, options];
SetBoolButton[vft.typeOption, vft.newOptions[type]];
SetBoolButton[vft.bytesOption, vft.newOptions[bytes]];
SetBoolButton[vft.authorOption, vft.newOptions[author]];
SetBoolButton[vft.createOption, vft.newOptions[create]];
SetBoolButton[vft.writeOption, vft.newOptions[write]];
SetBoolButton[vft.readOption, vft.newOptions[read]];
SetBoolButton[vft.pagesOption, vft.newOptions[pages]];
};
vft.closeButton => {
-- meed interlock with dataTransferring?
Labels.Set[vft.msg, " "];
VFTOps.CloseCommand[vft];
};
vft.dfGetButton => Command[vft, dfGet, FALSE];
vft.dfGetBothButton => Command[vft, dfGetBoth, FALSE];
vft.localDeleteButton => Command[vft, localDelete, TRUE];
vft.remoteDeleteButton => Command[vft, remoteDelete, TRUE];
ENDCASE => ERROR;
};

LocalRemoteGet: PROC [self: VFTOps.Handle, cmd: VFTOps.FTPCommand] = {
VFTOps.GetTheFile[self, cmd=dfGetBoth];
NeedCommand[self];
};

StrButtonProc: Buttons.ButtonProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[clientData];
viewer: Buttons.Button ← NARROW[parent];
SELECT viewer FROM
vft.stringFileNames => StartOther[vft.fileNamesBox];
vft.stringLocal => StartOther[vft.localBox];
vft.stringDFFile => StartOther[vft.dfFileBox];
vft.stringConnect => StartOther[vft.connectBox];
vft.stringDirectory => StartOther[vft.directoryBox];
ENDCASE => ERROR;
};

StrCPasswordButtonProc: Buttons.ButtonProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[clientData];
-- PCharProc: TypeScript.CharProc = {
-- SELECT char FROM
-- ' , '\l, '\n, '\177 => RETURN[process: FALSE, activate: TRUE];
-- '\b => RETURN[process: TRUE, activate: FALSE];
-- ENDCASE => {
-- TypeScript.PutChar[ts, '*];
-- RETURN[process: FALSE, activate: FALSE];
-- };
-- };
--IF vft.cPasswordActive THEN RETURN;
--vft.cPasswordActive ← TRUE;
--Buttons.SetDisplayStyle[button: vft.stringCPassword, style: $BlackOnGrey];
ViewerTools.SetContents[vft.cpasswordBox, "Doesn't Work!"];
--ViewerTools.SetSelection[vft.cpasswordBox, NIL];
-- vft.cPasswordRope ← TypeScript.GetLine[ts: vft.cpasswordBox, charProc: PCharProc];
--Buttons.SetDisplayStyle[button: vft.stringCPassword, style: $BlackOnWhite];
--vft.cPasswordActive ← FALSE;
};

StartOther: PROC [v: ViewerClasses.Viewer] = {
IF v#NIL THEN ViewerTools.SetSelection[v, NIL];
};

ConfirmButtonProc: Buttons.ButtonProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[clientData];
viewer: Buttons.Button ← NARROW[parent];
ca: VFTOps.ConfirmAbort ← SELECT viewer FROM
vft.confirmButton => confirm,
vft.denyButton => skip,
vft.stopButton => stop
ENDCASE => ERROR;
ChangeCommandSubwindow[vft, empty];
VFTOps.GiveConfirm[vft, ca];
};

-- Display routines

PostComment: PUBLIC PROC [self: VFTOps.Handle, s: Rope.ROPE] = {
Labels.Set[self.msg, s];
self.log.PutF["%s\n", IO.rope[s]];
};

InvertIndicator: PUBLIC PROC [self: VFTOps.Handle]= {
SELECT self.data.indicator FROM
left => self.data.indicator ← right;
off => self.data.indicator ← left;
right => self.data.indicator ← left;
ENDCASE => ERROR;
ViewerOps.PaintViewer[self.transferBox, client, FALSE];
};

DelayChangeCommandSubwindow: PROC [self: VFTOps.Handle, function: VFTOps.Functions] = {
IF function # self.data.function THEN {
SELECT function FROM
empty => self.commandSW.child ← self.transferChildren;
commands => self.commandSW.child ← self.commandChildren;
confirms => self.commandSW.child ← self.confirmChildren;
options => self.commandSW.child ← self.optionsChildren;
ENDCASE => ERROR;
self.data.function ← function;
self.data.indicator ← off;
Process.Detach[FORK DelayPaintViewer[viewer: self.commandSW]];
};
};

DelayPaintViewer: PROC [viewer: ViewerClasses.Viewer] = {
Process.Pause[Process.SecondsToTicks[1]];
ViewerOps.PaintViewer[viewer: viewer, hint: all];
};

ChangeCommandSubwindow: PROC [self: VFTOps.Handle, function: VFTOps.Functions] = {
IF function # self.data.function THEN {
SELECT function FROM
empty => self.commandSW.child ← self.transferChildren;
commands => self.commandSW.child ← self.commandChildren;
confirms => self.commandSW.child ← self.confirmChildren;
options => self.commandSW.child ← self.optionsChildren;
ENDCASE => ERROR;
self.data.function ← function;
self.data.indicator ← off;
ViewerOps.PaintViewer[viewer: self.commandSW, hint: all];
};
};

SlashToRightAngle: Rope.TranslatorType = TRUSTED { new ← IF old = '/ THEN '> ELSE old; };

Command: PROC [self: VFTOps.Handle, cmd: VFTOps.FTPCommand, useSTP: BOOL] = {

-- stop future button pushes!
ChangeCommandSubwindow[self, empty]; -- command underway;

self.connectNameRope ← ViewerTools.GetContents[self.connectBox];
self.localRope ← Rope.Translate[base: ViewerTools.GetContents[self.localBox], translator: SlashToRightAngle];
self.fileNamesRope ← Rope.Translate[base: ViewerTools.GetContents[self.fileNamesBox], translator: SlashToRightAngle];

IF self.directoryBox.newVersion THEN {
lastCharOK: BOOL;
newHostRope: Rope.ROPE;
c: CHAR;
dirDelimIndex: INT;
path: Rope.ROPE ← ViewerTools.GetContents[self.directoryBox];
pathLength: INT ← path.Length[];
IF pathLength=0 THEN { -- why??
 self.hostRope ← "";
 self.directoryRope ← "";
GOTO FinParsing;
};

-- The last character must be the end of a legal host name or the end of a legal directory name.
c ← path.Fetch[pathLength-1]; -- last char of path
lastCharOK ← c='> OR c='/ OR c=']; -- '] if only host e.g. Alto, with filename in Remote:

-- The first character of the Directory string must be a '[ or a '/
c ← path.Fetch[0];
IF c#'[ AND c#'/ THEN GOTO FNError;

-- Directory is assumed to be a directory, but may not have a terminator already
IF ~lastCharOK THEN {
 -- ONLY a host name
IF c='[ AND Rope.Find[s1: path, s2: ">"] = -1 THEN path ← Rope.Cat[path, "]"]
ELSE path ← Rope.Cat[path, IF c='[ THEN ">" ELSE "/"];
pathLength ← pathLength+1;
lastCharOK ← TRUE;
};
-- At this point, the remote name is in fileNames and the rest is in path
dirDelimIndex ← Rope.Index[s1: path, pos1: 1, s2: IF c='[ THEN "]" ELSE "/"];
IF dirDelimIndex = pathLength THEN GOTO FNError; -- no trailing directory character !
newHostRope ← Rope.Substr[base: path, start: 1, len: dirDelimIndex-1];
  IF (dirDelimIndex + 1) = pathLength THEN self.directoryRope ← NIL
  ELSE {
   IF c='[ THEN { -- old syntax
    IF path.Fetch[dirDelimIndex+1] # '< THEN GOTO FNError;
    self.directoryRope ← Rope.Substr[base: path, start: dirDelimIndex + 2, len: path.Length[] - dirDelimIndex - 3];
    }
  ELSE { -- New syntax
   self.directoryRope ← Rope.Translate[base: Rope.Substr[base: path, start: dirDelimIndex + 1, len: pathLength - dirDelimIndex - 2], translator: SlashToRightAngle];
    };
   };
IF NOT Rope.Equal[s1: newHostRope, s2: self.hostRope, case: FALSE] THEN {
-- Close connection, forcing a re-open with the new host
self.hostRope ← newHostRope;
};
EXITS
FinParsing => NULL;
};
-- there remains to determine what is a source and what is a destination for various cases...
SELECT cmd FROM
remoteDelete => {
self.sourceRope ← self.fileNamesRope;
self.destRope ← NIL;
};
localDelete => {
self.sourceRope ← self.localRope;
self.destRope ← NIL;
};
remoteList => {
self.sourceRope ← IF self.fileNamesRope.Length[]>0 THEN self.fileNamesRope ELSE self.localRope;
self.destRope ← NIL;
};
localList => {
self.sourceRope ← IF self.localRope.Length[]>0 THEN self.localRope ELSE self.fileNamesRope;
self.destRope ← NIL;
};
store => IF self.localRope.Length[]>0 THEN {
self.sourceRope ← self.localRope;
self.destRope ← self.fileNamesRope;
}
ELSE {
self.sourceRope ← self.fileNamesRope;
self.destRope ← NIL;
};
retrieve, dfGet, dfGetBoth => IF self.fileNamesRope.Length[]>0 THEN {
self.sourceRope ← self.fileNamesRope;
self.destRope ← self.localRope;
}
ELSE {
self.sourceRope ← self.localRope;
self.destRope ← NIL;
};
ENDCASE => ERROR;
IF (cmd = store OR cmd = retrieve) AND self.sourceRope.Length[] > 0 AND self.destRope.Length[] > 0 THEN {
  -- Check for multiple rename
  IF (Rope.SkipTo[s: self.sourceRope, skip: "  #*@"] # self.sourceRope.Length[]) OR (Rope.SkipTo[s: self.destRope, skip: "  #*@"] # self.destRope.Length[]) THEN {
   PostComment[self, "Multiple rename not implemented"];
   self.log.PutF["The FileTool does not understand what you want to happen when both the FileNames and Local fields contain something and at least one of them contains either more than one filename, a command file (@) or a pattern.\r"];
   NeedCommand[self];
   RETURN;
   };
  };
Labels.Set[self.msg, " "];
IF useSTP THEN self.data.transferring ← TRUE;
IF cmd = dfGet OR cmd = dfGetBoth THEN LocalRemoteGet[self, cmd]
ELSE [] ← VFTOps.GiveCommand[self, cmd];
EXITS
FNError => {
PostComment[self, "Illegal Directory or Filename!"];
NeedCommand[self];
};
};

NeedCommand: PUBLIC PROC [self: VFTOps.Handle] = {
ChangeCommandSubwindow[self, commands];
self.data.transferring ← FALSE;
};

NeedConfirm: PUBLIC PROC [self: VFTOps.Handle] = {
ChangeCommandSubwindow[self, confirms];
};

-- create world
CreateVFT: Commander.CommandProc = TRUSTED {
CR0, CR1, top: INTEGER;
vft: VFTOps.Handle ← NEW[VFTOps.VFTObject];

-- NO FORK!
MakeButton: PROC [name: Rope.ROPE, proc: Buttons.ButtonProc, x, y: INTEGER, fork: BOOL ← FALSE, doc: Rope.ROPE] RETURNS [Buttons.Button] = {
top ← y;
RETURN[Buttons.Create[ info: [
name: name, wx: x, wy: y, wh: CHT, parent: vft.win, border: FALSE],
proc: proc, clientData: vft, fork: fork, paint: FALSE, documentation: doc]];
};

MakeText: PROC [x, y, w: INTEGER] RETURNS [ViewerClasses.Viewer] = {
top ← y;
RETURN[ViewerOps.CreateViewer[flavor: $Text, info: [
parent: vft.win,
wx: x,
wy: y,
ww: w,
wh: CHT,
border: FALSE], paint: FALSE]];
};

-- FORK
MakeSubButton: PROC [name: Rope.ROPE, proc: Buttons.ButtonProc, x, y: INTEGER, guarded: BOOL ← FALSE, doc: Rope.ROPE] RETURNS [Buttons.Button] = {
top ← y;
RETURN[Buttons.Create[
info: [ name: name, wx: x, wy: y, wh: CHT, parent: vft.commandSW, border: FALSE],
proc: proc,
clientData: vft,
paint: FALSE,
guarded: guarded,
documentation: doc]];
};

vft.bits[off] ← NEW[GraphicsOps.BitmapRep ← [NEW[VFTOps.SmallMap ← ALL[0]], 1, 16, 16]];
vft.bits[left] ← NEW[GraphicsOps.BitmapRep ← [NEW[VFTOps.SmallMap ← VFTOps.LeftSmallMap], 1, 16, 16]];
vft.bits[right] ← NEW[GraphicsOps.BitmapRep ← [NEW[VFTOps.SmallMap ← VFTOps.RightSmallMap], 1, 16, 16]];

vft.win ← ViewerOps.CreateViewer[flavor: $FileTool, info: [name: "File Tool", iconic: TRUE, scrollable: FALSE], paint: TRUE ];

ViewerOps.AddProp[vft.win, $FileToolData, vft];

vft.stop ← Buttons.Create[info: [ name: "Stop! ", wx: 0, wy: 0, wh: MsgHT, parent: vft.win, border: FALSE],
proc: MyStop,
clientData: vft,
fork: TRUE,
paint: FALSE,
guarded: FALSE,
documentation: "Abort transfer"];

vft.msg ← Labels.Create[info: [
name: IO.PutFR["VFT of %t", IO.time[Runtime.GetBcdTime[]]],
parent: vft.win, wx: vft.stop.ww, wy: 0, ww: TWidth-vft.stop.ww, wh: MsgHT,
border: FALSE], paint: FALSE ];

vft.bottomMsgRule ← Rules.Create[info: [parent: vft.win, wx: 0, wy: vft.msg.wy+vft.msg.wh+4, ww: TWidth, wh: 1], paint: FALSE];

vft.stringDirectory ← MakeButton[name: "Directory: ", proc: StrButtonProc, x: PCol0, y: vft.bottomMsgRule.wy+vft.bottomMsgRule.wh+4, doc: "Full remote directory name"];
vft.stringFileNames ← MakeButton[name: "FileName(s): ", proc: StrButtonProc, x: PCol0, y: top+CHT, doc: "Remote file name here"];
vft.stringLocal ← MakeButton[name: "Local: ", proc: StrButtonProc, x: PCol0, y: top+CHT, doc: "Local file name if different from remote name"];
vft.stringDFFile ← MakeButton[name: "DF File: ", proc: StrButtonProc, x: PCol0, y: top+CHT, doc: "DF file name for use by DFGet and DFGetBoth"];
vft.stringConnect ← MakeButton[name: "Connect: ", proc: StrButtonProc, x: PCol0, y: top+CHT, doc: "Connect name, if needed"];

vft.stringCPassword ← MakeButton[name: "Password: ", proc: StrCPasswordButtonProc, x: PCol1, y: vft.stringConnect.wy, fork: TRUE, doc: "Connect password, end with CR"];

vft.directoryBox ← MakeText[
x: vft.stringDirectory.wx+vft.stringDirectory.ww,
y: vft.stringDirectory.wy+BaselineFix,
w: TWidth-(vft.stringDirectory.wx+vft.stringDirectory.ww)
];

vft.fileNamesBox ← MakeText[
x: vft.stringFileNames.wx+vft.stringFileNames.ww,
y: vft.stringFileNames.wy+BaselineFix,
w: PCol2-(vft.stringFileNames.wx+vft.stringFileNames.ww)
];

vft.updateAlwaysButton ← MakeButton[name: "Update a>", proc: UpdateButtonProc, x: PCol2, y: vft.stringFileNames.wy, doc: "Store or retrieve if the file does not exist or if the source file is newer than the destination file"];

vft.localBox ← MakeText[
x: vft.stringLocal.wx+vft.stringLocal.ww,
y: vft.stringLocal.wy+BaselineFix,
w: PCol2-(vft.stringLocal.wx+vft.stringLocal.ww)
];

vft.updateButton ← MakeButton[name: "Update >", proc: UpdateButtonProc, x: PCol2, y: vft.stringLocal.wy, doc: "Store or retrieve only if the destination file already exists, but is older than the source file"];

vft.dfFileBox ← MakeText[
x: vft.stringDFFile.wx+vft.stringDFFile.ww,
y: vft.stringDFFile.wy+BaselineFix,
w: PCol2-(vft.stringDFFile.wx+vft.stringDFFile.ww)
];

vft.exportsOnlyButton ← MakeButton[name: "ExportsOnly", proc: UpdateButtonProc, x: PCol2, y: vft.stringDFFile.wy, doc: "Retrieve only Exported files from DF file"];

vft.connectBox ← MakeText[
x: vft.stringConnect.wx+vft.stringConnect.ww,
y: vft.stringConnect.wy+BaselineFix,
w: PCol1-(vft.stringConnect.wx+vft.stringConnect.ww)
];

vft.cpasswordBox ← TypeScript.Create[info: [
parent: vft.win,
wx: vft.stringCPassword.wx+vft.stringCPassword.ww,
wy: vft.stringCPassword.wy+BaselineFix,
ww: PCol2-(vft.stringCPassword.wx+vft.stringCPassword.ww),
wh: CHT,
border: FALSE], paint: FALSE];

vft.verifyButton ← MakeButton[name: "Verify", proc: UpdateButtonProc, x: PCol2, y: vft.stringConnect.wy, doc: "FileTool will ask for confirmation for each file transferred"];

vft.bottomParamRule ← Rules.Create[info: [parent: vft.win, wx: 0, wy: vft.stringConnect.wy+vft.stringConnect.wh+4, ww: TWidth, wh: 1], paint: FALSE];

vft.commandSW ← ViewerOps.CreateViewer[ flavor: $Container, info: [
parent: vft.win, wx: 0, wy: vft.bottomParamRule.wy+4,
ww: TWidth, wh: SWHeight, scrollable: FALSE,
border: FALSE], paint: FALSE ];

vft.bottomCommandRule ← Rules.Create[info: [parent: vft.win, wx: 0, wy: vft.commandSW.wy+vft.commandSW.wh+4, ww: TWidth, wh: 1], paint: FALSE];

CR0 ← 4;
CR1 ← CR0+CHT;

vft.confirmButton ← MakeSubButton[name: "Confirm!", proc: ConfirmButtonProc, x: CCol0, y: CR0, guarded: FALSE, doc: "Approve transfer"];
vft.denyButton ← MakeSubButton[name: "Deny!", proc: ConfirmButtonProc, x: CCol1, y: CR0, guarded: FALSE, doc: "Reject transfer of this file, but continue"];
vft.stopButton ← MakeSubButton[name: "Stop!", proc: ConfirmButtonProc, x: CCol2, y: CR0, guarded: FALSE, doc: "Reject transfer and stop"];

vft.confirmChildren ← vft.commandSW.child;
vft.commandSW.child ← NIL;

vft.transferBox ← ViewerOps.CreateViewer[ flavor: $Bitmap, info: [
parent: vft.commandSW, wx: 10, wy: 10, ww: 16, wh: 16,
data: vft, scrollable: FALSE, border: FALSE]];

vft.transferChildren ← vft.commandSW.child;
vft.commandSW.child ← NIL;

vft.typeOption ← MakeSubButton[name: "Type!", proc: OptionsButtonProc, x: CCol0, y: CR0, doc: "Show file type (text or binary) if known"];
vft.bytesOption ← MakeSubButton[name: "Bytes!", proc: OptionsButtonProc, x: CCol1, y: CR0, doc: "Show file length in bytes"];
vft.authorOption ← MakeSubButton[name: "Author!", proc: OptionsButtonProc, x: CCol2, y: CR0, doc: "Show last writer of file"];
vft.applyOption ← MakeSubButton[name: "Apply!", proc: OptionsButtonProc, x: CCol4, y: CR0, doc: "Activate current options"];
vft.pagesOption ← MakeSubButton[name: "Pages!", proc: OptionsButtonProc, x: CCol3, y: CR0, doc: "Size of file in Disk pages, local files only"];

vft.createOption ← MakeSubButton[name: "Create!", proc: OptionsButtonProc, x: CCol0, y: CR1, doc: "Show create date"];
vft.writeOption ← MakeSubButton[name: "Write!", proc: OptionsButtonProc, x: CCol1, y: CR1, doc: "Show write date"];
vft.readOption ← MakeSubButton[name: "Read!", proc: OptionsButtonProc, x: CCol2, y: CR1, doc: "Show read date"];
vft.abortOption ← MakeSubButton[name: "Abort!", proc: OptionsButtonProc, x: CCol4, y: CR1, doc: "Restore previous options"];

vft.optionsChildren ← vft.commandSW.child;
vft.commandSW.child ← NIL;

vft.retrieveButton ← MakeSubButton[name: "Retrieve!", proc: CommandButtonProc, x: CCol0, y: CR0, doc: "Retrieve remote file(s) to local disk"];
vft.storeButton ← MakeSubButton[name: "Store!", proc: CommandButtonProc, x: CCol0, y: CR1, doc: "Store local file(s) to remote server"];

vft.localListButton ← MakeSubButton[name: "Local-List!", proc: CommandButtonProc, x: CCol1, y: CR0, doc: "List local files matching pattern"];
vft.remoteListButton ← MakeSubButton[name: "Remote-List!", proc: CommandButtonProc, x: CCol1, y: CR1, doc: "List remote files matching pattern"];

vft.closeButton ← MakeSubButton[name: "Close!", proc: CommandButtonProc, x: CCol2, y: CR1, doc: "Close current Ethernet connection"];
vft.listOptionsButton ← MakeSubButton[name: "List-Options!", proc: CommandButtonProc, x: CCol2, y: CR0, doc: "Display option menu for file listings"];
vft.dfGetButton ← MakeSubButton[name: "DFGet!", proc: CommandButtonProc, x: CCol3, y: CR0, doc: "Get named files from DF file"];
vft.dfGetBothButton ← MakeSubButton[name: "DFGetBoth!", proc: CommandButtonProc, x: CCol3, y: CR1, doc: "Get named .mesa and .bcd files from DF file"];
vft.localDeleteButton ← MakeSubButton[name: "Local-Delete!", proc: CommandButtonProc, x: CCol4, y: CR0, guarded: TRUE, doc: "Delete local file(s), requires confirmation, file names must be in Local field"];
vft.remoteDeleteButton ← MakeSubButton[name: "Remote-Delete!", proc: CommandButtonProc, x: CCol4, y: CR1, guarded: TRUE, doc: "Delete remote file(s), requires confirmation, file names must be in Path or Remote fields"];

vft.commandChildren ← vft.commandSW.child;

top ← vft.bottomCommandRule.wy+4;

vft.ts ← TypeScript.Create[info: [parent: vft.win, wx: 0, wy: top, ww: TWidth, wh: 800 -- ← McGregor did this--, border: FALSE], paint: FALSE];

Containers.ChildXBound[vft.win, vft.bottomMsgRule];
 Containers.ChildXBound[vft.win, vft.bottomParamRule];
 Containers.ChildXBound[vft.win, vft.bottomCommandRule];
 Containers.ChildXBound[vft.win, vft.ts];
 Containers.ChildYBound[vft.win, vft.ts];

[in: vft.logIn, out: vft.log] ← ViewerIO.CreateViewerStreams[name: "FileTool.log", viewer: vft.ts, editedStream: FALSE];
[] ← vft.logIn.SetEcho[NIL];

--ViewerOps.CloseViewer[viewer: vft.win];

vft.cPasswordRope ← "";
ViewerTools.SetContents[vft.cpasswordBox, ""];

SetBoolButton[vft.updateButton, vft.update];
SetBoolButton[vft.updateAlwaysButton, vft.updateAlways];
SetBoolButton[vft.exportsOnlyButton, vft.publiconly];
SetBoolButton[vft.verifyButton, vft.verify];

VFTOps.BuildOuter[vft]; -- create Remote Get stuff

-- TypeScript.PutRope[vft.ts, CommentRope];
-- vftlist ← CONS[vft, vftlist];
};

-- Destroy and Cleanup routines
-- The filetool needs to use something like Event to clean up in the event of a world swap!
-- Remember to apply the cleanup procedure to each active filetool.

FileToolDestroy: ViewerClasses.DestroyProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[ViewerOps.FetchProp[self, $FileToolData]];

vft.task ← VFTOps.FreeTask[vft.task];
-- vftlist ← List.Remove[vft, vftlist];
Process.Detach[FORK MyDestroyInternal[vft]];
-- next line is a crock to avoid signal in TypeScripts, see McGregor
Process.Pause[Process.SecondsToTicks[1]];
};

MyDestroyInternal: PROC [self: VFTOps.Handle] = {
-- vft.log.Close[];
VFTOps.StopFTP[self];
IF self.user # NIL THEN self.user ← STP.Destroy[self.user];
VFTOps.RGDestroy[self]; -- remote get stuff
self.log.Flush[];
};

MyStop: Buttons.ButtonProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[clientData];
IF vft=NIL THEN RETURN;
VFTOps.StopFTP[vft];
};

Init: PROC = {
fileToolClass.destroy ← FileToolDestroy;
fileToolClass.icon ← fileCabinet;
ViewerOps.RegisterViewerClass[$FileTool, fileToolClass];
ViewerOps.RegisterViewerClass[$Bitmap, bitmapClass];
Commander.Register[
key: "FileTool",
proc: CreateVFT,
doc: "Tool for manipulating local and remote files"];
[] ← CreateVFT[NIL];
};

BitmapPaint: ViewerClasses.PaintProc = TRUSTED {
vft: VFTOps.Handle ← NARROW[self.data];
Graphics.SetCP[context, 0, 16];
GraphicsOps.DrawBitmap[context, vft.bits[vft.data.indicator], 16, 16];
};

-- MAINLINE CODE

Init[];

END.
-- Phil Karlton; 16-Mar-81 12:26:37
-- Mark; 12-Mar-81 19:53:13
16-Jan-82 15:59:28, Stewart, created from FileInterface.mesa
25-Jan-82 23:39:50, Stewart, objects
March 27, 1982 8:18 pm, Stewart, Cedar 3.0 cleanup & bug fixing
April 24, 1982 4:44 pm, Stewart, Cedar 3.0
June 7, 1982 7:13 pm, Stewart, Cedar 3.2, Menus
July 3, 1982 7:53 pm, Stewart, Cedar 3.2 bug fixes before release
September 16, 1982 10:33 am, Stewart, Cedar 3.4
March 28, 1983 3:18 pm, Stewart, Commander, remove vftList

FileTool notes

Ideas for improvements:

VTables style sets of filenames
Menus for list options and commands instead of subwindow
Check for multiple rename
Incorporate official Cedar pattern matcher and use CIFS
Provide programmer's interface (Filetool should just be a forms-fill-out user interface for the file moving and listing machinery.)