VwrFrameWork.c

/*
 * This is a viewer application shell
 *
 * Nick Thompson, nickt@apple.com
 * send bug reports to devsupport@apple.com
 *
 * ©1995-7 Apple Computer Inc., All Rights Reserved.
 *
 * This is a sample app that was originally written for an article in Develop
 * issue 29, but please note that it is really extended from the example 
 * presented in that article.  
 *
 * I've extended it to be the "reference" app for supporting plug-in 
 * renderers.  Along the way it became apparent that just sticking all the renderers
 * into a popup menu, simply did not cut the mustard.  Here's why.  There is a 
 * distinction between interactive (for example the apple interactive renderer)
 * and non interactive renderers (such as the lightwork renderer).  Bear in mind
 * that the interactivity of a renderer is often a tradeoff against speed.  In particular, 
 * it makes no sense whatsoever to support interactivity with a non interactive
 * renderer.
 *
 * This led me to the big change in the application.  I decided that if the 
 * renderer was non-interactive, then it should draw into a new window, which
 * the user could save as a pict, print, or whatever.  Bear in mind that this is
 * not the only way to do this.  Another simpler way would be to let the user
 * chose a non interactive renderer, and just render one into the window, then
 * switch back to the non interactive renderer.  I saw a couple of problems with
 * this approach for this particular application, in particular that if the 
 * window received an update event (out of the users control) then the window 
 * or worse - part of the window - would get redrawn with the "wrong" renderer.
 *
 * This remains a work in progress.  As sample code it is useful as an 
 * illustration of how to use the viewer, and an illustration of how to use
 * plug-in renderers.
 *
 * As usual this is a work in progress, which means use the code at your own risk!!  
 *
 * If you fix a bug, or add some functionality feel free to send me the change and
 * I'll roll it in.
 *
 * 
 *
 * TO DO: drag and drop support for pict windows
 *        saving and restoring print records to files.
 *
 * 2/4/99 nick updated to universal headers 3.2 and CW4.
 */
 
 
/*------------------------------------------------------------ */
 
#include <AppleEvents.h>
#include <CodeFragments.h>
#include <ColorPicker.h>
#include <Devices.h>
#include <Dialogs.h>
#include <Diskinit.h>
#include <Events.h>
#include <Errors.h>
#include <Fonts.h>
#include <Gestalt.h>
#include <LowMem.h>
#include <Printing.h>
#include <QDOffscreen.h>
#include <QuickDraw.h>
#include <Menus.h>
#include <Scrap.h>
#include <SegLoad.h>
#include <StandardFile.h>
#include <TextUtils.h>
#include <Strings.h>
#include <Sound.h>
#include <Windows.h>
 
// is alpha macros etc...
#include <ctype.h>
 
/* always include this before anything else when using the QuickDraw 3D library  */
#include "QD3D.h"
 
/* viewer support  */
#include "QD3DViewer.h"
 
/* this has the routines for renderer objects  */
#include "QD3DRenderer.h"
 
/* this has the routines for view objects  */
#include "QD3DView.h"
 
 
/*------------------------------------------------------------ */
 
/* convenience macros to peek at event records  */
 
#define     HiWrd(aLong)            (((aLong) >> 16) & 0xFFFF)
#define     LoWrd(aLong)            ((aLong) & 0xFFFF)
 
#define     kMaxRendererCount       30
#define     RETURN_KEY              0x0D
#define     ENTER_KEY               0x03
#define     ESCAPE                  0x1B
 
#define     kProgressBarDlogID      1028
#define     kProgressBarPBItemID    2
 
 
/*------------------------------------------------------------ */
 
/* constants  */
const   short               kWindHeight = 350 ;         /* default theWindow height  */
const   short               kWindWidth = 300 ;          /* default theWindow width  */
const   short               kMaxHeight = 400 ;          /* max theWindow height  */
const   short               kMaxWidth = 560 ;           /* max theWindow width  */
const   SFTypeList          kTypeList = { '3DMF' } ;    /* file type we can open  */
const   short               kNumTypes = 1 ;             /* num of file types we can recognise  */
 
const   short               kAppMenuBarID = 128 ;       /* resource ID for the application's menu bar  */
const   short               kFatalAlert = 128 ;         /* res ID for error alert before we bail  */
const   short               kErrorAlertStrings = 128 ;  /* res ID for alert strs  */
const   short               kAboutDialogID = 129;       /* ID for the about box dialog  */
const   short               kFinalRenderDialogID = 130; /* ID for the final quality render dialog  */
 
const   short               kInsetPixelsConst = 30 ;    /* num of pixels to inset view by  */
/* error codes  */
enum {
    kNoMenuBar = 1,
    kNoQD3DViewerLib,
    kNoAEVTSupport
} ;
 
/* menu resource IDs  */
enum {
    mInteractiveRendererMenu = 1,   /* hierarchical, stored in the view menu  */
    mNonInteractiveRendererMenu ,   /* hierarchical, stored in final dialog  */
    mAppleMenu = 128,
    mFileMenu,
    mEditMenu,
    mViewMenu
} ;
 
/* item numbers - Apple menu  */
enum {
    iAppleAboutItem = 1
} ;
 
/* item numbers - File menu  */
enum {
    iFileNewItem = 1,
    iFileOpenItem,
    iFileCloseItem = 4,
    iFileSaveItem,
    iFileSaveAsItem,
    iFileRevertItem,
    iFilePageSetupItem = 9,
    iFilePrintItem,
    iFileQuitItem = 12
} ;
 
/* item numbers - Edit menu  */
enum {
    iEditUndoItem = 1,
    iEditCutItem = 3,
    iEditCopyItem,
    iEditPasteItem,
    iEditClearItem = 7,
    iEditRendererPrefsItem = 9
} ;
 
/* item numbers - View menu  */
enum {
    iViewRendererItem = 1,
    iViewFinalRendererItem,
    iViewBadgeItem = 4,
    iViewCameraButtonItem = 6,
    iViewTruckButtonItem,
    iViewOrbitButtonItem,
    iViewZoomButtonItem,
    iViewDollyButtonItem,
    iViewInsetNFrameItem = 12,
    iViewSetBackgroundColorItem
} ;
 
/* items in the final quality render menu */
enum {
    kFinalRendrOK = 1,
    kFinalRendrCancel,
    kFinalRendrIcon,
    kFinalRendrTitle,
    kFinalRendrText,
    kFinalRendrSep1,
    kFinalRendrSep2,
    kFinalRendrSep3,
    kFinalRendrPopup,
    kFinalRendrConfigure,
    kFinalRendrWinSzTxt,
    kFinalRendrHtTxt,
    kFinalRendrWiTxt,
    kFinalRendrHeight,
    kFinalRendrWidth
} ;
 
 
/* res ID's for small picts used in the progress bar dialog */
enum {
    kResIDStartCap = 1028,
    kResIDEndCap,
    kResIDDropShadow
} ;
 
 
/*------------------------------------------------------------ */
 
/*
 * type definitions - these magic numbers are the first field of the struct
 * that we stuff in the window's refcon field
 */
 
/*
 * as this app grow's I'm in the process of factoring it, here's a list
 * of functions that can be stuffed in the document record 
 */
typedef void        (*AdjustMenusProc)( WindowPtr  theWindow )  ;
typedef Boolean     (*HandleEventProc)( WindowPtr theWindow, const EventRecord *theEventRecord) ;
typedef void        (*UpdateContentProc)( WindowPtr  theWindow ) ;
typedef WindowPtr   (*NewProc)( unsigned char *windowTitle ) ;
typedef OSErr       (*SaveAsProc)( WindowPtr theWindow ) ;
typedef OSErr       (*SaveProc)( WindowPtr theWindow ) ;
typedef OSErr       (*RevertProc)( WindowPtr theWindow ) ;
typedef WindowPtr   (*OpenProc)( FSSpec *theFSSpec ) ;
typedef OSErr       (*CloseProc)( WindowPtr theWindow ) ;
typedef short       (*CountPagesProc)( WindowPtr theWindow, Rect *pageRect ) ;
typedef void        (*PrintPageProc)( WindowPtr theWindow, Rect *pageRect, GrafPtr imagingPort, short pageNum ) ;
typedef void        (*PrePrintProc)( WindowPtr  theWindow ) ;
typedef void        (*PostPrintProc)( WindowPtr  theWindow )    ;
typedef OSErr       (*CutProc)( WindowPtr   theWindow ) ;
typedef OSErr       (*CopyProc)( WindowPtr  theWindow ) ;
typedef OSErr       (*PasteProc)( WindowPtr theWindow ) ;
typedef OSErr       (*ClearProc)( WindowPtr theWindow ) ;
typedef OSErr       (*UndoProc)( WindowPtr  theWindow ) ;
 
/*
 * This struct is used to store related items for a progress bar,
 * these cached items can then be used when updating the PB
 */
typedef struct {
    DialogPtr       thePBDialog ;       /* the dialog containing a 14 pixel high user item  *
                                         * within which the PB is drawn                     */
    short           thePBItemNumber ;   /* item number in the DITL for the PB user item */
    Rect            thePBRect ;         /* it's rect, local coodinates in the dialog */
    
    GWorldPtr       thePBGWorld ;       /* a GWorld that is used to draw the pb, it's then blitted to the dialog */
    
    long            thePBMaxValue ;     /* the maximum value the PB can be */
    long            thePBCurrValue ;    /* the current value for the progress bar */
    
} ProgressBarDlogInfoData, **ProgressBarDlogInfoHdl ;
 
 
 
/*
 * this is a struct that contains the fp's associated with a document class
 * later we'll use this to set up menus.  If a fp is null then the menu is 
 * disabled, this will really reduce the size of the adjust menus proc.
 *
 * Each document record will have this as the second field.
 */
 
typedef struct Procs {
    AdjustMenusProc     adjustMenusP ;      /* fix up window specific menus */
    UpdateContentProc   updateWindowP ;     /* redraw the window */
    HandleEventProc     handleEventP ;      /* handle events for the window */
    NewProc             newP ;              /* do window specific stuff post create (unused at the moment) */
    SaveAsProc          saveAsP ;           /* save the window contens ask for a file */
    SaveProc            saveP ;             /* save the window contents to the original file */
    RevertProc          revertP ;           /* revert to the last saved copy of the windows file */
    OpenProc            openP ;             /* read in some document data */
    CloseProc           closeP ;            /* close and destroy */
    CountPagesProc      countPagesP ;       /* count the number of pages for a print job */
    PrintPageProc       printPageP ;        /* print one page */
    PrePrintProc        prePrintP ;         /* stuff to do before printing */
    PostPrintProc       postPrintP ;        /* stuff to do after printing */
    CutProc             cutProc ;           /* handle clipboard cut, same as copy followed by clear */
    CopyProc            copyProc ;          /* handle clipboard copy */
    PasteProc           pasteProc ;         /* paste compatible scrap type from the scrap */
    ClearProc           clearProc ;         /* clear the document contents without a save to the scrap */
    UndoProc            undoProc ;          /* undo the last action - if supported */
} Procs ;
 
 
/*
 * eventually to make a new doc, we'll just define the structure of the private 
 * field for each class.  Right now they are kind of copies which can be cast around.
 *
 * Make sure that the fields are in the same order, again this is something I'm 
 * planning to fix.
 */
typedef struct {
    unsigned long       fDocumentMagic ;    /* use this to describe the contents of the fPrivate field */
    Procs               *procs ;            /* a pointer to a set of function pointers for this window type */
    THPrint             fPrintRec ;         /* print record so we can print the doc contents */
    void                *fPrivate ;         /* the private data associated with this class */
    
} Document, *DocumentPtr, **DocumentHdl ;
 
/* these structures get stuffed in the private field of a generic document record */
typedef struct {
    TQ3ViewerObject     fViewer ;       /* stores reference to the viewer object  */
    GWorldPtr           fGWorld ;       /* used temporarily during printing */
    FSSpec              fFSSpec ;       /* reference to the file for the document  */
} ViewerData, *ViewerDataPtr, **ViewerDataHdl ;
 
 
typedef struct {
    GWorldPtr           fGWorld ;       /* a buffer that stores the picture */
} PictData, *PictDataPtr, **PictDataHdl ;
 
 
/*
 * Structure for the private data for a popup control.
 * This structure is documented on page 5-77 
 * Inside Macintosh: Macintosh Toolbox Essentials
*/
typedef struct popupPrivateData {
    MenuHandle  mHandle;     /* the popup menu handle */
    short       mID;         /* the popup menu ID */
    /*
     * after these two public fields is the mPrivate private data, 
     * which may be any old size and should not be messed with 
     */
}   popupPrivateData;
 
/*------------------------------------------------------------ */
 
/* function prototypes for the application  */
 
void            SetUpRendererMenu( void ) ;
void            TearDownRendererMenu( void ) ;
 
void            LoadApplicationDialogs( void ) ;
void            LoadMenuBarForApplication( short myMenuBarID ) ;
void            FatalAlert( short theErrorMessage ) ;
void            InitializeToolBox( void ) ;
Boolean         HasQuickDraw3DViewer( void ) ;
Boolean         SupportsAEVT( void ) ;
void            RegisterRequiredAppleEventHandlers( void ) ;
 
/* routines to handle the renderer progress bar */
ProgressBarDlogInfoHdl PB_New( 
                DialogPtr   thePBDialog,
                short       thePBItemNumber,
                long        maxValue,
                long        currValue ) ;
 
void            PB_Update(ProgressBarDlogInfoHdl theInfo ) ;
void            PB_Delete(ProgressBarDlogInfoHdl theInfo ) ;
 
pascal OSErr    HandleCoreAppleEventOfTypeOAPP( AppleEvent *theAppleEvent, 
                                AppleEvent *theAppleEventReply, 
                                long userDefinedReferenceConstant) ;
                                
pascal OSErr    HandleCoreAppleEventOfTypeODOC( AppleEvent *theAppleEvent, 
                                AppleEvent *theAppleEventReply, 
                                long userDefinedReferenceConstant) ;
                                
pascal OSErr    HandleCoreAppleEventOfTypePDOC( AppleEvent *theAppleEvent, 
                                AppleEvent *theAppleEventReply,
                                long userDefinedReferenceConstant) ;
                                
pascal OSErr    HandleCoreAppleEventOfTypeQUIT( AppleEvent *theAppleEvent, 
                                AppleEvent *theAppleEventReply,
                                long userDefinedReferenceConstant) ; 
                                
void            MainEventLoop( void ) ; 
TQ3Boolean      HandleEvent( const EventRecord *theEventRecord ) ;
 
void            HandleKeyPress(const EventRecord *theEventRecord) ;
void            HandleMenuCommand(long menuResult) ;
void            HandleAppleMenu( short menuItem ) ;
void            HandleFileMenu( short menuItem ) ;
OSErr           HandleFilePageSetupItem( WindowPtr theWindow ) ;
OSErr           HandleFilePrintItem( WindowPtr theWindow ) ;
OSErr           HandleFileQuitItem( void ) ;
void            HandleEditMenu( short menuItem ) ;
pascal Boolean  OurFilter(DialogPtr dlg, EventRecord *event, short *itemHit) ;
void            HandleViewFinalRendererOption( void ) ;
void            HandleInteractiveRendererMenu( short menuItem ) ;
void            HandleViewMenu( short menuItem ) ;
OSErr           GetViewerVersion( unsigned long *major, unsigned long *minor ) ;
 
Boolean         BackgroundColor(RGBColor *theRGBColor, unsigned char *thePrompt ) ;
void            AdjustMenus( void ) ;
void            DoDrawGrowIcon(WindowPtr theWindow) ;
 
WindowPtr       DoCreateNewViewerWindow( unsigned char *windowName ) ;
 
void            ViewerWindow_Update( WindowPtr  theWindow ) ;
void            ViewerWindow_AdjustMenus( WindowPtr  theWindow ) ;
Boolean         ViewerWindow_HandleEvent( WindowPtr theWindow, const EventRecord *theEventRecord) ;
WindowPtr       ViewerWindow_New( unsigned char *windowTitle ) ;
OSErr           ViewerWindow_SaveAs( WindowPtr theWindow ) ;
OSErr           ViewerWindow_Save( WindowPtr theWindow ) ;
OSErr           ViewerWindow_Revert( WindowPtr theWindow ) ;
WindowPtr       ViewerWindow_Open( FSSpec *theFSSpec ) ;
OSErr           ViewerWindow_Close( WindowPtr theWindow ) ;
short           ViewerWindow_CountPages( WindowPtr theWindow, Rect *pageRect ) ;
void            ViewerWindow_PrintPage( WindowPtr theWindow, Rect *pageRect, GrafPtr imagingPort, short pageNum ) ;
void            ViewerWindow_PrePrint( WindowPtr theWindow ) ;
void            ViewerWindow_PostPrint( WindowPtr theWindow ) ;
OSErr           ViewerWindow_Cut( WindowPtr theWindow ) ;
OSErr           ViewerWindow_Copy( WindowPtr    theWindow ) ;
OSErr           ViewerWindow_Paste( WindowPtr   theWindow ) ;
OSErr           ViewerWindow_Clear( WindowPtr   theWindow ) ;
OSErr           ViewerWindow_Undo( WindowPtr    theWindow ) ;
 
WindowPtr       DoCreateNewPictWindow( unsigned char *windowName, long windWidth, long windHeight ) ;
void            PictWindow_Update( WindowPtr  theWindow ) ;
void            PictWindow_AdjustMenus( WindowPtr  theWindow ) ;
Boolean         PictWindow_HandleEvent( WindowPtr theWindow, const EventRecord *theEventRecord) ;
WindowPtr       PictWindow_New( unsigned char *windowTitle ) ;
OSErr           PictWindow_SaveAs( WindowPtr theWindow ) ;
OSErr           PictWindow_Save( WindowPtr theWindow ) ;
OSErr           PictWindow_Revert( WindowPtr theWindow ) ;
WindowPtr       PictWindow_Open( FSSpec *theFSSpec ) ;
OSErr           PictWindow_Close( WindowPtr theWindow ) ;
short           PictWindow_CountPages( WindowPtr theWindow, Rect *pageRect ) ;
void            PictWindow_PrintPage( WindowPtr theWindow, Rect *pageRect, GrafPtr imagingPort, short pageNum ) ;
void            PictWindow_PrePrint( WindowPtr theWindow ) ;
void            PictWindow_PostPrint( WindowPtr theWindow ) ;
OSErr           PictWindow_Copy( WindowPtr theWindow ) ;
 
//
 
OSErr SetupPrintHdl(THPrint *hPrint) ;
void DoCreatePrintRecord( DocumentHdl theDocument ) ;
OSErr DoStyleDlog(THPrint hPrint) ;
OSErr DoJobDlog(THPrint hPrint) ;
OSErr CommandPeriod() ;
pascal void IdleProc() ;
short GeneralCountPages( GWorldPtr theGWorld, Rect *pageRect ) ;
void GeneralPrintPage( GWorldPtr theGWorld, Rect *pageRect, GrafPtr imagingPort, short pageNum ) ;
OSErr DoPrintLoop( WindowPtr theWindow ) ;
 
 
 
TQ3ViewerObject GetViewerFromViewerWindow( WindowPtr theWindow ) ;
GWorldPtr GetGWorldFromPictWindow( WindowPtr  theWindow ) ;
TQ3RendererObject   GetRendererByType(TQ3ObjectType theRendererType) ;
TQ3Status   ViewerSetRenderer( TQ3ObjectType theRendererType, TQ3ViewerObject theViewer ) ;
TQ3Status MyViewIdleProgressMethod( 
    TQ3ViewObject       view,
    const void          *idlerData,     /* contains the dialog ref */
    unsigned long       current,
    unsigned long       completed) ;
OSErr GetOutputPictFileRef(short    *dstPictFRef ) ;
 
 
/*------------------------------------------------------------ */
 
/* global variables  */
DialogPtr           gFinalRenderDialog = NULL ; /* the non interactive renderer dial0g */
DialogPtr           gProgressModelessDialog = NULL ;
 
Boolean             gQuitFlag = false ; /* set to true to quit application  */
AEAddressDesc       gSelfAddress;       /* A self-addressed address descriptor record  */
 
/* these are used for the menus */
static TQ3ObjectType        pInteractiveTypes[ kMaxRendererCount ] ; /* renderer types installed - includes plug-ins */
static TQ3ObjectType        pNonInteractiveTypes[ kMaxRendererCount ] ; /* renderer types installed - includes plug-ins */
static long                 pNonInteractiveRendererCount = 0;   /* the number of non-interactive renderers installed on this system */
static long                 pInteractiveRendererCount = 0;      /* the number of interactive renderers installed on this system */
static Boolean              pHasFinalQualityRenderer = false ;  /* set true if one or more non interactive renderers are installed */
 
