TweetFollow Us on Twitter

MACINTOSH C CARBON

Demonstration Program CarbonPrinting

Goto Contents

// *******************************************************************************************
// CarbonPrinting.h                                                        CLASSIC EVENT MODEL
// *******************************************************************************************
// 
// This program:
//
// o  Demonstrates printing using the Carbon Printing Manager session functions. 
//
// o  Opens two windows. The first window is a document window in which is displayed the
//    document to be printed.  The second window displays some printing-related information
//    obtained from PMPageFormat and PMPrintSettings objects.
//
// o  Customises the Pring dialog by adding a pop-up menu button, three radio buttons, a 
//    checkbox, and a group box. 
//
// o  Allows the user to print a document containing a picture and text, with the text
//    being printed in the font and font size, and with the fractional widths setting, 
//    specified using the items added to the Print dialog.
//
// The customising of the Print dialog uses the AppendDITL method.  Accordingly, on Mac OS X,
// the dialogs are application-modal and are not displayed as window-modal sheet dialogs.
//
// The program utilises the following resources:
//
// o  A 'plst' resource.
//
// o  'MBAR' resource and associated 'MENU' resources (preload, non-purgeable).
//
// o  Two 'WIND' resources (purgeable).
//
// o  A 'TEXT' resource (purgeable) used for printing.
//
// o  A 'PICT' resource (non-purgeable) used for printing.
//
// o  'CNTL' resources (purgeable) for controls added to the Print dialog box.
//
// o  A 'DITL' resource (purgeable) specifying the items to be appended to the Print dialog
//    box.
//
// o  A 'MENU' resource (preload, non-purgeable) for the pop-up menu button.
//
// o  A 'SIZE' resource with the acceptSuspendResumeEvents, canBackground, 
//    doesActivateOnFGSwitch, and isHighLevelEventAware flags set.
//
// *******************************************************************************************

// .................................................................................. includes

#include <Carbon.h>

// ................................................................................... defines

#define rMenubar               128
#define mAppleApplication      128
#define  iAbout                1
#define mFile                  129
#define  iPageSetup            9
#define  iPrint                10
#define  iQuit                 12
#define mFont                  131
#define rDocWindow             128
#define rInfoWindow            129
#define rText                  128
#define rPicture               128
#define rPrintDialogAppendDITL 128
#define  iPopupButton          1
#define  iRadioButton10pt      2
#define  iRadioButton12pt      3
#define  iRadioButton14pt      4
#define  iCheckboxFracWidths   5
#define kMargin                90
#define MAX_UINT32             0xFFFFFFFF
#define MIN(a,b)               ((a) < (b) ? (a) : (b))

// .................................................................................. typedefs

typedef struct
{
  PMPrintSession  printSession;
  PMPageFormat    pageFormat;
  PMPrintSettings printSettings;
  TEHandle        editTextStrucHdl;
  PicHandle       pictureHdl;
} docStructure, *docStructurePtr, **docStructureHdl;

// ....................................................................... function prototypes

void      main                         (void);
void      doPreliminaries              (void);
OSErr      quitAppEventHandler         (AppleEvent *,AppleEvent *,SInt32);
void      doGetDocument                (void);
void      doEvents                     (EventRecord *);
void      doUpdate                     (EventRecord *);
void      doUpdateDocumentWindow       (WindowRef);
void      doMenuChoice                 (SInt32);
OSStatus  doCreateOrValidatePageFormat (WindowRef);
OSStatus  doPageSetUpDialog            (WindowRef);
OSStatus  doPrintSettingsDialog        (WindowRef);
OSStatus  doPrinting                   (WindowRef);
SInt16    doCalcNumberOfPages          (WindowRef,Rect);
void      doDrawPage                   (WindowRef,Rect,SInt16,SInt16);
void      doDrawPrintInfo              (void);
void      doDrawRectStrings            (Str255,SInt16,SInt16,Str255,SInt16,SInt16,Str255);
void      doErrorAlert                 (OSStatus);
void      doConcatPStrings             (Str255,Str255);

void      initialisationFunction       (PMPrintSettings, PMDialog *);
void      itemEvaluationFunction       (DialogPtr,SInt16);
Boolean   eventFilter                  (DialogPtr,EventRecord *,SInt16 *);

// *******************************************************************************************
// CarbonPrinting.c
// *******************************************************************************************

// .................................................................................. includes

#include "CarbonPrinting.h"

// .......................................................................... global variables

Boolean         gRunningOnX = false;
WindowRef       gDocumentWindowRef, gPrintInfoWindowRef;
SInt16          gFontNumber;
SInt16          gFontSize;
Boolean         gDone;
PMItemUPP       gNewItemEvaluateFunctionUPP;
ModalFilterUPP  gEventFilterUPP;
PMPrintSettings gPrintSettings = kPMNoPrintSettings;
PMDialog        gPMDialog;
UInt32          gFirstPage, gLastPage, gCopies;
Boolean         gDrawPrintSettingsStuff;

// ************************************************************************************** main

void  main(void)
{
  MenuBarHandle   menubarHdl;
  SInt32          response;
  MenuRef         menuRef;
  docStructureHdl docStrucHdl;
  SInt16          fontNum;
  RGBColor        whiteColour = { 0xFFFF, 0xFFFF, 0xFFFF };
  RGBColor        blueColour  = { 0x4444, 0x4444, 0x9999 };
  Rect            portRect;
  EventRecord     eventStructure;
  Boolean         gotEvent;

  // ......................................................................... do prelimiaries

  doPreliminaries();

  // ............................................................... set up menu bar and menus

  menubarHdl = GetNewMBar(rMenubar);
  if(menubarHdl == NULL)
    ExitToShell();
  SetMenuBar(menubarHdl);
  DrawMenuBar();

  Gestalt(gestaltMenuMgrAttr,&response);
  if(response & gestaltMenuMgrAquaLayoutMask)
  {
    menuRef = GetMenuRef(mFile);
    if(menuRef != NULL)
    {
      DeleteMenuItem(menuRef,iQuit);
      DeleteMenuItem(menuRef,iQuit - 1);
    }

    gRunningOnX = true;
  }

  // ...................................... open document window and attach document structure

  if(!(gDocumentWindowRef = GetNewCWindow(rDocWindow,NULL,(WindowRef)-1)))
    ExitToShell();
  
  SetPortWindowPort(gDocumentWindowRef);
  GetFNum("\pGeneva",&fontNum);
  TextFont(fontNum);
  TextSize(10);
  gFontNumber = fontNum;
  gFontSize = 10;
  
  if(!(docStrucHdl = (docStructureHdl) NewHandle(sizeof(docStructure))))
    ExitToShell();
  SetWRefCon(gDocumentWindowRef,(SInt32) docStrucHdl);

  (*docStrucHdl)->printSession  = NULL;
  (*docStrucHdl)->pageFormat    = kPMNoPageFormat;
  (*docStrucHdl)->printSettings = kPMNoPrintSettings;

  // ........................................................ open printing information window
  
  if(!(gPrintInfoWindowRef = GetNewCWindow(rInfoWindow,NULL,(WindowRef)-1)))
    ExitToShell();
    
  SetPortWindowPort(gPrintInfoWindowRef);
  TextFont(fontNum);
  TextSize(10);
  RGBForeColor(&whiteColour);
  RGBBackColor(&blueColour);
  GetWindowPortBounds(gPrintInfoWindowRef,&portRect);
  EraseRect(&portRect);

  // ..................................................... load and display simulated document

  doGetDocument();

  // .............................................................................. event loop

  gDone = false;

  while(!gDone)
  {
    gotEvent = WaitNextEvent(everyEvent,&eventStructure,MAX_UINT32,NULL);
    if(gotEvent)
      doEvents(&eventStructure);
  }
}

// *************************************************************************** doPreliminaries

void  doPreliminaries(void)
{
  OSErr osError;

  MoreMasterPointers(128);
  InitCursor();
  FlushEvents(everyEvent,0);

  osError = AEInstallEventHandler(kCoreEventClass,kAEQuitApplication,
                            NewAEEventHandlerUPP((AEEventHandlerProcPtr) quitAppEventHandler),
                            0L,false);
  if(osError != noErr)
    ExitToShell();
}

