Publishers of technology books, eBooks, and videos for creative people

Home > Articles > Digital Audio, Video > QuickTime

This chapter is from the book

This chapter is from the book

QuickTime Movies

A sound knowledge of the fundamentals of developing an interface for your Mac OS X program is of great importance, but you didn't choose to learn about Mac programming for the sole purpose of creating windows that include a few buttons. You most certainly also want to know how your own program can include at least some multimedia capabilities.

Unfortunately, Mac OS X programming for sound playing, sound recording, and smooth animation are worthy of their own programming book. So, what can I show you in just half a chapter? Well, I can show you one multimedia topic that best showcases Mac OS X multimedia in action: QuickTime. By giving your program the ability to play QuickTime movies, you can add high-resolution graphics, animation, and sound playing to your program.

Note

QuickTime is now cross-platform software, but it started out as an extension of Mac-only system software.

In this section, you see how to use the previously discussed Navigation Services routines to present the user with an Open dialog box that lets him or her choose a QuickTime movie file to open. After that file is open, you use Carbon API routines (which are grouped into the Movie Toolbox area of the Carbon API) to play the movie.

Opening a QuickTime Movie File

This chapter's "Files and Navigation Services" section provides all the details for presenting the user with a standard Open dialog box. It also shows how to respond to a user's selection of a file that is listed in that dialog box.

In this part of the chapter, you'll be using that information to give the user the power to pick a QuickTime movie file to display. Specifically, I'll jump into descriptions of the techniques and Movie Toolbox routines that your program will use to get QuickTime movie file data that exists on the user's disk into a format that's ready to play as a movie in a window. The result will be an application-defined routine named OpenOneQTMovieFile. Then, after you've developed this routine, you can insert it into the kNavUserActionOpen case label section of the switch statement in the MyOpenDialogEventCallback routine that was developed earlier in this chapter.

Transferring Movie File Data to Memory

Scan back just a bit in this chapter and you'll see the heading "Opening a QuickTime Movie File." Look ahead a little and you'll see the heading "Playing a QuickTime Movie." In broad terms, these are the two steps a program performs so that a user can view a movie. However, each step is more involved that it would first appear. For instance, in the case of opening a movie file, what's actually taking place is the opening of that file (so its data can be accessed), the copying of that file's movie data content into memory (where it can be referenced by the application), and the closing of the file (because its contents are no longer needed). The goal of what's loosely described as the opening of a file is actually the transferring (or copying) of a file's data into memory.

To open a file, your program needs to know the file's name and location. If the user selected the file in the Open dialog box, that dialog box's event handler gets the required information from the NavReplyRecord variable. Recall from this chapter's "Open Dialog Box Event Handler" section that the Open dialog box event handler called NavDialogGetReply to fill a NavReplyRecord with information about the user-selected file to open:

NavReplyRecord  reply;

err = NavDialogGetReply( callBackParms->context, &reply );

With years of computer programming experience comes an appreciation for a programming task as simple as adding two numbers; the job's simplicity ensures there's little or no chance of error. This is in contrast to a task such as file handling, which can be fraught with peril! The task involves selecting a file, opening it, copying its contents to memory, and then accessing that memory to make use of the data within. One flipped bit in this process can really play havoc on a program or even the drive itself!

In an attempt to avoid intimacy with the debugger, file-handling code often makes judicious use of error checking. To increase the explanation-to-code ratio in this book, I've provided descriptions of some basic error-handling techniques in Chapter 2, "Overview of Mac OS X Programming," and then for the most part, kept error- handling code to a minimum in the subsequent chapters. Now, however, is no time to be stingy with error checking, so in upcoming snippets, you'll see a little extra precautionary code, starting right here:

OSStatus  err;
AEDesc    newDescriptor;
FSRef     movieRef;

err = AECoerceDesc( &reply->selection, typeFSRef, &newDescriptor ); 