/* 
 * this is a list of the installed renderers that we can use to switch easily. 
 * By this point you are probably muttering that this app has way to many globals,
 * and you might be right, however keeping a list of renderer objects makes sense.
 * The most obvious reason to build a list of the available renderers, together
 * with an instance of each mish be efficiency considerations, but the real reason
 * is somewhat more subtle.  We want renderer prefs to be "sticky".  In other words
 * if I set prefs for a renderer I want those prefs to be the same for multiple
 * uses of the renderer.  Just setting the renderer is not enough, since each time 
 * a renderer object is created it is created with the default set of prefs which
 * may not be what I selected last time I bought the prefs dialog up.
 *
 * The way this structure is used is to build a list of renderer types and an 
 * instance of each when the application is launched, then this list is used
 * whenever renderer prefs need to be set or when a new renderer is selected.
 * This way the prefs will stay with the renderer for the life time of the program.
 *
 * This list contains both interactive and non interactive renderers, it has no
 * real order (first come first served) and is seached linearly by renderer type.
 *
 * In the future, it might be good to build a list of renderer prefs for each 
 * document and save these along with the document, or to have a global list of
 * prefs for the application..
 */
 
static struct {
    TQ3ObjectType       fRendererType ;     /* the object type for this renderer */
    TQ3RendererObject   fRendererObject ;   /* an instance of the renderer type */  
} pRendererList[ 2 * kMaxRendererCount ] ;
 
static short        pRendererCount = 0 ;        /* indexes the pRendererList array */
 
ModalFilterUPP      gModalFilterProcUPP ; 
 
/********************************************************************************
 * Colors used to draw the progress bar 
 */
 
/* 
 
I got these by dumping a scroll bar from the MacOS 8 finder and poking around in photoshop.
 
Color table, byte value (e.g. from photoshop) on LHS
 
8bit    16bit       16bit (Hex)
 
0,      0,          0x0000
51,     13107,      0x3333
102,    26214,      0x6666
136,    34952,      0x8888
153,    39321,      0x9999
170,    43690,      0xaaaa
187,    48059,      0xbbbb
204,    52428,      0xcccc
221,    56797,      0xdddd
255,    65535,      0xffff
 
*/
 
const RGBColor      RGBBlack    = { 0x0000, 0x0000, 0x0000 } ;
const RGBColor      RGBDkGrey   = { 0x8888, 0x8888, 0x8888 } ;
const RGBColor      RGBMedGrey  = { 0xaaaa, 0xaaaa, 0xaaaa } ;
const RGBColor      RGBGrey     = { 0xbbbb, 0xbbbb, 0xbbbb } ;
const RGBColor      RGBLtGrey   = { 0xdddd, 0xdddd, 0xdddd } ;
const RGBColor      RGBWhite    = { 0xffff, 0xffff, 0xffff } ;
 
/* blue shades used */
const RGBColor      RGBDkBlue   = { 0x3333, 0x3333, 0x6666 } ;
const RGBColor      RGBMedBlue  = { 0x6666, 0x6666, 0x9999 } ;
const RGBColor      RGBBlue     = { 0x9999, 0x9999, 0xcccc } ;
const RGBColor      RGBLtBlue   = { 0xcccc, 0xcccc, 0xffff } ;
 
 
static  GWorldPtr   theStartCap     = NULL;
static  GWorldPtr   theEndCap       = NULL ;
static  GWorldPtr   theDropShadow   = NULL ;
 
const unsigned long kViewerMagic    =   0xBADB00CA ;    /* viewer windows */
const unsigned long kPICTMagic      =   0xBADABADA ;    /* picture windows */
 
/* these MUST be in the same order as above or bad things happen.
 * function procs for the regular viewer window.  Setting these up
 * this way is a little dangerous, if the fields get jumbled up
 * unexpected results can happen, it might make more sense to use
 * a metahandler approach to install the methods - like QD3D
 * extensions...
 */
Procs viewerProcs = { 
    ViewerWindow_AdjustMenus, 
    ViewerWindow_Update, 
    ViewerWindow_HandleEvent,
    ViewerWindow_New, 
    ViewerWindow_SaveAs, 
    ViewerWindow_Save, 
    ViewerWindow_Revert, 
    ViewerWindow_Open, 
    ViewerWindow_Close,
    ViewerWindow_CountPages,
    ViewerWindow_PrintPage,
    ViewerWindow_PrePrint,
    ViewerWindow_PostPrint,
    ViewerWindow_Cut,
    ViewerWindow_Copy,
    ViewerWindow_Paste,
    ViewerWindow_Clear,
    ViewerWindow_Undo
    
};
 
/* function procs for the pict window */
Procs pictProcs = { 
    PictWindow_AdjustMenus, 
    PictWindow_Update, 
    PictWindow_HandleEvent,
    PictWindow_New, 
    PictWindow_SaveAs, 
    PictWindow_Save, 
    PictWindow_Revert, 
    PictWindow_Open, 
    PictWindow_Close,
    PictWindow_CountPages,
    PictWindow_PrintPage,
    NULL,                       /* pre print method is not supported */
    NULL,                       /* post print method is not supported */
    NULL,                       /* cut method is not supported */
    PictWindow_Copy,
    NULL,                       /* paste method is not supported */
    NULL,                       /* clear method is not supported */
    NULL                        /* undo method is not supported */
};
 
/*------------------------------------------------------------ */
 
 
/* REMOVE THIS */
 
#include <Types.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <Gestalt.h>
#include <Fonts.h>
#include <Events.h>
#include <Menus.h>
#include <Windows.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <OSUtils.h>
#include <ToolUtils.h>
#include <SegLoad.h>
#include <Notification.h>
#include <OSUtils.h>
 
    
/* Prototypes */
Boolean IsVMCurrentlyOn(void) ;
void notify(void) ;
 
static Str255       VMIsCurrentlyOnStr      = "\pVirtual memory is currently on" ;
static Str255       VMIsCurrentlyOffStr     = "\pVirtual memory is currently off" ;
 
static  NMRec       myNotification ;
void notify(void)
{
    OSErr theErr ;
 
    myNotification.qType = nmType ;
    myNotification.nmIcon = NULL ;
    myNotification.nmSound = NULL ;
    myNotification.nmResp = NULL ;
    myNotification.nmRefCon = 0L ;
    
    /* check Gestalt to see if VM is on */
    if( IsVMCurrentlyOn() )
    {
        myNotification.nmStr = VMIsCurrentlyOnStr ;
    
    }
    else
    {
        myNotification.nmStr = VMIsCurrentlyOffStr ;
    
    }
    
    theErr = NMInstall( &myNotification ) ;
    
}
 
/*
 * returns true if the VM is on
 */
Boolean IsVMCurrentlyOn(void)
{
    OSErr theError;
    long response;
        
    theError = Gestalt(gestaltVMAttr, &response);
    if (theError!=noErr)
        return false;
        
    return (response && (response << gestaltVMPresent));
}
 
 
/* END REMOVE THIS */
 
void main(void)
{
    InitializeToolBox() ;
    gModalFilterProcUPP = NewModalFilterProc( OurFilter ) ;
    LoadApplicationDialogs() ;
    LoadMenuBarForApplication( kAppMenuBarID ) ;
 
    /*
     * check that we have AppleEvents, and if so install the
     * AppleEvent handlers for the required AppleEvents
     */
    
    if( SupportsAEVT() ) 
    {
        RegisterRequiredAppleEventHandlers() ;
        
        /*
         * Check that the viewer is installed
         */
        if( HasQuickDraw3DViewer() )
        {
            Q3Initialize() ;
            SetUpRendererMenu() ;
 
            MainEventLoop() ;
            
            TearDownRendererMenu() ;
            Q3Exit() ;
        }
    }
 
    ExitToShell();  
    
}
 
/*------------------------------------------------------------ */
 
/*
 * Load the final quality renderer dialog.  We do this here so
 * that we can set up the menu properly.  Don't display it just 
 * yet though.
 */
void    LoadApplicationDialogs(void)
{
    /* 
     * load the dialog but don't show it.  As a side note since the 
     * dialog is going to be around for a while, the items in the dialog 
     * should not be purgable.
     */
    gFinalRenderDialog = GetNewDialog( kFinalRenderDialogID, NULL, (WindowPtr)-1) ;
    gProgressModelessDialog = GetNewDialog( kProgressBarDlogID, nil, (WindowPtr)-1) ;
    
    /* the popup menu for the renderers is item 9 */
}
 
/*------------------------------------------------------------ */
 
/*
 * Load the menu bar specified.
 */
 
void LoadMenuBarForApplication( short myMenuBarID )
{
    Handle      menuBar = NULL;
 
    /* 
     *Read menus into menu bar specified by the resource 
     * id passed into this routine by myMenuBarID 
     */
    menuBar = GetNewMBar(myMenuBarID);  
    
    if ( menuBar == NULL )
         ExitToShell();
         
    /* Install the menus we just read in  */             
    SetMenuBar(menuBar);
    
    /* it is now safe to dispose the menu handle  */
    DisposeHandle(menuBar);
    
    /* 
     * this next routine adds the names of desk accessories 
     * to the specified menu, in this case the apple menu
     */
    AppendResMenu( GetMenuHandle( mAppleMenu ), 'DRVR' );
    
    /*
     * now add the two hierarchical menus
     */
    InsertMenu(GetMenu( mInteractiveRendererMenu ), -1);
    
    /* call our routine to grey out menus as appropriate  */
    AdjustMenus() ;
    
    /* finally ensure the new menubar gets drawn  */
    DrawMenuBar();
}
/*------------------------------------------------------------ */
 
/*
 * dispose of the global rtenderer list
 */
