TweetFollow Us on Twitter

Sep 99 Getting Started

Volume Number: 15 (1999)
Issue Number: 9
Column Tag: Getting Started

More Printing

by Dan Parks Sydow

How a Mac program opens and prints a document

In last month's Getting Started article you were introduced to printing. In that article we described the basics of the Printing Manager and the basics of getting an application to print. In that article's PrintPict example program you saw how a program could load PICT resource data to memory and then print the resulting picture. While that example served the purpose of demonstrating how to send data to a printer, it didn't provide an entirely realistic environment in which to implement printing. This month we'll remedy that. Here we'll dig a little deeper into printing techniques to see how a program provides the user with the ability to open any PICT file (one of the most common formats for Macintosh picture files), display the file's picture in a window, and then print that picture. This month's PrintDoc example is a menu-driven program that provides you with all the code you need to easily add a document-printing feature to your own program.

Printing Basics Recap

Last month you saw how to write a program that displays the two standard printing dialog boxes found in most Mac programs. Figures 1 and 2 provide examples of the Printing Style dialog box and the Printing Job dialog box. Recall that the exact look of these dialog boxes is dependent on the printer connected to the user's Mac.


Figure 1. A typical Printing Style dialog box.


Figure 2. A typical Printing Job dialog box.

To display the Printing Style dialog box your program calls the Printing Manager function PrStlDialog(). To display the Printing Job dialog box your program makes a call to the Printing Manager function PrJobDialog(). Before calling either function your program must declare a THPrint variable and call NewHandle() or NewHandleClear() in order to create a new print record and to receive a handle to that record. A call to PrintDefault() then fills the record with default values. User-entered values originating in either the Printing Style or Printing Job dialog boxes can alter some or all of the values in this record.

THPrint		printRecord;
Boolean		doPrint;
printRecord = (THPrint)NewHandleClear( sizeof (TPrint) );
PrintDefault( printRecord );
PrStlDialog( printRecord );
doPrint = PrJobDialog( printRecord );

For printing a document, the display of the Printing Style and Printing Job dialog boxes are important, user-friendly steps. However, printing can be accomplished without calls to PrStlDialog() and PrJobDialog(). Printing can't take place, though, without making calls to a few other very important Printing Manager functions.

A call to PrOpen() prepares the current printer resource file (the one associated with the printer the user has selected from the Chooser) for use. PrOpenDoc() initializes a printing graphics port-the port to which all subsequent QuickDraw commands are to be sent. PrOpenPage()begins the definition of a single page to be printed.

PrClosePage() signals the end of the current page. PrCloseDoc() closes a printing graphics port. PrClose() closes the Printing Manager and the printer resource file.

TPPrPort		printerPort;
PrOpen();
printerPort = PrOpenDoc( printRecord, nil, nil );
PrOpenPage( printerPort, nil );
// QuickDraw calls that define what's to be printed
PrClosePage( printerPort );  
PrCloseDoc( printerPort );
PrClose();

Printing a Document

When last month's example program is run, the Printing Style and Printing Job dialog boxes mysteriously appear and a picture from an unknown (to the user) source prints. No window opens, so to the user, there's no apparent origin of the graphics that get printed. A more realistic use of printing would be for an application to bring up the Printing Style and Printing Job dialog boxes only when requested by the user. When the user opts to print, then the contents of the frontmost window should be sent to the printer.

To implement a useful printing scheme, begin by creating a print record. Use a global variable to keep track of the record. Call PrintDefault() to fill the record with default values.

THPrint	gPrintRecord;
gPrintRecord = (THPrint)NewHandleClear( sizeof(TPrint) );
PrOpen();
PrintDefault( gPrintRecord );
PrClose();