err = AEGetDescData( &newDescriptor, ( void * )( &movieRef ),
           sizeof( FSRef ) );      

The Apple Event Manager routine AECoerceDesc accepts data of one type (the first argument), manipulates it to another type (specified by the second argument), and saves the results in a new variable (the third argument). The usage of this routine verifies that the reply variable that holds the user-selected file is in the format of an FSRef. After the call to AECoerceDesc completes, your program is assured of having an FSRef within the variable newDescriptor. The Apple Event Manager routine AEGetDescData then is called to retrieve the FSRef from the newDescriptor variable.

At this point, the program has an FSRef (the variable movieRef) that holds information about the user-selected file. Thus, we're almost ready to open the QuickTime movie file. However, we need to make one quick detour. Some of the Carbon API routines are older (they existed as original Macintosh Toolbox API routines), and some are newer (they were created to handle Mac OS X tasks for which no original Macintosh Toolbox routine existed). The newer file-handling Carbon API routines that require information about a file accept that information in the form of an argument of type FSRef. In contrast, original file-handling Toolbox routines that became part of the Carbon API look for this same information in the form of an argument of type FSSpec. In addition, opening a QuickTime movie file requires the use of one of these older FSSpec-accepting routines. Fortunately, for situations such as this, the routine FSGetCatalogInfo function can be used to convert an FSRef to an FSSpec:

FSSpec  userFileFSSpec;

FSGetCatalogInfo( &movieRef, kFSCatInfoNone, NULL, NULL, 
         &userFileFSSpec, NULL );

FSGetCatalogInfo is a workhorse of a utility routine in that it can be used to obtain all sorts of information about a catalog file. (A catalog file is a special file used to keep information about all the files and directories on a volume.) You can use FSGetCatalogInfo to obtain information such as the reference number of the volume on which a file resides or the parent directory ID of a file. You also can use FSGetCatalogInfo to simply obtain an FSSpec for a file for which you already have an FSRef. That's what I'm interested in here. Of most importance in this usage of FSGetCatalogInfo is the first argument, which is a pointer to the FSRef to convert, and the fifth argument, which is a pointer to an FSSpec variable that FSGetCatalogInfo is to fill with the file system specification. The only other non-NULL value is the second argument. This argument normally is used to specify which of many pieces of information about a file or directory are to be returned. I don't need any of this information, so the constant kFSCatInfoNone is used here.

Now it's time to open the file. The Movie Toolbox routine OpenMovieFile does that. The first OpenMovieFile argument is a file system specification. You can use the one returned by the call to FSGetCatalogInfo. After OpenMovieFile opens the specified filem it provides your program with a reference number for that file. That reference number is your program's means of (you guessed it) referring to that file in subsequent calls to Movie Toolbox routines. The next argument is a pointer to a variable in which OpenMovieFile places this reference value. The last argument is a permission level for the opened file. A program that opens a movie for playing but that won't enable the altering of the movie contents should use the constant fsRdPerm.

OSErr   err;
FSSpec  userFileFSSpec 
short   movieRefNum;

err = OpenMovieFile( &userFileFSSpec, &movieRefNum, fsRdPerm );

Caution

Besides fsRdPerm, other permission constants include fsWrPerm (to enable writing) and fsRdWrPerm (to enable reading and writing). In my simple examples, the permission level isn't crucial. That is, you can change it to, say, fsRdWrPerm, and the user still won't be able to cut any frames from an opened movie. However, in your full-blown application, permissions might be of importance. If your program includes a functioning Edit menu that supports the cutting and pasting of multiple data types, you might not want to give the user the ability to alter the frames of a movie. In such an instance, you'll want to make sure that movie files are opened with the fsRdPerm constant rather than with one of the constants that enables file writing.

After opening a movie file, that file's data needs to be loaded into memory. A call to the Movie Toolbox routine NewMovieFromFile does this:

Movie   movie = NULL;
short   movieResID = 0;

err = NewMovieFromFile( &movie, movieRefNum, &movieResID,
            NULL, newMovieActive, NULL );