void TearDownRendererMenu( void )
{
    long        theIndex ;
    
    for( theIndex = 0; theIndex < pRendererCount ; theIndex++ ) 
        Q3Object_Dispose( pRendererList[ theIndex ].fRendererObject ) ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * set up the interactive renderer menu and the non interactive 
 * renderer popup menu for the "final render" dialog.  Build up
 * a list of renderer objects in the pRendererList struct to make 
 * switching between renderers easier
 */
 
void SetUpRendererMenu( void ) 
{
    MenuHandle                  theMenu, interactiveRendererMenu, nonInteractiveRendererMenu ;
    TQ3SubClassData             subClassData;
    TQ3ObjectType               *classPointer;
    short                       i;
    TQ3ObjectClassNameString    objectClassName;
    TQ3RendererObject           tempRendererObject ;
    unsigned char               nameBuffer[256] ;
    unsigned long               actualLength ;
    TQ3ObjectClassNameString    objectClassString ;
    Boolean                     isInteractive ;
    popupPrivateData            **myPopupPrivateDataPtr ;
    
    /* some variables for accessing the fields of the dialog */
    short               iKind;
    Handle              iHandle;
    Rect                iRect;
        
    interactiveRendererMenu = GetMenuHandle(mInteractiveRendererMenu);
 
    /*
     * Set up the dialog renderer popup.  We know the item number for the popup
     * we need to get the menuhandle associated with the control, it is in the private
     * control data field, as documented in Inside Macintosh: Toolbox page 5-77
     */
    
    /* get the control handle for the popup from the dialog */  
    GetDialogItem ( gFinalRenderDialog, kFinalRendrPopup, &iKind, &iHandle, &iRect) ;
    
    /* extract from the control the menuhandle */
    myPopupPrivateDataPtr = (popupPrivateData **)(**(ControlHandle)iHandle).contrlData ; 
    nonInteractiveRendererMenu = (**myPopupPrivateDataPtr).mHandle ;
    HLock((Handle)nonInteractiveRendererMenu) ;
    
    /* the popup control has a problem under 7.5 and 7.6, since it expects the 
     * menu to alredy be constructed otherwise the default value for the control 
     * is 0, which means nothing is selected after the menu items have been added, 
     * so i put a dummy item in the menu, we need to delete this before adding 
     * any new items
     */
     
    DeleteMenuItem ( nonInteractiveRendererMenu, 1 ) ;
    Q3ObjectHierarchy_GetSubClassData(kQ3SharedTypeRenderer, &subClassData);
    
    classPointer = subClassData.classTypes;
    
    i = subClassData.numClasses;
    
    while( i-- > 0 && pInteractiveRendererCount <= kMaxRendererCount) {
        
        /* 
         * the "generic" renderer is used internally, it can't draw, 
         * so don't display it in any user interface item
         */
        if( *classPointer != kQ3RendererTypeGeneric )
        {
            /*
             * Q3RendererClass_GetNickNameString is only available on 1.5.1, so 
             * check for the existance of this trap before calling.  If the call 
             * is not installed just set the string to nil, we'll use the class name
             * instead and everthing will be hunky dory.
             */
            if( Q3RendererClass_GetNickNameString != (void *)kUnresolvedCFragSymbolAddress ) 
                Q3RendererClass_GetNickNameString(*classPointer, objectClassString );
            else
                objectClassString[0] = '\0' ;
                
            /*
             * Create an instance of a renderer object and then call 
             * Q3Renderer_IsInteractive to determine if the renderer is 
             * an interactive renderer (this is something the renderer 
             * tells QuickDraw 3D).  Stash the renderer and the type in
             * the pRendererList structure array.
             */
             
            tempRendererObject = Q3Renderer_NewFromType( *classPointer ) ;
            isInteractive = Q3Renderer_IsInteractive( tempRendererObject ) ;
            
            /* cache the renderer and type */
            pRendererList[ pRendererCount ].fRendererType = *classPointer ;
            pRendererList[ pRendererCount ].fRendererObject = tempRendererObject;
            pRendererCount++ ;      /* bump the count of elements in the array */
            
            if( isInteractive )
            {
                theMenu = interactiveRendererMenu ;
                pInteractiveTypes[pInteractiveRendererCount++] = *classPointer ;
            }
            else
            {
                theMenu = nonInteractiveRendererMenu ;
                pHasFinalQualityRenderer = true ;
                pNonInteractiveTypes[pNonInteractiveRendererCount++] = *classPointer ;
            }
            /*
             * check to see if we got a string back, if not use the renderer class name
             */
                
            if( objectClassString[0] == '\0' )
            {
                /* the renderer did not provide the name, just use the class name */
                Q3ObjectHierarchy_GetStringFromType(*classPointer, objectClassName);
                
                AppendMenu(theMenu,c2pstr((char *)objectClassName));
            }
            else
            {
                AppendMenu(theMenu,c2pstr(objectClassString));
            }
                
        }
        
        classPointer++ ;        
    }
    
    HLock((Handle)nonInteractiveRendererMenu) ;
    
    /* null terminate the arrays */
    pInteractiveTypes[pInteractiveRendererCount] = NULL ;
    pNonInteractiveTypes[pInteractiveRendererCount] = NULL ;
    pRendererList[ pRendererCount ].fRendererType = 0L ;
    pRendererList[ pRendererCount ].fRendererObject = NULL;
    
    Q3ObjectHierarchy_EmptySubClassData( &subClassData ) ;
}
 
/*------------------------------------------------------------ */
 
/*
 * Display an alert and quit.
 */
 
void FatalAlert( short theErrorMessage )
{
    unsigned char   *thePString, theErrorStr[256] ;
    
    thePString = theErrorStr ;
    
    GetIndString( thePString, kErrorAlertStrings, theErrorMessage ) ;
    ParamText( thePString, "\p", "\p", "\p" ) ;
    StopAlert( kFatalAlert, NULL ) ;
    ExitToShell() ;
}
 
/*------------------------------------------------------------ */
 
/*
 * Initialize all the needed managers.
 */
 
void    InitializeToolBox( void )
{
    /*
     * Initialize the toolbox managers
     */
    InitGraf((Ptr)&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs((long)NULL);
    InitCursor();
 
    /*
     * must call this so that the heap is expanded to maximum
     * size before calling any viewer routines
     */
    MaxApplZone() ;
    
}
 
/*------------------------------------------------------------ */
 
/*
 * return true if the viewer is installed
 */
 
Boolean     HasQuickDraw3DViewer( void )
{
    return((long) Q3ViewerNew != kUnresolvedCFragSymbolAddress) ;
}
 
/*------------------------------------------------------------ */
 
/*
 * returns true if the platform supports appleevents - we won't run
 * if it doesn't
 */
Boolean SupportsAEVT(void)
{
    OSErr theError;
    long response;
        
    theError = Gestalt(gestaltAppleEventsAttr,&response);
    if (theError!=noErr)
        return false;
        
    return (response && (response << gestaltAppleEventsPresent));
}
 
 
/********************************************************************************
 * LoadPictIntoGWorld - read pict from the current resource chain, create a 32 bit
 * GWorld the size of the pict, image the pict into the GWorld, dispose of the 
 * pict.  Return a reference to the gworld.
 */
 
static GWorldPtr LoadPictIntoGWorld( short resID )
{
    Rect        pictRect ;
    GWorldPtr   theGWorld = NULL;
    QDErr       theErr ;
    PicHandle   aPicH = GetPicture(resID);
    
    if( aPicH != NULL ) 
    {
        /* create a GWorld the size of the pict */
        pictRect.top = (**aPicH).picFrame.top ;
        pictRect.left = (**aPicH).picFrame.left ;
        pictRect.bottom = (**aPicH).picFrame.bottom ;
        pictRect.right = (**aPicH).picFrame.right ;
        OffsetRect(&pictRect, -pictRect.left, -pictRect.top);
        
        theErr = NewGWorld(&theGWorld, 32, &pictRect, NULL, NULL, 0L) ;
        if( theErr != noErr )
            theGWorld = NULL ;
        else
        {
            CGrafPtr        savedPort ;
            GDHandle        gdh ;
            
            GetGWorld( &savedPort, &gdh);
            SetGWorld( (CGrafPtr)theGWorld, NULL ) ;
            
            DrawPicture( aPicH, &theGWorld->portRect ) ;
            
            SetGWorld( savedPort, gdh);
        }
    }
    
    return theGWorld ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PBNew set up for a new progress bar in a dialog, get all of the info we'll
 * need in order to draw it and allocate a GWorld to composite the Scroll bar.
 */
 
ProgressBarDlogInfoHdl  PB_New( 
    DialogPtr   thePBDialog, 
    short       thePBItemNumber,
    long        maxValue,
    long        currValue )
{
    ProgressBarDlogInfoHdl  theInfo ;
    
    /* first make sure we have the requisite pict's loaded */
    if(theStartCap == NULL )
        theStartCap = LoadPictIntoGWorld( kResIDStartCap ) ;
        
    if(theEndCap == NULL )
        theEndCap = LoadPictIntoGWorld( kResIDEndCap ) ;
        
    if(theDropShadow == NULL )
        theDropShadow = LoadPictIntoGWorld( kResIDDropShadow ) ;
            
    theInfo = (ProgressBarDlogInfoHdl)NewHandle(sizeof(ProgressBarDlogInfoData)) ;
    if( theInfo != NULL ) 
    {
        short       itemType ;
        Handle      item ;
        Rect        box ;
        QDErr       theErr ;
        GWorldPtr   theOffscreen ;
        
        /* stash the other values passed in */
        (**theInfo).thePBDialog = thePBDialog ;         /* reference to the dialog containing the progressbar */
        (**theInfo).thePBItemNumber = thePBItemNumber ; /* the item number in the dialog that is the progress bar */
        (**theInfo).thePBMaxValue = maxValue ;          /* the max value that the progress bar can be */
        (**theInfo).thePBCurrValue = currValue ;        /* current value of the progress bar (usually 0 ) */
        
        /* get the rect of the given item number in local co-ordinates */
        GetDialogItem(thePBDialog, thePBItemNumber, &itemType, &item, &box) ;
                
        /* save the box information, we'll use this to draw into */
        (**theInfo).thePBRect.top       = box.top ;
        (**theInfo).thePBRect.left      = box.left ;
        (**theInfo).thePBRect.bottom    = box.bottom ;
        (**theInfo).thePBRect.right     = box.right ;
        
        OffsetRect( &box, -box.left, -box.top ) ;
    
        /* make a new GWorld for the progress bar, we composite the PB in here and copy to the screen */
        theErr = NewGWorld( &theOffscreen, 32, &box, NULL, NULL, 0L);
        if( theErr != noErr || theOffscreen == NULL )
        {
            /* we couldn't create the offscree, zero the info rec. */
            DisposeHandle((Handle)theInfo) ;
            theInfo = NULL ;
        }
        else
        {
            (**theInfo).thePBGWorld = theOffscreen ;
        }
    }
    return theInfo ;
}
 
/*------------------------------------------------------------ */
 
/*
 * Redraw the progress bar, assumes the caller has just updated the 
 * value in current.
 */
void    PB_Update( ProgressBarDlogInfoHdl theInfo )
{
    CGrafPtr    savedPort ;
    GDHandle    gdh ;
    Rect        theFrame ;
    short       numPixels ;
    Rect        tmpRect ;
    
    Rect        destRect ;
    
    HLock((Handle)theInfo) ;
    
    GetGWorld( &savedPort, &gdh);
    SetGWorld( (CGrafPtr)(**theInfo).thePBGWorld, NULL ) ;
        
    /* fill the entire rect with the default grey */
    
    RGBForeColor(&RGBGrey) ;
    PaintRect(&(**theInfo).thePBGWorld->portRect) ;
    
    /* frame the outline of the progress bar */
    
    /* copy the rect to the frame rect */
    theFrame.top    = (**theInfo).thePBGWorld->portRect.top ;
    theFrame.left   = (**theInfo).thePBGWorld->portRect.left ;
    theFrame.bottom = (**theInfo).thePBGWorld->portRect.bottom ;
    theFrame.right  = (**theInfo).thePBGWorld->portRect.right ;
    
    /* how long is the progress part of the progress bar in pixels */
    numPixels = ((float)(**theInfo).thePBCurrValue / (**theInfo).thePBMaxValue ) 
                    * ((**theInfo).thePBGWorld->portRect.right - (**theInfo).thePBGWorld->portRect.left - 2) ;
    
    /* and inset for the frame */
    InsetRect( &theFrame, 1, 1 ) ;
 
    RGBForeColor( &RGBBlack ) ;
    FrameRect( &theFrame ) ;
    
    /* apply highlight to top and left edges (outermost) */
    MoveTo( (**theInfo).thePBGWorld->portRect.left, 
            (**theInfo).thePBGWorld->portRect.bottom - 1) ;
 
    RGBForeColor( &RGBLtGrey ) ;
    MoveTo( (**theInfo).thePBGWorld->portRect.left, 
            (**theInfo).thePBGWorld->portRect.bottom - 2 ) ;
 
    
    RGBForeColor( &RGBMedGrey ) ;
    LineTo( (**theInfo).thePBGWorld->portRect.left, 
            (**theInfo).thePBGWorld->portRect.top );        
            
    LineTo( (**theInfo).thePBGWorld->portRect.right - 1, 
            (**theInfo).thePBGWorld->portRect.top );
            
    /* hit the pixel at the top left */     
    RGBForeColor( &RGBLtGrey ) ;
    LineTo( (**theInfo).thePBGWorld->portRect.right, 
            (**theInfo).thePBGWorld->portRect.top );
    
    /* right and bottom edges */
    RGBForeColor( &RGBWhite ) ;
    MoveTo( (**theInfo).thePBGWorld->portRect.right - 1, 
            (**theInfo).thePBGWorld->portRect.top + 1 ) ;
            
    LineTo( (**theInfo).thePBGWorld->portRect.right - 1, 
            (**theInfo).thePBGWorld->portRect.bottom - 1);
    
    LineTo( (**theInfo).thePBGWorld->portRect.left + 1, 
            (**theInfo).thePBGWorld->portRect.bottom - 1);
    
    
    /* apply highlight to top and left edges (innermost) */
    MoveTo( (**theInfo).thePBGWorld->portRect.left + 2, 
            (**theInfo).thePBGWorld->portRect.bottom - 3) ;
    
    RGBForeColor( &RGBDkGrey ) ;
    
    LineTo( (**theInfo).thePBGWorld->portRect.left + 2, 
            (**theInfo).thePBGWorld->portRect.top + 2 ) ;       
            
    LineTo( (**theInfo).thePBGWorld->portRect.right - 4, 
            (**theInfo).thePBGWorld->portRect.top + 2) ;
    
    /* hit the single pixel in the inside corner */
    RGBForeColor( &RGBGrey ) ;
    
    LineTo( (**theInfo).thePBGWorld->portRect.right - 3, 
            (**theInfo).thePBGWorld->portRect.top + 2) ;
    
 
    /* right and bottom */
    RGBForeColor( &RGBLtGrey ) ;
    
    LineTo( (**theInfo).thePBGWorld->portRect.right - 3, 
            (**theInfo).thePBGWorld->portRect.bottom - 3) ;
            
    LineTo( (**theInfo).thePBGWorld->portRect.left + 3, 
            (**theInfo).thePBGWorld->portRect.bottom - 3) ;
            
    RGBForeColor( &RGBBlack ) ;
    
    /* set up the rect for the progress part of the PB */
    tmpRect.top = theFrame.top ;
    tmpRect.left = theFrame.left  ;
    tmpRect.bottom = theFrame.bottom ;
    tmpRect.right = theFrame.right ;
    
    InsetRect( & tmpRect, 1, 1 ) ;
    tmpRect.right = theFrame.left + numPixels ;
 
    /* lock the compositing GWorld for updates */
    LockPixels(GetGWorldPixMap((**theInfo).thePBGWorld)) ;
 
    /* what do we want to draw today */
    if( numPixels <= 8 )
    {
        /* we don't start doing the 3d effect until we have enough pixels for the
         * start and end caps, so before that just draw a blue rect.
         */
        RGBForeColor( &RGBBlue ) ;
        PaintRect(&tmpRect) ;
        RGBForeColor( &RGBBlack ) ;
    }   
    else
    {
        short startLine = tmpRect.top ;
        
        /* draw the line gradation */
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBDkBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBMedBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBLtBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBWhite ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBLtBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBMedBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBDkBlue ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        MoveTo( tmpRect.left, startLine ) ;
        RGBForeColor( &RGBBlack ) ;
        LineTo( tmpRect.right, startLine++) ;
        
        if( theStartCap != NULL )
        {
            destRect.top = theStartCap->portRect.top ;
            destRect.left = theStartCap->portRect.left ;
            destRect.bottom = theStartCap->portRect.bottom ;
            destRect.right = theStartCap->portRect.right ;
            
            OffsetRect(&destRect, 2, 2) ;
            
            LockPixels(GetGWorldPixMap(theStartCap)) ;      
            CopyBits(   (BitMapPtr) &theStartCap->portPixMap, 
                        (BitMapPtr) &(**theInfo).thePBGWorld->portPixMap, 
                        & theStartCap->portRect, 
                        & destRect, 
                        srcCopy, 
                        0L ) ;
            UnlockPixels(GetGWorldPixMap(theStartCap)) ;        
        }
        
        if( theEndCap != NULL )
        {
            destRect.top = theEndCap->portRect.top ;
            destRect.left = theEndCap->portRect.left ;
            destRect.bottom = theEndCap->portRect.bottom ;
            destRect.right = theEndCap->portRect.right ;
            
            OffsetRect(&destRect, numPixels + 2 - 6 , 2) ;
            
            LockPixels(GetGWorldPixMap(theEndCap)) ;        
            CopyBits(   (BitMapPtr) &theEndCap->portPixMap, 
                        (BitMapPtr) &(**theInfo).thePBGWorld->portPixMap, 
                        & theEndCap->portRect, 
                        & destRect, 
                        srcCopy, 
                        0L ) ;
            UnlockPixels(GetGWorldPixMap(theEndCap)) ;      
        }
    }   
            
    
    /* handle the drop shadow */
    if( numPixels > 3 && theDropShadow != NULL )
    {
        destRect.top = theDropShadow->portRect.top ;
        destRect.left = theDropShadow->portRect.left ;
        destRect.bottom = theDropShadow->portRect.bottom ;
        destRect.right = theDropShadow->portRect.right ;
        
        OffsetRect(&destRect, numPixels + 2 - 3, 2) ;
        
        LockPixels(GetGWorldPixMap(theDropShadow)) ;        
        CopyBits(   (BitMapPtr) &theDropShadow->portPixMap, 
                    (BitMapPtr) &((**theInfo).thePBGWorld->portPixMap), 
                    & theDropShadow->portRect, 
                    & destRect, 
                    srcCopy, 
                    0L ) ;
        UnlockPixels(GetGWorldPixMap(theDropShadow)) ;      
    }
    
    
    SetGWorld( savedPort, gdh ) ;
        
    CopyBits(   (BitMapPtr) &(**theInfo).thePBGWorld->portPixMap, 
                &(**theInfo).thePBDialog->portBits, 
                &(**theInfo).thePBGWorld->portRect, 
                &(**theInfo).thePBRect, 
                srcCopy, 
                0L ) ;
        
    UnlockPixels(GetGWorldPixMap((**theInfo).thePBGWorld)) ;
    
    HUnlock((Handle)theInfo) ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * free up a progress bar info record
 */
void    PB_Delete( ProgressBarDlogInfoHdl theInfo ) 
{
    GWorldPtr   theGWorld ;
    
    /* dispose of the GWorld associated with the info record */
    if((theGWorld = (**theInfo).thePBGWorld ) != NULL )
        DisposeGWorld( theGWorld ) ;
    
    
    /* 
     * dispose of the memory allocated for the picts used to 
     * build the progress bar first make sure we have the 
     * requisite pict's loaded 
     */
    if(theStartCap == NULL )
        DisposeGWorld( theStartCap ) ;
        
    if(theEndCap == NULL )
        DisposeGWorld( theEndCap ) ;
        
    if(theDropShadow == NULL )
        DisposeGWorld( theDropShadow ) ;
        
    /* last of all blow away the info record */
    DisposeHandle((Handle)theInfo) ;
}
 
 
 
/* printing support */
 
 
 
/*---------------------------------------------------------------------*/
 
/*
 * SetupPrintHdl - This routine returns a default print handle for the 
 * current printer driver.
 *
 * The printer driver is expected to be closed upon entry, and is 
 * therefore opened and closed in this routine.
 */
 
OSErr SetupPrintHdl(THPrint *hPrint)
{
    OSErr err;
    
    /*
     * Create a handle, open the printer driver, set the
     * default values for the handle, and close the driver.
     * Be on the lookout for errors.
     */
 
    *hPrint = (THPrint) NewHandle(sizeof(TPrint));
 
    if ( *hPrint != nil )
    {
        PrOpen() ;
 
        err = PrError() ;
                
        if (err == noErr) 
        {
            PrintDefault( *hPrint ) ;
            err = PrError() ;
        }
 
        PrClose() ;
 
    }
    else
        err = MemError() ;
    
    return err;
}
 
void DoCreatePrintRecord( DocumentHdl theDocument )
{
    Boolean         changed = false ;
    char            theState = 0 ;
    short           theResRef ;
    FSSpec          theFile ;
    
    /*  create a new print record */
    theState = HGetState( (Handle)theDocument ) ;
    MoveHHi( (Handle)theDocument ) ;
    HLock( (Handle)theDocument ) ;
    
    if(SetupPrintHdl( &(**theDocument).fPrintRec ) == noErr) 
    {       
        /*  check we have a "good" print record */
        changed = PrValidate( (**theDocument).fPrintRec ) ;
        
    }
    HSetState( (Handle)theDocument, theState ) ;
}
 
/* ---------------------------------------------------------------------------------- */
 
OSErr DoStyleDlog(THPrint hPrint)
{
    OSErr   err = noErr ;
    Boolean changed = false ;
    
    PrOpen() ;
    if((err = PrError()) == noErr ) {
        changed = PrStlDialog(hPrint);
        err = PrError();
        if (!err && !changed) err = iPrAbort;
    }
 
    PrClose();
    
    return err;
}
 
/* ---------------------------------------------------------------------------------- */
 
OSErr DoJobDlog(THPrint hPrint)
{
    OSErr   err = noErr ;
    Boolean changed = false ;
    
    PrOpen() ;
    if((err = PrError()) == noErr ) {
        changed = PrJobDialog(hPrint);
        err = PrError();
        if (!err && !changed) err = iPrAbort;
    }
 
    PrClose();
    
    return err;
}
 
/* ---------------------------------------------------------------------------------- */
/*   CommandPeriod - This routine checks to see if the user has */
/*   pressed command-period.  The Printing Manager does this */
/*   for us, but a routine like this is helpful to allow users */
/*   to cancel in the middle of a rendering routine, rather */
/*   than only Printing Manager operations. */
/*    */
/*   If there's a command-period in the event queue, the */
/*   routine returns iPrAbort just like the Printing Manager. */
/*   Otherwise, it returns noErr. */
 
OSErr CommandPeriod()
{
    Boolean     cancel = false;
    EventRecord evtRec;
    
    while (!cancel && (WaitNextEvent(keyDownMask | autoKeyMask, &evtRec, 0, nil)))
        cancel = (evtRec.modifiers & cmdKey) &&
                 ((evtRec.message & charCodeMask) == '.');
 
    return ((cancel)? iPrAbort: noErr);
}
 
/* ---------------------------------------------------------------------------------- */
/*  IdleProc - This routine is a PrIdleProcPtr that spins our */
/*   cursor and looks for command-period presses during */
/*   printing. */
 
 
pascal void IdleProc()
{
    OSErr       err;
//  BumpCursor();
    if (err = CommandPeriod()) 
        PrSetError(err);
}
/* ---------------------------------------------------------------------------------- */
short   GeneralCountPages( 
    GWorldPtr   theGWorld, 
    Rect        *pageRect ) 
{
    Rect            pictRect ;
    short           horizOffset, 
                    vertOffset,
                    pagesWide = 0,  /*  the number of pages wide the image is */
                    pagesHigh = 0,  /*  the number pages high for this image */
                    pageWidth,      /*  the width of one page */
                    pageHeight ;    /*  the height of one page */
 
    DocumentHdl     theDocumentHdl ;
    PictDataHdl     thePictDataHdl ;
    OSErr           theErr ;
 
    pictRect = theGWorld->portRect ;
    
    horizOffset = -(pictRect.left) ;
    vertOffset = -(pictRect.top) ;
    
    OffsetRect( &pictRect, horizOffset, vertOffset ) ;  /*  this should make the origin of the rect (0,0) */
 
    pageWidth = pageRect->right - pageRect->left ;
    pageHeight = pageRect->bottom - pageRect->top ;
    
    pagesWide = (1.0 + pictRect.right / pageWidth ) ;   /*  round up the number of pages to the nearest */
    pagesHigh = (1.0 + pictRect.top  / pageHeight ) ;   /*  whole page in each direction. */
 
    return pagesWide * pagesHigh ;
}
 
/* ---------------------------------------------------------------------------------- */
 
void    GeneralPrintPage( 
    GWorldPtr       theGWorld, 
    Rect            *pageRect, 
    GrafPtr         imagingPort, 
    short           pageNum ) 
{
    GWorldPtr       docGWorld;
    Rect            pictRect, rectToPrint,srcRect,dstRect;
    short           pagesWide,      /*  the number of pages wide the image is */
                    pagesHigh,      /*  the number pages high for this image */
                    horozTile,      /*  used in the loop to denote the H tile to print from the image */
                    vertTile ;      /*  used in the loop to denote the V tile to print from the image */
    short           thisPage = 1;   /*  used to find the page they want us to print */
    short           pictHOff,
                    pictVOff,
                    pictWidth,      /*  the width of the doc's GWorld */
                    pictHeight ;    /*  the height of the doc's GWorld */
    short           pageWidth,      /*  the width of one page */
                    pageHeight ;    /*  the height of one page */
    DocumentHdl     theDocumentHdl ;
    PictDataHdl     thePictDataHdl ;
    OSErr           theErr ;
    PixMapHandle    offPixMap ;
 
    pictRect = theGWorld->portRect ;
    
    SetPort( imagingPort ) ;
    
    pictHOff   = -(pictRect.left) ;
    pictVOff   = -(pictRect.top) ;
    pictWidth  = pictRect.right - pictRect.left ;
    pictHeight = pictRect.bottom - pictRect.top ;
    
    pageWidth = pageRect->right - pageRect->left ;
    pageHeight = pageRect->bottom - pageRect->top ;
    
    pagesWide = (1.0 + pictWidth  / pageWidth  ) ;  /*  round up the number of pages to the nearest */
    pagesHigh = (1.0 + pictHeight / pageHeight ) ;  /*  whole page in each direction. */
    
    thisPage = 1;
    for (vertTile=0; vertTile<pagesHigh; vertTile++)
    {
        for (horozTile=0; horozTile<pagesWide; horozTile++)
        {
        
            /*  check this is the page we were called to print */
            
            if( thisPage == pageNum )
            {
                srcRect = *pageRect;
                
                OffsetRect( &srcRect,
                            pictRect.left - srcRect.left,
                            pictRect.top  - srcRect.top );
                    
                OffsetRect( &srcRect,
                            horozTile * pageWidth,
                            vertTile * pageHeight );
                
                SectRect( &srcRect, &pictRect, &srcRect);
                
 
                /*  finess the rect to print, we'll assume the the top left is always correct. */
                /*  this helps with areas of the pict that don't fit entirely on one sheet of */
                /*  paper */
                
                dstRect = srcRect;
                
                OffsetRect( &dstRect,
                            pageRect->left - srcRect.left,
                            pageRect->top  - srcRect.top );
 
                ClipRect( &dstRect ) ;
                
                /* get the gworld pixmap and lock it down */
                offPixMap = GetGWorldPixMap( theGWorld ) ;
                (void) LockPixels( offPixMap ) ;
                
                /* copy from the gWorld Image to the window's port */
                CopyBits( (BitMap *) *offPixMap,
                          &imagingPort->portBits,
                          &srcRect,                         
                          &dstRect,
                          srcCopy,
                          nil ) ;
                
                /* unlock the pixmap */
                (void) UnlockPixels( offPixMap ) ;
 
            }
            
            /*  bump the page number */
            thisPage++ ;
        }
    }
        
}
 
/* ---------------------------------------------------------------------------------- */
 
OSErr DoPrintLoop( WindowPtr    theWindow ) 
{
    short               savedResFile = CurResFile() ;
    GrafPtr             savedPort ;
    TPPrPort            printingPort ;
    OSErr               theErr = noErr ;
    THPrint             thePrintRec  ;
    PrIdleUPP           myIdleProc = NewPrIdleProc(IdleProc) ;
    DocumentHdl         theDocumentHdl ;
    CountPagesProc      countPagesP ;
    PrintPageProc       printPageP ;
    
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL)
    {
        
        thePrintRec = (**theDocumentHdl).fPrintRec ; 
        
        GetPort( &savedPort ) ;
        PrOpen() ;
        
        if( (theErr = PrError()) == noErr ) {
        
            short   numCopies = (**thePrintRec).prJob.iCopies ;
            short   firstPage = (**thePrintRec).prJob.iFstPage ;    /*  save the first page */
            short   lastPage = (**thePrintRec).prJob.iLstPage;      /*  save the last page */
            short   copies, pageNum ;
            
            (**thePrintRec).prJob.iFstPage = 1 ;            /*  reset to 1 */
            (**thePrintRec).prJob.iLstPage = iPrPgMax ; /*  reset to maximum */
 
            /*  we don't assign this directly because NewPrIdleProc may move memory, */
            /*  so assign to an intermediate - myIdleProc */
            (**thePrintRec).prJob.pIdleProc =  myIdleProc; /*  install the cursor spinning idle proc */
            
            /*
             * since the count pages function is dereferenced by a pointer we have two choices,
             * either lock the handle, or copy the function pointer.
             */
            countPagesP = (**theDocumentHdl).procs->countPagesP ;
            
            /* if there is one, call the save handling proc with this window */
            if( countPagesP )
                lastPage = (*countPagesP)( theWindow, &(**thePrintRec).prInfo.rPage ) ;
            
            for( copies = 1; copies <= numCopies ; copies++ ) {
            
                printingPort = PrOpenDoc( thePrintRec, nil, nil ) ;
                SetPort( (GrafPtr)printingPort ) ;
                if((theErr = PrError()) == noErr ) {
                
                    for( pageNum = firstPage; pageNum <= lastPage; pageNum++ ) {
                    
                        PrOpenPage( printingPort, nil ) ;
                        if((theErr = PrError()) == noErr ) {
                            
                            /*
                             * since the print page function is dereferenced by a pointer 
                             * we have two choices, either lock the handle, or copy the 
                             * function pointer.
                             */
                            printPageP = (**theDocumentHdl).procs->printPageP ;
                            
                            /*  image the page */
                            if( printPageP )
                                (*printPageP)( theWindow,  &(**thePrintRec).prInfo.rPage, (GrafPtr)printingPort, pageNum  ) ;
            
                            PrClosePage(printingPort) ;
                        }
                    
                    }
                    if( (theErr = PrError()) == noErr ) {
 
                        TPrStatus       theStatus ;
 
                        PrCloseDoc(printingPort) ;
                        theErr = PrError() ;
 
                        /*  if we're printing to our good friend the ImageWriter, or similar, despool */
                        if( theErr == noErr && ((**thePrintRec).prJob.bJDocLoop == bSpoolLoop))
                            PrPicFile( thePrintRec, nil, nil, nil, &theStatus ) ; 
                    }
                }
            }
        }
        PrClose() ;
 
        DisposeRoutineDescriptor((UniversalProcPtr)myIdleProc) ;
 
        SetPort( savedPort ) ;
    }
 
    return theErr ;
    
}
 
 
/*------------------------------------------------------------ */
 
/*
 * called to register our AppleEvent handlers.
 *
 * Should really have some error handling in here.
 */
void RegisterRequiredAppleEventHandlers(void)
{
    OSErr               theError;
    ProcessSerialNumber myApplicationPSN;           /* This application's psn  */
    
    /* Set up a self-addressed descriptor record.  */
    myApplicationPSN.highLongOfPSN = 0;
    myApplicationPSN.lowLongOfPSN = kCurrentProcess ;
    
    theError = AECreateDesc(typeProcessSerialNumber,
                            (Ptr)&myApplicationPSN,
                            sizeof(ProcessSerialNumber),
                            &gSelfAddress);
    if (theError!=noErr)
        return;
    
    theError = AEInstallEventHandler(   kCoreEventClass,
                                        kAEOpenApplication,
                                        NewAEEventHandlerProc(HandleCoreAppleEventOfTypeOAPP),
                                        0L,
                                        false);
    if (theError!=noErr)
        return;
                
    theError = AEInstallEventHandler(   kCoreEventClass,
                                        kAEOpenDocuments,
                                        NewAEEventHandlerProc(HandleCoreAppleEventOfTypeODOC),
                                        0L,
                                        false);
    if (theError!=noErr)
        return;
                
    theError = AEInstallEventHandler(   kCoreEventClass,
                                        kAEPrintDocuments,
                                        NewAEEventHandlerProc(HandleCoreAppleEventOfTypePDOC),
                                        0L,
                                        false);
    if (theError!=noErr)
        return;
                
    theError = AEInstallEventHandler(   kCoreEventClass,
                                        kAEQuitApplication,
                                        NewAEEventHandlerProc(HandleCoreAppleEventOfTypeQUIT),
                                        0L,
                                        false);
    if (theError!=noErr)
        return;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * open application theEventRecord handler for the core theEventRecord suite, 
 * by default we just want a blank new document
 */
pascal OSErr HandleCoreAppleEventOfTypeOAPP( AppleEvent *theAppleEvent, AppleEvent *theAppleEventReply, long userDefinedReferenceConstant)
{
    /*
     * we don't actually do anything on open - you could,
     * for example you might want to open a blank untitled 
     * theWindow
     */
    OSErr theError = noErr ;
    return theError;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * handler for the open document AppleEvent handler
 */
 
pascal OSErr HandleCoreAppleEventOfTypeODOC(AppleEvent *theAppleEvent, AppleEvent *theAppleEventReply, long userDefinedReferenceConstant)
{
    FSSpec      theFileSpec;
    AEDescList  theDocumentList;
    OSErr       theError,
                theIgnoredError;
    long        myTempIndex,
                numberOfItemsInList;
    Size        actualSize;
    AEKeyword   theAEKeyword;
    DescType    theDescriptorType;
 
    
    theError = AEGetParamDesc(theAppleEvent,keyDirectObject,typeAEList,&theDocumentList);
    if (theError == noErr) {
        /*
         * see how many descriptor items are in the list
         * this is the number of documents we want to open
         */
        theError = AECountItems(&theDocumentList,&numberOfItemsInList);
        
        /*
         * now get each descriptor record from the list
         * coerce the returned data to an FSSpec record, and
         * open the asoociated file
         */
        for (myTempIndex=1; myTempIndex <= numberOfItemsInList && theError == noErr; myTempIndex++) {
        
            theError = AEGetNthPtr( &theDocumentList, 
                                    myTempIndex,
                                    typeFSS,
                                    &theAEKeyword,
                                    &theDescriptorType,
                                    (Ptr)&theFileSpec,
                                    sizeof(theFileSpec),
                                    &actualSize);
    
            if (theError == noErr)  {
            
                FInfo       fndrInfo ;
                
                /*
                 * we now have a valid FSSpec to reference the file, we need to know 
                 * what type the file is to determine which file open function to call
                 * we can determine this from the finder info for the file
                 */
                
                theError = FSpGetFInfo( &theFileSpec, &fndrInfo );  
                
                /*
                 * if we got that ok, then we switch on the file  
                 * type (we don't care about the creator type)  
                 */
                        
                if (theError == noErr)  {
                
                    switch( fndrInfo.fdType ) {
                        case 'TEXT':    /* your app should NOT really open text files since
                                         * they may not be of type 3DMF, this is here for 
                                         * debugging, note that the standard file dlog will
                                         * not allow you to open TEXT files.
                                         */
                        case '3DMF':
                            ViewerWindow_Open( &theFileSpec );
                            break ;
                    }
                }
            }
        }
        theIgnoredError = AEDisposeDesc(&theDocumentList);
    }
    return theError ;
}
 
/*------------------------------------------------------------ */
 
/*
 * handler for the print document theEventRecord handler
 */
pascal OSErr HandleCoreAppleEventOfTypePDOC(AppleEvent *theAppleEvent,AppleEvent *theAppleEventReply,long userDefinedReferenceConstant)
{
    FSSpec      theFileSpec;
    AEDescList  theDocumentList;
    OSErr       theError;
    long        myTempIndex,
                numberOfItemsInList;
    Size        actualSize;
    AEKeyword   theAEKeyword;
    DescType    theDescriptorType;
 
    
    theError = AEGetParamDesc(theAppleEvent,keyDirectObject,typeAEList,&theDocumentList);
    if (theError == noErr) {    
        
        /*
         * see how many descriptor items are in the list
         * this is the number of documents we want to print
         */
        
        theError = AECountItems(&theDocumentList,&numberOfItemsInList);
 
        /*
         * now get each descriptor record from the list
         * coerce the returned data to an FSSpec record, and
         * print the asoociated file
         */
        
        for (myTempIndex=1; myTempIndex <= numberOfItemsInList && theError == noErr; myTempIndex++) {
        
            theError = AEGetNthPtr( &theDocumentList, 
                                myTempIndex,
                                typeFSS,
                                &theAEKeyword,
                                &theDescriptorType,
                                (Ptr)&theFileSpec,
                                sizeof(theFileSpec),
                                &actualSize);
    
            if (theError == noErr)  {   
                            
                /*
                 * if the app handles printing then you could do 
                 * something like: theError = HandlePrintDoc( &theFileSpec );
                 */
                
                theError = errAEEventNotHandled ;
            }
        }
        theError = AEDisposeDesc(&theDocumentList);
    }
    return theError ;
}
 
/*------------------------------------------------------------ */
 
/*
 * Quit AppleEvent handler
 */
 
pascal OSErr HandleCoreAppleEventOfTypeQUIT(AppleEvent *theAppleEvent,AppleEvent *theAppleEventReply,long userDefinedReferenceConstant)
{
    OSErr           theError = noErr ;      /* this is used as the return value  */
 
    theError = HandleFileQuitItem() ;
            
    return theError ;
}
 
/*------------------------------------------------------------ */
 
/*
 * we want to handle events until the fronmost theWindow goes away
 */
 
void MainEventLoop( void )
{
    EventRecord     theEventRecord;
 
    while( !gQuitFlag )
    {
        Point           mouseLoc ;
        
        if (WaitNextEvent( everyEvent, &theEventRecord,  GetCaretTime(), NULL ))
        {
            AdjustMenus() ;
            ( void ) HandleEvent( &theEventRecord ) ;
        }
    }
}
 
/*------------------------------------------------------------ */
 
/*
 * actually handle the event, this function can also be used for 
 * the anchor for the renderer.
 */
 
 
TQ3Boolean HandleEvent( const EventRecord *theEventRecord )
{
    WindowPtr           theWindow;
    GrafPtr             savedPort ;
    short               thePart;
    Rect                screenRect;
    Rect                growRect ;
    Point               aPoint = {100, 100};
    GrafPtr             oldPort ;
    Boolean             eventWasHandled = false ;
    unsigned long       width ;
    unsigned long       height ;
    long                newSize ;
    OSErr               theErr ;
    Point               mouseLoc ;
    TQ3Boolean          handledEvent = kQ3False ;
    TQ3ViewerObject     theViewer = NULL ;
    ViewerDataHdl       theViewerDataHdl ;
    DocumentHdl         theDocumentHdl ;
    UpdateContentProc   theUpdateProc ;
    HandleEventProc     theEventProc ;
    CloseProc           closeP ;
        
    SetCursor( &qd.arrow ) ;
            
    switch (theEventRecord->what) 
    {
        case mouseDown:
        
            /* we set up thePart in the window test at the top of this routine */
            thePart = FindWindow ( theEventRecord->where, &theWindow ) ;
 
            switch( thePart ) {
                case inMenuBar: 
                    HandleMenuCommand(MenuSelect(theEventRecord->where));
                    handledEvent = kQ3True ;
                    break;
                
                case inDrag:
            
                    screenRect = (**GetGrayRgn()).rgnBBox;
                    DragWindow( theWindow, theEventRecord->where, &screenRect );
                    handledEvent = kQ3True ;
                    break ;
            
                case inContent:
            
                    if (theWindow != FrontWindow()) 
                    {
                        SelectWindow( theWindow );
                        handledEvent = kQ3True ;
                    }
                    else
                    {
                        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                        
                        /*
                         * since the update function is dereferenced by a pointer we have two choices,
                         * either lock the handle, or copy the function pointer.
                         */
                        theEventProc = (**theDocumentHdl).procs->handleEventP ;
                        
                        /* if there is one, call the event handling proc with this window */
                        if( theEventProc )
                            handledEvent = (*theEventProc)( theWindow, theEventRecord ) ;
                        else
                            handledEvent = kQ3False ;
                    }
                    break ;
            
                case inGrow:
                
                    /*
                     * get the viewer object associated with this window
                     */
                    
                    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                    if(theDocumentHdl != NULL) 
                    {
                        if((**theDocumentHdl).fDocumentMagic == kViewerMagic 
                            && (theViewerDataHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL
                            && (theViewer = (**theViewerDataHdl).fViewer) != NULL )
                        {
                            
                            /* 
                             * first we need to calculate the minimum size 
                             * for this viewer window, fortunately the
                             * viewer library has a handy little utility
                             * function that we can use here:
                             */
                            
                            
                            GetPort( &savedPort ) ;
                            SetPort( (GrafPtr)theWindow ) ;
                            
                            theErr = Q3ViewerGetMinimumDimension( 
                                        theViewer,
                                        &width,
                                        &height );
 
                            growRect.top = height + LMGetMBarHeight() ; /* the height of tuhe title bar is the same as the height of the menu bar */
                            growRect.left = width + 34;     /* +34 so the grow box look neat */
                            growRect.bottom = kMaxHeight ;
                            growRect.right = kMaxWidth ;        
                            
                            newSize = GrowWindow(theWindow, theEventRecord->where, &growRect);
                            if (newSize != 0) {
                                width = LoWrd(newSize) ;
                                height = HiWrd(newSize) ;
                                SizeWindow(theWindow, width, height, true);
                                Q3ViewerSetBounds( theViewer, &theWindow->portRect ) ;
                                
                                EraseRect( &theWindow->portRect ) ;
                                Q3ViewerDraw( theViewer ) ;
                                DoDrawGrowIcon( theWindow ) ;
                                
                            }
                            handledEvent = kQ3True ;
                            SetPort( savedPort ) ;
 
                        }
                    }
                    break;
                    
                case inGoAway:
                    if (TrackGoAway( theWindow, theEventRecord->where )) {
                        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                        closeP = (**theDocumentHdl).procs->closeP ;
                        if( closeP )
                            (*closeP)( theWindow ) ;
                    }
                    handledEvent = kQ3True ;
                    break ;
                    
                default:
                    break ;
            }
            break ;
                    
                
        case updateEvt:
 
            handledEvent = kQ3True  ;
 
            theWindow = (WindowPtr)theEventRecord->message;
            theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
            
            /*
             * since the update function is dereferenced by a pointer we have two choices,
             * either lock the handle, or copy the finction pointer.
             */
            theUpdateProc = (**theDocumentHdl).procs->updateWindowP ;
            
            /* call the update proc with this window */
            if( theUpdateProc )
                (*theUpdateProc)(theWindow) ;
                
            break ;
            
        case keyDown:
        case autoKey:
            HandleKeyPress(theEventRecord);
            handledEvent = kQ3True ;
            break ;
            
        case diskEvt:
            if ( HiWrd(theEventRecord->message) != noErr ) 
                (void) DIBadMount(aPoint, theEventRecord->message) ;
            handledEvent = kQ3True ;
            break ;
            
        case osEvt:
        case activateEvt:
            handledEvent = kQ3False  ;
            break ;
 
        case kHighLevelEvent:
            AEProcessAppleEvent(theEventRecord);
            handledEvent = kQ3True ;
            break ;
    }
 
Done:
    return handledEvent ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * handle keydow events, all we do is dispatch to the 
 * menu handler if the command key is down, otherwise
 * the keypress is ignored.
 */
 
void HandleKeyPress(const EventRecord *theEventRecord)
{
    char    key;
 
    key = theEventRecord->message & charCodeMask;
    
    /* check to see if we selected a menu  */
    if ( theEventRecord->modifiers & cmdKey ) {     /* Command key down?  */
        HandleMenuCommand(MenuKey(key));
    } 
}
 
 
/*------------------------------------------------------------ */
 
/*
 * handle menu commands
 */
 
void HandleMenuCommand(long menuResult)
{
    short       menuID;
    short       menuItem;
 
    menuID = HiWrd(menuResult);
    menuItem = LoWrd(menuResult);
 
    switch ( menuID ) {
    
        case mInteractiveRendererMenu:
            HandleInteractiveRendererMenu( menuItem ) ;
            break ;
            
        case mAppleMenu:
            HandleAppleMenu( menuItem ) ;
            break ;
            
        case mFileMenu:
            HandleFileMenu( menuItem ) ;
            break;
        
        case mEditMenu:
            HandleEditMenu( menuItem ) ;
            break ;
            
        case mViewMenu:
            HandleViewMenu( menuItem ) ;
            break ;
 
    }
    HiliteMenu(0);      // Unhighlight whatever MenuSelect or MenuKey hilited
}
 
 
 
/*------------------------------------------------------------ */
 
/*
 * handle menu commands in the apple menu
 */
 
 
void HandleAppleMenu( short menuItem )
{
    Str255      daName;
    DialogPtr   theDialog ; 
    short       itemHit ;
    
    switch ( menuItem ) {
        ModalFilterUPP      theProc ;
 
        case iAppleAboutItem:
            
            theDialog = GetNewDialog ( kAboutDialogID, NULL, (WindowPtr)-1 );
            
            GetStdFilterProc( &theProc ) ;
            SetDialogDefaultItem(theDialog, ok) ;
            do {
                ModalDialog ( theProc, &itemHit );
            } while( itemHit != ok ) ;
            DisposeDialog ( theDialog );
            
            break;
            
        default:
            GetMenuItemText(GetMenuHandle(mAppleMenu), menuItem, daName);
            (void) OpenDeskAcc(daName);
            break;
    }
}
 
/*------------------------------------------------------------ */
 
/*
 * handle menu commands in the file menu
 */
 
 
void HandleFileMenu( short menuItem )
{
    WindowPtr           theWindow ;
    StandardFileReply   theSFReply ;
    DocumentHdl         theDocumentHdl ;
    SaveAsProc          saveAsP ;
    SaveProc            saveP ;
    RevertProc          revertP ;
    CloseProc           closeP ;
    PrePrintProc        prePrintP ;
    PostPrintProc       postPrintP ;
 
    switch ( menuItem ) {
    
        case iFileNewItem:
        
            ViewerWindow_New( "\pViewer Window" ) ;
            break ;
    
        case iFileSaveAsItem:
        
            if( (theWindow = FrontWindow()) != NULL )
            {
                theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                
                /*
                 * since the save function is dereferenced by a pointer we have two choices,
                 * either lock the handle, or copy the function pointer.
                 */
                saveAsP = (**theDocumentHdl).procs->saveAsP ;
                
                /* if there is one, call the save handling proc with this window */
                if( saveAsP )
                    (*saveAsP)( theWindow ) ;
                
            }
            break ;
 
        case iFileSaveItem:
            if( (theWindow = FrontWindow()) != NULL )
            {
                theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                
                /*
                 * since the save function is dereferenced by a pointer we have two choices,
                 * either lock the handle, or copy the function pointer.
                 */
                saveP = (**theDocumentHdl).procs->saveP ;
                
                /* if there is one, call the save handling proc with this window */
                if( saveP )
                    (*saveP)( theWindow ) ;
                
            }
            break ;
            
        
        case iFileRevertItem:
            if( (theWindow = FrontWindow()) != NULL )
            {
                theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                
                /*
                 * since the revert function is dereferenced by a pointer we have two choices,
                 * either lock the handle, or copy the function pointer.
                 */
                revertP = (**theDocumentHdl).procs->revertP ;
                
                /* if there is one, call the revert handling proc with this window */
                if( revertP )
                    (*revertP)( theWindow ) ;
                
            }
            break ;
        
        /* these need to be converted */
        case iFilePageSetupItem:
            if( (theWindow = FrontWindow()) != NULL )
            {
                THPrint     thePrintRec ;
                theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                
                thePrintRec = (**theDocumentHdl).fPrintRec ;
                DoStyleDlog( thePrintRec ) ;
            }
            break ;
        
        case iFilePrintItem:
            if( (theWindow = FrontWindow()) != NULL )
            {
                THPrint     thePrintRec ;
                theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                
                thePrintRec = (**theDocumentHdl).fPrintRec ;
                if( DoJobDlog( thePrintRec ) == noErr )
                {
                
                    /*
                     * since the preprint function is dereferenced by a pointer we have two choices,
                     * either lock the handle, or copy the function pointer.
                     */
                    prePrintP = (**theDocumentHdl).procs->prePrintP ;
                    
                    /* if there is one, call the revert handling proc with this window */
                    if( prePrintP )
                        (*prePrintP)( theWindow ) ;
                        
                    DoPrintLoop( theWindow ) ;
                    
                    postPrintP = (**theDocumentHdl).procs->postPrintP ;
                    
                    /* if there is one, call the revert handling proc with this window */
                    if( postPrintP )
                        (*postPrintP)( theWindow ) ;
                }
                
            }
            break ;
                
        case iFileOpenItem:
        
            /* currently we only open 3dmf files */
            /* Get the file name to open  */
            StandardGetFile( NULL, kNumTypes, kTypeList, &theSFReply ) ;
    
            /* did the user cancel?  */
            if(theSFReply.sfGood)
                ViewerWindow_Open( &theSFReply.sfFile ) ; /* no - process the file  */
                
            break ;
            
        case iFileCloseItem:
            if( (theWindow = FrontWindow()) != NULL )
            {
                theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                
                /*
                 * since the revert function is dereferenced by a pointer we have two choices,
                 * either lock the handle, or copy the function pointer.
                 */
                closeP = (**theDocumentHdl).procs->closeP ;
                
                /* if there is one, call the revert handling proc with this window */
                if( closeP )
                    (*closeP)( theWindow ) ;
                
            }
            break ;
            
        case iFileQuitItem:
            HandleFileQuitItem();
            break;
    }
 
}
 
 
/*------------------------------------------------------------ */
 
 
/*
 * handles the Quit menu item in the file menu
 */
 
OSErr HandleFileQuitItem( void ) 
{
    OSErr           theError = noErr ;      /* this is used as the return value  */
    WindowPtr       theWindow ;
    Boolean         tempQuitFlag = true ;
    CloseProc       closeP ;
    DocumentHdl     theDocumentHdl ;
 
    /*
     * if the application has any open windows, close them before 
     * tempQuitFlag.  Since the user may cancelduring the process of 
     * closing, make sure we don't quit the app, unless they can 
     * be closed
     */
    while(( theWindow = FrontWindow()) != NULL && tempQuitFlag )
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        
        /*
         * since the save function is dereferenced by a pointer we have two choices,
         * either lock the handle, or copy the function pointer.
         */
        closeP = (**theDocumentHdl).procs->closeP ;
        
        /* if there is one, call the save handling proc with this window */
        if( closeP )
            tempQuitFlag = ((*closeP)( theWindow ) == noErr)  ;         
    }
 
    /*
     * if we closed everything up successfully, return noErr, otherwise
     * indicate to the caller of this function that we canceled
     */
    
    if (tempQuitFlag) {
        gQuitFlag = true;                   /* in other word the user didn't cancel  */
        AEDisposeDesc(&gSelfAddress);       /* don't forget to dispose of the        */
    }
    else {
        theError = userCanceledErr ;
    }
    
    return theError ;
 
}
 
/*------------------------------------------------------------ */
 
/*
 * handle menu commands in the edit menu
 *
 * To Do: each menu command needs to be factored into the list of procs
 *        for the document type.
 */
 
 
void HandleEditMenu( short menuItem )
{
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
 
    WindowPtr           theWindow ;
    TQ3ViewerObject     theViewer ;
    OSErr               theError ;
    GrafPtr             savedPort ;
    
    CutProc             cutProc ;           /* handle clipboard cut, same as copy followed by clear */
    CopyProc            copyProc ;          /* handle clipboard copy */
    PasteProc           pasteProc ;         /* paste compatible scrap type from the scrap */
    ClearProc           clearProc ;         /* clear the document contents without a save to the scrap */
    UndoProc            undoProc ;          /* undo the last action - if supported */
    
    /*
     * because we do this we are assuming that this menu is disabled for the
     * case where there is no window.
     */
    theWindow = FrontWindow() ;
    SetPort((GrafPtr)theWindow) ;
    if( theWindow != NULL )
    {
        long                aLong ;
        
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
                
        switch ( menuItem ) 
        {
            
            case iEditUndoItem:
                undoProc = (**theDocumentHdl).procs->undoProc ;
                /* if there is one, call the undo handling proc with this window */
                if( undoProc )
                    (*undoProc)( theWindow ) ;
                
                /* redraw the content region of the window to reflect the change */
                GetPort( &savedPort ) ; 
                SetPort( theWindow ) ;
                InvalRect( &theWindow->portRect ) ;
                SetPort( savedPort ) ;
                break ;
                
            case iEditCutItem:
                cutProc = (**theDocumentHdl).procs->cutProc ;
                /* if there is one, call the cut handling proc with this window */
                if( cutProc )
                    (*cutProc)( theWindow ) ;
                aLong = UnloadScrap();
                break ;
                
            case iEditCopyItem:
                copyProc = (**theDocumentHdl).procs->copyProc ;
                /* if there is one, call the copy handling proc with this window */
                if( copyProc )
                    (*copyProc)( theWindow ) ;
                aLong = UnloadScrap();
                break ;
                
            case iEditPasteItem:
                pasteProc = (**theDocumentHdl).procs->pasteProc ;
                /* if there is one, call the paste handling proc with this window */
                if( pasteProc )
                    (*pasteProc)( theWindow ) ;
                aLong = UnloadScrap();
                break ;
                
            case iEditClearItem:
                clearProc = (**theDocumentHdl).procs->clearProc ;
                /* if there is one, call the clear handling proc with this window */
                if( clearProc )
                    (*clearProc)( theWindow ) ;
                aLong = UnloadScrap();
                break ;     
            
            case iEditRendererPrefsItem:
                {
                    /* this should probably be factored out somewhere */
                    TQ3RendererObject       qd3dRenderer;
                    TQ3ViewObject           qd3dView;   
                    TQ3Status               qd3dStatus;
                    TQ3Boolean              qd3dCanceled;
                    TQ3DialogAnchor         qd3dAnchor ;
                                        
                    /*
                     * get the reference to our viewer document data structure
                     * from the long reference constant for the window.  Cast
                     * it to the appropriate type.  If we can't get it (i.e. it's
                     * null we want to bail
                     */
                    if(theDocumentHdl != NULL  
                        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
                        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL)
                    {
                        
                        /* get the reference to the viewer object from our data structure  */
                        theViewer = (**theViewerHdl).fViewer ;
                        if(theViewer == NULL) 
                            return ;
                        
                        /* Get the renderer */
                        qd3dView = Q3ViewerGetView(theViewer);
                        qd3dStatus = Q3View_GetRenderer(qd3dView, &qd3dRenderer);
 
                        /* Put up the configure dialog */
                        if (Q3Renderer_HasModalConfigure(qd3dRenderer))
                        {
                            /* this will be modal */
                            qd3dAnchor.clientEventHandler = NULL;
                            qd3dStatus = Q3Renderer_ModalConfigure(
                                                    qd3dRenderer,
                                                    qd3dAnchor,
                                                    &qd3dCanceled);
                        }
 
                        /* Clean up */
                        qd3dStatus = Q3Object_Dispose(qd3dRenderer);
                        /*
                         * assume that the renderer prefs changed something 
                         * so redraw the content region of the window 
                         */
                        GetPort( &savedPort ) ;
                        SetPort( theWindow ) ;
                        InvalRect( &theWindow->portRect ) ;
                        SetPort( savedPort ) ;
                    }
                }
                break;
        }
    }
 
}
 
/*------------------------------------------------------------ */
 
/*
 * utility function to get the viewer object 
 * associated with a particular viewer window
 */
 
 
TQ3ViewerObject GetViewerFromViewerWindow( WindowPtr theWindow )
{
    OSErr               theError = paramErr ;
    short               theRef ;
    TQ3ViewerObject     theViewer = NULL ;
    FSSpec              theFSSpec ;
    DocumentHdl         theDocumentHdl ;
    
    if( theWindow != NULL )     /* sanity check  */
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL  
            && (**theDocumentHdl).fDocumentMagic == kViewerMagic) 
        {
            /* get the viewer object from the document */
            ViewerDataHdl       theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate) ;
            if( theViewerHdl != NULL )
            {
                theViewer = (**theViewerHdl).fViewer ;
            }
        }
    }
    return theViewer ;
}
 
/*------------------------------------------------------------ */
 
/*
 * utility function to get the GWorld object 
 * associated with a particular pict window
 */
 
 
GWorldPtr GetGWorldFromPictWindow( WindowPtr  theWindow ) 
{
    DocumentHdl         theDocumentHdl ;
    PictDataHdl         thePictDataHdl ;
    GWorldPtr           theGWorld = NULL;
    OSErr               theErr ;
    
    if( theWindow != NULL )     /* sanity check  */
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL
            && (**theDocumentHdl).fDocumentMagic == kPICTMagic
            && (thePictDataHdl = (PictDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
        {
            theGWorld = (**thePictDataHdl).fGWorld;
        }
    }
    return theGWorld ;
}
/*------------------------------------------------------------ */
 
/*
 * utility function to get the renderer associated with a particular
 * type.  Does a linear search of the pRendererList array to 
 * retrieve the renderer object associated with the type passed in.
 * Will return NULL if the renderer type is not in the list.
 */
TQ3RendererObject   GetRendererByType(TQ3ObjectType theRendererType)
{
    TQ3RendererObject   myRenderer = NULL ;
    long                theIndex ;
    
    for( theIndex = 0; theIndex < pRendererCount; theIndex++ ) 
    {
        if( pRendererList[ theIndex ].fRendererType == theRendererType )
            myRenderer = pRendererList[ theIndex ].fRendererObject ;
    }
    return myRenderer ;
}
 
/*------------------------------------------------------------ */
 
/*
 * utility function to set the renderer for a viewer
 */
 
TQ3Status   ViewerSetRenderer( TQ3ObjectType theRendererType, TQ3ViewerObject theViewer )
{
    TQ3RendererObject   myRenderer = NULL ;
    long                theIndex ;
    TQ3ViewObject       myView ;
    TQ3Status           myStatus = kQ3Failure;
    OSErr               theError ;
    
    
    myRenderer = GetRendererByType( theRendererType ) ;
        
    /*
     * set the renderer for the view
     */
    myView = Q3ViewerGetView( theViewer );
    if( myView != NULL && myRenderer != NULL )
    {
        /* set the renderer to the one recovered in the loop above  */
        myStatus = Q3View_SetRenderer(myView, myRenderer) ;
        
    }
    return myStatus ;
 
}
/*------------------------------------------------------------ */
 
/*
 * handle menu commands in the renderer menu
 */
 
 
void HandleInteractiveRendererMenu( short menuItem )
{
    WindowPtr           theWindow ;
    TQ3ObjectType       theRendererType ;
    TQ3ViewerObject     theViewer ;
    GrafPtr             savedPort ;
    
    theWindow = FrontWindow() ;
 
    if( theWindow != NULL )
    {
        theViewer = GetViewerFromViewerWindow( theWindow ) ;    
        theRendererType = pInteractiveTypes[ menuItem - 1 ] ;
        ViewerSetRenderer( theRendererType, theViewer ) ;
        GetPort(&savedPort) ;
        SetPort((GrafPtr)theWindow) ;
        InvalRect(&theWindow->portRect) ;
        SetPort(savedPort) ;
    }
}
 
 
 
/*------------------------------------------------------------ */
 
/*
 * The user chose to render with a non interactive renderer, put
 * up a dialog to allow them to select and configure the renderer
 * and copy the appropriate values from the current viewer window.
 */
 
 
pascal Boolean OurFilter(DialogPtr dlg, EventRecord *event, short *itemHit)
{
 
    ModalFilterUPP      theProc ;
    Boolean             retVal ;
    
    static  Boolean     isDisabled = false ;
    OSErr               theErr = noErr ;
    
    /* stuff for getditems etc */
    Str255              theItem ;
    short               iKind;
    Handle              iHandle;
    Rect                iRect;
    char                theKey ;
    
    
    /* get the std filter proc */
    
    theErr = GetStdFilterProc( &theProc ) ;
    
    if( theErr != noErr )
        ExitToShell() ;
        
    /* try to call the standard filter, if it handles the event, we don't */
    if( !(retVal = CallModalFilterProc(theProc, dlg, event, itemHit)) )
    {
        switch (event->what) {
    
            case nullEvent:
                break;
    
            case keyDown:
            case autoKey:
                theKey = event->message & charCodeMask;
        
                if( isalpha(theKey) || ispunct(theKey) || isspace(theKey) ) 
                {
                    /* It's not a number, reject it */
                    SysBeep(1);
                    
                    /* tell the dialog manager that we handled this already and 
                     * it doesn't have to, so the keystroke will _not_ get 
                     * added to the edit line 
                     */
                    retVal = true; 
                }
                else
                    retVal = false;
                    
                break ;
    
            case updateEvt:
                PenPat( &qd.ltGray ) ;
                
                /* get the text item we are drawing in */
                GetDialogItem ( dlg, kFinalRendrSep1, &iKind, &iHandle, &iRect) ;
                FrameRect(&iRect ) ;
                GetDialogItem ( dlg, kFinalRendrSep2, &iKind, &iHandle, &iRect) ;
                FrameRect(&iRect ) ;
                GetDialogItem ( dlg, kFinalRendrSep3, &iKind, &iHandle, &iRect) ;
                FrameRect(&iRect ) ;
                PenPat( &qd.black ) ;       
                /*
                 * enable or disable the OK button depending on whether 
                 * we made a selection yet
                 */
                GetDialogItem(  dlg, ok, &iKind, &iHandle, &iRect) ;
                
                retVal = false;
                break ;
    
            default:
                retVal = false;
                break ;
        }
    }
    
    return retVal ;
}
 
/*------------------------------------------------------------ */
 
/*
 * IdleProgress method, handles setup, drawing and teardown for
 * the renderer progress dialog 
 */
 
TQ3Status MyViewIdleProgressMethod(
    TQ3ViewObject       view,
    const void          *idlerData,     /* contains the dialog ref */
    unsigned long       current,
    unsigned long       completed)
{
 
    /* 
     * Return kQ3Failure to cancel rendering, kQ3Success to continue. Don't
     * bother posting an error.
     */
     
    TQ3Status                   retVal = kQ3Success ;
    DialogPtr                   theDialog = (DialogPtr)idlerData ;
    ProgressBarDlogInfoHdl      theInfo ;
    CGrafPtr                    savedPort ;
    GDHandle                    savedGDH ;
    EventRecord                 theEvent ;
    char                        theKey ;
    
    /*
     *  Q3View_SetIdleMethod registers a callback that can be called
     *  by the system during rendering.  Unfortunately there is no way yet
     *  to set timer intervals when you want to be called.  Basically, it is
     *  up to the application's idler callback to check clocks to see if you
     *  were called back only a millisecond ago or an hour ago!
     *
     *  Q3View_SetIdleProgressMethod registers a callback that also gives
     *  progress information. This information is supplied by the renderer, and
     *  may or may not be based on real time.
     *
     *  If a renderer doesn't support the progress method, your method will be
     *  called with current == 0 and completed == 0.
     *  
     *  Otherwise, you are GUARANTEED to get called at least 2 or more times:
     *  
     *  ONCE            idleMethod(view, 0, n)      -> Initialize, Show Dialog
     *  zero or more    idleMethod(view, 1..n-1, n) -> Update progress
     *  ONCE            idleMethod(view, n, n)      -> Exit, Hide Dialog
     *  
     *  "current" is guaranteed to be less than or equal to "completed"
     *  "completed" may change values, but current/complete always indicates
     *  the degree of completion.
     *
     *  The calling conventions aid in managing any data associated with a 
     *  progress user interface indicator.
     */
    if( current == 0 && completed == 0 )
    {
        /* renderer doesn't support progress info, so just return success */
        retVal = kQ3Success ;
    }
    else
    {
        if( current == 0 )
        {
            /*
             * make a new progress bar dlog struct - we wait til here because 
             * prior to this point we have no idea what the max and min values
             * are for the dialog.
             */
            theInfo = PB_New( theDialog, kProgressBarPBItemID, completed, current ) ;
            
            /* the theInfo rec in the refCon field of the dialog */
            SetWRefCon((WindowPtr)theDialog, (long)theInfo);
            
            GetGWorld( &savedPort, &savedGDH ) ;
            SetGWorld( (CGrafPtr)theDialog, NULL ) ;
            
            /* make the hidden dialog visible */
            ShowWindow( theDialog ) ; 
            
            /* select it */
            SelectWindow( theDialog ) ;
            
            /* draw the dialog */
            UpdateDialog( theDialog, theDialog->visRgn ) ;
            
            SetGWorld( savedPort, savedGDH ) ;
            
        }
        else if( current == completed )
        {           
            GetGWorld( &savedPort, &savedGDH ) ;
            SetGWorld( (CGrafPtr)theDialog, NULL ) ;
            
            /* draw the last part of the progress bar */
            theInfo = (ProgressBarDlogInfoHdl)GetWRefCon((WindowPtr)theDialog) ;
            (**theInfo).thePBCurrValue = current ;
            
            /* update the progress bar */
            PB_Update( theInfo ) ;
            
            SetGWorld( savedPort, savedGDH ) ;
 
            /* tear down the progressbar dialog struct */
            theInfo = (ProgressBarDlogInfoHdl)GetWRefCon((WindowPtr)theDialog) ;
            DisposeHandle( (Handle)theInfo ) ;
            HideWindow( theDialog ) ;
        }
        else
        {           
            /* update the progress bar */
            GetGWorld( &savedPort, &savedGDH ) ;
            SetGWorld( (CGrafPtr)theDialog, NULL ) ;
 
            theInfo = (ProgressBarDlogInfoHdl)GetWRefCon((WindowPtr)theDialog) ;
            (**theInfo).thePBCurrValue = current ;
            
            /* update the progress bar */
            InvalRect( &theDialog->portRect ) ;
            DrawDialog( theDialog ) ;
            PB_Update( theInfo ) ;
            
            SetGWorld( savedPort, savedGDH ) ;
            
        }
    }
    
    /* 
     * last thing... check the event queue to see if there is a keydown event,
     * we want to see if the user hit command period to cancel.  Be warned - this 
     * way of doing this will flush keyevents from the event queue.
     */
     
    if( GetOSEvent( (keyDownMask | autoKeyMask), &theEvent) == true )
    {
        /* examine the event to see if its a command period */
        theKey = theEvent.message & charCodeMask;
        if( (theEvent.modifiers & cmdKey) && theKey == '.')
        {
            /* 
             * we want to cancel, we can signal this by returning kQ3Failure
             * before leaving this function, clean up & hide the progress dialog
             */
             
            /* tear down the progressbar dialog struct */
            theInfo = (ProgressBarDlogInfoHdl)GetWRefCon((WindowPtr)theDialog) ;
            DisposeHandle( (Handle)theInfo ) ;
            HideWindow( theDialog ) ;
 
            SysBeep(10) ;
            retVal = kQ3Failure ;
        }
        
    }
    return retVal ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * The user chose to render with a non interactive renderer, put
 * up a dialog to allow them to select and configure the renderer
 * and copy the appropriate values from the current viewer window.
 * 
 * this seems like a long winded way to do this.  We create a viewer
 * use that to image (which will take advantage of the renderers updates
 * get the pict for the viewer, image that into the offscreen for the window.
 */
 
 
void HandleViewFinalRendererOption( void ) 
{
    WindowPtr           theFrontWindow ;
    short               itemHit ;
    GrafPtr             savedPort ;
    popupPrivateData    **myPopupPrivateDataPtr ;
    
    short               iKind;
    Handle              iHandle;
    Rect                iRect;
    
    MenuHandle          thePopupMenuHdl ;
    unsigned char       rendererName[255] ;
    TQ3RendererObject   theRenderer ;
    unsigned char       numText[255] ;
    
    OSErr               theErr ;
    
    long                width ;
    long                height ;
    long                theControlValue = 0;
 
    /* 
     * the final renderer option should only be available for a 
     * viewer window, so we can assume the the front window at the 
     * time this is called is a viewer
     */
    theFrontWindow = FrontWindow() ;
 
    GetPort( &savedPort ) ;
    SetPort( (GrafPtr)gFinalRenderDialog ) ;
    InvalRect( &((GrafPtr)gFinalRenderDialog)->portRect) ;
 
    SetDialogDefaultItem(gFinalRenderDialog, kStdOkItemIndex) ;
 
    ShowWindow( (WindowPtr)gFinalRenderDialog ) ;
    SelectWindow( (WindowPtr)gFinalRenderDialog ) ;
    
    do {
        ModalDialog( gModalFilterProcUPP, &itemHit ) ;
        
        if( itemHit == kFinalRendrPopup ) {
        
            /* the user choose the popup.  The item number selected will be the control value
             * we need to get the menuhandle associated with the control, it is in the private
             * control data field, as documented in Inside Macintosh: Toolbox page 5-77
             */
            
            /* get the control handle for the popup */      
            GetDialogItem ( gFinalRenderDialog, kFinalRendrPopup, &iKind, &iHandle, &iRect) ;
            
            /* extract from the control the menuhandle */
            myPopupPrivateDataPtr = (popupPrivateData **)(**(ControlHandle)iHandle).contrlData ; 
            thePopupMenuHdl = (**myPopupPrivateDataPtr).mHandle ;
    
            /* get the string associated with the users selection */
            HLock((Handle)iHandle) ;
            theControlValue = GetControlValue((ControlHandle)iHandle) ;
 
            GetMenuItemText ( thePopupMenuHdl, theControlValue, rendererName );
            HUnlock((Handle)iHandle) ;
                        
            /* reset itemHit to something else */
            itemHit = 0 ;
        
        }
        else if( itemHit == kFinalRendrConfigure ) {
            
            /* get the control handle for the popup */      
            GetDialogItem ( gFinalRenderDialog, kFinalRendrPopup, &iKind, &iHandle, &iRect) ;
            
            /* extract from the control the menuhandle */
            myPopupPrivateDataPtr = (popupPrivateData **)(**(ControlHandle)iHandle).contrlData ; 
            thePopupMenuHdl = (**myPopupPrivateDataPtr).mHandle ;
    
            /* get the string associated with the users selection */
            HLock((Handle)iHandle) ;
            theControlValue = GetControlValue((ControlHandle)iHandle) ;
                        
            if( theControlValue != 0 )
            {
                theRenderer = GetRendererByType( pNonInteractiveTypes[theControlValue-1] ) ;
                
                if( theRenderer != NULL && Q3Renderer_HasModalConfigure( theRenderer )) 
                {
                    TQ3DialogAnchor         qd3dAnchor ;
                    TQ3Boolean              qd3dCanceled ;
                    TQ3Status               theStatus ;
                    
                    
                    /* the renderer prefs dialog will be modal */
                    qd3dAnchor.clientEventHandler = NULL ;
                    theStatus = Q3Renderer_ModalConfigure(
                                            theRenderer,
                                            qd3dAnchor,
                                            &qd3dCanceled) ;
                                            
                }
            }
            else
                SysBeep(10) ;
        }
    } while( itemHit != kStdCancelItemIndex && itemHit != kStdOkItemIndex ) ;
    
    /* we're done with the dialog window, hide it 'till next time */
    HideWindow( (WindowPtr)gFinalRenderDialog ) ;
    
    /* check we didn't cancel */
    if( itemHit == kStdOkItemIndex )
    {
        TQ3ViewerObject     tempViewer ;
        WindowPtr           finalImageWin ;
        PicHandle           theImage ;
 
        /* get the control handle for the popup */      
        GetDialogItem ( gFinalRenderDialog, kFinalRendrPopup, &iKind, &iHandle, &iRect) ;
        
        /* extract from the control the menuhandle */
        myPopupPrivateDataPtr = (popupPrivateData **)(**(ControlHandle)iHandle).contrlData ; 
        thePopupMenuHdl = (**myPopupPrivateDataPtr).mHandle ;
 
        /* get the string associated with the users selection */
        HLock((Handle)iHandle) ;
        theControlValue = GetControlValue((ControlHandle)iHandle) ;
 
        GetMenuItemText ( thePopupMenuHdl, theControlValue, rendererName );
        HUnlock((Handle)iHandle) ;
                    
        /* reset itemHit to something else */
        itemHit = 0 ;
        
        /* get the window size - we can get the dialog item string and just StringToNum it
         * since our filter proc filders out all key stroked except digits
         */
        GetDialogItem ( gFinalRenderDialog, kFinalRendrHeight, &iKind, &iHandle, &iRect) ;
        GetDialogItemText ( iHandle, numText ) ;
        StringToNum ( numText, &height ) ;
        
        GetDialogItem ( gFinalRenderDialog, kFinalRendrWidth, &iKind, &iHandle, &iRect) ;
        GetDialogItemText ( iHandle, numText ) ;
        StringToNum ( numText, &width ) ;
                
        /* make a new window with the renderer name of the correct size */
        finalImageWin = DoCreateNewPictWindow( rendererName, width, height ) ;
        
        /* make a viewer for this window with no controller */
        if( finalImageWin != NULL ) 
        {
            /* get these from the original viewer referenced by FrontWindow */
            TQ3GroupObject      savedGroup ;
            TQ3CameraObject     savedCamera ;
            TQ3GroupObject      savedLights ;
            TQ3ColorARGB        savedBGColor ;
            
            TQ3ViewerObject     originalViewer ;
            TQ3ViewObject       originalView ;
            unsigned long       theViewerFlags ;
            GWorldPtr           theGWorld ; 
            
            CGrafPtr            savedPort ;
            GDHandle            savedGDH ;
 
            theGWorld = GetGWorldFromPictWindow( finalImageWin ) ;
            
            /* make a temp viewer to use to make the PICT */
            if( theGWorld != NULL 
                && (tempViewer = Q3ViewerNew( (CGrafPtr)theGWorld, 
                                &theGWorld->portRect, 
                                kQ3ViewerDefault )) != NULL)
            {
                
                /* lock the viewer's GWorld for updates */
                LockPixels(GetGWorldPixMap(theGWorld)) ;
 
                GetGWorld( &savedPort, &savedGDH ) ;
                SetGWorld( (CGrafPtr)theGWorld, NULL ) ;
 
                originalViewer = GetViewerFromViewerWindow( theFrontWindow ) ;
 
                if( originalViewer != NULL ) 
                {
                    TQ3ViewObject       newView ;
                    
                    /* set the group up, copy from original viewer */
                    savedGroup = Q3ViewerGetGroup( originalViewer ) ;
                    if( savedGroup != NULL )
                    {
                        Q3ViewerUseGroup( tempViewer, savedGroup) ;
                        Q3Object_Dispose( savedGroup ) ;
                    }
                    
                    /* copy the camera from the original viewer */
                    originalView = Q3ViewerGetView( originalViewer ) ;
                    newView = Q3ViewerGetView( tempViewer ) ;
                    
                    if( originalView != NULL )
                    {
                        Q3View_GetCamera( originalView, &savedCamera );
                        if( savedCamera != NULL )
                        {
                            Q3View_SetCamera( newView, savedCamera );
                            Q3Object_Dispose( savedCamera ) ;
                        }
                    }
                    
                    /* copy the light group from the original viewer */
                    if( originalView != NULL )
                    {
                        Q3View_GetLightGroup( originalView, &savedLights );
                        if( savedCamera != NULL )
                        {
                            Q3View_SetLightGroup( newView, savedLights );
                            Q3Object_Dispose( savedLights ) ;
                        }
                    }
 
                    /* copy the background color from the original viewer */
                    if( originalView != NULL )
                    {
                        Q3ViewerGetBackgroundColor( originalViewer, &savedBGColor ) ;
                        if( savedCamera != NULL )
                        {
                            Q3ViewerSetBackgroundColor( tempViewer, &savedBGColor ) ;
                        }
                    }
                    
                    /*
                     * we don't need to dispose the view, since viewergetview doesn't
                     * increment the reference counts of the viewer.
                     */
                    
                    theViewerFlags = Q3ViewerGetFlags( tempViewer ) ;
                    
                    theViewerFlags = theViewerFlags &~ kQ3ViewerControllerVisible;
                    theViewerFlags = theViewerFlags &~ kQ3ViewerDrawFrame;
                    theViewerFlags = theViewerFlags &~ kQ3ViewerDrawDragBorder;
                    
                    Q3ViewerSetFlags( tempViewer, theViewerFlags ) ;
                    
                    /* this is a little funky - we are using a modeless dialog to display the
                     * progress of the render, but the main event loop will never get called,
                     * since the dialog is only "called back" from the render.
                     */
                                    
                    if( gProgressModelessDialog != NULL )
                    {
                        /* install the callback */
                        Q3View_SetIdleProgressMethod( 
                                newView, 
                                MyViewIdleProgressMethod,
                                (const void *)gProgressModelessDialog ) ;
                    
                    }
                    
                    /* image the document */
 
                    /* set the renderer to the renderer from the popup for this document */
                    ViewerSetRenderer( pNonInteractiveTypes[theControlValue-1], tempViewer ) ;
                    Q3ViewerDraw( tempViewer ) ;
                    
                    /* dispose of the viewer object */
                    Q3ViewerDispose( tempViewer ) ;
                    
                    /*
                     * it is possible that if the renderer does not follow the calling conventions
                     * to update the progress dialog, that that dialog can be lest on the screen
                     * so check the fron window, if it is the progress dialog then hide it
                     */
                    if( FrontWindow() == gProgressModelessDialog )
                        HideWindow( gProgressModelessDialog ) ;
                }
                
                SetGWorld( savedPort, savedGDH ) ;
                
                /* lock the viewer's GWorld for updates */
                UnlockPixels(GetGWorldPixMap(theGWorld)) ;
 
            }
        }
    }
    else
    {
        SelectWindow( theFrontWindow ) ;
        SetPort( savedPort ) ;
    }   
}
 
/*------------------------------------------------------------ */
 
/*
 * handle menu commands in the view menu
 */
 
 
void HandleViewMenu( short menuItem )
{
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    WindowPtr           theWindow ;
    TQ3ViewerObject     theViewer ;
 
    unsigned long       theViewerFlags;
    Rect                theTmpRect ;
    GrafPtr             savedPort ;
    RGBColor            theRGBColor ;
    TQ3ColorARGB        theViewerBGColor;
 
    theWindow = FrontWindow() ;
    
    if( theWindow != NULL )
    {
        /*
         * get the reference to our viewer document data structure
         * from the long reference constant for the window.  Cast
         * it to the appropriate type.  If we can't get it (i.e. it's
         * null we want to bail
         */
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl == NULL) 
            return ;
        
        /* get the reference to the viewer object from our data structure  */   
        if((theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) == NULL)
            return ;
            
        /* get the reference to the viewer object from our data structure  */   
        theViewer = (**theViewerHdl).fViewer ;
        if(theViewer == NULL) 
            return ;
        
        /*
         * since most of the items in this menu rely on adjusting
         * the viewer flags, we'll get the flags here so we can
         * bitwise manipulate them in the switch below
         */ 
        theViewerFlags = Q3ViewerGetFlags( theViewer ) ;
    
        switch ( menuItem ) 
        {
            case iViewFinalRendererItem:
            
                HandleViewFinalRendererOption() ;
                break ;
 
            /*
             * the renderer item in this menu is handled
             * by handlers for it's respective hierarchical     
             * menus, so we just need to handle the ones below
             */ 
            
            case iViewBadgeItem:
                /* 
                 * this toggles the viewer bage and hides the
                 * vcontroller, clicking in the badge will
                 * hide the badge and show the controller
                 */
                theViewerFlags ^= kQ3ViewerShowBadge;
                theViewerFlags ^= kQ3ViewerControllerVisible;
                Q3ViewerSetFlags( theViewer, theViewerFlags ) ;
                break ;
                
            case iViewCameraButtonItem:
                theViewerFlags ^= kQ3ViewerButtonCamera ;
                Q3ViewerSetFlags( theViewer, theViewerFlags ) ;
                break ;
                
            case iViewTruckButtonItem:
                theViewerFlags ^= kQ3ViewerButtonTruck ;
                Q3ViewerSetFlags( theViewer, theViewerFlags ) ;
                break ;
                
            case iViewOrbitButtonItem:
                theViewerFlags ^= kQ3ViewerButtonOrbit ;
                Q3ViewerSetFlags( theViewer, theViewerFlags ) ;
                break ;
                
            case iViewZoomButtonItem:
                theViewerFlags ^= kQ3ViewerButtonZoom ;
                Q3ViewerSetFlags( theViewer, theViewerFlags ) ;
                break ;
                
            case iViewDollyButtonItem:
                theViewerFlags ^= kQ3ViewerButtonDolly ;
                Q3ViewerSetFlags( theViewer, theViewerFlags ) ;
                break ;
                
            case iViewInsetNFrameItem:
                /*
                 * this toggles drawing the viewer slightly smaller 
                 *than the window.  We key off the kQ3ViewerDrawFrame
                 * viewer flag - if it is set then we are already inset
                 */
                theTmpRect = theWindow->portRect ;
                if( theViewerFlags & kQ3ViewerDrawFrame ) 
                {
                    Q3ViewerSetBounds( theViewer, &theTmpRect ) ;
                }
                else
                {
                    InsetRect( &theTmpRect, kInsetPixelsConst, kInsetPixelsConst ) ;
                    Q3ViewerSetBounds( theViewer, &theTmpRect ) ;
                }
                theViewerFlags ^= kQ3ViewerDrawFrame ;
                Q3ViewerSetFlags( theViewer, theViewerFlags ) ;
                
                GetPort( &savedPort ) ;
                SetPort( (GrafPtr)theWindow ) ;
                EraseRect( &theWindow->portRect ) ;
                SetPort( savedPort ) ;
                
                break ;
            
            case iViewSetBackgroundColorItem:
    
                Q3ViewerGetBackgroundColor(theViewer, &theViewerBGColor);
                theRGBColor.red = theViewerBGColor.r * 65535.0;
                theRGBColor.green = theViewerBGColor.g * 65535.0;
                theRGBColor.blue = theViewerBGColor.b * 65535.0;
                
                if(BackgroundColor( &theRGBColor, "\pPick a viewer background color:" ))
                {
                    theViewerBGColor.a = 1;
                    theViewerBGColor.r = theRGBColor.red / 65535.0;
                    theViewerBGColor.g = theRGBColor.green / 65535.0;
                    theViewerBGColor.b = theRGBColor.blue / 65535.0;
                    Q3ViewerSetBackgroundColor(theViewer, &theViewerBGColor);
                }
                break;                              
        }
    }
    
    GetPort( &savedPort ) ;
    SetPort( (GrafPtr)theWindow ) ;
    InvalRect( &theWindow->portRect ) ;
    SetPort( savedPort ) ;
}
 
Boolean BackgroundColor(RGBColor *theRGBColor, unsigned char *thePrompt )
{
    ColorPickerInfo     cpInfo;
    Boolean             returnValue = false ;
    
    /* setting input color to be an RGB color  */
    cpInfo.theColor.color.rgb.red = (*theRGBColor).red;
    cpInfo.theColor.color.rgb.blue = (*theRGBColor).blue;
    cpInfo.theColor.color.rgb.green = (*theRGBColor).green;
    
    cpInfo.theColor.profile = 0L;
    
    /* no colorsync destination profile  */
    cpInfo.dstProfile = 0L;
    
    /* set the color picker flags  */
    cpInfo.flags = kColorPickerAppIsColorSyncAware | kColorPickerCanModifyPalette | 
                        kColorPickerCanAnimatePalette;
                        
    /* center dialog box on the deepest color screen  */
    cpInfo.placeWhere = kDeepestColorScreen;
    
    /* use the system default picker  */
    cpInfo.pickerType = 0L;
    
    /* install event filter and color-changed functions  */
    cpInfo.eventProc = nil;         
    cpInfo.colorProc = nil;
    cpInfo.colorProcData = 0L;
    
    /* sanity check  */
    if( thePrompt[ 0 ] >= 255 )
        thePrompt[ 0 ] = 255 ;
        
    BlockMove( thePrompt, cpInfo.prompt, thePrompt[0] ) ;
    
    /* describe the Edit menu for Color Picker Manager  */
    cpInfo.mInfo.editMenuID = mEditMenu ;
    cpInfo.mInfo.cutItem    = iEditCutItem;
    cpInfo.mInfo.copyItem   = iEditCopyItem;
    cpInfo.mInfo.pasteItem  = iEditPasteItem;
    cpInfo.mInfo.clearItem  = iEditClearItem;
    cpInfo.mInfo.undoItem   = iEditUndoItem;
 
    /* display dialog box to allow user to choose a color  */
    if(PickColor(&cpInfo) == noErr && cpInfo.newColorChosen)
    {
        /* use this new color  */
        (*theRGBColor).red = cpInfo.theColor.color.rgb.red ;
        (*theRGBColor).blue = cpInfo.theColor.color.rgb.blue ;
        (*theRGBColor).green = cpInfo.theColor.color.rgb.green ;
        
        /* set up the return value  */
        returnValue = cpInfo.newColorChosen ;
    }
        
    return returnValue ;
}
 
/*------------------------------------------------------------ */
 
/*
 * make sure that menus are enabled and disabled in an
 * appropriate manner
 */
 
 
void AdjustMenus( void ) 
{
    MenuHandle          theMenu ;
    DocumentHdl         theDocumentHdl ;
    WindowPtr           theWindow ;
    TQ3ViewerObject     theViewer ;
    TQ3ViewObject       myView ;
    TQ3Status           myStatus ;
    TQ3RendererObject   myRenderer ;
    TQ3ObjectType       theRendererType ;
    long                tmpLong, theScrapOffset ;
    unsigned long       theViewerFlags;
    unsigned long       theViewerState ;
    AdjustMenusProc     myAdjustMenus ;
    char                itemString[255] ;
    unsigned long       itemStringLength ;
 
    theWindow = FrontWindow() ;
    
    /* do we have a viewer window open  */
    if( theWindow != NULL ) {
        /*
         * get the reference to our viewer document data structure
         * from the long reference constant for the window.  Cast
         * it to the appropriate type.  If we can't get it (i.e. it's
         * null we want to bail
         */
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl == NULL) 
            return ;
            
        myAdjustMenus = (**theDocumentHdl).procs->adjustMenusP ;
        
        if( myAdjustMenus != NULL )
            (*myAdjustMenus)(theWindow) ;
    }
    else {
    
        /* there is NOT a front window, set defaults for menu commands */
    
        /* adjust the file menu */
        theMenu = GetMenuHandle ( mFileMenu ) ;
        
        EnableItem ( theMenu, iFileNewItem );       /* always enabled */
        EnableItem ( theMenu, iFileOpenItem );      /* always enabled */
        EnableItem ( theMenu, iFileQuitItem );      /* always enabled */
 
        DisableItem ( theMenu, iFileCloseItem );
        DisableItem ( theMenu, iFileSaveItem );
        DisableItem ( theMenu, iFileSaveAsItem );
        DisableItem ( theMenu, iFileRevertItem );
        DisableItem ( theMenu, iFilePageSetupItem );
        DisableItem ( theMenu, iFilePrintItem );
 
        /* adjust the edit menu */
        theMenu = GetMenuHandle ( mEditMenu ) ;
        DisableItem ( theMenu, 0L );
        
        /* disable the entire view menu if there is no window */
        theMenu = GetMenuHandle ( mViewMenu ) ;
        DisableItem ( theMenu, 0L );
 
    }
    DrawMenuBar() ;
}
 
/*------------------------------------------------------------  */
 
/* Determining the 3D Viewer version 
 *
 * Version 1.5 of the 3D viewer adds some new APIs and new features. 
 * In order for an application to determine at runtime that the version 
 * 1.5 functionality of the 3D viewer is available an application must 
 * check the version of the viewer. Unfortunately prior to version 1.5 
 * the 3D viewer did not have a gestalt or an API call to get its version. 
 * Version 1.5 of the viewer adds a call Q3ViewerGetVersion. But an 
 * application will want to first check that the Q3ViewerGetVersion 
 * symbol is resolved before calling the Q3ViewerGetVersion function 
 * on the chance the user has an older version of viewer shared library 
 * installed. An application which depends on the newer functionality 
 * will then need to take appropriate action such as displaying an alert 
 * or disabling specific features.
 */
 
OSErr GetViewerVersion( unsigned long *major, unsigned long *minor ) 
{ 
    
    /*
     * version 1.0 of the QuickDraw 3D viewer did not 
     * have a get version call, so check to see if the 
     * symbol for the API routine descriptor is loaded
     */ 
    if((Boolean)Q3ViewerGetVersion == kUnresolvedCFragSymbolAddress) 
    {
        *major = 1; 
        *minor = 0; 
        return noErr; 
    } 
    else 
    { 
        return Q3ViewerGetVersion(major, minor); 
    } 
}
 
 
/*------------------------------------------------------------  */
 
/*
 *  DoDrawGrowIcon 
 *  
 *  Draw the grow icon. We do this in such a way that the scrollbar lines are 
 *  not drawn. Normally, when the Toolbox routine DrawGrowIcon is called, 
 *  vertical and horizontal lines are also drawn indicating where the 
 *  scrollbars will be drawn. We donÕt have scrollbars, so we donÕt want those 
 *  lines drawn. We avoid them by setting the clipping region of the window to 
 *  include the grow box only. We then call DrawGrowIcon, and restore the old 
 *  clipping region before returning.
 *
 *  Make sure that this gets called after the control strip for
 *  a window is drawn. 
 */
 
void    DoDrawGrowIcon(WindowPtr theWindow)
{
    Rect        iconRect;
    RgnHandle   oldClip;
    
 
    SetPort(theWindow);
    oldClip = NewRgn();
    GetClip(oldClip);
 
    iconRect = theWindow->portRect;
    iconRect.top = iconRect.bottom - 15;
    iconRect.left = iconRect.right - 15;
    ClipRect(&iconRect);
 
    PenNormal();
    DrawGrowIcon(theWindow);
 
    SetClip(oldClip);
    DisposeRgn(oldClip);
}
 
 
/*------------------------------------------------------------ */
 
/*
 * create a new viewer document window, this includes creating a
 * document record, a window and an associated viewer object.  the
 * document record is stored in the window's refcon field, so it
 * can be accessed as required via the window record.
 */
 
WindowPtr DoCreateNewViewerWindow( unsigned char *windowName )
{
    WindowPtr           theWindow ;
    Rect                myRect = { 0, 0, kWindHeight, kWindWidth } ;
    TQ3ViewerObject     myViewer ;
    DocumentHdl         myViewerDocument = NULL ;
    
    /* create a document record to hold the  */
    /* data for this instance  */
    if((myViewerDocument = (DocumentHdl)NewHandleClear(sizeof(Document))) == NULL)
        return NULL ;
    
    
    /* ideally we should stagger the rect  */
    OffsetRect( &myRect, 50, 50 ) ; 
                      
    theWindow = NewCWindow( NULL, 
                         &myRect, 
                         windowName, 
                         true, 
                         documentProc, 
                         (WindowPtr)-1, 
                         true, 
                         0L ) ;
                         
    /* create the viewer object associated with this window  */                   
    if((myViewer = Q3ViewerNew( (CGrafPtr)theWindow, 
                                &theWindow->portRect, 
                                kQ3ViewerDefault )) != NULL) 
    {
        ViewerDataHdl       myViewerData = (ViewerDataHdl)NewHandleClear(sizeof(ViewerData)) ;
        if(myViewerData == NULL)
        {
            DisposeHandle( (Handle)myViewerDocument ) ;
            return NULL ;
        }
        
        /* put the magic cookie in the first field so we can identify this as a valid viewer */
        (**myViewerDocument).fDocumentMagic = kViewerMagic ;
        
        /* set up the procs field */
        (**myViewerDocument).procs = &viewerProcs ;
        
        /* set up the private data part of the document structure */
        (**myViewerData).fViewer = myViewer ;
        
        /* stuff the handle in the viewerdata field */
        (**myViewerDocument).fPrivate = (void *) myViewerData ;
        
        /* store a reference to the document structure in the refcon
         * field of the window  
         */
        SetWRefCon( theWindow, (long)myViewerDocument ) ;
 
        /* finally create a new print record */
        DoCreatePrintRecord( myViewerDocument ) ;
 
    }
    else
    {
        /* clean up any allocates storage and quit  */
        if( myViewerDocument )
            DisposeHandle( (Handle)myViewerDocument ) ;
        
        if( theWindow ) 
            CloseWindow( theWindow ) ;
        
        theWindow = NULL ;
    }
    
    return theWindow ;
}
 
/*------------------------------------------------------------ */
 
/*
 * handles the New menu item in the file menu
 */
 
 
WindowPtr ViewerWindow_New( unsigned char *windowTitle )
{
    WindowPtr   theWindow ; 
    theWindow = DoCreateNewViewerWindow( windowTitle ) ;
    return theWindow ;
}
    
/*------------------------------------------------------------ */
 
/*
 * handles the Save As menu item in the file menu
 */
 
 
OSErr ViewerWindow_SaveAs( WindowPtr theWindow )
{
    OSErr               theError = paramErr ;
    short               theRef ;
 
    TQ3ViewerObject     theViewer ;
    StandardFileReply   theSFReply ;
    DocumentHdl         theDocumentHdl ;
 
 
    /* this option can't be selected unless there is a frontwindow  */
    /* the option is dimmed in adjustmenus if there is no window  */
    if( theWindow != NULL ) /* sanity check  */
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL
            && (**theDocumentHdl).fDocumentMagic == kViewerMagic ) 
        {
            StandardPutFile("\pSave model as:", "\pUntitled", &theSFReply);
            if (theSFReply.sfGood)
            {
                theError = FSpOpenDF(&theSFReply.sfFile, fsWrPerm, &theRef);
                if (theError != noErr)
                {
                    theError = FSpCreate(&theSFReply.sfFile, '????', '3DMF', theSFReply.sfScript);
                    if (theError == noErr)
                        theError = FSpOpenDF(&theSFReply.sfFile, fsCurPerm, &theRef);
                }
                if (theError == noErr)
                {
                    /* get the viewer object from the document */
                    ViewerDataHdl       theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate) ;
                    if( theViewerHdl != NULL )
                    {
                        HLock((Handle)theViewerHdl) ;
                        (**theViewerHdl).fFSSpec = theSFReply.sfFile ;
                        theViewer= (**theViewerHdl).fViewer ;
                        theError = Q3ViewerWriteFile(theViewer, (long)theRef);
                        theError = FSClose(theRef);
                        HUnlock((Handle)theViewerHdl) ;
                    }
                }
                
                /* reset the window title  */
                SetWTitle( theWindow, theSFReply.sfFile.name );
            }                   
        }
    }
    return theError ;
}
 
/*------------------------------------------------------------ */
 
/*
 * handles the Save menu item in the file menu
 */
 
 
OSErr ViewerWindow_Save( WindowPtr theWindow )
{
    OSErr               theError = paramErr ;
    short               theRef ;
 
    TQ3ViewerObject     theViewer ;
 
    DocumentHdl theDocumentHdl ;
    FSSpec              theFSSpec ;
 
    /* this option can't be selected unless there is a frontwindow  */
    /* the option is dimmed in adjustmenus if there is no window  */
    if( theWindow != NULL )     /* sanity check  */
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL  
            && (**theDocumentHdl).fDocumentMagic == kViewerMagic) 
        {
            /* get the viewer object from the document */
            ViewerDataHdl       theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate) ;
            if( theViewerHdl != NULL )
            {
                HLock((Handle)theViewerHdl) ;
                theFSSpec = (**theViewerHdl).fFSSpec ;
                /* open the file  */
                theError = FSpOpenDF ( &theFSSpec, fsWrPerm, &theRef ) ;
                if( theError == noErr ) 
                {
                    if((theViewer = (**theViewerHdl).fViewer) != NULL) 
                    {
                        theError = Q3ViewerWriteFile( theViewer, (long)theRef );
                        if( theError == noErr )
                            theError = FSClose ( theRef ) ;
                    }
                }
                HUnlock((Handle)theViewerHdl) ;
            }
        }
    }
    return theError ;
            
}
        
