Figure 17-6 Application Supplies Job

The application supplies a page description, which goes through the 
GString handle supplied by the Print Control into the Spool File.
Figure 17-1 Print Dialog Box

This Print dialog box shows a Print Control plus some 
specialized gadgets supplied by an application. 
Figure 17-5 Preparing for New Print Job

The Print Control readies a file to hold data and alerts the application
Figure 17-7 Job Traverses Queue
Advanced Topic
Figure 17-0

Display 17-0

SwatDisplay 17-0

Table 17-0
Figure 17-3 Multiple Printer Queues

The Spooler maintains separate queues for separate ports. If all 
printers shared the same queue, the incoming job C wouldn't be 
able to print until after jobs A and B had finished. Instead, job 
C will be the next thing printed on its device.
Figure 17-4 User Print Trigger

In some specific user interfaces, the standard User Print Trigger is an 
entry in the File Menu. 
Figure 17-8 Printing

The Spooler converts the print job to a format the printer can understand.
Now more 
than just a 
fun family 
activity.
It's nutritious!
Figure 17-9 Hurrying and Delaying Jobs

Print jobs that have already been sent to the printer are not shown here.
Advanced Topic

Only work with 
instance data if the 
PageSizeControl is in 
Gadget mode.
Advanced Topic
Advanced Topic
Advanced Topic
Advanced Topic
Advanced Topic
Figure 17-2 Printer Control Panel
17The Spool Library

Revision:  l

Any geode can work with printers, fax machines, and other print devices by 
including the spool library and instantiating a PrintControl object to handle 
system interactions. The PrintControlClass provides the printing 
interface. It includes both user interface and an interface between the geode 
and the printing thread. There is also a PageSizeControlClass which 
provides UI concerning page layout.

Before reading this chapter, you should be familiar with the graphics system.

	17.1	Introduction to Printing

By including a PrintControl object, an application gains access to the powers 
of the GEOS printing system. Below are several of the features built into 
GEOS:

u	Single Imaging Model
You'll use the same commands to describe print jobs as you did for screen 
drawings. These commands work for all supported printers. Thus, 
anything you can draw can be printed, and the application doesn't have 
to worry about what model of printer is being used.

u	Background Printing
Background printing allows users to continue working while their 
documents print. This is an example of multitasking at work. A process 
known as the Spooler spawns a separate thread for each active printer. 
Since each printer's thread priority is low, printing will take place in the 
background, interfering little with the user's interactions. If you want 
printing to take place in the foreground, your application can boost the 
priority of a printer thread.

u	Standard UI Components
The PrintControl is a full-featured control object and it provides a UI 
mechanism for finding out what a user wants to print. For user choices 
which depend on the printer (for example, some printers have no 
low-quality mode), the PrintControl will take printer abilities into 
consideration. (See Figure 17-1.)

u	Control Over Scheduling
The Spooler normally handles jobs in FIFO (first in, first out) order. 
Geodes can exercise a great deal of control over the Spooler's scheduling 
of jobs. Any scheduling options the user might request using the Printer 
Control Panel (see Figure 17-1), the application can request using spool 
routines. 

u	Managing the Printer Yourself
If you can't or won't use standard GEOS commands to describe a print job, 
you can use Raw Mode to send a packet of commands in the printer's own 
language. For example, you could send escape codes or commands from a 
page description language. Since this is a standard printing mode, you 
still benefit from all the usual spool scheduling features.

u	Management of Multiple Printers
The user may have multiple printing devices hooked up and installed at 
one time. The system handles this situation intelligently: the Spooler 
maintains a separate queue for each serial or parallel port. Thus, if two 
printers are installed on two different ports, both may print at once. If 
two or more printers are installed on the same port then they share a 
queue (see Figure 17-3). Thus, jobs for one printer won't try to print at 
the same time as those for another on the same port. To make sure the 
job prints to the correct printer, the Spooler will put up a dialog box 
advising the user to put the correct device on line.

	17.2	Simple Printing Example

Adding printing capability to an application is fairly simple, especially if it 
will be printing WYSIWYG. Somewhere in the application there is probably a 
message handler which is in charge of drawing the document. By including a 
Print Control object and writing handlers to some standard messages, it is a 
simple manner to redirect the document drawing commands to a printer.

Code Display 17-1 Printing Example

@object GenApplicationClass MyApp = {
	/* -Much other GenApplication instance data omitted- */
	gcnList(MANUFACTURER_ID_GEOWORKS, GAGCNLT_SELF_LOAD_OPTIONS) = 
 		@MyPrintControl;
	ATTR_GEN_APPLICATION_PRINT_CONTROL = @MyPrintControl; 
}

@object PrintControlClass MyPrintControl = {
/* The PrintControlClass is a subclass of GenControlClass. Its "features"
 * correspond to the "Print" and "Fax" triggers in the File menu. Its instance
 * data contains information about the document to be printed, and links to
 * objects which will provide information. */

/* Single page documents would leave out page range UI from the Print dialog: 
	PCI_attrs = 		(@default & ~(PCA_PAGE_CONTROLS| PCA_VERIFY_PRINT)); 
	PCI_startUserPage = 1;
	PCI_endUserPage = 1 */

/* The PCI_output, or Print Output object, is in charge of supplying the graphics
 * commands describing the print job whenever the user wants to print. This object
 * is expected to handle a MSG_PRINT_CONTROL_START_PRINTING. */	

 * The print output object is normally either the model, target or process. */
	PCI_output = 		TO_APP_TARGET;

/* The PCI_docNameOutput object is supposed to provide the name of the document
 * on demand. This name is used to identify the document to the user in the Print
 * Control Panel. If this object is a GenDocumentGroup, it automatically does this.
 * If the object in the following field is not a GenDocumentGroup object, then
 * it must have a handler for MSG_PRINT_GET_DOC_NAME. */
	PCI_docNameOutput = MyGenDocumentGroup;

/* Many simple applications only support one document size,
 * and may specify it in the PrintControl and never bother with it again: */
	PCI_docSizeInfo = {			(15/2*72), 
				(19/2*72), 
				0, 
				{0, 0, 0, 0}}; */
/* Many simple applications do not support multiple documents. These applications 
 * should set the GS_ENABLED flag of the PrintControl's GI_states field. */
}

@method MVTStartPrinting, MyVisTargetedClass, MSG_PRINT_START_PRINTING
/* We've set up our Print Control to send this message to the Target, so whatever
 * object will have the target when the user wants to print needs a handler for 
 * this message. We could have easily have put the Model or process in charge
 * of printing, changing PCI_output accordingly. It is often convenient to use
 * the same object to handle drawing to screen and to the printer. */

/* Arguments:		optr 		printControlOD,
		GStateHandle		gstate */

{
	PCMarginParams 	margins;

/*
 * 	Applications which allow the user to change the document size may handle
 *	the situation in more than one way. If the user has changed the page setup
 *	by working with a PageSizeControl, then the application has probably 
 * 	already been alerted to the page size. If the application has its own
 *	way of computing document size, it should use it. Such applications should
 * 	send MSG_PRINT_CONTROL_SET_DOC_SIZE and MSG_PRINT_CONTROL_SET_DOC_MARGINS 
 * 	to the PrintControl, either in this handler or else whenever the page size
 *	changes. Either time is fine, just so long as the document size is set
 * 	correctly by the time this MSG_PRINT_START_PRINTING handler is finished.

 *	Applications	 supporting only a single document size should probably set
 *	the size (and margins) in the PrintControl's instance data, as shown
 *	above. It is also possible to send a MSG_PRINT_CONTROL_SET_DOC_SIZE on
 *	every print, but if the size never changes, this is a bit wasteful.

 *	This application supports only one size of document. If the document
 *	doesn't fit on the page, the spooler will tile it onto multiple pages
 *	as necessary. It uses the printer's margins as the document margins: */

	@call 	MyPrintControl::MSG_PRINT_CONTROL_GET_PRINTER_MARGINS(
		&margins, 		/* Fill in structure with printer's margins */
		TRUE);		/* ...and automatically use printer margins
				 * as document margins */

	@call 	self::MSG_VIS_DRAW(DF_PRINT, gstate);

	/* If the MSG_VIS_DRAW handler didn't end with a form feed, put one in: 
	GrNewPage(gstate, PEC_FORM_FEED); */

	@send 	MyPrintControl::MSG_PRINT_CONTROL_PRINTING_COMPLETED();
}

@method MVTDraw, MyVisTargetClass, MSG_VIS_DRAW
/* Chances are, whatever object this is has some sort of draw handler already.
 * However, if you're building up this class from scratch, you'll be glad to
 * know that commands of the following form work just as well drawing in
 * response to a print request as they do drawing anything else: */

{	GrSetLineColor(gstate, CF_INDEX, C_RED, 0, 0);
	GrDrawLine(gstate, 144, 144, 288, 288);
	GrNewPage(gstate, PEC_FORM_FEED);
}



	17.3	How Jobs Get Printed

You may wonder how and when these various features get implemented. 
Simply stated, the application describes the print job, which gets placed onto 
a queue where it remains until it is fed to a printer. For many programmers, 
that's enough to know. If you want a more complete explanation, read on.

	17.3.1	Printing System Components

Printing involves the concerted effort of several objects running in different 
threads. The most important pieces of the printing system are

PrintControl
The printing system is big and would be difficult to interact 
with if not for the Print Control, which acts as a sort of 
intermediary. This object is a member of the 
PrintControlClass, a GenControl subclass provided by the 
spool library. It handles the UI, communication with the spool 
thread, and most other printing functions. Every geode which 
is going to print includes its own Print Control, placing the 
control object in its generic tree.

