Windows.c

/*
    File:       Windows.c
 
    Contains:   Handle application's windows
 
    Written by: Chris White 
 
    Copyright:  Copyright © 1996-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):
                8/6/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
 
#pragma segment Core
 
 
#include <Sound.h>
// System Includes
 
#ifndef __TYPES__
    #include <Types.h>
#endif
 
#ifndef __WINDOWS__
    #include <Windows.h>
#endif
 
#ifndef __DIALOGS__
    #include <Dialogs.h>
#endif
 
#ifndef __QUICKDRAW__
    #include <Quickdraw.h>
#endif
 
#ifndef __PICTUTILS__
    #include <PictUtils.h>
#endif
 
#ifndef __RESOURCES__
    #include <Resources.h>
#endif
 
#ifndef __FONTS__
    #include <Fonts.h>
#endif
 
#ifndef __TOOLUTILS__
    #include <ToolUtils.h>
#endif
 
#ifndef __ERRORS__
    #include <Errors.h>
#endif
 
 
 
 
// Application Includes
 
#ifndef __BAREBONES__
    #include "BareBones.h"
#endif
 
#ifndef __PROTOTYPES__
    #include "Prototypes.h"
#endif
 
 
 
 
// Static prototypes
static OSErr        CreateDocumentWindow ( WindowRef* windowRef );
static GWorldPtr    CreateOffscreen ( CTabHandle theCTabHndl, SInt16 theXsize, SInt16 theYsize,
                                        SInt16 theBitDepth, GWorldFlags theFlags );
static OSErr        DrawPictToOffscreen ( PicHandle thePictHndl, GWorldPtr theOffscreen );
static OSErr        DrawOffscreenToWindow ( GWorldPtr theOffscreen, WindowPtr theWindow );
static Rect         GetWindowVisibleRect ( WindowRef theWindow, SInt16 theSizeX, SInt16 theSizeY );
static void         SaveSetMMUMode ( Boolean bIsSaveMode );
static void         DrawClippedScrollBarLines ( WindowRef theWindow );
static void         DrawClippedGrowIcon ( WindowRef theWindow );
static void         HandleContentClick ( WindowRef theWindow, EventRecord* event );
static OSErr        CreateWindowInfo ( WindowRef windowRef, Size infoSize );
static pascal OSErr SafeGetPictInfo ( PicHandle thePictHandle, PictInfo* thePictInfo,
                                        SInt16 verb, SInt16 colorsRequested,
                                        SInt16 colorPickMethod, SInt16 version );
static void         SizeScrollBars ( WindowRef theWindow );
static Point        GetMaximumWindowSize ( WindowRef theWindow );
 
 
 
// Default RGB Colors
 
static const RGBColor   kRGBBlack = {0x0000, 0x0000, 0x0000};
static const RGBColor   kRGBWhite = {0xFFFF, 0xFFFF, 0xFFFF};
 
 
 
 
 
 
//
// This is called to create the application's window.
//
void CreateWindow ( void )
{
    OSErr       theErr;
    WindowRef   theWindow;
    
    theErr = CreateDocumentWindow ( &theWindow );
    if ( theErr )
        AlertUser ( kGenericErrorStr, theErr, "\p" );
    
    return;
}
 
 
 
//
// This will close the application's window, dispose of any storage we've hung
// off the window, and then dispose of the window itself.
//
WindowRef DestroyWindow ( WindowRef windowRef )
{
    if ( windowRef )
    {
        tWindowInfoPtr  theInfo;
        
        theInfo = (tWindowInfoPtr) GetWRefCon ( windowRef );
        if ( theInfo )
        {
            if ( theInfo->hScrollBar )
                DisposeControl ( theInfo->hScrollBar );
            if ( theInfo->vScrollBar )
                DisposeControl ( theInfo->vScrollBar );
                
            if ( theInfo->offscreen )
                DisposeGWorld ( theInfo->offscreen );               
            
            DisposePtr ( (Ptr) theInfo );
        }
        
        DisposeWindow ( windowRef );
        
    }
    
    return nil;
}
 
 
 
void DoActivate ( EventRecord* theEvent )
{
    Boolean         bActiveFlag = theEvent->modifiers & resumeFlag;
    WindowRef       theWindow = (WindowRef) theEvent->message;
    GrafPtr         savePort;
    tWindowInfoPtr  theInfo;
    
    
    gInBackground = (theEvent->modifiers & resumeFlag) == 0;
    
    GetPort ( &savePort );
    SetPortWindowPort ( theWindow );
    
    theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
    
    if ( bActiveFlag )
    {
        ShowControl ( theInfo->hScrollBar );
        ShowControl ( theInfo->vScrollBar );
    }
    else
    {
        HideControl ( theInfo->hScrollBar );
        HideControl ( theInfo->vScrollBar );
        DrawClippedScrollBarLines ( theWindow );
    }
    
    DrawClippedGrowIcon ( theWindow );
    
    SetPort ( savePort );
    
    return;
}
 
 
 
void DoUpdate ( WindowRef theWindow )
{
    GrafPtr         savePort;
    CGrafPtr        thePort;
    
    
    thePort = GetWindowPort ( theWindow );
    
    GetPort ( &savePort );
    SetPortWindowPort ( theWindow );
    BeginUpdate ( theWindow );                  // visRgn temporarily = updateRgn
    EraseRect ( &thePort->portRect );
    
    UpdateWindowContent ( theWindow );
    
    if ( gInBackground )
        DrawClippedScrollBarLines ( theWindow );
    else
        UpdateControls ( theWindow, theWindow->visRgn );
    DrawClippedGrowIcon ( theWindow );
    
    EndUpdate ( theWindow );                    // restore normal visRgn of grafport
    SetPort ( savePort );
    
    return;
}
 
 
 
void UpdateWindowContent ( WindowRef theWindow )
{
    tWindowInfoPtr  theInfo;
    
    theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
    DrawOffscreenToWindow ( theInfo->offscreen, theWindow );
    
    return;
}
 
 
 
void DoContentClick ( WindowRef theWindow, EventRecord* theEvent )
{
    OSErr       theErr = noErr;
    WindowRef   frontWindow;
    
    // If a movable modal is active, ignore click in an inactive 
    // window, otherwise select it or handle the content click.
    
    frontWindow = FrontWindow ( );
    if ( theWindow != frontWindow )
    {
        if ( IsMovableModal ( frontWindow ) )
            SysBeep ( 30 );
        else
            SelectWindow ( theWindow );
    }
    else
    {
        SInt16          thePart;
        GrafPtr         savePort;
        ControlRef      theControl;
        Point           localPt;
    
        localPt = theEvent->where;
        GlobalToLocal ( &localPt );
        thePart = FindControl ( localPt, theWindow, &theControl );
        
        GetPort ( &savePort );
        SetPortWindowPort ( theWindow );
        
        if ( thePart )
        {
            switch ( thePart )
            {
                case kControlUpButtonPart:
                case kControlDownButtonPart:
                case kControlPageUpPart:
                case kControlPageDownPart:
                    TrackControl ( theControl, localPt, gScrollControlActionUPP );
                break;
                
                case kControlIndicatorPart:
                    if ( BeginThumbTracking ( theControl ) == noErr )
                    {
                        TrackControl ( theControl, localPt,
                                        (ControlActionUPP) gScrollThumbActionUPP );
                        EndThumbTracking ( );
                    }
                break;
            }
        }
        else
            HandleContentClick ( theWindow, theEvent );
            
        SetPort ( savePort );
    }
        
    return;
    
} // DoContentClick
 
 
 
void DoGrowWindow ( WindowRef theWindow, EventRecord* theEvent )
{
    WindowPtr           savePort;
    SInt32              returnCoord;
    Rect                resizeBounds;
    SInt16              theWidth,
                        theHeight;
    Point               theMaxSize;
    
    GetPort ( &savePort );
    theMaxSize = GetMaximumWindowSize ( theWindow );
    SetRect ( &resizeBounds, 96, 96, theMaxSize.h, theMaxSize.v );
    returnCoord = GrowWindow ( theWindow, theEvent->where, &resizeBounds );
    theHeight   = HiWord ( returnCoord );
    theWidth = LoWord ( returnCoord );
    
    SizeWindow ( theWindow, theWidth, theHeight, true );
    SizeScrollBars ( theWindow );
    SetPort ( theWindow );
    InvalRect ( &theWindow->portRect );
    
    SetPort ( savePort );
    
    
    return;
}
 
 
 
void DoDragWindow ( WindowRef theWindow, EventRecord* theEvent )
{
    WindowRef   frontWindow;
    
    
    // If a movable modal is active, ignore click in an inactive 
    // title bar, otherwise let the Window Manager handle it.
    
    frontWindow = FrontWindow ( );
    if ( theWindow != frontWindow && IsMovableModal ( frontWindow ) )
        SysBeep ( 30 );
    else                                
    {
        RgnHandle   theRgn;
        Rect        dragRect;
        
        theRgn = GetGrayRgn ( );
        dragRect = (*theRgn)->rgnBBox;
        DragWindow ( theWindow, theEvent->where, &dragRect );
    }
    
    return;
}
 
 
 
OSErr DoAboutBox ( void )
{
 
    OSErr           theErr = noErr;
    SInt16          theItem = 0;
    GrafPtr         savePort = nil;
    DialogRef       theDialog;
    ModalFilterUPP  theFilter = nil;
    
    
    theDialog = GetNewDialog ( kAboutDialog, nil, (WindowPtr) -1 );
    
    GetPort ( &savePort );
    SetPort ( theDialog );
 
    ShowWindow ( theDialog );
    
    // Get the standard filter proc
    theErr = GetStdFilterProc ( &theFilter );
    if ( theErr )
        goto CleanupAndBail;
    
    // Tell the dialog manager to use the default button
    SetDialogDefaultItem ( theDialog, kStdOkItemIndex );
    
    
    // Modal dialog loop    
    do
    {
        // Use "theFilter" in ModalDialog call
        ModalDialog ( theFilter, &theItem );
        
    } while ( theItem != kStdOkItemIndex );
    
    
CleanupAndBail:
    
    DisposeDialog ( theDialog );
    SetPort ( savePort );
 
    return theErr;
}
 
 
 
//
// Creates a document window containing a picture.
//
static OSErr CreateDocumentWindow ( WindowRef* windowRef )
{
    OSErr           theErr = noErr;
    WindowRef       theWindow;
    Point           theImageSize;
    PicHandle       thePict = nil;
    PictInfo        thePictInfo;
    tWindowInfoPtr  theInfo;
    
    
    
    theWindow = GetNewCWindow ( kDisplayWindow, nil, (WindowRef) -1 );
    if ( theWindow == nil )
        return (ResError ( )) ? ResError ( ) : resNotFound;
    
    theErr = CreateWindowInfo ( theWindow, sizeof ( tWindowInfo ) );
    if ( theErr )   goto CleanupAndBail;
    
    theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
    
    SetPortWindowPort ( theWindow );
    
    thePict = GetPicture ( kPictureID );
    if ( thePict == nil )   goto CleanupAndBail;
    
    theErr = SafeGetPictInfo ( thePict, &thePictInfo, returnColorTable, 256, systemMethod, 0 );
    if ( theErr )   goto CleanupAndBail;
    
    theImageSize.h = thePictInfo.sourceRect.right - thePictInfo.sourceRect.left;
    theImageSize.v = thePictInfo.sourceRect.bottom - thePictInfo.sourceRect.top;
    
    theInfo->offscreen = CreateOffscreen ( (thePictInfo.depth <= 8) ? thePictInfo.theColorTable : nil,
                                            theImageSize.h, theImageSize.v, thePictInfo.depth,
                                            kNoFlags );
    if ( theInfo->offscreen == nil )    goto CleanupAndBail;
    
    theInfo->hScrollBar = GetNewControl ( kScrollBar, theWindow );
    theInfo->vScrollBar = GetNewControl ( kScrollBar, theWindow );
    if ( theInfo->hScrollBar == nil || theInfo->vScrollBar == nil)
        goto CleanupAndBail;
    
    
    // Setup the correct control values
    SetControlMinimum ( theInfo->hScrollBar, 0 );
    SetControlMinimum ( theInfo->vScrollBar, 0 );
    SetControlValue ( theInfo->hScrollBar, 0 );
    SetControlValue ( theInfo->vScrollBar, 0 );
    
    SizeScrollBars ( theWindow );
    
    theErr = DrawPictToOffscreen ( thePict, theInfo->offscreen );
    if ( theErr )   goto CleanupAndBail;
    
    SelectWindow ( theWindow );
    ShowWindow ( theWindow );
    
    *windowRef = theWindow;
    
    if ( thePict )
        ReleaseResource ( (Handle) thePict );
        
    return noErr;
    
CleanupAndBail:
    
    // Don't forget to free any storage we've used so far
    if ( thePict )
        ReleaseResource ( (Handle) thePict );
    
    DestroyWindow ( theWindow );
    
    return theErr;
}
 
 
 
static GWorldPtr CreateOffscreen ( CTabHandle theCTabHndl, SInt16 theXsize, SInt16 theYsize,
                                    SInt16 theBitDepth, GWorldFlags theFlags )
{
    GWorldPtr   theGWorld = nil;
    QDErr       theErr;
    Rect        theRect;
    
    
    SetRect ( &theRect, 0, 0, theXsize, theYsize );
    theErr = NewGWorld ( &theGWorld, theBitDepth, &theRect, theCTabHndl, nil, theFlags );
    
    #if WARNINGS
    if ( theErr )
        DebugStrNum ( "\p CreateOffscreen: ", theErr );
    #endif
    
    return theGWorld;
}
 
 
 
static OSErr DrawPictToOffscreen ( PicHandle thePictHndl, GWorldPtr theOffscreen )
{
    PixMapHandle    theGWorldPMHndl;
    GDHandle        saveGDevice;
    CGrafPtr        saveGWorld;
    OSErr           theErr = noErr;
    
    
    GetGWorld ( &saveGWorld, &saveGDevice );
    SetGWorld ( theOffscreen, nil );
    
    // We'll initialize out port settings here.
    // If you don't set the foreground and background colors to black
    // and white, colorisation can occur during the call to CopyBits.
    RGBForeColor ( &kRGBBlack );                            
    RGBBackColor ( &kRGBWhite );
    // Reset the transfer mode
    PenMode ( srcCopy );                                    
    
    
    theGWorldPMHndl = GetGWorldPixMap ( theOffscreen );
    LockPixels ( theGWorldPMHndl );
    
    EraseRect ( &theOffscreen->portRect );
    // render the image into the offscreen buffer
    HLock ( (Handle) thePictHndl );
    DrawPicture ( thePictHndl, &theOffscreen->portRect );
    HUnlock ( (Handle) thePictHndl );
    
    UnlockPixels ( theGWorldPMHndl );
    SetGWorld ( saveGWorld, saveGDevice );
    
    return  theErr;
}
 
 
 
static OSErr DrawOffscreenToWindow ( GWorldPtr theOffscreen, WindowPtr theWindow )
{
    OSErr           theErr = noErr;
    CGrafPtr        savePort = nil;
    GDHandle        saveGDevice;
    PixMapHandle    thePixMapHndl = nil;
    RGBColor        saveForeColor,
                    saveBackColor;
    Rect            sourceRect,
                    destRect;
    
    
    GetGWorld ( &savePort, &saveGDevice );
    SetGWorld ( theOffscreen, nil );
    GetForeColor ( &saveForeColor );
    GetBackColor ( &saveBackColor );
    
    RGBForeColor ( &kRGBBlack );
    RGBBackColor ( &kRGBWhite );
    thePixMapHndl = GetGWorldPixMap ( theOffscreen );
    if ( PixMap32Bit ( thePixMapHndl ) )                    // if 32bit mode needed == true
        SaveSetMMUMode ( true );
    if ( !LockPixels ( thePixMapHndl ) )
        goto CleanupAndBail;
        
    sourceRect = theOffscreen->portRect;
    destRect = theWindow->portRect;
    destRect.right -= kScrollBarWidth;                      // exclude scrollbar area
    destRect.bottom -= kScrollBarWidth;
    if ( !EqualRect ( &sourceRect, &destRect ) )
        sourceRect = GetWindowVisibleRect ( theWindow, destRect.right, destRect.bottom );
    SetGWorld ( savePort, saveGDevice );
    savePort = nil;
    
    CopyBits ( (BitMap*) *thePixMapHndl, (BitMap*) &theWindow->portBits,
                    &sourceRect, &destRect, srcCopy, nil);
    
    
CleanupAndBail:
    
    if ( savePort )
        SetGWorld ( savePort, saveGDevice );
    
    RGBForeColor ( &saveForeColor );
    RGBBackColor ( &saveBackColor );
    UnlockPixels ( thePixMapHndl );
    SaveSetMMUMode ( false );
    
    return theErr;
}
 
 
 
static void DrawClippedScrollBarLines ( WindowRef theWindow )
{
    CGrafPtr    thePort;
    Rect        theRect;
    
    
    thePort = GetWindowPort ( theWindow );
    theRect = thePort->portRect;
    
    MoveTo ( theRect.left, theRect.bottom - kScrollBarWidthAdjust );
    LineTo ( theRect.right - kScrollBarWidthAdjust, theRect.bottom - kScrollBarWidthAdjust );
    MoveTo ( theRect.right - kScrollBarWidthAdjust, theRect.top );
    LineTo ( theRect.right - kScrollBarWidthAdjust, theRect.bottom - kScrollBarWidthAdjust );
    
    return;
}
 
 
 
static void DrawClippedGrowIcon ( WindowRef theWindow )
{
    CGrafPtr    thePort;
    RgnHandle   saveClip = nil;
    Rect        newClip;
    
    
    thePort = GetWindowPort ( theWindow );
    saveClip = NewRgn ( );
    GetClip ( saveClip );
    
    newClip = thePort->portRect;
    newClip.top = newClip.bottom - kScrollBarWidth;
    newClip.left = newClip.right - kScrollBarWidth;
    ClipRect ( &newClip );
    DrawGrowIcon ( theWindow );
    
    SetClip ( saveClip );
    DisposeRgn ( saveClip );
    
    return;
}
 
 
 
static Rect GetWindowVisibleRect ( WindowRef theWindow, SInt16 theSizeX, SInt16 theSizeY )
{
    Rect            displayRect;
    SInt16          theValueX,
                    theValueY;
    tWindowInfoPtr  theInfo;
    
    theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
    theValueX = GetControlValue ( theInfo->hScrollBar );
    theValueY = GetControlValue ( theInfo->vScrollBar );
    SetRect ( &displayRect, theValueX, theValueY, theValueX + theSizeX, theValueY + theSizeY );
    
    return displayRect;
}
 
 
 
static void SaveSetMMUMode ( Boolean bIsSaveMode )
{
    static signed char      theAddrMode = true32b;
    static Boolean          bSwapModeNeeded = false;
    
    if ( bIsSaveMode )
    {
        if ( GetMMUMode ( ) == false32b )           // get current addr mode
        {
            bSwapModeNeeded = true;                 // in 24 bit mode swap needed
            SwapMMUMode ( &theAddrMode );           // switch to 32 bit mode
        }
    }
    else if ( bSwapModeNeeded )
        SwapMMUMode ( &theAddrMode );
        
    return;
}
 
 
 
static void HandleContentClick ( WindowRef theWindow, EventRecord* event )
{
    #pragma unused(theWindow)
    Point       localPt;
    
    localPt = event->where;
    GlobalToLocal ( &localPt );
    
    // Handle any content clicks here
    
    return;
}
 
 
 
//
// Creates the storage for the data to hang off a window or dialog
//
static OSErr CreateWindowInfo ( WindowRef windowRef, Size infoSize )
{
    OSErr   theErr;
    Ptr     theInfo = nil;
    
    
    theInfo = NewPtrClear ( infoSize );
    theErr = MemError ( );
    if ( theErr )
        return theErr;
    
    SetWRefCon ( windowRef, (long) theInfo );
    
    return noErr;
}
 
 
 
//
// The GetPictInfo routine has a bug which will allow all hell to
// break loose if there isn't enough temporary memory available.
//
static pascal OSErr SafeGetPictInfo ( PicHandle thePictHandle, PictInfo* thePictInfo, SInt16 verb, SInt16 colorsRequested, SInt16 colorPickMethod, SInt16 version )
{
    const SInt32 kMinLowMem = 10240;        // 10K isn't too much to ask for!
    
    if ( TempFreeMem ( )  < kMinLowMem )
        return memFullErr;
    
    return GetPictInfo ( thePictHandle, thePictInfo, verb, colorsRequested, colorPickMethod, version );
}
 
 
 
static void SizeScrollBars ( WindowRef theWindow )
{
    SInt16          maxXsize,
                    maxYsize;
    tWindowInfoPtr  theInfo;
    
    
    theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
    
    MoveControl ( theInfo->vScrollBar, theWindow->portRect.right - kScrollBarWidthAdjust,
                                        theWindow->portRect.top - 1 );
    MoveControl ( theInfo->hScrollBar, theWindow->portRect.left - 1, 
                                        theWindow->portRect.bottom - kScrollBarWidthAdjust );
    SizeControl ( theInfo->vScrollBar, kScrollBarWidth,
                                        theWindow->portRect.bottom - theWindow->portRect.top - 13 );
    SizeControl ( theInfo->hScrollBar, theWindow->portRect.right - theWindow->portRect.left - 13,
                                        kScrollBarWidth );
                                        
    maxXsize = theInfo->offscreen->portRect.right - 
                ((theWindow->portRect.right - kScrollBarWidthAdjust) - theWindow->portRect.left);
    maxYsize = theInfo->offscreen->portRect.bottom - 
                ((theWindow->portRect.bottom - kScrollBarWidthAdjust) - theWindow->portRect.top);
    SetControlMaximum ( theInfo->vScrollBar, maxYsize );
    SetControlMaximum ( theInfo->hScrollBar, maxXsize );
    
    return;
}
 
 
 
//
// Calculates the maximum window size based on the image size and the
// current size of the main screen. The image size is used, but resticted
// by the current screen size.
//
static Point GetMaximumWindowSize ( WindowRef theWindow )
{
    GDHandle            theMainGDevHndl;
    Point               theWindowSize,
                        theImageSize;
    tWindowInfoPtr      theInfo;
    
    
    theInfo = (tWindowInfoPtr) GetWRefCon ( theWindow );
    
    // Calulate the image size
    theImageSize.h = theInfo->offscreen->portRect.right - theInfo->offscreen->portRect.left;
    theImageSize.v = theInfo->offscreen->portRect.bottom - theInfo->offscreen->portRect.top;
    theImageSize.h += kScrollBarWidth;
    theImageSize.v += kScrollBarWidth;
    
    // Calulate the screen size
    theMainGDevHndl = GetMainDevice ( );
    theWindowSize.h = (*theMainGDevHndl)->gdRect.right - (*theMainGDevHndl)->gdRect.left;
    theWindowSize.v = (*theMainGDevHndl)->gdRect.bottom - (*theMainGDevHndl)->gdRect.top;
    theWindowSize.v -= GetMBarHeight ( );
    
    // Make sure the image size is within the constraints of the screen
    theWindowSize.h = theWindowSize.h < theImageSize.h ? theWindowSize.h : theImageSize.h;
    theWindowSize.v = theWindowSize.v < theImageSize.v ? theWindowSize.v : theImageSize.v;
    
    return theWindowSize;
}