/*------------------------------------------------------------ */
 
/*
 * handles the Revert menu item in the file menu
 */
 
 
OSErr ViewerWindow_Revert( WindowPtr theWindow )
{
    OSErr               theError = paramErr ;
    short               theRef ;
    GrafPtr             savedPort ;
 
    TQ3ViewerObject     theViewer ;
 
    DocumentHdl theDocumentHdl ;
    FSSpec              theFSSpec ;
 
    /* this option can't be selected unless there is a frontwindow  */
    /* the option is dimmed in adjustmenus if there is no window  */
    if( theWindow != NULL )     /* sanity check  */
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL
            && (**theDocumentHdl).fDocumentMagic == kViewerMagic) 
        {
            /* get the viewer object from the document */
            ViewerDataHdl       theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate) ;
            if( theViewerHdl != NULL )
            {
                HLock((Handle)theViewerHdl) ;
                theFSSpec = (**theViewerHdl).fFSSpec ;
                
                /* open the file  */
                theError = FSpOpenDF ( &theFSSpec, fsRdPerm, &theRef ) ;
                if( theError == noErr ) 
                {
                    if((theViewer = (**theViewerHdl).fViewer) != NULL) 
                    {
                        theError = Q3ViewerUseFile( theViewer, (long)theRef );
                        if( theError == noErr )
                            theError = FSClose ( theRef ) ;
                    }
                }
 
                HUnlock((Handle)theViewerHdl) ;
            }
        }
    }
    GetPort( &savedPort ) ;
    SetPort( (GrafPtr)theWindow ) ;
    InvalRect( &theWindow->portRect ) ;
    SetPort( savedPort ) ;
 
    return theError ;
}
 
 
    