After NewMovieFromFile completes, the first argument holds a reference to the movie (a variable of type Movie). To create this movie, NewMovieFromFile needs the movie file reference number that was returned by the prior call to OpenMovieFile. You should pass this as the second argument. NewMovieFromFile also needs the ID of the movie data in the file in question. Although a single file typically holds one movie, it can hold multiple movies. Thus, it's necessary to specify which of a file's movies is to be used. A value of 0 as the third argument tells NewMovieFromFile to use the first movie in the file. Thus, even if there is only one movie in the file, this value of 0 does the job.

When NewMovieFromFile exits, it fills in the fourth argument (movieName) with the name of the movie resource that was used to create the movie. Note that this isn't the name of the file that holds the movie; it's the name of a resource within the file. That's usually not of importance, so your program can pass NULL here. The fifth argument is used to provide supplemental information to NewMovieFromFile. Using the constant newMovieActive specifies that the new movie should be active; a movie needs to be active for it to be played. The last argument tells whether NewMovieFromFile had to make any changes to the data in the file. This shouldn't occur, so again a value of NULL typically suffices.

The call to OpenMovieFile opened the file in preparation for access to it. NewMovieFromFile is the routine that accessed the file. Now, with the movie data safe in memory and a Movie variable referencing that data, the file can be closed:

CloseMovieFile( movieRefNum );

CloseMovieFile needs to know which file to close. The reference number returned by OpenMovieFile provides that information.

Displaying a Movie in a Window

At this point, a movie is in memory and accessible by way of a Movie variable. Now the movie needs to be associated with a window. There's nothing special about a window that holds a movie; you just create a new window resource in your program's main.nib file. You can make the window any size you want. Your code resizes this window to match the size of the movie that eventually gets displayed within the window. With the window resource defined, include the standard window-creation code in your code:

WindowRef  window;
OSStatus  err;
IBNibRef  nibRef;

err = CreateNibReference( CFSTR("main"), &nibRef );
err = CreateWindowFromNib( nibRef, CFSTR("MovieWindow"), &window );
DisposeNibReference( nibRef );

Now, for the link between the movie and the window, call SetPortWindowPort to make the window's port the active port. Then, call the Movie Toolbox routine SetMovieGWorld to associate the movie with the currently active port:

SetPortWindowPort( window );

SetMovieGWorld( movie, NULL, NULL );

The GWorld in SetMovieGWorld refers to a graphics world, which is a complex memory drawing environment used in the preparation of images before their onscreen display. The first SetMovieGWorld argument is the movie to associate with a port. The second argument is the port; pass NULL here to tell SetMovieGWorld to associate the movie with the current port, which is the window named in the call to SetPortWindowPort. The last argument is a handle to a Gdevice, which is a structure describing a graphics device. A value of NULL here tells SetMovieGWorld to use the current device.

Now determine the size of the open movie and use those coordinates to resize the window to match the movie size:

Rect  movieBox;

GetMovieBox( movie, &movieBox );
OffsetRect( &movieBox, -movieBox.left, -movieBox.top );
SetMovieBox( movie, &movieBox );
SizeWindow( window, movieBox.right, movieBox.bottom, TRUE ); 
ShowWindow( window );

Pass GetMovieBox a movie and the routine returns a rectangle that holds the size of the movie. This might be all you need, or it might not be. Although the returned rectangle does hold the size of the movie, it's possible that the top and left coordinates of this rectangle each might not be 0. In such a case, looking at movieBox.right for the movie's width and movieBox.bottom for the movie's height would provide erroneous information. For instance, a movieBox.left value of 50 and a movieBox.right value of 200 means that the movie has a width of 150 pixels. A call to the QuickDraw routine OffsetRect simply offsets the movieBox rectangle such that its left and top coordinates each have a value of 0. A call to SetMovieBox makes the new, offset values the boundaries for the rectangle that defines the size of the movie.

