TReportPrint
Volume Number: 9
Issue Number: 6
Column Tag: MacApp Workshop
Related Info: Print Manager
TReportPrinter 
A supplemental printing class for MacApp
By William L. Colsher, Minneapolis, Minnesota
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
About the author
William L. Colsher, 5400 3rd Avene South. Minneapolis, Minnesota 55419.
MacApp is, without question, the most comprehensive commercially available
class library for the Macintosh. And to a large extent it fulfills its rather ambitious
goal of providing a complete Macintosh application framework. But no class designer
can anticipate every use to which his work will be put. He must produce a set of
general purpose classes that are easily subclassed to provide specific behavior yet still
contain enough functionality to be worth using. Nowhere is this more evident than in
MacApp’s printing architecture.
One of the fundamental assumptions built into MacApp is the relationship between
a document (represented by a subclass of TFileBasedDocument) and the presentation of
that document’s data (by the many subclasses of TView). Specifically, it is assumed
that some combination of TView objects will be able to present the document’s data both
on screen and when that data is printed. As a consequence, MacApp’s printing class
TStdPrintHandler depends on a document’s TView objects to draw the data on the
selected printer.
This is a reasonable and powerful assumption. It takes advantage of QuickDraw’s
ability to draw in any GrafPort regardless of whether that GrafPort represents a
window, a printer, a buffer in RAM, or something we haven’t yet imagined. It also
greatly simplifies the creation of WYSIWYG style applications. For some documents
this is ideal. To print one simply attaches a TStdPrintHandler or a subclass of it to the
TView to be printed. This TView need not be the same view used to display the document
on screen. Nevertheless, it must be a subclass of TView.
In many cases that assumption is perfectly valid. But for a large class of
real-world applications, the TView/TStdPrintHandler approach is unnecessarily
(even hopelessly) complex. These are the applications that must print reports. Boring
though they may be to program, printed reports are the lifeblood of modern business.
The problem with reports (from the standpoint of MacApp’s printing architecture) is
that it is often impossible to predict their length or exact form without first
processing all the data that is to be printed. While it is possible to create views “on the
fly” to represent a printed report, it is often impractical. The process is complex and
time consuming at best. The data must be processed at least twice (once to build the
views, then again to print them). Unless they are very carefully designed, the TView
subclasses will tend to multiply uncontrollably as reporting requirements change.
And, perhaps most important from an object programming point of view, the data
objects lose control of a very important piece of functionality: their own printing.
TReportPrinter attempts to provide an alternative to MacApp’s built in printing
architecture that addresses just this problem. TReportPrinter’s goal is to encapsulate
the Macintosh printing process in a way that makes it easy for an application to format
and print text oriented reports. It effectively hides the often poorly understood
mechanics of the Macintosh Printing Manager, leaving the well understood process of
formatting report lines to the document and it’s data objects.
Overview of TReportPrinter
The structure of TReportPrinter corresponds quite closely to that of the
traditional Macintosh “printing loop”. Its fields contain the various pointers required
by the Printing Manager and its methods implement the many steps required to print
the lines and pages of a report and to provide an adequate safety net should something go
wrong. In addition there are methods that provide control of print characteristics
(font, size, etc.), pagination, and the printing of headers and footers. The rest of this
section explains the function of each field and method of TReportPrinter in some detail.
They are documented in alphabetical order for easy reference, Their usage is
documented in the next section: Using TReportPrinter.
Fields:
Boolean fAutoFormFeed - TRUE if TReportPrinter is to start a new page whenever
fBottomOfPage will be passed by the next print operation.
short fBottomOfPage - Stores the highest location (in pixels) to be printed on a page.
It may not be greater than the length of the page in pixels.
short fCurrentFace - Store the current font’s “face” (i.e. bold, italic, etc.).
CStr255 fCurrentFontName - Stores the name of the font currently used for
printing. It is provided for the convenience of subclasses.
short fCurrentFontNumber - Stores the number of the current printing font.
short fCurrentPage - A convenience for subclasses that need to print page numbers.
short fCurrentSize - Stores the current font’s size.
short fLeftMargin - The left margin location in pixels, offset from zero.
long fMaxSpoolLines - The maximum number of lines that the Printing Manager can
spool to disk. It is calculated in IReportPrinter().
TReportStatusWindow fMyStatusWindow - Reference to the modal status window
TReportPrinter creates when starting a report. This window is responsible for
intercepting Cmd-. It may display any status message.
long fPrintedLines - The number of lines that have been printed. Used with
fMaxSpoolLines to determine when to close the Printing Manager “document” and
start a new one.
TPPrPort fPrintingPort - Pointer to the Printing Manager TPPrPort being used for
printing.
Boolean fPrintManagerIsOpen - There are two methods where TReportPrinter might
open the Printing Manager with a call to PrOpen(). However, it is important to
do this only once. It is set to TRUE when PrOpen() is called.
THPrint fPrintRecord - Handle to the Printing Manager “print record” currently in
use.
CStr255 fReportTitle - Displayed in the status window, this field is usually the
parent document’s name. It does not have to be however. It also may be a
convenience when printing headers or footers.
Boolean fUserAborted - Set to TRUE if a user hits Cmd-.
Methods:
pascal void DoClosePage(void) - DoClosePage() encapsulates PrClosePage(). Rarely
called by the user of TReportPrinter.
pascal void DoFormFeed(void) - DoFormFeed() calls PrintFooter(), advances to the
top of the next page (by calling DoClosePage() and DoOpenPage()) and then calls
PrintHeader().
pascal Boolean DoOpenPage(void) - DoOpenPage() encapsulates PrOpenPage().
Rarely called by the user of TReportPrinter. (Call DoFormFeed() instead.)
pascal void DoPrClose(void) - Calls PrClose(), sets fPrintManagerIsOpen to false,
frees the print record and closes the status window if it exists. Not normally
called by the user of the class.
pascal void DoPrOpen(void) - Calls PrOpen() and allocates a print record if
necessary. Sets fPrintManagerIsOpen to true. Not normally called by the user of
the class.
pascal void EndReport(void) - Call EndReport() when your printing is finished. It
closes the current page and print document, then calls the appropriate Print
Manager routines to “print” your report.
pascal void Free(void) - Call this method from you document’s Free() method.
pascal unsigned long GetFreeSpaceOnDisk(int theVolume) - Used internally to
determine how large a document we can “spool”.
pascal Boolean HandlePageSetup(void) - Call HandlePageSetup() in response to a
cPageSetup command in your document’s DoMenuCommand() method.
pascal void IReportPrinter(CStr255 theFont,
short theFace,
short theSize,
short leftMargin,
short bottomOfPage,
Boolean autoFormFeed);
IReportPrinter() sets the initial parameters for printing. Notice that the
font to be used is specified by its name. theFace and theSize are specified as you would
for QuickDraw. The two margin arguments are specified in pixels. If autoFormFeed is
true, TReportPrinter will advance to the next page whenever a “line feed” operation
would cause the next line to print beyond bottomOfPage
pascal Boolean PrintALine(CStr255 theLine,
Boolean doLineFeed);
PrintALine() prints the contents of theLine. If doLineFeed is true, it also
advances the cursor to the logical next line.
pascal void PrintFooter(void) - Override this method to print your page footer.
pascal void PrintHeader(void) - Override this method to print your page header.
pascal void SetDocumentName(Str255 docName) - Used to tell TReportPrinter the
name of the document being printed. TReportPrinter passes this information on
to the status window.
pascal void SetFace(short theFace);
pascal void SetFont(CStr255 theFont);
pascal void SetSize(short theSize);
These three methods set the face, font, and size that will be used for printing.
These methods must be called after a DoOpenPage() to work correctly.
pascal Boolean StartReport(CmdNumber aCmdNumber) - StartReport() opens the
print manager, calls PRJobDialog() and instantiates the status window. It also
verifies that the bottom margin does not exceed the size of the paper. (If it does,
the bottom margin is set to the bottom of the printing GrafPort.)
pascal void UserAbort(void) - Called by the status window if the user hits Cmd-.
pascal void VerticalTab(short pixels) - Advances the cursor down the current page
by pixels. It will cross page boundaries in the obvious way.
Using TReportPrinter
Incorporating TReportPrinter into your program is a straightforward task. The
first step is to create an instance of TReportPrinter and initialize it. This is typically
done in your document’s initialization method and requires only two lines (all these
examples are from the demonstration program available on the source code disk):
/* 1 */
fMyReportPrinter= new TReportPrinter;
fMyReportPrinter->IReportPrinter(“\pmonaco”,// Font
normal, // Face
9, // Size
36, // Left margin
648, // Bottom Margin
false); // no auto-FF
Of course you will also have to respond to menu commands. In your document’s
DoMenuCommand() method, the following lines handle all that is necessary:
/* 2 */
case cPageSetup:
fMyReportPrinter->HandlePageSetup();
break;
case cPrint:
GetInspectorName(docName); // get this document’s name
fMyReportPrinter->SetDocumentName(docName);
if(fMyReportPrinter->StartReport(aCmdNumber)) {
fMyReportPrinter->PrintHeader();
// Your code to feed lines to the report printer goes here
fMyReportPrinter->EndReport();
}
break;
Except in very simple cases (such as the demo program that accompanies this
article) it is probably preferable to create and post a command to handle the cPrint