WindowPtr ViewerWindow_Open( FSSpec *theFSSpec )
{
    OSErr               theError ;
    short               theRef ;
    WindowPtr           theWindow ;
    TQ3ViewerObject     theViewer ;
    DocumentHdl theDocumentHdl ;
 
    /* open the file  */
    theError = FSpOpenDF( theFSSpec, fsRdPerm, &theRef ) ;
    
    if( theError == noErr )
    {                   
        theWindow = DoCreateNewViewerWindow( theFSSpec->name) ;
        
        if( theWindow != NULL )
        {
            
            theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
            if(theDocumentHdl != NULL 
                && (**theDocumentHdl).fDocumentMagic == kViewerMagic) 
            {
                /* get the viewer object from the document */
                ViewerDataHdl       theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate) ;
                if( theViewerHdl != NULL )
                {
                    HLock((Handle)theViewerHdl) ;
                    if((theViewer = (**theViewerHdl).fViewer) != NULL) 
                    {
                        (**theViewerHdl).fFSSpec = *theFSSpec ;
                        theError = Q3ViewerUseFile( theViewer, theRef );
                        if( theError == noErr )
                            theError = FSClose ( theRef ) ;
                    }
                    HUnlock((Handle)theViewerHdl) ;
                }
            }
        }
    }
    return theWindow ;
}   
        