Print Output
The Print Output is an object (often the process object) chosen 
by the geode when creating the Print Control. This object is in 
charge of building print jobs and must be prepared to provide 
page descriptions whenever it receives a 
MSG_PRINT_START_PRINTING from the Print Control.

Spooler	The Spooler handles most of the important "behind the scenes" 
work of printing. As mentioned, it handles the scheduling of 
jobs and manages the operations by which generic print jobs 
are translated for specific printers.

Printer Driver
GEOS printer drivers handle the back end of printing. GEOS 
has many printer drivers, each of which serves one or more 
models of printer. The driver supplies the Spooler with 
information about the printer and makes some final 
adjustments to the translated print job to get it ready for the 
specific printer.

	17.3.2	Chronology

Now that you're somewhat familiar with these components, you're ready for 
the detailed account of how they work together to print a job. Note that the 
application is only involved at one point. The printing system takes care of 
the rest automatically.

1	User Activates Trigger
Printing normally begins when the user activates the User Print Trigger 
provided by the Print Control (see Figure 17-4). This trigger sends a 
message to the Print Control telling it to put up the Print Dialog box.

2	User Interacts with the Print Dialog Box
The Print Control responds to the Print Trigger's message by presenting 
a dialog box in which the user selects such things as page range and print 
quality (see Figure 17-1 on page u 1048). The user then either initiates 
printing or cancels. If the user wants to print, he'll confirm this by 
clicking on the appropriate trigger. The application will then get a chance 
to veto the user's choices. The Print Control asks the dialog box to remove 
itself, and it then gets ready for the upcoming print job.

3	Print Control Prepares for New Job
Soon the Print Control is going to ask for the page descriptions, but first 
it needs a place to store the data. It creates a spool file in which to store 
the upcoming graphics commands. It also allocates a GString handle by 
which the Print Output can transmit the commands. It then sends a 
message to the application's Print Output, signalling that the Print 
Output should supply the job. (See Figure 17-5.) 

4	Application Supplies Job
Having received the Print Control's message, the Print Output supplies 
graphics routines to the provided GString handle, using the same 
commands as when printing to any other GState. This string of graphics 
commands ends with a message saying the job is completed. The Print 
Control supplies some extra information to the spooler, and the print job 
is ready to enter the queue. (See Figure 17-6.)

5	Job Traverses Queue
At this point, the spool library places the print job on a printer queue 
where the job waits to be printed. (Actually, the Spooler uses a trick to 
save on overhead. If there are no jobs for a printer, the Spooler doesn't 
maintain a queue for that printer. When a new job comes in for a printer 
that has no queue so far, a new queue is prepared for that printer and the 
job placed on the new queue. When the last job of a queue is printed and 
there are no more jobs forthcoming, the queue is removed.) The Spooler's 
various scheduling powers, if exercised, affect jobs in the queue. (See 
Figure 17-7.)

6	Printing
Eventually, the job reaches the head of the queue. It is now ready to print. 
The Spooler works with the Printer Driver to transform the print job into 
a form that the printer can work with. This may involve building a 
bitmap depicting the job, or it might involve translating the GString's 
commands into the commands of some other page description language, 
such as PostScript. (See Figure 17-8.)

7	Hard Copy
The printer driver sends the appropriate printer commands to the 
printer. If the printer doesn't have sufficient memory to hold the whole 
job, the driver sends only part of the job at a time.

	17.4	Print Control Instance Data


The PrintControl is a powerful and adaptable subclass of GenControl and 
contains many instance fields to fit the needs of different types of 
applications. Each field is described in greater detail later in this section; all 
are shown in Code Display 17-2.

