QTSkins.c

//////////
//
//  File:       QTSkins.c
//
//  Contains:   Sample code for using QuickTime's skins.
//
//  Written by: Tim Monroe
//
//  Copyright:  © 2000 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <5>      12/27/00    rtm     reworked window region geometry calculations, following StupidPlayer.c
//     <4>      12/06/00    rtm     further refinements
//     <3>      12/06/00    rtm     added Windows support
//     <2>      11/24/00    rtm     more work; now apparently working fine on Macintosh
//     <1>      11/15/00    rtm     first file 
//
//////////
 
 
//////////
//     
// header files
//     
//////////
 
#include "QTSkins.h"
 
 
//////////
//     
// global variables
//     
//////////
 
extern Handle               gValidFileTypes;                            // the list of file types that our application can open
extern long                 gFirstGITypeIndex;                          // the index in gValidFileTypes of the first graphics importer file type
 
WindowDefSpec               gDefSpec;
Movie                       gSkinnedMovieBeingOpened = NULL;            // the skinned movie being opened
 
static RgnHandle            gContentRegion = NULL;
static RgnHandle            gDragRegion = NULL;
static RgnHandle            gStructRegion = NULL;
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Utilities for creating movies with skin tracks.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTSkin_AddSkinTrack
// Add a skin track to the specified movie.
//
//////////
 
OSErr QTSkin_AddSkinTrack (Movie theMovie)
{
    Track                   myTrack = NULL;         // the movie track
    Media                   myMedia = NULL;         // the movie track's media
    Rect                    myRect;
    MediaHandler            myHandler = NULL;
    PicHandle               myContentPic = NULL;
    PicHandle               myDragPic = NULL;
    OSErr                   myErr = paramErr;
 
    if (theMovie == NULL)
        goto bail;
        
    // elicit the two pictures we need from the user
    myContentPic = QTSkin_GetPicHandleFromFile();
    if (myContentPic == NULL)
        goto bail;
 
    myDragPic = QTSkin_GetPicHandleFromFile();
    if (myDragPic == NULL)
        goto bail;
    
    // get the movie's dimensions
    GetMovieBox(theMovie, &myRect);
    MacOffsetRect(&myRect, -myRect.left, -myRect.top);
    
    // create the skin track and media
    myTrack = NewMovieTrack(theMovie, FixRatio(myRect.right, 1), FixRatio(myRect.bottom, 1), kNoVolume);
    if (myTrack == NULL)
        goto bail;
        
    myMedia = NewTrackMedia(myTrack, FOUR_CHAR_CODE('skin'), GetMovieTimeScale(theMovie), NULL, 0);
    if (myMedia == NULL)
        goto bail;
 
    myHandler = GetMediaHandler(myMedia);
    if (myHandler == NULL)
        goto bail;
 
    // add the skin content picture as public media information
    myErr = MediaSetPublicInfo(myHandler, FOUR_CHAR_CODE('skcr'), (void *)myContentPic, 0);
    if (myErr != noErr)
        goto bail;
    
    // add the skin drag picture as public media information
    myErr = MediaSetPublicInfo(myHandler, FOUR_CHAR_CODE('skdr'), (void *)myDragPic, 0);
    if (myErr != noErr)
        goto bail;
    
    // add the media to the track
    myErr = InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia), fixed1);
 
bail:
    if (myContentPic != NULL)
        KillPicture(myContentPic);
 
    if (myDragPic != NULL)
        KillPicture(myDragPic);
 
    return(myErr);
}
 
 
//////////
//
// QTSkin_GetPicHandleFromFile
// Elicit a picture file from the user and return the picture in it.
//
//////////
 
PicHandle QTSkin_GetPicHandleFromFile (void)
{
    OSType                  myTypeList = kQTFileTypeQuickTimeImage;     // any image file readable by GetGraphicsImporterForFile
    short                   myNumTypes = 1;
    FSSpec                  myPictSpec;
    QTFrameFileFilterUPP    myFilterUPP = NULL;
    GraphicsImportComponent myImporter = NULL;
    PicHandle               myPicture = NULL;
    OSErr                   myErr = noErr;
 
#if TARGET_OS_MAC
    myNumTypes = 0;
#endif
 
    // have the user select an image file
    myFilterUPP = QTFrame_GetFileFilterUPP((ProcPtr)QTSkin_FileFilterFunction);
    
    myErr = QTFrame_GetOneFileWithPreview(myNumTypes, (QTFrameTypeListPtr)&myTypeList, &myPictSpec, myFilterUPP);
    if (myErr != noErr)
        goto bail;
 
    // get a graphics importer for the image file
    myErr = GetGraphicsImporterForFile(&myPictSpec, &myImporter);
    if (myErr != noErr)
        goto bail;
    
    // convert the image into a PicHandle
    myErr = GraphicsImportGetAsPicture(myImporter, &myPicture);
    
bail:
    if (myFilterUPP != NULL)
        DisposeNavObjectFilterUPP(myFilterUPP);
 
    if (myImporter != NULL)
        CloseComponent(myImporter);
 
    return(myPicture);
}
 
 
//////////
//
// QTSkin_FileFilterFunction
// Filter files for a file-opening dialog box.
//
//////////
 