/*------------------------------------------------------------ */
 
/*
 * handles the close menu item in the file menu
 * close the window after disposing of the associated
 * data structures (viewer, etc).
 */
 
 
OSErr ViewerWindow_Close( WindowPtr theWindow )
{
 
    OSErr               theError = paramErr ;
 
 
    TQ3ViewerObject     theViewer ;
 
    DocumentHdl theDocumentHdl ;
 
 
    /* this option can't be selected unless there is a frontwindow  */
    /* the option is dimmed in adjustmenus if there is no window  */
    if( theWindow != NULL )     /* sanity check  */
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL 
            && (**theDocumentHdl).fDocumentMagic == kViewerMagic) 
        {
            /* get the viewer object from the document */
            ViewerDataHdl       theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate) ;
 
            /* dispose of the print record for the document */
            THPrint thePrintRec = (**theDocumentHdl).fPrintRec ;
            if( thePrintRec != NULL )
                DisposeHandle((Handle)thePrintRec ) ;
 
            if( theViewerHdl != NULL )
            {
                HLock((Handle)theViewerHdl) ;
                if((theViewer = (**theViewerHdl).fViewer) != NULL) 
                {
                    theError = Q3ViewerDispose( theViewer ) ;
                    if( theError == noErr )
                        DisposeWindow( theWindow ) ;
                }
                HUnlock((Handle)theViewerHdl) ;
            }
        }
    }
    return theError ;
            
}
/*------------------------------------------------------------ */
 
