-- YodelRoot.mesa
-- Yodel: Button-style interface
-- Last Edited by: Hagmann, January 23, 1985 3:48:55 pm PST
Rhagmann, January 24, 1985 8:18:07 am PST

DIRECTORY
AlpineEnvironment USING[ Property],
AlpineFile  USING [ PropertySet],
Atom    USING[ GetPName ],
Buttons   USING[ Button, ButtonProc, Create, Destroy, SetDisplayStyle ],
Commander  USING[ CommandProc, Register ],
Containers  USING[ ChildXBound, ChildYBound, Create ],
IO,
Labels   USING[ Create ],
MBQueue  USING[ Create, CreateButton, Queue ],
Rope,
Rules    USING[ Create ],
TypeScript  USING[ Create ],
UserCredentials USING[ Get ],
ViewerClasses USING[ Viewer ],
ViewerIO   USING[ CreateViewerStreams ],
ViewerOps  USING[ ComputeColumn, DestroyViewer, MoveViewer, SetOpenHeight ],
ViewerTools  USING[ GetContents, GetSelectedViewer,
      MakeNewTextViewer, SetContents, SetSelection],
YodelData;
YodelRoot: CEDAR MONITOR LOCKS d USING d: MyData
IMPORTS Atom, Buttons, Commander, Containers, IO, Labels, MBQueue, Rope, Rules, TypeScript, UserCredentials, ViewerIO, ViewerOps, ViewerTools, YodelData
EXPORTS YodelData

=
BEGIN OPEN YodelData ;
NamePrefixes: LIST OF ROPE = LIST[
"Luther.Alpine",
"Ebbetts.Alpine",
"Ivy",
"Indigo",
""
];
PropertySetToRopeArray: PUBLIC ARRAY [0..NumberOfAlpineProperties) OF PropertySetToRope ← [
[byteLength, "byteLength"],
[createTime, "createTime"],
[highWaterMark, "highWaterMark"],
[modifyAccess, "modifyAccess"],
[owner, "owner"],
[readAccess, "readAccess"],
[stringName, "stringName"],
[version, "version"]
];
******** Enquiry operations ******** --
ROPE: TYPE = Rope.ROPE;
Create: Commander.CommandProc =
[exec: ExecHandle, clientData: REF ANY ← NIL] RETURNS[ok: BOOLEAN ← TRUE]
BEGIN
d: MyData = NEW[MyDataObject ];
this is the container that includes all the other containers
v: ViewerClasses.Viewer = Containers.Create[
info: [name: "Yodel", column: left, scrollable: FALSE, iconic: TRUE]];
BEGIN
kludge to find max button sizes! --
temp1: Buttons.Button = Buttons.Create[
info: [name: "Dest Server:", parent: v, border: FALSE,
wx: 0, wy: 0],
proc: NIL, clientData: d, fork: FALSE, paint: FALSE];
temp2: Buttons.Button = Buttons.Create[
info: [name: "highWaterMark", parent: v, border: FALSE,
wx: 0, wy: 0],
proc: NIL, clientData: d, fork: FALSE, paint: FALSE];

