QTWiredSprite.c Sample Code

This appendix includes sample code from QTWiredSprite.c. Note this is only a partial code listing. You can download the full sample code at QuickTime Web site at

http://developer.apple.com/samplecode/Sample_Code/QuickTime/Wired_Movies_and_Sprites.htm

It is also available on the QuickTime SDK.

Refer to Chapter 3, Sprite Media Handler, and Chapter 4, Authoring Wired Movies and Sprite Animations, for information on how to use this sample code in your application.

//  File:       QTWiredSprites.c
//
//  Contains:   QuickTime wired sprites support for QuickTime movies.
//
 
//  Written by: Sean Allen
//  Revised by: Chris Flick and Tim Monroe
//              Based (heavily!) on the existing MakeActionSpriteMovie.c
//              code written by Sean Allen.
//
//  Copyright:  © 1997-1999 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <2>      03/26/98    rtm     made fixes for Windows compiles
//     <1>      03/25/98    rtm     first file; integrated existing code
//                                                  with shell framework
//
//
//  This sample code creates a wired sprite movie containing one sprite
//track. The sprite track contains six sprites: two penguins and four
//buttons.
//
//  The four buttons are initially invisible. When the mouse enters (or
//  "rolls over") a button, it appears.
//  When the mouse is clicked inside a button, its image changes to its
//"pressed" image. When the mouse
//  is released, its image changes back to its "unpressed" image. If the
//  mouse is released inside the button,
//  an action is triggered. The buttons perform the actions of go to
//  beginning of movie, step backward,
//  step forward, and go to end of movie.
 
//
//  The first penguin shows all of the buttons when the mouse enters it,
//  and hides them when the mouse exits.
//  The first penguin is the only sprite that has properties that are
//  overriden by the override sprite samples.
//  These samples override its matrix (in order to move it) and its image
//  index (in order to make it "waddle").
//
//  When the mouse is clicked on the second penguin, it changes its image
//  index to its "eyes closed" image.
//  When the mouse is released, it changes back to its normal image. This
//  makes it appear to blink when clicked on.
//  When the mouse is released over the penguin, several actions are
//  triggered. Both penguins' graphics states are
//  toggled between copyMode and blendMode, and the movie's rate is
//  toggled between 0 and one.
//
//  The second penguin moves once per second. This occurs whether the
//  movie's rate is currently 0 or one,
//  because it is being triggered by a gated idle event. When the penguin
//  receives the idle event, it changes
//  its matrix using an action which uses min, max, delta, and wraparound
//  options.
//
//  The movie's looping mode is set to palindrome by a frame-loaded
//  action.
//
//  So, our general strategy is as follows (though perhaps not in the
//  order listed):
//
//      (1) Create a new movie file with a single sprite track.
//      (2) Assign the "no controller" movie controller to the movie.
//      (3) Set the sprite track's background color, idle event
//          frequency, and hasActions properties.
//      (4) Convert our PICT resources to animation codec images with
//          transparency.
//      (5) Create a key frame sample containing six sprites and all of
//          their shared images.
//      (6) Assign the sprites their initial property values.
//      (7) Create a frameLoaded event for the key frame.
//      (8) Create some override samples that override the matrix and
//          image index properties of the first penguin sprite.
//
 