In the above code, the call to the Printing Manager function PrintDefault() is nested between calls to PrOpen() and PrClose(). Last month's example called PrOpen() near the start of the program and PrClose() near the end. That worked fine for our short, simple program. For a more sophisticated program, it's wiser to open and close the printing resource file at each use. Recall that when an application launches, its resource fork is opened by the system. When PrOpen() is called, the printer resource file is opened. At that point, the program has two resource files open. When making certain Toolbox calls, this has to be kept in mind (as it's possible for two resource forks to each include a resource of the same type and ID). If your program closes the printing resource after using it, you won't have to be concerned with issues such as which resource file is considered the current file.

Your printing-ready program should include a Page Setup item in the File menu. Handling a Page Setup choice involves little more than calling PrStlDialog(). A File menu-handling routine would include a case section similar to this one:

case iPageSetup:
	PrOpen();   
	PrStlDialog( gPrintRecord );
	PrClose();   
	break;

Your print-capable program will of course include a Print item in the File menu. Handling a Print choice begins with a determination of the front window. A call to the Toolbox routine FrontWindow() takes care of that task. Next, a call to PrJobDialog() displays the Printing Job dialog box. If from this dialog box the user cancels printing, there's nothing your program needs to do. If the user instead clicks the Print button, you'll invoke an application-defined function that carries out the printing.

WindowPtr	window;
Boolean		doPrint;
case iPrint:
	window = FrontWindow();
	PrOpen();
	doPrint = PrJobDialog( gPrintRecord );
	if ( doPrint == true )
		DoPrintChoice( window );
	PrClose();
	break;

Regardless of the contents of the window, your print-handling routine should look similar to the DoPrintChoice() function shown below. That's because this function does the busy work of getting ready for printing and cleaning up after printing. What actually gets printed is determined in the application-defined DrawToPort() routine.

void		DoPrintChoice( WindowPtr window )
{
	TPPrPort		printerPort; 
	printerPort = PrOpenDoc( gPrintRecord, nil, nil );    
	PrOpenPage( printerPort, nil );
	DrawToPort( window );
	PrClosePage( printerPort );  
	PrCloseDoc( printerPort );
}

PrintDoc

This month's program is PrintDoc. When you run PrintDoc, you see a menu bar that holds the File menu pictured in Figure 3. Choosing the Open item displays the standard Open dialog box pictured in Figure 4. From this dialog box you can select and open any picture file (any file of type PICT) you have available.


Figure 3. The File menu from the PrintDoc program.


Figure 4. Choosing a picture file to open.

After selecting a file, PrintDoc opens it, loads the picture data to memory, and displays the resulting picture in its own window. Figure 5 shows that PrintDoc sizes the window to match the size of the picture. A window can be dragged and closed. PrintDoc allows for any number of windows to be open (memory permitting). When one or more windows are on the screen, the program enables the File menu items that are initially disabled. As shown in Figure 3, these are the Close, Page Setup, and Print items. Closing all windows again disables those same items.


Figure 5. A typical picture displayed in the PrintDoc program.

Choosing Page Setup from the File menu displays the Printing Style dialog box. Choosing Print from the File menu displays the Printing Job dialog box. If you click the Print button, the program prints the picture that's in the frontmost window.

Because the program doesn't handle any type of editing, all of the items in the Edit menu are disabled.

Creating the PrintDoc Resources

Start the project by creating a new folder named PrintDoc in your CodeWarrior development folder. Start up ResEdit, then create a new resource file named PrintDoc.rsrc. Make sure to specify the PrintDoc folder as the resource file's destination. The resource file will hold resources of the types shown in Figure 6.


Figure 6. The PrintDoc resources.

Figure 6 shows the three MENU resources. After creating these resources, create a single MBAR resource that includes MENU IDs 128, 129, and 130.

The one ALRT and one DITL resource are used to define the program's error-handling alert. The only other resource needed is a single WIND. This resource serves as the window that's to display a picture. Because the program resizes the window to match the size of the picture that's to be displayed, the WIND boundaries you specify are unimportant.

Creating the PrintDoc Project

Launch CodeWarrior and choose New Project from the File menu. As usual, choose the MacOS:C_C++:MacOS Toolbox:MacOS Toolbox Multi-Target project stationary for the new project. You already have a project folder created, so uncheck the Create Folder check box before clicking the OK button. Name the project PrintDoc.mcp, and make sure the project's destination is the PrintDoc folder.

Add the PrintDoc.rsrc resource file to the project. Remove the SillyBalls.rsrc file. Because the project doesn't make use of any ANSI libraries, you can go ahead and remove the ANSI Libraries folder from the project window if you want.

Create a new source code window by choosing New from the File menu.. Save the window, giving it the name PrintDoc.c. Choose Add Window from the Project menu to add this empty file to the project. Now remove the SillyBalls.c placeholder file from the project window. You're all ready to type in the source code.

To save yourself the work involved in typing all the source code, visit MacTech's ftp site at <ftp://ftp.mactech.com/src/>. There you'll find the PrintDoc source code file available for downloading.

Walking Through the Source Code

PrintDoc begins with a number of constants. Most of the constants define menu-related resources and items. Exceptions are kSleep and kMoveToFront, which are used in calls to WaitNextEvent() and GetNewCWindow(), respectively.

/********************* constants *********************/
#define		kWINDResID			128
#define		kMBARResID			128
#define		kALRTResID			128
#define		kSleep					7
#define		kMoveToFront		(WindowPtr)-1L
#define		mApple					128
#define		iAbout					8
#define		mFile					129
#define		iOpen					1
#define		iClose					3
#define		iPageSetup			5
#define		iPrint					6
#define		iQuit					8
#define		mEdit					130
#define		iUndo					1
#define		iCut						3
#define		iCopy					4
#define		iPaste					5
#define		iClear					6

PrintDoc declares four global variables. The THPrint variable gPrintRecord is the print record that holds information about the currently selected printer. The MenuHandle variables gFileMenuHandle and gEditMenuHandle are used in the enabling and disabling of items in the menus referenced by these handles. The Boolean variable gDone is used to signal the end of the program's execution.

/****************** global variables *****************/
Boolean			gDone;
THPrint			gPrintRecord;
MenuHandle		gFileMenuHandle;
MenuHandle		gEditMenuHandle;

Next come the program's function prototypes.

/********************* functions *********************/
void				ToolBoxInit( void );
void				MenuBarInit( void );
void				PrintingInit( void );
void				OpenPictureDisplayWindow( void );
								PicHandle	LoadPictureFromPICTFile( void );
void				DoPrintChoice( WindowPtr window );
void				DrawToPort( WindowPtr window );
void				EventLoop( void );
void				DoEvent( EventRecord *eventPtr );
void				HandleMouseDown( EventRecord *eventPtr );
void				AdjustMenus( void );
void				HandleMenuChoice( long menuChoice );
void				HandleAppleChoice( short item );
void				HandleFileChoice( short item );
void				DoError( Str255 errorString );

The main() function does nothing remarkable - it initializes the Toolbox and the Printing Manager, sets up the program's menu bar, and then enters an event loop.

/********************** main *************************/
void		main( void )
{  
	ToolBoxInit();
	PrintingInit();
	MenuBarInit();
	EventLoop();
}

As expected, ToolBoxInit() is identical to previous versions.

/******************** ToolBoxInit ********************/
void		ToolBoxInit( void )
{
	InitGraf( &qd.thePort );
	InitFonts();
	InitWindows();
	InitMenus();
	TEInit();
	InitDialogs( nil );
	InitCursor();
}

The application-defined function PrintingInit() takes care of the little bit of work necessary to initialize the Printing Manager. As mentioned earlier, in a non-trivial program it's wise to open and close the printing resource file at each use. That's what we do here in PrintingInit().

/******************* PrintingInit ********************/
void PrintingInit( void )
{
  gPrintRecord = (THPrint)NewHandleClear( sizeof(TPrint) );
  PrOpen();
  PrintDefault( gPrintRecord );
  PrClose();
}

MenuBarInit() is similar to previous versions. Here in PrintDoc, though, we use a couple of global variables to obtain handles to the File and Edit menus. Later (in AdjustMenus()) we'll use these handles to enable and disable items in each of these two menus.

/******************** MenuBarInit ********************/
void		MenuBarInit( void )
{
	Handle				menuBar;
	MenuHandle		menu;
	menuBar = GetNewMBar( kMBARResID );
	SetMenuBar( menuBar );
	menu = GetMenuHandle( mApple );
	AppendResMenu( menu, 'DRVR' );
	gFileMenuHandle = GetMenuHandle( mFile );  
	gEditMenuHandle = GetMenuHandle( mEdit );  
	DrawMenuBar();
}

When the user chooses Open from the File menu, OpenPictureDisplayWindow() is called to display the standard Open dialog box, open the user-selected file, load that file's data to memory, and display the resulting data in a window. OpenPictureDisplayWindow() divvies up this work a bit by calling another application-defined function, LoadPictureFromPICTFile() (discussed ahead), to handle the tasks of displaying the Open dialog box and loading the picture to memory.

/************** OpenPictureDisplayWindow *************/
void		OpenPictureDisplayWindow( void )
{
	PicHandle	pict = nil;
	Rect				pictRect;
	short			width;
	short			height;
	WindowPtr	window;
	pict = LoadPictureFromPICTFile(); 
	if ( pict == nil )
		DoError( "\pError loading picture" );

LoadPictureFromPICTFile() returns a handle to the memory that holds the picture from the just-opened PICT file. OpenPictureDisplayWindow() saves this handle in a variable named pict, and uses the picFrame field of the Picture record referenced by this handle to get the size of the picture. Then it opens a window and calls the Toolbox routine SetWindowPic(). When SetWindowPic() is passed a pointer to a window and a handle to a picture, it establishes a link between the two. It does this by adding the picture handle to the windowPic field of the window's WindowRecord. Once that is done, updating the window is no longer a concern of the programmer. Instead, the Window Manager redraws the picture any time the window needs updating. Assigning a picture to the windowPic field of a window isn't the solution for all picture-displaying purposes, but when it is appropriate, it's a good trick to use!

	pictRect = (**pict).picFrame;
	width = pictRect.right - pictRect.left;
	height = pictRect.bottom - pictRect.top;
	window = GetNewCWindow( kWINDResID, nil, (WindowPtr)-1L );
	SetWindowPic( window, pict );

OpenPictureDisplayWindow() ends by making the newly opened window's port the current port, resizing the window so that it is the same size as the picture, and then showing the window.

	SetPort( window );
	SizeWindow( window, width, height, true );
	ShowWindow( window );
}

You just saw that OpenPictureDisplayWindow() relies on LoadPictureFromPICTFile() to take care of the work of loading a picture to memory. LoadPictureFromPICTFile() begins with a number of local variable declarations, and then a call to StandardGetFile().

/*************** LoadPictureFromPICTFile *************/
PicHandle	LoadPictureFromPICTFile( void )
{
	SFTypeList					typeList = { 'PICT', 0, 0, 0 };
	StandardFileReply		reply;
	short							pictRefNum = 0;
	long								fileLength;
	Size								pictSize;
	Handle							tempHandle = nil;
	PicHandle					pict = nil;
	StandardGetFile( nil, 1, typeList, &reply );

The standard Open dialog box is also referred to as the Standard Get dialog box, and it's brought to the screen by way of a call to the Toolbox function StandardGetFile(). The first three parameters are used to tell the File Manager which types of files to display in the dialog box display list. StandardGetFile() can easily display four different types of files. If your application is capable of opening more than four different types of files, you'll need to pass a pointer to a filter function as the first parameter. Since the PrintDoc program only works with one file type (PICT files), we pass nil here. The second parameter specifies each file type. You can fill the SFTypeList when you declare it, as done here. Surround the type in single quotes, and for an unused type simply supply a 0. The last parameter is a pointer to a variable of type StandardFileReply. When the user clicks on the Open or Cancel button in the Open dialog box, the File Manager fills the members of the StandardFileReply structure. This structure has a number of members, but only the sfGood and sfFile members are generally of importance. By examining the value of sfGood, your program can determine if the user clicked on the Open button (sfGood will be true) or the Cancel button (sfGood will be false). If the user cancels, then no file is to be opened, no picture data is to go to memory, and LoadPictureFromPICTFile() can end by returning nil (rather than a valid picture handle) to OpenPictureDisplayWindow().

	if ( reply.sfGood == false )
		return ( nil );

If the user doesn't cancel, then a file is to be opened. The sfFile member of the StandardFileReply structure filled in by the call to StandardGetFile() holds a file system specification, or FSSpec, of the file to open. A call to the Toolbox function FSpOpenDF() opens the data fork of the file named in the first parameter. The second parameter holds a file-access permission level (the constant fsRdPerm means read-only permission), while the third parameter is a pointer to a variable of type short. After FSpOpenDF() successfully opens the file referenced by the FSSpec named in the first parameter, the routine fills in the last parameter with a file reference number by which your program can reference the now opened file.

	FSpOpenDF( &reply.sfFile, fsRdPerm, &pictRefNum );

Now that a picture file is open, it's time to work on getting the file's data into memory. The Toolbox function GetEOF() is used to get the size (in bytes) of the contents of a file. SetFPos() is used to move the file mark-the position maker used to keep track of the current position to read from or write to-to a particular byte location in a file. All PICT files have a 512-byte header that is used in different ways by different applications. These first 512 bytes hold data unrelated to the picture data, so you'll want to move the file mark past them before starting to read the picture data. The fsFromStart constant used in the call to SetFPos() tells the function to count from the start of the file.

	GetEOF( pictRefNum, &fileLength );
	SetFPos( pictRefNum, fsFromStart, 512 );

The total length of the file was found by GetEOF() and is held in the variable fileLength. The actual size of the picture is this total size minus the 512-byte header.

	pictSize = fileLength - 512;

At this point, the file is open and all of the information necessary to read its contents has been obtained. Before beginning the read, we make a call to NewHandleClear() to allocate an area of memory the size of the picture, and to obtain a handle to this area. The Toolbox routine FSRead() is then used to read in the file data. Pass this function the reference number of the file to read from, a pointer to the number of bytes to read, and a pointer to a buffer to read to. Because this third parameter needs to be a pointer to a buffer, it's necessary to dereference the handle once. Since a handle is relocatable, take the precaution of locking it during the moving of data from the open file to the buffer memory.

	tempHandle = NewHandleClear( pictSize );
	HLock( tempHandle );
		FSRead( pictRefNum, &pictSize, *tempHandle );
	HUnlock( tempHandle );

After the call to FSRead() is complete, tempHandle references the picture data in memory. Now it's a simple matter to typecast this generic handle to a PicHandle. At this point the application has a PicHandle that can be used as any other picture handle is used. For instance, it can be used in a call to DrawPicture(). Alternately, it can be stored in a window's windowPic field. After LoadPictureFromPICTFile() returns the PicHandle to OpenPictureDisplayWindow(), that routine does in fact use the handle to store the picture in the window's windowPic field.

	pict = ( PicHandle )tempHandle;
	return ( pict );
}

The Print item in the File menu is enabled when a window is open. As you'll see ahead, choosing Print causes the PrintDoc program to determine which window is frontmost (because the program allows for multiple windows) and then passes a pointer to that window to the DoPrintChoice() routine. This routine's code should look quite familiar to you - it consists mostly of calls to the Printing Manager functions that make printing happen.

A call to PrOpenDoc() initializes a printing graphics port and tells the program to route subsequent QuickDraw calls to this port (the printer) rather than to a port associated with a window. A call to PrOpenPage() is used to begin printing a single page of a document. Exactly what it is that gets printed is found in the application-defined function DrawToPort(). After printing is complete (after DrawToPort() executes), a call to PrClosePage() signals the end of the current page. A call to PrCloseDoc() closes the printing graphics port.

/******************** DoPrintChoice ******************/
void		DoPrintChoice( WindowPtr window )
{
	TPPrPort		printerPort;
	printerPort = PrOpenDoc( gPrintRecord, nil, nil );
	PrOpenPage( printerPort, nil );
	DrawToPort( window );
	PrClosePage( printerPort );  
	PrCloseDoc( printerPort );
}

DrawToPort() includes the QuickDraw calls that define what gets printed to a single page. If the current port was a window's graphics port, then drawing would take place in that window. Because a printing graphics port is current when DrawToPort() is called, the QuickDraw calls instead "draw" to the printer. In the PrintDoc program it's the window's picture (which is referenced by the PicHandle assigned to the window's windowPic field) that is to be printed. Earlier we used the Toolbox routine GetWindowPic() to retrieve that same handle. The coordinates of the picture are obtained by examining the picFrame field of the Picture structure referenced by the PicHandle, and then a call to the Toolbox function DrawPicture() sends the picture to the printer.