d.q ← MBQueue.Create[];
d.maxW ← temp1.ww;
d.buttH ← temp1.wh;
d.maxBW ← temp2.ww;
Buttons.Destroy[temp1];
Buttons.Destroy[temp2];
END;
start displaying only these two properties
d.displayProperties[byteLength] ← TRUE;
d.displayProperties[createTime] ← TRUE;
build viewer for "Level" menu
d.topChild ← CreateSelector[name: "Level:",
values: LIST[$User, $FileProperties, $OwnerProperties, $Administrator, $STOP],
change: ChangeLevel,
clientData: d,
viewer: v,
x: 2, y: 1].child;
create type script; it will be bound at the end of CreateButtons
under all the menus
d.script ← TypeScript.Create[
info: [parent: v, wh: v.ch - (d.topChild.wy + d.topChild.wh + 2), ww: v.cw,
border: FALSE,
wy: d.topChild.wy + d.topChild.wh + 2, wx: 0] ];
Containers.ChildXBound[v, d.script];
Containers.ChildYBound[v, d.script];
[in: d.in, out: d.out] ← ViewerIO.CreateViewerStreams[NIL, d.script];
CreateButtons[d, v];
END;
ChangeLevel: PROC[parent: ViewerClasses.Viewer, clientData: REF ANY, value: ATOM] =
BEGIN
d: MyData = NARROW[clientData];
p: ViewerClasses.Viewer = NARROW[parent];
SELECT value FROM
$User => d.level ← user;
$FileProperties => d.level ← fileProperties;
$OwnerProperties => d.level ← ownerProperties;
$Administrator => d.level ← administrator;
$STOP => {
d.stopFlag ← TRUE ;
RETURN;
};
ENDCASE => {
d.out.PutRope["\nNot Implemented\n"];
};
CreateButtons[d, parent]
END;
build an image from the data in parameter d
CreateButtons: PUBLIC ENTRY PROC[d: MyData, parent: ViewerClasses.Viewer] =
BEGIN
child: ViewerClasses.Viewer ← NIL;
EnquiryButton: PROC[q: MBQueue.Queue, name: Rope.ROPE, proc: Buttons.ButtonProc, width: INTEGER ← d.maxW,
guarded: BOOLFALSE, doc:Rope.ROPENIL] =
BEGIN
IF q # NIL THEN child ← q.CreateButton[
info: [name: name, parent: kids, border: TRUE,
wy: child.wy, wx: child.wx + width - 1, ww: width],
proc: proc,
clientData: d,
fork: TRUE,
paint: FALSE,
guarded: guarded,
documentation: doc]
ELSE child ← Buttons.Create[
info: [name: name, parent: kids, border: TRUE,
wy: child.wy, wx: child.wx + width - 1, ww: width],
proc: proc,
clientData: d,
fork: TRUE,
paint: FALSE,
guarded: guarded,
documentation: doc];
END;
LabelText: PROC[q: MBQueue.Queue, name, data: Rope.ROPE, prev: ViewerClasses.Viewer,
width: INTEGER ← d.maxW, textWidth: INTEGER ← 2*d.maxW, newline: BOOLTRUE,
textLabelProc: Buttons.ButtonProc ← OtherTextLabelProc]
RETURNS[ViewerClasses.Viewer] =
BEGIN
x: INTEGER = IF newline THEN 2 ELSE child.wx + width + (textWidth) - 1;
y: INTEGER = IF newline THEN child.wy + child.wh + 1 ELSE child.wy;
child ← ViewerTools.MakeNewTextViewer[
info: [parent: kids, wh: d.buttH, ww: width + (textWidth), scrollable: FALSE,
data: IF prev = NIL THEN data ELSE ViewerTools.GetContents[prev],
border: FALSE,
wx: x + width + 2, wy: y],
paint: FALSE ];
[] ← q.CreateButton[
info: [name: name, parent: kids, wh: d.buttH,
border: FALSE, wx: x, wy: y],
proc: textLabelProc, clientData: child, fork: FALSE, paint: FALSE];
RETURN[child]
END;
Label: PROC[name: Rope.ROPE] =
BEGIN
child ← Labels.Create[
info: [name: name, parent: kids, border: FALSE,
wy: child.wy + child.wh + (IF child.class.flavor = $Button THEN -1 ELSE 2),
wx: 2],
paint: FALSE ];
END;
Rule: PROC =
BEGIN
child ← Rules.Create[
info: [parent: kids, border: FALSE,
wy: IF child = NIL THEN 0 ELSE child.wy + child.wh + 2, wx: 0, ww: kids.ww, wh: 1],
paint: FALSE ];
Containers.ChildXBound[kids, child];
END;
userButtons: PROC = BEGIN
Label["Command: "];
EnquiryButton[q: d.q, name: "List", proc: ListFilesProc];
EnquiryButton[q: d.q, name: "Delete", proc: DeleteFilesProc, guarded: TRUE,
doc: "Delete requires confirmation"];
EnquiryButton[q: d.q, name: "Copy", proc: CopyFilesProc];
EnquiryButton[q: d.q, name: "FullCopy", proc: FullCopyFilesProc];
EnquiryButton[q: d.q, name: "Options", proc: OptionsProc];
IF d.displayOptions THEN BEGIN
buttonCount: INTEGER ← 0 ;
count: INT;
option: AlpineEnvironment.Property;
propertyName: ROPE ;
Rule[];
Label["Option: "];
FOR count IN [0..NumberOfAlpineProperties) DO
[option, propertyName] ← PropertySetToRopeArray[count];
IF (buttonCount >= 4) THEN BEGIN
Label[""];
buttonCount ← 0 ;
END;
buttonCount ← buttonCount + 1 ;
EnquiryButton[q: NIL, name: propertyName, proc: ChangeOptionsProc, width: d.maxBW];
IF d.displayProperties[option] THEN BEGIN
Buttons.SetDisplayStyle[child, $WhiteOnBlack];
END;
ENDLOOP;
END;
END;
filePropertiesButtons: PROC =
BEGIN
Label["Function: "];
EnquiryButton[q: d.q, name: "Examine", proc: ExamineProc];
EnquiryButton[q: d.q, name: "Apply", proc: ApplyProc, guarded: TRUE,
doc: "Apply requires confirmation; single file application only"];
Rule[];
d.oStringName ← LabelText[
q: d.q,
name: "stringName:",
data: "",
prev: d.oStringName,
width: d.maxBW,
textWidth: 5*d.maxW];
d.oByteLength ← LabelText[
q: d.q,
name: "byteLength:",
data: "",
prev: d.oByteLength,
width: d.maxBW ];
d.oSize ← LabelText[
q: d.q,
name: "size:",
data: "",
prev: d.oSize,
width: d.maxBW,
newline: FALSE ];
d.oHighWaterMark ← LabelText[
q: d.q,
name: "highWaterMark:",
data: "",
prev: d.oHighWaterMark,
width: d.maxBW ];
d.oOwner ← LabelText[
q: d.q,
name: "owner:",
data: "",
prev: d.oOwner,
width: d.maxBW,
newline: FALSE ];
d.oReadAccess ← LabelText[
q: d.q,
name: "readAccess:",
data: "",
prev: d.oReadAccess,
width: d.maxBW,
textWidth: 5*d.maxW];
d.oModifyAccess ← LabelText[
q: d.q,
name: "modifyAccess:",
data: "",
prev: d.oModifyAccess,
width: d.maxBW,
textWidth: 5*d.maxW];