//  NOTES:
//
//  *** (1) ***
//  There are event types other that mouse related events (for instance,
//  Idle and FrameLoaded).
//  Idle events are independent of the movie's rate, and they can be
//  gated so they are send at most
//  every n ticks. In our sample movie, the second penguin moves when the
//  movie's rate is 0,
//  and moves only once per second because of the value of the sprite
//  track's idleEventFrequencey property.
//
//  *** (2) ***
//  Multiple actions may be executed in response to a single event. In
//  our sample movie, rolling over
//  the first penguin shows and hides four different buttons.
//
//  *** (3) ***
//  Actions may target any sprite or track in the movie. In our sample
//  movie, clicking on one penguin
//  changes the graphics mode of the other.
//
//  *** (4) ***
//  Conditional and looping control structures are supported. In our
// sample movie, the second penguin
//  uses the "case statement" action.
//
//  *** (5) ***
//  Sprite track variables that have not been set have a default value of
//  0. (The second penguin's
//  conditional code relies on this.)
//
//  *** (6) ***
//  Wired sprites were previously known as "action sprites". Don't let
//  the names of some of the utility
//  functions confuse you. We'll try to update the source code as time
//  permits.
//
//  *** (7) ***
//  Penguins don't fly, but I hear they totally shred halfpipes on
//  snowboards.
//
//////////
// header files
#include "QTWiredSprites.h"
 
 
//////////
//
// QTWired_CreateWiredSpritesMovie
// Create a QuickTime movie containing a wired sprites track.
//
//////////
 