Code Display 17-2 Print Control Instance Data and Features

		/* The following bitfield contains the PrintControl's attributes */
	@instance PrintControlAttrs 		PCI_attrs = 
				(PCA_COPY_CONTROLS | PCA_PAGE_CONTROLS |
				 PCA_QUALITY_CONTROLS | PCA_USES_DIALOG_BOX |
				 PCA_GRAPHICS_MODE | PCA_TEXT_MODE );
		/* Possible PCI_attrs flags (may be combined using | and &):
		 * PCA_MARK_APP_BUSY, 				PCA_VERIFY_PRINT,
		 * PCA_SHOW_PROGRESS, 				PCA_PROGRESS_PERCENT, 
		 * PCA_PROGRESS_PAGE, 				PCA_FORCE_ROTATION 
		 * PCA_COPY_CONTROLS,				PCA_PAGE_CONTROLS,
		 * PCA_QUALITY_CONTROLS, 				PCA_USES_DIALOG_BOX,
		 * PCA_GRAPHICS_MODE,				PCA_TEXT_MODE 
		 * PCA_DEFAULT_QUALITY						*/

		/* The fields below are the complete and requested page ranges */
	@instance word 		PCI_startPage = 1;
	@instance word 		PCI_endPage = 1;
	@instance word 		PCI_startUserPage = 0;
	@instance word 		PCI_endUserPage = 0x7fff";

		/* The following field contains the default printer number */
	@instance word 		PCI_defPrinter = -1;

		/* The following fields deal with document dimensions */
	@instance PageSizeReport 		PCI_docSizeInfo = 0;

		/* Pointers to objects receiving vital messages */
	@instance optr 		PCI_output;
	@instance optr 		PCI_docNameOutput;

		/* The PrintControl's features determine whether to use standard
		 * print and fax triggers to bring up the Print Dialog box. */
	typedef ByteFlags PrintControlFeatures;
	/* The following flags may be combined with | and &:
		PRINTCF_PRINT_TRIGGER,
		PRINTCF_FAX_TRIGGER */
	/* To provide a non-standard print trigger, use the GenControl vardata
	 * field ATTR_GEN_CONTROL_APP_UI. */
	typedef ByteFlags PrintControlToolboxFeatures;
	/* The following flags may be combined with | and &:
		PRINTCTF_PRINT_TRIGGER 
		PRINTCTF_FAX_TRIGGER */

	/* To include application-specific UI in the print dialog box. */
	@vardata optr ATTR_PRINT_CONTROL_APP_UI;

	/* This piece of temporary vardata is internal: */
	@vardata TempPrintCtrlInstance TEMP_PRINT_CONTROL_INSTANCE;

	/* Its structures are defined as follows:
	typedef struct {
	 optr 			TPCI_currentSummons; ( currently active summons )
	 optr 			TPCI_progressBox; ( OD of progress dialog box */
	 ChunkHandle 			TPCI_jobParHandle; ( handle to JobParamters )
	 word 			TPCI_fileHandle; ( file handle (if printing) )
	 word 			TPCI_gstringHandle; ( gstring handle if printing )
	 word 			TPCI_printBlockHan; ( the printer block handle )
	 PrintControlAttrs 			TPCI_attrs;
	 PrintStatusFlags 			TPCI_status;
	 byte 			TPCI_holdUpCompletionCount;
	} TempPrintCtrlInstance;
	typedef ByteFlags PrintStatusFlags;
	#define PSF_FAX_AVAILABLE 0x80 ( set if a fax driver is available )
	#define PSF_ABORT 0x08 ( user wants to abort printing )
	#define PSF_RECEIVED_COMPLETED 0x04 ( MSG_-_PRINTING_COMPLETED received )
	#define PSF_RECEIVED_NAME 0x02 ( MSG_PC_SET_DOC_NAME received )
	#define PSF_VERIFIED 0x01 ( PSG_PC_VERIFY_? received ) */

	@vardata TempPrintCompletionEventData TEMP_PRINT_COMPLETION_EVENT;

	/* The TempPrintCompletionEventData structure is defined:
		typedef struct {
		 MemHandle 		TPCED_event;
		 MessageFlags 		TPCED_messageFlags;
		} TempPrintCompletionEventData; 



	17.4.1	Alerting the GenApplication

PrintControl objects should be placed on the GenApplication's 
MANUFACTURER_ID_GEOWORKS/GAGCNLT_SELF_LOAD_OPTIONS general 
change notification list. Also, if the application is to allow printing of 
documents from the file from the file manager, then the PrintControl's optr 
should be specified in the GenApplication's 
ATTR_GEN_APPLICATION_PRINT_CONTROL field.

	17.4.2	Attributes

PCI_attrs, MSG_PRINT_CONTROL_SET_ATTRS, 
MSG_PRINT_CONTROL_GET_ATTRS

The Print Control includes several attributes which are grouped together 
into a record of type PrintControlAttrs. These represent some choices 
made when instantiating the Print Control.

PCA_MARK_APP_BUSY
Describing large print jobs can take a fair amount of time. If 
this bit is set, the application will reassure the user that it is 
busy (by showing the busy cursor) while spooling long print 
jobs.

PCA_VERIFY_PRINT
If this bit is set, the Print Control will ask the application for 
confirmation before printing anything. This request will come 
in the form of a MSG_PRINT_VERIFY_PRINT_REQUEST sent to 
the PCI_output object. The Print Control sends this message 
after the user has finished interacting with the Print dialog box 
so that the application can make sure that the user has made 
valid choices.

PCA_SHOW_PROGRESS, PCA_PROGRESS_PERCENT, PCA_PROGRESS_PAGE
When describing a large print job, it is considerate to let the 
user know when the application is making some kind of 
progress. If the first of these bits is set, the Print Control will 
display a progress dialog box when spooling a print job. The 
next two bits determine whether progress will be reported as a 
percentage, number of pages completed, both, or neither. 
Regardless of whether percents or page number progress is 
reported, the progress box can display an application-supplied 
string. 

PCA_FORCE_ROTATION
If this bit is set, the output will automatically be printed 
rotated on the page. Normally, this bit is not set so the Spooler 
will make a choice to make the job fit on the minimum number 
of pages. The Banner program sets this bit to make sure that 
fanfold paper printers will create continuous banners; if it 
didn't print sideways, then users would have to use a lot of tape 
to put their banners together (they have to anyhow, if using non 
continuous-feed paper).

PCA_COPY_CONTROLS
This flag determines whether the Print Control will allow the 
user to request that multiple copies be printed. If your 
application uses this option, then it is vital that the print 
control be updated whenever the total page count of the 
document changes.

PCA_PAGE_CONTROLS
If this bit is set, the user will be allowed to select specific page 
ranges to print. Even if it seems like users would have little 
reason to print short ranges, it's still nice to include this option: 
if the printer messes up one page, it's usually irritating to have 
to reprint the whole document.

PCA_QUALITY_CONTROLS
If this flag is on, the user will be allowed to select which quality 
to print with. Note that not all printers can print at all 
qualities, but most support more than one mode.

PCA_USES_DIALOG_BOX
This bit determines whether the Print Control will display a 
dialog box containing printing choices for the user. Specialty 
geodes and GCM applications might want to turn this flag off; 
it is set by default.

PCA_GRAPHICS_MODE, PCA_TEXT_MODE
These bits determine whether the Control should support 
graphic mode and/or text mode output.

PCA_DEFAULT_QUALITY
These two bits contain the default print quality to use. The 
enumerated type PrintQualityEnum defines the values 
available: PQT_HIGH, PQT_MEDIUM, and PQT_LOW.

Those attributes which are on by default are PCA_COPY_CONTROLS, 
PCA_PAGE_CONTROLS, PCA_QUALITY_CONTROLS, 
PCA_USES_DIALOG_BOX, PCA_GRAPHICS_OUTPUT, and 
PCA_TEXT_OUTPUT.

There are messages to get and set these attributes.

n	MSG_PRINT_CONTROL_SET_ATTRS



void	MSG_PRINT_CONTROL_SET_ATTRS(

	PrintControlAttrs 	attributes);

Use this message to change the values stored in the PCI_attrs structure. Pass 
a record of type PrintControlAttrs containing the desired values.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	attributes	The new PrintControlAttrs.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_ATTRS



PrintControlAttrs 	MSG_PRINT_CONTROL_GET_ATTRS();

Use this message to retrieve the values stored in the PCI_attrs structure. It 
will return a record of type PrintControlAttrs containing the desired 
values.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	The present print control attributes.

Interception:	Unlikely.

	17.4.3	Page Range Information

PCI_startPage, PCI_endPage, PCI_startUserPage, 
PCI_endUserPage, MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE, 
MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE, 
MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE, 
MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE

The PCI_startPage and PCI_endPage fields should contain the page numbers 
of the first and last pages possible to print, known as the total page range. 
The PCI_startUserPage and PCI_endUserPage contain the beginning and 
ending page numbers of the range the user wants to print, often called the 
user page range. 

For documents whose page length may change (such as word processor 
documents), the page ranges should be updated whenever the Print dialog 
box goes up, at which time the application will be receiving a 
MSG_PRINT_NOTIFY_PRINT_DB. Normally, the user page range will start out 
being the same as the total page range. For most documents the first page of 
the total page range is page one, but applications which will include cover 
sheets might want the cover sheet to be page zero.

n	MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE



void	MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE(

	int 	firstPage,

	int	lastPage);

The application should send this message to set the first and last page 
numbers of the document. You might want to send this message every time 
the document length changes or just every time the Print dialog box is put 
up. The Print Control warns the PCI_output object of the occurrence of the 
latter event with a MSG_PRINT_NOTIFY_PRINT_DB. 

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	firstPage	The page number of the document's first page.

lastPage 	The page number of the document's last page.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE



dword	MSG_PRINT_CONTROL_GET_TOTAL_PAGE_RANGE();

This message returns two integers. These integers are the numbers of the 
first and last pages of the range of possible pages. These values aren't 
necessarily the first and last pages of the range the user wants to print. Use 
MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE for that information.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	A double word. The high word is the first page; the low word is the last 
page.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE



void	MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE(

	int 	firstPage,

	int	lastPage);

This message takes the passed values and uses them as the first and last 
pages that the user wants to print.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	firstPage	The page number of the first page to print.

lastPage 	The page number of the last page to print.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE



dword	MSG_PRINT_CONTROL_GET_SELECTED_PAGE_RANGE();

This message returns the user's selected range of pages to print. 

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	A double word. The high word is the first page which the user wishes 
to print; the low word is the last page of this range.

Interception:	Unlikely.

	17.4.4	Document Size

PCI_docSizeInfo, MSG_PRINT_CONTROL_SET_DOC_SIZE, 
MSG_PRINT_CONTROL_GET_DOC_SIZE, 
MSG_PRINT_CONTROL_SET_DOC_MARGINS, 
MSG_PRINT_CONTROL_GET_DOC_MARGINS, 
MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE, 
MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE, 
MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO, 
MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO

It is possible to specify the size of the document when creating the Print 
Control. Note that this is the size of the document, not the size of the piece of 
paper. Ideally, the document should fit on the paper, though obviously in the 
case of huge documents like some spreadsheets, this may not be the case. The 
document size includes the margin size; it is possible to set the margin size 
as well.

The document size must be set correctly before the document is finished 
printing. If all documents the application produces have the same 
dimensions (or if you want some size to be the default), you may specify 
dimensions for the document when instantiating the print control. You may 
also set up document margins at this time.

The messages listed above get and set the document and margin sizes. You 
must use MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE and 
MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE when working with the 
dimensions of 32-bit extended documents.

n	MSG_PRINT_CONTROL_SET_DOC_SIZE



void	MSG_PRINT_CONTROL_SET_DOC_SIZE(

	int 	width,

	int	height);

This message changes the values of the document size. It takes two integers, 
representing the new width and height for the document to use. It can only 
use 16-bit values, so use MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE 
when working with 32-bit extended graphics spaces.

Remember that the document size includes the document margins.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	width	The document's width, in points.

height 	The document's height, in points.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_DOC_SIZE



dword	MSG_PRINT_CONTROL_GET_DOC_SIZE();

Use this message to retrieve the present document size. The size is returned 
as a width and height, each expressed in points. 

Note that if the size might be a 32-bit value (which might happen if the 
document uses an extended graphics space), you must use the 
MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE. If either dimension is a 
32-bit number, using a regular MSG_PRINT_CONTROL_GET_DOC_SIZE will 
result in an error.

Remember that the document size includes the document margins.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	A double word. The high word is the width, the low word is the height.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE



void	MSG_PRINT_CONTROL_SET_EXTENDED_DOC_SIZE(

	PCDocSizeParams 	*ptr);

This message changes the values of the document size using the two passed 
double integers as the new width and height to use. When working with 
normal 16 bit graphics spaces, use MSG_PRINT_CONTROL_SET_DOC_SIZE 
instead.

Remember that the document size includes the document margins.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	ptr 	Pointer to a PCDocSizeParams structure 
containing the document size.

Return:	Nothing.

Interception:	Unlikely.

Structures:	The PCDocSizeParams structure has the following definition:

typedef struct {
	dword		PCDSP_width;
	dword		PCDSP_height;
} PCDocSizeParams;

n	MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE



void	MSG_PRINT_CONTROL_GET_EXTENDED_DOC_SIZE(

	PCDocSizeParams 	*ptr);

Use this message to retrieve the present document size. It returns two double 
integers representing the width and height, expressed in points. If the size is 
a 16 bit value, you can use MSG_PRINT_CONTROL_GET_DOC_SIZE instead.

Remember that the document size includes the document margins.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	ptr 	Pointer to a PCDocSizeParams structure to hold 
document parameters.

Return:	Nothing is returned explicitly.

ptr 	The structure is filled with document size.

Interception:	Unlikely.

Structures:	The PCDocSizeParams structure has the following definition:

typedef struct {
	dword		PCDSP_width;
	dword		PCDSP_height;
} PCDocSizeParams;

n	MSG_PRINT_CONTROL_SET_DOC_MARGINS



void	MSG_PRINT_CONTROL_SET_DOC_MARGINS(

	PCMarginParams 	*ptr);

Use this message to set new values for the document margins. It takes four 
arguments, the point values to use for the left, top, right, and bottom 
margins.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	ptr 	Pointer to a PCMarginParams structure with 
new document margins.

Return:	Nothing.

Interception:	Unlikely.

Structures:	The PCMarginParams structure has the following definition:

typedef struct {
	word		PCMP_left;
	word		PCMP_top;
	word		PCMP_right;
	word		PCMP_bottom;
} PCMarginParams;

n	MSG_PRINT_CONTROL_GET_DOC_MARGINS



void	MSG_PRINT_CONTROL_GET_DOC_MARGINS(

	PCMarginParams 	*ptr);

Use this message to get the present values for the document margins. It 
returns four integers. These integers represent the left, top, right, and 
bottom margins, expressed in typographer's points.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	ptr 	Pointer to a PCMarginParams structure which 
will hold return value.

Return:	Nothing returned explicitly. 

ptr 	Structure filled in with document margins.

Interception:	Unlikely.

Structures:	The PCMarginParams structure has the following definition:

typedef struct {
	word		PCMP_left;
	word		PCMP_top;
	word		PCMP_right;
	word		PCMP_bottom;
} PCMarginParams;

n	MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO



void	MSG_PRINT_CONTROL_SET_DOC_SIZE_INFO(

	PageSizeReport 	*ptr);

Use this message to set all of the information about the document size and 
orientation.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	ptr 	Pointer to a PageSizeReport structure.

Return:	Nothing. 

Interception:	Unlikely.

Structures:	The PageSizeReport structure has the following definition:

typedef struct {
	dword			PSR_width;
	dword			PSR_height;
	PageLayout			PSR_layout;
	PCMarginParams 			PSR_margins;
} PCMarginParams;

n	MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO



void	MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO(

	PageSizeReport 	*ptr);

Use this message to set all of the information about the document size and 
orientation.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	ptr 	Pointer to an empty PageSizeReport structure 
which the message handler will fill in.

Return:	Nothing. 

Interception:	Unlikely.

Structures:	The PageSizeReport structure has the following definition:

typedef struct {
	dword			PSR_width;
	dword			PSR_height;
	PageLayout			PSR_layout;
	PCMarginParams 			PSR_margins;
} PCMarginParams;

	17.4.5	Print Output Object

PCI_output, MSG_PRINT_START_PRINTING, 
MSG_PRINT_VERIFY_PRINT_REQUEST, MSG_PRINT_NOTIFY_PRINT_DB

Some object is going to describe the print jobs to the Print Control. When 
instantiating the Print Control, set the chosen object's name in the 
PCI_output field:

PCI_output = <yourObject>;

There are three messages the Print Control may send to its output: 
MSG_PRINT_START_PRINTING, MSG_PRINT_VERIFY_PRINT_REQUEST, and 
MSG_PRINT_NOTIFY_PRINT_DB. The Print Output must respond to 
MSG_PRINT_START_PRINTING, which is the signal that it's time to describe 
a print job. The Print Output also must respond to 
MSG_PRINT_VERIFY_PRINTING if the PrintControl's PCA_VERIFY_PRINT bit 
has been set. Otherwise, it need not handle this message. Finally, the Print 
Output may have a handler for the MSG_PRINT_NOTIFY_DB, a message the 
PrintControl will send whenever the Print dialog box comes up or goes away.

	17.4.5.1	The Print Method

Assuming your geode doesn't try to do anything fancy with its own printing 
UI or weird scheduling, probably the most complicated thing you'll have to do 
when adding printing capability to your geode is write a Print Method.

This message must be handled by the Print Output object, though the 
handler probably won't be too complicated. In its simplest form, a 
MSG_PRINT_START_PRINTING handler could just call some graphics 
commands and finish off by sending 
MSG_PRINT_CONTROL_PRINTING_COMPLETED to the PrintControl.

The message is accompanied by a pointer back to the Print Control and a 
GString handle. Any graphics commands drawn to the GString handle will 
be retained and will become part of the print job. 

Before looking at examples, be warned that there are some requirements 
that every Print Method must meet:

u	Be sure the document size has been set correctly. Before the job is 
finished and you send MSG_PRINT_CONTROL_PRINTING_COMPLETED, 
you must be certain the document's size and margins have been set 
somewhere. This may have been set using the PCI_docSizeInfo field when 
defining the Print Control. If you haven't already let the Print Control 
know about the document size elsewhere, then you must send a 
MSG_SPOOL_PRINT_CONTROL_SET_DOC_SIZE, probably either in the 
MSG_PRINT_START_PRINTING or wherever your geode sets up document 
information in general.

u	Be certain the number of pages to be printed has been correctly set. If 
your application hasn't already set the page range (when the Print 
Control was created or when the Print dialog box goes up are popular 
times for this), send a MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE 
to your Print Control. Make sure you do it before sending in the 
MSG_PRINT_CONTROL_PRINTING_COMPLETED.

u	Be sure the document name has been set. If you use a 
GenDocumentGroup as your PCI_docNameOutput, this will be taken 
care of for you. Otherwise, some time before the 
MSG_PRINT_CONTROL_PRINTING_COMPLETED is sent, your 
PCI_docNameOutput object is going to be asked for the document name, 
and the job won't be spooled until your PCI_docNameOutput sends the 
correct name back to the Print Control.

u	The Print Method should end each page with a GrNewPage(); the last 
thing drawn before the print job ends is a GrNewPage().

u	Finally, when the Print Method is finished describing the print job, it 
must end with a MSG_PRINT_CONTROL_PRINTING_COMPLETED. 
Otherwise, the Print Control has no way of knowing the job is ready.

When the Print Control receives the message 
MSG_PRINT_CONTROL_PRINTING_COMPLETED, it responds by cleaning up 
and sending the print job to the spooler.

Your handler can send a MSG_PRINT_CONTROL_REPORT_PROGRESS to the 
Print Control if it is spooling a large job and wants to reassure the user. If 
something goes wrong, or if MSG_PRINT_CONTROL_REPORT_PROGRESS 
returns a signal indicating the user wishes to cancel, 
MSG_PRINT_START_PRINTING should send a 
MSG_PRINT_CONTROL_PRINTING_CANCELLED instead.

n	MSG_PRINT_START_PRINTING



void	MSG_PRINT_START_PRINTING(

	optr		printControlOD,

	GStateHandle		gstate);

The handler for this message should call a number of graphics routines, 
using the passed GState. When done with graphics routines, the handler 
should send a MSG_PRINT_CONTROL_PRINTING_COMPLETED (or 
MSG_PRINT_CONTROL_PRINTING_CANCELED) to the Print Control. The 
handler may have to accomplish other tasks; see the above bulleted list (the 
one marked as "the most used and useful section of this chapter").

Source:	PrintControl object.

Destination:	The object specified in PCI_output.

Parameters:	printControlOD	The optr of the PrintControl object.

gstate 	The GState handle to draw to.

Return:	Nothing.

Interception:	The Print output object must intercept this message, build the print job 
by drawing to the passed GState, then send 
MSG_PRINT_CONTROL_PRINTING_COMPLETED to the object in 
printControlOD.

	17.4.5.2	Verifying User Choices

You may write a handler for MSG_PRINT_VERIFY_PRINT_REQUEST, which 
will be sent to the Print Output if the PCA_VERIFY_PRINT bit has been set. 
The message will be sent after the user has dismissed the Print dialog box, 
after the system has had a chance to make sure the document fits on the 
paper but before the document is actually spooled.

This message gives the application a chance to check out the state of the UI 
and make sure that the user's choices are valid. Note that the PrintControl 
does its own checking to make sure that the document will fit on the page. 
After examining the UI, the handler must send back a 
MSG_PRINT_CONTROL_VERIFY_COMPLETED, passing the argument to say 
whether it's okay to print.

n	MSG_PRINT_VERIFY_PRINT_REQUEST



void	MSG_PRINT_VERIFY_PRINT_REQUEST(

	optr 	printControlOD);

The handler for this message should make whatever checks are necessary to 
make sure that the user has made valid choices with the printing UI. The 
handler should then send back an indication of whether the user's choices are 
all right by means of a MSG_PRINT_CONTROL_VERIFY_COMPLETED.

Source:	PrintControl object.

Destination:	The object specified in PCI_output.

Parameters:	printControlOD	The optr of the PrintControl object.

Return:	Nothing explicitly. However, the handler for this message must send a 
MSG_PRINT_CONTROL_VERIFY_COMPLETED back to the print 
control.

Interception:	If you've set the PCA_VERIFY_PRINT flag, you must intercept this 
message correctly or else the print job will never start.

	17.4.5.3	Dialog Box Notification

The Print Control will send the Print Output a MSG_PRINT_NOTIFY_DB 
every time the Print dialog box comes up or goes away. Note that this 
message allows the application to update the page range and print group's UI 
at only those times that updates are needed.

This message will arrive with one argument, a pointer back to the 
PrintControl object that sent it. The handler for this message might wish to 
update the page range information using 
MSG_PRINT_CONTROL_SET_TOTAL_PAGE_RANGE and 
MSG_PRINT_CONTROL_SET_SELECTED_PAGE_RANGE.

n	MSG_PRINT_NOTIFY_PRINT_DB



void	MSG_PRINT_NOTIFY_PRINT_DB(

	optr		printControlOD,

	PrintControlStatus		pcs);

The handler for this message can do any almost anything; this message 
signals that the Print dialog box has just come up or gone away.

Source:	PrintControl object.

Destination:	The object specified in PCI_output.

Parameters:	printControlOD	The optr of the PrintControl object.

pcs 	The status of the print control; either 
PCS_PRINT_BOX_VISIBLE or 
PCS_PRINT_BOX_NOT_VISIBLE.

Return:	Nothing.

Interception:	Some Print Output objects will intercept this message to set page 
ranges.

	17.4.6	Document Name Output

PCI_docNameOutput, MSG_PRINT_GET_DOC_NAME

The PCI_docNameOutput field must contain the optr of an object which can 
tell the Spooler the document's name. The document name is displayed on 
the Printer Control Panel and is how the user can figure out which job is 
which on the queue. If a GenDocumentGroup is set as the Print Control's 
PCI_docNameOutput, then that object will automatically do the right thing. 
If you use some other type of object, it must be prepared to supply document 
names on demand. This demand will come in the form of a 
MSG_PRINT_GET_DOC_NAME.

This message comes with a pointer back to the Print Control. The handler 
should respond by sending a MSG_PRINT_CONTROL_SET_DOC_NAME with a 
string containing the name.

n	MSG_PRINT_GET_DOC_NAME



void	MSG_PRINT_GET_DOC_NAME(

	optr	printControlOD);

If your PCI_docNameOutput object is not a GenDocumentGroup, then it must 
have a handler for this message.

Source:	PrintControl object.

Destination:	The object specified in PCI_docNameOutput.

Parameters:	printControlOD	The optr of the PrintControl object.

Return:	Nothing returned explicitly, but should send a 
MSG_PRINT_CONTROL_SET_DOC_NAME back to the Print Control.

Interception:	If receiving object is a GenDocument, unlikely. Otherwise, interception 
is necessary.

	17.4.7	The Default Printer

PCI_defPrinter, MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER, 
MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER

As this documentation has already stressed, one of the printing system's 
more important features is that it is device independent. However, certain 
special-purpose applications could conceivably depend on the user's printer 
type, and those applications may have use for the Print Control's 
PCI_defPrinter field. When the Print Control first appears, it will normally 
have the system default printer selected. Using the PCI_defPrinter option, 
another printer might be selected. The user may of course override the 
default printer and select another.

You can set or retrieve the value in PCI_defPrinter with the messages listed 
above. For information on them, see "Working with Instance Data" on page 
1081.

There are utility routines to get information about the system default 
printer. There are commands to retrieve information about each of the 
installed printers so that the application may find out whether the desired 
type of printer is installed and, if so, what its printer number is. These 
routines are described in section 17.7.2.4 on page 1097.

	17.4.8	Adding UI Gadgetry

ATTR_PRINT_CONTROL_APP_UI

If the Print Control supplies UI that meets your geode's needs, feel free to 
skip this section. On the other hand, if you've decided you want a 
non-standard print trigger or want some specialized gadgetry to appear in 
the application's Print dialog box, it is possible to create objects and tell the 
Print Control to work with them. Make sure that any objects you want to use 
in this fashion aren't part of a generic or visual tree already. Also, these 
objects must be set not usable (~GS_USABLE).

Normally the user has two ways to print up a Print dialog box: the User Print 
Trigger or the User Fax Trigger, both appearing in the File menu. You may 
specify a special trigger to be used in place of the standard trigger provided 
by the Print Control. Normally the default trigger is not only sufficient but 
preferable, if only because it will automatically display with the correct 
appearance for the specific UI. To define your own trigger object, create the 
object(s), setting the correct instance data. Then, when instantiating your 
Print Control, include an ATTR_GEN_CONTROL_APP_UI:

ATTR_GEN_CONTROL_APP_UI = {yourTrigger};

When this option is used at all, normally the trigger object is a simple trigger. 
The action descriptor of this application-defined trigger should be

GTI_output = <your_PrintControl>;
GTI_method = MSG_SPOOL_PRINT_CONTROL_INITIATE_PRINT;

By working with the control's features, you may remove either or both of the 
default triggers.

The Print Control's vardata field ATTR_PRINT_CONTROL_APP_UI allows 
application-specific gadgetry to appear in the Print dialog box. Create the 
object(s) to be included and set their instance data, then use the 
ATTR_PRINT_CONTROL_APP_UI field to signal that the Print Control should 
include the gadgetry. 

ATTR_PRINT_CONTROL_APP_UI = {yourPrintGroup};

When this option is used at all, usually a GenInteraction serves as the print 
group object, with children appropriate to the task at hand. This generic tree 
must not be destroyed unless the vardata field has been removed.

	17.5	Print Control Messages

Now you have a rather good idea of what's required to set up a Print Control. 
Once it's been set up, your geode may send the following messages to it.

	17.5.1	Common Response Messages

MSG_PRINT_CONTROL_VERIFY_COMPLETED, 
MSG_PRINT_CONTROL_SET_DOC_NAME

These messages are normally used only to respond to messages sent out by 
the Print Control.

n	MSG_PRINT_CONTROL_VERIFY_COMPLETED



void	MSG_PRINT_CONTROL_VERIFY_COMPLETED(

	Boolean 	continue);

If you have decided to verify the user's Print dialog box choices, you must 
send this message back to the Print Control in response to 
MSG_PRINT_VERIFY_PRINT_REQUEST. If true is passed, the Print Control 
will continue preparing the job for printing. If the value passed is false, the 
print will be cancelled. It is up to the application to explain the problem to 
the user.

Source:	Unrestricted, normally the PCI_output object.

Destination:	Any PrintControl object.

Parameters:	continue	Flag signalling whether printing should continue 
or be cancelled.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_SET_DOC_NAME



void	MSG_PRINT_CONTROL_SET_DOC_NAME(

	char	* string);

This message must be sent by the PCI_docNameOutput before the print job 
can be spooled. It is sent in response to a MSG_PRINT_GET_DOC_NAME from 
the Print Control. The string argument should be something that will allow 
the user to recognize the print job in the Printer Control Panel.

Source:	Unrestricted-normally the PCI_docNameOutput object.

Destination:	Any PrintControl object.

Parameters:	string	The document's name, as a null-terminated string. 
Maximum length of this string is 
FILE_LONGNAME_LENGTH+1.

Return:	Nothing.

Interception:	Unlikely.

	17.5.2	Flow of Control Messages

MSG_PRINT_CONTROL_PRINTING_COMPLETED, 
MSG_PRINT_CONTROL_REPORT_PROGRESS, 
MSG_PRINT_CONTROL_PRINTING_CANCELLED, 
MSG_PRINT_CONTROL_REPORT_STRING, 
MSG_PRINT_CONTROL_INITIATE_PRINT, MSG_PRINT_CONTROL_PRINT

Sending these messages to the Print Control signals that a new stage of 
printing is ready to begin.

n	MSG_PRINT_CONTROL_PRINTING_COMPLETED



void	MSG_PRINT_CONTROL_PRINTING_COMPLETED();

This message signals the Print Control that the application is finished 
describing the print job. The application must send this message (or a 
MSG_PRINT_CONTROL_PRINTING_CANCELLED) some time after receiving a 
MSG_PRINT_START_PRINTING.

Source:	The object that received MGS_PRINT_START_PRINTING.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	Nothing.

Interception:	Unlikely, unadvised.

n	MSG_PRINT_CONTROL_REPORT_PROGRESS



Boolean	MSG_PRINT_CONTROL_REPORT_PROGRESS(

	PCProgressType 	progress,

	int 	pageOrPercent);

This message may be sent between the time the Print Output receives a 
MSG_PRINT_START_PRINTING and the time it sends a 
MSG_PRINT_CONTROL_PRINTING_COMPLETED. Its arguments include a 
progress type (page number or percentage) and a number representing the 
progress. It returns a Boolean value, which will normally be true but will be 
false if the user wishes to cancel printing.

The application should send out this message periodically over the course of 
a long print job, probably about once per page. Progress may be reported as 
a page number, a percentage of the job completed, or as a null-terminated 
string. 

If the return value is false, the application should stop drawing to the print 
GString immediately and send the Print Control a 
MSG_PRINT_CONTROL_PRINTING_CANCELLED. The return value may be 
safely ignored, but if the user wishes to cancel printing, it's polite to cancel 
immediately instead of making him wait until the 
MSG_PRINT_START_PRINTING has finished describing the entire job.

Source:	The object that received MGS_PRINT_START_PRINTING when building 
a time-consuming job.

Destination:	The PrintControl object specified by MSG_PRINT_START_PRINTING.

Parameters:	progress 	How progress is being reported, by page 
(PCPT_PAGE) or percent (PCPT_PERCENT).

pageOrPercent	How much progress has been made.

Return:	Flag signalling that printing should continue. If the flag is false (i.e. 
zero), then the user wants to cancel printing, and it would be polite to 
stop.

Interception:	Unlikely.

Structures:	The PCProgressType enumeration is defined:

typedef enum {
 PCPT_PAGE,
 PCPT_UNUSED1, /* Unused type */
 PCPT_PERCENT,
 PCPT_UNUSED2, /* Unused type */
 PCPT_TEXT
} PCProgressType;