// **************************************************************************** doQuitAppEvent

 OSErr  quitAppEventHandler(AppleEvent *appEvent,AppleEvent *reply,SInt32 handlerRefcon)
{
  OSErr    osError;
  DescType returnedType;
  Size     actualSize;

  osError = AEGetAttributePtr(appEvent,keyMissedKeywordAttr,typeWildCard,&returnedType,NULL,0,
                              &actualSize);

  if(osError == errAEDescNotFound)
  {
    gDone = true;
    osError = noErr;
  } 
  else if(osError == noErr)
    osError = errAEParamMissed;

  return osError;
}

// ***************************************************************************** doGetDocument

void  doGetDocument(void)
{
  docStructureHdl docStrucHdl;
  Rect            portRect, destRect, viewRect;
  Handle          textHdl;
  
  SetPortWindowPort(gDocumentWindowRef);

  docStrucHdl = (docStructureHdl) GetWRefCon(gDocumentWindowRef);  

  GetWindowPortBounds(gDocumentWindowRef,&portRect);  
  destRect = portRect;
  InsetRect(&destRect,4,4);
  destRect.bottom +=4;
  viewRect = destRect;
  (*docStrucHdl)->editTextStrucHdl = TENew(&destRect,&viewRect);

  textHdl = GetResource('TEXT',rText);
  if(textHdl == NULL)
    ExitToShell();

  HLock(textHdl);
  TEInsert(*textHdl,GetHandleSize(textHdl),(*docStrucHdl)->editTextStrucHdl);
  HUnlock(textHdl);
  
  ReleaseResource(textHdl);

  (*docStrucHdl)->pictureHdl = GetPicture(rPicture);
  if((*docStrucHdl)->pictureHdl == NULL)
    ExitToShell();

  InvalWindowRect(gDocumentWindowRef,&portRect);
}

// ********************************************************************************** doEvents

void  doEvents(EventRecord *eventStrucPtr)
{
  WindowRef      windowRef;
  WindowPartCode partCode;

  windowRef = (WindowRef) eventStrucPtr->message;

  switch(eventStrucPtr->what)
  {
    case kHighLevelEvent:
      AEProcessAppleEvent(eventStrucPtr);
      break;

    case mouseDown:
      partCode = FindWindow(eventStrucPtr->where,&windowRef);
      switch(partCode)
      {
        case inMenuBar:
          doMenuChoice(MenuSelect(eventStrucPtr->where));
          break;

        case inContent:
          if(windowRef != FrontWindow())
            SelectWindow(windowRef);
          break;

        case inDrag:
          DragWindow(windowRef,eventStrucPtr->where,NULL);
          break;
      }
      break;

    case keyDown:
      if((eventStrucPtr->modifiers & cmdKey) != 0)
        doMenuChoice(MenuEvent(eventStrucPtr));
      break;

    case updateEvt:
      doUpdate(eventStrucPtr);
      break;
  }
}

// ********************************************************************************** doUpdate

void  doUpdate(EventRecord *eventStrucPtr)
{
  WindowRef windowRef;
  GrafPtr   oldPort;
  Rect      portRect;
    
  windowRef = (WindowRef) eventStrucPtr->message;
  
  GetPort(&oldPort);

  BeginUpdate(windowRef);
  
  if(windowRef == gDocumentWindowRef)
    doUpdateDocumentWindow(windowRef);
  else if(windowRef == gPrintInfoWindowRef)
  {
    SetPortWindowPort(gPrintInfoWindowRef);
    GetWindowPortBounds(gPrintInfoWindowRef,&portRect);
    EraseRect(&portRect);
    doDrawPrintInfo();
  }

  EndUpdate(windowRef);

  SetPort(oldPort);
}

// ******************************************************************** doUpdateDocumentWindow

void  doUpdateDocumentWindow(WindowRef windowRef)
{
  Rect            portRect, pictureRect, savedDestRect;
  docStructureHdl docStrucHdl;
  SInt16          savedFontNum, savedFontSize, savedLineHeight, fontNum;

  SetPortWindowPort(windowRef);
  GetWindowPortBounds(gDocumentWindowRef,&portRect);
  docStrucHdl = (docStructureHdl) GetWRefCon(windowRef);

  savedDestRect = (*(*docStrucHdl)->editTextStrucHdl)->destRect;
  savedFontNum = (*(*docStrucHdl)->editTextStrucHdl)->txFont;
  savedFontSize = (*(*docStrucHdl)->editTextStrucHdl)->txSize;
  savedLineHeight = (*(*docStrucHdl)->editTextStrucHdl)->lineHeight;

  (*(*docStrucHdl)->editTextStrucHdl)->destRect = portRect;
  InsetRect(&(*(*docStrucHdl)->editTextStrucHdl)->destRect,4,4);
  (*(*docStrucHdl)->editTextStrucHdl)->destRect.bottom += 4;
  GetFNum("\pGeneva",&fontNum);
  (*(*docStrucHdl)->editTextStrucHdl)->txFont = fontNum;
  (*(*docStrucHdl)->editTextStrucHdl)->txSize = 10;
  (*(*docStrucHdl)->editTextStrucHdl)->lineHeight = 13;
  TECalText((*docStrucHdl)->editTextStrucHdl);
  TEUpdate(&portRect,(*docStrucHdl)->editTextStrucHdl);  

  (*(*docStrucHdl)->editTextStrucHdl)->destRect = savedDestRect;
  (*(*docStrucHdl)->editTextStrucHdl)->txFont = savedFontNum;
  (*(*docStrucHdl)->editTextStrucHdl)->txFont = savedFontSize;
  (*(*docStrucHdl)->editTextStrucHdl)->lineHeight = savedLineHeight;

  SetRect(&pictureRect,2,2,180,134);
  DrawPicture((*docStrucHdl)->pictureHdl,&pictureRect);
}

// ****************************************************************************** doMenuChoice

void  doMenuChoice(SInt32 menuChoice)
{
  MenuID        menuID;
  MenuItemIndex menuItem;
  OSStatus      osStatus;
  Rect          portRect;

  menuID = HiWord(menuChoice);
  menuItem = LoWord(menuChoice);

  if(menuID == 0)
    return;

  switch(menuID)
  {
    case mAppleApplication:
      if(menuItem == iAbout)
        SysBeep(10);
      break;
      
    case mFile:
      if(menuItem == iPageSetup)
      {
        osStatus = doPageSetUpDialog(gDocumentWindowRef);
        if(osStatus != kPMNoError)
          doErrorAlert(osStatus);
      }
      
      if(menuItem == iPrint)
      {
        osStatus = doPrintSettingsDialog(gDocumentWindowRef);
        if(osStatus == kPMNoError)
        {
          osStatus = doPrinting(gDocumentWindowRef);
          if(osStatus != kPMNoError)
            doErrorAlert(osStatus);
        }
        else if(osStatus != kPMCancel)
          doErrorAlert(osStatus);
      }

      GetWindowPortBounds(gPrintInfoWindowRef,&portRect);
      InvalWindowRect(gPrintInfoWindowRef,&portRect);
      
      if(menuItem == iQuit)
        gDone = true;
  
      break;
  }

  HiliteMenu(0);
}

// ************************************************************** doCreateOrValidatePageFormat 

OSStatus  doCreateOrValidatePageFormat (WindowRef windowRef)
{
  docStructureHdl docStrucHdl;
  OSStatus        osStatus     = kPMNoError;
  PMPrintSession  printSession = NULL;
  PMPageFormat    pageFormat   = kPMNoPageFormat;

  docStrucHdl = (docStructureHdl) GetWRefCon(windowRef);  
  HLock((Handle) docStrucHdl);

  // ................................................................. create printing session

  osStatus = PMCreateSession(&printSession);

  // .......... if necessary, create and store page format object, otherwise validate existing
  
  if(osStatus == noErr)
  {
    if((*docStrucHdl)->pageFormat == kPMNoPageFormat)
    {
      osStatus = PMCreatePageFormat(&pageFormat);

      if((osStatus == kPMNoError) && (pageFormat != kPMNoPageFormat))
      {
        osStatus = PMSessionDefaultPageFormat(printSession,pageFormat);

        if(osStatus == kPMNoError)
          (*docStrucHdl)->pageFormat = pageFormat;
      }
      else
      {
        if(osStatus == kPMNoError)
          osStatus = kPMGeneralError;
      }
    }
    else
    {
      osStatus = PMSessionValidatePageFormat(printSession,(*docStrucHdl)->pageFormat,
                                             kPMDontWantBoolean);
    }
  }

  // ............................................ store printing session, or clean up if error

  if(osStatus == kPMNoError)
    (*docStrucHdl)->printSession = printSession;
  else
  {
    if(pageFormat != kPMNoPageFormat)
      PMRelease(pageFormat);
    if(printSession != NULL)
      PMRelease(printSession);
  }

  HUnlock((Handle) docStrucHdl);

  return osStatus;
}