OSErr QTWired_CreateWiredSpritesMovie (void)
{
    short                   myResRefNum = 0;
    Movie                   myMovie = NULL;
    Track                   myTrack;
    Media                   myMedia;
    StandardFileReply       myReply;
    QTAtomContainer         mySample = NULL;
    QTAtomContainer         myActions = NULL;
    QTAtomContainer         myBeginButton, myPrevButton, myNextButton,
                            myEndButton;
    QTAtomContainer         myPenguinOne, myPenguinTwo,
                            myPenguinOneOverride;
    QTAtomContainer         myBeginActionButton, myPrevActionButton,
                            myNextActionButton, myEndActionButton;
    QTAtomContainer         myPenguinOneAction, myPenguinTwoAction;
    RGBColor                myKeyColor;
    Point                   myLocation;
    short                   isVisible, myLayer, myIndex, myResID, i,
                            myDelta;
    Boolean                 hasActions;
    long                    myFlags = createMovieFileDeleteCurFile |
                                        createMovieFileDontCreateResFile;
    OSType                  myType = FOUR_CHAR_CODE('none');
    UInt32                  myFrequency;
    QTAtom                  myEventAtom;
    long                    myLoopingFlags;
    ModifierTrackGraphicsModeRecord     myGraphicsMode;
    OSErr                   myErr = noErr;
 
    //////////
    //
    // create a new movie file and set its controller type
    //
    //////////
 
    // ask the user for the name of the new movie file
    StandardPutFile("\pSprite movie file name:", "\pSprite.mov",
                    &myReply);
    if (!myReply.sfGood)
        goto bail;
 
    // create a movie file for the destination movie
    myErr = CreateMovieFile(&myReply.sfFile, FOUR_CHAR_CODE('TVOD'), 0,
                            myFlags, &myResRefNum, &myMovie);
    if (myErr != noErr)
        goto bail;
 
    // select the "no controller" movie controller
    myType = EndianU32_NtoB(myType);
    SetUserDataItem(GetMovieUserData(myMovie), &myType, sizeof(myType),
                    kUserDataMovieControllerType, 1);
 
    //////////
    //
    // create the sprite track and media
    //
    //////////
 
    myTrack = NewMovieTrack(myMovie, ((long)kSpriteTrackWidth << 16),
                            ((long)kSpriteTrackHeight << 16), kNoVolume);
    myMedia = NewTrackMedia(myTrack, SpriteMediaType, kSpriteMediaTimeScale, NULL, 0);
 
    //////////
    //
    // create a key frame sample containing six sprites and all of their
    // shared images
    //
    //////////
 
    // create a new, empty key frame sample
    myErr = QTNewAtomContainer(&mySample);
    if (myErr != noErr)
        goto bail;
 
    myKeyColor.red = 0xffff;                        // white
    myKeyColor.green = 0xffff;
    myKeyColor.blue = 0xffff;
 
    // add images to the key frame sample
    AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonUp,
                &myKeyColor, kGoToBeginningButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToBeginningButtonDown,
                &myKeyColor, kGoToBeginningButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonUp, &myKeyColor,
                kGoToEndButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToEndButtonDown,
                &myKeyColor, kGoToEndButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonUp,
                &myKeyColor, kGoToPrevButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToPrevButtonDown,
                &myKeyColor, kGoToPrevButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonUp,
                &myKeyColor, kGoToNextButtonUpIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kGoToNextButtonDown,
                &myKeyColor, kGoToNextButtonDownIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kPenguinForward, &myKeyColor,
                kPenguinForwardIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kPenguinLeft, &myKeyColor,
                kPenguinLeftIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kPenguinRight, &myKeyColor,
                kPenguinRightIndex, NULL, NULL);
    AddPICTImageToKeyFrameSample(mySample, kPenguinClosed, &myKeyColor,
                kPenguinClosedIndex, NULL, NULL);
 
    for (myIndex = kPenguinDownRightCycleStartIndex, myResID =
    kWalkDownRightCycleStart; myIndex <= kPenguinDownRightCycleEndIndex;
    myIndex++, myResID++)
        AddPICTImageToKeyFrameSample(mySample, myResID, &myKeyColor,
                myIndex, NULL, NULL);
 
    // assign group IDs to the images
    AssignImageGroupIDsToKeyFrame(mySample);
 
    //////////
    //
    // add samples to the sprite track's media
    //
    //////////
 
    BeginMediaEdits(myMedia);
 
    // go to beginning button with no actions
    myErr = QTNewAtomContainer(&myBeginButton);
    if (myErr != noErr)
        goto bail;
    myLocation.h    = (1 * kSpriteTrackWidth / 8) -
                        (kStartEndButtonWidth / 2);
    myLocation.v    = (4 * kSpriteTrackHeight / 5) -
                        (kStartEndButtonHeight / 2);
    isVisible       = false;
    myLayer         = 1;
    myIndex         = kGoToBeginningButtonUpIndex;
    myErr = SetSpriteData(myBeginButton, &myLocation, &isVisible,
                            &myLayer, &myIndex, NULL, NULL, myActions);
    if (myErr != noErr)
        goto bail;
 
    // go to previous button with no actions
    myErr = QTNewAtomContainer(&myPrevButton);
    if (myErr != noErr)
        goto bail;
    myLocation.h    = (3 * kSpriteTrackWidth / 8) -
                (kNextPrevButtonWidth / 2);
    myLocation.v    = (4 * kSpriteTrackHeight / 5) -
                (kStartEndButtonHeight / 2);
    isVisible       = false;
    myLayer         = 1;
    myIndex         = kGoToPrevButtonUpIndex;
    myErr = SetSpriteData(myPrevButton, &myLocation, &isVisible,
                            &myLayer, &myIndex, NULL, NULL, myActions);
    if (myErr != noErr)
        goto bail;
 
    // go to next button with no actions
    myErr = QTNewAtomContainer(&myNextButton);
    if (myErr != noErr)
        goto bail;
    myLocation.h    = (5 * kSpriteTrackWidth / 8) -
                    (kNextPrevButtonWidth / 2);
    myLocation.v    = (4 * kSpriteTrackHeight / 5) -
                    (kStartEndButtonHeight / 2);
    isVisible       = false;
    myLayer         = 1;
    myIndex         = kGoToNextButtonUpIndex;
    myErr = SetSpriteData(myNextButton, &myLocation, &isVisible,
                            &myLayer, &myIndex, NULL, NULL, myActions);
    if (myErr != noErr)
        goto bail;
 
    // go to end button with no actions
    myErr = QTNewAtomContainer(&myEndButton);
    if (myErr != noErr)
        goto bail;
    myLocation.h    = (7 * kSpriteTrackWidth / 8) -
                        (kStartEndButtonWidth / 2);
    myLocation.v    = (4 * kSpriteTrackHeight / 5) -
                        (kStartEndButtonHeight / 2);
    isVisible       = false;
    myLayer         = 1;
    myIndex         = kGoToEndButtonUpIndex;
    myErr = SetSpriteData(myEndButton, &myLocation, &isVisible, &myLayer,
                            &myIndex, NULL, NULL, myActions);
    if (myErr != noErr)
        goto bail;
 
    // first penguin sprite with no actions
    myErr = QTNewAtomContainer(&myPenguinOne);
    if (myErr != noErr)
        goto bail;
    myLocation.h    = (3 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
    myLocation.v    = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
    isVisible       = true;
    myLayer         = 2;
    myIndex         = kPenguinDownRightCycleStartIndex;
    myGraphicsMode.graphicsMode = blend;
    myGraphicsMode.opColor.red = myGraphicsMode.opColor.green =
                myGraphicsMode.opColor.blue = 0x8FFF;           // grey
    myErr = SetSpriteData(myPenguinOne, &myLocation, &isVisible,
                &myLayer, &myIndex, &myGraphicsMode, NULL, myActions);
    if (myErr != noErr)
        goto bail;
 
    // second penguin sprite with no actions
    myErr = QTNewAtomContainer(&myPenguinTwo);
    if (myErr != noErr)
        goto bail;
    myLocation.h    = (5 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
    myLocation.v    = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
    isVisible       = true;
    myLayer         = 3;
    myIndex         = kPenguinForwardIndex;
    myErr = SetSpriteData(myPenguinTwo, &myLocation, &isVisible,
                            &myLayer, &myIndex, NULL, NULL, myActions);
    if (myErr != noErr)
        goto bail;
 
    //////////
    //
    // add actions to the six sprites
    //
    //////////
 
    // add go to beginning button
    myErr = QTCopyAtom(myBeginButton, kParentAtomIsContainer, &myBeginActionButton);
    if (myErr != noErr)
        goto bail;
 
    AddSpriteSetImageIndexAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL,
        kGoToBeginningButtonDownIndex, NULL);
    AddSpriteSetImageIndexAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0,
        NULL, kGoToBeginningButtonUpIndex, NULL);
    AddMovieGoToBeginningAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL,
        true, NULL);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL,
        false, NULL);
    AddSpriteToSample(mySample, myBeginActionButton,
        kGoToBeginningSpriteID);
        QTDisposeAtomContainer(myBeginActionButton);
 
 
    // add go to prev button
    myErr = QTCopyAtom(myPrevButton, kParentAtomIsContainer,
                        &myPrevActionButton);
    if (myErr != noErr)
        goto bail;
 
    AddSpriteSetImageIndexAction(myPrevActionButton,
        kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL,
        kGoToPrevButtonDownIndex, NULL);
    AddSpriteSetImageIndexAction(myPrevActionButton,
        kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0,
        NULL, kGoToPrevButtonUpIndex, NULL);
    AddMovieStepBackwardAction(myPrevActionButton,
        kParentAtomIsContainer, kQTEventMouseClickEndTriggerButton);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL,
        true, NULL);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL,
        false, NULL);
    AddSpriteToSample(mySample, myPrevActionButton, kGoToPrevSpriteID);
    QTDisposeAtomContainer(myPrevActionButton);
 
    // add go to next button
    myErr = QTCopyAtom(myNextButton, kParentAtomIsContainer,
        &myNextActionButton);
    if (myErr != noErr)
        goto bail;
 
    AddSpriteSetImageIndexAction(myNextActionButton,
        kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL,
        kGoToNextButtonDownIndex, NULL);
    AddSpriteSetImageIndexAction(myNextActionButton,
        kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0,
        NULL, kGoToNextButtonUpIndex, NULL);
    AddMovieStepForwardAction(myNextActionButton, kParentAtomIsContainer,
        kQTEventMouseClickEndTriggerButton);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL,
        true, NULL);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL,
        false, NULL);
    AddSpriteToSample(mySample, myNextActionButton, kGoToNextSpriteID);
    QTDisposeAtomContainer(myNextActionButton);
 
    // add go to end button
    myErr = QTCopyAtom(myEndButton, kParentAtomIsContainer,
        &myEndActionButton);
    if (myErr != noErr)
        goto bail;
 
    AddSpriteSetImageIndexAction(myEndActionButton,
        kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL,
        kGoToEndButtonDownIndex, NULL);
    AddSpriteSetImageIndexAction(myEndActionButton,
        kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0,
        NULL, kGoToEndButtonUpIndex, NULL);
    AddMovieGoToEndAction(myEndActionButton, kParentAtomIsContainer,
        kQTEventMouseClickEndTriggerButton);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseEnter, 0, NULL, 0, 0, NULL,
        true, NULL);
    AddSpriteSetVisibleAction(myBeginActionButton,
        kParentAtomIsContainer, kQTEventMouseExit, 0, NULL, 0, 0, NULL,
        false, NULL);
    AddSpriteToSample(mySample, myEndActionButton, kGoToEndSpriteID);
    QTDisposeAtomContainer(myEndActionButton);
 
    // add penguin one
    myErr = QTCopyAtom(myPenguinOne, kParentAtomIsContainer,
                        &myPenguinOneAction);
    if (myErr != noErr)
        goto bail;
 
    // show the buttons on mouse enter and hide them on mouse exit
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToBeginningSpriteID, true, NULL);
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToBeginningSpriteID, false, NULL);
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToPrevSpriteID, true, NULL);
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToPrevSpriteID, false, NULL);
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToNextSpriteID, true, NULL);
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToNextSpriteID, false, NULL);
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseEnter, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToEndSpriteID, true, NULL);
    AddSpriteSetVisibleAction(myPenguinOneAction, kParentAtomIsContainer,
        kQTEventMouseExit, 0, NULL, 0, kTargetSpriteID,
        (void *)kGoToEndSpriteID, false, NULL);
    AddSpriteToSample(mySample, myPenguinOneAction, kPenguinOneSpriteID);
    QTDisposeAtomContainer(myPenguinOneAction);
 
    // add penguin two
    myErr = QTCopyAtom(myPenguinTwo, kParentAtomIsContainer,
                        &myPenguinTwoAction);
    if (myErr != noErr)
        goto bail;
 
    // blink when clicked on
    AddSpriteSetImageIndexAction(myPenguinTwoAction,
        kParentAtomIsContainer, kQTEventMouseClick, 0, NULL, 0, 0, NULL,
        kPenguinClosedIndex, NULL);
    AddSpriteSetImageIndexAction(myPenguinTwoAction,
        kParentAtomIsContainer, kQTEventMouseClickEnd, 0, NULL, 0, 0,
        NULL, kPenguinForwardIndex, NULL);
 
    AddQTEventAtom(myPenguinTwoAction, kParentAtomIsContainer,
        kQTEventMouseClickEndTriggerButton, &myEventAtom);
 
    // toggle the movie rate and both of the birds' graphics modes
    QTWired_AddPenguinTwoConditionalActions(myPenguinTwoAction,
        myEventAtom);
 
    QTWired_AddWraparoundMatrixOnIdle(myPenguinTwoAction);
 
    AddSpriteToSample(mySample, myPenguinTwoAction, kPenguinTwoSpriteID);
    QTDisposeAtomContainer(myPenguinTwoAction);
 
    // add an action for when the key frame is loaded, to set the movie's
    // looping mode to palindrome;
    // note that this will actually be triggered every time the key frame
    // is reloaded,
    // so if the operation was expensive we could use a conditional to
    // test if we've already done it
    myLoopingFlags = loopTimeBase | palindromeLoopTimeBase;
    AddMovieSetLoopingFlagsAction(mySample, kParentAtomIsContainer,
        kQTEventFrameLoaded, myLoopingFlags);
 
    // add the key frame sample to the sprite track media
    //
    // to add the sample data in a compressed form, you would use a
    // QuickTime DataCodec to perform the
    // compression; replace the call to the utility
    // AddSpriteSampleToMedia with a call to the utility
    // AddCompressedSpriteSampleToMedia to do this
 
    AddSpriteSampleToMedia(myMedia, mySample, kSpriteMediaFrameDuration,
        true, NULL);
    //AddCompressedSpriteSampleToMedia(myMedia, mySample,
    // kSpriteMediaFrameDuration, true, zlibDataCompressorSubType, NULL);
 
    //////////
    //
    // add a few override samples to move penguin one and change its
    // image index
    //
    //////////
 
    // original penguin one location
    myLocation.h    = (3 * kSpriteTrackWidth / 8) - (kPenguinWidth / 2);
    myLocation.v    = (kSpriteTrackHeight / 4) - (kPenguinHeight / 2);
 
    myDelta = (kSpriteTrackHeight / 2) / kNumOverrideSamples;
    myIndex = kPenguinDownRightCycleStartIndex;
 
    for (i = 1; i <= kNumOverrideSamples; i++) {
        QTRemoveChildren(mySample, kParentAtomIsContainer);
        QTNewAtomContainer(&myPenguinOneOverride);
 
        myLocation.h += myDelta;
        myLocation.v += myDelta;
        myIndex++;
        if (myIndex > kPenguinDownRightCycleEndIndex)
            myIndex = kPenguinDownRightCycleStartIndex;
 
        SetSpriteData(myPenguinOneOverride, &myLocation, NULL, NULL,
            &myIndex, NULL, NULL, NULL);
        AddSpriteToSample(mySample, myPenguinOneOverride,
            kPenguinOneSpriteID);
        AddSpriteSampleToMedia(myMedia, mySample,
            kSpriteMediaFrameDuration, false, NULL);
        QTDisposeAtomContainer(myPenguinOneOverride);
    }
 
    EndMediaEdits(myMedia);
 
    // add the media to the track
    InsertMediaIntoTrack(myTrack, 0, 0, GetMediaDuration(myMedia),
                            fixed1);
 
    //////////
    //
    // set the sprite track properties
    //
    //////////
    {
        QTAtomContainer     myTrackProperties;
        RGBColor            myBackgroundColor;
 
        // add a background color to the sprite track
        myBackgroundColor.red = EndianU16_NtoB(0x8000);
        myBackgroundColor.green = EndianU16_NtoB(0);
        myBackgroundColor.blue = EndianU16_NtoB(0xffff);
 
        QTNewAtomContainer(&myTrackProperties);
        QTInsertChild(myTrackProperties, 0,
                        kSpriteTrackPropertyBackgroundColor, 1, 1,
                        sizeof(RGBColor), &myBackgroundColor, NULL);
 
        // tell the movie controller that this sprite track has actions
        hasActions = true;
        QTInsertChild(myTrackProperties, 0,
                        kSpriteTrackPropertyHasActions, 1, 1,
                        sizeof(hasActions), &hasActions, NULL);
 
        // tell the sprite track to generate QTIdleEvents
        myFrequency = EndianU32_NtoB(60);
        QTInsertChild(myTrackProperties, 0,
                        kSpriteTrackPropertyQTIdleEventsFrequency, 1, 1,
                        sizeof(myFrequency), &myFrequency, NULL);
        myErr = SetMediaPropertyAtom(myMedia, myTrackProperties);
        if (myErr != noErr)
            goto bail;
 
        QTDisposeAtomContainer(myTrackProperties);
    }
 
    //////////
    //
    // finish up
    //
    //////////
 
    // add the movie resource to the movie file
    myErr = AddMovieResource(myMovie, myResRefNum, 0,
                                myReply.sfFile.name);