n	MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING



@alias(MSG_PRINT_CONTROL_REPORT_PROGRESS) \ 

Boolean	MSG_PRINT_CONTROL_REPORT_PROGRESS_STRING(

	PCProgressType 	progress,

	Chars 	*progressString);

This message may be sent between the time the Print Output receives a 
MSG_PRINT_START_PRINTING and the time it sends a 
MSG_PRINT_CONTROL_PRINTING_COMPLETED. Its arguments include a 
text string giving some indication of progress. This message returns a 
Boolean value, which will normally be true but will be false if the user wishes 
to cancel printing.

The application should send out this message periodically over the course of 
a long print job, probably about once per page. Progress may be reported as 
a page number, a percentage of the job completed, or as a null-terminated 
string. 

If the return value is false, the application should stop drawing to the print 
GString immediately and send the Print Control a 
MSG_PRINT_CONTROL_PRINTING_CANCELLED. The return value may be 
safely ignored, but if the user wishes to cancel printing, it's polite to cancel 
immediately instead of making him wait until the 
MSG_PRINT_START_PRINTING has finished describing the entire job.

Source:	The object that received MGS_PRINT_START_PRINTING when building 
a time-consuming job.

Destination:	The PrintControl object specified by MSG_PRINT_START_PRINTING.