// ************************************************************************* doPageSetUpDialog

OSStatus  doPageSetUpDialog(WindowRef windowRef)
{
  OSStatus        osStatus = kPMNoError;
  docStructureHdl docStrucHdl;
  Boolean         userClickedOKButton;

  osStatus = doCreateOrValidatePageFormat (windowRef);
  if(osStatus != kPMNoError)
    return osStatus;

  docStrucHdl = (docStructureHdl) GetWRefCon(windowRef);  
  HLock((Handle) docStrucHdl);

  osStatus = PMSessionPageSetupDialog((*docStrucHdl)->printSession,(*docStrucHdl)->pageFormat,
                                      &userClickedOKButton);

  if((*docStrucHdl)->printSession != NULL)
  {
    PMRelease((*docStrucHdl)->printSession);
    (*docStrucHdl)->printSession = NULL;
  }

  HUnlock((Handle) docStrucHdl);
  gDrawPrintSettingsStuff = false;

  return osStatus;
}

// ********************************************************************* doPrintSettingsDialog

OSStatus  doPrintSettingsDialog(WindowRef windowRef)
{
  OSStatus             osStatus      = kPMNoError;
  docStructureHdl      docStrucHdl;
  PMPrintSettings      printSettings = kPMNoPrintSettings;
  CFStringRef          stringRef;
  PMPrintDialogInitUPP initialisationFunctionUPP;
  Boolean              userClickedPrintButton;

  osStatus = doCreateOrValidatePageFormat (windowRef);
  if(osStatus != kPMNoError)
    return osStatus;

  docStrucHdl = (docStructureHdl) GetWRefCon(windowRef);  
  HLock((Handle) docStrucHdl);

  osStatus = PMCreatePrintSettings(&printSettings);
  if((osStatus == kPMNoError) && (printSettings != kPMNoPrintSettings))
  {
    osStatus = CopyWindowTitleAsCFString(windowRef,&stringRef);
    if(osStatus == noErr)
    {
      osStatus = PMSetJobNameCFString(printSettings,stringRef);
      CFRelease(stringRef);
    }
    
    if(osStatus == noErr)
      osStatus = PMSessionDefaultPrintSettings((*docStrucHdl)->printSession,printSettings);
    
    if(osStatus == kPMNoError)
    {
      (*docStrucHdl)->printSettings = printSettings;
      printSettings = kPMNoPrintSettings;
    }
  }
  
  if(osStatus == kPMNoError)
  {
    if(gRunningOnX)
    {
      PMSetPageRange((*docStrucHdl)->printSettings,1,kPMPrintAllPages);
      PMSetFirstPage((*docStrucHdl)->printSettings,1,false);
      PMSetLastPage((*docStrucHdl)->printSettings,9999,false);
    }
  }
  
  if(osStatus == kPMNoError)
  {
    initialisationFunctionUPP   = NewPMPrintDialogInitUPP((PMPrintDialogInitProcPtr)
                                                          initialisationFunction);
    gNewItemEvaluateFunctionUPP = NewPMItemUPP((PMItemProcPtr) itemEvaluationFunction);
    gEventFilterUPP             = NewModalFilterUPP((ModalFilterProcPtr) eventFilter);

    osStatus = PMSessionPrintDialogInit((*docStrucHdl)->printSession,
                                        (*docStrucHdl)->printSettings,
                                        (*docStrucHdl)->pageFormat,&gPMDialog);
    if(osStatus == kPMNoError)
      osStatus = PMSessionPrintDialogMain((*docStrucHdl)->printSession,
                                          (*docStrucHdl)->printSettings,
                                          (*docStrucHdl)->pageFormat,
                                          &userClickedPrintButton,initialisationFunctionUPP);

    if(osStatus == kPMNoError && !userClickedPrintButton)
      osStatus = kPMCancel;

    DisposePMPrintDialogInitUPP(initialisationFunctionUPP);
    DisposePMItemUPP(gNewItemEvaluateFunctionUPP);
    DisposeModalFilterUPP(gEventFilterUPP);
  }

  if(osStatus != kPMNoError || osStatus == kPMCancel)
  {
    if((*docStrucHdl)->printSettings != kPMNoPrintSettings)
    {
      PMRelease((*docStrucHdl)->printSettings);
      (*docStrucHdl)->printSettings = kPMNoPrintSettings;
    }
    if((*docStrucHdl)->printSession != NULL)
    {
      PMRelease((*docStrucHdl)->printSession);
      (*docStrucHdl)->printSession = NULL;
    }
  }    

  HUnlock((Handle) docStrucHdl);
  gDrawPrintSettingsStuff = userClickedPrintButton;

  return osStatus;
}

// ******************************************************************************** doPrinting

OSStatus  doPrinting(WindowRef windowRef)
{
  docStructureHdl docStrucHdl;
  OSStatus        osStatus = kPMNoError;
  UInt32          firstPage, lastPage, numberOfPages;
  PMRect          adjustedPageRect;
  Rect            pageRect;
  SInt16          page;
  GrafPtr         oldPort, currentPort, printingPort;

  GetPort(&oldPort);
  docStrucHdl = (docStructureHdl) GetWRefCon(windowRef);  
  HLock((Handle) docStrucHdl);

  // ......................... get first and last page to print as set by user in Print dialog

  osStatus = PMGetFirstPage((*docStrucHdl)->printSettings,&firstPage);

  if(osStatus == kPMNoError)
    osStatus = PMGetLastPage((*docStrucHdl)->printSettings,&lastPage);

  // for demo purposes, store first, last page and copies for Some Printing Information window

  gFirstPage = firstPage;
  gLastPage = lastPage;
  PMGetCopies((*docStrucHdl)->printSettings,&gCopies);

  // .............. get actual number of pages in document and, if necessary, adjust last page

  if(osStatus == kPMNoError)
    osStatus = PMGetAdjustedPageRect((*docStrucHdl)->pageFormat,&adjustedPageRect);

  if(osStatus == kPMNoError)
  {
    pageRect.top    = adjustedPageRect.top;
    pageRect.left   = adjustedPageRect.left;
    pageRect.bottom = adjustedPageRect.bottom;
    pageRect.right  = adjustedPageRect.right;

    numberOfPages = doCalcNumberOfPages(windowRef,pageRect);

    if(numberOfPages < lastPage)
      lastPage = numberOfPages;
  }

  // ............................... for Mac OS X, set first and last page for progress dialog

  if(gRunningOnX)
  {
    if(osStatus == kPMNoError)
      osStatus = PMSetFirstPage((*docStrucHdl)->printSettings,firstPage,false);

    if(osStatus == kPMNoError)
      osStatus = PMSetLastPage((*docStrucHdl)->printSettings,lastPage,false);
  }

  // ........................................................................... printing loop

  if(osStatus == kPMNoError)
  {
    osStatus = PMSessionBeginDocument((*docStrucHdl)->printSession,
                                      (*docStrucHdl)->printSettings,
                                      (*docStrucHdl)->pageFormat);
    if(osStatus == kPMNoError)
    {
      page = 1;

      while((page <= lastPage) && (osStatus == kPMNoError) &&
            (PMSessionError((*docStrucHdl)->printSession) == kPMNoError))
      {
        osStatus = PMSessionBeginPage((*docStrucHdl)->printSession,
                                      (*docStrucHdl)->pageFormat,NULL);
        if(osStatus != kPMNoError)
          break;
  
        GetPort(¤tPort);
            
        osStatus = PMSessionGetGraphicsContext((*docStrucHdl)->printSession,
                                               kPMGraphicsContextQuickdraw,
                                               (void **) &printingPort);
        if(osStatus == kPMNoError)
        {
          SetPort(printingPort);
          doDrawPage(windowRef,pageRect,page,numberOfPages);
          SetPort(currentPort);
        }
          
        osStatus = PMSessionEndPage((*docStrucHdl)->printSession);
        if(osStatus != kPMNoError)
          break;
            
        page++;
      }
    }
      
    PMSessionEndDocument((*docStrucHdl)->printSession);
  }

  // ................................................................................ clean up

  if((*docStrucHdl)->printSettings != kPMNoPrintSettings)
  {
    PMRelease((*docStrucHdl)->printSettings);
    (*docStrucHdl)->printSettings = kPMNoPrintSettings;
  }
  if((*docStrucHdl)->printSession != NULL)
  {
    PMRelease((*docStrucHdl)->printSession);
    (*docStrucHdl)->printSession = NULL;
  }

  HUnlock((Handle) docStrucHdl);
  SetPort(oldPort);

  return osStatus;
}