bail:
    if (mySample != NULL)
        QTDisposeAtomContainer(mySample);
 
    if (myBeginButton != NULL)
        QTDisposeAtomContainer(myBeginButton);
 
    if (myPrevButton != NULL)
        QTDisposeAtomContainer(myPrevButton);
 
    if (myNextButton != NULL)
        QTDisposeAtomContainer(myNextButton);
 
    if (myEndButton != NULL)
        QTDisposeAtomContainer(myEndButton);
 
    if (myResRefNum != 0)
        CloseMovieFile(myResRefNum);
 
    if (myMovie != NULL)
        DisposeMovie(myMovie);
 
    return(myErr);
}
 
 
//////////
//
// QTWired_AddPenguinTwoConditionalActions
// Add actions to the second penguin that transforms them into a two
// state button
// that plays or pauses the movie.
//
// We are relying on the fact that a "GetVariable" for a variable ID
// which has never been set
// will return 0. If we needed a different default value, we could
// initialize it using the
// frameLoaded event.
//
// A higher-level description of the logic is:
//
//  On MouseUpInside
//     If (GetVariable(DefaultTrack, 1) = 0)
//        SetMovieRate(1)
//        SetSpriteGraphicsMode(DefaultSprite, { blend, grey } )
//        SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
//              { ditherCopy, white } )
//        SetVariable(DefaultTrack, 1, 1)
//     ElseIf (GetVariable(DefaultTrack, 1) = 1)
//        SetMovieRate(0)
//        SetSpriteGraphicsMode(DefaultSprite, { ditherCopy, white })
//        SetSpriteGraphicsMode(GetSpriteByID(DefaultTrack, 5),
//              { blend, grey })
//        SetVariable(DefaultTrack, 1, 0)
//     Endif
//  End
//
//////////
 