/*
 * handle event for a viewer window
 */
 
 
Boolean ViewerWindow_HandleEvent( WindowPtr theWindow, const EventRecord *theEventRecord)
{
 
    GrafPtr             savedPort ;
    short               thePart;
    Rect                screenRect;
    Rect                growRect ;
    Point               aPoint = {100, 100};
    GrafPtr             oldPort ;
    Boolean             eventWasHandled ;
    unsigned long       width ;
    unsigned long       height ;
    long                newSize ;
    OSErr               theErr ;
    Point               mouseLoc ;
    TQ3Boolean          handledEvent = kQ3False ;
    TQ3ViewerObject     theViewer = NULL ;
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    
    /* 
     * check to see if the event is a viewer event, also since
     * the update event processing needs to be done between begin update
     * and end update, don't handle updates here for viewer objects
     */
    if( theWindow != NULL && theEventRecord->what != updateEvt)
    {       
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL) 
        {
            if((**theDocumentHdl).fDocumentMagic == kViewerMagic 
                && ((theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate))) != NULL
                && (theViewer = (**theViewerHdl).fViewer) != NULL ) 
            {
                /*
                 * There is a bug in versions 1.0.4 and earlier of the Viewer,
                 * so the port has to be set and restored.
                 */
                 
                GetPort( &oldPort ) ;
                SetPort((GrafPtr)theWindow ) ;
 
                eventWasHandled = Q3ViewerEvent( theViewer, (EventRecord *)theEventRecord );
                DoDrawGrowIcon(theWindow) ;
                GetMouse( &mouseLoc ) ;
                Q3ViewerAdjustCursor( theViewer,  &mouseLoc );
                
                /* restore the port */
                SetPort( oldPort ) ;
                
                /* 
                 * if the window is not the frontmost 
                 * window, give the eventhandler 
                 * a chance to process the event.
                 */
                if(theWindow != FrontWindow())
                    eventWasHandled = false ;
                    
            }
        }
        
    }
    else
    {
        eventWasHandled = false ;
    }
    return eventWasHandled ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * handle update for a viewer window
 */
 
void    ViewerWindow_Update(WindowPtr  theWindow)
{
    GrafPtr             oldPort ;
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    OSErr               theErr ;
    
    GetPort( &oldPort ) ;   
    SetPort( theWindow );
    BeginUpdate( theWindow );
    {
        theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
        if(theDocumentHdl != NULL
            && (**theDocumentHdl).fDocumentMagic == kViewerMagic
            && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
        {
            if((theViewer = (**theViewerHdl).fViewer) != NULL) 
            {
                theErr = Q3ViewerDraw( theViewer );
                DoDrawGrowIcon(theWindow) ;
            }
        }
    }
    EndUpdate( theWindow );
    SetPort( oldPort ) ;
}
 
/*------------------------------------------------------------ */
 
/*
 * make sure that menus are enabled and disabled in an
 * appropriate manner for the case where the front window 
 * is a viewer window.
 */
 
void ViewerWindow_AdjustMenus( WindowPtr  theWindow )
{
        
    MenuHandle          theMenu ;
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    TQ3ViewObject       myView ;
    TQ3Status           myStatus ;
    TQ3RendererObject   myRenderer ;
    TQ3ObjectType       theRendererType ;
    long                tmpLong, theScrapOffset ;
    unsigned long       theViewerFlags;
    unsigned long       theViewerState ;
    
    char                itemString[255] ;
    unsigned long       itemStringLength ;
 
    /*
     * get the reference to our viewer document data structure
     * from the long reference constant for the window.  Cast
     * it to the appropriate type.  If we can't get it (i.e. it's
     * null we want to bail
     */
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl == NULL) 
        return ;
        
    /* get the reference to the viewer object from our data structure  */   
    if((theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) == NULL)
        return ;
    
    /* get the reference to the viewer object from our data structure  */   
    theViewer = (**theViewerHdl).fViewer ;
    if(theViewer == NULL) 
        return ;
        
    /* get the viewer state, we need to know if it is empty  */ 
    theViewerState = Q3ViewerGetState( theViewer ) ;
    
    /* adjust the file menu  */
    theMenu = GetMenuHandle ( mFileMenu ) ;
    
    EnableItem ( theMenu, iFileNewItem );       /* always enabled  */
    EnableItem ( theMenu, iFileOpenItem );      /* always enabled  */
    EnableItem ( theMenu, iFileQuitItem );      /* always enabled  */
 
    EnableItem ( theMenu, iFileCloseItem );
    
    if( ((theViewerState & kQ3ViewerHasModel) ? true : false ) ) 
    {
        EnableItem ( theMenu, iFileSaveItem );
        EnableItem ( theMenu, iFileSaveAsItem );
        EnableItem ( theMenu, iFileRevertItem );
        EnableItem ( theMenu, iFilePageSetupItem );
        EnableItem ( theMenu, iFilePrintItem );
    }
    else
    {
        DisableItem ( theMenu, iFileSaveItem );
        DisableItem ( theMenu, iFileSaveAsItem );
        DisableItem ( theMenu, iFileRevertItem );
        DisableItem ( theMenu, iFilePageSetupItem );
        DisableItem ( theMenu, iFilePrintItem );
    }
    
    
    /* adjust the edit menu  */
    theMenu = GetMenuHandle ( mEditMenu ) ;
    EnableItem ( theMenu, 0L );
    
    if( ((theViewerState & kQ3ViewerHasUndo) ? true : false ) ) 
    {
        /* undo is possible, get the string for this item and enable it */
        Boolean     canUndo ;
        
        /*
         * Hokeyness alert.  We pass in the address of the second element of 
         * the itemString array, allowing us to set the length later in the
         * first element of the array, saving us the need to to an inplace
         * C to P string conversion (the Mac toolbox routines require a 
         * pascal format string that has the length as the first byte.
         */
        canUndo = Q3ViewerGetUndoString( theViewer, &itemString[1], &itemStringLength );
        itemString[0] = (char)itemStringLength ;
                        
        /* if we can undo then enable the new string, else use the default cant undo string */
        if( canUndo == true && itemStringLength > 0 )
        {   
            SetMenuItemText ( theMenu, iEditUndoItem, (unsigned char *)itemString ) ;
            EnableItem ( theMenu, iEditUndoItem );
        }   
        else 
        {
            GetIndString ( (unsigned char *)itemString, 2223, 1 ) ;
            SetMenuItemText ( theMenu, iEditUndoItem, (unsigned char *)itemString ) ;
            DisableItem ( theMenu, iEditUndoItem ); 
        }
    }
    else
    {
        GetIndString ( (unsigned char *)itemString, 2223, 1 ) ;
        SetMenuItemText ( theMenu, iEditUndoItem, (unsigned char *)itemString ) ;
        DisableItem ( theMenu, iEditUndoItem ); 
    }
 
    if( ((theViewerState & kQ3ViewerHasModel) ? true : false ) ) 
    {
        EnableItem ( theMenu, iEditCutItem );
        EnableItem ( theMenu, iEditCopyItem );
        EnableItem ( theMenu, iEditClearItem );
    }
    else
    {
        DisableItem ( theMenu, iEditCutItem );
        DisableItem ( theMenu, iEditCopyItem );
        DisableItem ( theMenu, iEditClearItem );
    }
    
    
    /*
     * check that there is some data that we can paste.  GetScrap returns
     * a long that gives either the length of the requested type, or a
     * negative error code that is used to indicate that no such type exists
     */
    
    tmpLong = GetScrap( nil, '3DMF', &theScrapOffset ) ;
    if( tmpLong < 0 )
        DisableItem ( theMenu, iEditPasteItem );
    else
        EnableItem ( theMenu, iEditPasteItem );
        
 
    
    /* adjust the view menu  */
    theMenu = GetMenuHandle ( mViewMenu ) ;
    EnableItem ( theMenu, 0L );
    
    /*
     * since most of the items in this menu rely on adjusting
     * the viewer flags, we'll get the flags here so we can
     * bitwise manipulate them in the switch below
     */ 
    theViewerFlags = Q3ViewerGetFlags( theViewer ) ;
    
    /*
     * check each of the items that have their flag
     * bits set to true in the viewer flags.
     *
     * Note that the Boolean flag for CheckItem is defined
     * (( theViewerFlags & kQ3ViewerShowBadge ) ? true : false)
     * and so on.  The reson for this is that simply adding
     * ( theViewerFlags & kQ3ViewerShowBadge ) won't be sufficient
     * if the flag is the 8th bit or more of the unsigned long 
     * defined to hold the flages, since the param passed will 
     * only be the first 8 bits (of couse this is dependent on
     * the develolment system size for Boolean, but this is true
     * for CodeWarrior.
     */
    
    CheckItem( theMenu, iViewBadgeItem, (( theViewerFlags & kQ3ViewerShowBadge ) ? true : false)) ;
    CheckItem( theMenu, iViewCameraButtonItem, (( theViewerFlags & kQ3ViewerButtonCamera ) ? true : false)) ;
    CheckItem( theMenu, iViewTruckButtonItem, (( theViewerFlags & kQ3ViewerButtonTruck ) ? true : false)) ;
    CheckItem( theMenu, iViewOrbitButtonItem, (( theViewerFlags & kQ3ViewerButtonOrbit ) ? true : false)) ;
    CheckItem( theMenu, iViewZoomButtonItem, (( theViewerFlags & kQ3ViewerButtonZoom ) ? true : false)) ;
    CheckItem( theMenu, iViewDollyButtonItem, (( theViewerFlags & kQ3ViewerButtonDolly ) ? true : false)) ;
    CheckItem( theMenu, iViewInsetNFrameItem, (( theViewerFlags & kQ3ViewerDrawFrame ) ? true : false)) ;
    
    /* adjust the renderer menu  */
    theMenu = GetMenuHandle ( mInteractiveRendererMenu ) ;
    
    /*
     * get the renderer for the view
     */
    myView = Q3ViewerGetView( theViewer );
    if( myView != NULL )
    {
        long                    rendererIndex = 0 ;
        MenuHandle              tempMenu ;
        
        /* set the renderer to the one created in the switch statement above */
        myStatus = Q3View_GetRenderer(myView, &myRenderer) ;
        theRendererType = Q3Renderer_GetType( myRenderer ) ;
        
        for( rendererIndex = 0; pInteractiveTypes[ rendererIndex ] ; rendererIndex++ ) 
        {
            CheckItem( theMenu, rendererIndex+1,  (theRendererType == pInteractiveTypes[rendererIndex])) ;
        }
        
        /*
         * Update the edit renderer prefs "edit menu" item.  Should probably be done with 
         * the edit menu stuff, I put this here because we have a reference
         * to the renderer here
         */
                
        /* enable the menu item appropriately */
        tempMenu = GetMenuHandle ( mEditMenu ) ;
        if( Q3Renderer_HasModalConfigure(myRenderer) )
            EnableItem ( tempMenu, iEditRendererPrefsItem );
        else
            DisableItem ( tempMenu, iEditRendererPrefsItem );
 
        /* 
         * getting the renderer incremented the ref count for it,
         * we can dispose of the reference to it 
         */
        myStatus = Q3Object_Dispose( myRenderer ) ;         
    }
}
 
/*------------------------------------------------------------ */
 
/*
 * ViewerWindow_CountPages - calculate the number of pages needed to
 * image the document associated with the viewer window.  For now we
 * assume 1.
 */
 
 
short   ViewerWindow_CountPages( 
    WindowPtr   theWindow, 
    Rect        *pageRect ) 
{
    Rect            pictRect ;
    short           horizOffset, 
                    vertOffset,
                    pagesWide = 0,  /*  the number of pages wide the image is */
                    pagesHigh = 0,  /*  the number pages high for this image */
                    pageWidth,      /*  the width of one page */
                    pageHeight ;    /*  the height of one page */
 
    DocumentHdl     theDocumentHdl ;
    ViewerDataHdl   theViewerHdl ;
    GWorldPtr       theGWorld ;
    OSErr           theErr ;
    
    short           retVal = 0 ;
    
    PicHandle       thePicture ;
    
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theGWorld = (**theViewerHdl).fGWorld) != NULL) 
        {
            retVal = GeneralCountPages( theGWorld, pageRect ) ;
        }
    }
    
    return retVal ;
}
 
/*------------------------------------------------------------ */
 
/*
 * ViewerWindow_PrintPage - image the requested page in the 
 * printer port
 */
 
void    ViewerWindow_PrintPage( 
    WindowPtr       theWindow, 
    Rect            *pageRect, 
    GrafPtr         imagingPort, 
    short           pageNum ) 
{
    GWorldPtr       theGWorld;
    Rect            pictRect, rectToPrint,srcRect,dstRect;
    short           pagesWide,      /*  the number of pages wide the image is */
                    pagesHigh,      /*  the number pages high for this image */
                    horozTile,      /*  used in the loop to denote the H tile to print from the image */
                    vertTile ;      /*  used in the loop to denote the V tile to print from the image */
    short           thisPage = 1;   /*  used to find the page they want us to print */
    short           pictHOff,
                    pictVOff,
                    pictWidth,      /*  the width of the doc's GWorld */
                    pictHeight ;    /*  the height of the doc's GWorld */
    short           pageWidth,      /*  the width of one page */
                    pageHeight ;    /*  the height of one page */
    OSErr           theErr ;
    PixMapHandle    offPixMap ;
    DocumentHdl     theDocumentHdl ;
    ViewerDataHdl   theViewerHdl ;
    TQ3ViewerObject theViewer ;
 
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theGWorld = (**theViewerHdl).fGWorld) != NULL) 
        {
            GeneralPrintPage( theGWorld, pageRect, imagingPort, pageNum ) ;
        }
    }
}
 
 
/*------------------------------------------------------------ */
 
/*
 * ViewerWindow_PrePrint - this gets called before we print, so any
 * setup for printing can be done here.
 */