// *********************************************************************** doCalcNumberOfPages

SInt16  doCalcNumberOfPages(WindowRef windowRef,Rect pageRect)
{
  docStructureHdl docStrucHdl;
  FontInfo        fontInfo;
  Rect            destRect;
  SInt16          heightDestRect, linesPerPage, numberOfPages;

  docStrucHdl = (docStructureHdl) GetWRefCon(windowRef);  
  
  TextFont(gFontNumber);
  TextSize(gFontSize);
  GetFontInfo(&fontInfo);

  (*(*docStrucHdl)->editTextStrucHdl)->txFont = gFontNumber;
  (*(*docStrucHdl)->editTextStrucHdl)->txSize = gFontSize;
  (*(*docStrucHdl)->editTextStrucHdl)->lineHeight = fontInfo.ascent + fontInfo.descent
                                                    + fontInfo.leading;

  SetRect(&destRect,pageRect.left + kMargin,pageRect.top + (kMargin * 1.5), 
          pageRect.right - kMargin,pageRect.bottom - (kMargin * 1.5));

  (*(*docStrucHdl)->editTextStrucHdl)->destRect = destRect;

  TECalText((*docStrucHdl)->editTextStrucHdl);

  heightDestRect = destRect.bottom - destRect.top;
  linesPerPage = heightDestRect / (*(*docStrucHdl)->editTextStrucHdl)->lineHeight; 
  numberOfPages = ((*(*docStrucHdl)->editTextStrucHdl)->nLines / linesPerPage) + 1;  

  return(numberOfPages);
}

// ******************************************************************************** doDrawPage

void  doDrawPage(WindowRef windowRef,Rect pageRect,SInt16 pageNumber,SInt16 numberOfpages)
{
  docStructureHdl docStrucHdl;
  TEHandle        docEditTextStrucHdl, pageEditTextStrucHdl;
  PicHandle       pictureHdl;
  Rect            destRect, pictureRect;
  SInt16          heightDestRect, linesPerPage, numberOfLines;
  Handle          textHdl;
  SInt32          startOffset, endOffset;
  Str255          theString;

  TextFont(gFontNumber);
  TextSize(gFontSize);

  docStrucHdl = (docStructureHdl) GetWRefCon(windowRef);  
  docEditTextStrucHdl  = (*docStrucHdl)->editTextStrucHdl;
  pictureHdl = (*docStrucHdl)->pictureHdl;

  destRect = (*docEditTextStrucHdl)->destRect;
  heightDestRect = destRect.bottom - destRect.top;
  linesPerPage = heightDestRect / (*docEditTextStrucHdl)->lineHeight; 
  numberOfLines = (*docEditTextStrucHdl)->nLines;

  startOffset = (*docEditTextStrucHdl)->lineStarts[(pageNumber - 1) * linesPerPage];
  if(pageNumber == numberOfpages)
    endOffset = (*docEditTextStrucHdl)->lineStarts[numberOfLines];
  else
    endOffset = (*docEditTextStrucHdl)->lineStarts[pageNumber * linesPerPage];

  pageEditTextStrucHdl = TENew(&destRect,&destRect);
  textHdl = (*docEditTextStrucHdl)->hText;
  HLock(textHdl);
  TEInsert(*textHdl + startOffset,endOffset - startOffset,pageEditTextStrucHdl);
  TEDispose(pageEditTextStrucHdl);

  if(pageNumber == 1)
  {
    SetRect(&pictureRect,destRect.left,destRect.top,
            destRect.left + ((*pictureHdl)->picFrame.right - (*pictureHdl)->picFrame.left),
            destRect.top + ((*pictureHdl)->picFrame.bottom - (*pictureHdl)->picFrame.top));
    DrawPicture(pictureHdl,&pictureRect);
  }

  MoveTo(destRect.left,pageRect.bottom - 25);
  NumToString((SInt32) pageNumber,theString);
  DrawString(theString);
}

// *************************************************************************** doDrawPrintInfo

void  doDrawPrintInfo(void)
{
  docStructureHdl docStrucHdl;
  OSStatus        osStatus = kPMNoError;
  PMRect          theRect;
  Str255          s2, s3;
  PMResolution    resolution;
  double          scale;
  PMOrientation   orientation;

  docStrucHdl = (docStructureHdl) GetWRefCon(gDocumentWindowRef);  
  if((*docStrucHdl)->pageFormat == kPMNoPageFormat)
    return;

  HLock((Handle) docStrucHdl);

  MoveTo(20,25);
  TextFace(bold);
  DrawString("\pFrom PMPageFormat Object:");
  TextFace(normal);

  PMGetAdjustedPaperRect((*docStrucHdl)->pageFormat,&theRect);
  NumToString((SInt32) theRect.top,s2);
  NumToString((SInt32) theRect.left,s3);
  doDrawRectStrings("\pPaper Rectangle (top,left):",20,45,s2,190,45,s3);
  NumToString((SInt32) theRect.bottom,s2);
  NumToString((SInt32) theRect.right,s3);
  doDrawRectStrings("\pPaper Rectangle (bottom,right):",20,60,s2,190,60,s3);

  PMGetAdjustedPageRect((*docStrucHdl)->pageFormat,&theRect);
  NumToString((SInt32) theRect.top,s2);
  NumToString((SInt32) theRect.left,s3);
  doDrawRectStrings("\pPage Rectangle (top,left):",20,75,s2,190,75,s3);
  NumToString((SInt32) theRect.bottom,s2);
  NumToString((SInt32) theRect.right,s3);
  doDrawRectStrings("\pPage Rectangle (bottom,right):",20,90,s2,190,90,s3);

  PMGetResolution((*docStrucHdl)->pageFormat,&resolution);
  MoveTo(20,105);
  DrawString("\pDrawing Resolution (Vertical):");
  NumToString((SInt32) resolution.vRes,s2);
  MoveTo(190,105);
  DrawString(s2);
  DrawString("\p dpi");
  MoveTo(20,120);
  DrawString("\pDrawing Resolution (Horizontal):");
  NumToString((SInt32) resolution.hRes,s2);
  MoveTo(190,120);
  DrawString(s2);
  DrawString("\p dpi");

  PMGetScale((*docStrucHdl)->pageFormat,&scale);
  MoveTo(20,135);
  DrawString("\pScale:");
  NumToString((SInt32) scale,s2);
  MoveTo(190,135);
  DrawString(s2);
  DrawString("\p%");

  PMGetOrientation((*docStrucHdl)->pageFormat,&orientation);
  MoveTo(20,150);
  DrawString("\pPage Orientation:");
  MoveTo(190,150);
  if(orientation == kPMPortrait)
    DrawString("\pPortrait");
  else if(orientation == kPMLandscape)
    DrawString("\pLandscape");

  if(gDrawPrintSettingsStuff)
  {
    MoveTo(20,170);
    TextFace(bold);
    DrawString("\pFrom PMPrintSettings Object:");
    TextFace(normal);

    MoveTo(20,190);
    DrawString("\pFirst Page:");
    NumToString((SInt32) gFirstPage,s2);
    MoveTo(190,190);
    DrawString(s2);

    MoveTo(20,205);
    DrawString("\pLast Page:");
    NumToString((SInt32) gLastPage,s2);
    MoveTo(190,205);
    DrawString(s2);

    MoveTo(20,220);
    DrawString("\pNumber of Copies:");
    NumToString((SInt32) gCopies,s2);
    MoveTo(190,220);
    DrawString(s2);
  }

  HUnlock((Handle) docStrucHdl);
}