/********************** DrawToPort *******************/
void DrawToPort( WindowPtr window )
{
	PicHandle	pict;
	Rect				pictRect;
	pict = GetWindowPic( window );
	pictRect = (**pict).picFrame;
	DrawPicture( pict, &pictRect );
}

Now it's on to the code that's common to most Mac applications. PrintDoc is event driven-so much of the remaining code (which is event-handling) should look familiar to you. EventLoop() is called from main(). Its purpose is to pull the next available event from the event queue and pass it to DoEvent().

/********************* EventLoop *********************/
void		EventLoop( void )
{
	EventRecord		event;
	gDone = false;
	while ( gDone == false )
	{
		if ( WaitNextEvent( everyEvent, &event, kSleep, nil ) )
			DoEvent( &event );
	}
}

Like EventLoop(), DoEvent() offers no surprises. The purpose of DoEvent() is to handle a single event.

/********************** DoEvent **********************/
void	 DoEvent( EventRecord *eventPtr )
{
	char		theChar;
	switch ( eventPtr->what )
	{
		case mouseDown: 
			HandleMouseDown( eventPtr );
			break;
		case keyDown:
		case autoKey:
			theChar = eventPtr->message & charCodeMask;
			if ( (eventPtr->modifiers & cmdKey) != 0 ) 
				HandleMenuChoice( MenuKey( theChar ) );
			break;
		case updateEvt:
			BeginUpdate( (WindowPtr)(eventPtr->message) );
			EndUpdate( (WindowPtr)(eventPtr->message) );
			break;
	}
}