Parameters:	progress 	This must be PCPT_TEXT. The reason that this 
parameter has only one possible value is that this 
message is actually an alias of 
MSG_PRINT_CONTROL_REPORT_PROGRESS, 
handled by the same assembly routine.

progressString	A string of text, giving an indication of progress.

Return:	Flag signalling that printing should continue. If the flag is false (i.e. 
zero), then the user wants to cancel printing, and it would be polite to 
stop.

Interception:	Unlikely.

Structures:	The PCProgressType enumeration is defined:

typedef enum {
 PCPT_PAGE,
 PCPT_UNUSED1, /* Unused type */
 PCPT_PERCENT,
 PCPT_UNUSED2, /* Unused type */
 PCPT_TEXT
} PCProgressType;

n	MSG_PRINT_CONTROL_PRINTING_CANCELLED



void	MSG_PRINT_CONTROL_PRINTING_CANCELLED();

The application may send this message to the PrintControl after receiving a 
MSG_PRINT_START_PRINTING. Do not send both this message and a 
MSG_PRINT_CONTROL_PRINTING_COMPLETED for a single print job.

The application may send this message to cancel a document while 
describing a print job, before it would send the 
MSG_PRINT_CONTROL_PRINTING_COMPLETED.

Source:	Unrestricted, probably the PCI_output. 

