PictDocument.c

/*
    File:       PictDocument.c
 
    Contains:   This source file is the most important part of this program because it shows
                all the code needed to maintain Picture Documents.  A Picture Document is a
                window which displays an off-screen image and lets you paint into it, and
                these two activities show how to use graphics environments to draw off screen
                and copy the off-screen image to the window on the screen.
 
    Written by: Forrest Tanaka  
 
    Copyright:  Copyright © 1988-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                7/13/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
 
/******************************************************************************\
* Header Files
\******************************************************************************/
 
/* System header files needed by MPW C */
#ifndef THINK_C
#include <Errors.h>
#include <Menus.h>
#include <Resources.h>
#include <Windows.h>
#endif
 
#include <GestaltEqu.h>
#include <Palettes.h>
#include <ColorPicker.h>
#include <QDOffscreen.h>
 
/* Header files specific to this program */
#include "PictDocument.h"
#include "EmergMem.h"
#include "ColorReset.h"
#include "MenuHandler.h"
#include "WindowPositioner.h"
 
#ifndef topLeft
#define topLeft(r)                      (((Point *) &(r))[0])
#endif
 
#ifndef botRight
#define botRight(r)                     (((Point *) &(r))[1])
#endif
 
 
/******************************************************************************\
* Private Constants
\******************************************************************************/
 
/* Picture Document window constants */
#define rPictDocWindID 128 /* Resource ID of Picture Document WIND resource */
#define rScrollBarID   128 /* Resource ID of scroll bar CNTL resource */
#define kDocWKind      128 /* windowKind field of Picture Document windows */
#define kMinWindowSize 64  /* Minimum size of the window in pixels */
 
/* PICT file constants */
#define kPictFileHeaderSize 512    /* Number of bytes in PICT file header */
#define kFileCreator        'CLIM' /* Creator code for this application */
#define kFileType           'PICT' /* File type for this appÕs documents */
 
/* Off-screen Constants */
#define kMaxRowBytes 0x3FFE     /* Maximum number of bytes in a row of pixels */
#define kDefaultRes  0x00480000 /* Default resolution is 72 DPI; Fixed type */
#define kITabRes     4          /* Inverse-table resolution */
 
/* Miscellaneous constants */
#define kPaintRadius 8 /* Radius of a drop of paint in pixels */
 
 
/******************************************************************************\
* Private Types
\******************************************************************************/
 
/* Picture Document information */
typedef struct
{
    GWorldPtr     image;      /* Pointer to image in off-screen GWorld */
    Rect          windowRect; /* Rectangle of window except for scroll bars */
    Rect          destRect;   /* Rectangle of image in window */
    RGBColor      foreground; /* Color of foreground */
    RGBColor      background; /* Color of background */
    ControlHandle vScrollBar; /* Horizontal scroll bar */
    ControlHandle hScrollBar; /* Vertical scroll bar */
    FSSpec        file;       /* File spec of Picture DocumentÕs PICT file */
    short         fileRef;    /* File ref # of Picture DocumentÕs PICT file */
    Boolean       dithering;  /* True if image should be dithered */
} PictDocInfoRec, *PictDocInfoPtr, **PictDocInfoHnd;
 
/* Basic picture information */
typedef struct
{
    CTabHandle colors; /* Handle to color table of deepest pixel map */
    short      depth;  /* Max depth for all pixmaps (in the picture) */
    Rect       frame;  /* Picture frame rect (contains entire picture) */
} PictureInfoRec;
 
 
/******************************************************************************\
* Prototypes of Private Functions
\******************************************************************************/
 
static OSErr CreatePictDoc(
    Rect       *pictureRect,
    short      pictureDepth,
    CTabHandle pictureClut,
    WindowPtr  *retDoc);
 
 
static void ScrollPictDoc(
    WindowPtr docWindow,
    short     hScrollDelta,
    short     vScrollDelta);
 
void ResizePictDoc(
    WindowPtr docWindow);
 
OSErr CreateOffScreen(
    Rect       *bounds,
    short      depth,
    CTabHandle colors,
    CGrafPtr   *retPort,
    GDHandle   *retGDevice);
 
OSErr SetUpPixMap(
    short        depth,
    Rect         *bounds,
    CTabHandle   colors,
    short        bytesPerRow,
    PixMapHandle aPixMap);
 
OSErr CreateGDevice(
    PixMapHandle basePixMap,
    GDHandle     *retGDevice);
 
void DisposeOffScreen(
    CGrafPtr doomedPort,
    GDHandle doomedGDevice);
 
OSErr GetPictureInfo(
    short          pictFileRef,
    PictureInfoRec *retPictInfo);
 
 
OSErr DrawFilePicture(
    short pictFileRef,
    Rect  *destRect);
 
 
OSErr OpenFilePicture(
    short pictFileRef,
    Rect  *sourceRect);
 
OSErr CloseFilePicture(void);
 
 
 
/******************************************************************************\
* Global Variables
\******************************************************************************/
 