Although the movie rectangle has been adjusted, the window that's to display the movie has not. A call to SizeWindow does that. Pass SizeWindow the window to resize, along with the new width and height to use in the size change. The last argument is a Boolean value that tells whether an update event should be generated. The call to ShowWindow finally reveals the movie-holding window to the user.

To ensure that the window displays a frame of the movie, call MoviesTask. This Movie Toolbox routine does just that. Pass the movie to use in the frame display as the first argument and a value of 0 as the second argument. This 0 value tells MoviesTask to service (update) each active movie. If your program can display more than one movie at a time, MoviesTask will jump to each open movie, displaying one new frame in each. Precede the call to MoviesTask with a call to GoToBeginningOfMovie. This Toolbox routine rewinds the movie to its first frame. Although a newly opened movie will most likely be set to the movie's first frame, a call to this routine ensures that that will be so:

GoToBeginningOfMovie( movie );
MoviesTask( movie, 0 );

Playing a QuickTime Movie

The movie's now open and displayed in a window. Let's play it from start to finish:

StartMovie( movie );

do
{
  MoviesTask( movie, 0 );
} while ( IsMovieDone( movie ) == FALSE );

Contrary to its name, StartMovie doesn't start a movie. Instead, it prepares the specified movie for playing by making the movie active and setting the movie's playback rate. To actually play a movie, call MoviesTask within a loop. Each call to MoviesTask plays a frame of the movie. Because your program won't know how many frames are in the movie to play, rely on a call to the Movie Toolbox routine IsMovieDone to determine when the frame-playing loop should terminate. Pass IsMovieDone a movie and the routine returns a value of TRUE if the last frame has been reached or FALSE if there's one or more frames left to play.

Note

Related to the running of a movie is the movie controller. It is the thin, three-dimensional control that runs along the bottom of a window displaying a QuickTime movie. The movie controller is under the user's control, and it enables the user to run, pause, or step forward or backwards through the movie displayed in the window. For more information on movie controllers, see the URL listed at the end of this chapter.

OpenPlayMovie Program

The purpose of OpenPlayMovie is to demonstrate how the Navigation Services routines are used to display the standard Open dialog box. It also shows how to respond to a user-selected file when that file is a QuickTime movie.

OpenPlayMovie starts by opening a window that includes a single line of text, as shown in Figure 9.2. When you follow that window's instructions, you see the standard Open dialog box. Use the file lists to move about your drive or drives to find a QuickTime movie. When you click the Open dialog box's Open button, a new window displaying the first frame of the movie appears. Choose Play Movie from the Movie menu and the movie plays from start to finish. You can choose Play Movie as often as you wish.

Figure 9.2 Windows displayed by the OpenPlayMovie program.

Nib Resources

The main.nib window in the project's main.nib file includes two windows, as shown in Figure 9.3. By default, Interface Builder sets a new window to be resizable, and it gives the window a small resize control in the lower-right corner of the window. The OpenPlayMovie program eliminates this resize control from the movie-playing window. The control would obscure a small part of one corner of the movie if it were present.

You can use Interface Builder to set a window so that it can't be resizable. To do that, click the MovieWindow window in main.nib (see Figure 9.3), choose Show Info from the Tools menu, display the Attributes pane, and then uncheck the Resizable checkbox. While you're there, uncheck both the Close and the Zoom checkboxes so that the window won't be closeable or zoomable.

The Movie menu includes two items: Open Movie and Play Movie. The Open Movie item has a command of Opmv. Assign it that command from the Attributes pane of the item's Info window. The Play Movie item has a command of PLmv. Play Movie item is initially disabled. Click that item, choose Show Info from the Tools menu, and, from the Attributes pane, uncheck the Enabled checkbox. Because the program's code will be accessing the Movie menu, this menu needs an ID. You can give the Movie menu a menu ID of 5 by clicking Movie in the menu window, choosing Show Info from the Tools menu, and entering 5 in the Menu ID field.