Destination:	The PrintControl object specified by MSG_PRINT_START_PRINTING.

Parameters:	None.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_INITIATE_PRINT



void	MSG_PRINT_CONTROL_INITIATE_PRINT();

This message, normally sent by the Print trigger in the File menu, is the 
signal that the Print Control should display the Print dialog box. Geodes with 
custom print triggers should make sure that those triggers send this message 
to the Print Control. Geodes using the Print Control's provided print trigger 
need not send this message.

Source:	Unrestricted-typically the user print trigger (or fax trigger).

Destination:	Any PrintControl object.

Parameters:	None.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_PRINT



void	MSG_PRINT_CONTROL_PRINT();

This message, normally sent by the Print trigger in the Print dialog box, is 
the signal that the user has made his printing choices and is ready for the 
Print Control to verify those choices and spool the job. 

Source:	Unrestricted-typically the Print trigger in the Print Dialog Box.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	Nothing.

Interception:	Unlikely.

	17.5.3	Working with Instance Data

MSG_PRINT_CONTROL_SET_OUTPUT, 
MSG_PRINT_CONTROL_GET_OUTPUT, 
MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT, 
MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT, 
MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER, 
MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER, 
MSG_PRINT_CONTROL_GET_PRINT_MODE, 
MSG_PRINT_CONTROL_GET_PAPER_SIZE, 
MSG_PRINT_CONTROL_GET_PRINTER_MARGINS, 
MSG_PRINT_CONTROL_CALC_DOCUMENT_DIMENSIONS, 
MSG_PRINT_CONTROL_VERIFY_DOC_MARGINS, 
MSG_PRINT_CONTROL_VERIFY_DOC_SIZE

The Print Control handles many messages which allow the geode to retrieve 
and alter the values of the instance data.

n	MSG_PRINT_CONTROL_SET_OUTPUT



void 	MSG_PRINT_CONTROL_SET_OUTPUT(

	optr 	objectPtr);

This message, used very rarely, specifies that a different object should 
become the Print Output. Pass the optr of the object to use.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	objectPtr 	The optr of the new PCI_output.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_OUTPUT



optr 	MSG_PRINT_CONTROL_GET_OUTPUT();

This message returns the optr of the Print Output.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	The optr of the current PCI_output object.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT



void 	MSG_PRINT_CONTROL_SET_DOC_NAME_OUTPUT(

	optr 	document);

This message, used very rarely, specifies that a different object should 
become the Document Name Output object. Pass the optr of the object to use.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	objectPtr 	The optr of the new PCI_docNameOutput.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT



optr 	MSG_PRINT_CONTROL_GET_DOC_NAME_OUTPUT();

This message returns the optr of the Document Name Output object.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	The optr of the Document Name output object.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER



void 	MSG_PRINT_CONTROL_SET_DEFAULT_PRINTER(

	int 	printerNum);		/* -1 for system's default printer */

This message sets the application default printer, to be used the next time 
the print dialog box appears. Pass the printer number of the printer to use. 
Passing a value of -1 means that the system default printer should be used.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	printerNum	Number of the new default printer.

Return:	Nothing.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER



int 	MSG_PRINT_CONTROL_GET_DEFAULT_PRINTER();

Use this message to retrieve the number of the present application default 
printer. You may not assume that this printer is the one the user is printing 
on, since the user may have changed to another printer. A return value of -1 
means that the application will use the system default printer.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	The number of the default printer.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_PRINT_MODE



byte 	MSG_PRINT_CONTROL_GET_PRINT_MODE(); 

This message retrieves the current user-selected print mode, if any. Its 
possible return values include zero, meaning no mode has been selected; 
PM_GRAPHICS_LOW_RES; PM_GRAPHICS_MED_RES; 
PM_GRAPHICS_HI_RES; PM_TEXT_DRAFT; and PM_TEXT_NLQ.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	None.

Return:	A PrinterOutputModes signalling the printer's quality.

Interception:	Unlikely.

Structures:	The PrinterOutputModes structure has the following definition:

typedef ByteFlags PrinterOutputModes;

#define POM_GRAPHICS_LOW 0x10 
#define POM_GRAPHICS_MEDIUM 0x08 
#define POM_GRAPHICS_HIGH 0x04
#define POM_TEXT_DRAFT 0x02 
#define POM_TEXT_NLQ 0x01

Each flag indicates the specified quality is available. Two useful masks which 
have been set up are PRINT_GRAPHICS and PRINT_TEXT.

n	MSG_PRINT_CONTROL_GET_PAPER_SIZE



void 	MSG_PRINT_CONTROL_GET_PAPER_SIZE(

	PCMargineParams 		*retVal); 

Use this message to retrieve the Print Control's present paper size.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	retVal	An empty PCMarginParams structure which the 
message handler will fill.

Return:	Nothing returned explicitly; the retVal structure will be filled.

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_GET_PAPER_SIZE_INFO



void	MSG_PRINT_CONTROL_GET_DOC_SIZE_INFO(

	PageSizeReport 	*ptr);

Use this message to set all of the information about the document size and 
orientation.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	ptr 	Pointer to an empty PageSizeReport structure 
which the message handler will fill in.

Return:	Nothing. 

Interception:	Unlikely.

Structures:	The PageSizeReport structure has the following definition:

typedef struct {
	dword			PSR_width;
	dword			PSR_height;
	PageLayout			PSR_layout;
	PCMarginParams 			PSR_margins;
} PCMarginParams;

n	MSG_PRINT_CONTROL_GET_PRINTER_MARGINS



void 	MSG_PRINT_CONTROL_GET_PRINTER_MARGINS(

	MarginDimensions 		*retVal,

	Boolean 		setMargins); 

This message returns the margins enforced by the requested printer. If the 
boolean argument is true, the document margins will be set to be the same as 
the printer margins.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	setMargins	Pass TRUE to set the document's margins equal to 
the printer's margins, FALSE to leave the margins 
as they are.

retVal	A pointer to an empty MarginDimensions 
structure to be filled in by the handler.

Return:	The printer's enforced margins.

Structures:	The following structure keeps track of all four of a document's margins.

typedef struct {
	int 	leftMargin;		/* measured in points */
	int 	topMargin;
	int 	rightMargin;
	int 	bottomMargin;
} MarginDimensions;

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_CALC_DOC_DIMENSIONS



void 	MSG_PRINT_CONTROL_CALC_DOCUMENT_DIMENSIONS(

	PageSizeReport 	*ptr); 

This message calculates the maximum printable area allowed by the 
user-selected paper size and printer-mandated margins. This message 
automatically resets the document size and margins to hold these values.

Note that this message correctly handles rotated pages.

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	retValue 	A pointer to a DocumentSize structure to hold 
return value.

Return:	Nothing returned explicitly.

retValue 	The pointer to the filled DocumentSize structure.

Structures:	The following structure holds a document's size, and the margin 
information used to place the top left point.

typedef struct {
	int 	leftMargin;		/* measured in points */
	int 	topMargin;
	int 	width;
	int 	height;
} DocumentSize;

Interception:	Unlikely.

n	MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT



Boolean 	MSG_PRINT_CONTROL_CHECK_IF_DOC_WILL_FIT(

	Boolean 	warning); 

This message returns true if the passed document margins will fit on the 
selected printer. 

Source:	Unrestricted.

Destination:	Any PrintControl object.

Parameters:	warning 	If set, the PrintControl will deliver a warning to the 
user.

Return:	Indication of whether the printer can work with the current margins 
(true if so, false if not).

Interception:	Unlikely.

	17.6	Page Size Control

MSG_PRINT_REPORT_PAGE_SIZE, SpoolSetDocSize(), 
MSG_PZC_GET_PAGE_SIZE, MSG_PZC_SET_PAGE_SIZE, 


Thanks to the Spooler, applications don't need to worry about what page size 
the user is working with. If a document is bigger than a printer's paper, then 
the Spooler will automatically tile the job onto as may sheets of paper as 
necessary. However, some applications will need to make the user decide on 
a page size. Page layout programs need to know what size page the user is 
working with. Spreadsheet programs need to make sure that cells aren't split 
in half by a page break.

Applications which ask the user to select a page size should incorporate a 
PageSizeControl into their generic tree. This generic control object provides 
a dialog box containing page setup choices for the user.

This dialog box contains UI allowing the user to specify what sort of paper the 
document is to be printed to: envelope, labels, or regular paper. The user may 
then specify a set of dimensions within that type; paper would have choices 
including letter sized, legal sized, and A4. The user can even set up a set of 
custom page dimensions.

Most applications which include a PageSizeControl will probably keep track 
of their own page size. In this case, use SpoolSetDocSize() to update the 
PageSizeControl's UI when changing the page size. When the user changes 
the paper size in the PageSizeControl, the control's output will receive a 
MSG_PRINT_REPORT_PAGE_SIZE.