If an event is a click of the mouse, then HandleMouseDown() is called to take care of the event. This version of HandleMouseDown() differs from previous versions only in the handling of a mouse click that occurs while the cursor is over the menu bar. For such a situation the inMenuBar case section includes a call to a new application-defined function - AdjustMenus(). As you'll see just ahead, AdjustMenus() disables and enables menu items as appropriate for the current state of the program. By calling AdjustMenus() at each occurrence of a mouse click in the menu bar, we're assured of always having the menu items set to the correct state. When a user clicks on the menu bar to perhaps reveal a menu, we care about the state of the menu items. At no other time in the running of the program do we worry about menu item states.

/****************** HandleMouseDown ******************/
void		HandleMouseDown( EventRecord *eventPtr )
{
	WindowPtr	window;
	short			thePart;
	long				menuChoice;
	thePart = FindWindow( eventPtr->where, &window );
	switch ( thePart )
	{
		case inMenuBar:
			AdjustMenus();
			menuChoice = MenuSelect( eventPtr->where );
			HandleMenuChoice( menuChoice );
			break;
		case inSysWindow : 
			SystemClick( eventPtr, window );
			break;
		case inContent:
			SelectWindow( window );
			break;
		case inDrag : 
			DragWindow( window, eventPtr->where,
								 &qd.screenBits.bounds);
			break;
		case inGoAway:
			if ( TrackGoAway( window, eventPtr->where ) )
				DisposeWindow( window );
			break;
	}
}