OSErr QTWired_AddPenguinTwoConditionalActions (QTAtomContainer
                                    theContainer, QTAtom theEventAtom)
{
    QTAtom          myNewActionAtom, myNewParamAtom, myConditionalAtom;
    QTAtom          myExpressionAtom, myOperatorAtom, myActionListAtom;
    short           myParamIndex, myConditionIndex, myOperandIndex;
    float           myConstantValue;
    QTAtomID        myVariableID;
    ModifierTrackGraphicsModeRecord     myBlendMode, myCopyMode;
    OSErr                               myErr = noErr;
 
    myBlendMode.graphicsMode = blend;
    myBlendMode.opColor.red = myBlendMode.opColor.green =
                        myBlendMode.opColor.blue = 0x8fff;      // grey
 
    myCopyMode.graphicsMode = ditherCopy;
    myCopyMode.opColor.red = myCopyMode.opColor.green =
                        myCopyMode.opColor.blue = 0xffff;       // white
 
    AddActionAtom(theContainer, theEventAtom, kActionCase,
                    &myNewActionAtom);
 
    myParamIndex = 1;
    AddActionParameterAtom(theContainer, myNewActionAtom, myParamIndex,
                            0, NULL, &myNewParamAtom);
 
    // first condition
    myConditionIndex = 1;
    AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
                        &myConditionalAtom);
    AddExpressionContainerAtomType(theContainer, myConditionalAtom,
                        &myExpressionAtom);
    AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
                        &myOperatorAtom);
 
    myOperandIndex = 1;
    myConstantValue = kButtonStateOne;
    AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
                        myOperandIndex, NULL, myConstantValue);
 
    myOperandIndex = 2;
    myVariableID = kPenguinStateVariableID;
    AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
                        0, NULL, 0, myVariableID);
 
    AddActionListAtom(theContainer, myConditionalAtom,
                        &myActionListAtom);
    AddMovieSetRateAction(theContainer, myActionListAtom, 0,
                        Long2Fix(1));
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, 0, NULL, &myBlendMode, NULL);
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
        &myCopyMode, NULL);
    AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
        kPenguinStateVariableID, kButtonStateTwo, 0, NULL, 0);
 
 
    // second condition
    myConditionIndex = 2;
    AddConditionalAtom(theContainer, myNewParamAtom, myConditionIndex,
        &myConditionalAtom);
    AddExpressionContainerAtomType(theContainer, myConditionalAtom,
        &myExpressionAtom);
    AddOperatorAtom(theContainer, myExpressionAtom, kOperatorEqualTo,
        &myOperatorAtom);
 
    myOperandIndex = 1;
    myConstantValue = kButtonStateTwo;
    AddOperandAtom(theContainer, myOperatorAtom, kOperandConstant,
        myOperandIndex, NULL, myConstantValue);
 
    myOperandIndex = 2;
    myVariableID = kPenguinStateVariableID;
    AddVariableOperandAtom(theContainer, myOperatorAtom, myOperandIndex,
        0, NULL, 0, myVariableID);
 
    AddActionListAtom(theContainer, myConditionalAtom,
        &myActionListAtom);
    AddMovieSetRateAction(theContainer, myActionListAtom, 0,
        Long2Fix(0));
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, 0, NULL, &myCopyMode, NULL);
    AddSpriteSetGraphicsModeAction(theContainer, myActionListAtom, 0, 0,
        NULL, 0, kTargetSpriteID, (void *)kPenguinOneSpriteID,
        &myBlendMode, NULL);
    AddSpriteTrackSetVariableAction(theContainer, myActionListAtom, 0,
        kPenguinStateVariableID, kButtonStateOne, 0, NULL, 0);
 
 
