TweetFollow Us on Twitter

Animal Crackers

Volume Number: 19 (2003)
Issue Number: 1
Column Tag: QuickTime Toolkit

Animal Crackers

Enhancing Cocoa QuickTime Applications

by Tim Monroe


In the previous QuickTime Toolkit article ("The Cocoanuts" in MacTech, December 2002), we took a look at using Cocoa, Apple's object-oriented application framework, to develop QuickTime applications. We saw how to put together a basic multi-window application that can open, play, and edit QuickTime movies, and we also investigated a few ways to extend that application by subclassing Cocoa's built-in QuickTime classes or by adding additional methods to those classes with categories.

In this article, I want to continue investigating Cocoa and QuickTime. Primarily, I want to tie up a few loose ends that we didn't have time to address in the previous article. For instance, we need to do a little bit more work to get all the items in the Edit and File menus working as expected, and we need to fix a few minor cosmetic problems with the application. Consider this article then to be some fine tuning to get our Cocoa application, MooVeez, to provide all the functionality of our sample application QTShell. Along the way, however, I also want to take time to investigate a few new topics, including the wired action introduced in QuickTime 6 that allows us to set a movie's scale. And, believe it or not, I'm going to slip in a few tweaks to QTShell itself. So don't be surprised if you see some Carbon code along the way.

Before we begin, I should note that in this article (and the previous one), we're relying on the features of QuickTime and Cocoa that shipped with the so-called Jaguar release: Mac OS X version 10.2 or later, and QuickTime 6.0 or later. Earlier versions of the QuickTime Cocoa classes, NSMovie and NSMovieView, have some noticeable problems in a few key areas. The Jaguar versions of these classes are much more reliable and efficient, so that's what I'll assume we're working with.

Displayable Paths

Let's begin with an easy but important update to our existing MooVeez code. Recall that our application can open a QuickTime movie in a window, and that this window provides a pop-out drawer listing some basic information about the movie (Figure 1).

Figure 1: A movie information drawer

The "Source" is the full path of the QuickTime movie file. In the previous article, I offered the setSource: method shown in Listing 1, which breaks the pathname into its components, tosses out the string "Volumes", and then rebuilds a path using the colon (":") as the path separator.

Listing 1: Displaying the movie's source