END;
ownerPropertiesButtons: PROC =
BEGIN
Label["Function: "];
EnquiryButton[q: d.q, name: "Quota", proc: QuotaProc];
EnquiryButton[q: d.q, name: "Get", proc: GetOwnerPropertiesProc];
EnquiryButton[q: d.q, name: "Put", proc: PutOwnerPropertiesProc, guarded: TRUE,
doc: "Put owner properties requires confirmation"];
Rule[];
d.oCreateAccessList ← LabelText[
q: d.q,
name: "createAccess:",
data: "",
prev: d.oCreateAccessList,
width: 2*d.maxW,
textWidth: 5*d.maxW];
d.oRootReadAccess ← LabelText[
q: d.q,
name: "root readAccess:",
data: "",
prev: d.oRootReadAccess,
width: 2*d.maxW,
textWidth: 5*d.maxW];
d.oRootModifyAccess ← LabelText[
q: d.q,
name: "root modifyAccess:",
data: "",
prev: d.oRootModifyAccess,
width: 2*d.maxW,
textWidth: 5*d.maxW];

END;
administratorButtons: PROC = {
Label["Command: "];
EnquiryButton[q: d.q, name: "CreateOwner", proc: CreateOwnerProc, width: d.maxBW, guarded: TRUE,
doc: "Create requires confirmation"];
EnquiryButton[q: d.q, name: "DestroyOwner", proc: DestroyOwnerProc, width: d.maxBW,
guarded: TRUE, doc: "Destroy requires confirmation"];
EnquiryButton[q: d.q, name: "WriteQuota", proc: WriteQuotaProc, width: d.maxBW,
guarded: TRUE, doc: "Write Quota requires confirmation"];
EnquiryButton[q: d.q, name: "ListOwners", proc: ListOwnersProc, width: d.maxBW];
EnquiryButton[q: d.q, name: "ReadStatistics", proc: ReadDBPropertiesProc, width: d.maxBW];
Rule[];
d.oQuota ← LabelText[
q: d.q,
name: "quota:",
data: "",
prev: d.oQuota,
width: d.maxBW,
textWidth: 5*d.maxW];
Rule[];
Label["Option: "];
EnquiryButton[q: NIL, name: "Assert Wheel", proc: ChangeAssertWheel, width: d.maxBW];
IF d.assertWheel THEN BEGIN
Buttons.SetDisplayStyle[child, $WhiteOnBlack];
END;
EnquiryButton[q: NIL, name: "Break Locks", proc: ChangeBreakLocks, width: d.maxBW];
IF d.breakLocks THEN {
Buttons.SetDisplayStyle[child, $WhiteOnBlack];
};
};
kids: ViewerClasses.Viewer = Containers.Create[
info: [parent: parent, border: FALSE, scrollable: FALSE, wx: 0, wy: -9999, ww: 9999, wh: 0] ];
userName, userPassword: ROPE;
start of body of CreateButtons
Containers.ChildXBound[parent, kids];
Rule[];
put up user/password on a line
[userName, userPassword] ← UserCredentials.Get[];
d.user ← LabelText[
q: d.q,
name: "User:",
data: userName,
prev: d.user ];
d.password ← LabelText[
q: d.q,
name: "Password:",
data: "",
prev: d.password,
newline: FALSE ];
ask for source and destination servers
d.srcServer ← LabelText[
q: d.q,
name: "Src Server:",
data: "Luther.alpine",
prev: d.srcServer,
textLabelProc: ServerTextLabelProc ];
IF d.level = user THEN {
d.destServer ← LabelText[
q: d.q,
name: "Dest Server:",
data: "",
prev: d.destServer,
newline: FALSE,
textLabelProc: ServerTextLabelProc ];
}
ELSE d.destServer ← NIL ;
ask for source and destination filenames
d.srcFile ← LabelText[
q: d.q,
name: "Src File:",
data: "",
prev: d.srcFile ];
IF d.level = user THEN {
d.destFile ← LabelText[
q: d.q,
name: "Dest File:",
data: "",
prev: d.destFile,
newline: FALSE ]
}
ELSE d.destFile ← NIL ;
IF d.level = user THEN userButtons[];
IF d.level = fileProperties THEN filePropertiesButtons[]
ELSE {
d.oStringName ← d.oByteLength ← d.oHighWaterMark ← d.oSize ← d.oReadAccess ← d.oModifyAccess ← d.oOwner ← NIL;
} ;
IF d.level = ownerProperties THEN ownerPropertiesButtons[] ELSE {
d.oCreateAccessList ← NIL ;
d.oRootReadAccess ← NIL ;
d.oRootModifyAccess ← NIL ;
};
IF d.level = administrator THEN administratorButtons[] ELSE d.oQuota ← NIL;
Rule[];
BEGIN
kidsY: INTEGER = d.topChild.wy + d.topChild.wh + 2;
kidsH: INTEGER = child.wy + child.wh + 2;
IF d.kids # NIL THEN ViewerOps.DestroyViewer[d.kids, FALSE];
d.kids ← kids;
ViewerOps.MoveViewer[viewer: d.script, x: 0, y: kidsY + kidsH, w: d.script.ww,
h: parent.ch - (kids.wy + kidsH),
paint: FALSE];
ViewerOps.SetOpenHeight[parent, kidsY + kidsH + 8 * d.buttH];
IF NOT parent.iconic THEN ViewerOps.ComputeColumn[parent.column];
ViewerOps.MoveViewer[viewer: kids, x: kids.wx, y: kidsY, w: kids.ww, h: kidsH];
END;