// ************************************************************************* doDrawRectStrings

void  doDrawRectStrings(Str255 s1,SInt16 x1,SInt16 y1,Str255 s2,SInt16 x2,SInt16 y2,Str255 s3)
{
  MoveTo(x1,y1);
  DrawString(s1);
  MoveTo(x2,y2);
  DrawString("\p(");
  DrawString(s2);
  DrawString("\p,");
  DrawString(s3);
  DrawString("\p)");
}

// ****************************************************************************** doErrorAlert

void  doErrorAlert(OSStatus errorType)
{
  Str255 theString, errorString = "\pCarbon Printing Manager Error ";
  SInt16 itemHit;

  NumToString((SInt32) errorType,theString);
  doConcatPStrings(errorString,theString);

  StandardAlert(kAlertCautionAlert,errorString,NULL,NULL,&itemHit);
}

// ************************************************************************** doConcatPStrings

void  doConcatPStrings(Str255 targetString,Str255 appendString)
{
  SInt16  appendLength;

  appendLength = MIN(appendString[0],255 - targetString[0]);

  if(appendLength > 0)
  {
    BlockMoveData(appendString+1,targetString+targetString[0]+1,(SInt32) appendLength);
    targetString[0] += appendLength;
  }
}

// *******************************************************************************************
// PrintDialogAppend.c
// *******************************************************************************************

// .................................................................................. includes

#include "CarbonPrinting.h"

// .......................................................................... global variables

SInt32    gFirstAppendedItemNo;
PMItemUPP gOldItemEvaluateFunctionUPP;

extern PMDialog       gPMDialog;
extern PMItemUPP      gNewItemEvaluateFunctionUPP;
extern ModalFilterUPP gEventFilterUPP;
extern SInt16         gFontNumber;
extern SInt16         gFontSize;

// ******************************************************************** initialisationFunction

void  initialisationFunction(PMPrintSettings printSettings,PMDialog *pmDialog)
{
  OSStatus   osStatus = kPMNoError;
  DialogRef  dialogRef;
  Handle     ditlHdl;
  SInt16     numberOfExistingItems, numberOfMenuItems;
  MenuRef    menuRef;
  ControlRef controlRef;
  Str255     itemName;

  *pmDialog = gPMDialog;

  osStatus = PMGetDialogPtr(*pmDialog,&dialogRef);

  if(osStatus == kPMNoError)
  {
    // ........................................................................... append DITL

    ditlHdl = GetResource('DITL',rPrintDialogAppendDITL);
    numberOfExistingItems = CountDITL(dialogRef);
    AppendDITL(dialogRef,ditlHdl,appendDITLBottom);
    gFirstAppendedItemNo = numberOfExistingItems + 1;

    // .......... create font menu and attach to popup button, set current font to first item

    menuRef = NewMenu(mFont,NULL);
    CreateStandardFontMenu(menuRef,0,0,0,NULL);
    GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo,&controlRef);
    SetControlMinimum(controlRef,1);
    numberOfMenuItems = CountMenuItems(menuRef);
    SetControlMaximum(controlRef,numberOfMenuItems);
    SetControlData(controlRef,kControlEntireControl,kControlPopupButtonMenuRefTag,
                   sizeof(menuRef),&menuRef);
    GetMenuItemText(menuRef,1,itemName);
    GetFNum(itemName,&gFontNumber);

    // ......................... set second radio button to on state and set current font size

    GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo + 2,&controlRef);
    SetControlValue(controlRef,1);
    gFontSize = 12;

    // .......................................................... switch fractional widths off

    GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo + 4,&controlRef);
    SetControlValue(controlRef,0);
    SetFractEnable(false);
  }

  if(osStatus == kPMNoError)
    osStatus = PMGetItemProc(*pmDialog,&gOldItemEvaluateFunctionUPP);
  if(osStatus == kPMNoError)
    osStatus = PMSetItemProc(*pmDialog,gNewItemEvaluateFunctionUPP);

  if(osStatus == kPMNoError)
    PMSetModalFilterProc(*pmDialog,gEventFilterUPP);

  if(osStatus != kPMNoError)
    doErrorAlert(osStatus);
}

// ******************************************************************** itemEvaluationFunction

void  itemEvaluationFunction(DialogRef dialogRef,SInt16 itemHit)
{
  SInt16     localizedItemNo, controlValue;
  ControlRef controlRef;
  MenuRef    menuRef;
  Str255     itemName;
  
  localizedItemNo = itemHit - gFirstAppendedItemNo + 1;

  if(localizedItemNo > 0)
  {
    if(localizedItemNo == iPopupButton)
    {
      GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo,&controlRef);
      controlValue = GetControlValue(controlRef);
      GetControlData(controlRef,kControlEntireControl,kControlPopupButtonMenuHandleTag,
                      sizeof(menuRef),(Ptr) &menuRef,NULL);
      GetMenuItemText(menuRef,controlValue,itemName);
      GetFNum(itemName,&gFontNumber);
    }
    else if(localizedItemNo >= iRadioButton10pt && localizedItemNo <= iRadioButton14pt)
    {
      GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo + 1,&controlRef);
      SetControlValue(controlRef,0);
      GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo + 2,&controlRef);
      SetControlValue(controlRef,0);
      GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo + 3,&controlRef);
      SetControlValue(controlRef,0);

      GetDialogItemAsControl(dialogRef,itemHit,&controlRef);
      SetControlValue(controlRef,1);

      if(localizedItemNo == iRadioButton10pt)
        gFontSize = 10;
      else if(localizedItemNo == iRadioButton12pt)
        gFontSize = 12;
      else if(localizedItemNo == iRadioButton14pt)
        gFontSize = 14;
    }
    else if(localizedItemNo == iCheckboxFracWidths)
    {
      GetDialogItemAsControl(dialogRef,gFirstAppendedItemNo + 4,&controlRef);
      SetControlValue(controlRef,!GetControlValue(controlRef));
      SetFractEnable(GetControlValue(controlRef));
    }
  }
  else
  {
    InvokePMItemUPP(dialogRef,itemHit,gOldItemEvaluateFunctionUPP);
  }
}

// ******************************************************************************* eventFilter

Boolean  eventFilter(DialogRef dialogRef,EventRecord *eventStrucPtr,SInt16 *itemHit)
{
  Boolean handledEvent;
  GrafPtr oldPort;

  handledEvent = false;

  if((eventStrucPtr->what == updateEvt) && 
     ((WindowRef) eventStrucPtr->message != GetDialogWindow(dialogRef)))
  {
    doUpdate(eventStrucPtr);
  }
  else
  {
    GetPort(&oldPort);
    SetPortDialogPort(dialogRef);

    handledEvent = StdFilterProc(dialogRef,eventStrucPtr,itemHit);

    SetPort(oldPort);
  }

  return handledEvent;
}

// *******************************************************************************************

Demonstration Program CarbonPrinting Comments

When the program is run, the user should:

o Choose Page Setup... from the File menu, make changes in the Page Setup dialog, and observe the
  information, extracted from the PMPageFormat object, drawn in the window titled Some Printing
  Information.

o Choose Print... from the File menu, note the items added to the Print dialog, select the
  desired font, font size, and fractional widths setting using these items, and print the
  document.

The user should print the document several times using different page size, scaling, and
orientation settings in the Page Setup dialog (noting the changed information in the Printing
Settings window), and occasionally limiting the number of changes printed by changing the
start-page and end-page settings in the Print dialog.