The adjusting of the state of a menu item (enabled or disabled) based on the current state of a program has been a topic we've largely ignored - until now. In a real-world application all menu items don't pertain to all situations. For instance, if no window is open, then a Close menu item certainly shouldn't be enabled - there's nothing to close. If a window is open in our PrintDoc program, then the Close, Page Setup, and Print items should be active. If no windows are open, then these same items of course do not apply - and should be dimmed. The AdjustMenus() function looks to see if a window is open (via a call to FrontWindow()), then makes calls to either DisableItem() or EnableItem() to set the File menu items to the proper state. AdjustMenus() also makes a call to DisableItem() with a second parameter of 0 to disable all of the Edit menu items (pass a second parameter of 1 to enable all items). If the PrintDoc program included editing, then we'd have to come up with a scheme for selectively enabling and disabling items from the Edit menu.

/******************** AdjustMenus ********************/
void		AdjustMenus( void )
{
	WindowPtr	window;
	DisableItem( gEditMenuHandle, 0 );
	window = FrontWindow();
	if ( window == nil )
	{
		DisableItem( gFileMenuHandle, iClose );
		DisableItem( gFileMenuHandle, iPageSetup );
		DisableItem( gFileMenuHandle, iPrint );
	}
	else
	{
		EnableItem( gFileMenuHandle, iClose );
		EnableItem( gFileMenuHandle, iPageSetup );
		EnableItem( gFileMenuHandle, iPrint );
	}
}