bail:
    return(myErr);
}
 
 
//////////
//
// QTWired_AddWraparoundMatrixOnIdle
// Add beginning, end, and change matrices to the specified atom
// container.
//
//////////
 
OSErr QTWired_AddWraparoundMatrixOnIdle (QTAtomContainer theContainer)
{
    MatrixRecord    myMinMatrix, myMaxMatrix, myDeltaMatrix;
    long            myFlags = kActionFlagActionIsDelta |
                                kActionFlagParameterWrapsAround;
    QTAtom          myActionAtom;
    OSErr           myErr = noErr;
 
    myMinMatrix.matrix[0][0] = myMinMatrix.matrix[0][1] =
        myMinMatrix.matrix[0][2] = EndianS32_NtoB(0xffffffff);
    myMinMatrix.matrix[1][0] = myMinMatrix.matrix[1][1] =
        myMinMatrix.matrix[1][2] = EndianS32_NtoB(0xffffffff);
    myMinMatrix.matrix[2][0] = myMinMatrix.matrix[2][1] =
        myMinMatrix.matrix[2][2] = EndianS32_NtoB(0xffffffff);
 
    myMaxMatrix.matrix[0][0] = myMaxMatrix.matrix[0][1] =
        myMaxMatrix.matrix[0][2] = EndianS32_NtoB(0x7fffffff);
    myMaxMatrix.matrix[1][0] = myMaxMatrix.matrix[1][1] =
        myMaxMatrix.matrix[1][2] = EndianS32_NtoB(0x7fffffff);
    myMaxMatrix.matrix[2][0] = myMaxMatrix.matrix[2][1] =
        myMaxMatrix.matrix[2][2] = EndianS32_NtoB(0x7fffffff);
 
    myMinMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((1 *
        kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
    myMaxMatrix.matrix[2][1] = EndianS32_NtoB(Long2Fix((3 *
        kSpriteTrackHeight / 4) - (kPenguinHeight / 2)));
 
    SetIdentityMatrix(&myDeltaMatrix);
    myDeltaMatrix.matrix[2][1] = Long2Fix(1);
 
    // change location
    myErr = AddSpriteSetMatrixAction(theContainer,
            kParentAtomIsContainer, kQTEventIdle, 0, NULL, 0, 0, NULL,
            &myDeltaMatrix, &myActionAtom);
    if (myErr != noErr)
        goto bail;
 
    myErr = AddActionParameterOptions(theContainer, myActionAtom, 1,
            myFlags, sizeof(myMinMatrix), &myMinMatrix,
            sizeof(myMaxMatrix), &myMaxMatrix);
 
bail:
    return(myErr);
}