void ViewerWindow_PrePrint( WindowPtr theWindow )
{
    /*
     * before we print we want to get a pixmap that represents the 
     * current document being imaged.  We use Q3ViewerGetPict to 
     * do this and use the fGWorld field of the viewer's data
     * to store this until it's been imaged.  Not efficient, but
     * that's the price of using the viewer.
     */
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theViewer = (**theViewerHdl).fViewer) != NULL) 
        {
            PicHandle       thePicture ;
                        GWorldPtr       theGWorld ;
            Rect            pictRect ;
            OSErr           theErr ;
            GrafPtr         savedPort ;
 
            GetPort( &savedPort ) ;
            SetPort( (GrafPtr) theWindow ) ;
            
            thePicture = Q3ViewerGetPict( theViewer ) ;
            
            SetPort( savedPort ) ;
            
            if( thePicture != NULL )
            {
                pictRect.top    = (**thePicture).picFrame.top ;
                pictRect.left   = (**thePicture).picFrame.left ;
                pictRect.bottom = (**thePicture).picFrame.bottom ;
                pictRect.right  = (**thePicture).picFrame.right ;
                
                /* make sure the GWorld's origin is (0,0) */
                OffsetRect( &pictRect, -pictRect.left, -pictRect.top ) ;
                
                /* make a new offscreen world */
                theErr =  NewGWorld( &theGWorld, 32, &pictRect, NULL, NULL, 0L );
                if( theErr == noErr && theGWorld != NULL )
                {
                    PixMapHandle            offPixMap ;
                    CGrafPtr                savedPort ;
                    GDHandle                gdh ;
                    
                    /* image the picture into the offscreen */
                    GetGWorld( &savedPort, &gdh ) ;
                    SetGWorld( theGWorld, NULL ) ;
                    
                    /* get the gworld pixmap and lock it down */
                    offPixMap = GetGWorldPixMap( theGWorld ) ;
                    (void) LockPixels( offPixMap ) ;
                
                    /* draw the Picture in the port */
                    DrawPicture( thePicture, &theGWorld->portRect ) ;
                    
                    /* unlock the pixmap */
                    (void) UnlockPixels( offPixMap ) ;
                        
                    SetGWorld( savedPort, gdh ) ;
                    (**theViewerHdl).fGWorld = theGWorld;
                
                }
                else
                {
                    (**theViewerHdl).fGWorld = NULL;
                }
                
                /* ditch the pict */
                KillPicture( thePicture ) ;
 
            }
            else
            {
                (**theViewerHdl).fGWorld = NULL ;
            }
        }
    }
     
    return ; 
}
 
 
/*------------------------------------------------------------ */
 
/*
 * ViewerWindow_PostPrint - this gets called before we print, so any
 * setup for printing can be done here.
 */
void ViewerWindow_PostPrint( WindowPtr theWindow )
{
    /* delete the cached pict */
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
 
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        GWorldPtr       theGWorld = (**theViewerHdl).fGWorld ;
        
        /* get rid of the gworld */
        DisposeGWorld(theGWorld);
        (**theViewerHdl).fGWorld = NULL ;
    }
     
    return ; 
}
 
/*------------------------------------------------------------ */
 
/*
 * Handle the edit menu cut command for this window type
 */
OSErr       ViewerWindow_Cut( WindowPtr theWindow ) 
{
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    OSErr               theError = paramErr ;
 
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theViewer = (**theViewerHdl).fViewer) != NULL) 
        {
            theError = Q3ViewerCut ( theViewer ) ;
        }
    }
     
    return theError ; 
 
}
 
/*------------------------------------------------------------ */
 
/*
 * Handle the edit menu copy command for this window type
 */
OSErr       ViewerWindow_Copy( WindowPtr    theWindow ) 
{
 
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    OSErr               theError = paramErr ;
 
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theViewer = (**theViewerHdl).fViewer) != NULL) 
        {
            theError = Q3ViewerCopy ( theViewer ) ;
        }
    }
     
    return theError ; 
 
}
 
/*------------------------------------------------------------ */
 
/*
 * Handle the edit menu paste command for this window type
 */
OSErr       ViewerWindow_Paste( WindowPtr   theWindow ) 
{
 
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    OSErr               theError = paramErr ;
 
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theViewer = (**theViewerHdl).fViewer) != NULL) 
        {
            theError = Q3ViewerPaste ( theViewer ) ;
        }
    }
     
    return theError ; 
 
}
 
/*------------------------------------------------------------ */
 
/*
 * Handle the edit menu clear command for this window type
 */
OSErr       ViewerWindow_Clear( WindowPtr   theWindow )
{
 
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    OSErr               theError = paramErr ;
 
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theViewer = (**theViewerHdl).fViewer) != NULL) 
        {
            theError = Q3ViewerClear ( theViewer ) ;
        }
    }
     
    return theError ; 
 
}
 
/*------------------------------------------------------------ */
 
/*
 * Handle the edit menu undo command for this window type
 */
OSErr       ViewerWindow_Undo( WindowPtr    theWindow )
{
 
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    OSErr               theError = paramErr ;
 
 
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kViewerMagic
        && (theViewerHdl = (ViewerDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theViewer = (**theViewerHdl).fViewer) != NULL) 
        {
            theError = Q3ViewerUndo ( theViewer ) ;
        }
    }
     
    return theError ; 
 
}
 
/*------------------------------------------------------------ */
 
/*
 * Get an output reference for a PICT file
 */
OSErr GetOutputPictFileRef(short    *dstPictFRef ) 
{
    StandardFileReply   aPictSFR;   /* new style Standard file record */
    OSErr               result ;
    SFTypeList  types = { 'PICT',0 };   /* we are only interested in PICT files */
        
    StandardPutFile( "\pSave PICT as:", "\pNew PICT", &aPictSFR ) ;
    if ( aPictSFR.sfGood  ) {
        
        /* 
         * Delete the file of the same name, if one exits, 
         * and create a new PICT file.
         */
        
        FSpDelete(&aPictSFR.sfFile);
        
        result = FSpCreate( &aPictSFR.sfFile,'????','PICT', aPictSFR.sfScript);
        if (result != noErr) {
            return result;
        }
        
        /* open the data fork for writing */
        
        result = FSpOpenDF( &aPictSFR.sfFile, fsCurPerm, dstPictFRef);
        if (result != noErr) {
            return result;
        }
    }
    else 
        return userCanceledErr ;
        
    return noErr ;
}
 
/*------------------------------------------------------------ */
 
/*
 * create a new viewer document window, this includes creating a
 * document record, a window and an associated viewer object.  the
 * document record is stored in the window's refcon field, so it
 * can be accessed as required via the window record.
 */
 
WindowPtr DoCreateNewPictWindow( unsigned char *windowName, long windWidth, long windHeight )
{
    WindowPtr           theWindow ;
    Rect                myRect ;
    TQ3ViewerObject     myViewer ;
    DocumentHdl         myPictDocument = NULL ;
    GWorldPtr           myGWorld ;
    PictDataHdl     myViewerPict ;
    
    myRect.top = 0 ;
    myRect.left = 0 ;
    myRect.bottom = windHeight ;
    myRect.right = windWidth ;
 
    /* create a document record to hold the  */
    /* data for this instance  */
    if((myPictDocument = (DocumentHdl)NewHandleClear(sizeof(Document))) == NULL)
        return NULL ;
    
    /* make a GWorld the size requested */
    if(NewGWorld ( &myGWorld, 32, &myRect, NULL, NULL, 0L ) != noErr)
        return NULL ;
        
    
    /* ideally we should stagger the window rect  */
    
    OffsetRect( &myRect, 50, 50 ) ; 
                      
    theWindow = NewCWindow( NULL, 
                         &myRect, 
                         windowName, 
                         true, 
                         documentProc, 
                         (WindowPtr)-1, 
                         true, 
                         0L ) ;
                         
    myViewerPict = (PictDataHdl)NewHandleClear(sizeof(PictData)) ;
    if(myViewerPict == NULL)
    {
        DisposeHandle( (Handle)myPictDocument ) ;
        return NULL ;
    }
        
    /* put the magic cookie in the first field so we can identify this as a valid pict */
    (**myPictDocument).fDocumentMagic = kPICTMagic ;
    
    /* set up the procs field */
    (**myPictDocument).procs = &pictProcs ;
    
    /* set up the private data part of the document structure */
    (**myViewerPict).fGWorld = myGWorld ;
    
    (**myPictDocument).fPrivate = (void *)myViewerPict ;
    
    /* finally create a new print record */
    DoCreatePrintRecord( myPictDocument ) ;
 
    /* store a reference to the document structure in the refcon
     * field of the window  
     */
    SetWRefCon( theWindow, (long)myPictDocument ) ;
    
    return theWindow ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_Update
 */
void            PictWindow_Update( WindowPtr  theWindow ) 
{
    GrafPtr             oldPort ;
    DocumentHdl         theDocumentHdl ;
    PictDataHdl         thePictDataHdl ;
    GWorldPtr           theGWorld ;
    OSErr               theErr ;
    
    GetPort( &oldPort ) ;   
    SetPort( theWindow );
    BeginUpdate( theWindow );
 
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kPICTMagic
        && (thePictDataHdl = (PictDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theGWorld = (**thePictDataHdl).fGWorld) != NULL) 
        {
            /* copybits from the GWorld here */
            
            CopyBits ((BitMapPtr) &theGWorld->portPixMap,
                      &theWindow->portBits,
                      &theGWorld->portRect, 
                      &theWindow->portRect,
                      srcCopy, 
                      0L);
            DoDrawGrowIcon(theWindow) ;
        }
    }
    
    EndUpdate( theWindow );
    SetPort( oldPort ) ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_AdjustMenus
 */
void            PictWindow_AdjustMenus( WindowPtr  theWindow ) 
{
        
    MenuHandle          theMenu ;
    DocumentHdl         theDocumentHdl ;
    ViewerDataHdl       theViewerHdl ;
    TQ3ViewerObject     theViewer ;
    TQ3ViewObject       myView ;
    TQ3Status           myStatus ;
    TQ3RendererObject   myRenderer ;
    TQ3ObjectType       theRendererType ;
    long                tmpLong, theScrapOffset ;
    unsigned long       theViewerFlags;
    unsigned long       theViewerState ;
    
    char                itemString[255] ;
    unsigned long       itemStringLength ;
 
    /*
     * get the reference to our Pict document data structure
     * from the long reference constant for the window.  Cast
     * it to the appropriate type.  If we can't get it (i.e. it's
     * null we want to bail
     */
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl == NULL) 
        return ;
        
    /* adjust the file menu  */
    theMenu = GetMenuHandle ( mFileMenu ) ;
    
    EnableItem ( theMenu, iFileNewItem );       /* always enabled  */
    EnableItem ( theMenu, iFileOpenItem );      /* always enabled  */
    EnableItem ( theMenu, iFileQuitItem );      /* always enabled  */
 
    EnableItem ( theMenu, iFileCloseItem );
    
    /* 
     * this only gets called if we have a pict window in the first 
     * place so enable saving and printing...
     */
    EnableItem ( theMenu, iFileSaveItem );
    EnableItem ( theMenu, iFileSaveAsItem );
    EnableItem ( theMenu, iFileRevertItem );
    EnableItem ( theMenu, iFilePageSetupItem );
    EnableItem ( theMenu, iFilePrintItem );
 
    /* adjust the edit menu  */
    theMenu = GetMenuHandle ( mEditMenu ) ;
    EnableItem ( theMenu, 0L );
    
    /* 
     * there's not really anything to undo with this window type so set 
     * the undo string to "Can't Undo"
     */
    GetIndString ( (unsigned char *)itemString, 2223, 1 ) ;
    SetMenuItemText ( theMenu, iEditUndoItem, (unsigned char *)itemString ) ;
    
    
    DisableItem ( theMenu, iEditUndoItem ); 
 
    /* enable copy */
    EnableItem ( theMenu, iEditCopyItem );
    
    /* disable cut, paste and clear */
    DisableItem ( theMenu, iEditClearItem );
    DisableItem ( theMenu, iEditPasteItem );
    DisableItem ( theMenu, iEditCutItem );
    
    /* disable the renderer prefs */
    DisableItem ( theMenu, iEditRendererPrefsItem );
                
    /* adjust the view menu  */
    theMenu = GetMenuHandle ( mViewMenu ) ;
    DisableItem ( theMenu, 0L );
    
    return ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_HandleEvent
 */
Boolean         PictWindow_HandleEvent( WindowPtr theWindow, const EventRecord *theEventRecord) 
{
    return false ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_New
 */
WindowPtr       PictWindow_New( unsigned char *windowTitle ) 
{
    return NULL ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_SaveAs
 */
OSErr           PictWindow_SaveAs( WindowPtr theWindow ) 
{
    short               dstPictFRef = 0 ;
    PicHandle           thePicture = NULL ;
    OSErr               theErr = noErr ;
    GWorldPtr           theGWorld ;
    CGrafPtr            savedPort ;
    GDHandle            savedGDH ;
    
    /* get an open a file for writing */
    if( (theErr = GetOutputPictFileRef( &dstPictFRef )) == noErr ) {
    
        theGWorld = GetGWorldFromPictWindow( theWindow ) ;
        
        /* make a temp viewer to use to make the PICT */
        if( theGWorld != NULL )
        {
            
            /* lock the viewer's GWorld for updates */
            LockPixels(GetGWorldPixMap(theGWorld)) ;
 
            GetGWorld( &savedPort, &savedGDH ) ;
            SetGWorld( (CGrafPtr)theGWorld, NULL ) ;
        
            thePicture = OpenPicture(&theGWorld->portRect) ;
            
            /* copy onto ourselves to make the picture */
            CopyBits ((BitMapPtr) &theGWorld->portPixMap,
                      (BitMapPtr) &theGWorld->portPixMap,
                      &theGWorld->portRect, 
                      &theGWorld->portRect, 
                      srcCopy, 
                      0L);
 
            ClosePicture() ;
        
            if( thePicture  != nil ) {
 
                long                    length ;
                long                    dummy ;
                long                    index ;
 
                /* ok, myPic now contains a handle to the picture */
                HLock( (Handle)thePicture);
 
                /* set up the 512 byte header for a PICT file */
                dummy = 0;
                
                for( index = 0; index < ( 512 / 4 ); index ++ ){
                    length = 4 ;
                    
                    if( (theErr = FSWrite(dstPictFRef, &length, &dummy)) != noErr ) {
                        return theErr ;     
                    }
                }               
                
                length = GetHandleSize( (Handle)thePicture);
                
                if( (theErr = FSWrite(dstPictFRef, &length, *thePicture)) != noErr ) {
                    return theErr ;     
                }
             
                /* now get rid of the picture handle */
                HUnlock( (Handle) thePicture ) ;
                KillPicture(thePicture);         
                FSClose(dstPictFRef);
            }
        }
    }
    
    return theErr ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_Save
 */
OSErr           PictWindow_Save( WindowPtr theWindow ) 
{
    /* we don't really work well with pictures so just call save as */
    PictWindow_SaveAs( theWindow ) ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_Revert
 */
OSErr           PictWindow_Revert( WindowPtr theWindow ) 
{
    return noErr ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_Open
 */
WindowPtr       PictWindow_Open( FSSpec *theFSSpec ) 
{
    return NULL ;
}
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_Close
 */
OSErr PictWindow_Close( WindowPtr theWindow ) 
{
    DocumentHdl         theDocumentHdl ;
    PictDataHdl         thePictDataHdl ;
    GWorldPtr           theGWorld ;
    OSErr               theErr ;
    THPrint             thePrintRec ;
    
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kPICTMagic
        && (thePictDataHdl = (PictDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theGWorld = (**thePictDataHdl).fGWorld) != NULL) 
        {
            DisposeGWorld( theGWorld ) ;
        }
        DisposeHandle((Handle)thePictDataHdl) ;
 
        thePrintRec = (**theDocumentHdl).fPrintRec ;
        if( thePrintRec != NULL )
            DisposeHandle((Handle)thePrintRec ) ;
 
        DisposeHandle((Handle)theDocumentHdl) ;
        
    }
    DisposeWindow( theWindow ) ;
    return noErr ;
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_CountPages - calculate the number of pages needed to
 * image the document associated with the picture window
 */
 
short   PictWindow_CountPages( 
    WindowPtr   theWindow, 
    Rect        *pageRect ) 
{
    Rect            pictRect ;
    short           horizOffset, 
                    vertOffset,
                    pagesWide = 0,  /*  the number of pages wide the image is */
                    pagesHigh = 0,  /*  the number pages high for this image */
                    pageWidth,      /*  the width of one page */
                    pageHeight ;    /*  the height of one page */
 
    DocumentHdl     theDocumentHdl ;
    PictDataHdl     thePictDataHdl ;
    GWorldPtr       theGWorld ;
    OSErr           theErr ;
    short           retVal = 0 ;
    
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kPICTMagic
        && (thePictDataHdl = (PictDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theGWorld = (**thePictDataHdl).fGWorld) != NULL) 
        {
            retVal = GeneralCountPages( theGWorld, pageRect ) ;     
        }
    }
    
    return retVal ;
}
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_PrintPage - image the requested page in the 
 * printer port
 */
 
void    PictWindow_PrintPage( 
    WindowPtr       theWindow, 
    Rect            *pageRect, 
    GrafPtr         imagingPort, 
    short           pageNum ) 
{
    GWorldPtr       docGWorld;
    Rect            pictRect, rectToPrint,srcRect,dstRect;
    short           pagesWide,      /*  the number of pages wide the image is */
                    pagesHigh,      /*  the number pages high for this image */
                    horozTile,      /*  used in the loop to denote the H tile to print from the image */
                    vertTile ;      /*  used in the loop to denote the V tile to print from the image */
    short           thisPage = 1;   /*  used to find the page they want us to print */
    short           pictHOff,
                    pictVOff,
                    pictWidth,      /*  the width of the doc's GWorld */
                    pictHeight ;    /*  the height of the doc's GWorld */
    short           pageWidth,      /*  the width of one page */
                    pageHeight ;    /*  the height of one page */
    DocumentHdl     theDocumentHdl ;
    PictDataHdl     thePictDataHdl ;
    GWorldPtr       theGWorld ;
    OSErr           theErr ;
    PixMapHandle    offPixMap ;
    
    theDocumentHdl = (DocumentHdl)GetWRefCon(theWindow) ;
    if(theDocumentHdl != NULL
        && (**theDocumentHdl).fDocumentMagic == kPICTMagic
        && (thePictDataHdl = (PictDataHdl)((**theDocumentHdl).fPrivate)) != NULL) 
    {
        if((theGWorld = (**thePictDataHdl).fGWorld) != NULL) 
        {
            GeneralPrintPage( theGWorld, pageRect, imagingPort, pageNum ) ;
        }
    }
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_PostPrint - this gets called before we print, so any
 * setup for printing can be done here.
 */
void PictWindow_PrePrint( WindowPtr theWindow )
{
    return ; /* noop for picts, couild put null in the struct to avoid the call */
}
 
 
/*------------------------------------------------------------ */
 
/*
 * PictWindow_PostPrint - this gets called after we print, so any
 * setup for printing can be torn down here.
 */
void PictWindow_PostPrint( WindowPtr theWindow )
{
    return ; /* noop for picts, couild put null in the struct to avoid the call */
}
 
 
/*
 * handle the edit copy command.
 */
OSErr PictWindow_Copy( WindowPtr theWindow )
{
    PicHandle       thePicture ;
    OpenCPicParams  thePicParams ;
    GWorldPtr       theGWorld ;
    OSErr           theErr ;
    PixMapHandle    offPixMap ;
    
    if((theGWorld = GetGWorldFromPictWindow( theWindow )) != NULL) 
    {
        CGrafPtr        savedPort ;
        GDHandle        gdh ;
        
        /* save the port */
        GetGWorld( &savedPort, &gdh);
        SetGWorld( (CGrafPtr)theGWorld, NULL ) ;
                    
        /* set up the pic params for the OpenCPicture call */
        thePicParams.srcRect.top    = theGWorld->portRect.top;
        thePicParams.srcRect.left   = theGWorld->portRect.left;
        thePicParams.srcRect.bottom = theGWorld->portRect.bottom;
        thePicParams.srcRect.right  = theGWorld->portRect.right;
        thePicParams.hRes           = 0x00480000 ;              /* specifies 72 dpi */
        thePicParams.vRes           = 0x00480000 ;              /* specifies 72 dpi */
        thePicParams.version        = -2 ;
        thePicParams.reserved1      = 0 ;
        thePicParams.reserved2      = 0 ;
        
        /* try to create a new picture */
        thePicture = OpenCPicture( &thePicParams );
        
        if(thePicture != NULL)
        {
            long        aLong ;
            ClipRect( &thePicParams.srcRect );
 
            /* lock it down */
            offPixMap = GetGWorldPixMap( theGWorld ) ;
            LockPixels( offPixMap ) ;   
            
            /* make the picture */  
            CopyBits(   (BitMapPtr)*offPixMap, 
                        (BitMapPtr)*offPixMap, 
                        & theGWorld->portRect, 
                        & theGWorld->portRect, 
                        srcCopy, 
                        0L ) ;
                        
            ClosePicture() ;
 
            /* unlock */
            UnlockPixels( offPixMap ) ;
                        
            /* place the picture on the scrap */
            HLock((Handle)thePicture) ;
            aLong = ZeroScrap() ;
            aLong = PutScrap(GetHandleSize((Handle)thePicture), 'PICT', *thePicture);
            HUnlock((Handle)thePicture) ;
            KillPicture(thePicture) ;
                
        }
        /* reset the port */    
        SetGWorld( savedPort, gdh);
            
    }
    return ; 
}