<> <> 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 <> 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, <> 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, <> 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, <> 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 <> 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 <> 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 <> 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 <> 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 <> 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 <> 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 <> 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.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}; 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; 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; <> 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]; EXITS done => NULL; }; ReadEntry: PROC [handle: Handle] RETURNS [e: Entry] = { <> 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}; <> 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 END.