The window in which the "document" is displayed is titled Simulated Document because, in this
demonstration, the document is not loaded from a file.  Rather, a document is simulated using
text from a 'TEXT' resource and a picture from a 'PICT' resource.  This approach is used in
order to keep that part of the source code not related to printing per se to a minimum.  For
the same reason, the (flattened) PMPageFormat object is not saved to the resource fork of a
document file in this demonstration.  (The demonstration program at Chapter 19 shows how to do
this.)

Printing.h

defines

Constants are established for the resource IDs of a 'PICT' and 'TEXT' resources used for the
printing demonstration, and for the 'DITL' resource containing the items to be appended to the
Print dialog.  Five constants are established for the item numbers of the items in the 'DITL'
resource.  kMargin is used to set the margins for the printed page.

typedefs

A handle to a structure of type docStructure will be stored in the Window object for the
Simulated Document window.  The fields of this structure will be assigned PMPrintSession,
PMPageFormat, and PMPrintSettings objects, together with handles to the 'TEXT' and 'PICT'
resources referred to above.

CarbonPrinting.c

Global Variables

gFontNumber will be assigned the current font number.  gFontSize will be assigned the current
font size.  Both of these values can be changed by the user via the items added to the Print
dialog.

gNewItemEvaluateFunctionUPP and gEventFilterUPP will be assigned a universal procedure pointer
to, respectively, the item evaluation function and event filter function for the customised
Print dialog.  gPMDialog will be assigned a pointer to a PMDialog object for the customised
Print dialog.

The last four variables are incidental to the demonstration, and will be used to store
information to be displayed in the second window (titled Some Printing Information) created by
the program.

main

After the Simulated Document window is created, its font is set to Geneva 10 point and the
global variables gFontNumber and gFontSize are set to reflect this.  A document structure is
then created and associated with the window.  The three fields of the document structure which
will be assigned Carbon Printing Manager objects are then initialised.

The Some Printing Information window is then created.

The call to doGetDocument displays the simulated document in the Simulated Document window.

Note that, in this program, error handling of all errors other than Carbon Printing Manager
errors is somewhat rudimentary.  The program simply exits.

doGetDocument

doGetDocument creates a monostyled TextEdit structure, assigns the handle to this structure to
the relevant field of the Simulated Document window's document structure, loads a 'TEXT'
resource, and inserts it into a TextEdit structure.  (The act of insertion causes the text to
be drawn on the screen.)  A 'PICT' resource is then loaded and its handle assigned to the
relevant field of the Simulated Document windowÕs document structure.  The call to
InvalWindowRect invalidates the content region, generating an update event which, as will be
seen, causes the picture to be drawn in the window.

TextEdit is not addressed until Chapter 21; however, to facilitate an understanding of the
TextEdit aspects of this program, it is sufficient at this stage to understand that a
monostyled TextEdit structure contains, amongst others, the following fields:

destRect    The destination rectangle into which text is drawn.  The bottom of the
            destination rectangle can extend to accommodate the end of the text.  In other
            words, you can think of the destination rectangle as bottomless.

viewRect    The rectangle within which text is actually displayed.

hText       A handle to the actual text.

txFont      The font number of the font to be used,

txSize      The size of the font in points.

lineHeight  The vertical spacing, in pixels, of the lines of text.

nLines      The total number of lines of text.

lineStarts  An array with a number of elements corresponding to the number of lines of text.
            Each element contains the offset of the first character in each line.

Note that the destination and view rectangles were passed in the call to TENew, these
rectangles having been defined in the preceding five lines of code.

doUpdateDocumentWindow

As will be seen, two of the functions directly related to printing operations will change the
values in the TextEdit structure's destRect, txFont, txSize, and lineHeight fields.  The middle
block of the doUpdateDocumentWindow code simply resets the values in these fields back to those
that existed after doGetDocument was called, and then calls TECalText to re-calculate the line
starts and TEUpdate to draw the text.

The second blocks saves the values in the TextEdit structure's destRect, txFont, txSize, and
lineHeight fields as they existed before the update event was received and the fourth block
restores these settings.  The last two lines redraw the picture in the window. 

doMenuChoice

If the user chose Page SetupÉ from the File menu, doPageSetupDialog is called to display the
Page Setup dialog, handle user interaction within the dialog, and record the settings made by
the user.  If an error is returned, doErrorAlert is called to present an alert advising of the
error number.

If the user chose PrintÉ from the File menu, doPrintSettingsDialog is to display the Print
dialog, handle user interaction within the dialog, and record the settings made by the user. 
If no error is returned, doPrinting is then called to perform the printing.  If doPrinting
returns an error, doErrorAlert is called to present an alert advising of the error number.

If doPrintSettingsDialog returns an error, doPrinting is not called and doErrorAlert is called
to present an alert advising of the error number.

doCreateOrValidatePageFormat

doCreateOrValidatePageFormat is the first of the major printing functions.  It is called, as
their first action, by doPageSetUpDialog and doPrintSettingsDialog Ñ that is, whenever the user
chooses Page SetupÉ or PrintÉ from the File menu.  The main purpose of this function is to
create a PMPageFormat object, assign default parameter values to that object, and associate the
object with the document's window or, if the object has previously been created, simply
validate the existing object.

The call to PMCreateSession creates a printing session object.

If a PMPageFormat object is not currently assigned to the pageFormat field of the window's
document structure, PMCreatePageFormat and PMSessionDefaultPageFormat are called to create a
PMPageFormat object and assign default parameters to it.  The object is then associated with
the document's window by assigning the object to the relevant field of the window's document
structure.

On the other hand, if a PMPageFormat object is currently assigned to the pageFormat field of
the window's document structure, PMSessionValidatePageFormat is called to validate the object
within the context of the current printing session.

If no errors occurred, the PMPrintSession object is assigned to the relevant field of the
document window's document structure, otherwise both the PMPrintSession and PMPageFormat
objects are released.

doPageSetUpDialog

doPageSetUpDialog is called when the user chooses Page SetupÉ from the File menu.  Its purpose
is to present the Page Setup dialog and modify the page setup information stored in the
PMPageFormat object associated with the document's window according to settings made by the
user within the dialog.

The call to doCreateOrValidatePageFormat ensures that PMPageFormat and PMPrintSession objects
have been created and assigned to the relevant field of the window's document structure.  The
call to PMSessionPageSetupDialog then presents the Page Setup dialog and handles all user
interaction within the dialog.  If the user clicks the OK push button to dismiss the dialog,
the PMPageFormat object will be updated to reflect any changed settings made by the user.  No
updating occurs if the user clicks the Cancel push button to dismiss the dialog.

When the dialog has been dismissed, PMRelease is called to release the PMPrintSession object.

doPrintSettingsDialog

doPrintSettingsDialog is called when the user chooses PrintÉ from the File menu.  Its main
purpose is to create a PMPrintSettings object and associate it with the document's window, and
present the Print dialog and modify the print settings information stored in the
PMPrintSettings object according to changed settings made by the user within the dialog.

The call to doCreateOrValidatePageFormat ensures that PMPageFormat and PMPrintSession objects
have been created and assigned to the relevant field of the window's document structure.  The
call to PMCreatePrintSettings then creates a PMPrintSettings object.

The call to PMSetJobNameCFString sets the print job name used by the printing system. 
PMSessionDefaultPrintSettings is then called to assign default parameter values (number of
copies, page range, etc.) to the PMPrintSettings object, following which the PMPrintSettings
object is assigned to the relevant field of the document window's document structure.

The next block executes only if the program is running on Mac OS X.  PMSetPageRange sets the
valid range of pages that can be printed to 1-32000.  (If the user enters values outside this
range in the Print dialog, the Carbon Printing Manager displays an "out-of-range" alert.)  The
next two calls set the default first and last page numbers to be drawn in the relevant edit
text fields in the Print dialog.  Note that, if kPMPrintAllPages is passed in PMSetLastPage's
last parameter, the All radio button in the Print dialog will be selected when the dialog is
displayed.

The Print dialog to be presented is to be customised.  Accordingly, the first three lines
inside the next if block create universal procedure pointers for the initialisation, item
evaluation, and event filter functions contained in the source code file PrintDialogAppend.c.

