PrintDir.mesa;
Last Edited by: Sweet, October 8, 1984 11:08:58 am PDT
DIRECTORY
Ascii,
BasicTime,
Buttons,
Commander USING [CommandProc, Register],
Containers USING [ChildXBound, ChildYBound, Container, Create],
Convert,
FS,
IO,
MessageWindow,
RefText,
Rope,
PrintDirDefs,
Rules USING [Create, Rule],
SafeStorage,
SirPress,
TSFont,
TSTypes,
TypeScript,
VFonts,
ViewerClasses USING [Viewer, ViewerClassRec],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [CreateViewer, PaintViewer],
ViewerTools USING [GetContents, MakeNewTextViewer, SetSelection];
PrintDir: CEDAR PROGRAM    
IMPORTS BasicTime, Buttons, Commander, Containers, Convert, FS, IO, MessageWindow, RefText, Rope, Rules, SafeStorage, SirPress, TSFont, TSTypes, TypeScript, VFonts, ViewerIO, ViewerOps, ViewerTools =
BEGIN OPEN PrintDirDefs;
The Containers interface is used to create an outer envelope or "container" for the different sections below. For uniformity, we define some standard distances between entries in the tool.
entryHeight: CARDINAL = 15; -- how tall to make each line of items
entryVSpace: CARDINAL = 8;  -- vertical leading space between lines
entryHSpace: CARDINAL = 10;  -- horizontal space between items in a line
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
dash: CHAR = Ascii.ControlV;
Problem: ERROR = CODE;
MakeTool: Commander.CommandProc = BEGIN
rule: Rules.Rule;
my: Handle ← NEW[MyRec];
my.outer ← Containers.Create[[-- construct the outer container
name: "Directory Printer", -- name displayed in the caption
iconic: TRUE,   -- so tool will be iconic (small) when first created
column: left,    -- initially in the left column
scrollable: FALSE ]];  -- inhibit user from scrolling contents
MakeCommands[my];  -- build each (sub)viewer in turn
rule ← Rules.Create [[parent: my.outer, wy: my.height, ww: my.outer.cw, wh: 2]];
Containers.ChildXBound[my.outer, rule];
my.height ← my.height + entryHeight + 2; -- interline spacing
MakeTypescript[my];
ViewerOps.PaintViewer[my.outer, all];    -- reflect above change
END;
faceNormal: CARDINAL = 0;
faceItalic: CARDINAL = 1;
faceBold: CARDINAL = 2;
faceBoldItalic: CARDINAL = 3;
LookupFonts: PROC [handle: Handle] = {
DoFont: PROC [class: FontClass, family: ROPE, size: INT, face: CARDINAL ← faceNormal] = {
handle.fontCode[class] ← handle.press.GetFontCode[
family: family,
size: size,
face: face];
handle.fontInfo[class] ← TSFont.Lookup[
(SELECT face FROM
faceItalic => Rope.Concat[family, "I"],
faceBold => Rope.Concat[family, "B"],
faceBoldItalic => Rope.Concat[family, "BI"],
ENDCASE => family),
TSTypes.IntDimn[size, TSTypes.bp]];
};
DoFont[body, "TimesRoman", handle.dim.fontSize];
DoFont[bodyBold, "TimesRoman", handle.dim.fontSize, faceBold];
DoFont[bodyItalic, "TimesRoman", handle.dim.fontSize, faceItalic];
DoFont[bodyBoldItalic, "TimesRoman", handle.dim.fontSize + 1, faceBoldItalic];
DoFont[headingLarge, "TimesRoman", 12];
DoFont[headingSmall, "TimesRoman", 10];
DoFont[pageNum, "TimesRoman", 10];
DoFont[date, "Helvetica", 10];
DoFont[tabs, "Helvetica", handle.dim.tabHeight];
DoFont[display, "TimesRoman", handle.dim.displayHeight, faceBold];
DoFont[symbols, "Math", 10];
DoFont[campaign, "Gacha", handle.dim.fontSize];
DoFont[comment, "Helvetica", 8, faceItalic];
};
MakeTypescript: PROC [handle: Handle] = BEGIN
handle.height ← handle.height + entryVSpace; -- space down from the top of the viewer
handle.ts ← TypeScript.Create[
info: [name: "PrintDir.ts", wy: handle.height, parent: handle.outer, border: FALSE ]];
[handle.tsIn, handle.tsOut] ← ViewerIO.CreateViewerStreams [
name: "PrintDir.ts", viewer: handle.ts, backingFile: "PrintDir.ts", editedStream: FALSE];
Containers.ChildXBound[handle.outer, handle.ts];
Containers.ChildYBound[handle.outer, handle.ts];
END;
MakeCommands: PROC [handle: Handle] = BEGIN
initialData: Rope.ROPE = NIL;
wx: INT ← 0;
NewLine: PROC = {handle.height ← handle.height + entryHeight + entryVSpace; wx ← 0};
LabeledItem: PROC [label: ROPE, width: INT, data: ROPENIL]
RETURNS [v: ViewerClasses.Viewer] = {
ph: PromptHandle ← NEW [PromptRec ← [handle: handle]];
t: Buttons.Button ← Buttons.Create[
info: [
name: Rope.Concat[label, ":"],
wy: handle.height,
default the width so that it will be computed for us --
wh: entryHeight, -- specify rather than defaulting so line is uniform
wx: wx,
parent: handle.outer,
border: FALSE ],
proc: Prompt,
clientData: ph]; -- this will be passed to our button proc
wx ← wx + t.ww + entryHSpace;
v ← ViewerTools.MakeNewTextViewer[ [
parent: handle.outer,
wx: wx,
wy: handle.height,
ww: width*VFonts.CharWidth['0],
wh: entryHeight,
data: data,
scrollable: FALSE,
border: FALSE]];
ph.viewer ← v;
wx ← wx + v.ww + entryHSpace};
Cmd: PROC [label: ROPE, proc: Buttons.ButtonProc] = {
t: Buttons.Button ← Buttons.Create[
info: [
name: label,
wx: wx,
wy: handle.height,
default the width so that it will be computed for us --
wh: entryHeight, -- specify rather than defaulting so line is uniform
parent: handle.outer,
border: TRUE ],
proc: proc,
clientData: handle]; -- this will be passed to our button proc
wx ← wx + t.ww + entryHSpace};
Bool: PROC [label: ROPE, initial: BOOL] RETURNS [flag: REF BOOL] = {
t: Buttons.Button;
flag ← NEW[BOOL ← initial];
t ← Buttons.Create[
info: [
name: label,
wx: wx,
wy: handle.height,
default the width so that it will be computed for us --
wh: entryHeight, -- specify rather than defaulting so line is uniform
parent: handle.outer,
border: TRUE ],
proc: ToggleBool,
clientData: flag]; -- this will be passed to our button proc
Buttons.SetDisplayStyle[
button: t, style: IF initial THEN $WhiteOnBlack ELSE $BlackOnWhite, paint: FALSE];
wx ← wx + t.ww + entryHSpace};
NewLine[];
Cmd["Print", DoIt];
NewLine[];
handle.cmd.inputFile ← LabeledItem["input", 50, "///FUMC/"];
NewLine[];
handle.cmd.pressFile ← LabeledItem["press", 50, "///FUMC/"];
NewLine[];
handle.cmd.heading ← LabeledItem["heading", 50];
NewLine[];
handle.actualLevel ← Bool["actualLevel", FALSE];
handle.mailingOnly ← Bool["mailing only", FALSE];
handle.flagCallers ← Bool["flag callers", FALSE];
handle.callersOnly ← Bool["callers only", FALSE];
handle.cmd.callers ← LabeledItem["callers", 40, "///FUMC/CallerNames.txt"];
NewLine[];
handle.cmd.firstRecruiter ← LabeledItem["first Recruiter #", 5, "2"];
handle.cmd.lastRecruiter ← LabeledItem["last Recruiter #", 5, "12"];
NewLine[];
handle.duplex ← Bool["duplex", FALSE];
handle.cmd.yLead ← LabeledItem["paraLead", 5, "8"];
handle.cmd.fontSize ← LabeledItem["fontSize", 5, "10"];
handle.cmd.lineHeight ← LabeledItem["lineHeight", 5, "12"];
NewLine[];
handle.separatePages ← Bool["separatePages", FALSE];
handle.showCallers ← Bool["showCallers", FALSE];
handle.alphaSort ← Bool["alphaSort", TRUE];
handle.zipSort ← Bool["zipSort", FALSE];
handle.ageSort ← Bool["ageSort", FALSE];
handle.dinnerSort ← Bool["dinnerSort", FALSE];
NewLine[];
handle.cmd.topMargin ← LabeledItem["topMargin", 5, "72"];
handle.cmd.bottomMargin ← LabeledItem["bottomMargin", 5, "66"];
handle.cmd.leftMargin ← LabeledItem["leftMargin", 5, "72"];
handle.cmd.rightMargin ← LabeledItem["rightMargin", 5, "72"];
NewLine[];
handle.cmd.displayHeight ← LabeledItem["displayFontSize", 5, "18"];
handle.cmd.tabHeight ← LabeledItem["indexFontSize", 5, "8"];
handle.cmd.nameIndent ← LabeledItem["nameIndent", 5, "18"];
NewLine[];
END;
Prompt: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE] -- = BEGIN
force the selection into the user input field
ph: PromptHandle ← NARROW[clientData];
ViewerTools.SetSelection[ph.viewer];  -- force the selection
END;
ToggleBool: Buttons.ButtonProc = {
switch: REF BOOLNARROW [clientData];
switch^ ← ~switch^;
Buttons.SetDisplayStyle[
button: NARROW[parent],
style: IF switch^ THEN $WhiteOnBlack ELSE $BlackOnWhite];
};
DoIt: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY ← NIL, mouseButton: Menus.MouseButton ← red, shift: BOOL ← FALSE, control: BOOL ← FALSE] -- = BEGIN
force the selection into the user input field
handle: Handle ← NARROW[clientData]; -- get our data
BEGIN
ENABLE {
UNWIND => {IF handle.in # NIL THEN handle.in.Close[]; handle.in ← NIL};
Problem, ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}};
fName: ROPE = ViewerTools.GetContents[handle.cmd.inputFile];
pName: ROPE = ViewerTools.GetContents[handle.cmd.pressFile];
Val: PROC [v: ViewerClasses.Viewer, default: INT] RETURNS [n: INT] = {
n ← Convert.IntFromRope[ViewerTools.GetContents[v] !
SafeStorage.NarrowFault => {n ← default; GO TO gub};
Convert.Error => {
MessageWindow.Append[message: "invalid number", clearFirst: TRUE];
n ← default; GO TO gub};
];
EXITS
gub => NULL;
};
IF fName = NIL OR pName = NIL THEN {
handle.tsOut.Put[[rope["specify input file name"]], [character['\n]]];
RETURN};
handle.in ← OpenFile[fName];
IF handle.in # NIL THEN handle.eof ← FALSE; handle.pageNumber ← 1;
IF pName # NIL AND Rope.Length[pName] # 0 THEN {
nameAddr, fumcWidth: INT;
handle.heading ← ViewerTools.GetContents[handle.cmd.heading];
handle.out ← FS.StreamOpen[fileName: pName, accessOptions: $create];
handle.press ← SirPress.Create[outputStream: handle.out, fileNameForHeaderPage: pName];
handle.date ← Convert.RopeFromTime[BasicTime.Now[], $years, $days];
handle.dim.yLead ← Val[handle.cmd.yLead, 8];
handle.dim.fontSize ← Val[handle.cmd.fontSize, 10];
handle.dim.lineHeight ← Val[handle.cmd.lineHeight, 12];
handle.dim.topMargin ← Val[handle.cmd.topMargin, 72];
handle.dim.bottomMargin ← Val[handle.cmd.bottomMargin, 60];
handle.dim.leftMargin ← Val[handle.cmd.leftMargin, 54];
handle.dim.rightMargin ← Val[handle.cmd.rightMargin, 72];
handle.dim.displayHeight ← Val[handle.cmd.displayHeight, 18];
handle.dim.tabHeight ← Val[handle.cmd.tabHeight, 8];
handle.dim.tabHeight ← Val[handle.cmd.tabHeight, 8];
handle.dim.firstRecruiter ← Val[handle.cmd.firstRecruiter, 18];
handle.dim.lastRecruiter ← Val[handle.cmd.lastRecruiter, 18];
handle.press.SetPageSize[110, 85];
LookupFonts[handle];
handle.yTop ← pageHeight - handle.dim.topMargin;
handle.callerRight ← handle.dim.leftMargin - PWidth[handle, "mmmm ", campaign] - 4;
handle.phoneX ← handle.dim.leftMargin;
handle.phoneX2 ← handle.phoneX + PWidth[handle, "(415) ", body];
handle.nameX ← handle.phoneX + PWidth[handle, "(415) 321-9039", body] + 12;
handle.zipX ← pageWidth - handle.dim.rightMargin - PWidth[handle, "94303", body];
handle.townX ← handle.zipX - PWidth[handle, "EPA", body] - 9;
nameAddr ← handle.townX - handle.nameX;
handle.addrX ← handle.nameX + (6*nameAddr)/10;
handle.callerX ← handle.addrX;
handle.recruiterX ← handle.callerX + (pageWidth - handle.dim.rightMargin - handle.callerX)/2;
handle.addrPWidth ← handle.townX - handle.addrX;
fumcWidth ←
PWidth[handle, handle.heading, headingLarge];
handle.fumcX ← CenterX[handle, fumcWidth];
handle.dateX ← RightX[handle, PWidth[handle, handle.date, date]];
handle.prevInitial ← 0C;
handle.prevZip ← NIL;
handle.firstOnPage ← NIL; handle.affiliateThisPage ← FALSE;
IF handle.callersOnly^ AND handle.callerName = NIL THEN
handle.callerName ← ParseCallerNames[handle];
}
ELSE handle.press ← NIL;
handle.tsOut.PutText["Processing:"];
WHILE ~handle.eof DO
ProcessEntry[handle];
ENDLOOP;
IF handle.press # NIL THEN {
FinishPage[handle];
handle.press.ClosePress[]; handle.press ← NIL};
EXITS
done => NULL;
END; -- of Enable
IF handle.in # NIL THEN handle.in.Close[];
IF handle.out # NIL THEN handle.out.Close[];
handle.tsOut.Put[[character['\n]], [rope["done"]], [character['\n]]];
END;
CenterX: PROC [h: Handle, w: INT] RETURNS [x: INT] = {
RETURN[ h.dim.leftMargin + (pageWidth - h.dim.leftMargin - h.dim.rightMargin - w)/2]};
RightX: PROC [h: Handle, w: INT] RETURNS [x: INT] = {
RETURN[pageWidth - h.dim.rightMargin - w]};
PWidth: PROC [h: Handle, s: ROPE, class: FontClass] RETURNS [INT] = {
w: TSTypes.Dimn ← [0];
ref: TSFont.Ref ← h.fontInfo[class];
IF ref = NIL THEN RETURN [0];
FOR i: INT IN [0..Rope.Length[s]) DO
w ← [w + TSFont.Width[ref, Rope.Fetch[s, i]]];
ENDLOOP;
RETURN [TSTypes.DimnInt[w, TSTypes.pt]];
};
UC: PROC [c: CHAR] RETURNS [CHAR] = {
RETURN[IF c IN ['a..'z] THEN VAL[c.ORD - ORD['a] + ORD['A]] ELSE c]};
CFName: PROC [h: Handle, name: ROPE] RETURNS [cf: ROPE] = {
funny: BOOLEANFALSE;
lastName: REF TEXTNEW[TEXT[Rope.Length[name]]]; -- plenty long
FOR i: INT IN [0..Rope.Length[name]) DO
c: CHARUC[Rope.Fetch[name, i]];
SELECT c FROM
', => EXIT;
'*, '+ => LOOP;
ENDCASE => {
IF c = ' AND ~funny THEN {
funny ← TRUE; h.tsOut.Put[[character['\n]], [rope[name]], [character['\n]]]};
lastName[lastName.length] ← c; lastName.length ← lastName.length + 1};
ENDLOOP;
RETURN[Rope.FromRefText[lastName]]};
PressNames: PROC [h: Handle, name: ARRAY [0..4) OF ROPE, flag: BOOLEAN] = {
ascent: INT = h.dim.fontSize;
PT: PROC [t: ROPE, x, y: INT] = {
SirPress.PutText[p: h.press, textString: t, xCoordinateOfLeftEdge: x, yCoordinateOfBaseline: y, unit: SirPress.pt]};
PTH: PROC [t: ROPE, f: FontClass] = {
h.press.SetFontFromCode[h.fontCode[f]];
SirPress.PutTextHere[p: h.press, textString: t]};
SF: PROC [class: FontClass] = {h.press.SetFontFromCode[h.fontCode[class]]};
NameBreak: IO.BreakProc = {
RETURN [SELECT char FROM
'&, ', => break,
'\t => sepr, -- blanks are allowed in names
ENDCASE => other]};
default: FontClass ← bodyBold;
ns: STREAMNIL;
GetName: PROC RETURNS [r: ROPE, cl: FontClass, affiliate: BOOLEAN, brk: CHAR] = {
cl ← default; affiliate ← FALSE;
[] ← ns.SkipWhitespace[];
IF ns.EndOf[] THEN RETURN [NIL, cl, FALSE, 0C];
SELECT ns.PeekChar[] FROM
'+ => {h.affiliateThisPage ← affiliate ← TRUE; [] ← ns.GetChar[]};
'* => {cl ← bodyItalic; [] ← ns.GetChar[]};
ENDCASE;
r ← GetTokenRope[ns, NameBreak].token;
IF ns.EndOf[] THEN brk ← 0C ELSE brk ← ns.GetChar[]};
t: ROPE; bk: CHAR;
affiliate: BOOLEAN;
fc: FontClass;
ns ← IO.RIS[name[0]];
[t, default, affiliate, bk] ← GetName[]; -- last name
IF flag THEN default ← bodyBoldItalic;
IF affiliate THEN {
SF[symbols]; PT[t: "!", x: h.nameX - PWidth[h, "!", symbols], y: h.yTop - ascent]};
SF[default];
PT[t: t, x: h.nameX, y: h.yTop - ascent];
PTH[", ", default];
DO
[t, fc, affiliate, bk] ← GetName[];
IF t = NIL THEN EXIT;
IF affiliate THEN PTH["!", symbols];
PTH[t, fc];
SELECT bk FROM
'& => PTH["& ", default];
', => PTH[", ", default];
ENDCASE;
ENDLOOP;
do I have to close ns? NO.
FOR i: CARDINAL IN [1..4) WHILE name[i] # NIL DO
y: INT = h.yTop - i*h.dim.lineHeight - ascent;
ns ← IO.RIS[name[i], ns];
[t, fc, affiliate, bk] ← GetName[];
IF t = NIL THEN LOOP;
IF affiliate THEN {
SF[symbols]; PT[t: "!", x: h.nameX + h.dim.nameIndent - PWidth[h, "!", symbols], y: y]};
SF[fc];
PT[t: t, x: h.nameX + h.dim.nameIndent, y: h.yTop - i*h.dim.lineHeight - ascent];
DO
SELECT bk FROM
'& => PTH["& ", default];
', => PTH[", ", default];
ENDCASE;
[t, fc, affiliate, bk] ← GetName[];
IF t = NIL THEN EXIT;
IF affiliate THEN PTH["!", symbols];
PTH[t, fc];
ENDLOOP;
ENDLOOP;
};
PressPhone: PROC [h: Handle, phone: ARRAY [0..4) OF ROPE] = {
ascent: INT = h.dim.fontSize;
SF: PROC [class: FontClass] = {h.press.SetFontFromCode[h.fontCode[class]]};
PT: PROC [t: ROPE, x, y: INT] = {
SirPress.PutText[p: h.press, textString: t, xCoordinateOfLeftEdge: x, yCoordinateOfBaseline: y, unit: SirPress.pt]};
FOR i: INT IN [0..4) WHILE phone[i] # NIL DO
p: ROPE = phone[i];
y: INT = h.yTop - i*h.dim.lineHeight - ascent;
x: INT ← h.phoneX;
first: INT ← 0;
work: BOOLEANFALSE;
IF UC[Rope.Fetch[p, 0]] = 'W THEN {work ← TRUE; first ← 1};
IF Rope.Fetch[p, first] # '( THEN x ← h.phoneX2
ELSE IF Rope.Length[p] > first+6 AND Rope.Equal[Rope.Substr[p, first, 6], "(415) "] THEN {
x ← h.phoneX2; first ← first + 6};
IF UC[Rope.Fetch[p, first]] = 'X THEN x ← h.phoneX2 + 12;
IF work THEN {
SF[bodyItalic]; PT[t: "w", x: x-PWidth[h, "w", bodyItalic]-2, y: y]};
SF[body]; PT[t: Rope.Substr[p, first, Rope.Length[p] - first], x: x, y: y];
ENDLOOP};
PressEntry: PROC [h: Handle, e: Entry] = {
ascent: INT = h.dim.fontSize;
lastName: ROPE;
eLines: CARDINAL ← 1;
tLine, zLine: CARDINAL ← 0;
newLetter: BOOLEANFALSE;
SF: PROC [class: FontClass] = {h.press.SetFontFromCode[h.fontCode[class]]};
PT: PROC [t: ROPE, x, y: INT] = {
SirPress.PutText[p: h.press, textString: t, xCoordinateOfLeftEdge: x, yCoordinateOfBaseline: y, unit: SirPress.pt]};
x, eh, eh2: INT;
myFirst: CHAR;
ez: ROPEIF e.zip = NIL THEN "" ELSE e.zip;
FOR i: CARDINAL DECREASING IN [0..4) DO
IF e.phone[i] # NIL THEN {eLines ← MAX[eLines, i+1]; EXIT};
ENDLOOP;
FOR i: CARDINAL DECREASING IN [0..4) DO
IF e.name[i] # NIL THEN {eLines ← MAX[eLines, i+1]; EXIT};
ENDLOOP;
IF ~h.callersOnly^ THEN {
FOR i: CARDINAL DECREASING IN [0..4) DO
IF e.addr [i] # NIL THEN {eLines ← MAX[eLines, i+1]; EXIT};
ENDLOOP;
IF e.town # NIL THEN {
WHILE PWidth[h, e.addr[tLine], body] > h.addrPWidth DO
IF tLine = 4 THEN Quit[h, "no room for town"];
tLine ← tLine + 1;
ENDLOOP;
zLine ← tLine}
ELSE FOR i: CARDINAL DECREASING IN [0..4) DO
IF e.addr[i] # NIL THEN {zLine ← i; EXIT};
ENDLOOP;
};
eLines ← MAX[eLines, tLine+1, zLine+1];
eh ← eLines * h.dim.lineHeight;
lastName ← CFName[h, e.name[0]];
myFirst ← SELECT TRUE FROM
h.alphaSort^ => Rope.Fetch[lastName, 0],
h.ageSort^ => e.age,
h.dinnerSort^ => e.dinner,
ENDCASE => 0C;
newLetter ← SELECT TRUE FROM
h.alphaSort^, h.ageSort^, h.dinnerSort^ => h.prevInitial # myFirst,
h.zipSort^ => (h.prevZip = NIL OR ~Rope.Equal[h.prevZip, ez]),
ENDCASE => FALSE;
IF newLetter THEN eh2 ← eh + 2*h.dim.displayHeight
ELSE eh2 ← eh;
IF h.yTop - eh2 < h.dim.bottomMargin THEN FinishPage[h];
IF newLetter THEN {
l: ROPE = IF h.zipSort^ THEN ez ELSE Rope.FromChar[myFirst];
IF h.separatePages^ AND h.firstOnPage # NIL THEN FinishPage[h];
h.yTop ← h.yTop - h.dim.displayHeight;
h.press.SetFontFromCode[h.fontCode[display]];
PT[t: l, x: CenterX[h, PWidth[h, l, display]], y: h.yTop];
IF h.zipSort^ THEN h.tsOut.Put[[rope[ez]], [character[' ]]] ELSE h.tsOut.PutChar[myFirst];
h.yTop ← h.yTop - h.dim.displayHeight;
h.prevInitial ← myFirst;
h.prevZip ← ez};
SF[body];
IF h.showCallers^ THEN {
t: ROPE = Convert.RopeFromInt[e.caller];
PT[t: t, x: h.callerRight - PWidth[h, t, body], y: h.yTop - ascent]};
SF[campaign];
PT[t: Rope.FromChar[e.activity],
x: h.dim.leftMargin - PWidth[h, "mmmm ", campaign], y: h.yTop - ascent];
IF ~h.actualLevel^ AND e.level IN ['1..'9] THEN e.level ← 'p;
PT[t: Rope.FromChar[e.level],
x: h.dim.leftMargin - PWidth[h, "mmm ", campaign], y: h.yTop - ascent];
PT[t: Rope.FromChar[e.dinner],
x: h.dim.leftMargin - PWidth[h, "mm ", campaign], y: h.yTop - ascent];
PT[t: Rope.FromChar[e.age],
x: h.dim.leftMargin - PWidth[h, "m ", campaign], y: h.yTop - ascent];
SF[body];
PressPhone[h, e.phone];
PressNames[h, e.name, h.flagCallers^ AND e.caller IN [h.dim.firstRecruiter..h.dim.lastRecruiter]];
SF[body];
IF h.mailingOnly^ THEN {
IF e.mailing # NIL THEN PT[t: e.mailing, x: h.callerX, y: h.yTop - ascent]}
ELSE IF h.callersOnly^ THEN {
c: ROPE;
r: CallerNumber;
rn: ROPE;
[name: c, recruiter: r] ← h.callerName[e.caller];
rn ← h.callerName[r].name;
IF c # NIL THEN PT[t: c, x: h.callerX, y: h.yTop - ascent];
IF rn # NIL THEN PT[t: rn, x: h.recruiterX, y: h.yTop - ascent];
}
ELSE {
x ← h.addrX;
FOR i: CARDINAL IN [0..4) DO
IF e.addr[i] = NIL THEN EXIT;
PT[t: e.addr[i], x: x, y: h.yTop - i*h.dim.lineHeight - ascent];
ENDLOOP;
IF e.town # NIL THEN
PT[t: e.town, x: h.townX, y: h.yTop - tLine*h.dim.lineHeight - ascent];
IF e.zip # NIL THEN
PT[t: e.zip, x: h.zipX, y: h.yTop - zLine*h.dim.lineHeight - ascent];
IF e.comment # NIL THEN {
SF[comment];
PT[t: e.comment, x: pageWidth - h.dim.rightMargin + 6, y: h.yTop - ascent]};
};
h.yTop ← h.yTop - eh - h.dim.yLead;
h.lastOnPage ← IF h.alphaSort^ THEN lastName ELSE ez;
IF h.firstOnPage = NIL THEN h.firstOnPage ← h.lastOnPage;
};
FinishPage: PROC [h: Handle] = {
names, pageText: ROPE;
nx, dx, hy: INT;
PT: PROC [t: ROPE, x, y: INT] = {
SirPress.PutText[p: h.press, textString: t, xCoordinateOfLeftEdge: x, yCoordinateOfBaseline: y, unit: SirPress.pt]};
PTH: PROC [t: ROPE, f: FontClass] = {
h.press.SetFontFromCode[h.fontCode[f]];
SirPress.PutTextHere[p: h.press, textString: t]};
SF: PROC [class: FontClass] = {h.press.SetFontFromCode[h.fontCode[class]]};
h.press.PutRectangle[
xstart: h.dim.leftMargin, ystart: pageHeight - h.dim.topMargin + h.dim.lineHeight,
xlen: pageWidth - h.dim.leftMargin - h.dim.rightMargin, ylen: 1,
unit: SirPress.pt];
names ← IF ~(h.alphaSort^ OR h.zipSort^) THEN NIL
ELSE IF Rope.Equal[h.firstOnPage, h.lastOnPage] THEN h.firstOnPage
ELSE Rope.Cat[h.firstOnPage, Rope.FromChar[dash], h.lastOnPage];
hy ← pageHeight - h.dim.topMargin + 2*h.dim.lineHeight;
IF ~h.duplex^ OR (h.pageNumber MOD 2 = 1) THEN {
nx ← RightX[h, PWidth[h, names, tabs]]; dx ← h.dim.leftMargin}
ELSE {nx ← h.dim.leftMargin; dx ← h.dateX};
SF[tabs]; PT[t: names, x: nx, y: hy];
SF[date];
PT[t: h.date, x: dx, y: hy];
SF[headingLarge];
PT[t: h.heading, x: h.fumcX, y: hy];
pageText ← Convert.RopeFromInt[h.pageNumber];
nx ← CenterX[h, PWidth[h, pageText, pageNum]];
SF[pageNum];
PT[t: pageText, x: nx, y: h.dim.bottomMargin - 3*h.dim.lineHeight];
IF h.affiliateThisPage THEN {
SF[symbols]; PT["!", h.dim.leftMargin, h.dim.bottomMargin - 2*h.dim.lineHeight];
PTH[" affiliate member", bodyItalic]};
h.press.WritePage[];
h.yTop ← pageHeight - h.dim.topMargin;
h.firstOnPage ← h.lastOnPage ← NIL; h.affiliateThisPage ← FALSE;
h.pageNumber ← h.pageNumber + 1;
};
MyBreak: IO.BreakProc -- [char: CHAR] RETURNS [IO.CharClass] -- = {
RETURN [SELECT char FROM
'\\, '|, '}, '\n => break,
'\t, ', ' => sepr,
ENDCASE => other];
};
OpenFile: PROC [name: ROPE] RETURNS [st: STREAM] = {
st ← FS.StreamOpen[name, $read
! FS.Error => IF error.group # bug THEN CONTINUE]};
ProcessEntry: PROC [handle: Handle] = {
e: Entry;
IF handle.eof THEN RETURN;
IF handle.in = NIL THEN {
MessageWindow.Append[
message: "Please open a file first",
clearFirst: TRUE];
MessageWindow.Blink[ ];
ERROR ABORTED};
[] ← handle.in.SkipWhitespace[];
IF handle.in.EndOf[] THEN {handle.eof ← TRUE; GO TO done};
e ← ReadEntry[handle];
IF handle.press # NIL THEN PressEntry[handle, e];
EXITS
done => NULL;
};
ReadEntry: PROC [handle: Handle] RETURNS [e: Entry] = {
one should SkipWhitespace before calling (and check for eof)
ENABLE IO.EndOfStream => {handle.eof ← TRUE; Quit[handle, "Syntax error "]};
st: STREAM ← handle.in;
ch: CHAR;
caller: ROPE;
i: CARDINAL;
IF (ch ← st.GetChar[]) # '{ THEN Quit[handle, "Syntax error "];
caller ← GetTokenRope[st, MyBreak].token;
IF caller # NIL THEN
e.caller ← Convert.IntFromRope[caller ! Convert.Error => Quit[handle, "bad caller #"]];
IF (ch ← st.GetChar[]) # '| THEN Quit[handle, "Syntax error "];
BEGIN -- get campaign info
IF (ch ← st.GetChar[]) = '| THEN GO TO done;
e.activity ← ch;
IF (ch ← st.GetChar[]) = '| THEN GO TO done;
e.level ← ch;
IF (ch ← st.GetChar[]) = '| THEN GO TO done;
e.dinner ← ch;
IF (ch ← st.GetChar[]) = '| THEN GO TO done;
e.age ← ch;
IF (ch ← st.GetChar[]) # '| THEN Quit[handle, "Syntax error "];
EXITS
done => NULL;
END;
i ← 0;
DO
e.phone[i] ← GetTokenRope[st, MyBreak].token;
SELECT (ch ← st.GetChar[]) FROM
'\\ => IF i = 2 THEN Quit[handle, "Syntax error "];
'| => EXIT;
'} => RETURN;
ENDCASE => Quit[handle, "Syntax error "];
i ← i + 1;
ENDLOOP;
i ← 0;
DO
e.name[i] ← GetTokenRope[st, MyBreak].token;
SELECT (ch ← st.GetChar[]) FROM
'\\ => IF i = 3 THEN Quit[handle, "Syntax error "];
'| => EXIT;
'} => RETURN;
ENDCASE => Quit[handle, "Syntax error "];
i ← i + 1;
ENDLOOP;
i ← 0;
DO
e.addr[i] ← GetTokenRope[st, MyBreak].token;
SELECT (ch ← st.GetChar[]) FROM
'\\ => IF i = 3 THEN Quit[handle, "Syntax error "];
'| => EXIT;
'} => RETURN;
ENDCASE => GO TO badsyntax;
i ← i + 1;
ENDLOOP;
e.town ← GetTokenRope[st, MyBreak].token;
IF st.GetChar[] = '} THEN RETURN;
e.zip ← GetTokenRope[st, MyBreak].token;
IF st.GetChar[] = '} THEN RETURN;
e.mailing ← GetTokenRope[st, MyBreak].token;
IF st.GetChar[] = '} THEN RETURN;
e.comment ← GetTokenRope[st, MyBreak].token;
IF st.GetChar[] # '} THEN Quit[handle, "Syntax error "];
EXITS
badsyntax => Quit[handle, "Syntax error "];
};
ParseCallerNames: PROC [h: Handle] RETURNS [n: REF CallerNameRec] = {
cnfile: ROPE ← ViewerTools.GetContents[h.cmd.callers];
ch: CHAR;
r: INT ← 0;
index: INT;
c, p: ROPE;
st: STREAM;
n ← NEW[CallerNameRec ← ALL[[NIL, NIL, 0]]];
st ← OpenFile[cnfile];
IF st = NIL THEN Quit2[h, st, "No caller names"];
WHILE ~st.EndOf[] DO
ENABLE IO.Error => Quit2[h, st, "invalid callernames"];
c ← p ← NIL;
[] ← st.SkipWhitespace[];
IF st.EndOf[] THEN RETURN;
index ← st.GetInt[];
IF NOT (index IN CallerNumber) THEN Quit2[h, st, "Index invalid in callernames"];
IF st.GetChar[] # '\t THEN Quit2[h, st, "Missing tab in callernames"];
c ← GetTokenRope[st, MyBreak].token;
BEGIN
IF ~st.EndOf[] THEN SELECT (ch ← st.GetChar[]) FROM
'\n => GO TO done;
'| => NULL;
ENDCASE => Quit2[h, st, "Syntax error in callernames"];
p ← GetTokenRope[st, MyBreak].token;
IF ~st.EndOf[] THEN SELECT (ch ← st.GetChar[]) FROM
'\n => GO TO done;
'| => NULL;
ENDCASE => Quit2[h, st, "Syntax error in callernames"];
[] ← st.SkipWhitespace[];
IF st.EndOf[] THEN RETURN;
r ← st.GetInt[];
IF st.GetChar[] # '\n THEN Quit2[h, st, "Missing CR in callernames"];
EXITS
done => NULL;
END;
n[index] ← [name: c, phone: p, recruiter: r];
ENDLOOP;
st.Close[]};
Quit: PROC [handle: Handle, reason: ROPENIL] = {
loc: INT = handle.in.GetIndex[];
handle.in.Close[]; handle.in ← NIL; handle.eof ← TRUE;
handle.tsOut.Put[[rope[reason]], [integer[loc]], [character['\n]]];
ERROR Problem};
Quit2: PROC [handle: Handle, st: STREAM, reason: ROPENIL] = {
loc: INT = st.GetIndex[];
st.Close[];
handle.tsOut.Put[[rope[reason]], [integer[loc]], [character['\n]]];
ERROR Problem};
copied from IOSearchImpl because it didn't handle empty tokens properly
GetToken: PROC [stream: STREAM, breakProc: IO.BreakProc, buffer: REF TEXT]
RETURNS[token: REF TEXT, charsSkipped: INT] = {
quit, include: BOOLFALSE;
anySeen: BOOLFALSE;
charsSkipped ← 0;
buffer.length ← 0;
DO
char: CHAR
stream.GetChar[ ! IO.EndOfStream => IF buffer.length > 0 THEN EXIT ELSE REJECT];
SELECT breakProc[char] FROM
break => {include ← FALSE; quit ← TRUE};
sepr => {include ← FALSE; quit ← anySeen };
other => {include ← TRUE; quit ← FALSE; anySeen ← TRUE};
ENDCASE => ERROR;
IF include THEN buffer ← RefText.InlineAppendChar[buffer, char]
ELSE
IF quit THEN stream.Backup[char] ELSE charsSkipped ← charsSkipped + 1;
IF quit THEN EXIT;
ENDLOOP;
RETURN[buffer, charsSkipped];
};
GetTokenRope: PUBLIC PROC [stream: STREAM, breakProc: IO.BreakProc]
RETURNS [token: ROPE, charsSkipped: INT] = {
buffer: REF TEXT = RefText.ObtainScratch[100];
{ ENABLE UNWIND => RefText.ReleaseScratch[buffer];
tokenText: REF TEXT;
[tokenText, charsSkipped] ← GetToken[stream, breakProc, buffer];
token ← IF tokenText.length = 0 THEN NIL ELSE Rope.FromRefText[tokenText];
};
RefText.ReleaseScratch[buffer];
RETURN [token, charsSkipped];
};
Commander.Register[key: "PrintDir", proc: MakeTool,
doc: "Create a church directory printer" ];
[ ] ← MakeTool[NIL]; -- and create an instance
END.