END;

ServerTextLabelProc: Buttons.ButtonProc =
parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL
BEGIN
text: ViewerClasses.Viewer = NARROW[clientData];
SELECT mouseButton FROM
red => {
IF ViewerTools.GetSelectedViewer[] = text THEN {
  NextName: PROC [this: ROPE, list: LIST OF ROPE] RETURNS [next: ROPENIL] = {
   IF list = NIL THEN RETURN;
   FOR l: LIST OF ROPE ← list, l.rest UNTIL l = NIL DO
    IF this.Equal[l.first, FALSE] THEN RETURN[IF l.rest = NIL THEN list.first ELSE
     l.rest.first]
    ENDLOOP;
   RETURN[list.first]
   };
  contents: ROPE ← ViewerTools.GetContents[text];
  ViewerTools.SetContents[text, contents ← NextName[contents,
   NamePrefixes]];
  
  };
 ViewerTools.SetSelection[text, NIL];
 };
blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] };
yellow => NULL;
ENDCASE => ERROR;
END;
OtherTextLabelProc: Buttons.ButtonProc =
parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL
BEGIN
text: ViewerClasses.Viewer = NARROW[clientData];
SELECT mouseButton FROM
red => ViewerTools.SetSelection[text, NIL];
blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] };
yellow => NULL;
ENDCASE => ERROR;
END;