PMSessionPrintDialogInit is called to initialise a custom Print dialog.  On return, the global
variable gPMDialog contains a pointer to an initialised PMDialog object, ready for
customisation.  This pointer is assigned to a global variable because the
PMSessionPrintDialogMain function does not include a parameter for passing this PMDialog object
to the dialog initialization function.

PMSessionPrintDialogMain presents the customised Print dialog, the universal procedure pointer
to the initialisation function being passed in the fifth parameter.

If the user clicked the Print push button, when PMSessionPrintDialogMain returns, the
PMPrintSettings object will contain information on the settings displayed in the Print dialog
(except for the settings in the customised section of the dialog).

If an error occurred, or if the the user clicked the Cancel push button in the Print dialog,
the PMPrintSettings and PMPrintSession objects are released and disassociated from the window. 
In addition, within the function doMenuChoice, the call to the function doPrinting will not
occur.

doPrinting

doPrinting contains the printing loop.

After the current graphics port is saved for later restoration, PMGetFirstPage and
PMGetLastPage are called to get the first and last page to print, as set in the Print dialog.

The next block is incidental to the demonstration, simply storing the first page, last page,
and number of copies information retrieved from the PMPrintSettings object in global variables
so that they can later be drawn in the Some Printing Information window.

PMGetAdjustedPageRect is then called to get the page rectangle, taking account of page
orientation setting, scale setting, and drawing resolution.  The double values in the fields of
this PMRect structure are assigned to the (SInt16) fields of a normal Rect structure before
that rectangle is passed to a function which calculates the actual number of pages in the
document.

If the calculated actual number of pages is less than the value returned by the call to
PMGetLastPage, the value in the variable lastPage is changed to the calculated number of pages.

For Mac OS X only, PMSetFirstPage and PMSetLastPage are called to set the first and actual last
page in the PMPrintSettings object.  This ensures that the page number information displayed in
the progress dialog invoked during printing will be correct.

The printing loop is now entered.  The first action is to call PMSessionBeginDocument to
establish a graphics context (PMPrintContext object) for imaging the document.  As will be
seen, a function exists to obtain the graphics port (that is, the printing port) associated
with this object.

The while loop spools all of the pages in the document, relying on the Carbon Printing Manager
to print the correct page range.  Within the loop:

o GetPort is called to save the current graphics port.

o PMSessionBeginPage is called to initialise the printing port.  (Note that, for Mac OS 8/9,
  this function may also be used to initialise a scaling rectangle for drawing the page, in 
  which case the address of a PMRect passed in the last parameter will be passed to the printer
  driver.)

o PMSessionGetGraphicsContext is called to obtain the current printing port, and SetPort is
  called to set this port as the current port.

o doDrawPage is called to draw the specified page in the current printing port.

o SetPort is called to restore the saved graphics port.

o PMSessionEndPage is called to print the page.

After the pages have been spooled, PMSessionEndDocument is called to close the printing port. 
On Mac OS X, this call also dismisses the progress dialog.

Note that the printing loop does not have to concern itself with the number of copies, since
this is handled automatically by the Carbon Printing Manager.

Finally, the PMPrintSettings and PMPrintSession objects are released and disassociated from the
document's window, and the graphics port saved at function entry is restored.

doCalcNumberOfPages

doCalcNumberOfPages is called by doPrinting to calculate the actual number of pages in the
document based on the page rectangle passed to it.

The first line retrieves the handle to the specified window's document structure.  The next two
lines set the current font and font size to the font number and size set by the user in the
appended items in the Print dialog.  This allows the call to GetFontInfo to retrieve some
relevant information about the font.

The next four lines change the values in the txFont, txSize, and lineHeight fields of the
TextEdit structure whose handle is stored in the window's document structure.  (Note that
information obtained by the GetFontInfo call is used to calculate line height.)

The next three lines change the rectangle stored in the destRect field of the TextEdit
structure to one equal to the received page rectangle less 180 pixels in width and 270 pixels
in height.  (This smaller rectangle is centred on the page rectangle both laterally and
vertically.)

With these changes made, TECalText is called to recalculate the line starts.  In addition to
changing the values in the lineStarts array in the TextEdit structure, this call will assign
the new total number of lines to the nLines field.

The matter of the actual calculation of the number of pages now follows.  The first line in the
last block gets the height of the previously defined destination rectangle.  The next line
calculates how many lines of text will fit into that height.  The third line then calculates
the total number of rectangles (and thus the number of pages) required to accommodate the whole
of the text.

doDrawPage

doDrawPage is called by doPrinting to draw a specified page in the printing graphics port.  

The first action is to set the printing graphics port's font and and font size to the font
number and size set by the user in the appended items in the Print dialog.

The next block retrieves a handle to the specified window's document structure, allowing
handles to the TextEdit structure and picture to be retrieved.

In the next block, the destination rectangle in the destRect field of the TextEdit structure is
assigned to a local variable, the height of this rectangle is assigned to a local variable, the
lines per page is calculated and assigned to a local variable, and the total number of lines is
assigned to a local variable.

In the next block, the first line gets the starting offset, that is, the offset from the first
character in the block of text to the first character in the first line of text for the
specified page number.  The next four lines get the ending offset, that is, the offset to the
last character in the last line of text for the specified page.  

The call to TENew creates a new monostyled TextEdit structure with the previously defined
destination rectangle passed in both the destination and view rectangle parameters.  The
following line gets a handle to the actual text in the TextEdit structure.  This handle is then
locked preparatory to a call to TEInsert.  Using the offsets previously calculated, TEInsert
then inserts the text for the current page into the newly created TextEdit structure, an action
which causes that text to be drawn in the printing graphics port.  The text having been drawn,
the TextEdit structure is then disposed of.

If this is the first page, the next block draws the previously loaded picture at the top left
of the previously defined rectangle.

The last three lines draw the page number at the bottom left of the original page rectangle.

doDrawPrintInfo and doDrawRectStrings

doDrawPrintInfo is called when an update event is received for the Some Printing Information
window.  Carbon Printing Manager accessor functions are then used to obtain information from
the PMPageFormat object associated with the document's window and draw that information in the
window.  In addition, information retrieved from the PMPrintSettings, and saved to global
variables within the function doPrinting, is drawn in the window if the global variable
doDrawPrintSettingsStuff is set to true.

doErrorAlert

doErrorAlert is called when the printing functions return an error other than the "user
cancelled" error.  An alert showing the error code is displayed.

PrintDialogAppend.c

initialisationFunction

Recall that, in the function doPrintSettingsDialog, a universal procedure pointer to
initialisationFunction was passed in the myInitProc parameter of the PMSessionPrintDialogMain
call.  PMSessionPrintDialogMain thus calls this function.

Recall also that the pointer to the initialised PMDialog object was assigned to a global
variable (gPMDialog) because the PMSessionPrintDialogMain function does not include a parameter
for passing a PMDialog object to a dialog initialisation function.  At the first line, the
pointer to the PMDialog object is copied to the initialisation function's formal parameter
pmDialog.

The call to PMGetDialogPtr gets a reference to the Print dialog dialog object.

The DITL to be appended to the dialog contains the following items:

o A pop-up menu for font selection.

o Three radio buttons for font size selection.

o A checkbox for selecting fractional widths on or off.

o A primary group box (text title variant).

GetResource loads the specified 'DITL' resource and gets a handle to it.  CountDITL counts the
current number of items in the Print dialog.  AppendDITL then appends the new items to the
dialog.  For some printers on Mac OS 8/9, this causes the dialog to expand downwards to
accommodate the added items.  For others (for, example, the LaserWriter 8), and on Mac OS X,
the result of the AppendDITL call is that a pane is created for the items and the name of the
application is inserted into the menu of a pop-up group box.  When the item containing the
applicationÕs name is chosen from the pop-up menu, the pane is displayed and the appended items
are accessible.

The global variable gFirstAppendedItemNo is then assigned the item number in the new Print
dialog item list of the first appended item (the pop-up menu button).  This will be required by
the function itemEvaluationFunction.

The next block calls CreateStandardFontMenu to create the menu for the pop-up menu button,
which is then assigned to the pop-up menu button by SetControlData.  Note that the font number
for the first item in the menu is assigned to the global variable gFontNumber.