HandleMenuChoice() and HandleAppleChoice() are both similar to prior versions.

/****************** HandleMenuChoice *****************/
void		HandleMenuChoice( long menuChoice )
{
	short	menu;
	short	item;
	if ( menuChoice != 0 )
	{
		menu = HiWord( menuChoice );
		item = LoWord( menuChoice );
		switch ( menu )
		{
			case mApple:
				HandleAppleChoice( item );
				break;
			case mFile:
				HandleFileChoice( item );
				break;
		}
		HiliteMenu( 0 );
	}
}
/***************** HandleAppleChoice *****************/
void		HandleAppleChoice( short item )
{
	MenuHandle		appleMenu;
	Str255				accName;
	short				accNumber;
	switch ( item )
	{
		case iAbout:
			SysBeep( 10 );
			break;
		default:
			appleMenu = GetMenuHandle( mApple );
			GetMenuItemText( appleMenu, item, accName );
			accNumber = OpenDeskAcc( accName );
			break;
	}
}

When the user chooses an item from the File menu, the chain of function calls goes like this: EventLoop(), DoEvent(), HandleMouseDown(), HandleMenuChoice(), HandleFileChoice(). What HandleFileChoice() does is based on the item selected from the File menu. An Open choice results in a call to OpenPictureDisplayWindow(). You've already seen that this function displays the Open dialog box, opens a picture file, and displays the file's picture in its own window. A Close choice closes the frontmost window. A Page Setup choice displays the Printing Style dialog box. A Print choice determines the frontmost window, displays the Printing Job dialog box, and then calls DoPrintChoice() to send the contents of the frontmost window to the printer. Choosing Quit of course quits the program.

/****************** HandleFileChoice *****************/
void		HandleFileChoice( short item )
{
	WindowPtr	window;
	Boolean		doPrint;
	switch ( item )
	{
		case iOpen:
			OpenPictureDisplayWindow();
			break;
		case iClose:
			window = FrontWindow();
			DisposeWindow( window );
		break;
		case iPageSetup:
			PrOpen();   
			PrStlDialog( gPrintRecord );
			PrClose();   
			break;
		case iPrint:
			window = FrontWindow();
			PrOpen();   
			doPrint = PrJobDialog( gPrintRecord );
			if ( doPrint == true )
				DoPrintChoice( window );
				PrClose();   
			break;
		case iQuit:
			gDone = true;
			break;
	}
} 

DoError() is unchanged from prior versions. A call to this function results in the posting of an alert that holds an error message. After the alert is dismissed the program ends.

/********************** DoError **********************/
void		DoError( Str255 errorString )
{
	ParamText( errorString, "\p", "\p", "\p" );
	StopAlert( kALRTResID, nil );
	ExitToShell();
}