#if TARGET_OS_MAC
PASCAL_RTN Boolean QTSkin_FileFilterFunction (AEDesc *theItem, void *theInfo, void *theCallBackUD, NavFilterModes theFilterMode)
{
#pragma unused(theCallBackUD, theFilterMode)
    NavFileOrFolderInfo     *myInfo = (NavFileOrFolderInfo *)theInfo;
    OSErr                   myErr = noErr;
    
    if (gValidFileTypes == NULL)
        QTFrame_BuildFileTypeList();
 
    if (theItem->descriptorType == typeFSS)
        if (!myInfo->isFolder) {
            OSType          myType = myInfo->fileAndFolder.fileInfo.finderInfo.fdType;
            OSType          *myTypes = (OSType *)*gValidFileTypes;
            short           myCount;
            short           myIndex;
            
            // see whether the file type is in the list of image file types that QuickTime can open 
            myCount = GetHandleSize(gValidFileTypes) / sizeof(OSType);
            for (myIndex = gFirstGITypeIndex; myIndex < myCount; myIndex++)
                if (myType == myTypes[myIndex])
                    return(true);
 
            // if we got to here, it's a file we cannot open
            return(false);      
        }
 
    return(true);
}
#endif
#if TARGET_OS_WIN32
PASCAL_RTN Boolean QTSkin_FileFilterFunction (CInfoPBPtr thePBPtr)
{
#pragma unused(thePBPtr)
    return(false);
}
#endif
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Utilities for displaying movies with skin tracks.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTSkin_Init
// Perform any application-specific initialization for skinned movie support.
//
//////////
 
void QTSkin_Init (void)
{
    // set up the window definition specification structure
    gDefSpec.defType = kWindowDefProcPtr;
    gDefSpec.u.defProc = NewWindowDefUPP(QTSkin_SkinWindowDef);
}
 
 
//////////
//
// QTSkin_Stop
// Perform any application-specific tear-down for skinned movie support.
//
//////////
 
void QTSkin_Stop (void)
{
    // dispose of the window procedure UPP
    if (gDefSpec.u.defProc != NULL)
        DisposeWindowDefUPP(gDefSpec.u.defProc);
}
 
 
//////////
//
// QTSkin_SkinWindowDef
// A custom window definition procedure (WDEF) for a skinned movie window.
//
//////////
 