Selector: TYPE = REF SelectorRec;

SelectorRec: TYPE = RECORD[
value: REF ATOM,
change: PROC[parent: ViewerClasses.Viewer, clientData: REF ANY, value: ATOM],
clientData: REF ANY,
buttons: LIST OF Buttons.Button,
values: LIST OF ATOM ];

CreateSelector: PROC[name: Rope.ROPE,
values: LIST OF ATOM,
init: REF ATOMNIL,
change: PROC[parent: ViewerClasses.Viewer, clientData: REF ANY,
value: ATOM] ← NIL,
clientData: MyData,
viewer: ViewerClasses.Viewer,
x, y: INTEGER]
RETURNS[child: ViewerClasses.Viewer, value: REF ATOM] =
BEGIN
selector: Selector ← NEW[ SelectorRec ←
[value: IF init # NIL THEN init ELSE NEW[ATOM←values.first],
change: change,
clientData: clientData,
buttons: NIL,
values: values ] ];
last: LIST OF Buttons.Button ← NIL;
value ← selector.value;
child ← Labels.Create[info: [name: name, parent: viewer, border: FALSE, wx: x, wy: y] ];
FOR a: LIST OF ATOM ← values, a.rest UNTIL a = NIL
DO
IF a.first = $STOP THEN child ← Buttons.Create[
info: [name: Atom.GetPName[a.first], parent: viewer, border: TRUE,
wy: child.wy, wx: child.wx + child.ww + 2],
proc: SelectorProc,
clientData: selector,
fork: TRUE,
paint: TRUE]
ELSE child ← clientData.q.CreateButton[
info: [name: Atom.GetPName[a.first], parent: viewer, border: TRUE,
wy: child.wy, wx: child.wx + child.ww + 2],
proc: SelectorProc,
clientData: selector,
fork: TRUE,
paint: TRUE];
IF last = NIL
THEN last ← selector.buttons ← CONS[first: child, rest: NIL]
ELSE { last.rest ← CONS[first: child, rest: NIL]; last ← last.rest };
IF a.first = selector.value^ THEN Buttons.SetDisplayStyle[child, $WhiteOnBlack];
ENDLOOP;
END;
SelectorProc: Buttons.ButtonProc =
parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton, shift, control: BOOL
BEGIN
self: Buttons.Button = NARROW[parent];
selector: Selector = NARROW[clientData];
buttons: LIST OF Buttons.Button ← selector.buttons;
changeHighlight: BOOL = NOT Rope.Equal[self.name,"STOP"];
FOR a: LIST OF ATOM ← selector.values, a.rest UNTIL a = NIL
DO IF self = buttons.first
THEN BEGIN
selector.value^ ← a.first;
IF selector.change # NIL THEN selector.change[self.parent, selector.clientData, a.first];
IF changeHighlight THEN Buttons.SetDisplayStyle[buttons.first, $WhiteOnBlack];
END
ELSE IF changeHighlight THEN Buttons.SetDisplayStyle[buttons.first, $BlackOnWhite];
buttons ← buttons.rest;
ENDLOOP;
END;

Commander.Register[key: "Yodel", proc: Create,
doc: "Performs Chat-like functions for Alpine File Servers"];

END.