- (void)setSource: (NSString *)name
   NSArray *pathComponents = [name 
   NSEnumerator *pathEnumerator = [pathComponents 
   NSString *component = [pathEnumerator nextObject];
   NSMutableString *massagedPath = [NSMutableString string];
   while (component != nil) {
      if (([component length] > 0) && 
               (strcmp([component cString], "Volumes") != 0)) {
         [massagedPath appendString:component];
         component = [pathEnumerator nextObject];
         if (component != nil)
                [massagedPath appendString:@":"];
         } else {
            component = [pathEnumerator nextObject];
   [_sourceName setStringValue: massagedPath];

This isn't quite right, however, since it will toss out a path component named "Volumes" wherever it occurs in the full pathname (not just at the beginning).

I have subsequently learned that there is a Cocoa method that can assist us here; the NSFileManager class provides the componentsToDisplayForPath: method, which returns an array containing the components of a file's displayable name (that is, the name that should be displayed to the user). So if, as above, name is the full UNIX pathname of a file, we can get the array of displayable components like this:

NSArray *pathComponents = [[NSFileManager defaultManager] 

For instance, the full UNIX pathname of the movie file shown in Figure 1 is:

/Volumes/Meditations/Applications/QuickTime/Sample Movie

The componentsToDisplayForPath: method returns an array whose 4 elements are these:

Sample Movie

Then we just need to reassemble these components with the appropriate path separator. Listing 2 shows our revised version of the setSource: method.

Listing 2: Displaying the movie's source (revised)

- (void)setSource:(NSString *)name
   NSArray *pathComponents = [[NSFileManager defaultManager] 
   NSEnumerator *pathEnumerator = [pathComponents 
   NSString *component = [pathEnumerator nextObject];
   NSMutableString *displayablePath = 
            [NSMutableString string];
   while (component != nil) {
      if ([component length] > 0) {
         [displayablePath appendString:component];
         component = [pathEnumerator nextObject];
         if (component != nil)
            [displayablePath appendString:@":"];
      } else {
         component = [pathEnumerator nextObject];
   [_sourceName setStringValue:displayablePath];

It's perhaps worth mentioning that Carbon applications can call the Launch Services functions LSCopyDisplayNameForRef or LSCopyDisplayNameForURL to get displayable path names.

Edit Menu

There is one outright bug in the Jaguar implementation of NSMovieView that rears its head when we try to undo a change to a movie. Do this: open a movie, select some of the movie data using the controller bar, and then cut the selected data. As expected, the Paste menu item is now enabled; the problem is that the Undo menu item is not enabled. (See Figure 2.) We ought to be able to undo the cut, and NSMovieView should be handling this automatically for us, but it's not.

Figure 2: The Edit menu of MooVeez

The Cocoa engineers are aware of this problem, and I believe that a fix has been found and will be incorporated into a future version of NSMovieView. In the meantime, there appears to be a fairly simple workaround to this problem. The workaround involves subclassing NSMovieView so that we can override the validateMenuItem: method. Listing 3 shows the override method.

Listing 3: Enabling and disabling the Undo menu item

- (BOOL)validateMenuItem:(NSMenuItem *)item
   BOOL isValid = NO;
   SEL action = [item action];
   // handle the Undo menu item
   if (action == @selector(undo:)) {
      MovieController mc = [self movieController];
      if (mc) {
         long flags = 0L;
         MCGetControllerInfo(mc, &flags);
         isValid = flags & mcInfoUndoAvailable;
   } else {
      isValid = [super validateMenuItem:item];
   return isValid;

Here we're just calling MCGetControllerInfo and checking the returned set of flags to see whether the mcInfoUndoAvailable flag is set. If it is, we enable the Undo menu item; otherwise we disable the menu item. Notice that we pass all other menu items to the superclass (that is, to NSMovieView).

Once we've added this code to our subclass of NSMovieView, the Undo and Redo menu items are enabled and disabled as expected, as shown in Figure 3. In this case, the user has already cut some of the movie data and then undone the cut (that's why the Redo menu item is enabled).

Figure 3: The Edit menu of MooVeez (revised)

In addition, the Undo and Redo menu items appear to function correctly now. Take this new code for a spin: undo some cuts and pastes and see if everything works as expected. My preliminary tests do not reveal any problems, but I'm a bit worried by the following console message that appears the first time the user performs an undo operation:

Warning: -[NSMovieView undo:] is obsolete.

I recommend employing this workaround only after thoroughly testing it yourself.

File Menu

Now that we've got the Edit menu working pretty much as expected, let's take a quick look back at the File menu. In the previous article, we explicitly postponed adding support for creating new, empty movie windows -- that is, supporting the New menu item. To disable the New menu item, we added a validateMenuItem: method to our application controller class (defined in the file AppController.m). And we overrode the newDocument: method to act as a no-op. Since we want to add support for creating new empty movies, we need to remove these methods from our application controller class entirely. Once we do that, the default behaviors of the NSDocument class will kick in and we'll be able to create new empty documents.

For this to work correctly, however, we need to make a few simple fixes to the code that is called by the windowControllerDidLoadNib: method. Hitherto we have assumed that the user had opened a movie file by selecting the Open or "Open Recent" menu item and choosing a movie file, or by dropping a movie file onto the application's icon. In either case, we know that the movie is associated with an existing movie file. But once we allow the user to create a new movie, we have to make sure not to rely on NSDocument's fileName method returning a non-nil value. We'll revise the code in our initializeMovieWindowFromFile: method, as shown in Listing 4.

Listing 4: Creating a new movie

if ([self fileName]) {
   // open the movie file with read/write permission and load the movie from it
   err = FSPathMakeRef([[self fileName] 
            fileSystemRepresentation], &fileRef, NULL);
   if (err == noErr)
      err = FSGetCatalogInfo(&fileRef, kFSCatInfoNone, NULL, 
            NULL, &fileSpec, NULL);
   if (err == noErr)
      err = OpenMovieFile(&fileSpec, &fileRefNum, 
   if (err == noErr)
      err = NewMovieFromFile(&qtMovie, fileRefNum, 
            &fileResNum, NULL, 0, NULL);
} else {
   qtMovie = NewMovie(newMovieActive);

As you can see, we've conditionalized the existing code so that it is called only if the document already has a file associated with it; otherwise, we simply call NewMovie to create a new empty movie.

We also need to make sure to set the window title to the last component of the file name only if the file actually has a name:

if ([self fileName])
   [[_movieView window] setTitle:[self 

If we do not explicitly set the window title, NSDocument will generate one automatically. Figure 4 shows a new document window with an empty movie.

Figure 4: A new, empty document window

We can cut or copy data in other movie windows and then paste it into this new movie. And we can save the new movie into a file, just as we would expect.

Listing 4 is interesting also because it illustrates how to convert a string of type NSString into a file system specification (of type FSSpec): get a C string representation of the NSString, create an FSRef, and then call FSGetCatalogInfo to get an FSSpec record from the FSRef. Listing 5 shows this technique packaged into a nice utility method.

Listing 5: Converting a pathname string into a file system specification

(void)NSStringToFSSpec:(NSString *)theFilePath 
            fsSpec:(FSSpec *)theFSSpecPtr
   FSRef fsRef;
   Boolean isDirectory = false;
   OSStatus err = noErr;
   // create an FSRef for the specified target file
   if (theFilePath) {
      err = FSPathMakeRef([theFilePath 
            fileSystemRepresentation], &fsRef, &isDirectory);
      // create an FSSpec record from the FSRef
      if (!err && !isDirectory) {
         err = FSGetCatalogInfo(&fsRef, kFSCatInfoNone, NULL, 
            NULL, theFSSpecPtr, NULL);

This is useful because Cocoa likes to work with full pathnames, while the QuickTime APIs tend to prefer FSSpec records. Note, however, that the NSStringToFSSpec function works only with pathnames that describe existing files; if we pass in a path to a nonexistent file, FSPathMakeRef will return fnfErr (file not found) and fail to create an FSRef.

If we want to create an FSSpec record for a file that does not exist, we need to be a bit more creative. Listing 6 defines the writeToFile: method, which might be called (for instance) when the user selects the "Save As" menu item. As you can see, if FSPathMakeRef returns fnfErr, we call the standard UNIX file system functions open, write, and close to create a new file. Then we call FSPathMakeRef and FSGetCatalogInfo (as above) to get an FSSpec record, which we pass to FlattenMovie.

Listing 6: Writing a movie into a file

(BOOL)writeToFile:(NSString *)path 
            ofType:(NSString *)type
   FSRef fsRef;
   FSSpec fsSpec;
   NSString *newPath;
   OSStatus err = noErr;
   newPath = [NSString stringWithFormat:@"%@~", path];
   // create an FSRef for the specified target file
   err = FSPathMakeRef([newPath fileSystemRepresentation], 
            &fsRef, NULL);
   if (err == fnfErr) {
      // if the file does not yet exist, then let's create the file
      int fd;
      fd = open([newPath fileSystemRepresentation], 
            O_CREAT | O_RDWR, 0600);
      if (fd < 0)
         return NO;
      write(fd, " ", 1);
      err = FSPathMakeRef([newPath fileSystemRepresentation], 
            &fsRef, NULL);
   if (err == noErr) {
      // create an FSSpec record from the FSRef
      err = FSGetCatalogInfo(&fsRef, kFSCatInfoNone, NULL, 
            NULL, &fsSpec, NULL);
      if (err == noErr) {
         short resId;
         // flatten the movie data into the specified target file
         FlattenMovie([[_movieView movie] QTMovie],
            flattenAddMovieToDataFork | 
            createMovieFileDeleteCurFile | 
      rename([newPath fileSystemRepresentation], 
            [path fileSystemRepresentation]);
      return YES;
   // clean up
   unlink([newPath fileSystemRepresentation]);
   return NO;

Window Resizing

Let's now take a look at a couple of issues related to resizing our movie windows. Typically our document windows will be resized directly by the user, by dragging the resize box in the lower right corner of the document window. In Interface Builder, we've set the Size attributes of the movie view as shown in Figure 5.

Figure 5: The size attributes of the movie view

The autosizing springs within the movie view and the rigid connections between the movie view and its superview (the document window) indicate that the movie view should grow or shrink to maintain a constant distance from all its edges to the edges of the document window. That is, when the user resizes the document window, the movie view will automatically be resized to maintain its border.

Occasionally, however, we need to adjust the size of the document window based on the desired size of the movie view. We did this previously when setting the size of the window for a newly-opened QuickTime movie. We'll also want to adjust the size of the document window when we receive the mcActionControllerSizeChanged movie controller action. So the first thing I want to do is rework our existing code a bit, to factor out the code that sets the movie size. Listing 7 shows our new method windowContentSizeForMovie:.

Listing 7: Getting a window size from a movie size

- (NSSize)windowContentSizeForMovie:(Movie)qtMovie
   NSSize size;
   Rect rect;
   GetMovieBox(qtMovie, &rect);
   size.width = (float)(rect.right - rect.left);
   size.height = (float)(rect.bottom -;
   // enforce a minimum width (important for sound-only movies)
   if (size.width == 0)
      size.width = (float)240;
   size.width += 2 * kMoviePaneOffset;
   size.height += 2 * kMoviePaneOffset;
   if ([_movieView isControllerVisible])
      size.height += kMovieControllerBarHeight;
   return size;

When the movie window is awaked from the nib file, we set the movie window size like this:

[[_movieView window] setContentSize:
            [self windowContentSizeForMovie:qtMovie]];

And when we receive the mcActionControllerSizeChanged movie controller action, we can set the movie window size as shown in Listing 8.

Listing 8: Setting a window size from a movie size

case mcActionControllerSizeChanged: 
   [[[doc movieView] window] setContentSize:
            [doc windowContentSizeForMovie:[[[doc movieView] 
            movie] QTMovie]]];

Now, when do we receive the mcActionControllerSizeChanged action? We'll receive it whenever the user manually resizes our document window, since NSMovieView informs the movie controller -- probably by calling MCSetControllerBoundsRect -- whenever the movie view is resized. (In this case, we really don't need to call setContentSize:, but it doesn't hurt to do so.) We'll also receive the mcActionControllerSizeChanged action when certain wired actions change the movie size. For instance, QuickTime 6 introduced the kActionMovieSetScale action, which sets the target movie's scale (or magnification). And earlier versions of QuickTime included the kActionTrackSetMatrix wired action, which sets a track's matrix. When the movie controller processes either of these actions, it will eventually inform our application of the size change by sending the mcActionControllerSizeChanged action to our filter procedure.

It turns out that the code in Listing 8 can lead to some drawing glitches when triggered by these wired actions. When a movie receives the kActionMovieSetScale action, our window can end up looking like the one in Figure 6. As you can see, the controller bar was not correctly erased at its previous position or redrawn fully in the new position.

Figure 6: Drawing problems after a set scale action

I haven't investigated this behavior enough to know whether it's a problem in QuickTime's action-handling code or in our own code. But there is at least one easy workaround: just inform the movie controller that the movie has changed, by calling MCMovieChanged. Listing 9 shows our updated code for handling the mcActionControllerSizeChanged action.

Listing 9: Setting a window size from a movie size (revised)

case mcActionControllerSizeChanged:
   [[[doc movieView] window] setContentSize:
            [doc windowContentSizeForMovie:[[[doc movieView] 
            movie] QTMovie]]];
   MCMovieChanged([[doc movieView] movieController], 
            [[[doc movieView] movie] QTMovie]);

By the way, this is not a Cocoa-specific problem. It will also affect our Carbon-based QTShell application. Accordingly, we should add a call to MCMovieChanged in QTShell's movie controller action filter procedure as well, as shown in Listing 10.

Listing 10: Setting a window size from a movie size (QTShell)

case mcActionControllerSizeChanged:
   if (theMC && (**myWindowObject).fMovie)
      MCMovieChanged(theMC, (**myWindowObject).fMovie);
   isHandled = true;

There is at least one other occasion when the movie controller might send us the mcActionControllerSizeChanged action, namely when a streamed movie changes its size dynamically during playback. We don't need any additional code to handle this, but we do need to tell the Movie Toolbox that we want to be informed of any changes in the size of streamed movies. We do this by setting a movie playback hint when we open the movie, as follows:

SetMoviePlayHints(qtMovie, hintsAllowDynamicResize, 

We are informed of movie size changes triggered by wired actions even if we don't set this play hint, but not those triggered by a dynamic size change of a streamed movie.

Cursor Adjustment

As you know, some media types change the cursor image as it moves over various regions in a track. Flash tracks and wired sprite tracks often do this, and QuickTime VR does this as a matter of course. (And with a vengeance: QuickTime VR defines nearly 80 different cursors that can be displayed as the user performs operations within a panoramic movie, and over 100 within an object movie.) Figure 7 shows a QuickTime VR movie with the mouse lying on top of a link hot spot (that is, a hot spot that, when clicked, moves the user to a new node).

Figure 7: A QuickTime VR node with the mouse over a link hot spot

The trouble is that some media types don't change the cursor back to the default arrow cursor when it moves outside of the movie box. The result is that our application ends up displaying an incorrect cursor, as shown in Figure 8. The mouse is just to the right of the movie box (toward the top of the movie). Note that its image is still one supplied by QuickTime VR, not the standard arrow cursor. We should fix that.

Figure 8: A QuickTime VR cursor displayed outside the movie box

Adjusting the Cursor in Carbon

Before we see how to adjust the cursor in our Cocoa application, let's digress briefly to consider how we already do this in our Carbon application QTShell. Listing 11 shows the definition of the QTApp_Idle function, which we call whenever we receive an idle event. As you can see, we simply check whether the current mouse position (which we get by calling GetMouse) is outside of the front movie window or front movie window's visible region; if the mouse is indeed outside of these regions, we call MacSetCursor to reset the cursor to the arrow cursor.

Listing 11: Adjusting the cursor in an idle procedure

void QTApp_Idle (WindowReference theWindow)
   WindowObject         myWindowObject = NULL;
   GrafPtr               mySavedPort;
   myWindowObject = QTFrame_GetWindowObjectFromWindow
   if (myWindowObject != NULL) {
      MovieController      myMC = NULL;
      myMC = (**myWindowObject).fController;
      if (myMC != NULL) {
         // restore the cursor to the arrow
         // if it's outside the front movie window or outside the window's visible region
         if (theWindow == QTFrame_GetFrontMovieWindow()) {
            Rect            myRect;
            Point         myPoint;
            RgnHandle   myVisRegion;
            Cursor         myArrow;
            myVisRegion = NewRgn();
            GetWindowPortBounds(theWindow, &myRect);
            if (!MacPtInRect(myPoint, &myRect) || 
                     !PtInRgn(myPoint, myVisRegion))

Frankly, this is old-style Mac programming. It works well enough, but it's likely to eat up CPU cycles unnecessarily since it's still stuck in that old polling mindset. Let's upgrade QTShell by reimplementing cursor adjusting using Carbon events. (For a general discussion of Carbon events and QuickTime, see "Event Horizon" in MacTech, May 2002.)

My first attempt to use Carbon events here was to have the window event handler register for events of class kEventClassMouse and type kEventMouseMoved. After all, we need to check to see if the cursor needs to change only if it's been moved. The trouble is that a movie in QTShell completely fills the content region of a movie window (except for the space occupied by the controller bar at the bottom of a movie window). Once the cursor moves out of the movie to the left or right, it's no longer over the movie window and hence subsequent mouse movements will not trigger kEventMouseMoved events for that movie window.

So I ended by having the application event handler register for kEventMouseMoved events, as shown in Listing 12. When it receives a mouse-moved event, it retrieves the position of the mouse and then checks to see if the mouse is currently outside the frontmost movie window. If it is, the cursor is reset to the default arrow cursor.

Listing 12: Adjusting the cursor in a Carbon event handler

case kEventClassMouse:
   switch (myKind) {
      case kEventMouseMoved:
         myErr = GetEventParameter(theEvent, 
                  kEventParamMouseLocation, typeQDPoint, NULL, 
                  sizeof(Point), NULL, &myPoint);
         if (myErr == noErr) {
            WindowRef      myWindow = NULL;
            Rect               myRect;
            Cursor            myArrow;
            // get the front movie window
            myWindow = QTFrame_GetFrontMovieWindow();
            if (myWindow != NULL) {
               GetWindowPortBounds(myWindow, &myRect);
               if (!PtInRect(myPoint, &myRect))

This strategy appears to work quite nicely, and it avoids the polling behavior of our original code. Strictly speaking, we need to adjust the cursor only when a movie contains interactive tracks (that is, Flash, wired sprite, wired text, or QuickTime VR), since they are the only kinds of tracks that are likely to change the cursor from the default arrow cursor. I doubt, however, that there is much to be gained by checking for interactive track types here. It's probably better just to reset the cursor to the arrow for every kind of QuickTime movie, as we do here.

Adjusting the Cursor in Cocoa

Let's return to the more pressing concern of adjusting the cursor in our Cocoa application. There are two principal ways we can return the cursor to its default arrow shape when it moves outside of the movie rectangle. First, we can add a tracking rectangle to the NSMovieView view that holds our movie, by executing the addTrackingRect: method. For instance, inside of windowControllerDidLoadNib:, we can execute this line of code:

[_movieView addTrackingRect:[_movieView bounds] owner:self 
            userData:nil assumeInside:NO];

The addTrackingRect: method causes the specified owner to receive mouseEntered: and mouseExited: messages whenever the mouse is moved into and out of the specified rectangle inside the target view. The message recipient is often the same view whose rectangle will be tracked, but it need not be. In the present case, indeed, we are setting the document instance as the owner, so that these messages are sent to it and not to the movie view. This allows us to define mouseEntered: and mouseExited: within our custom document class, so that we do not need to subclass NSMovieView.

Actually, since we are only interested in knowing when the mouse leaves the movie view, we shall implement only the mouseExited: method (Listing 13).

Listing 13: Handling mouse-exited messages

- (void) mouseExited:(NSEvent*)event
   // set cursor to the default cursor
   [[NSCursor arrowCursor] set];

Here we send the factory method arrowCursor to the NSCursor class and then set the resulting cursor.

It turns out that windowControllerDidLoadNib: is not really the best place to call addTrackingRect:. The main reason for this is that the tracking rectangle is not automatically resized when the target view is resized. Rather, we need to explicitly remove any current tracking rectangle and then add a new one each time the movie view changes size. So let's make the call to addTrackingRect: inside of our movie controller action filter procedure, when we handle the mcActionControllerSizeChanged action. Listing 14 shows our revised code for handling this action.

Listing 14: Resetting the tracking rectangle

case mcActionControllerSizeChanged:
   [[[doc movieView] window] setContentSize:
            [doc windowContentSizeForMovie:
            [[[doc movieView] movie] QTMovie]]];
   [[doc movieView] removeTrackingRect:
            [doc trackingRectTag]];
   [doc setTrackingRectTag:[[doc movieView] 
            addTrackingRect:[[doc movieView] bounds] owner:doc 
            userData:nil assumeInside:NO]];
   MCMovieChanged([[doc movieView] movieController], 
            [[[doc movieView] movie] QTMovie]);

First, we send the removeTrackingRect: message to the movie view to remove any existing tracking rectangle. Notice that removeTrackingRect: takes a parameter, which specifies a tracking rectangle tag (of the scalar type NSTrackingRectTag). This tag is returned to us by addTrackingRect:, and we are storing it in the new instance variable _trackingRectTag in our document class. Since we are calling removeTrackingRect: and addTrackingRect: within our movie controller action filter procedure, the document class needs to provide accessor methods for us to get and set the tag. Listings 15 and 16 show our definitions of these two methods. Nothing earthshaking here.

Listing 15: Getting the tracking rectangle tag

- (NSTrackingRectTag)trackingRectTag
   return _trackingRectTag;
Listing 16: Setting the tracking rectangle tag
- (void)setTrackingRectTag:(NSTrackingRectTag)rectTag
   _trackingRectTag = rectTag;

The second way of adjusting our cursor is to define a cursor rectangle for the movie view. A cursor rectangle is a special kind of tracking rectangle. What's special about it is that NSView knows that the rectangle is likely to change if the associated view is resized or moved. Accordingly, NSView calls the view's resetCursorRects method whenever that happens, to give the view a chance to resize or move the cursor rectangle. Listing 17 shows how we might define the resetCursorRects method.

Listing 17: Resetting the cursor rectangle

- (void)resetCursorRects
   NSCursor      *newCursor;
   [super resetCursorRects];
   newCursor = [NSCursor arrowCursor];
   [self addCursorRect:[self bounds] cursor:newCursor];
   [newCursor setOnMouseExited:YES];

Here we create a new arrow cursor and call addCursorRect: to attach it to a new cursor rectangle that is as large as the target view (that is, the movie view). Then we call setOnMouseExited: to configure the cursor to set itself as the current cursor when the mouse moves outside of the cursor rectangle.

As you can see, there is much less code required when using cursor rectangles than when using tracking rectangles. We don't need to keep track of the tracking rectangle tag, and we don't need any accessor functions to pass that tag to the movie controller action filter procedure. The only downside to using cursor rectangles is that we must subclass NSMovieView (so that we have a class to define the resetCursorRects method). As we saw above, we did not need to subclass NSMovieView when using tracking rectangles.


In this article, we revisited the basic Cocoa movie playing and editing application that we developed in the previous article, with an eye to tying up a few loose ends. We've now got all the items in the File and Edit menus working as expected, and we've cleaned up a few cosmetic glitches in our original version of MooVeez. With these improvements, MooVeez now matches quite closely the capabilities of QTShell, our benchmark Carbon movie playback and editing application. Moreover, the Cocoa framework upon which MooVeez is built provides a number of additional features not found in QTShell, including the "Open Recent" menu item in the File menu and the Window menu for managing all open document windows.


Listing 6 is based on some code by Vince DeMarco.

Tim Monroe in a member of the QuickTime engineering team. You can contact him at The views expressed here are not necessarily shared by his employer.


Community Search:
MacTech Search:

Software Updates via MacUpdate

BusyContacts 1.6.4 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more
Steam 4.0 - Multiplayer and communicatio...
Steam is a digital distribution, digital rights management, multiplayer and communications platform developed by Valve Corporation. It is used to distribute a large number of games and related media... Read more
OmniGraffle Pro 7.19.3 - Create diagrams...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
OmniGraffle 7.19.3 - Create diagrams, fl...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
Hopper Disassembler 5.3.3- - Binary disa...
Hopper Disassembler is a binary disassembler, decompiler, and debugger for 32- and 64-bit executables. It will let you disassemble any binary you want, and provide you all the information about its... Read more
calibre 5.35.0 - Complete e-book library...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Sound Studio 4.10.0 - Robust audio recor...
Sound Studio lets you easily record and professionally edit audio on your Mac. Easily rip vinyls and digitize cassette tapes, or record lectures and voice memos. Prepare for live shows with live... Read more
Sparkle Pro 4.0 - Visual website creator...
Sparkle Pro will change your mind if you thought building websites wasn't for you. Sparkle is the intuitive site builder that lets you create sites for your online portfolio, team or band pages, or... Read more
Dropbox 140.4.1951 - Cloud backup and sy...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
FotoMagico 6.0.5 - Powerful slideshow cr...
FotoMagico lets you create professional slideshows from your photos and music with just a few, simple mouse clicks. It sports a very clean and intuitive yet powerful user interface. High image... Read more

Latest Forum Discussions

See All

Best iPhone Game Updates: ‘Garena Free F...
Hello everyone, and welcome to the week! It’s time once again for our look back at the noteworthy updates of the last seven days. I got busted last week for not including the obligatory free-to-play matching puzzle game update of the week, and my... | Read more »
‘Horizon Chase’ China Spirit DLC Release...
Following the release of the excellent reveal of the Horizon Chase Senna Forever expansion, the game will be getting a new DLC on mobile platforms today. Today, the Horizon Chase China Spirit DLC pack will release on iOS and Android bringing in 9... | Read more »
‘PUZZLED’ from SNK and Hamster Is Out No...
Following ZED BLADE ACA NeoGeo earlier this month, SNK has brought over another game in the ACA NeoGeo series to both iOS and Android in the form of PUZZLED. SNK and Hamster originally brought the series to mobile with Samurai Shodown IV, Alpha... | Read more »
A House Full of Covid – The TouchArcade...
It’s been a rough week as both of our young children tested positive for Covid, and since recording this early on Friday my wife has tested positive now too. Thankfully the kids seemed to recover fairly quickly and are mostly back to normal, and I... | Read more »
TouchArcade Game of the Week: ‘Krispee S...
Krispee Street is a new hidden object game from Frosty Pop that is based on their popular and almost painfully sweet webcomic Krispee. This is one of the latest titles to be added to the Netflix Games catalog, which means you’ll need to log into... | Read more »
SwitchArcade Round-Up: ‘Escape Lala’, ‘B...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 21st, 2022. In today’s article, we’ve got a lot of new releases. A lot. There were eight on the schedule when I went to bed last night. There were twenty-four when I woke up... | Read more »
Beta Testers Needed for Huge Version 2.0...
Ya’ll remember Dungeon Raid, right? The phenomenal matching RPG hybrid that launched on mobile more than a decade ago, but was more or less abandoned by its developer only to die a slow death on the App Store before the 32-bit Appocalypse finally... | Read more »
‘Ark Legends’ Gives Players a Chance to...
It’s Airpods and Amazon gift cards galore as Melting Games opens pre-registration for Ark Legends. The upcoming mobile RPG is giving away tons of in-game goodies such as gold, energy, iron core, hero summon chest and rare iron core to players who... | Read more »
‘Nickelodeon Extreme Tennis’ Out Now on...
Nickelodeon Extreme Tennis () from Old Skull Games and Nickelodeon is this week’s new Apple Arcade release. Nickelodeon Extreme Tennis features characters from old and new Nickelodeon shows including SpongeBob, TMNT, and many more. The tennis game... | Read more »
SwitchArcade Round-Up: ‘RPGolf Legends’,...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for January 20th, 2022. In today’s article, we’ve got a massive amount of new releases to check out. We’ve got summaries of all of them, from heaven to hell. We also have the lists of... | Read more »

Price Scanner via

Verizon’s 2022 iPad promo: $100-$310 off any...
Verizon has cellular-capable iPads on sale for $100-$310 off MSRP when purchased with an Unlimited service plan. Sale price is applied to your account monthly over a 24 or 30 month period, depending... Read more
Sunday Sale: Apple AirPods are on sale for up...
Amazon has Apple AirPods on sale for $10-$100 off MSRP today, depending on the model. All are in stock today with free delivery: – AirPods Max headphones (Blue): $449 $100 off MSRP – AirPods Max... Read more
These Apple resellers are offering 13″ M1 Mac...
Apple resellers are offering discounts on 13″ MacBook Pros with M1 Apple Silicon processors ranging up to $150 off MSRP. Here’s where to get one today: (1): Apple’s 13″ MacBook Pros with M1 Apple... Read more
Amazon lowers prices on select 13″ M1 MacBook...
Amazon has select Apple 13″ M1 MacBook Airs on sale for $150 off MSRP this weekend, starting at only $849. Their prices are the lowest available for new MacBook Airs today. Stock may come and go, so... Read more
Apple has 13″ M1 MacBook Airs back in stock s...
Apple has restocked a full line of 13″ M1 MacBook Airs, Certified Refurbished, starting at only $849 and up to $190 off original MSRP. These are the cheapest M1-powered MacBooks for sale today at... Read more
In stock and on sale! 16″ 10-Core M1 Pro MacB...
Amazon has new 16″ 10-Core/512GB M1 Pro MacBook Pros in stock today and on sale for $50 off MSRP including free shipping. Their prices are the lowest available for new M1 Pro 16″ MacBook Pro from any... Read more
Deal Alert!: 14″ M1 Pro with 10-Core CPU in s...
Amazon has the new 14″ M1 Pro MacBook Pro with a 10-Core CPU and 16-Core GPU in stock today and on sale for $2299.99 including free shipping. Their price is $200 off Apple’s standard MSRP, and it’s... Read more
Apple has 24-inch M1 iMacs (8-Core CPU/8-Core...
Apple has restocked a wide array of 24-inch M1 iMacs with 8-Core CPUs and 8-Core GPUs in their Certified Refurbished store. Models are available starting at only $1269 and range up to $260 off... Read more
Select 24″ M1 iMacs are on sale for $100 off...
Sales of Apple’s new 24″ M1 iMacs have been rare since its introduction, perhaps due to global supply issues. However, B&H is offering a $100 discount on select 24″ iMacs, and they’re in stock... Read more
M1 Mac minis are back in stock today at Apple...
Apple has M1-powered Mac minis available in their Certified Refurbished section starting at only $589 and up to $140 off MSRP. Each mini comes with Apple’s one-year warranty, and shipping is free: –... Read more

Jobs Board

Registered Nurse (RN) Employee Health PSJH -...
…is calling for a Registered Nurse (RN) Employee Health PSJH to our location in Apple Valley, CA.** We are seeking a Registered Nurse (RN) Employee Health PSJH to be Read more
Systems Administrator - Pearson (United State...
…and troubleshoot Windows operating systems (workstation and server), laptop computers, Apple iPads, Chromebooks and printers** + **Administer and troubleshoot all Read more
IT Assistant Level 1- IT Desktop Support Anal...
…providing tier-1 or better IT help desk support in a large Windows and Apple environment * Experience using IT Service Desk Management Software * Knowledge of IT Read more
Human Resources Business Partner PSJH - Provi...
…**is calling a** **Human Resources Business Partner, PSJH** **to our location in Apple Valley, CA.** **Applicants that meet qualifications will receive a text with Read more
Manager Community Health Investment Programs...
…is calling a Manager Community Health Investment Programs PSJH to our location in Apple Valley, CA.** **Qualified candidates will be invited to do a self-paced video Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.