static PASCAL_RTN long QTSkin_SkinWindowDef (short theVarCode, WindowRef theWindow, short theMessage, long theParam)
{
#pragma unused(theVarCode)
    RgnHandle               myContentRegion = NULL;
    RgnHandle               myDragRegion = NULL;
    RgnHandle               myStructRegion = NULL;
 
    switch (theMessage) {
 
        case kWindowMsgInitialize:
            // read the skin regions from the movie data into global variables
            QTSkin_GetRegionsFromMovie(gSkinnedMovieBeingOpened);
            break;
            
        case kWindowMsgCleanUp:
        case kWindowMsgDrawGrowOutline:
        case kWindowMsgDrawGrowBox:
        case kWindowMsgDraw:
            // nothing here
            break;
          
        case kWindowMsgHitTest: {
            ApplicationDataHdl      myAppData = NULL;
            Point                   myPoint;    // location of cursor when mouse button pressed, in global coordinates
            Point                   myLocal;    // location of cursor when mouse button pressed, in local coordinates
            GrafPtr                 myPort;
            
            myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindow(QTFrame_GetWindowReferenceFromWindow(theWindow));
            if (myAppData == NULL) {
                // the window object hasn't been created yet; use our global regions
                myContentRegion = gContentRegion;
                myDragRegion = gDragRegion;
            } else {
                myContentRegion = (**myAppData).fContentRegion;
                myDragRegion = (**myAppData).fDragRegion;
            }
                
            // on entry, theParam contains the mouse location, in global screen coordinates
            myPoint.v = HiWord(theParam);
            myPoint.h = LoWord(theParam);
            
            // the content and drag regions are offset relative to the window origin
            GetPort(&myPort);           
            SetPortWindowPort(theWindow);
                            
            myLocal = myPoint;
            GlobalToLocal(&myLocal);
            
            MacSetPort(myPort);
            
            // look first to see if the mouse event is in the drag region;
            // it takes precedence over the content region
            if (PtInRgn(myLocal, myDragRegion))
                return(wInDrag);
                
            if (PtInRgn(myLocal, myContentRegion))
                return(wInContent);
            
            return(wNoHit);
        }
      
        case kWindowMsgGetFeatures:
            if (theParam != 0L)
                *(OptionBits *)theParam = kWindowCanGetWindowRegion;
            return(1);
          
        case kWindowMsgGetRegion: {
            // return the region corresponding to the specified region code, in global coordinates
            GetWindowRegionRec      *myRgnRec = (GetWindowRegionRec *)theParam;
            ApplicationDataHdl      myAppData = NULL; 
            GrafPtr                 myPort;
            Rect                    myPortBounds;
            Point                   myTopLeft;
        
            if (myRgnRec == NULL)
                return(noErr);
 
            myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindow(QTFrame_GetWindowReferenceFromWindow(theWindow));
            if (myAppData == NULL) {
                // the window object hasn't been created yet; use our global regions
                myContentRegion = gContentRegion;
                myDragRegion = gDragRegion;
                myStructRegion = gStructRegion;
            } else {
                myContentRegion = (**myAppData).fContentRegion;
                myDragRegion = (**myAppData).fDragRegion;
                myStructRegion = (**myAppData).fStructRegion;
            }
                            
            // get the top-left corner of the window, in global coordinates
            GetPort(&myPort);           
            SetPortWindowPort(theWindow);
            
#if TARGET_API_MAC_CARBON
            GetPortBounds(GetWindowPort(theWindow), &myPortBounds);
#else
            myPortBounds = theWindow->portRect;
#endif
            myTopLeft.h = myPortBounds.left;
            myTopLeft.v = myPortBounds.top;
            LocalToGlobal(&myTopLeft);
            
            MacSetPort(myPort);
 
            switch (myRgnRec->regionCode) {
                case kWindowTitleBarRgn:
                case kWindowCloseBoxRgn:
                    break;
                    
                case kWindowDragRgn:
                    MacCopyRgn(myDragRegion, myRgnRec->winRgn);
                    MacOffsetRgn(myRgnRec->winRgn, myTopLeft.h, myTopLeft.v);
                    break;
                    
                case kWindowContentRgn:
                    MacCopyRgn(myContentRegion, myRgnRec->winRgn);
                    MacOffsetRgn(myRgnRec->winRgn, myTopLeft.h, myTopLeft.v);
                    break;
                    
                case kWindowStructureRgn:
                    MacCopyRgn(myStructRegion, myRgnRec->winRgn);
                    MacOffsetRgn(myRgnRec->winRgn, myTopLeft.h, myTopLeft.v);
                    break;
                    
                default:
                    break;
            }
            
            return(noErr);
        }
       
        default:
            break;
    }
  
    return(0L);
}
 
 
//////////
//
// QTSkin_CreateSkinsWindow
// Create a new window for a movie that contains a skins track.
//
// We need to call this function only on MacOS, where it is called instead of QTFrame_CreateMovieWindow;
// on Windows, QTFrame_CreateMovieWindow eventually calls QTSkin_InitWindowData, which in turn calls SetWindowRgn
// to install the custom window shape.
//
//////////
 
WindowReference QTSkin_CreateSkinsWindow (Movie theMovie)
{
    WindowPtr               myWindow = NULL;
    WindowReference         myWindowRef = NULL;
    Rect                    myRect = {10, 60, 200, 200};
 
    // keep track of the movie for which we are creating a window
    gSkinnedMovieBeingOpened = theMovie;
 
    // call CreateCustomWindow to create a window using our custom window definition procedure
    CreateCustomWindow(&gDefSpec, kDocumentWindowClass, kWindowNoAttributes, &myRect, &myWindow);
    if (myWindow != NULL) {
        // get the "window reference" for this window
        myWindowRef = QTFrame_GetWindowReferenceFromWindow(myWindow);
        
        // create a new window object associated with the new window
        QTFrame_CreateWindowObject(myWindowRef);
    }
    
    gSkinnedMovieBeingOpened = NULL;
    
    return(myWindowRef);
}
 
 
//////////
//
// QTSkin_ConvertPictureToRegion
// Get a region for the specified picture.
//
//////////
 