The next block selects the second radio (12pt) button and sets the global variable gFontSize to
12.  The next block unchecks the checkbox and sets fractional widths to off.

The printer driver's item evaluation function will be called upon by the item evaluation
function (itemEvaluationFunction) to handle mouse-downs in the Print dialog's standard items. 
Accordingly, PMGetItemProc is called to assign the universal procedure pointer to the driver's
evaluation function to a global variable for later use.  The call to PMSetItemProc makes
itemEvaluationFunction the current item evaluation function.

Finally, the call to PMSetModalFilterProc makes the application-defined (callback) function
eventFilter the event filter function for the Print dialog.

itemEvaluationFunction

itemEvaluationFunction handles item hits in the Print dialog.  The item number of the item hit
is received in the second parameter.

At the first line, the item number of the item hit is "localised".  This means that, for
example, the localised item number of the pop-up menu button will be 1.  In other words, if the
localised item number is greater than 0, it will be the item number of one of the appended
items; otherwise, it will be the item number of one of the Print dialog's standard items.

If the localised item number is greater than 0, and if it is the localised item number for the
pop-up menu button, the control's value (that is, the menu item number) is retrieved.
GetMenuItemText is called to get the text of the menu item, and GetFNum is called to get the
font number for this font and assign it to the relevant global variable.

If the localised item number is the localised item number for one of the radio buttons, all
radio buttons are unchecked, the radio button hit is checked, and the global variable which
holds the current text size is assigned the appropriate value.  If the localised item number is
the localised item number for the checkbox, the current value of that control is flipped and
SetFractEnable is called to set fractional widths on or off as appropriate.

If the localised item number is 0 or less, the item must be one of the Print dialog's standard
items.  In this case, the printer driver's item evaluation function is called upon to handle
the item hit.

eventFilter

eventFilter is identical to the custom event filter for modal dialogs introduced at Chapter 8. 
The use of a the event filter is optional.  Its use in this program simply allows the Print
dialog, together with windows belonging to background applications, to receive update events
(required only on Mac OS 8/9).

The Page Setup and Print dialogs in this demonstration program are application-modal.  This is
because the AppendDITL method used to customise the Print dialog prevents that dialog from
being created as a window-modal (sheet) dialog.  The basic modifications required to cause the
dialogs to be window-modal are as follows:

o Display the Print dialog using the function PMSessionPrintDialog and eliminate all code
  relating to dialog customisation.

o Immediately before the calls to PMSessionPageSetupDialog and PMSessionPrintDialog, call the
  function PMSessionUseSheets, passing the parent window's reference in the documentWindow
  parameter and a universal procedure pointer to an application-defined (callback) function in
  the sheetDoneProc parameter.

o Add two application-defined (callback) functions which perform the actions required
  immediately following the dismissal of the dialogs, and modify the existing post-dismissal
  code accordingly.
 
AAPL
$118.93
Apple Inc.
-0.07
MSFT
$47.81
Microsoft Corpora
+0.06
GOOG
$541.83
Google Inc.
+1.46

MacTech Search:
Community Search:

Software Updates via MacUpdate

Adobe Photoshop Elements 13.0 - Consumer...
Adobe Photoshop Elements 12--the #1 selling consumer photo editing software--helps you edit pictures with powerful, easy-to-use options and share them via print, the web, Facebook, and more.Version... Read more
Skype 7.2.0.412 - Voice-over-internet ph...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more

Latest Forum Discussions

See All

Mystery Case Files: Dire Grove, Sacred G...
Mystery Case Files: Dire Grove, Sacred Grove HD Review By Jennifer Allen on November 28th, 2014 Our Rating: iPad Only App - Designed for the iPad A decent new installment for the popular Mystery Case Files series.   | Read more »
Castaway Paradise – Tips, Tricks, and St...
Ahoy there, castaways: Were you curious about our own thoughts regarding this pristine shipwreck? Check out our Castaway Paradise review! Castaway Paradise is out for iOS, finally giving mobile gamers the opportunity to enjoy the idyllic lifestyle... | Read more »
Castaway Paradise VIP Subs are on Sale f...
Castaway Paradise VIP Subs are on Sale for a Limited Time, and a Special Holiday Update is Coming Soon Posted by Rob Rich on November 28th, 2014 [ | Read more »
Primitive Review
Primitive Review By Jordan Minor on November 28th, 2014 Our Rating: :: FOLK ARTUniversal App - Designed for iPhone and iPad True to its name, Primitive is about as straightforward as runners get.   | Read more »
7 tips to get ahead of the competition i...
7 tips to get ahead of the competition in Dynasty of Dungeons Posted by Simon Reed on November 28th, 2014 [ permalink ] Playcrab has launched their action-packed new dungeon crawler, Dynasty of Dungeons, today. | Read more »
Master of Tea Kung Fu Review
Master of Tea Kung Fu Review By Jordan Minor on November 28th, 2014 Our Rating: :: ONE DROP RULESUniversal App - Designed for iPhone and iPad Master of Tea Kung Fu is a creative and complex caffeinated brawler.   | Read more »
Monster Strike Review
Monster Strike Review By Campbell Bird on November 28th, 2014 Our Rating: :: BILLIARD STRATEGYUniversal App - Designed for iPhone and iPad Collect monsters and battle by flinging them across the battlefield in this strangely... | Read more »
Proun+ Review
Proun+ Review By Jennifer Allen on November 28th, 2014 Our Rating: :: TWITCHY RACINGUniversal App - Designed for iPhone and iPad Twitchy racing aplenty in Proun+, an enjoyably tricky title.   | Read more »
Lucha Amigos (Games)
Lucha Amigos 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Forget Ninja Turtles, and meet Wrestlers Turtles! Crazier, Spicier and…Bouncier! Sling carapaces of 7 Luchadores to knock all... | Read more »
Record of Agarest War Zero (Games)
Record of Agarest War Zero 1.0 Device: iOS Universal Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: HyperDevbox Holiday Turkey Black Friday Special Pricing! To celebrate the opening of the holiday season HyperDevbox... | Read more »

Price Scanner via MacPrices.net

Best Black Friday Deal: 15-inch Retina MacBoo...
 B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for $300 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina... Read more
Up To 75% Off Infovole Text Apps Over Black F...
Infovole’s entire range of apps, including the Textkraft family of word processors for iPads and iPhones, is being offered at 50-75% off over the Black Friday and Cyber Monday weekend. The five-day... Read more
Black Friday: Up to $60 off Mac minis, NY tax...
 B&H Photo has new 2014 Mac minis on sale for up to $60 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $449.99 $50 off... Read more
Black Friday: 27-inch 5K iMac for $2299, save...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Karalux Announces 24K Gold-Plated iPhone 6
Karalux, a Vietnam-based jewellery firm, has launched a unique 24 karat gold-plated iPhone 6 version with gold-cast monolithic dragon on its back panel. The real 24 karat gold plated enclosure doesn’... Read more
Black Friday: 13-inch 2.6GHz Retina MacBook P...
 B&H Photo has lowered their price for the 13″ 2.6GHz/128GB Retina MacBook Pro to $1159 for Black Friday. That’s $140 off MSRP, and it’s the lowest price for this model (except for Apple’s $1099... Read more
View all the Black Friday sales on our Mac Pr...
We’ve updated our Mac Price Trackers with the latest information on prices, bundles, and availability on systems from Apple’s authorized internet/catalog resellers. View Black Friday sale prices at a... Read more
Black Friday: 11-inch MacBook Air for $779, s...
 Best Buy has lowered their price for the 2014 11″ 1.4GHz/128GB MacBook Air to $779.99 for Black Friday. That’s $120 off MSRP. Choose free shipping or free local store pickup (if available). Sale... Read more
Apple Store Black Friday sale for 2014: $100...
BLACK FRIDAY The Apple Store has posted their Black Friday deals for 2014. Receive a $100 PRODUCT(RED) branded iTunes gift card with the purchase of select Macs, $50 with iPads, and $25 with iPods,... Read more
Black Friday: 15% off iTunes Gift Cards
Staples is offering 15% off $50 and $100 iTunes Gift Cards on their online store as part of their Black Friday sale. Click here for more information. Shipping is free. Best Buy is offering $100... 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
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the 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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.