Running PrintDoc

Run PrintDoc by selecting Run from CodeWarrior's Project menu. After compiling the code and building a program, CodeWarrior runs the program. Begin by choosing Open from the File menu in order to open a new window that displays a picture (you'll need to have at least one PICT file somewhere on your Mac's drive or drives). Then choose Page Setup from the File menu to bring up the Printing Style dialog box to make any desired changes to the characteristics (such as page orientation or scaling) of the printing that's to take place. Now choose Print from the File menu to print the contents of the frontmost window. Open and print as many windows as you want. When you're satisfied that PrintDoc performs as expected, choose Quit from the File menu to end the program.

Till Next Month...

Note the PrintDoc program's application-defined DrawToPort() function contains no printing-specific code. Instead, it's similar to a window updating function. In fact, we could use DrawToPort() to update a window. That's no accident - your own program should define a single routine that defines what gets drawn to a window and defines what gets sent to a printer. This one routine can then be called in response to the need for window updating and in response to a user's request to print the contents of the window. Your program will determine where the code from DrawToPort() gets sent depending on which port is current (a window's graphics port or the printer graphics port). With the window updating/printing code isolated, PrintDoc makes it easy to experiment with changing the content of a window, and thus to experiment with what gets sent to the printer. Of course, you won't be able to simply change the QuickDraw calls in DrawToPort() - you'll also need to change what initially gets displayed in a window. Our PrintDoc program assumes a window always displays the contents of a PICT file - your program will most likely display something else.

The PrintDoc program enhances last month's PrintPict program to provide you with the code necessary to add printing capabilities to your own program. Spend the coming weeks refining your own application's printing power. Spend some time looking over the Imaging With QuickDraw volume of Inside Macintosh to read up on how a program allows for multiple page printing. Then visit next month's Getting Started column for information on a new topic...

 
AAPL
$98.91
Apple Inc.
+1.24
MSFT
$44.22
Microsoft Corpora
-0.29
GOOG
$591.51
Google Inc.
+2.49

MacTech Search:
Community Search:

Software Updates via MacUpdate

Bartender 1.2.20 - Organize your menu ba...
Bartender lets you organize your menu bar apps. Features: Lets you tidy your menu bar apps how you want. See your menu bar apps when you want. Hide the apps you need to run, but do not need to... Read more
TotalFinder 1.6.2 - Adds tabs, hotkeys,...
TotalFinder is a universally acclaimed navigational companion for your Mac. Enhance your Mac's Finder with features so smart and convenient, you won't believe you ever lived without them. Tab-based... Read more
Vienna 3.0.0 RC 2 :be5265e: - RSS and At...
Vienna is a freeware and Open-Source RSS/Atom newsreader with article storage and management via a SQLite database, written in Objective-C and Cocoa, for the OS X operating system. It provides... Read more
VLC Media Player 2.1.5 - Popular multime...
VLC Media Player is a highly portable multimedia player for various audio and video formats (MPEG-1, MPEG-2, MPEG-4, DivX, MP3, OGG, ...) as well as DVDs, VCDs, and various streaming protocols. It... Read more
Default Folder X 4.6.7 - Enhances Open a...
Default Folder X attaches a toolbar to the right side of the Open and Save dialogs in any OS X-native application. The toolbar gives you fast access to various folders and commands. You just click... Read more
TinkerTool 5.3 - Expanded preference set...
TinkerTool is an application that gives you access to additional preference settings Apple has built into Mac OS X. This allows to activate hidden features in the operating system and in some of the... Read more
Audio Hijack Pro 2.11.0 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Intermission 1.1.1 - Pause and rewind li...
Intermission allows you to pause and rewind live audio from any application on your Mac. Intermission will buffer up to 3 hours of audio, allowing users to skip through any assortment of audio... Read more
Autopano Giga 3.6 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
Airfoil 4.8.7 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more

Latest Forum Discussions

See All

The Phantom PI Mission Apparition Review
The Phantom PI Mission Apparition Review By Jordan Minor on July 28th, 2014 Our Rating: :: GHOSTS BUSTEDUniversal App - Designed for iPhone and iPad The Phantom PI is an exceedingly clever and well-crafted adventure game.   | Read more »
More Stubies Are Coming Your Way in a Ne...
More Stubies Are Coming Your Way in a New Update Posted by Jessica Fisher on July 28th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
The Great Prank War Review
The Great Prank War Review By Nadia Oxford on July 28th, 2014 Our Rating: :: PRANKING IS SERIOUS BUSINESSUniversal App - Designed for iPhone and iPad Though short, The Great Prank War offers an interesting and fun mix of action and... | Read more »
Marvel Contest of Champions Announced at...
Marvel Contest of Champions Announced at Comic-Con Posted by Jennifer Allen on July 28th, 2014 [ permalink ] Announced over the weekend at San Diego Comic-Con was the fairly exciting looking Marvel Contest of Champions. | Read more »
Teenage Mutant Ninja Turtles Review
Teenage Mutant Ninja Turtles Review By Jennifer Allen on July 28th, 2014 Our Rating: :: DULL SWIPINGUniversal App - Designed for iPhone and iPad The pizza power is weak when it comes to this Teenage Mutant Ninja Turtles game.   | Read more »
Exploration Focused Puzzle Game Beatbudd...
Exploration Focused Puzzle Game Beatbuddy Set to Make Transition from PC to iOS this September Posted by Jennifer Allen on July 28th, 2014 [ permalink ] | Read more »
PlanetHD
PlanetHD By Nadia Oxford on July 28th, 2014 Our Rating: :: SPACE MADNESSUniversal App - Designed for iPhone and iPad PlanetHD will keep players busy for a while, though its unpredictable physics are a handful to deal with.   | Read more »
This Week at 148Apps: July 21-25, 2014
Another Week of Expert App Reviews   At 148Apps, we help you sort through the great ocean of apps to find the ones we think you’ll like and the ones you’ll need. Our top picks become Editor’s Choice, our stamp of approval for apps with that little... | Read more »
Reddme for iPhone - The Reddit Client (...
Reddme for iPhone - The Reddit Client 1.0 Device: iOS iPhone Category: News Price: $.99, Version: 1.0 (iTunes) Description: Reddme for iPhone is an iOS 7-optimized Reddit client that offers a refreshing new way to experience Reddit... | Read more »
Jacob Jones and the Bigfoot Mystery : Ep...
Jacob Jones and the Bigfoot Mystery : Episode 2 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Jacob Jones is back in Episode 2 of one of Apples 'Best of 2013' games and an App Store... | Read more »

Price Scanner via MacPrices.net

13-inch 2.5GHz MacBook Pro on sale for $1099,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $1099.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $100 off MSRP. Price is... Read more
Roundup of Apple refurbished MacBook Pros, th...
The Apple Store has Apple Certified Refurbished 13″ and 15″ MacBook Pros available for up to $400 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. Their prices... Read more
Record Mac Shipments In Q2/14 Confound Analys...
A Seeking Alpha Trefis commentary notes that Apple’s fiscal Q3 2014 results released July 22, beat market predictions on earnings, although revenues were slightly lower than anticipated. Apple’s Mac’... Read more
Intel To Launch Core M Silicon For Use In Not...
Digitimes’ Monica Chen and Joseph Tsai, report that Intel will launch 14nm-based Core M series processors specifically for use in fanless notebook/tablet 2-in-1 models in Q4 2014, with many models to... Read more
Apple’s 2014 Back to School promotion: $100 g...
 Apple’s 2014 Back to School promotion includes a free $100 App Store Gift Card with the purchase of any new Mac (Mac mini excluded), or a $50 Gift Card with the purchase of an iPad or iPhone,... Read more
iMacs on sale for $150 off MSRP, $250 off for...
Best Buy has iMacs on sale for up to $160 off MSRP for a limited time. Choose free home shipping or free instant local store pickup (if available). Prices are valid for online orders only, in-store... Read more
Mac minis on sale for $100 off MSRP, starting...
Best Buy has Mac minis on sale for $100 off MSRP. Choose free shipping or free instant local store pickup. Prices are for online orders only, in-store prices may vary: 2.5GHz Mac mini: $499.99 2.3GHz... Read more
Global Tablet Market Grows 11% in Q2/14 Notwi...
Worldwide tablet sales grew 11.0 percent year over year in the second quarter of 2014, with shipments reaching 49.3 million units according to preliminary data from the International Data Corporation... Read more
New iPhone 6 Models to Have Staggered Release...
Digitimes’ Cage Chao and Steve Shen report that according to unnamed sources in Apple’s upstream iPhone supply chain, the new 5.5-inch iPhone will be released several months later than the new 4.7-... Read more
New iOS App Helps People Feel Good About thei...
Mobile shoppers looking for big savings at their favorite stores can turn to the Goodshop app, a new iOS app with the latest coupons and deals at more than 5,000 online stores. In addition to being a... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Sr. Product Leader, *Apple* Store Apps - Ap...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
WW Sales Program Manager, *Apple* Online St...
**Job Summary** Imagine what you could do here. At Apple , great ideas have a way of becoming great products, services, and customer experiences very quickly. Bring Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.