OSErr QTSkin_ConvertPictureToRegion (PicHandle thePicture, RgnHandle *theRegionPtr)
{
    Rect                    myRect;
    GWorldPtr               myGWorld = NULL;
    PixMapHandle            myPixMap = NULL;
    CGrafPtr                mySavedPort = NULL;
    GDHandle                mySavedDevice = NULL;
    RgnHandle               myRegion = NULL;
    OSErr                   myErr = noErr;
 
    if ((thePicture == NULL) || (theRegionPtr == NULL))
        return(paramErr);
 
    // get the current graphics port and device
    GetGWorld(&mySavedPort, &mySavedDevice);
    
    // get the bounding box of the picture
    myRect = (**thePicture).picFrame;
    myRect.bottom = EndianS16_BtoN(myRect.bottom);
    myRect.right = EndianS16_BtoN(myRect.right);
 
    // create a new GWorld and draw the picture into it
    myErr = QTNewGWorld(&myGWorld, k1MonochromePixelFormat, &myRect, NULL, NULL, kICMTempThenAppMemory);
    if (myGWorld == NULL)
        goto bail;
        
    SetGWorld(myGWorld, NULL);
 
    myPixMap = GetPortPixMap(myGWorld);
    if (myPixMap == NULL)
        goto bail;
 
    LockPixels(myPixMap);
    HLock((Handle)myPixMap);
    
    EraseRect(&myRect);
    DrawPicture(thePicture, &myRect);
 
    // create a new region and convert the pixmap into a region
    myRegion = NewRgn();
    myErr = MemError();
    if (myErr != noErr)
        goto bail;
        
    myErr = BitMapToRegion(myRegion, (BitMap *)*myPixMap);
    
bail:
    if (myErr != noErr) {
        if (myRegion != NULL) {
            DisposeRgn(myRegion);
            myRegion = NULL;
        }
    }
 
    if (myGWorld != NULL)
        DisposeGWorld(myGWorld);
 
    // restore the original graphics port and device
    SetGWorld(mySavedPort, mySavedDevice);
    
    *theRegionPtr = myRegion;
    
    return(myErr);
}
 
 
//////////
//
// QTSkin_InitWindowData
// Do any skin-specific initialization for the specified window.
//
//////////
 
ApplicationDataHdl QTSkin_InitWindowData (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
    OSErr                   myErr = noErr;
 
    // if we already have some window data, dump it
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData != NULL)
        QTSkin_DumpWindowData(theWindowObject);
 
    myAppData = (ApplicationDataHdl)NewHandleClear(sizeof(ApplicationDataRecord));
    if (myAppData != NULL) {
        if (gContentRegion == NULL)
            QTSkin_GetRegionsFromMovie((**theWindowObject).fMovie);
 
        (**myAppData).fContentRegion = gContentRegion;
        (**myAppData).fDragRegion = gDragRegion;
        (**myAppData).fStructRegion = gStructRegion;
    }
 
    return(myAppData);
}
 
 
//////////
//
// QTSkin_DumpWindowData
// Do any skin-specific tear-down for the specified window.
//
//////////
 
void QTSkin_DumpWindowData (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
        
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData != NULL) {
        if ((**myAppData).fContentRegion != NULL)
            DisposeRgn((**myAppData).fContentRegion);
    
        if ((**myAppData).fDragRegion != NULL)
            DisposeRgn((**myAppData).fDragRegion);
    
        if ((**myAppData).fStructRegion != NULL)
            DisposeRgn((**myAppData).fStructRegion);
 
#if TARGET_OS_WIN32
        // according to the documentation, after passing a region to SetWindowRgn, the operating system
        // owns that region; I assume this means that we do not need to delete (**myAppData).fWinHRGN
#endif
    
        DisposeHandle((Handle)myAppData);
        (**theWindowObject).fAppData = NULL;
    }
}
 
 
#if TARGET_OS_WIN32
//////////
//
// QTSkin_IsDragClick
// Return true if the mouse click whose coordinates are in lParam is in the drag region, false otherwise.
//
//////////
 