Figure 9.3 The OpenPlayMovie nib resources.

Source Code

The QuickTime function prototypes aren't included in a project by default, so you'll need to include QuickTime.h along with Carbon.h:

#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>

Define a constant to match the commands assigned to the Open Movie and Play Movie menu items in the nib resource. Also, define constants to match the Movie menu ID and the menu placement of the two items in the Movie menu:

#define kOpenMovieCommand   'OPmv'
#define kPlayMovieCommand   'PLmv'
#define kMovieMenuID            5
#define kMovieMenuOpenItemNum   1
#define kMovieMenuPlayItemNum   2

OpenPlayMovie declares three global variables. The procedure pointer gNavEventHandlerPtr is used in setting up the Open dialog box event handler, gMovie will reference the movie after it's opened, and gMovieMenu will hold a handle to the Movie menu so that the menu's items can be enabled and disabled:

NavEventUPP  gNavEventHandlerPtr;
Movie        gMovie = NULL;
MenuHandle   gMovieMenu;

Almost all the main routine is the same as in past examples. Additions of note include a call to EnterMovies (a Movie Toolbox initialization routine that's required before a program makes use of other Movie Toolbox routines) and a call to GetMenuHandle (to obtain a handle to the Movie menu):

int main( int argc, char* argv[] )
{
  IBNibRef         nibRef;
  WindowRef        window;
  OSStatus         err;
  EventTargetRef   target;
  EventHandlerUPP  handlerUPP;
  EventTypeSpec    appEvent = { kEventClassCommand, 
                                kEventProcessCommand };

  EnterMovies(); 

  // set up menu bar, open window, install event handler

  gMovieMenu = GetMenuHandle( kMovieMenuID ); 

  RunApplicationEventLoop();

  return( 0 );
}

OpenPlayMovie demonstrates some simple menu adjustment techniques that make it possible to force the program to enable only one movie to be opened. When the program launches, the Open Movie item is enabled and the Play Movie item is disabled, as specified in the menu resource in the nib file. If a menu item doesn't make sense at a particular moment in the running of a program, it should be disabled. When the program launches, no movie is open, so the Play Movie item isn't applicable. That's why it's initially disabled. The program enables a user to select a movie to open, so the Open Movie item starts out enabled. The toggling of the state of these two items takes place in the application's event handler.

The following snippet comes from MyAppEventHandler and shows the code that responds to a command issued by the user's choosing of the Open Movie menu item:

case kOpenMovieCommand:
  DisplayOpenFileDialog();
  if ( gMovie != NULL )
  {
   DisableMenuItem( gMovieMenu, kMovieMenuOpenItemNum );
   EnableMenuItem( gMovieMenu, kMovieMenuPlayItemNum );
  }
  result = noErr;
  break;

Handling an Open Movie menu item selection begins with a call to the application-defined DisplayOpenFileDialog routine. The global Movie variable gMovie was initialized to a value of NULL to signify that no movie is open. If the user opens a movie, gMovie references that movie and will have a value other than NULL. In that case, MyAppEventHandler disables the Open Movie item and enables the Play Movie item. That makes it impossible for the user to attempt to open a second movie, and it makes possible the playing of the now-open movie. If the user clicks the Cancel button in the Open dialog box, gMovie will retain its NULL value and the two menu items will retain their initial state. This enables the user to again choose Open Movie to open a movie.

In your more sophisticated movie-playing programs, you might allow the display and playing of multiple movies. In that case, you can expand on the technique discussed here by allowing the closing of movie windows and the toggling of the Play Movie item from enabled to disabled when all such movie windows are closed. One way to do that is to intercept window-closing events. When a window closes, check whether it was the last movie window. (You could keep a global movie window counter that increments and decrements as movies are opened and closed.) In Chapter 3, "Events and the Carbon Event Manager," the MyCloseWindow example introduces the topic of window closing events. (The program sounds a beep when the user clicks a window's Close button.) In Chapter 4, "Windows," the MenuButtonCloseWindow example elaborates on this technique. Finally, in Chapter 6, "Menus," you learn how to enable and disable menu items.

If the user chooses Play Movie, the application-defined PlayOneMovie routine is called. Note that there's no need for any menu-item disabling or enabling here. If this item is enabled, it means a movie window is open and can be played. If no movie window is open, this item will be disabled and the kPlayMovieCommand can't be generated by the program!

case kPlayMovieCommand:
  PlayOneMovie( gMovie );
  result = noErr;
  break;

In response to the user's choosing Open Movie, the program calls DisplayOpenFileDialog. This application-defined routine was developed in this chapter's "Implementing an Open Dialog Box" section. The OpenPlayMovie source code listing (Example 9.1) shows this routine. The event handler, or callback routine, that DisplayOpenFileDialog installs is the application-defined routine MyOpenDialogEventCallback.(This is another routine discussed at length in the "Implementing an Open Dialog Box" section. Refer to those pages for more information on this callback function.) Here I'll point out that if the system invokes this routine with a user action of kNavUserActionOpen, the callback routine invokes the application-defined function OpenOneQTMovieFile to open the user-selected movie file.

The OpenOneQTMovieFile routine is basically a compilation of the code discussed in this chapter's "Transferring Movie File Data to Memory" section. AECoerceDesc makes sure that the NavReplyRecord filled in by the Open dialog box is valid, AEGetDescData retrieves an FSRef from that reply record, and FSGetCatalogInfo converts the FSRef to an FSSpec for use in opening the movie file:

void OpenOneQTMovieFile( NavReplyRecord *reply ) 
{               
  AEDesc     newDescriptor;
  FSRef      movieRef;
  WindowRef  window;
  OSStatus   err;
  FSSpec     userFileFSSpec;
  IBNibRef   nibRef;

  err = AECoerceDesc( &reply->selection, typeFSRef, &newDescriptor ); 

  err = AEGetDescData( &newDescriptor, ( void * )( &movieRef ),
            sizeof( FSRef ) );      

  FSGetCatalogInfo( &movieRef, kFSCatInfoNone, NULL, NULL, 
          &userFileFSSpec, NULL );

  gMovie = GetMovieFromFile( userFileFSSpec );

The application-defined routine GetMovieFromFile (discussed next) opens the movie file and assigns gMovie a reference to the movie. A new window then is opened, its port is set to the current port, and the application-defined routine AdjustMovieWindow (discussed shortly) resizes the window and associates the movie with the window. OpenOneQTMovieFile ends with a call to AEDisposeDesc to dispose of the AEDisc created earlier in the routine:

err = CreateNibReference( CFSTR("main"), &nibRef );
  err = CreateWindowFromNib( nibRef, CFSTR("MovieWindow"), &window );
  DisposeNibReference( nibRef );

  SetPortWindowPort( window );

  AdjustMovieWindow( gMovie, window );

  AEDisposeDesc( &newDescriptor );                        
}

GetMovieFromFile is a short routine that makes three Movie Toolbox calls. OpenMovieFile opens the user-selected file. NewMovieFromFile loads the movie data to memory and returns a reference to the movie. CloseMovieFile closes the movie file. This chapter's "Transferring Movie File Data to Memory" section discusses each routine.

Movie GetMovieFromFile( FSSpec userFileFSSpec )
{ 
  OSErr   err;
  Movie   movie = NULL;
  short   movieRefNum;
  short   movieResID = 0;

  err = OpenMovieFile( &userFileFSSpec, &movieRefNum, fsRdPerm );

  err = NewMovieFromFile( &movie, movieRefNum, &movieResID,
              NULL, newMovieActive, NULL );

  CloseMovieFile( movieRefNum );

  return ( movie );
}

AdjustMovieWindow combines the code discussed in this chapter's "Displaying a Movie in a Window" section to create a routine that calls SetMovieGWorld to associate the open movie with the recently opened window and to resize the window to match the size of the movie.

void AdjustMovieWindow( Movie movie, WindowRef window )
{  
  Rect  movieBox;

  SetMovieGWorld( movie, NULL, NULL );

  GetMovieBox( movie, &movieBox );
  OffsetRect( &movieBox, -movieBox.left, -movieBox.top );
  SetMovieBox( movie, &movieBox );

  SizeWindow( window, movieBox.right, movieBox.bottom, TRUE ); 
  ShowWindow( window );

  GoToBeginningOfMovie( gMovie );
  MoviesTask( gMovie, 0 );
}

At this point, a movie file has been opened and the first frame of the movie is displayed in a window. To play the movie, the user chooses Play Movie from the Movie menu. Doing that initiates a command that the application event handler handles by calling PlayOneMovie. This routine bundles the code discussed in this chapter's "Playing a QuickTime Movie" section, with the result being the playing of the movie from start to finish:

void PlayOneMovie( Movie movie )
{
  GoToBeginningOfMovie( movie );

  StartMovie( movie );

  do
  {
   MoviesTask( movie, 0 );
  } while ( IsMovieDone( movie ) == FALSE );
}

Example 9.1 OpenPlayMovie Source Code

#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>

#define  kOpenMovieCommand   'OPmv'
#define  kPlayMovieCommand   'PLmv'
#define  kMovieMenuID       5
#define  kMovieMenuOpenItemNum   1
#define  kMovieMenuPlayItemNum   2

Movie           GetMovieFromFile( FSSpec userFileFSSpec );
void            AdjustMovieWindow( Movie movie, WindowRef window );
void            PlayOneMovie( Movie movie );
void            DisplayOpenFileDialog( void ); 
void            OpenOneQTMovieFile( NavReplyRecord *reply ) ;
pascal OSStatus MyAppEventHandler( EventHandlerCallRef handlerRef, 
                  EventRef event, void *userData );
pascal void     MyOpenDialogEventCallback( 
                NavEventCallbackMessage callBackSelector, 
                NavCBRecPtr             callBackParms, 
                void*                   callBackUD );

NavEventUPP  gNavEventHandlerPtr;
Movie        gMovie = NULL;
MenuHandle   gMovieMenu;


int main( int argc, char* argv[] )
{
  IBNibRef         nibRef;
  WindowRef        window;
  OSStatus         err;
  EventTargetRef   target;
  EventHandlerUPP  handlerUPP;
  EventTypeSpec    appEvent = { kEventClassCommand, 
                   kEventProcessCommand };

  EnterMovies(); 

  err = CreateNibReference( CFSTR("main"), &nibRef );
  err = SetMenuBarFromNib( nibRef, CFSTR("MainMenu") );
  err = CreateWindowFromNib( nibRef, CFSTR("MainWindow"), &window );
  DisposeNibReference( nibRef );

  ShowWindow( window );

  target = GetApplicationEventTarget();
  handlerUPP = NewEventHandlerUPP( MyAppEventHandler );
  InstallEventHandler( target, handlerUPP, 1, &appEvent, 0, NULL );

  gMovieMenu = GetMenuHandle( kMovieMenuID );

  RunApplicationEventLoop();

  return( 0 );
}


pascal OSStatus MyAppEventHandler( EventHandlerCallRef handlerRef, 
                  EventRef event, void *userData)
{
  OSStatus   result = eventNotHandledErr;
  HICommand  command;

  GetEventParameter( event, kEventParamDirectObject, typeHICommand, 
                     NULL, sizeof (HICommand), NULL, &command);
          
  switch ( command.commandID )
  {
   case kOpenMovieCommand:
     DisplayOpenFileDialog();
     if ( gMovie != NULL )
     {
        DisableMenuItem( gMovieMenu, kMovieMenuOpenItemNum );
        EnableMenuItem( gMovieMenu, kMovieMenuPlayItemNum );
     }
     result = noErr;
     break;
    
   case kPlayMovieCommand:
     PlayOneMovie( gMovie );
     result = noErr;
     break;
  }
  return result;
}


void DisplayOpenFileDialog( void )
{
   OSStatus                  err;
   NavDialogRef              openDialog;
   NavDialogCreationOptions  dialogAttributes;

  err = NavGetDefaultDialogCreationOptions( &dialogAttributes );
  dialogAttributes.modality = kWindowModalityAppModal;

  gNavEventHandlerPtr = NewNavEventUPP( MyOpenDialogEventCallback );   

err = NavCreateGetFileDialog( &dialogAttributes, NULL, gNavEventHandlerPtr, NULL, NULL, NULL, &openDialog ); err = NavDialogRun( openDialog ); if ( err != noErr ) { NavDialogDispose( openDialog ); DisposeNavEventUPP( gNavEventHandlerPtr ); } } pascal void MyOpenDialogEventCallback( NavEventCallbackMessage callBackSelector, NavCBRecPtr callBackParms, void* callBackUD ) { OSStatus err; NavReplyRecord reply; NavUserAction userAction = 0; switch ( callBackSelector ) { case kNavCBUserAction: err = NavDialogGetReply( callBackParms->context, &reply ); userAction = NavDialogGetUserAction( callBackParms->context ); switch ( userAction ) { case kNavUserActionOpen: OpenOneQTMovieFile( &reply ); break; } err = NavDisposeReply( &reply ); break; case kNavCBTerminate: NavDialogDispose( callBackParms->context ); DisposeNavEventUPP( gNavEventHandlerPtr ); break; } } void OpenOneQTMovieFile( NavReplyRecord *reply ) { AEDesc newDescriptor; FSRef movieRef; WindowRef window; OSStatus err; FSSpec userFileFSSpec; IBNibRef nibRef; err = AECoerceDesc( &reply->selection, typeFSRef, &newDescriptor ); err = AEGetDescData( &newDescriptor, ( void * )( &movieRef ), sizeof( FSRef ) ); FSGetCatalogInfo( &movieRef, kFSCatInfoNone, NULL, NULL, &userFileFSSpec, NULL ); gMovie = GetMovieFromFile( userFileFSSpec ); err = CreateNibReference( CFSTR("main"), &nibRef ); err = CreateWindowFromNib( nibRef, CFSTR("MovieWindow"), &window ); DisposeNibReference( nibRef ); SetPortWindowPort( window ); AdjustMovieWindow( gMovie, window ); AEDisposeDesc( &newDescriptor ); } Movie GetMovieFromFile( FSSpec userFileFSSpec ) { OSErr err; Movie movie = NULL; short movieRefNum; short movieResID = 0; err = OpenMovieFile( &userFileFSSpec, &movieRefNum, fsRdPerm ); err = NewMovieFromFile( &movie, movieRefNum, &movieResID, NULL, newMovieActive, NULL ); CloseMovieFile( movieRefNum ); return ( movie ); } void AdjustMovieWindow( Movie movie, WindowRef window ) { Rect movieBox; SetMovieGWorld( movie, NULL, NULL ); GetMovieBox( movie, &movieBox ); OffsetRect( &movieBox, -movieBox.left, -movieBox.top ); SetMovieBox( movie, &movieBox ); SizeWindow( window, movieBox.right, movieBox.bottom, TRUE ); ShowWindow( window ); GoToBeginningOfMovie( gMovie ); MoviesTask( gMovie, 0 ); } void PlayOneMovie( Movie movie ) { GoToBeginningOfMovie( movie ); StartMovie( movie ); do { MoviesTask( movie, 0 ); } while ( IsMovieDone( movie ) == FALSE ); }

Peachpit Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from Peachpit and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about Peachpit products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites; develop new products and services; conduct educational research; and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email ask@peachpit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by Adobe Press. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.peachpit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020