PictureInfoRec gPictureInfo;  /* Information about a PICT file */
PicHandle      gSpoolPicture; /* Temporary picture used to spool from PICT file */
QDProcsPtr     gSavedProcs;   /* Saves existing QDProcs; used when spooling out */
CQDProcs       gCustomProcs;  /* Customized bottlenecks to spool to PICT file */
short          gPictFileRef;  /* File reference number of a PICT file */
short          gPictureSize;  /* Size of picture in bytes */
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* FindPictDoc: Find the Picture Document window for a specific file
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* Each Picture Document window in the window list is examined to see whether it
* has a corresponding disk file.  The fileRef field of the Picture Document
* information record is non-zero if the Picture Document has a disk file
* associated with it.  If there is a disk file, then EqualFSSpec is called to
* see if the FSSpec that I saved in the documentÕs info record is the same as
* the one passed in fileSpec.  If they are, then a pointer to the window is
* returned.  Otherwise, the loop goes to the next Picture Document window, if
* there is one.
*
* See ColorReset.h for a definition of EqualFSSpec.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
WindowPtr FindPictDoc(
    FSSpecPtr fileSpec) /* Specification of file to search for */
{
    WindowPtr      pictDocWindow; /* Pointer to Picture Document being tested */
    PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document info rec */
    Boolean        foundDoc;      /* True if found Pict Doc w/ same FSSpec */
 
    /* Get a pointer to the first Picture Document window in the window list */
    pictDocWindow = NextPictDocWindow( nil );
 
    /* Keep searching until found or at end of list */
    foundDoc = false;
    while (pictDocWindow != nil && !foundDoc)
    {
        /* Only have to check FSSpecs if document has a corresponding file */
        pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
        if ((**pictDocInfo).fileRef != 0)
        {
            /* See if the Picture DocumentÕs file is same as fileSpec */
            HLock( (Handle)pictDocInfo );
            foundDoc = EqualFSSpec( fileSpec, &(**pictDocInfo).file );
            HUnlock( (Handle)pictDocInfo );
 
            /* If FSSpecs arenÕt the same, then go to next Picture Document */
            if (!foundDoc)
                pictDocWindow = NextPictDocWindow( pictDocWindow );
        }
    }
 
    return pictDocWindow;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* NextPictDocWindow: Return a pointer to the next Picture Document window
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* NextPictDocWindow goes through every window in the window list thatÕs behind
* the window specified by aWindow until either a Picture Document window is
* found or until the end of the window list is reached.  If aWindow is nil, then
* the search starts with the front window.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
WindowPtr NextPictDocWindow(
    WindowPtr aWindow) /* Window to start search from, or nil if front */
{
    /* Start search from next window, or front window if aWindow is nil */
    if (aWindow == nil)
        aWindow = FrontWindow();
    else
        aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
 
    /* Search until Picture Document wind found or end of wind list reached */
    while (aWindow != nil && !IsPictDocWindow( aWindow ))
        aWindow = (WindowPtr)((WindowPeek)aWindow)->nextWindow;
 
    return aWindow;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* IsPictDocWindow: Is a window a Picture Document window?
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* When I create a window, I store a code for that kind of window in the
* windowKind field.  IsPictDocWindow checks the windowKind field of the given
* window against the constant kDocWKind.  If theyÕre equal, then aWindow
* must be a Picture Document window.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
Boolean IsPictDocWindow (
    WindowPtr aWindow) /* Pointer to the window being tested */
{
    return ((WindowPeek)aWindow)->windowKind == kDocWKind;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* DrawPictDoc: Draw the contents of a Picture Document
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* The image for Picture Documents is stored in an off-screen GrafPort, and its
* GrafPtr is stored in the information record.  This GrafPtr is retrieved, and
* itÕs used to CopyBits the GrafPort to the window.  ItÕs possible that not all
* of the image can be copied to the window.  For example, part of the window
* might be covered by another window.  In this case, the visRgn of the window
* takes care of clipping out the undesireable parts of the original image.  The
* other case in which part of the image shouldnÕt be drawn is if part of the
* image is scrolled out of the window.  The visRgn of the window takes care of
* part of this, but we still have to make sure that the image doesnÕt draw
* itself over the scroll bar areas of the window in case the scroll bars are
* hidden, which they are if the window is inactive.  To take care of this, the
* destination rectangle which is the rectangle that the off-screen image is
* copied to, is first intersected with the window rectangle, which is the
* rectangle of the window except for the scroll bars.  This is used as the
* destination rectangle to CopyBits.  Then this rectangle is offset by the top-
* left corner of the original destination rectangle, and this is used as the
* source rectangle to CopyBits.  The off-screen image is then copied to the
* window.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
void DrawPictDoc(
    WindowPtr docWindow) /* Ptr to Picture Document window being drawn */
{
    PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
    GWorldPtr      image;       /* Pointer to the off-screen image */
    Rect           destRect;    /* Rectangle to copy image into */
    Rect           sourceRect;  /* Rectangle to copy image from */
    Rect           windowRect;  /* Rect of the window except for scroll bars */
    short          mode;        /* Drawing mode to use */
 
    /* Get a pointer to the off-screen image and the window rectangle */
    pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
    image = (**pictDocInfo).image;
    windowRect = (**pictDocInfo).windowRect;
 
    /* Calculate the rectangle to copy the image to */
    destRect = (**pictDocInfo).destRect;
    (void)SectRect( &destRect, &windowRect, &destRect );
 
    /* Calculate the rectangle to copy the image from */
    sourceRect = destRect;
    OffsetRect( &sourceRect, -(**pictDocInfo).destRect.left,
            -(**pictDocInfo).destRect.top );
 
    /* Specify the drawing mode to use */
    mode = srcCopy;
    if ((**pictDocInfo).dithering)
        mode |= ditherCopy;
 
    /* Draw the off-screen image to the screen */
    EraseRect( &docWindow->portRect );
    CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
            &sourceRect, &destRect,
            mode, nil );
 
    /* Draw the grow box */
    DrawGrowIcon( docWindow );
 
    /* Draw the scroll bars */
    DrawControls( docWindow );
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* ClickPictDoc: Handle a mouse click in a Picture Document window
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* The first really important call in this routine is FindControl, which is the
* toolbox call which tells you which control and which part of the control the
* mouse was clicked in.  If it returns zero, then the mouse was clicked outside
* of any control, and so we let the user paint into the document.
*
* Painting into the document is done within a loop which iterates as long as the
* mouse button is held down.  While this is the case, the current mouse position
* is retrieved with a call to GetMouse.  If the mouse position changed from the
* previous iterationÕs mouse position, then the mouse position is offset to take
* into account the currently-scrolled position, and then the current port is set
* to the documentÕs off-screen GrafPort.  Then, PaintOval is called which draws
* a drop of paint into the off-screen GrafPort.
*
* The off-screen image then has to be copied to the window so that the user can
* see the new drop of paint.  This is done by first seeing if the drop of paint
* can actually be seen in the window in the currently-scrolled position.  If it
* can, then the rectangle of the drop of paint is offset to take into account
* the currently-scrolled position.  Then the current port is set to the document
* window, and CopyBits is called to copy the new drop of paint to the document
* window.
*
* Finally, the mouse position is remembered and the loop iterates again if the
* mouse button is still held down.
*
* If FindControl indicates that the mouse click was in the thumb of a scroll
* bar, TrackControl is called which lets the user move the thumb around until
* the user releases the mouse button.  Then, the difference between the old and
* new scroll bar position is calculated which indicates the number of pixels
* that the document must be scrolled.  ScrollPictDoc, defined below, is then
* called to scroll the documentÕs image, update the scrolled-in part of the
* image, and update the document information record to reflect the new scrolled
* position.
*
* If FindControl indicates that the mouse click was in the up arrow, down arrow,
* page up, or page down areas of either scroll bar, then TrackControl is called
* to scroll the document appropriately.  The ScrollActionProc routine, defined
* below, handles the logistics of scrolling in this case.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
void ClickPictDoc(
    WindowPtr   docWindow,   /* Picture Document window that was clicked */
    EventRecord *clickEvent) /* Mouse click event */
{
    PictDocInfoHnd pictDocInfo;     /* Handle to Picture Document info rec */
    GWorldPtr      image;           /* Pointer to the off-screen image */
    GDHandle       savedDevice;     /* Saves default GDevice */
    Rect           paintRect;       /* Rectangle to draw paint into */
    Rect           windowPaintRect; /* Rectangle to draw paint into */
    Rect           windowRect;      /* Window rect except for scroll bars */
    Rect           destRect;        /* Rectangle of image in window */
    Point          newPosition;     /* New pos of mouse click; local coords */
    Point          oldPosition;     /* Old pos of mouse click; local coords */
    ControlHandle  scrollBar;       /* Handle to the clicked scroll bar */
    short          partNumber;      /* Part # of clicked part of control */
    short          scrollDelta;     /* Number of pixels to scroll */
 
    savedDevice = GetGDevice();
 
    /* Get information about the Picture Document */
    pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
    image = (**pictDocInfo).image;
    windowRect = (**pictDocInfo).windowRect;
    destRect = (**pictDocInfo).destRect;
 
    /* Get the mouse click position */
    SetPort( docWindow );
    newPosition = clickEvent->where;
    GlobalToLocal( /*×*/&newPosition );
 
    /* Find whether the mouse was in a control */
    partNumber = FindControl( newPosition, docWindow, /*<*/&scrollBar );
    switch (partNumber)
    {
        case 0:
            /* Clicked outside of any control; see if it was in the image */
            if (PtInRect( newPosition, &windowRect ))
            {
                /* Click in image; paint while mouse button is held down */
                oldPosition.v = oldPosition.h = -32767;
                while (StillDown())
                {
                    /* Only do something if the mouse moved */
                    if (!EqualPt( newPosition, oldPosition ))
                    {
                        /* Need to offset paint brush for scrolled position */
                        SubPt( topLeft( destRect ), /*×*/&newPosition );
                        SetRect( /*<*/&paintRect, newPosition.h - kPaintRadius,
                                newPosition.v - kPaintRadius,
                                newPosition.h + kPaintRadius,
                                newPosition.v + kPaintRadius );
 
                        /* Drop some paint into the off-screen image */
                        SetGWorld( image, nil );
                        PaintOval( &paintRect );
 
                        /* Offset brush back to window position */
                        AddPt( topLeft( destRect ), /*×*/&newPosition );
 
                        /* Calc rect in window for to brush rect in image */
                        windowPaintRect = paintRect;
                        OffsetRect( &windowPaintRect, destRect.left,
                                destRect.top );
 
                        /* Only copy if some part of brush within image */
                        if (SectRect( &windowPaintRect, &windowRect,
                                &windowPaintRect ))
                        {
                            /* Make clipped paint rect in off-screen image */
                            paintRect = windowPaintRect;
                            OffsetRect( &paintRect, -destRect.left,
                                    -destRect.top );
 
                            /* Copy painted off-screen image to the screen */
                            SetGWorld( (CGrafPtr)docWindow, nil );
                            CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
                                    &paintRect, &windowPaintRect,
                                    srcCopy, nil );
                        }
 
                        /* Remember the new mouse position */
                        oldPosition = newPosition;
                    }
 
                    /* Get the new position of the mouse */
                    SetPort( docWindow );
                    GetMouse( /*<*/&newPosition );
                }
            }
            break;
        case kControlIndicatorPart:
            /* Mouse in thumb of scroll bar; get existing control value */
            scrollDelta = GetControlValue( scrollBar );
 
            /* Track mouse in thumb until mouse button is released */
            partNumber = TrackControl( scrollBar, newPosition, nil );
            if (partNumber != 0)
            {
                /* Find diff between old and new scroll bar values */
                scrollDelta -= GetControlValue( scrollBar );
                if (scrollDelta != 0)
                {
                    if (scrollBar == (**pictDocInfo).vScrollBar)
                        ScrollPictDoc( docWindow, 0, scrollDelta );
                    else
                        ScrollPictDoc( docWindow, scrollDelta, 0 );
                }
            }
            break;
        case kControlUpButtonPart:
        case kControlDownButtonPart:
        case kControlPageUpPart:
        case kControlPageDownPart:
            TrackControl( scrollBar, newPosition, gActionProc);
            break;
        default:
            break;
    }
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* ScrollActionProc: Handle a click in a scroll bar (except for thumb)
*
* PARAMETERS:
* ControlHandle control: Control that received the mouse click
* short         part:    Part number of the clicked control part
*
* DEFINITION:
* ScrollActionProc is called by the Control Manager when TrackControl is called
* in the ClickPictDoc function above.  It handles scrolling when the user clicks
* in the scroll arrows or page areas of the scroll bar.  The document is
* scrolled by the appropriate amount.
*
* DESCRIPTION:
* ScrollActionProc first checks to see which part of the scroll bar was clicked.
* If it was in either of the arrows, then the desired scroll amount is one
* pixel.  If it was in either of the paging areas, then the desired scroll
* amount is 32 pixels.  The current value of the clicked scroll bar is then
* adjusted by the desired scroll amount, and the result is compared against the
* limits of the scroll barÕs values.  If the new value is beyond either limit,
* then itÕs pinned back into the limits.  Then the real amount to scroll is
* calculated simply by subtracting the new scroll value from the old scroll
* value, and ScrollPictDoc (defined below) is called to scroll the Picture
* Document window.
*
* RETURN VALUES:
* None
\******************************************************************************/
 
pascal  void ScrollActionProc(
    ControlHandle control, /* Handle to clicked control */
    short         part)    /* Part number of clicked control part */
{
    PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
    WindowPtr      docWindow;   /* Clicked Picture Document window */
    Rect           windowRect;  /* Window rect except for scroll bars */
    Rect           destRect;    /* Rectangle of image in window */
    short          scrollDelta; /* Number of pixels to scroll by */
    short          oldValue;    /* Value of scroll bar before scrolling */
    short          newValue;    /* Value of scroll bar after scrolling */
    short          maxValue;    /* Maximum value of the scroll bar */
 
    /* Get information about the Picture Document */
    docWindow = (**control).contrlOwner;
    pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
    windowRect = (**pictDocInfo).windowRect;
    destRect = (**pictDocInfo).destRect;
 
    /* Only process if the part # isnÕt zero */
    if (part != 0)
    {
        /* Decide scroll amount based on part of control that was clicked */
        switch (part)
        {
            case kControlUpButtonPart:
                scrollDelta = 1;
                break;
            case kControlDownButtonPart:
                scrollDelta = -1;
                break;
            case kControlPageUpPart:
                scrollDelta = 32;
                break;
            case kControlPageDownPart:
                scrollDelta = -32;
                break;
            default:
                scrollDelta = 0;
                break;
        }
 
        /* Only do something if weÕre actually scrolling */
        if (scrollDelta != 0)
        {
            /* Get the current scroll bar state */
            maxValue = GetControlMaximum( control );
            oldValue = GetControlValue( control );
 
            /* Calculate the new scroll bar value pinned to the limits */
            newValue = oldValue - scrollDelta;
            if (newValue < 0)
                newValue = 0;
            else if (newValue > maxValue)
                newValue = maxValue;
 
            /* Only scroll if the old and new values are different */
            if (oldValue != newValue)
            {
                /* Set the new value of the scroll bar */
                SetControlValue( control, newValue );
 
                /* Scroll by difference between old and new scroll bar values */
                scrollDelta = oldValue - newValue;
                if (control == (**pictDocInfo).vScrollBar)
                    ScrollPictDoc( docWindow, 0, scrollDelta );
                else
                    ScrollPictDoc( docWindow, scrollDelta, 0 );
            }
        }
    }
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* ScrollPictDoc: Scroll a Picture Document windowÕs image
*
* PARAMETERS:
* WindowPtr docWindow:    Pointer to Picture Document window to scroll
* short     vScrollDelta: Number of pixels to scroll vertically
* short     hScrollDelta: Number of pixels to scroll horizontally
*
* DEFINITION:
* ScrollPictDoc scrolls the picture document window specified by the docWindow
* parameter by vScrollDelta pixels vertically and hScrollDelta pixels
* horizontally.  The part of the window thatÕs scrolled in is updated, so no
* updating is needed outside of ScrollPictDoc.
*
* DESCRIPTION:
* ScrollRect is called to scroll the Picture DocumentÕs image by the specified
* amount.  The scrolled-in part is then redrawn with a call to CopyBits.  The
* destRect field of the Picture Document information record keeps track of the
* scrolled position of the image, so itÕs updated to reflect the new scrolled
* position.
*
* RETURN VALUES:
* None
\******************************************************************************/
 
static void ScrollPictDoc(
    WindowPtr docWindow,    /* Pointer to document window to scroll */
    short     hScrollDelta, /* Number of pixels to scroll horizontally */
    short     vScrollDelta) /* Number of pixels to scroll vertically */
{
    PictDocInfoHnd pictDocInfo;  /* Handle to Picture Document info rec */
    RgnHandle      updateRegion; /* Region covering scrolled-in area */
    Rect           windowRect;   /* Rect of window except for scroll bars */
    Rect           destRect;     /* New destination rectangle */
    GWorldPtr      image;        /* Off-screen document image */
 
    /* Get the window rectangle and destination rectangle */
    pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
    windowRect = (**pictDocInfo).windowRect;
    destRect = (**pictDocInfo).destRect;
    image = (**pictDocInfo).image;
 
    /* Scroll the image by the specified amount */
    updateRegion = NewRgn();
    ScrollRect( &windowRect, hScrollDelta, vScrollDelta, updateRegion );
 
    /* Update the destination rectangle to the new scrolled position */
    OffsetRect( &destRect, hScrollDelta, vScrollDelta );
    (**pictDocInfo).destRect = destRect;
 
    /* Update the scrolled-in part of the window */
    CopyBits( &((GrafPtr)image)->portBits, &docWindow->portBits,
            &image->portRect, &destRect,
            srcCopy, updateRegion );
 
    /* Get rid of the update region */
    DisposeRgn( updateRegion );
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* GrowPictDoc: Let the user change the size of a Picture Document window
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* GrowWindow is called with a boundary rectangle that specifies that the window
* has a minimum size of kMinWindowSize (defined at the top of this source file)
* and a maximum size thatÕs the size of the image.  After the user releases the
* mouse button, SizeWindow is called to change the size of the window, and
* ResizePictDoc (defined below) is called to update the Picture Document to the
* new size.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
void GrowPictDoc(
    WindowPtr   docWindow,   /* Picture Document window thatÕs to be grown */
    EventRecord *clickEvent) /* Mouse down event */
{
    PictDocInfoHnd pictDocInfo; /* Handle to Picture Document info rec */
    Rect           growBounds;  /* Window can be dragged over this rectangle */
    Point          newSize;     /* New size of the new window */
 
    pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
 
    growBounds = (**pictDocInfo).image->portRect;
    growBounds.left = growBounds.top = kMinWindowSize;
    growBounds.right += kScrollBarWidth;
    growBounds.bottom += kScrollBarWidth;
 
    /* Let the user resize the window to any size */
    *((long *)&newSize) = GrowWindow( docWindow, clickEvent->where,
            &growBounds );
 
    /* If the user actually changed the size of the window, resize the window */
    if (*((long *)&newSize) != 0)
    {
        SizeWindow( docWindow, newSize.h, newSize.v, true );
        ResizePictDoc( docWindow );
    }
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* ResizePictDoc: Update a picture document window to a new size
*
* PARAMETERS:
* WindowPtr docWindow: Picture document window that was resized
*
* DEFINITION:
* ResizePictDoc updates the Picture Document window specified by the docWindow
* to a new size.  This routine is called right after the size of docWindow has
* been changed.  The act of updating the Picture Document window involves moving
* the scroll bars to the edge of the window and scrolling the document image.
* The image has to be scrolled when the window is grown in case the bottom or
* right edge of the window goes below or to the right of the image, in which
* case the bottom or right edges of the image has to ÒchaseÓ the bottom or right
* edges of the window, and that means scrolling.
*
* DESCRIPTION:
* The first major thing thatÕs done is a new destination rectangle is calculated
* in case the document has to be scrolled as described in the definition.  There
* are three conditions that have to be met before scrolling can happen:
*     1) The upper-left corner of destRect must not be (0,0), meaning that the
*        documentÕs image is scrolled from its original position.
*     2) The window has become bigger.
*     3) The windowÕs rectangle has moved below or to the right of the image in
*        the window.
* If all three of these conditions are met for either the horizontal or
* vertical cases, a new destRect is calculated.  If this new destRect is
* different from the old destRect, ScrollPictDoc (defined above) is called to
* scroll the image to the new position.
*
* After this, the windowRect field of the Picture Document information record
* is updated to the new size of the window.  Then, the maximum values of the
* scroll bars are set to the number of pixels in the horizontal or vertical
* direction that canÕt be seen.  And then, the old position of the scroll icon
* is invalidated so that itÕll be erased with the documentÕs image the next time
* the window is updated.
*
* Both of the scroll bars are then moved along the bottom and right edges of the
* window, and the new position of the grow icon is invalidated so that itÕs
* redrawn in case the window was made smaller.  Finally, the rectangles of the
* scroll bars are validated so that they wonÕt be redrawn in the next update
* event.
*
* RETURN VALUES:
* None
\******************************************************************************/
 
static void ResizePictDoc(
    WindowPtr docWindow) /* Picture document window that was resized */
{
    PictDocInfoHnd pictDocInfo;   /* Handle to Picture Document information rec */
    ControlHandle  vScrollBar;    /* Vertical scroll bar */
    ControlHandle  hScrollBar;    /* Horizontal scroll bar */
    Rect           windowRect;    /* Covers part of window that contains image */
    Rect           newWindowRect; /* Covers all of window except scroll bars */
    Rect           scrollRect;    /* Rectangle of scroll bar */
    Rect           destRect;      /* Rectangle of to draw image into */
    Rect           newDestRect;   /* Rectangle of to draw image into */
    CGrafPtr       image;         /* Off-screen document image */
    short          remaining;     /* # rows, columns of pixels not visible */
 
    SetPort( docWindow );
 
    /* Get the Picture DocumentÕs information */
    pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
    vScrollBar = (**pictDocInfo).vScrollBar;
    hScrollBar = (**pictDocInfo).hScrollBar;
    image = (**pictDocInfo).image;
    destRect = (**pictDocInfo).destRect;
    windowRect = (**pictDocInfo).windowRect;
 
    /* Find the new window document area */
    newWindowRect = docWindow->portRect;
    newWindowRect.right -= kScrollBarWidth - 1;
    newWindowRect.bottom -= kScrollBarWidth - 1;
 
    /* Find new destRect in case window growing causes scrolling */
    newDestRect = destRect;
    if (newDestRect.left != 0 && 
            (newWindowRect.right - newWindowRect.left) >
            (windowRect.right - windowRect.left) &&
            newWindowRect.right > newDestRect.right)
        OffsetRect( /*×*/&newDestRect, (newWindowRect.right - windowRect.right)
                - (newDestRect.right - windowRect.right), 0 );
    if (newDestRect.top != 0 && 
            (newWindowRect.bottom - newWindowRect.top) >
            (windowRect.bottom - windowRect.top) &&
            newWindowRect.bottom > newDestRect.bottom)
        OffsetRect( /*×*/&newDestRect, 0, (newWindowRect.bottom -
                windowRect.bottom) - (newDestRect.bottom - windowRect.bottom) );
 
    /* If the new scrolled position has changed, update the windowÕs image */
    if (!EqualRect( &newDestRect, &destRect))
        ScrollPictDoc( docWindow, newDestRect.left - destRect.left,
                newDestRect.top - destRect.top );
 
    /* Set up the rectangles for scroll bar calculations */
    (**pictDocInfo).windowRect = newWindowRect;
 
    /* Calc # rows of image not visible; set as max value of scroll bar */
    remaining = image->portRect.bottom - newWindowRect.bottom;
    if (remaining < 0)
        remaining = 0;
    SetControlMaximum( vScrollBar, remaining );
 
    /* Calc # cols of image not visible; set as max value of scroll bar */
    remaining = image->portRect.right - newWindowRect.right;
    if (remaining < 0)
        remaining = 0;
    SetControlMaximum( hScrollBar, remaining );
 
    /* Invalidate the old position of the grow icon */
    SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
            (**vScrollBar).contrlRect.bottom,
            (**vScrollBar).contrlRect.right,
            (**hScrollBar).contrlRect.bottom );
    InvalRect( &scrollRect );
 
    /* Move the scroll bars along the edges of the window */
    HideControl( vScrollBar );
    HideControl( hScrollBar );
    MoveControl( vScrollBar, newWindowRect.right, newWindowRect.top - 1 );
    MoveControl( hScrollBar, newWindowRect.left - 1, newWindowRect.bottom );
    SizeControl( vScrollBar, kScrollBarWidth, newWindowRect.bottom -
            newWindowRect.top + 2 );
    SizeControl( hScrollBar, newWindowRect.right - newWindowRect.left + 2,
            kScrollBarWidth );
    ShowControl( vScrollBar );
    ShowControl( hScrollBar );
 
    /* Invalidate the new position of the grow icon */
    SetRect( &scrollRect, (**hScrollBar).contrlRect.right,
            (**vScrollBar).contrlRect.bottom,
            (**vScrollBar).contrlRect.right,
            (**hScrollBar).contrlRect.bottom );
    InvalRect( &scrollRect );
 
    /* Validate the new scroll bar areas */
    scrollRect = (**vScrollBar).contrlRect;
    ValidRect( &scrollRect );
    scrollRect = (**hScrollBar).contrlRect;
    ValidRect( &scrollRect );
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* ActivatePictDoc: Activate or deactivate a Picture Document window
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* If the Picture Document window is becoming active, the scroll bars are shown.
* If itÕs becoming inactive, the scroll bars are hidden.  Because hiding
* controls causes the control rectangle to be invalidated, ActivatePictDoc
* validates them again to avoid a very slight flicker in the lines that delimit
* the scroll bar areas.
*
* DrawGrowIcon is called to show or hide the grow icon.  The Window Manager
* automatically decides whether to show or hide the grow icon.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
void ActivatePictDoc(
    WindowPtr docWindow,      /* Pointer to Doc window being updated */
    Boolean   becomingActive) /* True if Doc wind is becoming active */
{
    PictDocInfoHnd pictDocInfo; /* Handle to Picture Document information rec */
    ControlHandle  vScrollBar;  /* Vertical scroll bar */
    ControlHandle  hScrollBar;  /* Horizontal scroll bar */
    Rect           scrollRect;  /* Rectangle of the scroll bars */
 
    /* Get the scroll bars of the window */
    pictDocInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
    vScrollBar = (**pictDocInfo).vScrollBar;
    hScrollBar = (**pictDocInfo).hScrollBar;
 
    /* Show scroll bars if becoming active; hide if becoming inactive */
    if (becomingActive)
    {
        ShowControl( vScrollBar );
        ShowControl( hScrollBar );
    }
    else
    {
        HideControl( vScrollBar );
        HideControl( hScrollBar );
 
        /* To avoid a slight flicker, validate the scroll bar rectangles */
        SetPort( docWindow );
        scrollRect = (**vScrollBar).contrlRect;
        ValidRect( &scrollRect );
        scrollRect = (**hScrollBar).contrlRect;
        ValidRect( &scrollRect );
    }
 
    /* Show or hide the grow icon */
    DrawGrowIcon( docWindow );
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* FixPictDocMenus: Dim, undim, check, or uncheck menus for Picture Documents
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* The Close and Save AsÉÊitems in the File menu are enabled when a Picture
* Document window is active, as is the Choose ColorÉ item in the Colors menu.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
void FixPictDocMenus(
    WindowPtr docWindow) /* Pointer to active Picture Document window */
{
#pragma unused(docWindow)
    MenuHandle aMenu; /* Handle to the menus being udpated */
 
    /* Set items in the File menu */
    aMenu = GetMenuHandle( mFile );
    EnableItem( aMenu, iClose );
    EnableItem( aMenu, iSaveAs );
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* DoOpenPictDoc: Open a new Picture Document
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* The usual Standard File dialog is used to get the name and location of the
* PICT file to open.  Then FindPictDoc (defined earlier in this file) is called
* to determine whether the chosen PICT file is already open within this
* application.  If it is, then that PICT fileÕs window is just selected and
* DoOpenPictDoc returns with a pointer to that window.
*
* If the file isnÕt open, then itÕs opened here with HOpenDF.  This routine is
* used because it works even on systems that donÕt have the FSSpec-based File
* Manager calls.  GetPictureRect (defined later in this file) is called to get
* the picFrame from the picture in the chosen PICT file.  Once this is done,
* CreatePictDoc is called to create a blank Picture Document window thatÕs ready
* to read the image into.
*
* The current port is set to the off-screen GrafPort that CreatePictDoc created,
* and then DrawFilePicture (defined later in this file) is called to spool the
* PICT file into the current port.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
WindowPtr DoOpenPictDoc()
{
    WindowPtr         pictDocWindow;  /* Pointer to new Picture Doc window */
    PictDocInfoHnd    pictDocInfo;    /* Handle to Picture Document info rec */
    PictureInfoRec    pictureInfo;    /* PixMap information from PICT file */
    Rect              pictureRect;    /* Rectangle of picture in PICT file */
    CGrafPtr          newImage;       /* New off-screen image */
    RGBColor          color;          /* Use to set default fore/back colors */
    CGrafPtr          savedPort;      /* Ptr to current port for restoring */
    GDHandle          savedDevice;    /* Current GDevice for restoring */
    StandardFileReply reply;          /* UserÕs choice for PICT file to open */
    SFTypeList        typeList;       /* Specifies we can open 'PICT' files */
    short             pictFileRef;    /* File reference number of PICT file */
    OSErr             error;
 
    
 
    pictDocWindow = nil;
    pictFileRef = 0;
 
    /* Let the user choose a PICT file */
    typeList [0] = 'PICT';
    if (FileSpecGet( nil, 1, typeList, /*<*/&reply ))
    {
        /* First determine whether chosen file is already open in this app */
        pictDocWindow = FindPictDoc( &reply.sfFile );
        if (pictDocWindow == nil)
        {
            /* Open the PICT file */
            error = HOpenDF( reply.sfFile.vRefNum, reply.sfFile.parID,
                    reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
            if (error != noErr)
                SysError(error);
            /* Get interesting information about a picture */
            error = GetPictureInfo( pictFileRef, /*<*/&pictureInfo );
            if (error != noErr)
                SysError(error);
            pictureRect = pictureInfo.frame;
            OffsetRect( /*×*/&pictureRect, -pictureRect.left,
                    -pictureRect.top );
 
            /* Open a new Picture Document window*/
            error = CreatePictDoc( &pictureRect, pictureInfo.depth,
                    pictureInfo.colors, /*<*/&pictDocWindow );
            if (error != noErr)
                SysError(error);
 
            /* Fill in the Picture Document information */
            pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
            color.red = color.green = color.blue = 0x0000;
            (**pictDocInfo).foreground = color;
            color.red = color.green = color.blue = 0xFFFF;
            (**pictDocInfo).background = color;
            (**pictDocInfo).fileRef = pictFileRef;
            (**pictDocInfo).file = reply.sfFile;
            (**pictDocInfo).dithering = false;
 
            /* Get a handle to the Picture Document information record */
            pictDocInfo = (PictDocInfoHnd)GetWRefCon( pictDocWindow );
 
            /* Save current port and set newImage as current port */
            newImage = (**pictDocInfo).image;
            GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
            SetGWorld( newImage, nil );
 
            /* Draw the contents of the PICT file into the current port */
            error = DrawFilePicture( pictFileRef, &newImage->portRect );
 
            /* Restore the current port and check for errors */
            SetGWorld( savedPort, savedDevice );
            if (error != noErr)
                SysError(error);
 
            /* Set the title of the window to the title of the file */
            SetWTitle( pictDocWindow, reply.sfFile.name );
 
            /* Show the window in its final size and position */
            ShowWindow( pictDocWindow );    
        }
        else
            /* Chosen file already open; just select it */
            SelectWindow( pictDocWindow );
    }
 
    return pictDocWindow;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* CreatePictDoc: Create an empty Picture Document
*
* PARAMETERS:
* Rect      *pictureRect: Bounding rectangle of the image
* WindowPtr *retDoc:      Returns pointer to new Picture Document window
*
* DEFINITION:
* This routine creates a new Picture Document window and returns a pointer to
* it.  If the Picture Document couldnÕt be created for some reason, then
* CreatePictDoc returns the error code, and the retDoc parameter is untouched.
*
* DESCRIPTION:
* Space for the window record is allocated before GetNewWindow is called so that]
* heap fragmentation is minimized.  Then, the window is initialized with
* GetNewWindow.  Then the vertical and horizontal scroll bars are created, and
* the Picture Document information record is allocated and initialized.  The
* last major task is to stagger the window into position, and the routines that
* are defined in WindowPositioner.c are used to do this.
*
* RETURN VALUES:
* Result: Error code if Picture Document window couldnÕt be created
* retDoc: Pointer to the new Picture Document window, 
\******************************************************************************/
 
static OSErr CreatePictDoc(
    Rect       *pictureRect, /* Bounding rectangle of off-screen image */
    short      pictureDepth, /* Pixel depth of off-screen image */
    CTabHandle pictureClut,  /* Color table of off-screen image */
    WindowPtr  *retDoc)      /* Returns ptr to new Picture Document window */
{
    WindowPtr      docWindow;      /* Pointer to new Picture Document window */
    Ptr            windowStore;    /* Pointer to pre-allocated window rec */
    PictDocInfoHnd docInfo;        /* Handle to Picture Document information */
    PaletteHandle  pictureColors;  /* Palette with pictureÕs colors */
    GWorldPtr      newImage;       /* New off-screen image */
    ControlHandle  vScrollBar;     /* Vertical scroll bar */
    ControlHandle  hScrollBar;     /* Horizontal scroll bar */
    Rect           windowRect;     /* Destination rectangle of window */
    Point          windowBias;     /* Bias of Picture Document window */
    OSErr          error;
    Point          tempPt;
 
    docWindow = nil;
    docInfo = nil;
    windowStore = nil;
 
    /* Pre-allocate the window record to avoid heap fragmentation */
    windowStore = NewPtrMargin( sizeof (WindowRecord), kAllocApp, !kAllocClr );
    if (windowStore == nil)
        SysError(memFullErr);
 
    /* Create the off-screen graphics environment */
    if (pictureClut == nil)
        pictureClut = GetCTable( pictureDepth );
    error = NewGWorld( /*<*/&newImage, pictureDepth, pictureRect, pictureClut,
            nil, 0 );
    if (error != noErr)
        SysError(error);
 
    /* Create a new Picture Document window */
    docWindow = GetNewCWindow( rPictDocWindID, windowStore,
            (WindowPtr)-1L );
    if (docWindow == nil)
        if (FailLowMemory( 0 ) || ResError() == memFullErr)
            SysError(memFullErr );
        else if (ResError() == noErr || ResError() == resNotFound)
            SysError(resNotFound );
    SetPort( docWindow );
 
    /* Give the window a palette with the window colors */
    pictureColors = NewPalette( (**pictureClut).ctSize + 1, pictureClut,
            pmTolerant, 0 );
    SetPalette( docWindow, pictureColors, true );
 
    /* Create the horizontal and vertical scroll bars */
    vScrollBar = GetNewControl( rScrollBarID, docWindow );
    if (vScrollBar == nil)
        if (FailLowMemory( 0 ) || ResError() == memFullErr)
            SysError(memFullErr );
        else if (ResError() == noErr || ResError() == resNotFound)
            SysError(resNotFound );
    hScrollBar = GetNewControl( rScrollBarID, docWindow );
    if (hScrollBar == nil)
        if (FailLowMemory( 0 ) || ResError() == memFullErr)
            SysError(memFullErr );
        else if (ResError() == noErr || ResError() == resNotFound)
            SysError(resNotFound );
 
    /* Allocate a new Picture Document information record */
    docInfo = (PictDocInfoHnd)NewHandleMargin( sizeof (PictDocInfoRec),
            kAllocApp, !kAllocClr );
    if (docInfo == nil)
        SysError(memFullErr);
    (**docInfo).image = newImage;
    (**docInfo).vScrollBar = vScrollBar;
    (**docInfo).hScrollBar = hScrollBar;
    (**docInfo).destRect = newImage->portRect;
    (**docInfo).fileRef = 0;
    SetWRefCon( docWindow, (long)docInfo );
 
    /* Stagger the window onto the screen */
    windowBias = CalcWindowBias( documentProc,
            ((WindowPeek)docWindow)->goAwayFlag );
    SetRect( &windowRect, 0, 0, pictureRect->right + kScrollBarWidth - 1,
            pictureRect->bottom + kScrollBarWidth - 1 );
            
    /*convert the windowRect to global coordinates */
    SetPt(&tempPt, windowRect.left, windowRect.top);
    LocalToGlobal(&tempPt);
    windowRect.left = tempPt.h;
    windowRect.top = tempPt.v;
    SetPt(&tempPt, windowRect.right, windowRect.bottom);
    LocalToGlobal(&tempPt);
    windowRect.right = tempPt.h;
    windowRect.bottom = tempPt.v;
 
    PositionScreenRect( &windowRect,
            kParentScreenPos, kStaggerPos, NextPictDocWindow( nil ),
            windowBias.h, windowBias.v );
    MoveWindow( docWindow, windowRect.left, windowRect.top, false );
    SizeWindow( docWindow, windowRect.right - windowRect.left,
            windowRect.bottom - windowRect.top, false );
    ResizePictDoc( docWindow );
 
    /* Identify this window for later */
    ((WindowPeek)docWindow)->windowKind = kDocWKind;
 
    *retDoc = docWindow;
    return noErr;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* DoSaveAsPictDoc: Save a new Picture Document
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* The user is asked for a file name for the new Picture Document file through
* Standard File.  If the user specifies the file name and location and chooses
* OK, DoSaveAsPictDoc creates a new PICT file if it didnÕt exist already, and
* then it opens the PICT file for writing.  The Picture DocumentÕs off-screen
* GrafPort is set as the current port, and then OpenFilePicture (defined below)
* is called which opens a new picture for spooling.  Then the off-screen image
* is CopyBitsÕed on top of itself which spools the off-screen image to the PICT
* file.  Then the spooled picture is closed, which completes the writing of the
* PICT file.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
void DoSaveAsPictDoc(
    WindowPtr docWindow) /* Pointer to Picture Document window being saved */
{
    StandardFileReply reply;       /* Location and name of new PICT file */
    Str255            docTitle;    /* Holds the existing name of the document */
    PictDocInfoHnd    docInfo;     /* Handle to Picture Document information */
    CGrafPtr          image;       /* Points to documentÕs image */
    CGrafPtr          savedPort;   /* Saves current port for later restoring */
    GDHandle          savedDevice; /* Saves current GDevice for restoring */
    short             pictFileRef; /* File reference number of PICT file */
    OSErr             error;
 
    pictFileRef = 0;
 
    /* Get the name and location of the new file from the user */
    GetWTitle( docWindow, /*<*/docTitle );
    StandardPutFile( "\p", docTitle, &reply );
    if (reply.sfGood)
    {
        /* Create the new PICT file if it doesnÕt already exist */
        if (!reply.sfReplacing)
        {
            error = HCreate( reply.sfFile.vRefNum, reply.sfFile.parID,
                    reply.sfFile.name, kFileCreator, kFileType );
            if (error != noErr)
                SysError(error);
        }
 
        /* Open the new PICT file */
        error = HOpen( reply.sfFile.vRefNum, reply.sfFile.parID,
                reply.sfFile.name, fsCurPerm, /*<*/&pictFileRef );
        if (error != noErr)
            SysError(error);
        /* Get the Picture DocumentÕs image and make it the current port */
        docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
        image = (**docInfo).image;
        GetGWorld( /*<*/&savedPort, /*<*/&savedDevice );
        SetGWorld( image, nil );
 
        /* Create a PICT file with the Picture DocumentÕs image */
        error = OpenFilePicture( pictFileRef, &image->portRect );
        if (error != noErr)
            SysError(error);
        CopyBits( &((GrafPtr)image)->portBits, &((GrafPtr)image)->portBits,
                &image->portRect, &image->portRect,
                srcCopy, nil );
        error = CloseFilePicture();
        if (error != noErr)
            SysError(error);
 
        /* Restore the current port */
        SetGWorld( savedPort, savedDevice );
 
        /* Close the existing PICT file if any */
        if ((**docInfo).fileRef != 0)
            (void)FSClose( (**docInfo).fileRef );
 
        /* Remember the new PICT fileÕs reference number */
        (**docInfo).fileRef = pictFileRef;
        (**docInfo).file = reply.sfFile;
 
        /* Set the title of the window to the title of the file */
        SetWTitle( docWindow, reply.sfFile.name );
    }
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* DoClosePictDoc: Close a Picture Document window
*
* PARAMETERS:
* See PictDocument.h
*
* DEFINITION:
* See PictDocument.h
*
* DESCRIPTION:
* The off-screen image is disposed of and the Picture DocumentÕs PICT file is
* closed.  Then all the memory associated with the window is disposed of.
*
* RETURN VALUES:
* See PictDocument.h
\******************************************************************************/
 
Boolean DoClosePictDoc(
    WindowPtr docWindow) /* Ptr to Picture Document window being closed */
{
    PictDocInfoHnd docInfo; /* Handle to Picture Document info record */
 
    docInfo = (PictDocInfoHnd)GetWRefCon( docWindow );
    if (docInfo != nil)
    {
        /* Get rid of the off-screen CGrafPort */
        if ((**docInfo).image != nil)
            DisposeGWorld( (**docInfo).image );
 
        /* Close the Picture File */
        if ((**docInfo).fileRef != 0)
            (void)FSClose( (**docInfo).fileRef );
 
        /* Dispose of the Picture Document information record */
        DisposeHandle( (Handle)docInfo );
    }
 
    /* Close and dispose of the Picture Document window */
    DisposePalette( GetPalette( docWindow ) );
    CloseWindow( docWindow );
    DisposePtr( (Ptr)docWindow );
 
    return true;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* GetPictureInfo: Get basic information about a PICT document
*
* PARAMETERS:
* short          pictFileRef:  File reference number of PICT file
* PictureInfoRec *retPictRect: Returns information on picture
*
* DEFINITION:
* GetPictureInfo examines the PICT file whose file reference number is specified
* in the pictFileRef parameter to get the picFrame of the picture, and the pixel
* depth and color table of the deepest PixMap in the picture.  This information
* is returned in the retPictInfo parameter.
*
* DESCRIPTION:
* GetPictureInfo uses picture spooling to get the information about the picture
* in the specified PICT file.  A temporary CGrafPort or GrafPort is set up just
* so that no existing ports are touched.  Then a CQDProcs or QDProcs record is
* set up so that picture data comes from the PICT file instead of memory, and so
* that any CopyBits calls within the picture get routed to the InfoBits routine
* (defined below).  Then the standard picture spooling mechanism is used to read
* the PICT file.  The clip region is set to nothing to prevent drawingÑÑonly the
* information about the PixMaps is needed, and InfoBits gets that.
*
* RETURN VALUES:
* Result:      Error code if anything went wrong
* retPictInfo: Size of picture, and depth and color table of deepest PixMap in
*              the picture.
\******************************************************************************/
 
static OSErr GetPictureInfo(
    short          pictFileRef,  /* File reference number of PICT file */
    PictureInfoRec *retPictInfo) /* Returns information on picture */
{
    CGrafPort    tempPort;     /* Temp port used for ÒDrawingÓ pict */
    Boolean      tempPortOpen; /* True if tempPort has been opened */
    GrafPtr      savedPort;    /* Saves current port for restoring */
    CQDProcs     customProcs;  /* Custom GrafProcs */
    PicHandle    spoolPict;    /* Picture used for spooling */
    Rect         emptyRect;    /* Used to set empty clipping region */
    long         dataLen;      /* Number of bytes of data to read */
    long         qdVersion;    /* Version number of QuickDraw */
    OSErr        error;
 
    error = noErr;
    tempPortOpen = false;
    savedPort = nil;
    spoolPict = nil;
 
    /* Get the current version of QuickDraw */
    (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
 
    /* Save current port so that we can restore it later */
    GetPort( /*<*/&savedPort );
 
    /* Open the temporary CGrafPort or GrafPort, depending on QD version */
    if (qdVersion >= gestalt8BitQD)
        OpenCPort( &tempPort );
    else
        OpenPort( (GrafPtr)&tempPort );
    tempPortOpen = true;
 
    /* DonÕt want anything drawn */
    SetRect( /*<*/&emptyRect, 0, 0, 0, 0 );
    ClipRect( &emptyRect );
 
    /* Set up the GrafProcs for spooling */
    if (qdVersion >= gestalt8BitQD)
        SetStdCProcs( /*<*/&customProcs );
    else
        SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
    customProcs.bitsProc = gQDBitsUPP;
    customProcs.getPicProc = gQDGetPicUPP;
 
    /* Install the custom GrafProcs into the tempPort */
    tempPort.grafProcs = &customProcs;
 
    /* Create the picture used for spooling */
    spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture), kAllocApp,
            !kAllocClr );
    if (spoolPict == nil)
        SysError(memFullErr );
 
    /* Skip the PICT file header */
    error = SetFPos( pictFileRef, fsFromStart, 512L );
    if (error != noErr)
        SysError(error);
 
    /* Read the picture header into spoolPict */
    dataLen = sizeof (Picture);
    error = FSRead( pictFileRef, /*×*/&dataLen, (Ptr) *spoolPict );
    if (error != noErr)
        SysError(error );
 
    /* Spool the picture in */
    gPictureInfo.colors = nil;
    gPictureInfo.depth = 0;
    gPictureInfo.frame = (**spoolPict).picFrame;
    gPictFileRef = pictFileRef;
    DrawPicture( spoolPict, &gPictureInfo.frame );
 
    /* Clean up after myself */
    SetPort( savedPort );
    ClosePort( (GrafPtr)&tempPort );
    tempPortOpen = false;
    DisposeHandle( (Handle)spoolPict );
 
    /* Return picture information */
    *retPictInfo = gPictureInfo;
    return noErr;
    }
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* InfoBits: GrafProc to get information about bit/pixel maps
*
* PARAMETERS:
* BitMapPtr srcBits:  Pointer to bit/pixel map being transferred
* Rect      *srcRect: Source rectangle of CopyBits call; ignored
* Rect      *dstRect: Destination rectangle of CopyBits call; ignored
* short     mode:     Transfer mode; ignored
* RgnHandle maskRgn:  Region to mask CopyBits; ignored
*
* DEFINITION:
* InfoBits is a custom QuickDraw graphics procedure which is used to find the
* depth and color table of the deepest PixMap in the picture thatÕs being drawn.
* This information is returned in the gPictureInfo global variable.
*
* DESCRIPTION:
* InfoBits is called every time a CopyBits call is found in a picture thatÕs
* being drawn.  It first checks to see if the CopyBits call is for a PixMap or
* a BitMap.  If itÕs a BitMap, the most significant bit of rowBytes is clear; if
* itÕs a PixMap, the most significant byte of rowBytes is set.  If itÕs a
* PixMap, its depth is compared against the depth of any previous PixMap that
* has been seen in the picture.  If itÕs deeper, then the depth and color table
* of the PixMap is saved.  Otherwise, the PixMap is ignored.
*
* If srcBits is a BitMap and no PixMaps have yet been seen in the picture, then
* InfoBits remembers a depth of 1 bit per pixel and no color table.
*
* RETURN VALUES:
* gPictureInfo: Depth and color table of deepest PixMap in a picture.
\******************************************************************************/
 
pascal void InfoBits(
    BitMapPtr srcBits,  /* Pointer to bit/pixel map being transferred */
    Rect      *srcRect, /* Source rectangle of CopyBits call; ignored */
    Rect      *dstRect, /* Destination rectangle of CopyBits call; ignored */
    short     mode,     /* Transfer mode; ignored */
    RgnHandle maskRgn)  /* Region to mask CopyBits; ignored */
{
#pragma unused (srcRect,dstRect,mode,maskRgn)
    PixMapPtr srcPix; /* Handle to the pictureÕs PixMap */
 
    if (srcBits->rowBytes & 0x8000)
    {
        /* srcBits is a pixel map, get depth and color table */
        srcPix = (PixMapPtr)srcBits;
 
        /* Only save info if this pixel map is deepest seen so far */
        if (srcPix->pixelSize > gPictureInfo.depth)
        {
            gPictureInfo.depth = srcPix->pixelSize;
            if (srcPix->pixelType != RGBDirect)
            {
                if (gPictureInfo.colors != nil)
                    DisposeHandle( (Handle)gPictureInfo.colors );
                gPictureInfo.colors = srcPix->pmTable;
                HandToHand( (Handle *)&gPictureInfo.colors );
            }
        }
    }
    else
    {
        /* srcBits is a bit map, has depth of 1 and B&W color table */
        if (gPictureInfo.depth == 0)
        {
            gPictureInfo.depth = 1;
            if (gPictureInfo.colors != nil)
                DisposeHandle( (Handle)gPictureInfo.colors );
            gPictureInfo.colors = nil;
        }
    }
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* DrawFilePicture: Read a PICT file into the current GrafPort
*
* PARAMETERS:
* short pictFileRef: File reference number of PICT file to open
* Rect  *destRect:   Rectangle to draw the picture into
*
* DEFINITION:
* This routine is used to read the picture data from the PICT file specified by
* pictFileRef and draw the picture into the rectangle specified by the destRect
* parameter in the current GrafPort.  ItÕs just like DrawPicture, except the
* file reference number of a PICT file is specified instead of a PicHandle.
*
* DESCRIPTION:
* DrawFilePicture works by using the picture spooling method described in Inside
* Macintosh V on pages 87 through 90.  In this method, the getPicProc GrafProc is
* replaced by my routine called FileGetPic which reads PICT information from
* disk.  By doing this, the DrawPicture command effectively reads PICT files.
*
* After setting up the GrafProcs record, this routine installs the GrafProcs
* into the current port.  Then, a temporary picture is manually allocated and
* placed into spoolPict.  This is essentially a placeholder to hold the picture
* header (which is different from the 512-byte PICT file header) and is always
* the size of the Picture record type without any picture data.
*
* The PICT file is opened using HOpen.  HOpen is documented in the File Manager
* chapter of Inside Macintosh VI, but it still works on pre-7.0 machines because
* itÕs just glue code that calls PBHOpen.  Because of the way the File Manager
* works, this routine also works on MFS volumes.  This routine is preferable to
* FSOpen because HOpen allows you to specify access permissions, and because it
* lets you specify a file by the more reliable volume reference number,
* directory ID, and file name rather than by working directory reference number
* and file name.  IÕm using the FSSpec record to hold file information, so
* specifying a file like this is especially convenient.  If this were a 7.0-only
* application, IÕd call FSpOpen instead of HOpen because FSpOpen takes an FSSpec
* record directly.
*
* After the PICT file is opened, SetFPos is called to skip the first 512 bytes
* of the file.  The first 512 bytes of a PICT file is called the PICT file
* header (not to be confused with the picture header, which contains the picSize
* and picFrame fields of the Picture type) and holds application-specific
* information.  This application doesnÕt care whatÕs there so it just skips it.
* The picture header follows the PICT file header and is read into spoolPict.
*
* Finally (almost), DrawPicture is called.  Because this routine replaced the
* current portÕs GrafProcs with my own, DrawPicture gets the picture information
* from the PICT file rather than from spoolPict.  It draws the picture into the
* current port.
*
* RETURN VALUES:
* Result:   Returns an error code.
* destRect: Returns the picFrame of the picture in the PICT file
\******************************************************************************/
 
static OSErr DrawFilePicture(
    short pictFileRef, /* File reference number of PICT file to open */
    Rect  *destRect)   /* Rectangle to draw the picture into */
{
    GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
    CQDProcs     customProcs; /* Custom GrafProcs */
    QDProcsPtr   savedProcs;  /* Current GrafPortÕs QD procs; used to restore */
    PicHandle    spoolPict;   /* Picture used for spooling */
    long         dataLen;     /* Number of bytes of data to read */
    long         qdVersion;   /* Version number of QuickDraw */
    OSErr        error;
 
    currPort = nil;
    spoolPict = nil;
    savedProcs = nil;
    error = noErr;
 
    /* Get the current version of QuickDraw */
    (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
 
    /* Get a pointer to the current port and determine its type */
    GetPort( /*<*/&currPort );
 
    /* Initialize the GrafProcs for reading from a PICT file */
    if (qdVersion >= gestalt8BitQD)
        SetStdCProcs( /*<*/&customProcs );
    else
        SetStdProcs( /*<*/(QDProcsPtr) &customProcs );
    customProcs.getPicProc = gQDGetPicUPP;
 
    /* Install the custom GrafProcs into the tempPort */
    savedProcs = currPort->grafProcs;
    currPort->grafProcs = (QDProcsPtr)&customProcs;
 
    /* Create the temporary picture used for spooling */
    spoolPict = (PicHandle)NewHandleMargin( sizeof (Picture),
            kAllocApp, !kAllocClr );
    if (spoolPict == nil)
        SysError(memFullErr );
 
    /* Skip the 512-byte PICT file header */
    error = SetFPos( pictFileRef, fsFromStart, kPictFileHeaderSize );
    if (error != noErr)
        SysError(error );
 
    /* Read the picture header (not PICT file header) into spoolPict */
    dataLen = sizeof (Picture);
    error = FSRead( pictFileRef, /*×*/&dataLen, (Ptr)*spoolPict );
    if (error != noErr)
        SysError(error );
 
    /* Spool the picture in */
    gPictFileRef = pictFileRef;
    DrawPicture( spoolPict, destRect );
 
    /* Clean up after myself */
    DisposeHandle( (Handle)spoolPict );
    currPort->grafProcs = savedProcs;
 
    return noErr;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* FileGetPic: Get QuickDraw picture information from a file
*
* PARAMETERS:
* Ptr   dataPtr:   Points to location that picture data should be placed
* short byteCount: Number of bytes of picture data to read
*
* DEFINITION:
* This routine is a custom QuickDraw graphics procedure which replaces the
* standard GetPicProc.  QuickDraw graphics procedures are routines that
* QuickDraw calls to do almost everything that it does, like drawing rectangles,
* or getting picture data.  They can be replaced by setting up a QDProcs record
* and setting the appropriate field to point to the custom QuickDraw graphics
* procedure.  In this case, the getPicProc field is set to point to FileGetPic
* so that any DrawPicture call within the GrafPort causes FileGetPic to be
* called to get picture data.  Instead of getting picture data from memory,
* FileGetPic gets picture data from the disk file whose file reference number is
* stored in the gPictFileRef global variable.
*
* DESCRIPTION:
* FileGetPic simply calls FSRead to read data from the file whose file reference
* number is stored in gPictFileRef into the area pointed to by dataPtr, which is
* an area of memory thatÕs set up by QuickDraw to receive picture data.  The
* number of bytes of picture data that QuickDraw wants is specified by the
* byteCount parameter.
*
* RETURN VALUES:
* None
\******************************************************************************/
 
pascal void FileGetPic(
    Ptr   dataPtr,   /* Points to location that picture data should be placed */
    short byteCount) /* Number of bytes of picture data to read */
{
    long longCount; /* Number of bytes to read from the PICT file */
 
    longCount = byteCount;
    (void)FSRead( gPictFileRef, /*×*/&longCount, dataPtr );
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* OpenFilePicture: Open a picture for spooling to a file
*
* PARAMETERS:
* short pictFileRef: File reference number of PICT file to write to
* Rect  *sourceRect: Rectangle to draw the picture from
*
* DEFINITION:
* Normally, when you create a picture, you call OpenPicture, draw, then call
* ClosePicture.  But if you can create a PICT file in a similar way by calling
* OpenFilePicture and CloseFilePicture.  Almost like you would normally, you
* should set up the current port however you want it, call OpenFilePicture,
* use QuickDraw routines to draw, then call CloseFilePicture when youÕre done.
* But before you call OpenFilePicture, you must open the PICT file for writing,
* and pass the fileÕs reference number in the pictFileRef parameter.  Once
* CloseFilePicture is called, the PICT file is complete.
*
* DESCRIPTION:
* OpenFilePicture installs the FilePutPic custom graphics procedure by setting
* up a QDprocs record with the default pointers, and then setting the putPicProc
* field to point to the FilePutPic function.  This QDProcs record is then
* installed into the current GrafPort.  Then, an empty picture is allocated just
* to have something to pass to the real OpenPicture routine.  Then the 512-byte
* PICT file header is written.  This program doesnÕt do anything with those 512
* bytes, so only zeroes are written.  Also, the ten-byte picture header is
* written with zeroes for now; itÕll get the real picture header after the PICT
* file data is written.  Finally, the global variables are set up so that
* FilePutPic can reach them.
*
* RETURN VALUES:
* Result: Error code of any error that occurs.
\******************************************************************************/
 
static OSErr OpenFilePicture(
    short pictFileRef, /* File reference number of PICT file to open */
    Rect  *sourceRect) /* Rectangle to draw the picture from */
{
    GrafPtr      currPort;    /* Ptr to current port used for Drawing pict */
    long         dataLen;     /* Number of bytes of data to write */
    short        zeroData;    /* Equal to 0; used to write PICT file header */
    short        count;       /* Generic counter */
    OSErr        error;
    currPort = nil;
    gSpoolPicture = nil;
    gSavedProcs = nil;
    error = noErr;
 
    /* Get a pointer to the current port */
    GetPort( /*<*/&currPort );
 
    /* Initialize the GrafProcs for reading from a PICT file */
    if (currPort->portBits.rowBytes & 0x8000)
        SetStdCProcs( /*<*/&gCustomProcs );
    else
        SetStdProcs( /*<*/(QDProcsPtr)&gCustomProcs );
    gCustomProcs.putPicProc = gQDPutPicUPP;
 
    /* Install the custom GrafProcs into the tempPort */
    gSavedProcs = currPort->grafProcs;
    currPort->grafProcs = (QDProcsPtr)&gCustomProcs;
 
    /* Create the temporary picture used for spooling */
    gSpoolPicture = (PicHandle)NewHandleMargin( sizeof (Picture),
            kAllocApp, kAllocClr );
    if (gSpoolPicture == nil)
        SysError(memFullErr );
 
    /* Make sure weÕre positioned at the beginning of the file */
    error = SetFPos( pictFileRef, fsFromStart, 0 );
    if (error != noErr)
        SysError( error );
 
    /* Zero the 512-byte PICT file header */
    dataLen = sizeof (short);
    zeroData = 0;
    for (count = 0; count < (kPictFileHeaderSize + sizeof (Picture))
            / sizeof (short); ++count)
    {
        error = FSWrite( pictFileRef, /*×*/&dataLen, &zeroData );
        if (error != noErr)
            SysError(error );
    }
 
    /* Begin spooling the picture out */
    gPictFileRef = pictFileRef;
    gPictureSize = sizeof (Picture);
    gSpoolPicture = nil;
    gSpoolPicture = OpenPicture( sourceRect );
 
    return noErr;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* CloseFilePicture: Close a spooled picture (donÕt close the PICT file)
*
* PARAMETERS:
* None
*
* DEFINITION:
* ClosePictureFile is called to complete the writing of a PICT file.  ItÕs
* called the same way youÕd call ClosePicture.
*
* DESCRIPTION:
* ClosePicture is called to close the currently-open picture, then the picture
* header is written with the final values.  Because the temporary picture isnÕt
* needed anymore, KillPicture is called to get rid of it.  Finally, the current
* portÕs graphics procedures are restored to what they were before
* OpenFilePicture was called.
*
* RETURN VALUES:
* Result: Error code of any error that occurs.
\******************************************************************************/
 
static OSErr CloseFilePicture()
{
    long         dataLen;  /* Number of bytes of data to write */
    GrafPtr      currPort; /* Pointer to the current GrafPort */
    OSErr        error;
 
    error = noErr;
 
    /* Close the spooled picture */
    ClosePicture();
 
    /* Make sure weÕre positioned just past the PICT file header */
    error = SetFPos( gPictFileRef, fsFromStart, kPictFileHeaderSize );
    if (error != noErr)
        SysError(error );
 
    /* Write the picture header (NOT the PICT file header!) */
    dataLen = sizeof (Picture);
    error = FSWrite( gPictFileRef, /*×*/&dataLen, (Ptr)*gSpoolPicture );
    if (error != noErr)
        SysError(error );
 
    /* Dispose of the picture */
    KillPicture( gSpoolPicture );
 
    /* Restore the previous graphics procedures */
    GetPort( /*<*/&currPort);
    currPort->grafProcs = gSavedProcs;
    return noErr;
}
 
 
/******************************************************************************\
* NAME & SYNOPSIS:
* FilePutPic: Write QuickDraw picture information to PICT file
*
* PARAMETERS:
* Ptr   dataPtr:   Points to location of picture data
* short byteCount: Number of bytes of picture data to write
*
* DEFINITION:
* This routine is a custom QuickDraw graphics procedure which replaces the
* standard PutPicProc.  QuickDraw graphics procedures are routines that
* QuickDraw calls to do almost everything that it does, like drawing rectangles,
* or getting picture data.  They can be replaced by setting up a QDProcs record
* and setting the appropriate field to point to the custom QuickDraw graphics
* procedure.  In this case, the getPicProc field is set to point to FileGetPic
* so that any drawing call within the GrafPort while a picture is open causes
* FilePutPic to be called to write picture data.  Instead of writing picture
* data to memory, FileGetPic writes picture data to the disk file whose file
* reference number is stored in the gPictFileRef global variable.
*
* DESCRIPTION:
* FileGetPic calls FSWrite to write data t=o the file whose file reference
* number is stored in gPictFileRef from the area pointed to by dataPtr, which is
* an area of memory thatÕs set up by QuickDraw to hold picture data.  The
* number of bytes of picture data that QuickDraw has is specified by the
* byteCount parameter.  Also, the picSize field of gSpoolPicture is updated so
* that QuickDraw can keep track of the current size of the picture.
*
* RETURN VALUES:
* None
\******************************************************************************/
 
pascal void FilePutPic(
    Ptr   dataPtr,   /* Points to location of picture data */
    short byteCount) /* Number of bytes of picture data to write */
{
    long dataLen; /* Number of bytes to write the PICT file */
 
    dataLen = byteCount;
    gPictureSize += dataLen;
    (void)FSWrite( gPictFileRef, /*×*/&dataLen, dataPtr );
    if (gSpoolPicture != nil)
        (**gSpoolPicture).picSize = gPictureSize;
}