In Chapter 8 you created the Facts for the Traveler (or moon facts) window to display various information about the moon. To add printing support to the moon facts window, you perform these steps:
Define constants for working with printing.
Define data types for working with printing.
Define prototypes for printing functions.
Write printing functions.
Add a print error format string to the Localizable.strings
file.
Add print setup code to the main
function.
Adjust code in the moon facts window event handler.
Test the Page Setup and Print commands to prove it all works.
These steps are described in the sections that follow.
In this section, you’ll define additional constants you’ll need to hook up printing for the moon facts window.
In Chapter 6 you
defined the constant kMTPApplicationSignature
for
the Moon Travel Planner application. In Chapter 8
you defined the constants kMTPPageSetupCommand
and kMTPPrintCommand
for
the Page Setup and Print commands, as well as kMTPMoonFactsTextKey
to
identify the moon facts window text. You’ll use all these constants
in your printing code.
Now you’ll need to define constants to use when you store
and retrieve printing information for the moon facts window and
when you display printing errors. Copy the following definitions to
the main.c
file, below
the definition for kMTPPrintCommand
:
#define kMTPPrintInfoProperty 'Piwp' #define KMTPPrintFormatStrKey "Error format"
The constant kMTPPrintInfoProperty
includes
the word “Property” because you’ll use it to store and retrieve
print information as a property of the moon facts window. (The print information
structure is described in Section 9.2.2.) You’ll use
the constant KMTPPrintFormatStrKey
to
obtain a localized format string to use when displaying printing
errors.
In this section, you’ll define a structure for storing print information, as well as pointer and function data types you’ll need for printing.
To print the contents of the moon facts window, you need a
convenient way to pass information among your printing functions
and in calls to Carbon Printing Manager functions. One such piece
of information is a structure to keep track of printing information.
You’ll define that structure below, but the C programming language
allows you to define a pointer to a structure before defining the
structure itself. You’ll do that, because you’ll need the structure
pointer for another type defined below. Copy the following type definition
to the main.c
file, right
after the last constant definition:
typedef struct MTPDocumentPrintInfo * MTPDocumentPrintInfoPtr;
You’ll also need a pointer to the function that draws one
page of the moon facts window. One of the parameters for the drawing
function is a pointer to our print information data structure. That’s
why you defined the pointer before the structure. To be able to
pass a pointer to a drawing function, copy the following type declaration
to the main.c
file, right after
the previous type definition:
typedef OSStatus (*MTPDrawDocPageProc) (MTPDocumentPrintInfoPtr documentPrintInfoPtr,UInt32 pageNumber);
You’ll see how this pointer type is used in Section 9.2.6, Section 9.2.4.1, and Section 9.2.4.10.
To keep track of printing information, the Moon Travel Planner
defines the MTPDocumentPrintInfo
structure.
Copy the following lines to the main.c
file,
after the MTPDrawDocPageProc
definition:
struct MTPDocumentPrintInfo { PMPrintSession printSession; // 1 PMPageFormat pageFormat; // 2 PMPrintSettings printSettings; // 3 PMSheetDoneUPP pageSetupDoneUPP; // 4 PMSheetDoneUPP printDialogDoneUPP; // 5 MTPDrawDocPageProc drawDocPageProc; // 6 }; typedef struct MTPDocumentPrintInfo MTPDocumentPrintInfo; // 7
This structure provides the following fields:
A printing
session to use for all moon facts printing. PMPrintSession
is
an opaque data structure defined by the Carbon Printing Manager.
A page format object to track format information, such as paper
size and orientation. PMPageFormat
is an opaque
data structure defined by the Carbon Printing Manager.
A print settings object to track settings information, such
as page range and number of copies. PMPrintSettings
is
an opaque data structure defined by the Carbon Printing Manager.
A pointer to a function the Carbon Printing Manager calls when the user closes the Page Setup dialog.
A pointer to a function the Carbon Printing Manager calls when the user closes the Print dialog.
A pointer to a function that draws one page of the moon facts
window. The definition for the pointer type (MTPDrawDocPageProc
)
is shown above.
The last item is not a field of the structure, but merely a type definition for the structure itself.
You’ll learn how to initialize this data structure in the MTPCreatePrintInfoForWindow
function.
In this section you’ll define function prototypes for the
Moon Travel Planner’s printing functions. Copy the following lines
to the main.c
file, right
after the function prototypes for window event and command handler
functions:
OSStatus MTPCreatePrintInfoForWindow ( WindowRef theWindow, MTPDrawDocPageProc drawDocPageProc, PMSheetDoneUPP pageSetupDoneProc, PMSheetDoneUPP printDialogDoneProc); OSStatus MTPSetupPageFormatForPrinting( MTPDocumentPrintInfoPtr documentPrintInfoPtr); pascal void MTPPageSetupDoneProc ( PMPrintSession printSession, WindowRef documentWindow, Boolean accepted); void MTPDoPageSetup (WindowRef parentWindow); pascal void MTPPrintDialogDoneProc ( PMPrintSession printSession, WindowRef documentWindow, Boolean accepted); void MTPDoPrint ( WindowRef parentWindow ); OSStatus MTPDrawMoonFactsPage ( MTPDocumentPrintInfoPtr documentPrintInfoPtr, UInt32 pageNumber); UInt32 MTPPagesInDoc (MTPDocumentPrintInfoPtr documentPrintInfoPtr); void MTPPostPrintingError (OSStatus status); void MTPDoPrintLoop (MTPDocumentPrintInfoPtr documentPrintInfoPtr);
These functions are described in the following section.
It’s time to write printing code for the Moon Travel Planner application. It may seem like you need a lot of functions to print some simple text, but the printing code in this chapter provides several benefits:
It demonstrates a variety of printing concepts, including how to display the Page Setup and Print dialogs and how to set up a print loop to print each page in a document.
It supports the really cool Mac OS X feature of sheets—the Page Setup and Print dialogs appear as sheets that pop out of the title bar of the moon facts window. A sheet is part of the window it appears on, providing a convenient way to associate a dialog with a window.
It provides a reusable template that you can easily adapt to print other, more complicated documents.
To print the text from the moon facts window, you’ll need to create the following functions:
MTPCreatePrintInfoForWindow
.
Initializes a print data structure and stores it with the window
to print.
MTPSetupPageFormatForPrinting
.
Prepares a valid page format object for use with page setup or printing and
creates a print session object.
MTPPageSetupDoneProc
. Called
by the Carbon Printing Manager when the user dismisses the Page
Setup dialog, regardless of whether the user accepted the dialog
or canceled it.
MTPDoPageSetup
. Displays the
Page Setup dialog (shown in Figure 9.1).
MTPPrintDialogDoneProc
. Called
by the Carbon Printing Manager when the user dismisses the Print
dialog, regardless of whether the user accepted the dialog or canceled
it.
MTPDoPrint
. Displays the Print
dialog (shown in Figure 9.2).
MTPDrawMoonFactsPage
. Draws the
specified page in the location specified by the page format object.
MTPPagesInDoc
. Returns the maximum
number of pages to print in the document.
MTPPostPrintingErrors
. Displays
an alert to notify the user that a printing error has occurred.
MTPDoPrintLoop
. Loops over the
specified page range, calling the draw page routine for each page.
The next sections describe the code for these functions.
The MTPCreatePrintInfoForWindow
function
initializes a print data structure and stores it with the window
to print (in this case, the moon facts window). Copy the code shown in Example 9.1 to the main.c
file,
below the MTPAboutWindowCommandHandler
function.
Example 9-1. A Function to Create Print Information for a Window
OSStatus MTPCreatePrintInfoForWindow ( WindowRef theWindow, MTPDrawDocPageProc drawDocPageProc, PMSheetDoneUPP pageSetupDoneProc, PMSheetDoneUPP printDialogDoneProc) { MTPDocumentPrintInfoPtr documentPrintInfoPtr = NULL; OSStatus status = noErr; documentPrintInfoPtr = (MTPDocumentPrintInfoPtr) NewPtr(sizeof(MTPDocumentPrintInfo));// 1 if (documentPrintInfoPtr) // 2 { documentPrintInfoPtr->printSession = NULL; // 3 documentPrintInfoPtr->pageSetupDoneUPP = pageSetupDoneProc; // 4 documentPrintInfoPtr->printDialogDoneUPP = printDialogDoneProc;// 5 documentPrintInfoPtr->drawDocPageProc = drawDocPageProc; // 6 documentPrintInfoPtr->pageFormat = kPMNoPageFormat; // 7 documentPrintInfoPtr->printSettings = kPMNoPrintSettings; // 8 status = SetWindowProperty(theWindow, kMTPApplicationSignature,// 9 kMTPPrintInfoProperty, sizeof(MTPDocumentPrintInfoPtr), &documentPrintInfoPtr); } else status = MemError(); // 10 if ((status != noErr) && (documentPrintInfoPtr != NULL)) // 11 DisposePtr( (Ptr) documentPrintInfoPtr); return status; // 12 }
Here’s how the MTPCreatePrintInfoForWindow
function
works:
It calls
the Memory Manager function NewPtr
to
allocate memory for a print information structure. If the call is
successful, the pointer documentPrintInfoPtr
points to
the allocated memory.
If the previous step was successful, it sets values for the print information structure.
It sets the printing session reference to NULL
,
indicating the object hasn’t been created yet. This object won’t
be created until it is needed—when the user opens the Page Setup or
Print dialog.
It stores a reference to a function the Carbon Printing Manager
calls when the user dismisses the Page Setup dialog. The reference
comes from the pageSetupDoneProc
parameter.
It stores a reference to a function the Carbon Printing Manager
calls when the user dismisses the Print dialog. The reference comes
from the printDialogDoneProc
parameter.
It stores a reference to a function the Moon Travel Planner’s MTPDoPrintLoop
function calls
to print a single page. The reference comes from the drawDocPageProc
parameter.
It sets the page format reference to a value that indicates the page format object hasn’t been created yet. This object won’t be created until it is needed—when the user opens the Page Setup or Print dialog.
It sets the print settings reference to a value that indicates the print settings object hasn’t been created yet. This object won’t be created until it is needed—when the user opens the Print dialog.
It calls the Window Manager function SetWindowProperty
to
store a pointer to the print information structure as a property
of the window. The constants kMTPApplicationSignature
and kMTPPrintInfoProperty
identify
the property, which other printing functions can access by calling
the Window Manager function GetWindowProperty
.
These constants are described in Section 9.2.1.
If it couldn’t create the print information structure by
calling NewPtr
, it calls the function MemError
,
which yields the result code produced by the last Memory Manager
function the application called directly (NewPtr
).
If there was an error after allocating the print information
structure, MTPDoPageSetup
calls the
Memory Manager function DisposePtr
to
dispose of the allocated memory.
It returns a value indicating whether the function successfully
created and initialized a print information structure for the window.
A return value of noErr
indicates
success.
The MTPSetupPageFormatForPrinting
function
prepares a valid page format object for use with the Page Setup
or Print dialog. Both of those dialogs require a printing session
object, so the function also creates a printing session object,
though it is possible to create a page format object without a printing
session. If any error occurs before the function completes, it frees
any objects it created.
Copy the code shown in Example 9.2 to the main.c
file,
below the MTPCreatePrintInfoForWindow
function.
Example 9-2. A Function to Set Up a Page Format Object for Printing
OSStatus MTPSetupPageFormatForPrinting( MTPDocumentPrintInfoPtr documentPrintInfoPtr) { OSStatus status = noErr; PMPrintSession printSession = NULL; PMPageFormat pageFormat = kPMNoPageFormat; status = PMCreateSession(&printSession); // 1 if (status == noErr) { if (documentPrintInfoPtr->pageFormat == kPMNoPageFormat) // 2 { status = PMCreatePageFormat(&pageFormat); // 3 if ((status == noErr) && (pageFormat != kPMNoPageFormat)) { status = PMSessionDefaultPageFormat( // 4 printSession, pageFormat); if (status == noErr) documentPrintInfoPtr->pageFormat = pageFormat; // 5 } else { if (status == noErr) // 6 status = kPMGeneralError; } } else { status = PMSessionValidatePageFormat( // 7 printSession, documentPrintInfoPtr->pageFormat, kPMDontWantBoolean); } } if (status == noErr) { documentPrintInfoPtr->printSession = printSession; // 8 } else // 9 { if (pageFormat != kPMNoPageFormat) PMRelease(pageFormat); if (printSession != NULL) PMRelease(printSession); } return status; }
Here’s how the MTPSetupPageFormatForPrinting
function
works:
It calls
the Carbon Printing Manager (CPM) function PMCreateSession
to
create a printing session object.
If the previous step succeeds, it checks whether the print
information structure pointed to by the documentPrintInfoPtr
parameter
already has a reference to a page format object. The constant kPMNoPageFormat
is
defined by the Carbon Printing Manager to specify that a page format
object (one of type PMPageFormat
) has not yet
been specified.
If no format object exists, it calls the CPM function PMCreatePageFormat
to
create a page format object.
If no error occurred in creating the page format object, it
calls the CPM function PMSessionDefaultPageFormat
to
set default values (such as 100 percent scaling and portrait orientation)
for a page format object.
If no error occurs in setting default values, it assigns the
page format object to the page format field in the print information
structure pointed to by the documentPrintInfoPtr
parameter.
If the function PMCreatePageFormat
didn’t
return an error in step 3, but was unable to create a page format
object, MTPSetupPageFormatForPrinting
uses
the CPM constant kPMGeneralError
to
set an error value.
If the print information structure pointed to by the passed
pointer already has a reference to a page format object, MTPSetupPageFormatForPrinting
calls the CPM function PMSessionValidatePageFormat
to
make sure the page format object is valid within the context of
the current printing session.
If no error has occurred, it assigns the printing session
object to the printing session field in the print information structure
pointed to by the documentPrintInfoPtr
parameter.
If any error has occurred, it releases any local variables it may have created (a printing session object and possibly a page format object).
It returns a value indicating whether the function successfully
created and initialized a printing session object and prepared a
page format object (creating it if necessary). A return value of noErr
indicates
success.
The Carbon Printing Manager calls the MTPPageSetupDoneProc
function
when the user dismisses the Page Setup dialog, regardless of whether
the user accepted the dialog or canceled it. This function merely
frees the current printing session object, which needn’t be saved
after the user dismisses the Page Setup dialog. However, applications
with more complicated printing needs may wish to perform other operations
here. For example, an application may wish to change the displayed
margins when a user changes the paper size in the Page Setup dialog.
Copy the code shown in Example 9.3 to the main.c
file,
below the MTPSetupPageFormatForPrinting
function.
Example 9-3. Function Called When Page Setup Dialog Is Dismissed
pascal void MTPPageSetupDoneProc ( PMPrintSession printSession, WindowRef documentWindow, Boolean accepted) { OSStatus status = noErr; // 1 UInt32 infoSize = sizeof (MTPDocumentPrintInfoPtr), actualSize; MTPDocumentPrintInfoPtr documentPrintInfoPtr = NULL; status = GetWindowProperty(documentWindow, // 2 kMTPApplicationSignature, kMTPPrintInfoProperty, infoSize, &actualSize, &documentPrintInfoPtr); if ((status == noErr) && (documentPrintInfoPtr != NULL)) // 3 { if (documentPrintInfoPtr->printSession != NULL) { PMRelease(documentPrintInfoPtr->printSession); documentPrintInfoPtr->printSession = NULL; } } }
Here’s how the MTPPageSetupDoneProc
function
works:
It declares and initializes local variables for getting stored print information from the passed window.
It calls the Window Manager function GetWindowProperty
,
passing the constants kMTPApplicationSignature
and kMTPPrintInfoProperty
to
specify the window property to retrieve (a pointer to a Moon Travel
Planner print information structure).
If it can get a pointer to the print info structure and if
the printing session field in the structure is not NULL
,
it releases the printing session object and sets the field to NULL
.
The MTPDoPageSetup
function displays
the Page Setup dialog (shown in Figure 9.1) so the user can specify
settings such as paper size and orientation.
Copy the code shown in Example 9.4 to the main.c
file,
below the MTPPageSetupDoneProc
function.
Example 9-4. The Page Setup Function
void MTPDoPageSetup( WindowRef parentWindow ) { OSStatus status = noErr; // 1 UInt32 infoSize = sizeof (MTPDocumentPrintInfoPtr), actualSize; MTPDocumentPrintInfoPtr documentPrintInfoPtr = NULL; status = GetWindowProperty(parentWindow, // 2 kMTPApplicationSignature, kMTPPrintInfoProperty, infoSize, &actualSize, &documentPrintInfoPtr); if ((status == noErr) && (documentPrintInfoPtr != NULL)) // 3 { status = MTPSetupPageFormatForPrinting( // 4 documentPrintInfoPtr); if (status == noErr) { Boolean accepted; PMSessionUseSheets ( // 5 documentPrintInfoPtr->printSession, parentWindow, documentPrintInfoPtr->pageSetupDoneUPP); status = PMSessionPageSetupDialog( // 6 documentPrintInfoPtr->printSession, documentPrintInfoPtr->pageFormat, &accepted); } } else if (status == noErr) // 7 status = memFullErr; if (status != noErr) // 8 { if (documentPrintInfoPtr != NULL) { if (documentPrintInfoPtr->printSession != NULL) // 9 { PMRelease(documentPrintInfoPtr->printSession); documentPrintInfoPtr->printSession = NULL; } } MTPPostPrintingError(status); // 10 } }
Here’s how the MTPDoPageSetup
function
works:
It declares and initializes local variables for getting stored print information from the passed window.
It calls the Window Manager function GetWindowProperty
,
passing the constants kMTPApplicationSignature
and kMTPPrintInfoProperty
to
specify the window property to retrieve (a pointer to a Moon Travel
Planner print information structure).
It verifies that GetWindowProperty
returned
a non-null pointer to a print information structure.
It calls the function MTPSetupPageFormatForPrinting
to
make sure the print information includes a valid page format object.
This function also creates a printing session object for the print
information pointer.
If no error occurs, it calls the Carbon Printing Manager function PMSessionUseSheets
, passing
the current printing session, the window to be printed, and a pointer
to a function the Printing Manager calls when the user dismisses
the Page Setup dialog. The pointer points to the MTPPageSetupDoneProc
function. Calling
the PMSessionUseSheets
function specifies
that a printing dialog (in this case the Page Setup dialog) should
be displayed as a sheet; that is, attached to the window of the
document being printed.
To display the Page Setup dialog, it calls the Carbon Printing
Manager function PMSessionPageSetupDialog
,
passing the current printing session, the page format object that
was set up in a previous step, and a pointer to a Boolean value.
When the user dismisses the Page Setup dialog, the Printing Manager
calls the function specified by documentPrintInfoPtr->pageSetupDoneUPP
in
the previous call to PMSessionUseSheets
. When
using sheets, the PMSessionPageSetupDialog
function
returns immediately and the Boolean value returned in the accepted
variable
always has the value false
.
If your application needs to perform additional tasks after the
user dismisses the Page Setup dialog, it can do so in the MTPPageSetupDoneProc
function,
which is called when the user dismisses the Page Setup dialog. If
the user clicks the OK button in the Page Setup dialog, the
page format object is updated with the user’s changes (if any)
and the value true
is
returned to the MTPPageSetupDoneProc
function.
If the user clicks the Cancel button, the page format object is
unchanged and the value false
is
returned to the MTPPageSetupDoneProc
function.
If the previous call to GetWindowProperty
was
unsuccessful in creating a pointer to a print information structure,
but did not return an error value, MTPDoPageSetup
sets
an error value.
It checks the status variable—if there is an error, the
Page Setup dialog was not displayed. The MTPDoPageSetup
function
will display an error message and may have some cleaning up to do
as well.
If an error occurred after getting a pointer to a print information
structure and the structure has a pointer to a session object,
it releases the object and sets the pointer to NULL
.
It displays an error message to the user.
The Carbon Printing Manager calls the MTPPrintDialogDoneProc
function
when the user dismisses the Print dialog, regardless of whether
the user accepts the dialog or cancels it. This function initiates
printing if the user accepted the Print dialog and frees the printing session
and print settings object if the user canceled the dialog.
Copy the code shown in Example 9.5 to the main.c
file,
below the MTPDoPageSetup
function.
Example 9-5. Function Called When Print Dialog Is Dismissed
pascal void MTPPrintDialogDoneProc (PMPrintSession printSession, WindowRef documentWindow, Boolean accepted) { OSStatus status = noErr; // 1 UInt32 infoSize = sizeof (MTPDocumentPrintInfoPtr), actualSize; MTPDocumentPrintInfoPtr documentPrintInfoPtr = NULL; status = GetWindowProperty(documentWindow, // 2 kMTPApplicationSignature, kMTPPrintInfoProperty, infoSize, &actualSize, &documentPrintInfoPtr); if ((status == noErr) && (documentPrintInfoPtr != NULL)) { if (accepted) MTPDoPrintLoop(documentPrintInfoPtr); // 3 else { if (documentPrintInfoPtr->printSettings // 4 != kPMNoPrintSettings) { PMRelease(documentPrintInfoPtr->printSettings); documentPrintInfoPtr->printSettings = kPMNoPrintSettings; } if (documentPrintInfoPtr->printSession != NULL) { PMRelease(documentPrintInfoPtr->printSession); documentPrintInfoPtr->printSession = NULL; } } } }
Here’s how the MTPPrintDialogDoneProc
function
works:
It declares and initializes local variables for getting stored print information from the passed window.
It calls the Window Manager function GetWindowProperty
,
passing the constants kMTPApplicationSignature
and kMTPPrintInfoProperty
to
specify the window property to retrieve (a pointer to a Moon Travel
Planner print information structure).
If step 2 succeeded and the user accepted the Print dialog
(accepted
has the value true
), MTPPrintDialogDoneProc
calls
the MTPDoPrintLoop
function to print
each page in the selected range. Applications with more complicated
printing needs can perform additional operations before commencing
printing.
If step 2 succeeded and the user canceled the Print dialog
(accepted
has the value false
), MTPPrintDialogDoneProc
calls
the Carbon Printing Manager function PMRelease
to
release the print information structure’s printing session object
and print settings object, if any, and sets the fields for these
objects to values indicating they are no longer valid. Releasing
an object decrements its reference count, causing it to be deallocated when
the count reaches 0. Other applications may need to perform
additional operations here in the case that the user cancels the
dialog.
The MTPDoPrint
function displays
the Print dialog (shown in Figure 9.2) so the user can specify
such settings as page range and number of copies before printing.
Copy the code shown in Example 9.6 to the main.c
file,
below the MTPPrintDialogDoneProc
function.
Example 9-6. The Print Function
void MTPDoPrint( WindowRef parentWindow ) { PMPrintSettings printSettings = kPMNoPrintSettings; UInt32 minPage = 1, maxPage = 1; OSStatus status = noErr; // 1 UInt32 infoSize = sizeof (MTPDocumentPrintInfoPtr), actualSize; MTPDocumentPrintInfoPtr documentPrintInfoPtr = NULL; status = GetWindowProperty( parentWindow, // 2 kMTPApplicationSignature, kMTPPrintInfoProperty, infoSize, &actualSize, &documentPrintInfoPtr); if ((status == noErr) && (documentPrintInfoPtr != NULL)) { status = MTPSetupPageFormatForPrinting(documentPrintInfoPtr); // 3 if (status == noErr) { status = PMCreatePrintSettings(&printSettings); // 4 if ((status == noErr) && (printSettings != kPMNoPrintSettings)) { CFStringRef windowTitleRef; status = CopyWindowTitleAsCFString(parentWindow, // 5 &windowTitleRef); if(status == noErr) { status = PMSetJobNameCFString(printSettings, // 6 windowTitleRef); CFRelease(windowTitleRef); } if (status == noErr) status = PMSessionDefaultPrintSettings( // 7 documentPrintInfoPtr->printSession, printSettings); if (status == noErr) { documentPrintInfoPtr->printSettings = printSettings;// 8 printSettings = kPMNoPrintSettings; } } if (status == noErr) { maxPage = MTPPagesInDoc(documentPrintInfoPtr); // 9 status = PMSetPageRange(documentPrintInfoPtr->printSettings, // 10 minPage, maxPage); } if (status == noErr) { Boolean accepted; PMSessionUseSheets ( // 11 documentPrintInfoPtr->printSession, parentWindow, documentPrintInfoPtr->printDialogDoneUPP); status = PMSessionPrintDialog( // 12 documentPrintInfoPtr->printSession, documentPrintInfoPtr->printSettings, documentPrintInfoPtr->pageFormat, &accepted); } } } else { if (status == noErr) // 13 status = memFullErr; } if (status != noErr) // 14 { if (documentPrintInfoPtr != NULL) { if (printSettings != kPMNoPrintSettings) // 15 PMRelease(printSettings); else if (documentPrintInfoPtr->printSettings // 16 != kPMNoPrintSettings) { PMRelease(documentPrintInfoPtr->printSettings); documentPrintInfoPtr->printSettings = kPMNoPrintSettings; } if (documentPrintInfoPtr->printSession != NULL) // 17 { PMRelease(documentPrintInfoPtr->printSession); documentPrintInfoPtr->printSession = NULL; } } MTPPostPrintingError(status); // 18 } }
Here’s how the MTPDoPrint
function
works:
It declares and initializes local variables for getting stored print information from the passed window.
It calls the Window Manager function GetWindowProperty
,
passing the constants kMTPApplicationSignature
and kMTPPrintInfoProperty
to
specify the window property to retrieve (a pointer to a Moon Travel
Planner print information structure).
If GetWindowProperty
successfully
returns a valid pointer to a print information structure, MTPDoPrint
calls
the MTPSetupPageFormatForPrinting
function
to make sure the print information structure has a valid page format
object for this printing session. That function also creates a printing
session object for the session.
If no error occurs, it calls the Carbon Printing Manager (CPM)
function PMCreatePrintSettings
to create
a print settings object for this print job. By not saving print
settings between calls to the Print dialog, you ensure that the
dialog will display with the current default settings, which is
the recommended behavior.
If no error occurs, it calls the Core Foundation function CopyWindowTitleAsCFString
to get
the window title as a CFStringRef
(or reference
to a string object).
If no error occurs, it calls the CPM function PMSetJobNameCFString
to
set the job name for this print job. This is the name that will
be used by the printing system, such as in the print queue. After setting
the title, it calls the Core Foundation function CFRelease
to release the string.
If no error occurs in the previous step, it calls the CPM
function PMSessionDefaultPrintSettings
to
set default values (such as printer, number of copies, and page range)
for the print settings object.
If no error occurs in the previous step, it assigns the print settings object to the print settings field in the print information structure and sets the local settings variable to a value indicating it is no longer in use.
It calls the function MTPPagesInDoc
so
the Moon Travel Planner can specify the maximum number of pages
to print.
It calls the CPM function PMSetPageRange
to
set the valid range of pages that can be printed.
If no error has occurred, it calls the CPM function PMSessionUseSheets
,
passing the current printing session, a reference to the window
to be printed, and a pointer to a function the Carbon Printing Manager
calls when the user dismisses the Print dialog. The pointer points
to the MTPPrintDialogDoneProc
function. Calling
the PMSessionUseSheets
function specifies
that a printing dialog (in this case the Print dialog) should be
displayed as a sheet; that is, attached to the window of the document
being printed.
To display the Print dialog, MTPDoPrint
calls
the CPM function PMSessionPrintDialog
, passing
the current printing session, the print settings object, the page
format object, and a pointer to a Boolean value. When the user dismisses
the Print dialog, the Carbon Printing Manager calls the function
specified by documentPrintInfoPtr->printDialogDoneUPP
in
the previous call to PMSessionUseSheets
(in
this case, MTPPrintDialogDoneProc
,
which takes care of printing the page if the user accepts the dialog). When
using sheets, the PMSessionPrintDialog
function
returns immediately and the Boolean value returned in the accepted
variable
always has the value false
. If
your application needs to perform additional tasks after the user
dismisses the Print dialog, it can do so in the MTPPrintDialogDoneProc
function. If
the user clicks the OK button in the Print dialog, the print settings
object is updated with the user’s changes (if any) and the value true
is
returned to the MTPPrintDialogDoneProc
function.
If the user clicks the Cancel button, the print settings object
is unchanged and the value false
is
returned to the MTPPrintDialogDoneProc
function.
If the earlier call to GetWindowProperty
was
unsuccessful in creating a pointer to a print information structure,
but did not return an error value, MTPDoPrint
sets
an error value.
It checks for any error. At this point, if an error has occurred, you know the Print dialog was never displayed. If an error occurred after getting a pointer to a print information structure, you may have some cleaning up to do—otherwise, you’ll just display the error number.
If the local print settings variable has a reference to a
print settings object, MTPDoPrint
calls
the CPM function PMRelease
to release
it. Releasing an object decrements its reference count, causing
it to be deallocated when the count reaches 0.
If the print information structure has a print settings object, MTPDoPrint
calls
the CPM function PMRelease
to release
it and sets the field for this object to a value indicating it is
no longer valid.
If the print information structure has a printing session
object, MTPDoPrint
calls the CPM function PMRelease
to
release it and sets the field for this object to a value indicating
it is no longer valid.
It calls the function MTPPostPrintingError
to
display the error to the user.
The MTPDrawMoonFactsPage
function
draws the specified page in the location specified by the page format object.
Copy the code shown in Example 9.7 to the main.c
file,
below the MTPDoPrint
function.
Example 9-7. Function to Draw One Page of the Moon Facts Window
OSStatus MTPDrawMoonFactsPage( MTPDocumentPrintInfoPtr documentPrintInfoPtr, UInt32 pageNumber) { OSStatus status = noErr; Rect bounds; CFStringRef text; PMRect pageRect; status = PMGetAdjustedPageRect( documentPrintInfoPtr->pageFormat, &pageRect); // 1 if (status == noErr) { text = CFCopyLocalizedString(CFSTR(kMTPMoonFactsTextKey),NULL);// 2 if (text != NULL) { SetRect(&bounds, pageRect.left, pageRect.top, // 3 pageRect.right, pageRect.bottom); TXNDrawCFStringTextBox (text, &bounds, NULL, NULL); // 4 CFRelease(text); // 5 } else status = coreFoundationUnknownErr; // 6 } return status; // 7 }
Here’s how the MTPDrawMoonFactsPage
function
works:
It calls
the Carbon Printing Manager function PMGetAdjustedPageRect
to
obtain the page size (the imageable area), in points, taking into
account orientation, application drawing resolution, and scaling
settings.
It calls the Core Foundation string function CFCopyLocalizedString
to
get the text that is displayed in the moon facts window.
If no error occurs, it sets a drawing location using the previously obtained page size information. This sample code sticks to printing one page, so it always prints the same text regardless of the specified page number. Applications that print more complicated documents would determine the correct material to print for the specified page.
It calls the Multilingual Text Engine (MLTE) function TXNDrawCFStringTextBox
to
draw the text from the moon facts window in the specified rectangle.
It calls the Core Foundation function CFRelease
to
release the string created by the call to CFCopyLocalizedString
.
If it was unable to obtain the text string in the previous
call to CFCopyLocalizedString
, it sets
an error value, using the constant coreFoundationUnknownErr
(for
an unknown Core Foundation error).
It returns a status value, indicating whether it succeeded in drawing the page.
The moon facts window has very simple printing needs and only
draws one page of text. More complex applications are likely to
require more complex drawing and page range calculations. Related
issues are described in the next section on the MTPPagesInDoc
function.
The MTPPagesInDoc
function returns
the maximum number of pages to print in the document. Copy the code shown in Example 9.8 to the main.c
file,
below the MTPDrawMoonFactsPage
function.
Example 9-8. A Function to Supply the Number of Pages in a Document
UInt32 MTPPagesInDoc(MTPDocumentPrintInfoPtr documentPrintInfoPtr) { PMRect paperRect, pageRect; UInt32 numPages = 1; OSStatus status = noErr; status = PMGetAdjustedPaperRect( documentPrintInfoPtr->pageFormat, &paperRect);// 1 if (status == noErr) { status = PMGetAdjustedPageRect( documentPrintInfoPtr->pageFormat, &pageRect); // 2 if (status == noErr) numPages = 1; // 3 } return numPages; // 4 }
Here’s how the MTPPagesInDoc
function
works:
It calls
the Carbon Printing Manager (CPM) function PMGetAdjustedPaperRect
to
obtain the paper size, taking into account orientation, application
drawing resolution, and scaling settings. Applications that print
more complicated documents could use this information in determining
the number of pages in the document based on the current print settings.
If no error occurred, it calls the CPM function PMGetAdjustedPageRect
to
obtain the page size (the imageable area), in points, taking into
account orientation, application drawing resolution, and scaling
settings. Again, some applications may need to use this information
to compute the number of pages in the document.
If no error occurred, it sets a hard-coded value of one for the number of pages. For most applications, your page count may vary.
It returns the computed number of pages to print in the document.
Of course, most applications will need to do more work than
shown here. The number of pages in a document is likely to depend
on a number of factors, including document-specific changes by the
user (such as adding text or changing font size), as well as Page
Setup and Print dialog settings. Some applications might require
a separate version of this function for each kind of document they
print. If so, you might add a field to the print information structure
(described in Section 9.2.2) to specify the
page count function to be called from the main printing function (in
this case, MTPDoPrint
).
The MTPPostPrintingErrors
function
displays an alert to notify the user that a printing error has occurred. The
alert message includes a localized string and the error number. Copy
the code shown in Example 9.9 to the main.c
file,
below the MTPPagesInDoc
function.
Example 9-9. A Function to Post a Printing Error Alert
void MTPPostPrintingError(OSStatus status) { CFStringRef formatStr = NULL, printErrorMsg = NULL; SInt16 alertItemHit = 0; Str255 stringBuf; // Display any error except user cancelled. if ((status != noErr) && (status != kPMCancel)) // 1 { formatStr = CFCopyLocalizedString( // 2 CFSTR(KMTPPrintFormatStrKey), NULL); if (formatStr != NULL) { printErrorMsg = CFStringCreateWithFormat( // 3 NULL, NULL, formatStr, status); if (printErrorMsg != NULL) { if (CFStringGetPascalString(printErrorMsg, // 4 stringBuf, sizeof(stringBuf), kCFStringEncodingASCII)) StandardAlert(kAlertStopAlert, stringBuf, // 5 NULL, NULL, &alertItemHit); CFRelease(printErrorMsg); // 6 } CFRelease(formatStr); // 7 } } }
Here’s how the MTPPostPrintingErrors
function
works:
It checks
whether the passed error should be displayed. Any error except kPMCancel
, indicating
the user cancelled printing, should be displayed.
It calls the Core Foundation string function CFCopyLocalizedString
to
get a localized formatting string that indicates how the error should
be formatted for display.
If no error occurs, it calls the Core Foundation string function CFStringCreateWithFormat
to
get a copy of the string that includes the error number.
If no error occurs, it calls the Core Foundation string function CFStringGetPascalString
to
get a copy of the string that it can display in a standard alert
dialog.
It calls the Carbon function StandardAlert
to
display the error message, including the error number.
It calls the Core Foundation function CFRelease
to
release the string (printErrorMsg
) created
by the call to CFCopyLocalizedString
.
It calls the Core Foundation function CFRelease
to
release the string (formatStr
)
created by the call to CFStringCreateWithFormat
.
The MTPDoPrintLoop
function loops
over the specified page range, calling your draw page function for
each page. This function is the Saturn V rocket of printing functions—it
does a lot of heavy lifting. But because it does most of its work
by calling Carbon Printing Manager functions, passing information
obtained from a document print information structure you defined,
you should be able to adapt this print loop code for applications with
more sophisticated printing requirements.
Copy the code shown in Example 9.10 into the main.c
file,
below the MTPPostPrintingErrors
function.
Example 9-10. A Print Loop Function
void MTPDoPrintLoop(MTPDocumentPrintInfoPtr documentPrintInfoPtr) { OSStatus status = noErr, tempErr = noErr; UInt32 realNumberOfPagesinDoc, pageNumber, firstPage, lastPage; GrafPtr currentPort, printingPort; realNumberOfPagesinDoc = MTPPagesInDoc(documentPrintInfoPtr); // 1 status = PMGetFirstPage( documentPrintInfoPtr->printSettings, &firstPage); // 2 if (status == noErr) status = PMGetLastPage( documentPrintInfoPtr->printSettings, &lastPage); // 3 if (status == noErr) { if (realNumberOfPagesinDoc < lastPage) // 4 lastPage = realNumberOfPagesinDoc; status = PMSetFirstPage( // 5 documentPrintInfoPtr->printSettings, firstPage, false); if (status == noErr) status = PMSetLastPage( // 6 documentPrintInfoPtr->printSettings, lastPage, false); } if (status == noErr) { status = PMSessionBeginDocument( // 7 documentPrintInfoPtr->printSession, documentPrintInfoPtr->printSettings, documentPrintInfoPtr->pageFormat); if (status == noErr) { pageNumber = firstPage; while ((pageNumber <= lastPage) && (status == noErr) // 8 && (PMSessionError(documentPrintInfoPtr->printSession) == noErr)) { status = PMSessionBeginPage( // 9 documentPrintInfoPtr->printSession, documentPrintInfoPtr->pageFormat, NULL); if (status == noErr) // 10 { GetPort(¤tPort); // 11 status = PMSessionGetGraphicsContext( // 12 documentPrintInfoPtr->printSession, kPMGraphicsContextQuickdraw, (void**) &printingPort); if (status == noErr) { SetPort(printingPort); // 13 if (documentPrintInfoPtr->drawDocPageProc) // 14 status = (*(documentPrintInfoPtr->drawDocPageProc)) (documentPrintInfoPtr, pageNumber); SetPort(currentPort); // 15 } tempErr = // 16 PMSessionEndPage(documentPrintInfoPtr->printSession); if (status == noErr) status = tempErr; pageNumber++; // 17 } } tempErr = // 18 PMSessionEndDocument(documentPrintInfoPtr->printSession); if (status == noErr) status = tempErr; } } tempErr = PMSessionError(documentPrintInfoPtr->printSession); // 19 if (status == noErr) status = tempErr; if (status != noErr) MTPPostPrintingError(status); // 20 if (documentPrintInfoPtr->printSettings != kPMNoPrintSettings) // 21 { PMRelease(documentPrintInfoPtr->printSettings); documentPrintInfoPtr->printSettings = kPMNoPrintSettings; } if (documentPrintInfoPtr->printSession != NULL) // 22 { PMRelease(documentPrintInfoPtr->printSession); documentPrintInfoPtr->printSession = NULL; } }
The MTPDoPrintLoop
function assumes
it has been passed a valid pointer to a properly initialized print information
structure for the document to print. Here’s how the function works:
It calls
the function MTPPagesInDoc
so the Moon
Travel Planner can specify the maximum number of pages to print.
It calls the Carbon Printing Manager (CPM) function PMGetFirstPage
to
obtain the number of the first page to be printed.
You
must use 32-bit unsigned containers to obtain the first page and
last page values because in some cases the PMGetFirstPage
and PMGetLastPage
functions
may return very large values from the print settings data structure. You
should not use the constant kPrintAllPages
in
your print loop. That constant is used only with the PMSetLastPage
and PMSetPageRange
functions
to specify a last page. It is not returned by the PMGetLastPage
function
and your code should not look for it here.
It calls the CPM function PMGetLastPage
to
obtain the number of the last page to be printed.
If the user specified a last page greater than the number
of pages in the document, MTPDoPrintLoop
sets
the last page to the real last page in the document.
It calls the CPM function PMSetFirstPage
to
set the first page of the page range in the print settings object
for this print job.
It calls the CPM function PMSetLastPage
to
set the last page of the page range in the print settings object
for this print job. Setting the first and last page provides information
to the progress dialog that is shown during printing.
It calls the CPM function PMSessionBeginDocument
to
establish a new print job. Note that if no error results from this
call, the ensuing code will always call PMSessionEndDocument
to
end the print job.
It sets up a while
loop
to loop over the range of pages selected for printing. Note that the
loop will terminate if any function returns an error (that is, if
the variable status
has a
value other than noErr
)
or if the CPM function PMSessionError
returns
an error.
It calls the CPM function PMSessionBeginPage
to inform
the printing system that the drawing that follows is part of a
new page.
It checks the return status after calling PMSessionBeginPage
.
Note that in the case that no error occurs, the ensuing code will
always call PMSessionEndPage
to
finish printing the current page.
It calls the QuickDraw function GetPort
to
save the current graphics port.
It calls the CPM function PMSessionGetGraphicsContext
to
obtain the QuickDraw graphics port for the page being printed.
It calls the QuickDraw function SetPort
to
set the graphics port to the port obtained in the previous step.
You must do this before calling the document’s function to draw
one page.
If the print information pointer has a valid drawDocPageProc
field, MTPDoPrintLoop
calls that
function to draw the current page.
It calls the SetPort
function
again to set the current port back to the saved port after drawing
one page.
It calls the CPM function PMSessionEndPage
to
finish printing the current page. It uses a temporary variable to
get the status for this function; then, if an error occurs, and there was
no previous error, it sets
the status variable to that error value. This approach ensures that
the current page is always finished and that if any error occurs
the loop terminates.
It increments the page count within the page-printing loop.
On completion of the page-printing loop, MTPDoPrintLoop
calls
the CPM function PMSessionEndDocument
to complete
the print job.
It calls the CPM function PMSessionError
to
determine if any printing error has occurred. It again uses a temporary
variable to get the status for this function to ensure that the
print job is always completed and that if any error occurs, it is
reported to the user.
If the previous step detects a printing error, MTPDoPrintLoop
calls
the MTPPostPrintingErrors
function
to display the error to the user.
Regardless of whether any error has occurred, it releases the print information structure’s print settings object, if any, and sets the field for this object to a value indicating it is no longer valid.
Regardless of whether any error has occurred, it releases the print information structure’s printing session object, if any, and sets the field for this object to a value indicating it is no longer valid.
In Section 9.2.1 you defined the
constant KMTPPrintFormatStrKey
to
obtain a localized format string for use in displaying error strings.
You saw how that constant was used in the MTPPostPrintingError
function.
In this section you’ll add the format string to Moon Travel Planner’s Localizable.strings
file, which
you learned about in Chapter 8.
To add a format string to the Localizable.strings
file, you
perform these steps:
Open the Resources folder in the Moon Travel Planner project.
Click on the Localizable.strings
file.
After the last current key/value entry and before the closing right bracket (}), type a semicolon (;).
Insert the following line after the semicolon; the
characters %ld
are C
formatting instructions for a long decimal value to display the error
number:
"Error format" = "Printing error: %ld"
To set up the Moon Travel Planner to print the text from the
moon facts window, you add code to the main
function
in several places. First, add the following variable declaration
at the top of the function with the other variables:
PMSheetDoneUPP pageSetupDoneUPP, printDialogDoneUPP;
The PMSheetDoneUPP
data type is defined
by the Carbon Printing Manager (CPM) as a pointer to a sheet done
function. The CPM calls your sheet done function when the user dismisses
a printing dialog that is being shown as a sheet (that is, attached
to the window of the document being printed).
Next you’ll use these function pointer variables to set up printing information for the moon facts window. Add the code shown in Example 9.11 to the main function after the code to install the various window event handlers.
Example 9-11. Code to Initialize a Print Information Structure for a Window
pageSetupDoneUPP = NewPMSheetDoneUPP(&MTPPageSetupDoneProc); // 1 if (pageSetupDoneUPP == NULL) goto CantSetUpPrinting; printDialogDoneUPP = NewPMSheetDoneUPP (&MTPPrintDialogDoneProc); // 2 if (printDialogDoneUPP == NULL) goto CantSetUpPrinting; err = MTPCreatePrintInfoForWindow(gMoonFactsWindow, // 3 (MTPDrawDocPageProc) MTPDrawMoonFactsPage, pageSetupDoneUPP, printDialogDoneUPP); require_noerr (err, CantSetUpPrinting );
Here’s what the code in Example 9.11 does:
It calls
the Carbon Printing Manager function NewPMSheetDoneUPP
to
create a universal procedure pointer (a type of function pointer)
to the MTPPageSetupDoneProc
function, which
is described in Section 9.2.4.3. If
the call is unsuccessful, the code branches to a failure exit label, CantSetUpPrinting
.
It calls NewPMSheetDoneUPP
again
to create a universal procedure pointer to the MTPPrintDialogDoneProc
function,
which is described in Section 9.2.4.5. If
the call is unsuccessful, the code branches to a failure exit label, CantSetUpPrinting
.
The Moon Travel Application creates these universal procedure pointers
for printing and keeps them throughout the life of the application.
In many cases, however, an application creates a universal procedure
pointer, uses it, then disposes of it. To dispose of a universal
procedure pointer of type PMSheetDoneUPP
, you call
the Carbon Printing Manager function DisposePMSheetDoneUPP
.
It calls the MTPCreatePrintInfoForWindow
function
to create a print information structure and attach a pointer to
it as a property of the moon facts window. It passes the following
values:
gMoonFactsWindow
.
A pointer to the moon facts window
(MTPDrawDocPageProc) MTPDrawMoonFactsPage
.
A pointer to the function to draw one page of the moon facts window
pageSetupDoneUPP
.
A pointer to the function to call when the user dismisses the Page Setup
dialog
printDialogDoneUPP
.
A pointer to the function to call when the user dismisses the Print
dialog
If the MTPCreatePrintInfoForWindow
function
returns an error, the code branches to a failure exit label, CantSetUpPrinting
.
The macro require_noerr
is described
in Chapter 3.
You use it to check for “show stopping” errors and jump to a label
to exit the application.
Finally, you’ll add a failure label to branch to if there
is an error while initializing printing. Copy the following line
below CantGetNibRef
:
CantSetUpPrinting:
In Chapter 8 you
added code to the MTPMoonFactsWindowEventHandler
function
to handle the Page Setup and Print commands, but that code was commented
out because you hadn’t written the functions yet. Now you can
remove the comment characters so that the code looks like this:
case kMTPPrintCommand: MTPDoPrint (gMoonFactsWindow); result = noErr; break; case kMTPPageSetupCommand: MTPDoPageSetup (gMoonFactsWindow); result = noErr; break;
To test the Page Setup command from the moon facts window, you perform the following steps. These steps assume you have a printer available and have already selected a default printer. If you haven’t selected a default printer, Mac OS X will prompt you to do so.
Click the Build button in the upper-left corner of the Moon Travel Planner project window.
Click the Run button in the upper-left corner of the project window.
Choose Facts for the Traveler in the Moon menu or press Command-F.
To open the Page Setup dialog, choose Page Setup in the File menu or press Command-Shift-P. The resulting Page Setup dialog is shown in Figure 9.1.
Change any setting in the Page Setup dialog and close the dialog by clicking the OK button. For example, you might click one of the icons for landscape paper orientation.
Open the Page Setup dialog as before. You should see that any changes you made are still present.
To open the Print dialog, choose Print in the File menu or press Command-P. The resulting Print dialog is shown in Figure 9.2.
Change any setting in the Print dialog. For example, you might change the printer or change the number of copies from 1 to 2.
Click either the Preview button to get a preview of the printed document, or the Print button to send it to the printer. Any changes you made in the Page Setup or Print dialogs should be reflected in the resulting printed or previewed document.
If you repeat the previous steps to open the Page Setup dialog, it should still reflect the changes you made. The Print dialog, however, should still reflect the original (default) settings.
In this chapter, you learned about the Carbon Printing Manager, which allows your Carbon application to take advantage of printing features available in Mac OS X, while still supporting printing in earlier versions of the Mac OS. You took a look at printing sessions (to perform one printing task), print loops (to print a range of pages), and print function sequence and scope (which determine the order in which you must call certain printing functions).
You saw that the Carbon Printing Manager provides opaque data types for key objects—the printing session, page format, and print settings objects—as well as functions for creating, modifying, extending, and saving these objects.
Finally, you added printing code to the Moon Travel Planner application to print the Facts for the Traveler window. Printing code can be a little daunting, but the code in this chapter should provide a good template for leveraging the support provided by the Carbon Printing Manager.