Applications which do not keep track of the page size but want to include a 
PageSizeControl may do so. However, these applications will probably need 
to find out the page size at a specific time, not just receive notification every 
time the user changes the page size. This doesn't really fit the normal 
controller model, and in fact such applications are asking the 
PageSizeControl to act not like a controller, but like an ordinary piece of UI 
gadgetry. Setting the PZCA_ACT_LIKE_GADGET flag in the PageSizeControl's 
PZCI_attrs instance field will make the PageSizeControl act like a gadget.

If the PageSizeControl is to act as a controller, it must appear on the 
GenApplication's GAGCNLT_SELF_LOAD_OPTIONS GCN list, as does the 
PrintControl.

No matter how an application interacts with its PageSizeControl, it will work 
with the PageSizeReport structure.

typedef struct {
	dword 			PSR_width;
	dword 			PSR_height;
	PageLayout 			PSR_layout;
	PCMarginParams 			PSR_margins;
} PageSizeReport;

typedef WordFlags PageLayout;
typedef enum {
	PT_PAPER,
	PT_UNUSED1, /* Unused type */
	PT_ENVELOPE,
	PT_UNUSED2, /* Unused type */
	PT_LABEL
} PageType;

/* Specifying a PageLayout is accomplished by 
 * OR-ing together the parts of the layout.
 * Exactly what those parts are depends on the 
 * PageType: */

/* Paper Layouts: 
 * 	(PT_PAPER | (PO_orient << 3) ) */
typedef ByteEnum 				PaperOrientation;
#define PO_PORTRAIT 					0x00
#define PO_LANDSCAPE 					0x01

typedef WordFlags 				PageLayoutPaper;
#define PLP_ORIENTATION 					0x0008
#define PLP_TYPE 					0x0004 /* PT_PAPER */

/* Envelope Layouts: 
 * (PT_ENVELOPE | (EP_path << 5) | (EO_ori << 3)) */
typedef ByteEnum 				EnvelopePath;
#define EP_LEFT 					0x00
#define EP_CENTER 					0x01
#define EP_RIGHT 					0x02

typedef ByteEnum 				EnvelopeOrientation;
#define EO_PORTAIT_LEFT 					0x00
#define EO_PORTAIT_RIGHT 					0x01
#define EO_LANDSCAPE_UP 					0x02
#define EO_LANDSCAPE_DOWN 					0x03

typedef WordFlags 				PageLayoutEnvelope;
#define PLE_PATH 					0x0040
#define PLE_ORIENTATION 					0x0010
#define PLE_TYPE 					0x0004 /*PT_ENVELOPE*/

/* Label Layouts:
 * 	(PT_LABEL | (rows <<8) | (cols << 3)) */
typedef WordFlags 				PageLayoutLabel;
#define PLL_ROWS 					0x7e00 /* # rows */
#define PLL_COLUMNS 					0x01f8 /* # cols */
#define 	PLL_TYPE 				0x0004 /* PT_LABEL */

Code Display 17-3 PageSizeControl Features

typedef ByteFlags 	PageSizeControlFeatures;
/* The following flags may be combined with | and &:
	PSIZECF_MARGINS, 			( Set margin sizes )
	PSIZECF_CUSTOM_SIZE,			( Custom dimension ranges )
	PSIZECF_LAYOUT,
	PSIZECF_SIZE_LIST,
	PSIZECF_PAGE_TYPE 			( Choice of standard dimensions ) */

#define PSIZEC_DEFAULT_FEATURES (PSIZECF_PAGE_TYPE | PSIZECF_SIZE_LIST | \
		 		PSIZECF_LAYOUT | PSIZECF_CUSTOM_SIZE) 



n	MSG_PRINT_REPORT_PAGE_SIZE



void	MSG_PRINT_REPORT_PAGE_SIZE(

	PageSizeReport *psr);

The page size control's output object should have a handler for this message. 
The control will send this message whenever the user has changed the page 
size. The handler may wish to change the bounds of some appropriate object, 
store the new bounds somewhere, or take some other action.

Source:	PageSizeControl object.

Destination:	The GCI_output object.

Parameters:	psr 	A pointer to a PageSizeReport structure 
containing the page type and size.

Return:	Nothing.

Interception:	The handler for this message should store the page size information. It 
may set the bounds of one or more visual objects.

In addition to the standard set of generic Control instance data, the Page Size 
control includes some of its own. This data, as you might expect, is largely 
concerned with paper size and setup. The fields are listed in Code 
Display 17-4.

PageSizeControls use the PageLayout data structure to hold and pass 
layout information such as page orientation. One part of this structure gives 
the type of page being described: paper, envelope, or label. The other parts of 
the structure have different meanings depending on the type of page. For 
paper, the layout keeps track of the orientation. For envelopes, the structure 
contains both path and orientation information. For labels, this structure 
keeps track of how many labels are on the sheet in each direction; these 
numbers are bounded by MAXIMUM_LABELS_ACROSS and 
MAXIMUM_LABELS_DOWN.

Code Display 17-4 Page Size Control Instance Data

@instance PageSizeControlAttrs				PZCI_attrs = 0;
	/* Possible attributes:
	 * PZCA_ACT_LIKE_GADGET
	 * PZCA_PAPER_SIZE
	 * PZCA_INITIALIZE				*/

	/* Current page dimensions and type/layout */
@instance dword 			PZCI_width = 0;
@instance dword 			PZCI_height = 0;
@instance PageLayout 			PZCI_layout = 0;
@instance PCMarginParams			PZCI_margins = { 0, 0, 0, 0 };

/* The following vardata field is internal: */
@vardata PageSizeControlMaxDimensions TEMP_PAGE_SIZE_CONTROL_MAX_DIMENSIONS;

/* Its structure is defined: 
typedef struct {
 	dword PZCMD_width; ( maximum width )
 	dword PZCMD_height; ( maximum height )
} PageSizeControlMaxDimensions; */


/* Attribute to allow applications to be made aware of every change made
 * to gadgetry in the PageSizeControl. This is especially useful for applications
 * that want to add UI that will be dependent upon the state of this
 * controller. No effort is made to eliminate the redundant output of data. */

@vardata PageSizeControlChanges ATTR_PAGE_SIZE_CONTROL_UI_CHANGES;

/* This field has structure:
	typedef struct {
	 optr PSCC_destination; ( destination for message )
	 Message PSCC_message; ( message to be sent )
	} PageSizeControlChanges; */

The message should follow the prototype: */
@prototype void PAGE_SIZE_UI_CHANGES_MSG(PageSizeReport _far *psr )



PZCI_width, PZCI_height
	These integers represent the current page dimensions, 
measured in points. Note that these values may never go 
outside the bounds described by the constants 
MINIMUM_PAGE_WIDTH_VALUE, 
MINIMUM_PAGE_HEIGHT_VALUE, 
MAXIMUM_PAGE_WIDTH_VALUE, and 
MAXIMUM_PAGE_HEIGHT_VALUE.

PZCI_layout	
This field contains a PageLayout structure describing the 
current page layout settings.

PZCI_margins
This field contains the page's margins.

The following messages allow applications to work with a PageSizeControl 
directly, like a regular UI gadget. There is one message which the 
PageSizeControl will send to its output, and several that any object may send 
to the controller.

Note that these messages are only useful if the PageSizeControl is operating 
in gadget mode (i.e. if its PZCA_ACT_LIKE_GADGET bit is set). Otherwise, the 
control's default behavior would override that requested by these messages. 

The PageSizeControlAttrs record has three flags:

PZCA_ACT_LIKE_GADGET
The PageSizeControl object will act like any other generic 
gadget and will not respond to normal controller notifications.

PZCA_PAPER_SIZE
The PageSizeControl object will display paper sizes instead of 
document sizes. Setting this would be very rare for 
applications.

PZCA_INITIALIZE
The PageSizeControl object will be initialized to system default 
values if PZCA_ACT_LIKE_GADGET is also set.

PZCA_LOAD_SAVE_OPTIONS
Allow the controller to load and save the user's options.

n	MSG_PZC_GET_PAGE_SIZE



void	MSG_PZC_GET_PAGE_SIZE(

	PageSizeReport		*psr);

This message returns the present page size in terms of width, height, and 
layout, stored in a PageSizeReport. Note that the PageSizeControl will be 
sending out a MSG_PRINT_REPORT_PAGE_SIZE every time the user applies 
changes to these values. 

Only send this message if the PageSizeControl is operating in gadget mode.

Source:	Unrestricted, as long as the PageSizeControl is in gadget mode.

Destination:	PageSizeControl object.

Parameters:	psr	A pointer to a PageSizeReport structure in which 
the page size will be returned.

Return:	The PageSizeReport structure pointed to by psr will be filled with the 
page information.

Interception:	Unlikely.

n	MSG_PZC_SET_PAGE_SIZE



void 	MSG_PZC_SET_PAGE_SIZE(

	PageSizeReport *	psr);		

This message changes the current page size. It takes a PageSizeReport 
data structure containing the new dimensions and layout. The Apply trigger 
of a Page Size Control normally sends this message, passing the present user 
page size as arguments.

Only send this message if the PageSizeControl is operating in gadget mode.

Source:	Unrestricted, as long as the PageSizeControl is in gadget mode.

Destination:	PageSizeControl object.

Parameters:	psr	A pointer to a PageSizeReport structure 
containing page information.

Return:	Nothing.

Interception:	Unlikely.

	17.7	Other Printing Components

Most geodes can fulfill all their drawing needs by working with their Print 
Control. The system has two other main parts that work with printing: the 
first is the spool library, which controls scheduling; the second is a collection 
of printer drivers, which handle interactions between the printing devices 
and the rest of the system.

	17.7.1	Spooler and Scheduling

