PrintDir.mesa;
Last Edited by: Sweet, September 14, 1985 1:09:32 am PDT
DIRECTORY
Ascii,
Buttons,
Commander USING [CommandProc, Register],
Containers USING [ChildXBound, ChildYBound, Container, Create],
Convert,
FS,
IO,
MessageWindow,
RefText,
Rope,
Rules USING [Create, Rule],
SafeStorage,
SirPress,
TSFont,
TSTypes,
TypeScript,
VFonts,
ViewerClasses USING [Viewer, ViewerClassRec],
ViewerIO USING [CreateViewerStreams],
ViewerOps USING [PaintViewer],
ViewerTools USING [GetContents, MakeNewTextViewer, SetSelection];
PrintDir:
CEDAR
PROGRAM
IMPORTS Buttons, Commander, Containers, Convert, FS, IO, MessageWindow, RefText, Rope, Rules, SafeStorage, SirPress, TSFont, TSTypes, TypeScript, VFonts, ViewerIO, ViewerOps, ViewerTools =
BEGIN
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;
FontClass: TYPE = {body, bodyBold, bodyItalic, headingLarge, headingSmall, pageNum, date, tabs, display, symbols, tiny};
Handle: TYPE = REF MyRec; -- a REF to the data for a particular instance of the sample tool; multiple instances can be created.
MyRec:
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
cmd: CommandViewer, -- the commands
dim: DimRecord ← TRASH,
in: STREAM, eof: BOOLEAN ← FALSE,
out: STREAM, -- for press file
press: SirPress.PressHandle ← NIL,
fontCode: ARRAY FontClass OF SirPress.FontCode,
fontInfo: ARRAY FontClass OF TSFont.Ref,
yTop: INT, firstOnPage, lastOnPage: ROPE,
pageNumber: INT ← 1,
phoneX, phoneX2, indent, nameX, addrX, townX, zipX, addrPWidth: INT,
prevInitial: CHAR ← 0C,
prevZip: ROPE ← NIL,
alphaSort: BOOL ← TRUE,
duplex, separatePages, affiliateThisPage, zipSort, workNum: BOOL ← FALSE,
phoneLast: ARRAY SmallCount OF INT ← ALL[0],
fumcX, dateX: INT,
tsIn, tsOut: STREAM,
ts: ViewerClasses.Viewer ]; -- the typescript
PromptRec:
TYPE =
RECORD [
handle: Handle, viewer: ViewerClasses.Viewer ← NIL];
PromptHandle: TYPE = REF PromptRec;
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;
CommandViewer:
TYPE =
RECORD [
inputFile, pressFile, yLead, fontSize, lineHeight, pageWidth, pageHeight, topMargin, bottomMargin, leftMargin, rightMargin, displayHeight, tabHeight, nameIndent: ViewerClasses.Viewer
];
DimRecord:
TYPE =
RECORD [
yLead, fontSize, lineHeight, pageWidth, pageHeight, topMargin, bottomMargin, leftMargin, rightMargin, displayHeight, tabHeight, nameIndent: INT];
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[tiny, "TimesRoman", handle.dim.fontSize - 2];
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];
};
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:
ROPE ←
NIL]
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, proc: Buttons.ButtonProc, initial:
BOOLEAN] = {
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
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];
NewLine[];
handle.cmd.pressFile ← LabeledItem["press", 50];
NewLine[];
Bool["duplex", ToggleDuplex, handle.duplex];
Bool["workNum", ToggleWorkNum, handle.workNum];
handle.cmd.yLead ← LabeledItem["paraLead", 5, "8"];
handle.cmd.fontSize ← LabeledItem["fontSize", 5, "12"];
handle.cmd.lineHeight ← LabeledItem["lineHeight", 5, "13"];
NewLine[];
Bool["separatePages", ToggleSeparatePages, handle.separatePages];
Bool["alphaSort", ToggleAlphaSort, handle.alphaSort];
Bool["zipSort", ToggleZipSort, handle.zipSort];
handle.cmd.pageWidth ← LabeledItem["pageWidth", 6, "612"];
handle.cmd.pageHeight ← LabeledItem["pageHeight", 6, "792"];
NewLine[];
handle.cmd.topMargin ← LabeledItem["topMargin", 5, "72"];
handle.cmd.bottomMargin ← LabeledItem["bottomMargin", 5, "66"];
handle.cmd.leftMargin ← LabeledItem["leftMargin", 5, "54"];
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;
ToggleDuplex: 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
handle.duplex ← NOT handle.duplex;
Buttons.SetDisplayStyle[
button: NARROW[parent],
style: IF handle.duplex THEN $WhiteOnBlack ELSE $BlackOnWhite];
END;
ToggleWorkNum: 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
handle.workNum ← NOT handle.workNum;
Buttons.SetDisplayStyle[
button: NARROW[parent],
style: IF handle.workNum THEN $WhiteOnBlack ELSE $BlackOnWhite];
END;
ToggleSeparatePages: 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
handle.separatePages ← NOT handle.separatePages;
Buttons.SetDisplayStyle[
button: NARROW[parent],
style: IF handle.separatePages THEN $WhiteOnBlack ELSE $BlackOnWhite];
END;
ToggleZipSort: 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
handle.zipSort ← NOT handle.zipSort;
Buttons.SetDisplayStyle[
button: NARROW[parent],
style: IF handle.zipSort THEN $WhiteOnBlack ELSE $BlackOnWhite];
END;
ToggleAlphaSort: 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
handle.alphaSort ← NOT handle.alphaSort;
Buttons.SetDisplayStyle[
button: NARROW[parent],
style: IF handle.alphaSort THEN $WhiteOnBlack ELSE $BlackOnWhite];
END;
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};
];
};
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.out ← FS.StreamOpen[fileName: pName, accessOptions: $create];
handle.press ← SirPress.Create[outputStream: handle.out, fileNameForHeaderPage: pName];
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.pageWidth ← Val[handle.cmd.pageWidth, 612];
handle.dim.pageHeight ← Val[handle.cmd.pageHeight, 792];
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.nameIndent ← Val[handle.cmd.nameIndent, 18];
handle.press.SetPageSize[110, 85];
LookupFonts[handle];
handle.yTop ← handle.dim.pageHeight - handle.dim.topMargin;
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 ← handle.dim.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.addrPWidth ← handle.townX - handle.addrX;
fumcWidth ←
PWidth[handle, "FUMPA", headingLarge] +
PWidth[handle, "IRST NITED ETHODIST, ALO LTO", headingSmall];
handle.fumcX ← CenterX[handle, fumcWidth];
handle.dateX ← RightX[handle, PWidth[handle, "Fall 1985", date]];
handle.prevInitial ← 0C;
handle.prevZip ← NIL;
handle.firstOnPage ← NIL; handle.affiliateThisPage ← FALSE;
}
ELSE Quit[handle, "Specify output file"];
handle.tsOut.PutText["Processing:"];
PrintFrontMatter[handle];
WHILE ~handle.eof
DO
ProcessEntry[handle];
ENDLOOP;
IF handle.press #
NIL
THEN {
FinishPage[handle];
handle.press.ClosePress[]; handle.press ← NIL};
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;
CenterText:
PROC [h: Handle, y:
INT, t:
ROPE, f: FontClass] = {
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]};
SF[f];
PT[t, CenterX[h, PWidth[h, t, f]], y]};
CenterX:
PROC [h: Handle, w:
INT]
RETURNS [x:
INT] = {
RETURN[ h.dim.leftMargin + (h.dim.pageWidth - h.dim.leftMargin - h.dim.rightMargin - w)/2]};
RightX:
PROC [h: Handle, w:
INT]
RETURNS [x:
INT] = {
RETURN[h.dim.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: BOOLEAN ← FALSE;
lastName: REF TEXT ← NEW[TEXT[Rope.Length[name]]]; -- plenty long
FOR i:
INT
IN [0..Rope.Length[name])
DO
c: CHAR ← UC[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..5)
OF
ROPE] = {
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: STREAM ← NIL;
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 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?
FOR i:
CARDINAL
IN [1..nLines)
WHILE name[i] #
NIL
DO
y: INT = h.yTop - i*h.dim.lineHeight - ascent;
x: INT ← MAX[h.nameX + h.dim.nameIndent, h.phoneLast[i] + 12];
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: x, 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..5)
OF
ROPE] = {
ascent: INT = h.dim.fontSize;
number: ROPE;
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]};
PTH:
PROC [t:
ROPE, f: FontClass] = {
h.press.SetFontFromCode[h.fontCode[f]];
SirPress.PutTextHere[p: h.press, textString: t]};
h.phoneLast ← ALL[0];
FOR i:
INT
IN SmallCount
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;
len: INT = Rope.Length[p];
last: INT ← len;
work: BOOLEAN ← FALSE;
IF UC[Rope.Fetch[p, 0]] = 'W THEN {work ← TRUE; first ← 1};
IF work AND ~h.workNum THEN RETURN;
IF len > 6
THEN
FOR j:
INT
IN [6..len)
DO
IF Rope.Fetch[p, j] = '( THEN {last ← j; EXIT};
ENDLOOP;
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]};
number ← Rope.Substr[p, first, last - first];
SF[body]; PT[t: number, x: x, y: y];
h.phoneLast[i] ← x + PWidth[h, number, body];
IF last < len
THEN
{
name: ROPE = Rope.Substr[p, last, len - last];
PTH[t: name, f: tiny];
h.phoneLast[i] ← h.phoneLast[i] + PWidth[h, name, tiny]};
ENDLOOP};
PressEntry:
PROC [h: Handle, e: Entry] = {
ascent: INT = h.dim.fontSize;
lastName: ROPE;
eLines: CARDINAL ← 1;
tLine, zLine: CARDINAL ← 0;
newLetter: BOOLEAN ← FALSE;
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: ROPE ← IF e.zip = NIL THEN "" ELSE e.zip;
FOR j:
INT
IN [0..Rope.Length[ez])
DO
IF Rope.Fetch[ez, j] = '- THEN {ez ← Rope.Substr[ez, 0, j]; EXIT};
ENDLOOP;
FOR i:
CARDINAL
DECREASING
IN SmallCount
DO
IF e.phone[i] # NIL THEN {eLines ← MAX[eLines, i+1]; EXIT};
ENDLOOP;
FOR i:
CARDINAL
DECREASING
IN SmallCount
DO
IF e.name[i] # NIL THEN {eLines ← MAX[eLines, i+1]; EXIT};
ENDLOOP;
FOR i:
CARDINAL
DECREASING
IN SmallCount
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 = nLines-1 THEN Quit[h, "no room for town"];
tLine ← tLine + 1;
ENDLOOP;
zLine ← tLine}
ELSE
FOR i:
CARDINAL
DECREASING
IN SmallCount
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 ← Rope.Fetch[lastName, 0];
newLetter ←
SELECT
TRUE
FROM
h.alphaSort => 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};
h.press.SetFontFromCode[h.fontCode[body]];
PressPhone[h, e.phone];
PressNames[h, e.name];
h.press.SetFontFromCode[h.fontCode[body]];
x ← h.addrX;
FOR i:
CARDINAL
IN SmallCount
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];
h.yTop ← h.yTop - eh - h.dim.yLead;
h.lastOnPage ← IF h.zipSort THEN ez ELSE lastName;
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: h.dim.pageHeight - h.dim.topMargin + h.dim.lineHeight,
xlen: h.dim.pageWidth - h.dim.leftMargin - h.dim.rightMargin, ylen: 1,
unit: SirPress.pt];
names ←
IF Rope.Equal[h.firstOnPage, h.lastOnPage]
THEN h.firstOnPage
ELSE Rope.Cat[h.firstOnPage, Rope.FromChar[dash], h.lastOnPage];
hy ← h.dim.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: "Fall 1985", x: dx, y: hy];
SF[headingLarge];
PT[t: "F", x: h.fumcX, y: hy];
PTH["IRST ", headingSmall];
PTH["U", headingLarge];
PTH["NITED ", headingSmall];
PTH["M", headingLarge];
PTH["ETHODIST, ", headingSmall];
PTH["P", headingLarge];
PTH["ALO ", headingSmall];
PTH["A", headingLarge];
PTH["LTO", headingSmall];
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 ← h.dim.pageHeight - h.dim.topMargin;
h.firstOnPage ← h.lastOnPage ← NIL; h.affiliateThisPage ← FALSE;
h.pageNumber ← h.pageNumber + 1;
};
PrintFrontMatter:
PROC [h: Handle] = {
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]]};
y: INT ← h.dim.pageHeight - h.dim.topMargin;
w, x: INT;
LabeledName:
PROC [label, name:
ROPE, bold:
BOOL ←
TRUE] = {
SF[IF bold THEN bodyBold ELSE body];
PT[label, x, y];
PTH[" \030 ", body];
PTH[name, body];
y ← y - (4*h.dim.lineHeight)/3;
};
TownAbbr:
PROC [abbr, name:
ROPE] = {
PT[abbr, x, y];
PT[name, x+30, y];
y ← y - h.dim.lineHeight;
};
CenterText[h, y, "First United Methodist Church", display];
y ← y - 2*h.dim.lineHeight;
CenterText[h, y, "625 Hamilton Avenue Palo Alto, California 94301", bodyBold];
y ← y - h.dim.lineHeight;
CenterText[h, y, "Telephone: (415) 323-6167", bodyBold];
y ← y - 2*h.dim.lineHeight;
CenterText[h, y, "Office Hours: 9:00-5:00 Monday through Friday", bodyBold];
y ← y - 4*h.dim.lineHeight;
CenterText[h, y, "Church Staff", display];
y ← y - 2*h.dim.lineHeight;
x ← CenterX[h, PWidth[h, "Office Hours: 9:00-5:00 Monday through Friday", bodyBold]];
SF[bodyBold];
PT["Ministers", x, y];
PTH[" \030 ", body];
w ← PWidth[h, "Ministers", bodyBold] + PWidth[h, " \030 ", body];
PT["Douglas I. Norris", x+w, y];
y ← y - h.dim.lineHeight;
PT["Glenn S. Fuller", x+w, y];
y ← y - (4*h.dim.lineHeight)/3;
LabeledName["Church Business Administrator", "Florence T. Wegner"];
SF[bodyBold];
PT["Secretaries", x, y];
PTH[" \030 ", body];
w ← PWidth[h, "Secretaries", bodyBold] + PWidth[h, " \030 ", body];
PT["Finance: Ruth Willard", x+w, y];
y ← y - h.dim.lineHeight;
PT["Ministers: Joanne Perry", x+w, y];
y ← y - h.dim.lineHeight;
PT["Communications: Cathy Floyd", x+w, y];
y ← y - (4*h.dim.lineHeight)/3;
LabeledName["Director of Children's Ministry", "Dorothy Power"];
LabeledName["Director of Youth Ministry", "Neli Moody-Berne"];
LabeledName["Director of Music", "Leroy Kromm"];
LabeledName["Organist", "Steven Gray"];
SF[bodyBold];
PT["Choir Directors", x, y];
y ← y - (4*h.dim.lineHeight)/3;
x ← x + 18;
LabeledName["Chancel Choir", "Leroy Kromm", FALSE];
LabeledName["Youth and Adult Choirs", "Leroy Kromm", FALSE];
LabeledName["Children's Choirs", "Linda Jordan", FALSE];
LabeledName["Handbell Choirs", "Sara Salsbury", FALSE];
x ← x - 18;
LabeledName["Custodian", "Brian Neeley"];
LabeledName["Librarian", "Virginia Williams"];
LabeledName["Membership Secretary", "Barbara Busby"];
y ← h.dim.bottomMargin + 15 * h.dim.lineHeight;
x ← 72;
SF[body];
PT["This Directory includes the names of those persons on the church roll as of", x, y];
y ← y - h.dim.lineHeight;
PT["September 15, 1985. Non-member spouses and children not yet confirmed in", x, y];
y ← y - h.dim.lineHeight;
PT["membership are shown in ", x, y]; PTH["italics.", bodyItalic];
SF[body];
y ← y - 2*h.dim.lineHeight;
PT["Cities are indicated as follows:", x, y];
y ← y - 2*h.dim.lineHeight;
y ← h.dim.bottomMargin + 9 * h.dim.lineHeight;
TownAbbr["A", "Atherton"];
TownAbbr["C", "Cupertino"];
TownAbbr["EPA", "East Palo Alto"];
TownAbbr["LA", "Los Altos"];
TownAbbr["LAH", "Los Altos Hills"];
TownAbbr["MP", "Menlo Park"];
TownAbbr["MV", "Mountain View"];
TownAbbr["PA", "Palo Alto"];
TownAbbr["PV", "Portola Valley"];
y ← h.dim.bottomMargin + 9 * h.dim.lineHeight;
x ← 3*72;
TownAbbr["RC", "Redwood City"];
TownAbbr["S", "Stanford"];
TownAbbr["Svl", "Sunnyvale"];
TownAbbr["SC", "Santa Clara"];
TownAbbr["SF", "San Francisco"];
TownAbbr["SJ", "San Jose"];
TownAbbr["SM", "San Mateo"];
TownAbbr["W", "Woodside"];
h.press.WritePage[]};
nLines: NAT = 5;
SmallCount: TYPE = [0..nLines);
Entry:
TYPE =
RECORD [
phone: ARRAY SmallCount OF ROPE ← ALL[NIL],
name: ARRAY SmallCount OF ROPE ← ALL[NIL],
addr: ARRAY SmallCount OF ROPE ← ALL[NIL],
town: ROPE ← NIL,
zip: ROPE ← NIL];
Problem: ERROR = CODE;
MyBreak:
IO.BreakProc
-- [char: CHAR] RETURNS [IO.CharClass] -- = {
RETURN [
SELECT char
FROM
'\\, '|, '} => 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];
};
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;
i: CARDINAL;
IF (ch ← st.GetChar[]) # '{ THEN Quit[handle, "Syntax error "];
i ← 0;
DO
e.phone[i] ← GetTokenRope[st, MyBreak].token;
SELECT (ch ← st.GetChar[])
FROM
'\\ => IF i = nLines-1 THEN Quit[handle, "Syntax error "];
'| => EXIT;
'} => RETURN;
ENDCASE => Quit[handle, "Syntax error "];
i ← i + 1;
ENDLOOP;
IF ~ handle.workNum
THEN {
smash: BOOL ← FALSE;
FOR j:
NAT
IN SmallCount
DO
IF smash THEN e.phone[j] ← NIL
ELSE
IF e.phone[j] #
NIL
AND
UC[Rope.Fetch[e.phone[j], 0]] = 'W
THEN {
smash ← TRUE;
e.phone[j] ← NIL};
ENDLOOP;
};
i ← 0;
DO
e.name[i] ← GetTokenRope[st, MyBreak].token;
SELECT (ch ← st.GetChar[])
FROM
'\\ => IF i = nLines-1 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 = nLines-1 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;
GO TO badsyntax;
EXITS
badsyntax => Quit[handle, "Syntax error "];
};
Quit:
PROC [handle: Handle, reason:
ROPE ←
NIL] = {
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};
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: BOOL ← FALSE;
anySeen: BOOL ← FALSE;
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