Boolean QTSkin_IsDragClick (WindowObject theWindowObject, LONG lParam)
{
    WindowObject            myWindowObject = NULL;
    ApplicationDataHdl      myAppData = NULL;
    HRGN                    myRegion = NULL;
    POINT                   myPoint;
    Boolean                 isDragClick = false;
        
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData != NULL) {
        myPoint.x = LOWORD(lParam);
        myPoint.y = HIWORD(lParam);
 
        myRegion = MacRegionToNativeRegion((**myAppData).fDragRegion);
 
        if (PtInRegion(myRegion, myPoint.x, myPoint.y))
            isDragClick = true;
            
        DeleteObject(myRegion);
    }
    
    return(isDragClick);
}
#endif
 
 
//////////
//
// QTSkin_GetRegionsFromMovie
// Get the skin regions from the specified movie.
//
//////////
 
OSErr QTSkin_GetRegionsFromMovie (Movie theMovie) 
{
    Track           myTrack = NULL;
    MediaHandler    myHandler = NULL;
    PicHandle       myPicture = NULL;
    RgnHandle       myRegion = NULL;
    MatrixRecord    myMatrix;
    OSErr           myErr = noErr;
 
    if (theMovie == NULL)
        return(paramErr);
    
    myTrack = GetMovieIndTrackType(theMovie, 1, FOUR_CHAR_CODE('skin'), movieTrackCharacteristic);
    if (myTrack != NULL) {
        myHandler = GetMediaHandler(GetTrackMedia(myTrack));
        if (myHandler != NULL) {
        
            // get the current movie matrix
            GetMovieMatrix(theMovie, &myMatrix);
            
            myPicture = (PicHandle)NewHandle(0);
            if (myPicture == NULL)
                goto bail;
                
            // get the content region picture
            myErr = MediaGetPublicInfo(myHandler, FOUR_CHAR_CODE('skcr'), myPicture, NULL);
            if (myErr != noErr)
                goto bail;
 
            // convert it to a region
            myErr = QTSkin_ConvertPictureToRegion(myPicture, &myRegion);
            gContentRegion = myRegion;
            if (myErr != noErr)
                goto bail;
 
            // scale that region so the window scales with the movie
            myErr = TransformRgn(&myMatrix, gContentRegion);
            if (myErr != noErr)
                goto bail;
                
#if TARGET_OS_WIN32
            (**myAppData).fWinHRGN = MacRegionToNativeRegion(gContentRegion);
            if ((**myAppData).fWinHRGN != NULL) {
                RECT        myRect;
                int         myResult;
 
                GetRgnBox((**myAppData).fWinHRGN, &myRect);
                // the coordinates of a window region are relative to the upper-left corner of the window
                // (not to the client area of the window)
                OffsetRgn((**myAppData).fWinHRGN, -myRect.left + GetSystemMetrics(SM_CXFRAME), -myRect.top + GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME));
                myResult = SetWindowRgn((**theWindowObject).fWindow, (**myAppData).fWinHRGN, true);
                if (myResult == 0) {
                    // SetWindowRgn failed
                    DeleteObject((**myAppData).fWinHRGN);
                    (**myAppData).fWinHRGN = NULL;
                    goto bail;
                }
            }
#endif
 
            // repeat with drag region picture
            myErr = MediaGetPublicInfo(myHandler, FOUR_CHAR_CODE('skdr'), myPicture, NULL);
            if (myErr != noErr)
                goto bail;
            
            // convert it to a region
            myErr = QTSkin_ConvertPictureToRegion(myPicture, &myRegion);
            gDragRegion = myRegion;
            if (myErr != noErr)
                goto bail;
            
            // scale that region so the window scales with the movie
            myErr = TransformRgn(&myMatrix, gDragRegion);
            if (myErr != noErr)
                goto bail;
                
            // copy the content region into the structure region
            gStructRegion = NewRgn();
            MacCopyRgn(gContentRegion, gStructRegion);
        }
    }
 
bail:
    return(myErr);
}
 
 
///////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Miscellaneous utilities.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////
 
//////////
//
// QTSkin_IsSkinnedMovie
// Is the specified movie a skinned movie?
//
//////////
 
Boolean QTSkin_IsSkinnedMovie (Movie theMovie) 
{
    return(GetMovieIndTrackType(theMovie, 1, FOUR_CHAR_CODE('skin'), movieTrackCharacteristic) != NULL);
}