SpoolInfo(), SpoolHurryJob(), SpoolDelayJob(), 
SpoolModifyPriority(), SpoolVerifyPrinterPort(), 
SpoolCreateSpoolFile(), SpoolDelJob(), 
MSG_PRINT_NOTIFY_PRINT_JOB_CREATED

Geodes may invoke certain routines that affect the Spooler, but for the most 
part they should avoid this practice. These routines allow the geode to take 
advantage of some of the Spooler's scheduling capabilities, so the geode can 
hurry some jobs and delay others. Most applications should not be concerned 
with the scheduling of printer jobs. If the user thinks that some job should be 
given a higher priority, he can work with the Printer Control Panel. Many of 
the following routines require the print job's ID. Applications that will use 
these routines should have a handler for 
MSG_PRINT_NOTIFY_PRINT_JOB_CREATED, which has reference material 
at the end of this section.

For those applications that will be scheduling, the SpoolHurryJob() and 
SpoolDelayJob() routines may come in handy. Passed a job ID, these 
routines move the associated job to the front or back of the queue (see 
Figure 17-9). SpoolModifyPriority() changes the priority of a spool thread 
so that it may print in the foreground or be moved further into the 
background. Each function returns a code describing the Spool's Operating 
Status, alerting the geode if the job wasn't found, the queue was empty, the 
port couldn't be verified, or even if the operation was successful.

The SpoolInfo() routine returns the list of jobs on a queue or information 
about any individual job. The other kind of information application writers 
might be interested in is the printer port status. It's sometimes unwise to 
spool a job when the printer is having problems, and 
SpoolVerifyPrinterPort() can give some warning about problems.

While the above routines are used only rarely in applications, there are 
others that are used even less often. SpoolCreateSpoolFile() creates a 
spool file, which may be used to create a pre-generated job for the printer 
kept handy. Such a file would only be useful to applications that can only 
print one thing and don't mind leaving around an extra spool file. 
SpoolDelJob() deletes jobs from the queue.

n	MSG_PRINT_NOTIFY_PRINT_JOB_CREATED



void 	MSG_PRINT_NOTIFY_PRINT_JOB_CREATED(

	word jobID);		

The PrintControl sends this message when it has sent the job to the print 
queue. Applications may intercept this message to retrieve the new job's ID 
number or for any other reason.

Source:	PrintControl object.

Destination:	GCI_output object.

Parameters:	jobID	Print job's ID.

Return:	Nothing.

Interception:	Applications that want to know the ID of created jobs should intercept 
this message. Note that this behavior is not encouraged, and most 
applications will want to leave meddling with a print job's status up to 
the user's discretion.

	17.7.2	Printer Drivers

Printer Drivers are very important but at the same time not something that 
any geode you write is going to interact with. Printer Drivers take care of 
translating the device-independent graphics commands into forms that 
individual models of printers can work with. Drivers also provide the Spooler 
with information about the printer and provide printer-specific UI to the 
Print Control.

As long as a geode uses only device-independent graphics commands, that 
geode need not worry about printer drivers. In fact, the only geodes that 
require knowledge of printer drivers are those that use Raw Mode (see below) 
printing and access the printer drivers directly. To incorporate Raw Mode 
printing into a geode (normally a bad idea), keep reading. Whether or not the 
geode you're writing will interact with Printer Drivers, you might want to 
continue reading this section to find out what printer drivers do.

	17.7.2.1	Dumb Printers

When dealing with a printer that has no page description language, the 
Spooler and Printer Driver have to be clever. First the Spooler allocates an 
offscreen bitmap and plays back the contents of the spool file into the bitmap. 
It does this through the vidmem driver, which has the same interface as a 
video driver but draws to offscreen bitmaps instead of to a device. Once the 
bitmap is complete, the Spooler starts feeding the bitmap to the Printer 
Driver, which translates the bitmap into a series of commands to the printer. 
If (as is often the case) the printer doesn't have enough memory to absorb a 
whole page's worth of data at a time, the Printer Driver feeds in one 
horizontal band, or "swath" of the bitmap at a time. This also allows for better 
backgrounding: The driver can feed in a swath, then block and allow other 
threads to complete other tasks until the printer is ready for the next swath.

	17.7.2.2	Smart Printers

When dealing with an "intelligent" printer, a printer that has its own page 
description language, the Spooler bypasses most of the work it has to do 
when dealing with dumb printers. Since an intelligent printer presumably 
has some commands with GEOS equivalents, the Spooler doesn't bother with 
creating a bitmap but just passes the GString on to the Printer Driver. The 
Printer Driver then builds a page description in the printer's native language 
based on the contents of the GString.

	17.7.2.3	Raw Mode

If you want to use some printer-specific command that doesn't have an 
equivalent in the GEOS graphics system, you will use raw mode. Raw Mode 
printing expects that you know what sorts of commands the printer will be 
expecting. If you try to send PostScript commands to a printer that doesn't 
understand PostScript, probably the results won't be the desired output.

	17.7.2.4	Printer Information and Manipulation

SpoolGetNumPrinters(), SpoolGetPrinterString(), 
SpoolGetPrinterInfo(), SpoolCreatePrinter(), 
SpoolDeletePrinter(), SpoolGetDefaultPrinter(), 
SpoolSetDefaultPrinter()

If you want to find out something about a printer without accessing the 
printer driver directly, the spool library provides a number of utility routines 
that work with printer drivers.

Most of these functions specify which of a user's printers they wish to work 
with by passing its printer number. The first installed printer will be printer 
number zero, the second printer will be number one, and so on. Use the 
SpoolGetNumPrinters() routine to find out how many printers have been 
installed. Once you have this number, you have an upper bound for printer 
numbers to test. There will always be fewer than 
MAXIMUM_NUMBER_OF_PRINTERS printers.

The SpoolGetPrinterString() and SpoolGetPrinterInfo() routines 
return information about the printer associated with the passed printer 
number. SpoolGetPrinterString() returns the a string containing the 
name of the printer, such as "HP DeskJet on COM1." The printer string will 
be of length at most MAXIMUM_PRINTER_NAME_LENGTH. 
SpoolGetPrinterInfo() returns various pieces of information helpful to 
geodes that work with printers directly.

The SpoolCreatePrinter() and SpoolDeletePrinter() routines can install 
and uninstall printers, tasks probably best left to the Preferences Manager. 
The SpoolDeletePrinter() routine uninstalls the printer corresponding to 
the passed printer number. It is easy to use but not something that most 
geodes should be doing. SpoolCreatePrinter() installs a new printer, 
returning the new printer's printer number. Note that any geode calling this 
latter function is going to be expected to provide considerable information 
about the printer being installed. Again, this is not a routine that many 
geodes should be using.

SpoolGetDefaultPrinter() returns the number of the system-default 
printer. SpoolSetDefaultPrinter() makes the printer associated with the 
passed printer number the new system-default printer. Note that these 
routines are concerned with the system-default printer as opposed to the 
application-default printer. Most applications won't specify an application 
specific default printer. For these applications, the system-default printer 
will be the default. Of course, the user will still be allowed to select any 
printer, ignoring the default.

	17.7.3	Page Size Related Routines

SpoolGetNumPaperSizes(), SpoolGetPaperString(), 
SpoolConvertPaperSize(), SpoolCreatePaperSize(), 
SpoolDeletePaperSize(), SpoolGetPaperSizeOrder(), 
SpoolSetPaperSizeOrder()

Most geodes won't be too concerned with what sort of page size choice the 
user has made. If the document fits on the page, all's well. If the document 
doesn't fit, the Spooler will tile the job using only as many pieces of paper as 
necessary. If the document size should be the same as the page size, the geode 
can send a MSG_PRINT_CONTROL_CALC_DOCUMENT_DIMENSIONS to the 
print control.

There are several routines which deal with page sizes. Most applications 
have no reason to call these functions, and we will not discuss them in detail 
here. Some general notes are in order for those programmers who might work 
with these functions, though.

It takes two indexes to access a page size. The first is the PageType: paper, 
envelope, or label. Within each type, there are up to MAX_PAPER_SIZES page 
sizes. To find out the number of page sizes presently defined for a given type, 
use the SpoolGetNumPaperSizes() routine. To find the number of a paper 
size associated with a PageType and a given set of dimensions, use the 
SpoolConvertPaperSize() routine. For the text string describing the size 
of the page, call SpoolGetPaperString() (the buffer to hold the string 
should be of size MAX_PAPER_STRING_LENGTH.

To find out the order in which the page sizes of a given type are stored, call 
SpoolGetPaperSizeOrder(). You may give a new order by calling 
SpoolSetPaperSizeOrder(). This will affect the order in which page sizes 
are presented in a page size control.

To create a new page size, call SpoolCreatePaperSize(). This routine takes 
a page type, a set of dimensions, and a string to describe those dimensions to 
the user. It returns a new page size number. To destroy a page size, call 
SpoolDeletePaperSize(). 

	17.8	Debugging Tips

Though GEOS provides an easy-to-use printer interface, mistakes are sure to 
happen. You may find the following information useful in catching your 
mistakes or for testing rigorously.

Under Swat, you can see the various threads the Spooler works with. There 
is always one spool thread running in the background. When the first job is 
queued on a printer, you can see that a new thread is created for that printer. 
When the last job for a printer is completed, the thread exits again. 

Here are the most common printing mistakes:

u	Forgetting to add the Print Control object to the application's Active List 
(the MGCNLT_ACTIVE_LIST GCN list).

u	Not setting the document name (see section 17.4.6 on page 1073).

u	Setting the number of pages (page range) incorrectly.
